From caeacf211cf4c264d770f2e10492528c6be9d9dd Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Thu, 22 Dec 2016 13:28:55 +0100 Subject: [PATCH] Add Doctrine Migrations Signed-off-by: Lukas Reschke --- .gitignore | 52 + composer.json | 3 +- composer.lock | 301 +++- composer/autoload_classmap.php | 298 ++++ composer/autoload_files.php | 2 +- composer/autoload_namespaces.php | 1 + composer/autoload_psr4.php | 4 + composer/autoload_static.php | 324 +++- composer/installed.json | 307 ++++ .../Migrations/AbortMigrationException.php | 24 + .../DBAL/Migrations/AbstractMigration.php | 189 +++ .../AbstractFileConfiguration.php | 149 ++ .../Configuration/ArrayConfiguration.php | 45 + .../Configuration/Configuration.php | 852 +++++++++++ .../Connection/ConnectionLoaderInterface.php | 33 + .../ArrayConnectionConfigurationLoader.php | 58 + .../ConnectionConfigurationChainLoader.php | 52 + .../Loader/ConnectionConfigurationLoader.php | 54 + .../Loader/ConnectionHelperLoader.php | 70 + .../Configuration/JsonConfiguration.php | 47 + .../Configuration/XML/configuration.xsd | 40 + .../Configuration/XmlConfiguration.php | 74 + .../Configuration/YamlConfiguration.php | 51 + .../DBAL/Migrations/Finder/AbstractFinder.php | 86 ++ .../DBAL/Migrations/Finder/GlobFinder.php | 45 + .../Finder/MigrationDeepFinderInterface.php | 29 + .../Finder/MigrationFinderInterface.php | 40 + .../Finder/RecursiveRegexFinder.php | 77 + .../IrreversibleMigrationException.php | 33 + .../Doctrine/DBAL/Migrations/Migration.php | 188 +++ .../DBAL/Migrations/MigrationException.php | 119 ++ .../DBAL/Migrations/MigrationsVersion.php | 45 + .../Doctrine/DBAL/Migrations/OutputWriter.php | 52 + .../Provider/LazySchemaDiffProvider.php | 110 ++ .../Migrations/Provider/OrmSchemaProvider.php | 82 + .../Provider/SchemaDiffProvider.php | 67 + .../Provider/SchemaDiffProviderInterface.php | 56 + .../Provider/SchemaProviderInterface.php | 36 + .../Provider/StubSchemaProvider.php | 48 + .../Migrations/SkipMigrationException.php | 24 + .../DBAL/Migrations/SqlFileWriter.php | 128 ++ .../Tools/Console/Command/AbstractCommand.php | 183 +++ .../Tools/Console/Command/DiffCommand.php | 196 +++ .../Tools/Console/Command/ExecuteCommand.php | 102 ++ .../Tools/Console/Command/GenerateCommand.php | 136 ++ .../Tools/Console/Command/LatestCommand.php | 48 + .../Tools/Console/Command/MigrateCommand.php | 194 +++ .../Tools/Console/Command/StatusCommand.php | 118 ++ .../Tools/Console/Command/VersionCommand.php | 179 +++ .../Console/Helper/ConfigurationHelper.php | 130 ++ .../Helper/MigrationDirectoryHelper.php | 93 ++ .../Helper/MigrationStatusInfosHelper.php | 86 ++ .../lib/Doctrine/DBAL/Migrations/Version.php | 428 ++++++ .../ProxyManager/Autoloader/Autoloader.php | 69 + .../Autoloader/AutoloaderInterface.php | 37 + .../src/ProxyManager/Configuration.php | 241 +++ .../Exception/DisabledMethodException.php | 42 + .../Exception/ExceptionInterface.php | 29 + .../Exception/FileNotWritableException.php | 69 + .../InvalidProxiedClassException.php | 79 + .../InvalidProxyDirectoryException.php | 40 + .../UnsupportedProxiedClassException.php | 47 + .../Factory/AbstractBaseFactory.php | 112 ++ .../Factory/AbstractLazyFactory.php | 46 + ...AccessInterceptorScopeLocalizerFactory.php | 59 + .../AccessInterceptorValueHolderFactory.php | 59 + .../Factory/LazyLoadingGhostFactory.php | 45 + .../Factory/LazyLoadingValueHolderFactory.php | 45 + .../Factory/NullObjectFactory.php | 56 + .../RemoteObject/Adapter/BaseAdapter.php | 81 + .../Factory/RemoteObject/Adapter/JsonRpc.php | 36 + .../Factory/RemoteObject/Adapter/Soap.php | 36 + .../Factory/RemoteObject/Adapter/XmlRpc.php | 36 + .../Factory/RemoteObject/AdapterInterface.php | 37 + .../Factory/RemoteObjectFactory.php | 76 + .../ProxyManager/FileLocator/FileLocator.php | 57 + .../FileLocator/FileLocatorInterface.php | 37 + .../ProxyManager/Generator/ClassGenerator.php | 54 + .../Generator/MagicMethodGenerator.php | 52 + .../Generator/MethodGenerator.php | 161 ++ .../Generator/ParameterGenerator.php | 149 ++ .../Generator/Util/ClassGeneratorUtils.php | 53 + .../Util/UniqueIdentifierGenerator.php | 53 + .../BaseGeneratorStrategy.php | 38 + .../EvaluatingGeneratorStrategy.php | 69 + .../FileWriterGeneratorStrategy.php | 105 ++ .../GeneratorStrategyInterface.php | 39 + .../Inflector/ClassNameInflector.php | 98 ++ .../Inflector/ClassNameInflectorInterface.php | 61 + .../Inflector/Util/ParameterEncoder.php | 41 + .../Inflector/Util/ParameterHasher.php | 40 + .../Proxy/AccessInterceptorInterface.php | 64 + .../Proxy/Exception/RemoteObjectException.php | 31 + .../Proxy/FallbackValueHolderInterface.php | 29 + .../Proxy/GhostObjectInterface.php | 29 + .../Proxy/LazyLoadingInterface.php | 64 + .../Proxy/NullObjectInterface.php | 29 + .../src/ProxyManager/Proxy/ProxyInterface.php | 29 + .../Proxy/RemoteObjectInterface.php | 29 + .../Proxy/SmartReferenceInterface.php | 29 + .../Proxy/ValueHolderInterface.php | 33 + .../Proxy/VirtualProxyInterface.php | 29 + .../MethodGenerator/MagicWakeup.php | 50 + .../SetMethodPrefixInterceptor.php | 50 + .../SetMethodSuffixInterceptor.php | 50 + .../MethodPrefixInterceptors.php | 43 + .../MethodSuffixInterceptors.php | 43 + .../MethodGenerator/Constructor.php | 92 ++ .../MethodGenerator/InterceptedMethod.php | 67 + .../MethodGenerator/MagicClone.php | 53 + .../MethodGenerator/MagicGet.php | 73 + .../MethodGenerator/MagicIsset.php | 73 + .../MethodGenerator/MagicSet.php | 77 + .../MethodGenerator/MagicSleep.php | 57 + .../MethodGenerator/MagicUnset.php | 73 + .../Util/InterceptorGenerator.php | 82 + ...cessInterceptorScopeLocalizerGenerator.php | 96 ++ .../MethodGenerator/Constructor.php | 79 + .../MethodGenerator/InterceptedMethod.php | 70 + .../MethodGenerator/MagicClone.php | 58 + .../MethodGenerator/MagicGet.php | 78 + .../MethodGenerator/MagicIsset.php | 78 + .../MethodGenerator/MagicSet.php | 82 + .../MethodGenerator/MagicUnset.php | 80 + .../Util/InterceptorGenerator.php | 85 ++ .../AccessInterceptorValueHolderGenerator.php | 138 ++ .../Assertion/CanProxyAssertion.php | 102 ++ .../MethodGenerator/Constructor.php | 58 + .../MethodGenerator/CallInitializer.php | 67 + .../MethodGenerator/GetProxyInitializer.php | 42 + .../MethodGenerator/InitializeProxy.php | 47 + .../MethodGenerator/IsProxyInitialized.php | 42 + .../LazyLoadingMethodInterceptor.php | 71 + .../MethodGenerator/MagicClone.php | 50 + .../MethodGenerator/MagicGet.php | 77 + .../MethodGenerator/MagicIsset.php | 77 + .../MethodGenerator/MagicSet.php | 81 + .../MethodGenerator/MagicSleep.php | 50 + .../MethodGenerator/MagicUnset.php | 78 + .../MethodGenerator/SetProxyInitializer.php | 49 + .../InitializationTracker.php | 43 + .../PropertyGenerator/InitializerProperty.php | 42 + .../LazyLoadingGhostGenerator.php | 114 ++ .../MethodGenerator/GetProxyInitializer.php | 42 + .../MethodGenerator/InitializeProxy.php | 49 + .../MethodGenerator/IsProxyInitialized.php | 42 + .../LazyLoadingMethodInterceptor.php | 72 + .../MethodGenerator/MagicClone.php | 53 + .../MethodGenerator/MagicGet.php | 69 + .../MethodGenerator/MagicIsset.php | 72 + .../MethodGenerator/MagicSet.php | 79 + .../MethodGenerator/MagicSleep.php | 53 + .../MethodGenerator/MagicUnset.php | 72 + .../MethodGenerator/SetProxyInitializer.php | 49 + .../PropertyGenerator/InitializerProperty.php | 42 + .../PropertyGenerator/ValueHolderProperty.php | 42 + .../LazyLoadingValueHolderGenerator.php | 111 ++ .../MethodGenerator/Constructor.php | 55 + .../NullObjectMethodInterceptor.php | 53 + .../ProxyGenerator/NullObjectGenerator.php | 65 + .../PublicPropertiesDefaults.php | 58 + .../PropertyGenerator/PublicPropertiesMap.php | 63 + .../ProxyGeneratorInterface.php | 42 + .../MethodGenerator/Constructor.php | 63 + .../RemoteObject/MethodGenerator/MagicGet.php | 49 + .../MethodGenerator/MagicIsset.php | 50 + .../RemoteObject/MethodGenerator/MagicSet.php | 54 + .../MethodGenerator/MagicUnset.php | 51 + .../MethodGenerator/RemoteObjectMethod.php | 63 + .../PropertyGenerator/AdapterProperty.php | 42 + .../ProxyGenerator/RemoteObjectGenerator.php | 93 ++ .../Util/ProxiedMethodsFilter.php | 56 + .../Util/PublicScopeSimulator.php | 190 +++ .../GetWrappedValueHolderValue.php | 42 + .../MethodGenerator/MagicSleep.php | 42 + .../Signature/ClassSignatureGenerator.php | 58 + .../ClassSignatureGeneratorInterface.php | 40 + .../Exception/ExceptionInterface.php | 29 + .../Exception/InvalidSignatureException.php | 50 + .../Exception/MissingSignatureException.php | 48 + .../Signature/SignatureChecker.php | 68 + .../Signature/SignatureCheckerInterface.php | 43 + .../Signature/SignatureGenerator.php | 66 + .../Signature/SignatureGeneratorInterface.php | 46 + .../src/ProxyManager/Version.php | 39 + symfony/yaml/Command/LintCommand.php | 234 +++ symfony/yaml/Dumper.php | 114 ++ symfony/yaml/Escaper.php | 99 ++ symfony/yaml/Exception/DumpException.php | 21 + symfony/yaml/Exception/ExceptionInterface.php | 21 + symfony/yaml/Exception/ParseException.php | 141 ++ symfony/yaml/Exception/RuntimeException.php | 21 + symfony/yaml/Inline.php | 728 +++++++++ symfony/yaml/Parser.php | 879 +++++++++++ symfony/yaml/Unescaper.php | 142 ++ symfony/yaml/Yaml.php | 119 ++ .../src/Annotation/AnnotationCollection.php | 32 + .../src/Annotation/AnnotationInterface.php | 21 + .../src/Annotation/AnnotationManager.php | 109 ++ .../Parser/DoctrineAnnotationParser.php | 153 ++ .../Parser/GenericAnnotationParser.php | 224 +++ .../src/Annotation/Parser/ParserInterface.php | 39 + .../src/Exception/BadMethodCallException.php | 15 + .../src/Exception/ExceptionInterface.php | 14 + .../Exception/InvalidArgumentException.php | 15 + .../src/Exception/RuntimeException.php | 14 + .../src/Generator/AbstractGenerator.php | 124 ++ .../src/Generator/AbstractMemberGenerator.php | 242 +++ .../zend-code/src/Generator/BodyGenerator.php | 44 + .../src/Generator/ClassGenerator.php | 1012 +++++++++++++ .../zend-code/src/Generator/DocBlock/Tag.php | 50 + .../DocBlock/Tag/AbstractTypeableTag.php | 96 ++ .../src/Generator/DocBlock/Tag/AuthorTag.php | 110 ++ .../src/Generator/DocBlock/Tag/GenericTag.php | 88 ++ .../src/Generator/DocBlock/Tag/LicenseTag.php | 110 ++ .../src/Generator/DocBlock/Tag/MethodTag.php | 98 ++ .../src/Generator/DocBlock/Tag/ParamTag.php | 124 ++ .../Generator/DocBlock/Tag/PropertyTag.php | 71 + .../src/Generator/DocBlock/Tag/ReturnTag.php | 67 + .../Generator/DocBlock/Tag/TagInterface.php | 16 + .../src/Generator/DocBlock/Tag/ThrowsTag.php | 33 + .../src/Generator/DocBlock/TagManager.php | 69 + .../src/Generator/DocBlockGenerator.php | 274 ++++ .../Exception/ExceptionInterface.php | 16 + .../Exception/InvalidArgumentException.php | 17 + .../Generator/Exception/RuntimeException.php | 17 + .../zend-code/src/Generator/FileGenerator.php | 561 +++++++ .../src/Generator/FileGeneratorRegistry.php | 44 + .../src/Generator/GeneratorInterface.php | 15 + .../src/Generator/InterfaceGenerator.php | 150 ++ .../src/Generator/MethodGenerator.php | 322 ++++ .../src/Generator/ParameterGenerator.php | 300 ++++ .../src/Generator/PropertyGenerator.php | 226 +++ .../src/Generator/PropertyValueGenerator.php | 23 + .../src/Generator/TraitGenerator.php | 173 +++ .../src/Generator/TraitUsageGenerator.php | 353 +++++ .../src/Generator/TraitUsageInterface.php | 159 ++ .../src/Generator/ValueGenerator.php | 463 ++++++ .../Prototype/PrototypeClassFactory.php | 121 ++ .../Prototype/PrototypeGenericInterface.php | 18 + .../Generic/Prototype/PrototypeInterface.php | 18 + .../zend-code/src/NameInformation.php | 156 ++ .../src/Reflection/ClassReflection.php | 283 ++++ .../src/Reflection/DocBlock/Tag/AuthorTag.php | 74 + .../Reflection/DocBlock/Tag/GenericTag.php | 109 ++ .../Reflection/DocBlock/Tag/LicenseTag.php | 74 + .../src/Reflection/DocBlock/Tag/MethodTag.php | 122 ++ .../src/Reflection/DocBlock/Tag/ParamTag.php | 98 ++ .../DocBlock/Tag/PhpDocTypedTagInterface.php | 20 + .../Reflection/DocBlock/Tag/PropertyTag.php | 100 ++ .../src/Reflection/DocBlock/Tag/ReturnTag.php | 75 + .../Reflection/DocBlock/Tag/TagInterface.php | 21 + .../src/Reflection/DocBlock/Tag/ThrowsTag.php | 74 + .../src/Reflection/DocBlock/TagManager.php | 48 + .../src/Reflection/DocBlockReflection.php | 296 ++++ .../Exception/BadMethodCallException.php | 17 + .../Exception/ExceptionInterface.php | 16 + .../Exception/InvalidArgumentException.php | 17 + .../Reflection/Exception/RuntimeException.php | 17 + .../src/Reflection/FileReflection.php | 317 ++++ .../src/Reflection/FunctionReflection.php | 269 ++++ .../src/Reflection/MethodReflection.php | 494 ++++++ .../src/Reflection/ParameterReflection.php | 116 ++ .../src/Reflection/PropertyReflection.php | 114 ++ .../src/Reflection/ReflectionInterface.php | 20 + .../src/Scanner/AggregateDirectoryScanner.php | 117 ++ .../src/Scanner/AnnotationScanner.php | 353 +++++ .../src/Scanner/CachingFileScanner.php | 160 ++ .../zend-code/src/Scanner/ClassScanner.php | 1344 +++++++++++++++++ .../zend-code/src/Scanner/ConstantScanner.php | 236 +++ .../src/Scanner/DerivedClassScanner.php | 381 +++++ .../src/Scanner/DirectoryScanner.php | 272 ++++ .../zend-code/src/Scanner/DocBlockScanner.php | 326 ++++ .../zend-code/src/Scanner/FileScanner.php | 46 + .../zend-code/src/Scanner/FunctionScanner.php | 16 + .../zend-code/src/Scanner/MethodScanner.php | 607 ++++++++ .../src/Scanner/ParameterScanner.php | 352 +++++ .../zend-code/src/Scanner/PropertyScanner.php | 317 ++++ .../src/Scanner/ScannerInterface.php | 16 + .../src/Scanner/TokenArrayScanner.php | 682 +++++++++ zendframework/zend-code/src/Scanner/Util.php | 74 + .../zend-code/src/Scanner/ValueScanner.php | 15 + .../src/AbstractListenerAggregate.php | 32 + zendframework/zend-eventmanager/src/Event.php | 201 +++ .../zend-eventmanager/src/EventInterface.php | 96 ++ .../zend-eventmanager/src/EventManager.php | 343 +++++ .../src/EventManagerAwareInterface.php | 24 + .../src/EventManagerAwareTrait.php | 75 + .../src/EventManagerInterface.php | 164 ++ .../src/EventsCapableInterface.php | 25 + .../src/Exception/DomainException.php | 14 + .../src/Exception/ExceptionInterface.php | 17 + .../Exception/InvalidArgumentException.php | 17 + .../Exception/InvalidCallbackException.php | 17 + .../src/Exception/RuntimeException.php | 16 + .../src/Filter/FilterInterface.php | 63 + .../src/Filter/FilterIterator.php | 120 ++ .../zend-eventmanager/src/FilterChain.php | 112 ++ .../src/LazyEventListener.php | 75 + .../zend-eventmanager/src/LazyListener.php | 122 ++ .../src/LazyListenerAggregate.php | 110 ++ .../src/ListenerAggregateInterface.php | 41 + .../src/ListenerAggregateTrait.php | 33 + .../src/ResponseCollection.php | 82 + .../src/SharedEventManager.php | 237 +++ .../src/SharedEventManagerInterface.php | 59 + .../src/SharedEventsCapableInterface.php | 24 + .../Test/EventListenerIntrospectionTrait.php | 152 ++ 308 files changed, 33655 insertions(+), 5 deletions(-) create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XML/configuration.xsd create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php create mode 100644 doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Configuration.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/FileWriterGeneratorStrategy.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php create mode 100644 ocramius/proxy-manager/src/ProxyManager/Version.php create mode 100644 symfony/yaml/Command/LintCommand.php create mode 100644 symfony/yaml/Dumper.php create mode 100644 symfony/yaml/Escaper.php create mode 100644 symfony/yaml/Exception/DumpException.php create mode 100644 symfony/yaml/Exception/ExceptionInterface.php create mode 100644 symfony/yaml/Exception/ParseException.php create mode 100644 symfony/yaml/Exception/RuntimeException.php create mode 100644 symfony/yaml/Inline.php create mode 100644 symfony/yaml/Parser.php create mode 100644 symfony/yaml/Unescaper.php create mode 100644 symfony/yaml/Yaml.php create mode 100644 zendframework/zend-code/src/Annotation/AnnotationCollection.php create mode 100644 zendframework/zend-code/src/Annotation/AnnotationInterface.php create mode 100644 zendframework/zend-code/src/Annotation/AnnotationManager.php create mode 100644 zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php create mode 100644 zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php create mode 100644 zendframework/zend-code/src/Annotation/Parser/ParserInterface.php create mode 100644 zendframework/zend-code/src/Exception/BadMethodCallException.php create mode 100644 zendframework/zend-code/src/Exception/ExceptionInterface.php create mode 100644 zendframework/zend-code/src/Exception/InvalidArgumentException.php create mode 100644 zendframework/zend-code/src/Exception/RuntimeException.php create mode 100644 zendframework/zend-code/src/Generator/AbstractGenerator.php create mode 100644 zendframework/zend-code/src/Generator/AbstractMemberGenerator.php create mode 100644 zendframework/zend-code/src/Generator/BodyGenerator.php create mode 100644 zendframework/zend-code/src/Generator/ClassGenerator.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/Tag/ThrowsTag.php create mode 100644 zendframework/zend-code/src/Generator/DocBlock/TagManager.php create mode 100644 zendframework/zend-code/src/Generator/DocBlockGenerator.php create mode 100644 zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php create mode 100644 zendframework/zend-code/src/Generator/Exception/InvalidArgumentException.php create mode 100644 zendframework/zend-code/src/Generator/Exception/RuntimeException.php create mode 100644 zendframework/zend-code/src/Generator/FileGenerator.php create mode 100644 zendframework/zend-code/src/Generator/FileGeneratorRegistry.php create mode 100644 zendframework/zend-code/src/Generator/GeneratorInterface.php create mode 100644 zendframework/zend-code/src/Generator/InterfaceGenerator.php create mode 100644 zendframework/zend-code/src/Generator/MethodGenerator.php create mode 100644 zendframework/zend-code/src/Generator/ParameterGenerator.php create mode 100644 zendframework/zend-code/src/Generator/PropertyGenerator.php create mode 100644 zendframework/zend-code/src/Generator/PropertyValueGenerator.php create mode 100644 zendframework/zend-code/src/Generator/TraitGenerator.php create mode 100644 zendframework/zend-code/src/Generator/TraitUsageGenerator.php create mode 100644 zendframework/zend-code/src/Generator/TraitUsageInterface.php create mode 100644 zendframework/zend-code/src/Generator/ValueGenerator.php create mode 100644 zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php create mode 100644 zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php create mode 100644 zendframework/zend-code/src/Generic/Prototype/PrototypeInterface.php create mode 100644 zendframework/zend-code/src/NameInformation.php create mode 100644 zendframework/zend-code/src/Reflection/ClassReflection.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/PropertyTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/Tag/ThrowsTag.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlock/TagManager.php create mode 100644 zendframework/zend-code/src/Reflection/DocBlockReflection.php create mode 100644 zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php create mode 100644 zendframework/zend-code/src/Reflection/Exception/ExceptionInterface.php create mode 100644 zendframework/zend-code/src/Reflection/Exception/InvalidArgumentException.php create mode 100644 zendframework/zend-code/src/Reflection/Exception/RuntimeException.php create mode 100644 zendframework/zend-code/src/Reflection/FileReflection.php create mode 100644 zendframework/zend-code/src/Reflection/FunctionReflection.php create mode 100644 zendframework/zend-code/src/Reflection/MethodReflection.php create mode 100644 zendframework/zend-code/src/Reflection/ParameterReflection.php create mode 100644 zendframework/zend-code/src/Reflection/PropertyReflection.php create mode 100644 zendframework/zend-code/src/Reflection/ReflectionInterface.php create mode 100644 zendframework/zend-code/src/Scanner/AggregateDirectoryScanner.php create mode 100644 zendframework/zend-code/src/Scanner/AnnotationScanner.php create mode 100644 zendframework/zend-code/src/Scanner/CachingFileScanner.php create mode 100644 zendframework/zend-code/src/Scanner/ClassScanner.php create mode 100644 zendframework/zend-code/src/Scanner/ConstantScanner.php create mode 100644 zendframework/zend-code/src/Scanner/DerivedClassScanner.php create mode 100644 zendframework/zend-code/src/Scanner/DirectoryScanner.php create mode 100644 zendframework/zend-code/src/Scanner/DocBlockScanner.php create mode 100644 zendframework/zend-code/src/Scanner/FileScanner.php create mode 100644 zendframework/zend-code/src/Scanner/FunctionScanner.php create mode 100644 zendframework/zend-code/src/Scanner/MethodScanner.php create mode 100644 zendframework/zend-code/src/Scanner/ParameterScanner.php create mode 100644 zendframework/zend-code/src/Scanner/PropertyScanner.php create mode 100644 zendframework/zend-code/src/Scanner/ScannerInterface.php create mode 100644 zendframework/zend-code/src/Scanner/TokenArrayScanner.php create mode 100644 zendframework/zend-code/src/Scanner/Util.php create mode 100644 zendframework/zend-code/src/Scanner/ValueScanner.php create mode 100644 zendframework/zend-eventmanager/src/AbstractListenerAggregate.php create mode 100644 zendframework/zend-eventmanager/src/Event.php create mode 100644 zendframework/zend-eventmanager/src/EventInterface.php create mode 100644 zendframework/zend-eventmanager/src/EventManager.php create mode 100644 zendframework/zend-eventmanager/src/EventManagerAwareInterface.php create mode 100644 zendframework/zend-eventmanager/src/EventManagerAwareTrait.php create mode 100644 zendframework/zend-eventmanager/src/EventManagerInterface.php create mode 100644 zendframework/zend-eventmanager/src/EventsCapableInterface.php create mode 100644 zendframework/zend-eventmanager/src/Exception/DomainException.php create mode 100644 zendframework/zend-eventmanager/src/Exception/ExceptionInterface.php create mode 100644 zendframework/zend-eventmanager/src/Exception/InvalidArgumentException.php create mode 100644 zendframework/zend-eventmanager/src/Exception/InvalidCallbackException.php create mode 100644 zendframework/zend-eventmanager/src/Exception/RuntimeException.php create mode 100644 zendframework/zend-eventmanager/src/Filter/FilterInterface.php create mode 100644 zendframework/zend-eventmanager/src/Filter/FilterIterator.php create mode 100644 zendframework/zend-eventmanager/src/FilterChain.php create mode 100644 zendframework/zend-eventmanager/src/LazyEventListener.php create mode 100644 zendframework/zend-eventmanager/src/LazyListener.php create mode 100644 zendframework/zend-eventmanager/src/LazyListenerAggregate.php create mode 100644 zendframework/zend-eventmanager/src/ListenerAggregateInterface.php create mode 100644 zendframework/zend-eventmanager/src/ListenerAggregateTrait.php create mode 100644 zendframework/zend-eventmanager/src/ResponseCollection.php create mode 100644 zendframework/zend-eventmanager/src/SharedEventManager.php create mode 100644 zendframework/zend-eventmanager/src/SharedEventManagerInterface.php create mode 100644 zendframework/zend-eventmanager/src/SharedEventsCapableInterface.php create mode 100644 zendframework/zend-eventmanager/src/Test/EventListenerIntrospectionTrait.php diff --git a/.gitignore b/.gitignore index f475acc55..67bbedc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,16 @@ doctrine/dbal/.travis.yml doctrine/inflector/tests doctrine/lexer/composer.json doctrine/lexer/LICENSE +doctrine/migrations/bin +doctrine/migrations/tests +doctrine/migrations/.coveralls.yml +doctrine/migrations/build-phar.sh +doctrine/migrations/CHANGELOG.md +doctrine/migrations/composer.json +doctrine/migrations/LICENSE +doctrine/migrations/phpunit.xml.dist +doctrine/migrations/README.md +doctrine/migrations/UPGRADE-1.0.md pear/archive_tar/.travis.sh pear/archive_tar/.travis.yml @@ -66,6 +76,14 @@ symfony/process/Symfony/Component/Process/phpunit.xml.dist symfony/process/Symfony/Component/Process/Tests symfony/process/Symfony/Component/Process/.gitignore +symfony/yaml/Tests +symfony/yaml/.gitignore +symfony/yaml/CHANGELOG.md +symfony/yaml/composer.json +symfony/yaml/LICENSE +symfony/yaml/phpunit.xml.dist +symfony/yaml/README.md + pimple/pimple/ext pimple/pimple/.gitignore pimple/pimple/.travis.yml @@ -201,3 +219,37 @@ paragonie/random_compat/.scrutinizer.yml paragonie/random_compat/ERRATA.md paragonie/random_compat/README.md paragonie/random_compat/SECURITY.md + +ocramius/proxy-manager/docs +ocramius/proxy-manager/examples +ocramius/proxy-manager/html-docs +ocramius/proxy-manager/tests +ocramius/proxy-manager/.gitignore +ocramius/proxy-manager/.scrutinizer.yml +ocramius/proxy-manager/.travis.install.sh +ocramius/proxy-manager/.travis.yml +ocramius/proxy-manager/composer.json +ocramius/proxy-manager/CONTRIBUTING.md +ocramius/proxy-manager/index.html +ocramius/proxy-manager/LICENSE +ocramius/proxy-manager/phpdox.xml.dist +ocramius/proxy-manager/phpmd.xml.dist +ocramius/proxy-manager/phpunit.xml.dist +ocramius/proxy-manager/proxy-manager.png +ocramius/proxy-manager/proxy-manager.svg +ocramius/proxy-manager/README.md +ocramius/proxy-manager/STABILITY.md +ocramius/proxy-manager/UPGRADE.md + +zendframework/zend-code/doc +zendframework/zend-code/CHANGELOG.md +zendframework/zend-code/composer.json +zendframework/zend-code/CONTRIBUTING.md +zendframework/zend-code/LICENSE.md +zendframework/zend-code/README.md +zendframework/zend-eventmanager/CHANGELOG.md +zendframework/zend-eventmanager/composer.json +zendframework/zend-eventmanager/CONDUCT.md +zendframework/zend-eventmanager/CONTRIBUTING.md +zendframework/zend-eventmanager/LICENSE.md +zendframework/zend-eventmanager/README.md diff --git a/composer.json b/composer.json index d7c4889cc..9bb71b948 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "sabre/dav": "^3.2.0", "symfony/polyfill-php70": "^1.0", "lukasreschke/id3parser": "^0.0.1", - "stecman/symfony-console-completion": "^0.7.0" + "stecman/symfony-console-completion": "^0.7.0", + "doctrine/migrations": "^1.4" } } diff --git a/composer.lock b/composer.lock index 20169e897..4cf89d721 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "f34b0dc10c6d9ddd6fd1296d2f4b5525", - "content-hash": "5da1e28d01bc81574941e20e05c6dcba", + "hash": "e23abe941ce0e13c3fb922cc32922a96", + "content-hash": "581a9055e465a299086c982e4425e3c0", "packages": [ { "name": "bantu/ini-get-wrapper", @@ -548,6 +548,79 @@ ], "time": "2014-09-09 13:34:57" }, + { + "name": "doctrine/migrations", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/0d0ff5da10c5d30846da32060bd9e357abf70a05", + "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.2", + "ocramius/proxy-manager": "^1.0|^2.0", + "php": "^5.5|^7.0", + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/coding-standard": "dev-master", + "doctrine/orm": "2.*", + "jdorn/sql-formatter": "~1.1", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "~4.7", + "satooshi/php-coveralls": "0.6.*" + }, + "suggest": { + "jdorn/sql-formatter": "Allows to generate formatted SQL with the diff command." + }, + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "v1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\Migrations\\": "lib/Doctrine/DBAL/Migrations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "Database Schema migrations using Doctrine DBAL", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "migrations" + ], + "time": "2016-03-14 12:29:11" + }, { "name": "guzzle/common", "version": "v3.8.1", @@ -1312,6 +1385,69 @@ ], "time": "2015-09-19 14:15:08" }, + { + "name": "ocramius/proxy-manager", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/ProxyManager.git", + "reference": "57e9272ec0e8deccf09421596e0e2252df440e11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/57e9272ec0e8deccf09421596e0e2252df440e11", + "reference": "57e9272ec0e8deccf09421596e0e2252df440e11", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "zendframework/zend-code": ">2.2.5,<3.0" + }, + "require-dev": { + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "1.5.*" + }, + "suggest": { + "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", + "zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)", + "zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)", + "zendframework/zend-stdlib": "To use the hydrator proxy", + "zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "ProxyManager\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", + "homepage": "https://github.com/Ocramius/ProxyManager", + "keywords": [ + "aop", + "lazy loading", + "proxy", + "proxy pattern", + "service proxies" + ], + "time": "2015-08-09 04:28:19" + }, { "name": "paragonie/random_compat", "version": "v1.4.1", @@ -2893,6 +3029,167 @@ "url" ], "time": "2016-08-16 14:58:24" + }, + { + "name": "symfony/yaml", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a7095af4b97a0955f85c8989106c249fa649011f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7095af4b97a0955f85c8989106c249fa649011f", + "reference": "a7095af4b97a0955f85c8989106c249fa649011f", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-12-10 10:07:06" + }, + { + "name": "zendframework/zend-code", + "version": "2.6.3", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-code.git", + "reference": "95033f061b083e16cdee60530ec260d7d628b887" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/95033f061b083e16cdee60530ec260d7d628b887", + "reference": "95033f061b083e16cdee60530ec260d7d628b887", + "shasum": "" + }, + "require": { + "php": "^5.5 || 7.0.0 - 7.0.4 || ^7.0.6", + "zendframework/zend-eventmanager": "^2.6 || ^3.0" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "^4.8.21", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "zendframework/zend-stdlib": "Zend\\Stdlib component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Code\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides facilities to generate arbitrary code using an object oriented interface", + "homepage": "https://github.com/zendframework/zend-code", + "keywords": [ + "code", + "zf2" + ], + "time": "2016-04-20 17:26:42" + }, + { + "name": "zendframework/zend-eventmanager", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-eventmanager.git", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "phpunit/phpunit": "^5.6", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + }, + "suggest": { + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://github.com/zendframework/zend-eventmanager", + "keywords": [ + "event", + "eventmanager", + "events", + "zf2" + ], + "time": "2016-12-19 21:47:12" } ], "packages-dev": [], diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index d6712299f..42b9aaae6 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -284,6 +284,49 @@ 'Doctrine\\DBAL\\Logging\\EchoSQLLogger' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php', 'Doctrine\\DBAL\\Logging\\LoggerChain' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php', 'Doctrine\\DBAL\\Logging\\SQLLogger' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php', + 'Doctrine\\DBAL\\Migrations\\AbortMigrationException' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\AbstractMigration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\AbstractFileConfiguration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\ArrayConfiguration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Configuration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\ConnectionLoaderInterface' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ArrayConnectionConfigurationLoader' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionConfigurationChainLoader' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionConfigurationLoader' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionHelperLoader' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\JsonConfiguration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\XmlConfiguration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\YamlConfiguration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\AbstractFinder' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\GlobFinder' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\MigrationDeepFinderInterface' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\MigrationFinderInterface' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\RecursiveRegexFinder' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php', + 'Doctrine\\DBAL\\Migrations\\IrreversibleMigrationException' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\Migration' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php', + 'Doctrine\\DBAL\\Migrations\\MigrationException' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php', + 'Doctrine\\DBAL\\Migrations\\MigrationsVersion' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php', + 'Doctrine\\DBAL\\Migrations\\OutputWriter' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\LazySchemaDiffProvider' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\OrmSchemaProvider' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaDiffProvider' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaDiffProviderInterface' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaProviderInterface' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\StubSchemaProvider' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php', + 'Doctrine\\DBAL\\Migrations\\SkipMigrationException' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\SqlFileWriter' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\AbstractCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\DiffCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\ExecuteCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\GenerateCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\LatestCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\MigrateCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\StatusCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\VersionCommand' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\ConfigurationHelper' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\MigrationDirectoryHelper' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\MigrationStatusInfosHelper' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php', + 'Doctrine\\DBAL\\Migrations\\Version' => $vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DrizzlePlatform' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php', @@ -946,6 +989,138 @@ 'Patchwork\\Utf8\\WindowsStreamWrapper' => $vendorDir . '/patchwork/utf8/src/Patchwork/Utf8/WindowsStreamWrapper.php', 'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php', 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'ProxyManager\\Autoloader\\Autoloader' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php', + 'ProxyManager\\Autoloader\\AutoloaderInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php', + 'ProxyManager\\Configuration' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Configuration.php', + 'ProxyManager\\Exception\\DisabledMethodException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php', + 'ProxyManager\\Exception\\ExceptionInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php', + 'ProxyManager\\Exception\\FileNotWritableException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php', + 'ProxyManager\\Exception\\InvalidProxiedClassException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php', + 'ProxyManager\\Exception\\InvalidProxyDirectoryException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php', + 'ProxyManager\\Exception\\UnsupportedProxiedClassException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php', + 'ProxyManager\\Factory\\AbstractBaseFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php', + 'ProxyManager\\Factory\\AbstractLazyFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php', + 'ProxyManager\\Factory\\AccessInterceptorScopeLocalizerFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php', + 'ProxyManager\\Factory\\AccessInterceptorValueHolderFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php', + 'ProxyManager\\Factory\\LazyLoadingGhostFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php', + 'ProxyManager\\Factory\\LazyLoadingValueHolderFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php', + 'ProxyManager\\Factory\\NullObjectFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php', + 'ProxyManager\\Factory\\RemoteObjectFactory' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php', + 'ProxyManager\\Factory\\RemoteObject\\AdapterInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\BaseAdapter' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\JsonRpc' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\Soap' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\XmlRpc' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php', + 'ProxyManager\\FileLocator\\FileLocator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php', + 'ProxyManager\\FileLocator\\FileLocatorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php', + 'ProxyManager\\GeneratorStrategy\\BaseGeneratorStrategy' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\EvaluatingGeneratorStrategy' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\FileWriterGeneratorStrategy' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/FileWriterGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\GeneratorStrategyInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.php', + 'ProxyManager\\Generator\\ClassGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php', + 'ProxyManager\\Generator\\MagicMethodGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php', + 'ProxyManager\\Generator\\MethodGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php', + 'ProxyManager\\Generator\\ParameterGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php', + 'ProxyManager\\Generator\\Util\\ClassGeneratorUtils' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php', + 'ProxyManager\\Generator\\Util\\UniqueIdentifierGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php', + 'ProxyManager\\Inflector\\ClassNameInflector' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php', + 'ProxyManager\\Inflector\\ClassNameInflectorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php', + 'ProxyManager\\Inflector\\Util\\ParameterEncoder' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php', + 'ProxyManager\\Inflector\\Util\\ParameterHasher' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizerGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\Constructor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\InterceptedMethod' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicClone' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicGet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicIsset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicSet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicSleep' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicUnset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\Util\\InterceptorGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolderGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\Constructor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\InterceptedMethod' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicClone' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicGet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicIsset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicSet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicUnset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\Util\\InterceptorGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\MagicWakeup' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\SetMethodPrefixInterceptor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\SetMethodSuffixInterceptor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\PropertyGenerator\\MethodPrefixInterceptors' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\PropertyGenerator\\MethodSuffixInterceptors' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php', + 'ProxyManager\\ProxyGenerator\\Assertion\\CanProxyAssertion' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhostGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\CallInitializer' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\GetProxyInitializer' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\InitializeProxy' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\IsProxyInitialized' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\LazyLoadingMethodInterceptor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicClone' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicGet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicIsset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicSet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicSleep' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicUnset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\SetProxyInitializer' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\PropertyGenerator\\InitializationTracker' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\PropertyGenerator\\InitializerProperty' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolderGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\GetProxyInitializer' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\InitializeProxy' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\IsProxyInitialized' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\LazyLoadingMethodInterceptor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicClone' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicGet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicIsset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicSet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicSleep' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicUnset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\SetProxyInitializer' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\PropertyGenerator\\InitializerProperty' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\PropertyGenerator\\ValueHolderProperty' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoading\\MethodGenerator\\Constructor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\NullObjectGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php', + 'ProxyManager\\ProxyGenerator\\NullObject\\MethodGenerator\\Constructor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\NullObject\\MethodGenerator\\NullObjectMethodInterceptor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\PropertyGenerator\\PublicPropertiesDefaults' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php', + 'ProxyManager\\ProxyGenerator\\PropertyGenerator\\PublicPropertiesMap' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php', + 'ProxyManager\\ProxyGenerator\\ProxyGeneratorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php', + 'ProxyManager\\ProxyGenerator\\RemoteObjectGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\Constructor' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicGet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicIsset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicSet' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicUnset' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\RemoteObjectMethod' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\PropertyGenerator\\AdapterProperty' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php', + 'ProxyManager\\ProxyGenerator\\Util\\ProxiedMethodsFilter' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php', + 'ProxyManager\\ProxyGenerator\\Util\\PublicScopeSimulator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php', + 'ProxyManager\\ProxyGenerator\\ValueHolder\\MethodGenerator\\GetWrappedValueHolderValue' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php', + 'ProxyManager\\ProxyGenerator\\ValueHolder\\MethodGenerator\\MagicSleep' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php', + 'ProxyManager\\Proxy\\AccessInterceptorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php', + 'ProxyManager\\Proxy\\Exception\\RemoteObjectException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php', + 'ProxyManager\\Proxy\\FallbackValueHolderInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php', + 'ProxyManager\\Proxy\\GhostObjectInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php', + 'ProxyManager\\Proxy\\LazyLoadingInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php', + 'ProxyManager\\Proxy\\NullObjectInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php', + 'ProxyManager\\Proxy\\ProxyInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php', + 'ProxyManager\\Proxy\\RemoteObjectInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php', + 'ProxyManager\\Proxy\\SmartReferenceInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php', + 'ProxyManager\\Proxy\\ValueHolderInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php', + 'ProxyManager\\Proxy\\VirtualProxyInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php', + 'ProxyManager\\Signature\\ClassSignatureGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php', + 'ProxyManager\\Signature\\ClassSignatureGeneratorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php', + 'ProxyManager\\Signature\\Exception\\ExceptionInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php', + 'ProxyManager\\Signature\\Exception\\InvalidSignatureException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php', + 'ProxyManager\\Signature\\Exception\\MissingSignatureException' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php', + 'ProxyManager\\Signature\\SignatureChecker' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php', + 'ProxyManager\\Signature\\SignatureCheckerInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php', + 'ProxyManager\\Signature\\SignatureGenerator' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php', + 'ProxyManager\\Signature\\SignatureGeneratorInterface' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php', + 'ProxyManager\\Version' => $vendorDir . '/ocramius/proxy-manager/src/ProxyManager/Version.php', 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php', @@ -1489,10 +1664,133 @@ 'Symfony\\Component\\Routing\\RouteCompilerInterface' => $vendorDir . '/symfony/routing/RouteCompilerInterface.php', 'Symfony\\Component\\Routing\\Router' => $vendorDir . '/symfony/routing/Router.php', 'Symfony\\Component\\Routing\\RouterInterface' => $vendorDir . '/symfony/routing/RouterInterface.php', + 'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php', + 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', + 'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php', + 'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php', + 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Exception/ParseException.php', + 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Exception/RuntimeException.php', + 'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Inline.php', + 'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Parser.php', + 'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Unescaper.php', + 'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Yaml.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => $vendorDir . '/symfony/polyfill-php70/Php70.php', 'System' => $vendorDir . '/pear/pear-core-minimal/src/System.php', 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', + 'Zend\\Code\\Annotation\\AnnotationCollection' => $vendorDir . '/zendframework/zend-code/src/Annotation/AnnotationCollection.php', + 'Zend\\Code\\Annotation\\AnnotationInterface' => $vendorDir . '/zendframework/zend-code/src/Annotation/AnnotationInterface.php', + 'Zend\\Code\\Annotation\\AnnotationManager' => $vendorDir . '/zendframework/zend-code/src/Annotation/AnnotationManager.php', + 'Zend\\Code\\Annotation\\Parser\\DoctrineAnnotationParser' => $vendorDir . '/zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php', + 'Zend\\Code\\Annotation\\Parser\\GenericAnnotationParser' => $vendorDir . '/zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php', + 'Zend\\Code\\Annotation\\Parser\\ParserInterface' => $vendorDir . '/zendframework/zend-code/src/Annotation/Parser/ParserInterface.php', + 'Zend\\Code\\Exception\\BadMethodCallException' => $vendorDir . '/zendframework/zend-code/src/Exception/BadMethodCallException.php', + 'Zend\\Code\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zend-code/src/Exception/ExceptionInterface.php', + 'Zend\\Code\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zend-code/src/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zend-code/src/Exception/RuntimeException.php', + 'Zend\\Code\\Generator\\AbstractGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/AbstractGenerator.php', + 'Zend\\Code\\Generator\\AbstractMemberGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/AbstractMemberGenerator.php', + 'Zend\\Code\\Generator\\BodyGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/BodyGenerator.php', + 'Zend\\Code\\Generator\\ClassGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/ClassGenerator.php', + 'Zend\\Code\\Generator\\DocBlockGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlockGenerator.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag.php', + 'Zend\\Code\\Generator\\DocBlock\\TagManager' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/TagManager.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\AbstractTypeableTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\AuthorTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\GenericTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\LicenseTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\MethodTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ParamTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\PropertyTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ReturnTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\TagInterface' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ThrowsTag' => $vendorDir . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ThrowsTag.php', + 'Zend\\Code\\Generator\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php', + 'Zend\\Code\\Generator\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zend-code/src/Generator/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Generator\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zend-code/src/Generator/Exception/RuntimeException.php', + 'Zend\\Code\\Generator\\FileGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/FileGenerator.php', + 'Zend\\Code\\Generator\\FileGeneratorRegistry' => $vendorDir . '/zendframework/zend-code/src/Generator/FileGeneratorRegistry.php', + 'Zend\\Code\\Generator\\GeneratorInterface' => $vendorDir . '/zendframework/zend-code/src/Generator/GeneratorInterface.php', + 'Zend\\Code\\Generator\\InterfaceGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/InterfaceGenerator.php', + 'Zend\\Code\\Generator\\MethodGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/MethodGenerator.php', + 'Zend\\Code\\Generator\\ParameterGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/ParameterGenerator.php', + 'Zend\\Code\\Generator\\PropertyGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/PropertyGenerator.php', + 'Zend\\Code\\Generator\\PropertyValueGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/PropertyValueGenerator.php', + 'Zend\\Code\\Generator\\TraitGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/TraitGenerator.php', + 'Zend\\Code\\Generator\\TraitUsageGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/TraitUsageGenerator.php', + 'Zend\\Code\\Generator\\TraitUsageInterface' => $vendorDir . '/zendframework/zend-code/src/Generator/TraitUsageInterface.php', + 'Zend\\Code\\Generator\\ValueGenerator' => $vendorDir . '/zendframework/zend-code/src/Generator/ValueGenerator.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeClassFactory' => $vendorDir . '/zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeGenericInterface' => $vendorDir . '/zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeInterface' => $vendorDir . '/zendframework/zend-code/src/Generic/Prototype/PrototypeInterface.php', + 'Zend\\Code\\NameInformation' => $vendorDir . '/zendframework/zend-code/src/NameInformation.php', + 'Zend\\Code\\Reflection\\ClassReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/ClassReflection.php', + 'Zend\\Code\\Reflection\\DocBlockReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlockReflection.php', + 'Zend\\Code\\Reflection\\DocBlock\\TagManager' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/TagManager.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\AuthorTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\GenericTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\LicenseTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\MethodTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ParamTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\PhpDocTypedTagInterface' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\PropertyTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/PropertyTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ReturnTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\TagInterface' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ThrowsTag' => $vendorDir . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ThrowsTag.php', + 'Zend\\Code\\Reflection\\Exception\\BadMethodCallException' => $vendorDir . '/zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php', + 'Zend\\Code\\Reflection\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zend-code/src/Reflection/Exception/ExceptionInterface.php', + 'Zend\\Code\\Reflection\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zend-code/src/Reflection/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Reflection\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zend-code/src/Reflection/Exception/RuntimeException.php', + 'Zend\\Code\\Reflection\\FileReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/FileReflection.php', + 'Zend\\Code\\Reflection\\FunctionReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/FunctionReflection.php', + 'Zend\\Code\\Reflection\\MethodReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/MethodReflection.php', + 'Zend\\Code\\Reflection\\ParameterReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/ParameterReflection.php', + 'Zend\\Code\\Reflection\\PropertyReflection' => $vendorDir . '/zendframework/zend-code/src/Reflection/PropertyReflection.php', + 'Zend\\Code\\Reflection\\ReflectionInterface' => $vendorDir . '/zendframework/zend-code/src/Reflection/ReflectionInterface.php', + 'Zend\\Code\\Scanner\\AggregateDirectoryScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/AggregateDirectoryScanner.php', + 'Zend\\Code\\Scanner\\AnnotationScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/AnnotationScanner.php', + 'Zend\\Code\\Scanner\\CachingFileScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/CachingFileScanner.php', + 'Zend\\Code\\Scanner\\ClassScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/ClassScanner.php', + 'Zend\\Code\\Scanner\\ConstantScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/ConstantScanner.php', + 'Zend\\Code\\Scanner\\DerivedClassScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/DerivedClassScanner.php', + 'Zend\\Code\\Scanner\\DirectoryScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/DirectoryScanner.php', + 'Zend\\Code\\Scanner\\DocBlockScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/DocBlockScanner.php', + 'Zend\\Code\\Scanner\\FileScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/FileScanner.php', + 'Zend\\Code\\Scanner\\FunctionScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/FunctionScanner.php', + 'Zend\\Code\\Scanner\\MethodScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/MethodScanner.php', + 'Zend\\Code\\Scanner\\ParameterScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/ParameterScanner.php', + 'Zend\\Code\\Scanner\\PropertyScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/PropertyScanner.php', + 'Zend\\Code\\Scanner\\ScannerInterface' => $vendorDir . '/zendframework/zend-code/src/Scanner/ScannerInterface.php', + 'Zend\\Code\\Scanner\\TokenArrayScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/TokenArrayScanner.php', + 'Zend\\Code\\Scanner\\Util' => $vendorDir . '/zendframework/zend-code/src/Scanner/Util.php', + 'Zend\\Code\\Scanner\\ValueScanner' => $vendorDir . '/zendframework/zend-code/src/Scanner/ValueScanner.php', + 'Zend\\EventManager\\AbstractListenerAggregate' => $vendorDir . '/zendframework/zend-eventmanager/src/AbstractListenerAggregate.php', + 'Zend\\EventManager\\Event' => $vendorDir . '/zendframework/zend-eventmanager/src/Event.php', + 'Zend\\EventManager\\EventInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/EventInterface.php', + 'Zend\\EventManager\\EventManager' => $vendorDir . '/zendframework/zend-eventmanager/src/EventManager.php', + 'Zend\\EventManager\\EventManagerAwareInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/EventManagerAwareInterface.php', + 'Zend\\EventManager\\EventManagerAwareTrait' => $vendorDir . '/zendframework/zend-eventmanager/src/EventManagerAwareTrait.php', + 'Zend\\EventManager\\EventManagerInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/EventManagerInterface.php', + 'Zend\\EventManager\\EventsCapableInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/EventsCapableInterface.php', + 'Zend\\EventManager\\Exception\\DomainException' => $vendorDir . '/zendframework/zend-eventmanager/src/Exception/DomainException.php', + 'Zend\\EventManager\\Exception\\ExceptionInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/Exception/ExceptionInterface.php', + 'Zend\\EventManager\\Exception\\InvalidArgumentException' => $vendorDir . '/zendframework/zend-eventmanager/src/Exception/InvalidArgumentException.php', + 'Zend\\EventManager\\Exception\\InvalidCallbackException' => $vendorDir . '/zendframework/zend-eventmanager/src/Exception/InvalidCallbackException.php', + 'Zend\\EventManager\\Exception\\RuntimeException' => $vendorDir . '/zendframework/zend-eventmanager/src/Exception/RuntimeException.php', + 'Zend\\EventManager\\FilterChain' => $vendorDir . '/zendframework/zend-eventmanager/src/FilterChain.php', + 'Zend\\EventManager\\Filter\\FilterInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/Filter/FilterInterface.php', + 'Zend\\EventManager\\Filter\\FilterIterator' => $vendorDir . '/zendframework/zend-eventmanager/src/Filter/FilterIterator.php', + 'Zend\\EventManager\\LazyEventListener' => $vendorDir . '/zendframework/zend-eventmanager/src/LazyEventListener.php', + 'Zend\\EventManager\\LazyListener' => $vendorDir . '/zendframework/zend-eventmanager/src/LazyListener.php', + 'Zend\\EventManager\\LazyListenerAggregate' => $vendorDir . '/zendframework/zend-eventmanager/src/LazyListenerAggregate.php', + 'Zend\\EventManager\\ListenerAggregateInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/ListenerAggregateInterface.php', + 'Zend\\EventManager\\ListenerAggregateTrait' => $vendorDir . '/zendframework/zend-eventmanager/src/ListenerAggregateTrait.php', + 'Zend\\EventManager\\ResponseCollection' => $vendorDir . '/zendframework/zend-eventmanager/src/ResponseCollection.php', + 'Zend\\EventManager\\SharedEventManager' => $vendorDir . '/zendframework/zend-eventmanager/src/SharedEventManager.php', + 'Zend\\EventManager\\SharedEventManagerInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/SharedEventManagerInterface.php', + 'Zend\\EventManager\\SharedEventsCapableInterface' => $vendorDir . '/zendframework/zend-eventmanager/src/SharedEventsCapableInterface.php', + 'Zend\\EventManager\\Test\\EventListenerIntrospectionTrait' => $vendorDir . '/zendframework/zend-eventmanager/src/Test/EventListenerIntrospectionTrait.php', 'ZipStreamer\\COMPR' => $vendorDir . '/mcnetic/zipstreamer/src/ZipStreamer.php', 'ZipStreamer\\Count64' => $vendorDir . '/mcnetic/zipstreamer/src/lib/Count64.php', 'ZipStreamer\\Count64Base' => $vendorDir . '/mcnetic/zipstreamer/src/lib/Count64.php', diff --git a/composer/autoload_files.php b/composer/autoload_files.php index fcf30f71f..c3a78ba0f 100644 --- a/composer/autoload_files.php +++ b/composer/autoload_files.php @@ -6,8 +6,8 @@ $baseDir = $vendorDir; return array( - '383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php', '3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php', '93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php', '2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php', diff --git a/composer/autoload_namespaces.php b/composer/autoload_namespaces.php index 7f7685a8b..49c797e07 100644 --- a/composer/autoload_namespaces.php +++ b/composer/autoload_namespaces.php @@ -6,6 +6,7 @@ $baseDir = $vendorDir; return array( + 'ProxyManager\\' => array($vendorDir . '/ocramius/proxy-manager/src'), 'Pimple' => array($vendorDir . '/pimple/pimple/src'), 'PEAR' => array($vendorDir . '/pear/pear_exception'), 'OpenCloud' => array($vendorDir . '/rackspace/php-opencloud/lib', $vendorDir . '/rackspace/php-opencloud/tests'), diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php index 76218762c..205ad1a1b 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -10,8 +10,11 @@ 'ownCloud\\TarStreamer\\' => array($vendorDir . '/deepdiver1975/tarstreamer/src'), 'bantu\\IniGetWrapper\\' => array($vendorDir . '/bantu/ini-get-wrapper/src'), 'ZipStreamer\\' => array($vendorDir . '/mcnetic/zipstreamer/src'), + 'Zend\\EventManager\\' => array($vendorDir . '/zendframework/zend-eventmanager/src'), + 'Zend\\Code\\' => array($vendorDir . '/zendframework/zend-code/src'), 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), @@ -39,6 +42,7 @@ 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Doctrine\\DBAL\\Migrations\\' => array($vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations'), 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'), ); diff --git a/composer/autoload_static.php b/composer/autoload_static.php index c1a9939f0..4477aa88a 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -7,8 +7,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 { public static $files = array ( - '383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php', '3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php', '93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php', '2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php', @@ -39,11 +39,14 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Z' => array ( 'ZipStreamer\\' => 12, + 'Zend\\EventManager\\' => 18, + 'Zend\\Code\\' => 10, ), 'S' => array ( 'Symfony\\Polyfill\\Php70\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\Routing\\' => 26, 'Symfony\\Component\\Process\\' => 26, 'Symfony\\Component\\EventDispatcher\\' => 34, @@ -89,6 +92,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 ), 'D' => array ( + 'Doctrine\\DBAL\\Migrations\\' => 25, 'Doctrine\\Common\\Cache\\' => 22, 'Doctrine\\Common\\' => 16, ), @@ -111,6 +115,14 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/mcnetic/zipstreamer/src', ), + 'Zend\\EventManager\\' => + array ( + 0 => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src', + ), + 'Zend\\Code\\' => + array ( + 0 => __DIR__ . '/..' . '/zendframework/zend-code/src', + ), 'Symfony\\Polyfill\\Php70\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', @@ -119,6 +131,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), + 'Symfony\\Component\\Yaml\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/yaml', + ), 'Symfony\\Component\\Routing\\' => array ( 0 => __DIR__ . '/..' . '/symfony/routing', @@ -228,6 +244,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), + 'Doctrine\\DBAL\\Migrations\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations', + ), 'Doctrine\\Common\\Cache\\' => array ( 0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache', @@ -241,6 +261,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 public static $prefixesPsr0 = array ( 'P' => array ( + 'ProxyManager\\' => + array ( + 0 => __DIR__ . '/..' . '/ocramius/proxy-manager/src', + ), 'Pimple' => array ( 0 => __DIR__ . '/..' . '/pimple/pimple/src', @@ -599,6 +623,49 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Logging\\EchoSQLLogger' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php', 'Doctrine\\DBAL\\Logging\\LoggerChain' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php', 'Doctrine\\DBAL\\Logging\\SQLLogger' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php', + 'Doctrine\\DBAL\\Migrations\\AbortMigrationException' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\AbstractMigration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\AbstractFileConfiguration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\ArrayConfiguration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Configuration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\ConnectionLoaderInterface' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ArrayConnectionConfigurationLoader' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionConfigurationChainLoader' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionConfigurationLoader' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\Connection\\Loader\\ConnectionHelperLoader' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\JsonConfiguration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\XmlConfiguration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Configuration\\YamlConfiguration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\AbstractFinder' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\GlobFinder' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\MigrationDeepFinderInterface' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\MigrationFinderInterface' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Finder\\RecursiveRegexFinder' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php', + 'Doctrine\\DBAL\\Migrations\\IrreversibleMigrationException' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\Migration' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php', + 'Doctrine\\DBAL\\Migrations\\MigrationException' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php', + 'Doctrine\\DBAL\\Migrations\\MigrationsVersion' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php', + 'Doctrine\\DBAL\\Migrations\\OutputWriter' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\LazySchemaDiffProvider' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\OrmSchemaProvider' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaDiffProvider' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaDiffProviderInterface' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\SchemaProviderInterface' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php', + 'Doctrine\\DBAL\\Migrations\\Provider\\StubSchemaProvider' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php', + 'Doctrine\\DBAL\\Migrations\\SkipMigrationException' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php', + 'Doctrine\\DBAL\\Migrations\\SqlFileWriter' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\AbstractCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\DiffCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\ExecuteCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\GenerateCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\LatestCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\MigrateCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\StatusCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Command\\VersionCommand' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\ConfigurationHelper' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\MigrationDirectoryHelper' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php', + 'Doctrine\\DBAL\\Migrations\\Tools\\Console\\Helper\\MigrationStatusInfosHelper' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php', + 'Doctrine\\DBAL\\Migrations\\Version' => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DrizzlePlatform' => __DIR__ . '/..' . '/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php', @@ -1261,6 +1328,138 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Patchwork\\Utf8\\WindowsStreamWrapper' => __DIR__ . '/..' . '/patchwork/utf8/src/Patchwork/Utf8/WindowsStreamWrapper.php', 'Pimple\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Container.php', 'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php', + 'ProxyManager\\Autoloader\\Autoloader' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php', + 'ProxyManager\\Autoloader\\AutoloaderInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php', + 'ProxyManager\\Configuration' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Configuration.php', + 'ProxyManager\\Exception\\DisabledMethodException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php', + 'ProxyManager\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php', + 'ProxyManager\\Exception\\FileNotWritableException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php', + 'ProxyManager\\Exception\\InvalidProxiedClassException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php', + 'ProxyManager\\Exception\\InvalidProxyDirectoryException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php', + 'ProxyManager\\Exception\\UnsupportedProxiedClassException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php', + 'ProxyManager\\Factory\\AbstractBaseFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php', + 'ProxyManager\\Factory\\AbstractLazyFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php', + 'ProxyManager\\Factory\\AccessInterceptorScopeLocalizerFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php', + 'ProxyManager\\Factory\\AccessInterceptorValueHolderFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php', + 'ProxyManager\\Factory\\LazyLoadingGhostFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php', + 'ProxyManager\\Factory\\LazyLoadingValueHolderFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php', + 'ProxyManager\\Factory\\NullObjectFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php', + 'ProxyManager\\Factory\\RemoteObjectFactory' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php', + 'ProxyManager\\Factory\\RemoteObject\\AdapterInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\BaseAdapter' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\JsonRpc' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\Soap' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php', + 'ProxyManager\\Factory\\RemoteObject\\Adapter\\XmlRpc' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php', + 'ProxyManager\\FileLocator\\FileLocator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php', + 'ProxyManager\\FileLocator\\FileLocatorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php', + 'ProxyManager\\GeneratorStrategy\\BaseGeneratorStrategy' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\EvaluatingGeneratorStrategy' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\FileWriterGeneratorStrategy' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/FileWriterGeneratorStrategy.php', + 'ProxyManager\\GeneratorStrategy\\GeneratorStrategyInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/GeneratorStrategyInterface.php', + 'ProxyManager\\Generator\\ClassGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php', + 'ProxyManager\\Generator\\MagicMethodGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php', + 'ProxyManager\\Generator\\MethodGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php', + 'ProxyManager\\Generator\\ParameterGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php', + 'ProxyManager\\Generator\\Util\\ClassGeneratorUtils' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php', + 'ProxyManager\\Generator\\Util\\UniqueIdentifierGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php', + 'ProxyManager\\Inflector\\ClassNameInflector' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php', + 'ProxyManager\\Inflector\\ClassNameInflectorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php', + 'ProxyManager\\Inflector\\Util\\ParameterEncoder' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php', + 'ProxyManager\\Inflector\\Util\\ParameterHasher' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizerGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\Constructor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\InterceptedMethod' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicClone' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicGet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicIsset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicSet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicSleep' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\MagicUnset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorScopeLocalizer\\MethodGenerator\\Util\\InterceptorGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolderGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\Constructor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\InterceptedMethod' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicClone' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicGet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicIsset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicSet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\MagicUnset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptorValueHolder\\MethodGenerator\\Util\\InterceptorGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\MagicWakeup' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\SetMethodPrefixInterceptor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\MethodGenerator\\SetMethodSuffixInterceptor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\PropertyGenerator\\MethodPrefixInterceptors' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php', + 'ProxyManager\\ProxyGenerator\\AccessInterceptor\\PropertyGenerator\\MethodSuffixInterceptors' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php', + 'ProxyManager\\ProxyGenerator\\Assertion\\CanProxyAssertion' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhostGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\CallInitializer' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\GetProxyInitializer' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\InitializeProxy' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\IsProxyInitialized' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\LazyLoadingMethodInterceptor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicClone' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicGet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicIsset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicSet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicSleep' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\MagicUnset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\MethodGenerator\\SetProxyInitializer' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\PropertyGenerator\\InitializationTracker' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingGhost\\PropertyGenerator\\InitializerProperty' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolderGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\GetProxyInitializer' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\InitializeProxy' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\IsProxyInitialized' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\LazyLoadingMethodInterceptor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicClone' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicGet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicIsset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicSet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicSleep' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\MagicUnset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\MethodGenerator\\SetProxyInitializer' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\PropertyGenerator\\InitializerProperty' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoadingValueHolder\\PropertyGenerator\\ValueHolderProperty' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php', + 'ProxyManager\\ProxyGenerator\\LazyLoading\\MethodGenerator\\Constructor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\NullObjectGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php', + 'ProxyManager\\ProxyGenerator\\NullObject\\MethodGenerator\\Constructor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\NullObject\\MethodGenerator\\NullObjectMethodInterceptor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php', + 'ProxyManager\\ProxyGenerator\\PropertyGenerator\\PublicPropertiesDefaults' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php', + 'ProxyManager\\ProxyGenerator\\PropertyGenerator\\PublicPropertiesMap' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php', + 'ProxyManager\\ProxyGenerator\\ProxyGeneratorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php', + 'ProxyManager\\ProxyGenerator\\RemoteObjectGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\Constructor' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicGet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicIsset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicSet' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\MagicUnset' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\MethodGenerator\\RemoteObjectMethod' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php', + 'ProxyManager\\ProxyGenerator\\RemoteObject\\PropertyGenerator\\AdapterProperty' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php', + 'ProxyManager\\ProxyGenerator\\Util\\ProxiedMethodsFilter' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php', + 'ProxyManager\\ProxyGenerator\\Util\\PublicScopeSimulator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php', + 'ProxyManager\\ProxyGenerator\\ValueHolder\\MethodGenerator\\GetWrappedValueHolderValue' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php', + 'ProxyManager\\ProxyGenerator\\ValueHolder\\MethodGenerator\\MagicSleep' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php', + 'ProxyManager\\Proxy\\AccessInterceptorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php', + 'ProxyManager\\Proxy\\Exception\\RemoteObjectException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php', + 'ProxyManager\\Proxy\\FallbackValueHolderInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php', + 'ProxyManager\\Proxy\\GhostObjectInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php', + 'ProxyManager\\Proxy\\LazyLoadingInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php', + 'ProxyManager\\Proxy\\NullObjectInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php', + 'ProxyManager\\Proxy\\ProxyInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php', + 'ProxyManager\\Proxy\\RemoteObjectInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php', + 'ProxyManager\\Proxy\\SmartReferenceInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php', + 'ProxyManager\\Proxy\\ValueHolderInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php', + 'ProxyManager\\Proxy\\VirtualProxyInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php', + 'ProxyManager\\Signature\\ClassSignatureGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php', + 'ProxyManager\\Signature\\ClassSignatureGeneratorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php', + 'ProxyManager\\Signature\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php', + 'ProxyManager\\Signature\\Exception\\InvalidSignatureException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php', + 'ProxyManager\\Signature\\Exception\\MissingSignatureException' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php', + 'ProxyManager\\Signature\\SignatureChecker' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php', + 'ProxyManager\\Signature\\SignatureCheckerInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php', + 'ProxyManager\\Signature\\SignatureGenerator' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php', + 'ProxyManager\\Signature\\SignatureGeneratorInterface' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php', + 'ProxyManager\\Version' => __DIR__ . '/..' . '/ocramius/proxy-manager/src/ProxyManager/Version.php', 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php', @@ -1804,10 +2003,133 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Symfony\\Component\\Routing\\RouteCompilerInterface' => __DIR__ . '/..' . '/symfony/routing/RouteCompilerInterface.php', 'Symfony\\Component\\Routing\\Router' => __DIR__ . '/..' . '/symfony/routing/Router.php', 'Symfony\\Component\\Routing\\RouterInterface' => __DIR__ . '/..' . '/symfony/routing/RouterInterface.php', + 'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php', + 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', + 'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php', + 'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php', + 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/yaml/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Yaml\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/yaml/Exception/ParseException.php', + 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/yaml/Exception/RuntimeException.php', + 'Symfony\\Component\\Yaml\\Inline' => __DIR__ . '/..' . '/symfony/yaml/Inline.php', + 'Symfony\\Component\\Yaml\\Parser' => __DIR__ . '/..' . '/symfony/yaml/Parser.php', + 'Symfony\\Component\\Yaml\\Unescaper' => __DIR__ . '/..' . '/symfony/yaml/Unescaper.php', + 'Symfony\\Component\\Yaml\\Yaml' => __DIR__ . '/..' . '/symfony/yaml/Yaml.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => __DIR__ . '/..' . '/symfony/polyfill-php70/Php70.php', 'System' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/System.php', 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', + 'Zend\\Code\\Annotation\\AnnotationCollection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/AnnotationCollection.php', + 'Zend\\Code\\Annotation\\AnnotationInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/AnnotationInterface.php', + 'Zend\\Code\\Annotation\\AnnotationManager' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/AnnotationManager.php', + 'Zend\\Code\\Annotation\\Parser\\DoctrineAnnotationParser' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php', + 'Zend\\Code\\Annotation\\Parser\\GenericAnnotationParser' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php', + 'Zend\\Code\\Annotation\\Parser\\ParserInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Annotation/Parser/ParserInterface.php', + 'Zend\\Code\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Exception/BadMethodCallException.php', + 'Zend\\Code\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Exception/ExceptionInterface.php', + 'Zend\\Code\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Exception/RuntimeException.php', + 'Zend\\Code\\Generator\\AbstractGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/AbstractGenerator.php', + 'Zend\\Code\\Generator\\AbstractMemberGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/AbstractMemberGenerator.php', + 'Zend\\Code\\Generator\\BodyGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/BodyGenerator.php', + 'Zend\\Code\\Generator\\ClassGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/ClassGenerator.php', + 'Zend\\Code\\Generator\\DocBlockGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlockGenerator.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag.php', + 'Zend\\Code\\Generator\\DocBlock\\TagManager' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/TagManager.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\AbstractTypeableTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\AuthorTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\GenericTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\LicenseTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\MethodTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ParamTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\PropertyTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ReturnTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\TagInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php', + 'Zend\\Code\\Generator\\DocBlock\\Tag\\ThrowsTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/DocBlock/Tag/ThrowsTag.php', + 'Zend\\Code\\Generator\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php', + 'Zend\\Code\\Generator\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Generator\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/Exception/RuntimeException.php', + 'Zend\\Code\\Generator\\FileGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/FileGenerator.php', + 'Zend\\Code\\Generator\\FileGeneratorRegistry' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/FileGeneratorRegistry.php', + 'Zend\\Code\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/GeneratorInterface.php', + 'Zend\\Code\\Generator\\InterfaceGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/InterfaceGenerator.php', + 'Zend\\Code\\Generator\\MethodGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/MethodGenerator.php', + 'Zend\\Code\\Generator\\ParameterGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/ParameterGenerator.php', + 'Zend\\Code\\Generator\\PropertyGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/PropertyGenerator.php', + 'Zend\\Code\\Generator\\PropertyValueGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/PropertyValueGenerator.php', + 'Zend\\Code\\Generator\\TraitGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/TraitGenerator.php', + 'Zend\\Code\\Generator\\TraitUsageGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/TraitUsageGenerator.php', + 'Zend\\Code\\Generator\\TraitUsageInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/TraitUsageInterface.php', + 'Zend\\Code\\Generator\\ValueGenerator' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generator/ValueGenerator.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeClassFactory' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeGenericInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php', + 'Zend\\Code\\Generic\\Prototype\\PrototypeInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Generic/Prototype/PrototypeInterface.php', + 'Zend\\Code\\NameInformation' => __DIR__ . '/..' . '/zendframework/zend-code/src/NameInformation.php', + 'Zend\\Code\\Reflection\\ClassReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/ClassReflection.php', + 'Zend\\Code\\Reflection\\DocBlockReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlockReflection.php', + 'Zend\\Code\\Reflection\\DocBlock\\TagManager' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/TagManager.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\AuthorTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\GenericTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\LicenseTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\MethodTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ParamTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\PhpDocTypedTagInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\PropertyTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/PropertyTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ReturnTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\TagInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php', + 'Zend\\Code\\Reflection\\DocBlock\\Tag\\ThrowsTag' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/DocBlock/Tag/ThrowsTag.php', + 'Zend\\Code\\Reflection\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php', + 'Zend\\Code\\Reflection\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/Exception/ExceptionInterface.php', + 'Zend\\Code\\Reflection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/Exception/InvalidArgumentException.php', + 'Zend\\Code\\Reflection\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/Exception/RuntimeException.php', + 'Zend\\Code\\Reflection\\FileReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/FileReflection.php', + 'Zend\\Code\\Reflection\\FunctionReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/FunctionReflection.php', + 'Zend\\Code\\Reflection\\MethodReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/MethodReflection.php', + 'Zend\\Code\\Reflection\\ParameterReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/ParameterReflection.php', + 'Zend\\Code\\Reflection\\PropertyReflection' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/PropertyReflection.php', + 'Zend\\Code\\Reflection\\ReflectionInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Reflection/ReflectionInterface.php', + 'Zend\\Code\\Scanner\\AggregateDirectoryScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/AggregateDirectoryScanner.php', + 'Zend\\Code\\Scanner\\AnnotationScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/AnnotationScanner.php', + 'Zend\\Code\\Scanner\\CachingFileScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/CachingFileScanner.php', + 'Zend\\Code\\Scanner\\ClassScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/ClassScanner.php', + 'Zend\\Code\\Scanner\\ConstantScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/ConstantScanner.php', + 'Zend\\Code\\Scanner\\DerivedClassScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/DerivedClassScanner.php', + 'Zend\\Code\\Scanner\\DirectoryScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/DirectoryScanner.php', + 'Zend\\Code\\Scanner\\DocBlockScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/DocBlockScanner.php', + 'Zend\\Code\\Scanner\\FileScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/FileScanner.php', + 'Zend\\Code\\Scanner\\FunctionScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/FunctionScanner.php', + 'Zend\\Code\\Scanner\\MethodScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/MethodScanner.php', + 'Zend\\Code\\Scanner\\ParameterScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/ParameterScanner.php', + 'Zend\\Code\\Scanner\\PropertyScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/PropertyScanner.php', + 'Zend\\Code\\Scanner\\ScannerInterface' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/ScannerInterface.php', + 'Zend\\Code\\Scanner\\TokenArrayScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/TokenArrayScanner.php', + 'Zend\\Code\\Scanner\\Util' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/Util.php', + 'Zend\\Code\\Scanner\\ValueScanner' => __DIR__ . '/..' . '/zendframework/zend-code/src/Scanner/ValueScanner.php', + 'Zend\\EventManager\\AbstractListenerAggregate' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/AbstractListenerAggregate.php', + 'Zend\\EventManager\\Event' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Event.php', + 'Zend\\EventManager\\EventInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventInterface.php', + 'Zend\\EventManager\\EventManager' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventManager.php', + 'Zend\\EventManager\\EventManagerAwareInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventManagerAwareInterface.php', + 'Zend\\EventManager\\EventManagerAwareTrait' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventManagerAwareTrait.php', + 'Zend\\EventManager\\EventManagerInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventManagerInterface.php', + 'Zend\\EventManager\\EventsCapableInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/EventsCapableInterface.php', + 'Zend\\EventManager\\Exception\\DomainException' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Exception/DomainException.php', + 'Zend\\EventManager\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Exception/ExceptionInterface.php', + 'Zend\\EventManager\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Exception/InvalidArgumentException.php', + 'Zend\\EventManager\\Exception\\InvalidCallbackException' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Exception/InvalidCallbackException.php', + 'Zend\\EventManager\\Exception\\RuntimeException' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Exception/RuntimeException.php', + 'Zend\\EventManager\\FilterChain' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/FilterChain.php', + 'Zend\\EventManager\\Filter\\FilterInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Filter/FilterInterface.php', + 'Zend\\EventManager\\Filter\\FilterIterator' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Filter/FilterIterator.php', + 'Zend\\EventManager\\LazyEventListener' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/LazyEventListener.php', + 'Zend\\EventManager\\LazyListener' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/LazyListener.php', + 'Zend\\EventManager\\LazyListenerAggregate' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/LazyListenerAggregate.php', + 'Zend\\EventManager\\ListenerAggregateInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/ListenerAggregateInterface.php', + 'Zend\\EventManager\\ListenerAggregateTrait' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/ListenerAggregateTrait.php', + 'Zend\\EventManager\\ResponseCollection' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/ResponseCollection.php', + 'Zend\\EventManager\\SharedEventManager' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/SharedEventManager.php', + 'Zend\\EventManager\\SharedEventManagerInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/SharedEventManagerInterface.php', + 'Zend\\EventManager\\SharedEventsCapableInterface' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/SharedEventsCapableInterface.php', + 'Zend\\EventManager\\Test\\EventListenerIntrospectionTrait' => __DIR__ . '/..' . '/zendframework/zend-eventmanager/src/Test/EventListenerIntrospectionTrait.php', 'ZipStreamer\\COMPR' => __DIR__ . '/..' . '/mcnetic/zipstreamer/src/ZipStreamer.php', 'ZipStreamer\\Count64' => __DIR__ . '/..' . '/mcnetic/zipstreamer/src/lib/Count64.php', 'ZipStreamer\\Count64Base' => __DIR__ . '/..' . '/mcnetic/zipstreamer/src/lib/Count64.php', diff --git a/composer/installed.json b/composer/installed.json index 1c6c57483..9976435d1 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -2987,5 +2987,312 @@ "uri", "url" ] + }, + { + "name": "symfony/yaml", + "version": "v3.2.1", + "version_normalized": "3.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a7095af4b97a0955f85c8989106c249fa649011f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7095af4b97a0955f85c8989106c249fa649011f", + "reference": "a7095af4b97a0955f85c8989106c249fa649011f", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "time": "2016-12-10 10:07:06", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com" + }, + { + "name": "zendframework/zend-eventmanager", + "version": "3.1.0", + "version_normalized": "3.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-eventmanager.git", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "phpunit/phpunit": "^5.6", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + }, + "suggest": { + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + }, + "time": "2016-12-19 21:47:12", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Zend\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://github.com/zendframework/zend-eventmanager", + "keywords": [ + "event", + "eventmanager", + "events", + "zf2" + ] + }, + { + "name": "zendframework/zend-code", + "version": "2.6.3", + "version_normalized": "2.6.3.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-code.git", + "reference": "95033f061b083e16cdee60530ec260d7d628b887" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/95033f061b083e16cdee60530ec260d7d628b887", + "reference": "95033f061b083e16cdee60530ec260d7d628b887", + "shasum": "" + }, + "require": { + "php": "^5.5 || 7.0.0 - 7.0.4 || ^7.0.6", + "zendframework/zend-eventmanager": "^2.6 || ^3.0" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "^4.8.21", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "zendframework/zend-stdlib": "Zend\\Stdlib component" + }, + "time": "2016-04-20 17:26:42", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Zend\\Code\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides facilities to generate arbitrary code using an object oriented interface", + "homepage": "https://github.com/zendframework/zend-code", + "keywords": [ + "code", + "zf2" + ] + }, + { + "name": "ocramius/proxy-manager", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/ProxyManager.git", + "reference": "57e9272ec0e8deccf09421596e0e2252df440e11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/57e9272ec0e8deccf09421596e0e2252df440e11", + "reference": "57e9272ec0e8deccf09421596e0e2252df440e11", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "zendframework/zend-code": ">2.2.5,<3.0" + }, + "require-dev": { + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "1.5.*" + }, + "suggest": { + "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", + "zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)", + "zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)", + "zendframework/zend-stdlib": "To use the hydrator proxy", + "zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)" + }, + "time": "2015-08-09 04:28:19", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "ProxyManager\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies", + "homepage": "https://github.com/Ocramius/ProxyManager", + "keywords": [ + "aop", + "lazy loading", + "proxy", + "proxy pattern", + "service proxies" + ] + }, + { + "name": "doctrine/migrations", + "version": "1.4.1", + "version_normalized": "1.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/0d0ff5da10c5d30846da32060bd9e357abf70a05", + "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.2", + "ocramius/proxy-manager": "^1.0|^2.0", + "php": "^5.5|^7.0", + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/coding-standard": "dev-master", + "doctrine/orm": "2.*", + "jdorn/sql-formatter": "~1.1", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "~4.7", + "satooshi/php-coveralls": "0.6.*" + }, + "suggest": { + "jdorn/sql-formatter": "Allows to generate formatted SQL with the diff command." + }, + "time": "2016-03-14 12:29:11", + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "v1.5.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\Migrations\\": "lib/Doctrine/DBAL/Migrations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "Database Schema migrations using Doctrine DBAL", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "migrations" + ] } ] diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php new file mode 100644 index 000000000..822e10268 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbortMigrationException.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +class AbortMigrationException extends MigrationException +{ +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php new file mode 100644 index 000000000..5c98b1bc7 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/AbstractMigration.php @@ -0,0 +1,189 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +use Doctrine\DBAL\Schema\Schema; + +/** + * Abstract class for individual migrations to extend from. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +abstract class AbstractMigration +{ + /** + * Reference to the Version instance representing this migration + * + * @var Version + */ + protected $version; + + /** + * The Doctrine\DBAL\Connection instance we are migrating + * + * @var \Doctrine\DBAL\Connection + */ + protected $connection; + + /** + * Reference to the SchemaManager instance referenced by $_connection + * + * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + protected $sm; + + /** + * Reference to the DatabasePlatform instance referenced by $_connection + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The OutputWriter object instance used for outputting information + * + * @var OutputWriter + */ + private $outputWriter; + + public function __construct(Version $version) + { + $config = $version->getConfiguration(); + + $this->version = $version; + $this->connection = $config->getConnection(); + $this->sm = $this->connection->getSchemaManager(); + $this->platform = $this->connection->getDatabasePlatform(); + $this->outputWriter = $config->getOutputWriter(); + } + + /** + * Indicates the transactional mode of this migration. + * If this function returns true (default) the migration will be executed in one transaction, + * otherwise non-transactional state will be used to execute each of the migration SQLs. + * + * Extending class should override this function to alter the return value + * + * @return bool TRUE by default. + */ + public function isTransactional() + { + return true; + } + + /** + * Get migration description + * + * @return string + */ + public function getDescription() + { + return ''; + } + + /** + * Print a warning message if the condition evaluates to TRUE. + * + * @param boolean $condition + * @param string $message + */ + public function warnIf($condition, $message = '') + { + if ($condition) { + $message = $message ?: 'Unknown Reason'; + $this->outputWriter->write(sprintf( + ' Warning during %s: %s', + $this->version->getExecutionState(), + $message + )); + } + } + + /** + * Abort the migration if the condition evaluates to TRUE. + * + * @param boolean $condition + * @param string $message + * + * @throws AbortMigrationException + */ + public function abortIf($condition, $message = '') + { + if ($condition) { + throw new AbortMigrationException($message ?: 'Unknown Reason'); + } + } + + /** + * Skip this migration (but not the next ones) if condition evaluates to TRUE. + * + * @param boolean $condition + * @param string $message + * + * @throws SkipMigrationException + */ + public function skipIf($condition, $message = '') + { + if ($condition) { + throw new SkipMigrationException($message ?: 'Unknown Reason'); + } + } + + public function preUp(Schema $schema) + { + } + + public function postUp(Schema $schema) + { + } + + public function preDown(Schema $schema) + { + } + + public function postDown(Schema $schema) + { + } + + abstract public function up(Schema $schema); + abstract public function down(Schema $schema); + + protected function addSql($sql, array $params = [], array $types = []) + { + $this->version->addSql($sql, $params, $types); + } + + protected function write($message) + { + $this->outputWriter->write($message); + } + + protected function throwIrreversibleMigrationException($message = null) + { + if (null === $message) { + $message = 'This migration is irreversible and cannot be reverted.'; + } + + throw new IrreversibleMigrationException($message); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php new file mode 100644 index 000000000..77415c8a9 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/AbstractFileConfiguration.php @@ -0,0 +1,149 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; + +use Doctrine\DBAL\Migrations\MigrationException; + +/** + * Abstract Migration Configuration class for loading configuration information + * from a configuration file (xml or yml). + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +abstract class AbstractFileConfiguration extends Configuration +{ + /** + * The configuration file used to load configuration information + * + * @var string + */ + private $file; + + /** + * Whether or not the configuration file has been loaded yet or not + * + * @var boolean + */ + private $loaded = false; + + /** + * @var array of possible configuration properties in migrations configuration. + */ + private $configurationProperties = [ + 'migrations_namespace' => 'setMigrationsNamespace', + 'table_name' => 'setMigrationsTableName', + 'column_name' => 'setMigrationsColumnName', + 'organize_migrations' => 'setMigrationOrganisation', + 'name' => 'setName', + 'migrations_directory' => 'loadMigrationsFromDirectory', + 'migrations' => 'loadMigrations', + ]; + + protected function setConfiguration(Array $config) + { + foreach($config as $configurationKey => $configurationValue) { + if (!isset($this->configurationProperties[$configurationKey])) { + $msg = sprintf('Migrations configuration key "%s" does not exists.', $configurationKey); + throw MigrationException::configurationNotValid($msg); + } + } + foreach($this->configurationProperties as $configurationKey => $configurationSetter) { + if (isset($config[$configurationKey])) { + $this->{$configurationSetter}($config[$configurationKey]); + } + } + } + + private function loadMigrationsFromDirectory($migrationsDirectory) + { + $this->setMigrationsDirectory($migrationsDirectory); + $this->registerMigrationsFromDirectory($migrationsDirectory); + } + + private function loadMigrations($migrations) + { + if (is_array($migrations)) { + foreach ($migrations as $migration) { + $this->registerMigration($migration['version'], $migration['class']); + } + } + } + + private function setMigrationOrganisation($migrationOrganisation) + { + if (strcasecmp($migrationOrganisation, static::VERSIONS_ORGANIZATION_BY_YEAR) == 0) { + $this->setMigrationsAreOrganizedByYear(); + } else if (strcasecmp($migrationOrganisation, static::VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH) == 0) { + $this->setMigrationsAreOrganizedByYearAndMonth(); + } else { + $msg = 'Unknown ' . var_export($migrationOrganisation, true) . ' for configuration "organize_migrations".'; + throw MigrationException::configurationNotValid($msg); + } + } + + /** + * Load the information from the passed configuration file + * + * @param string $file The path to the configuration file + * + * @throws MigrationException Throws exception if configuration file was already loaded + */ + public function load($file) + { + if ($this->loaded) { + throw MigrationException::configurationFileAlreadyLoaded(); + } + if (file_exists($path = getcwd() . '/' . $file)) { + $file = $path; + } + $this->file = $file; + + if (!file_exists($file)) { + throw new \InvalidArgumentException('Given config file does not exist'); + } + + $this->doLoad($file); + $this->loaded = true; + } + + protected function getDirectoryRelativeToFile($file, $input) + { + $path = realpath(dirname($file) . '/' . $input); + + return ($path !== false) ? $path : $input; + } + + public function getFile() + { + return $this->file; + } + + /** + * Abstract method that each file configuration driver must implement to + * load the given configuration file whether it be xml, yaml, etc. or something + * else. + * + * @param string $file The path to a configuration file. + */ + abstract protected function doLoad($file); +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php new file mode 100644 index 000000000..e01f309b1 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/ArrayConfiguration.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; + +/** + * Load migration configuration information from a PHP configuration file. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author David Havl + */ +class ArrayConfiguration extends AbstractFileConfiguration +{ + /** + * @inheritdoc + */ + protected function doLoad($file) + { + $config = require $file; + + if (isset($config['migrations_directory'])) { + $config['migrations_directory'] = $this->getDirectoryRelativeToFile($file, $config['migrations_directory']); + } + + $this->setConfiguration($config); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php new file mode 100644 index 000000000..402a48e81 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Configuration.php @@ -0,0 +1,852 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Migrations\Finder\MigrationDeepFinderInterface; +use Doctrine\DBAL\Migrations\MigrationException; +use Doctrine\DBAL\Migrations\OutputWriter; +use Doctrine\DBAL\Migrations\Version; +use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface; +use Doctrine\DBAL\Migrations\Finder\RecursiveRegexFinder; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; + +/** + * Default Migration Configuration object used for configuring an instance of + * the Migration class. Set the connection, version table name, register migration + * classes/versions, etc. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class Configuration +{ + /** + * Configure versions to be organized by year. + */ + const VERSIONS_ORGANIZATION_BY_YEAR = 'year'; + + /** + * Configure versions to be organized by year and month. + * + * @var string + */ + const VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH = 'year_and_month'; + + /** + * Name of this set of migrations + * + * @var string + */ + private $name; + + /** + * Flag for whether or not the migration table has been created + * + * @var boolean + */ + private $migrationTableCreated = false; + + /** + * Connection instance to use for migrations + * + * @var Connection + */ + private $connection; + + /** + * OutputWriter instance for writing output during migrations + * + * @var OutputWriter + */ + private $outputWriter; + + /** + * The migration finder implementation -- used to load migrations from a + * directory. + * + * @var MigrationFinderInterface + */ + private $migrationFinder; + + /** + * The migration table name to track versions in + * + * @var string + */ + private $migrationsTableName = 'doctrine_migration_versions'; + + /** + * The migration column name to track versions in + * + * @var string + */ + private $migrationsColumnName = 'version'; + + /** + * The path to a directory where new migration classes will be written + * + * @var string + */ + private $migrationsDirectory; + + /** + * Namespace the migration classes live in + * + * @var string + */ + private $migrationsNamespace; + + /** + * Array of the registered migrations + * + * @var Version[] + */ + private $migrations = []; + + /** + * Versions are organized by year. + * + * @var boolean + */ + private $migrationsAreOrganizedByYear = false; + + /** + * Versions are organized by year and month. + * + * @var boolean + */ + private $migrationsAreOrganizedByYearAndMonth = false; + + /** + * Construct a migration configuration object. + * + * @param Connection $connection A Connection instance + * @param OutputWriter $outputWriter A OutputWriter instance + * @param MigrationFinderInterface $finder Migration files finder + */ + public function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null) + { + $this->connection = $connection; + if ($outputWriter === null) { + $outputWriter = new OutputWriter(); + } + $this->outputWriter = $outputWriter; + if ($finder === null) { + $finder = new RecursiveRegexFinder(); + } + $this->migrationFinder = $finder; + } + + /** + * @return bool + */ + public function areMigrationsOrganizedByYear() + { + return $this->migrationsAreOrganizedByYear; + } + + /** + * @return bool + */ + public function areMigrationsOrganizedByYearAndMonth() + { + return $this->migrationsAreOrganizedByYearAndMonth; + } + + /** + * Validation that this instance has all the required properties configured + * + * @throws MigrationException + */ + public function validate() + { + if (!$this->migrationsNamespace) { + throw MigrationException::migrationsNamespaceRequired(); + } + if (!$this->migrationsDirectory) { + throw MigrationException::migrationsDirectoryRequired(); + } + } + + /** + * Set the name of this set of migrations + * + * @param string $name The name of this set of migrations + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Returns the name of this set of migrations + * + * @return string $name The name of this set of migrations + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the output writer. + * + * @param OutputWriter $outputWriter + */ + public function setOutputWriter(OutputWriter $outputWriter) + { + $this->outputWriter = $outputWriter; + } + + /** + * Returns the OutputWriter instance + * + * @return OutputWriter $outputWriter The OutputWriter instance + */ + public function getOutputWriter() + { + return $this->outputWriter; + } + + /** + * Returns a timestamp version as a formatted date + * + * @param string $version + * + * @return string The formatted version + * @deprecated + */ + public function formatVersion($version) + { + return $this->getDateTime($version); + } + + /** + * Returns the datetime of a migration + * + * @param $version + * @return string + */ + public function getDateTime($version) + { + $datetime = str_replace('Version', '', $version); + $datetime = \DateTime::createFromFormat('YmdHis', $datetime); + + if ($datetime === false) { + return ''; + } + + return $datetime->format('Y-m-d H:i:s'); + } + + /** + * Returns the Connection instance + * + * @return Connection $connection The Connection instance + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Set the migration table name + * + * @param string $tableName The migration table name + */ + public function setMigrationsTableName($tableName) + { + $this->migrationsTableName = $tableName; + } + + /** + * Returns the migration table name + * + * @return string $migrationsTableName The migration table name + */ + public function getMigrationsTableName() + { + return $this->migrationsTableName; + } + + /** + * Set the migration column name + * + * @param string $columnName The migration column name + */ + public function setMigrationsColumnName($columnName) + { + $this->migrationsColumnName = $columnName; + } + + /** + * Returns the migration column name + * + * @return string $migrationsColumnName The migration column name + */ + public function getMigrationsColumnName() + { + return $this->migrationsColumnName; + } + + /** + * Set the new migrations directory where new migration classes are generated + * + * @param string $migrationsDirectory The new migrations directory + */ + public function setMigrationsDirectory($migrationsDirectory) + { + $this->migrationsDirectory = $migrationsDirectory; + } + + /** + * Returns the new migrations directory where new migration classes are generated + * + * @return string $migrationsDirectory The new migrations directory + */ + public function getMigrationsDirectory() + { + return $this->migrationsDirectory; + } + + /** + * Set the migrations namespace + * + * @param string $migrationsNamespace The migrations namespace + */ + public function setMigrationsNamespace($migrationsNamespace) + { + $this->migrationsNamespace = $migrationsNamespace; + } + + /** + * Returns the migrations namespace + * + * @return string $migrationsNamespace The migrations namespace + */ + public function getMigrationsNamespace() + { + return $this->migrationsNamespace; + } + + /** + * Set the implementation of the migration finder. + * + * @param MigrationFinderInterface $finder The new migration finder + * @throws MigrationException + */ + public function setMigrationsFinder(MigrationFinderInterface $finder) + { + if (($this->migrationsAreOrganizedByYear || $this->migrationsAreOrganizedByYearAndMonth) && + !($finder instanceof MigrationDeepFinderInterface)) { + + throw MigrationException::configurationIncompatibleWithFinder( + 'organize-migrations', + $finder + ); + } + + $this->migrationFinder = $finder; + } + + /** + * Register migrations from a given directory. Recursively finds all files + * with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers + * them as migrations. + * + * @param string $path The root directory to where some migration classes live. + * + * @return Version[] The array of migrations registered. + */ + public function registerMigrationsFromDirectory($path) + { + $this->validate(); + + return $this->registerMigrations($this->findMigrations($path)); + } + + /** + * Register a single migration version to be executed by a AbstractMigration + * class. + * + * @param string $version The version of the migration in the format YYYYMMDDHHMMSS. + * @param string $class The migration class to execute for the version. + * + * @return Version + * + * @throws MigrationException + */ + public function registerMigration($version, $class) + { + $this->ensureMigrationClassExists($class); + + $version = (string) $version; + $class = (string) $class; + if (isset($this->migrations[$version])) { + throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version])); + } + $version = new Version($this, $version, $class); + $this->migrations[$version->getVersion()] = $version; + ksort($this->migrations); + + return $version; + } + + /** + * Register an array of migrations. Each key of the array is the version and + * the value is the migration class name. + * + * + * @param array $migrations + * + * @return Version[] + */ + public function registerMigrations(array $migrations) + { + $versions = []; + foreach ($migrations as $version => $class) { + $versions[] = $this->registerMigration($version, $class); + } + + return $versions; + } + + /** + * Get the array of registered migration versions. + * + * @return Version[] $migrations + */ + public function getMigrations() + { + return $this->migrations; + } + + /** + * Returns the Version instance for a given version in the format YYYYMMDDHHMMSS. + * + * @param string $version The version string in the format YYYYMMDDHHMMSS. + * + * @return Version + * + * @throws MigrationException Throws exception if migration version does not exist. + */ + public function getVersion($version) + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + if (!isset($this->migrations[$version])) { + throw MigrationException::unknownMigrationVersion($version); + } + + return $this->migrations[$version]; + } + + /** + * Check if a version exists. + * + * @param string $version + * + * @return boolean + */ + public function hasVersion($version) + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + return isset($this->migrations[$version]); + } + + /** + * Check if a version has been migrated or not yet + * + * @param Version $version + * + * @return boolean + */ + public function hasVersionMigrated(Version $version) + { + $this->createMigrationTable(); + + $version = $this->connection->fetchColumn( + "SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName . " WHERE " . $this->migrationsColumnName . " = ?", + [$version->getVersion()] + ); + + return $version !== false; + } + + /** + * Returns all migrated versions from the versions table, in an array. + * + * @return Version[] + */ + public function getMigratedVersions() + { + $this->createMigrationTable(); + + $ret = $this->connection->fetchAll("SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName); + + return array_map('current', $ret); + } + + /** + * Returns an array of available migration version numbers. + * + * @return array + */ + public function getAvailableVersions() + { + $availableVersions = []; + + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + foreach ($this->migrations as $migration) { + $availableVersions[] = $migration->getVersion(); + } + + return $availableVersions; + } + + /** + * Returns the current migrated version from the versions table. + * + * @return string + */ + public function getCurrentVersion() + { + $this->createMigrationTable(); + + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + $where = null; + if (!empty($this->migrations)) { + $migratedVersions = []; + foreach ($this->migrations as $migration) { + $migratedVersions[] = sprintf("'%s'", $migration->getVersion()); + } + $where = " WHERE " . $this->migrationsColumnName . " IN (" . implode(', ', $migratedVersions) . ")"; + } + + $sql = sprintf("SELECT %s FROM %s%s ORDER BY %s DESC", + $this->migrationsColumnName, $this->migrationsTableName, $where, $this->migrationsColumnName + ); + + $sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1); + $result = $this->connection->fetchColumn($sql); + + return $result !== false ? (string) $result : '0'; + } + + /** + * Returns the version prior to the current version. + * + * @return string|null A version string, or null if the current version is + * the first. + */ + public function getPrevVersion() + { + return $this->getRelativeVersion($this->getCurrentVersion(), -1); + } + + /** + * Returns the version following the current version. + * + * @return string|null A version string, or null if the current version is + * the latest. + */ + public function getNextVersion() + { + return $this->getRelativeVersion($this->getCurrentVersion(), 1); + } + + /** + * Returns the version with the specified offset to the specified version. + * + * @return string|null A version string, or null if the specified version + * is unknown or the specified delta is not within the + * list of available versions. + */ + public function getRelativeVersion($version, $delta) + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + $versions = array_keys($this->migrations); + array_unshift($versions, 0); + $offset = array_search($version, $versions); + if ($offset === false || !isset($versions[$offset + $delta])) { + // Unknown version or delta out of bounds. + return null; + } + + return (string) $versions[$offset + $delta]; + } + + /** + * Returns the version number from an alias. + * + * Supported aliases are: + * - first: The very first version before any migrations have been run. + * - current: The current version. + * - prev: The version prior to the current version. + * - next: The version following the current version. + * - latest: The latest available version. + * + * If an existing version number is specified, it is returned verbatimly. + * + * @return string|null A version number, or null if the specified alias + * does not map to an existing version, e.g. if "next" + * is passed but the current version is already the + * latest. + */ + public function resolveVersionAlias($alias) + { + if ($this->hasVersion($alias)) { + return $alias; + } + switch ($alias) { + case 'first': + return '0'; + case 'current': + return $this->getCurrentVersion(); + case 'prev': + return $this->getPrevVersion(); + case 'next': + return $this->getNextVersion(); + case 'latest': + return $this->getLatestVersion(); + default: + return null; + } + } + + /** + * Returns the total number of executed migration versions + * + * @return integer + */ + public function getNumberOfExecutedMigrations() + { + $this->createMigrationTable(); + + $result = $this->connection->fetchColumn("SELECT COUNT(" . $this->migrationsColumnName . ") FROM " . $this->migrationsTableName); + + return $result !== false ? $result : 0; + } + + /** + * Returns the total number of available migration versions + * + * @return integer + */ + public function getNumberOfAvailableMigrations() + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + return count($this->migrations); + } + + /** + * Returns the latest available migration version. + * + * @return string The version string in the format YYYYMMDDHHMMSS. + */ + public function getLatestVersion() + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + $versions = array_keys($this->migrations); + $latest = end($versions); + + return $latest !== false ? (string) $latest : '0'; + } + + /** + * Create the migration table to track migrations with. + * + * @return boolean Whether or not the table was created. + */ + public function createMigrationTable() + { + $this->validate(); + + if ($this->migrationTableCreated) { + return false; + } + + if ($this->connection->getSchemaManager()->tablesExist([$this->migrationsTableName])) { + $this->migrationTableCreated = true; + + return false; + } + + $columns = [ + $this->migrationsColumnName => new Column($this->migrationsColumnName, Type::getType('string'), ['length' => 255]), + ]; + $table = new Table($this->migrationsTableName, $columns); + $table->setPrimaryKey([$this->migrationsColumnName]); + $this->connection->getSchemaManager()->createTable($table); + + $this->migrationTableCreated = true; + + return true; + } + + /** + * Returns the array of migrations to executed based on the given direction + * and target version number. + * + * @param string $direction The direction we are migrating. + * @param string $to The version to migrate to. + * + * @return Version[] $migrations The array of migrations we can execute. + */ + public function getMigrationsToExecute($direction, $to) + { + if (empty($this->migrations)) { + $this->registerMigrationsFromDirectory($this->getMigrationsDirectory()); + } + + if ($direction === Version::DIRECTION_DOWN) { + if (count($this->migrations)) { + $allVersions = array_reverse(array_keys($this->migrations)); + $classes = array_reverse(array_values($this->migrations)); + $allVersions = array_combine($allVersions, $classes); + } else { + $allVersions = []; + } + } else { + $allVersions = $this->migrations; + } + $versions = []; + $migrated = $this->getMigratedVersions(); + foreach ($allVersions as $version) { + if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) { + $versions[$version->getVersion()] = $version; + } + } + + return $versions; + } + + /** + * Find all the migrations in a given directory. + * + * @param string $path the directory to search. + * @return array + */ + protected function findMigrations($path) + { + return $this->migrationFinder->findMigrations($path, $this->getMigrationsNamespace()); + } + + /** + * @param bool $migrationsAreOrganizedByYear + * @throws MigrationException + */ + public function setMigrationsAreOrganizedByYear($migrationsAreOrganizedByYear = true) + { + $this->ensureOrganizeMigrationsIsCompatibleWithFinder(); + + $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYear; + } + + /** + * @param bool $migrationsAreOrganizedByYearAndMonth + * @throws MigrationException + */ + public function setMigrationsAreOrganizedByYearAndMonth($migrationsAreOrganizedByYearAndMonth = true) + { + $this->ensureOrganizeMigrationsIsCompatibleWithFinder(); + + $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYearAndMonth; + $this->migrationsAreOrganizedByYearAndMonth = $migrationsAreOrganizedByYearAndMonth; + } + + /** + * @throws MigrationException + */ + private function ensureOrganizeMigrationsIsCompatibleWithFinder() + { + if (!($this->migrationFinder instanceof MigrationDeepFinderInterface)) { + throw MigrationException::configurationIncompatibleWithFinder( + 'organize-migrations', + $this->migrationFinder + ); + } + } + + /** + * Check if we should execute a migration for a given direction and target + * migration version. + * + * @param string $direction The direction we are migrating. + * @param Version $version The Version instance to check. + * @param string $to The version we are migrating to. + * @param array $migrated Migrated versions array. + * + * @return boolean + */ + private function shouldExecuteMigration($direction, Version $version, $to, $migrated) + { + if ($direction === Version::DIRECTION_DOWN) { + if (!in_array($version->getVersion(), $migrated)) { + return false; + } + + return $version->getVersion() > $to; + } + + if ($direction === Version::DIRECTION_UP) { + if (in_array($version->getVersion(), $migrated)) { + return false; + } + + return $version->getVersion() <= $to; + } + } + + /** + * @param string $class + */ + private function ensureMigrationClassExists($class) + { + if ( ! class_exists($class)) { + throw MigrationException::migrationClassNotFound($class, $this->getMigrationsNamespace()); + } + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php new file mode 100644 index 000000000..6e287902a --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/ConnectionLoaderInterface.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration\Connection; + + +use Doctrine\DBAL\Connection; + +interface ConnectionLoaderInterface +{ + /** + * read the input and return a Configuration, returns `false` if the config + * is not supported + * @return Connection|null + */ + public function chosen(); +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php new file mode 100644 index 000000000..5e6401525 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ArrayConnectionConfigurationLoader.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration\Connection\Loader; + + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Migrations\Configuration\Connection\ConnectionLoaderInterface; + +class ArrayConnectionConfigurationLoader implements ConnectionLoaderInterface +{ + private $filename; + + public function __construct($filename) + { + $this->filename = $filename; + } + + /** + * read the input and return a Configuration, returns `false` if the config + * is not supported + * @return Connection|null + */ + public function chosen() + { + if (empty($this->filename)) { + return null; + } + + if (!file_exists($this->filename)) { + return null; + } + + $params = include $this->filename; + if (!is_array($params)) { + throw new \InvalidArgumentException('The connection file has to return an array with database configuration parameters.'); + } + + return DriverManager::getConnection($params); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php new file mode 100644 index 000000000..f77ca8f52 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationChainLoader.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration\Connection\Loader; + + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Migrations\Configuration\Connection\ConnectionLoaderInterface; + +final class ConnectionConfigurationChainLoader implements ConnectionLoaderInterface +{ + /** @var ConnectionLoaderInterface[] */ + private $loaders; + + + public function __construct(array $loaders) + { + $this->loaders = $loaders; + } + + /** + * read the input and return a Configuration, returns `false` if the config + * is not supported + * @return Connection|null + */ + public function chosen() + { + foreach($this->loaders as $loader) { + if (null !== $confObj = $loader->chosen()) { + return $confObj; + } + } + + return null; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php new file mode 100644 index 000000000..1e5a3412c --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionConfigurationLoader.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration\Connection\Loader; + + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Configuration\Connection\ConnectionLoaderInterface; + +class ConnectionConfigurationLoader implements ConnectionLoaderInterface +{ + /** @var Configuration */ + private $configuration; + + public function __construct(Configuration $configuration=null) + { + if ($configuration !== null) { + $this->configuration = $configuration; + } + } + + /** + * read the input and return a Configuration, returns `false` if the config + * is not supported + * @return Connection|null + */ + public function chosen() + { + if ($this->configuration) { + + return $this->configuration->getConnection(); + } + + return null; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php new file mode 100644 index 000000000..887578633 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/Connection/Loader/ConnectionHelperLoader.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration\Connection\Loader; + + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Migrations\Configuration\Connection\ConnectionLoaderInterface; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Symfony\Component\Console\Helper\HelperSet; + +class ConnectionHelperLoader implements ConnectionLoaderInterface +{ + /** + * @var string + */ + private $helperName; + + /** @var HelperSet */ + private $helperSet; + + + /** + * ConnectionHelperLoader constructor. + * @param HelperSet $helperSet + * @param string $helperName + */ + public function __construct(HelperSet $helperSet = null, $helperName) + { + $this->helperName = $helperName; + if ($helperSet === null) { + $helperSet = new HelperSet(); + } + $this->helperSet = $helperSet; + } + + /** + * read the input and return a Configuration, returns `false` if the config + * is not supported + * @return Connection|null + */ + public function chosen() + { + if ($this->helperSet->has($this->helperName)) { + $connectionHelper = $this->helperSet->get($this->helperName); + if ($connectionHelper instanceof ConnectionHelper) { + + return $connectionHelper->getConnection(); + } + } + + return null; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php new file mode 100644 index 000000000..15264f93d --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/JsonConfiguration.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +/** + * Load migration configuration information from a PHP configuration file. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author David Havl + */ +class JsonConfiguration extends AbstractFileConfiguration +{ + /** + * @inheritdoc + */ + protected function doLoad($file) + { + $config = json_decode(file_get_contents($file), true); + + if (isset($config['migrations_directory'])) { + $config['migrations_directory'] = $this->getDirectoryRelativeToFile($file, $config['migrations_directory']); + } + + $this->setConfiguration($config); + + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XML/configuration.xsd b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XML/configuration.xsd new file mode 100644 index 000000000..225fd8fca --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XML/configuration.xsd @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php new file mode 100644 index 000000000..b8a02c431 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/XmlConfiguration.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; + +use Doctrine\DBAL\Migrations\MigrationException; + +/** + * Load migration configuration information from a XML configuration file. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class XmlConfiguration extends AbstractFileConfiguration +{ + /** + * @inheritdoc + */ + protected function doLoad($file) + { + libxml_use_internal_errors(true); + $xml = new \DOMDocument(); + $xml->load($file); + if (!$xml->schemaValidate(__DIR__ . DIRECTORY_SEPARATOR . "XML" . DIRECTORY_SEPARATOR . "configuration.xsd")) { + libxml_clear_errors(); + throw MigrationException::configurationNotValid('XML configuration did not pass the validation test.'); + } + + $xml = simplexml_load_file($file, "SimpleXMLElement", LIBXML_NOCDATA); + $config = []; + + if (isset($xml->name)) { + $config['name'] = (string) $xml->name; + } + if (isset($xml->table['name'])) { + $config['table_name'] = (string) $xml->table['name']; + } + if (isset($xml->table['column'])) { + $config['column_name'] = (string) $xml->table['column']; + } + if (isset($xml->{'migrations-namespace'})) { + $config['migrations_namespace'] = (string) $xml->{'migrations-namespace'}; + } + if (isset($xml->{'organize-migrations'})) { + $config['organize_migrations'] = $xml->{'organize-migrations'}; + } + if (isset($xml->{'migrations-directory'})) { + $config['migrations_directory'] = $this->getDirectoryRelativeToFile($file, (string) $xml->{'migrations-directory'}); + } + if (isset($xml->migrations->migration)) { + $config['migrations'] = $xml->migrations->migration; + } + + $this->setConfiguration($config); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php new file mode 100644 index 000000000..4b1677bca --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Configuration/YamlConfiguration.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Configuration; + +use Symfony\Component\Yaml\Yaml; + +/** + * Load migration configuration information from a YAML configuration file. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class YamlConfiguration extends AbstractFileConfiguration +{ + /** + * @inheritdoc + */ + protected function doLoad($file) + { + $config = Yaml::parse(file_get_contents($file)); + + if (!is_array($config)) { + throw new \InvalidArgumentException('Not valid configuration.'); + } + + if (isset($config['migrations_directory'])) { + $config['migrations_directory'] = $this->getDirectoryRelativeToFile($file, $config['migrations_directory']); + } + + $this->setConfiguration($config); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php new file mode 100644 index 000000000..14c159b53 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/AbstractFinder.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Finder; + +/** + * Abstract base class for MigrationFinders + * + * @since 1.0.0-alpha3 + */ +abstract class AbstractFinder implements MigrationFinderInterface +{ + protected static function requireOnce($path) + { + require_once $path; + } + + protected function getRealPath($directory) + { + $dir = realpath($directory); + if (false === $dir || !is_dir($dir)) { + throw new \InvalidArgumentException(sprintf( + 'Cannot load migrations from "%s" because it is not a valid directory', + $directory + )); + } + + return $dir; + } + + /** + * Load the migrations and return an array of thoses loaded migrations + * @param $files array of migration filename found + * @param $namespace namespace of thoses migrations + * @return array constructed with the migration name as key and the value is the fully qualified name of the migration + */ + protected function loadMigrations($files, $namespace) + { + $migrations = []; + + uasort($files, $this->getFileSortCallback()); + + foreach ($files as $file) { + static::requireOnce($file); + $className = basename($file, '.php'); + $version = (string) substr($className, 7); + if ($version === '0') { + throw new \InvalidArgumentException(sprintf( + 'Cannot load a migrations with the name "%s" because it is a reserved number by doctrine migraitons' . PHP_EOL . + 'It\'s used to revert all migrations including the first one.', + $version + )); + } + $migrations[$version] = sprintf('%s\\%s', $namespace, $className); + } + + return $migrations; + } + + /** + * Return callable for files basename uasort + * + * @return callable + */ + protected function getFileSortCallback(){ + return function ($a, $b) { + return (basename($a) < basename($b)) ? -1 : 1; + }; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php new file mode 100644 index 000000000..36f6c00d1 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/GlobFinder.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Finder; + +/** + * A MigrationFinderInterface implementation that uses `glob` and some special file and + * class names to load migrations from a directory. + * + * The migrations are expected to reside in files with the filename + * `VersionYYYYMMDDHHMMSS.php`. Each file should contain one class named + * `VersionYYYYMMDDHHMMSS`. + * + * @since 1.0.0-alpha3 + */ +final class GlobFinder extends AbstractFinder +{ + /** + * {@inheritdoc} + */ + public function findMigrations($directory, $namespace = null) + { + $dir = $this->getRealPath($directory); + + $files = glob(rtrim($dir, '/') . '/Version*.php'); + + return $this->loadMigrations($files, $namespace); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php new file mode 100644 index 000000000..6e8ad2ecd --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationDeepFinderInterface.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Finder; + +/** + * A MigrationDeepFinderInterface is a MigrationFinderInterface, which locates + * migrations not only in a directory itself, but in subdirectories of this directory, + * too. + */ +interface MigrationDeepFinderInterface extends MigrationFinderInterface +{ +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php new file mode 100644 index 000000000..8052ac85c --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/MigrationFinderInterface.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Finder; + +/** + * MigrationFinderInterface implementations locate migrations (classes that extend + * `Doctrine\DBAL\Migrations\AbstractMigration`) in a directory. + * + * @since 1.0.0-alpha3 + */ +interface MigrationFinderInterface +{ + /** + * Find all the migrations in a directory for the given path and namespace. + * + * @param string $directory The directory in which to look for migrations + * @param string|null $namespace The namespace of the classes to load + * @throws \InvalidArgumentException if the directory does not exist + * @return string[] An array of class names that were found with the version + * as keys. + */ + public function findMigrations($directory, $namespace = null); +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php new file mode 100644 index 000000000..b626c4184 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Finder/RecursiveRegexFinder.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Finder; + +/** + * A MigrationFinderInterface implementation that uses a RegexIterator along with a + * RecursiveDirectoryIterator. + * + * @since 1.0.0-alpha3 + */ +final class RecursiveRegexFinder extends AbstractFinder implements MigrationDeepFinderInterface +{ + + /** + * {@inheritdoc} + */ + public function findMigrations($directory, $namespace = null) + { + $dir = $this->getRealPath($directory); + + return $this->loadMigrations($this->getMatches($this->createIterator($dir)), $namespace); + } + + /** + * Create a recursive iterator to find all the migrations in the subdirectories. + * @param $dir + * @return \RegexIterator + */ + private function createIterator($dir) + { + return new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + $this->getPattern(), + \RegexIterator::GET_MATCH + ); + } + + private function getPattern() + { + return sprintf('#^.+\\%sVersion[^\\%s]{1,255}\\.php$#i', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR); + } + + /** + * Transform the recursiveIterator result array of array into the expected array of migration file + * @param $iteratorFilesMatch + * @return array + */ + private function getMatches($iteratorFilesMatch) + { + $files = []; + foreach ($iteratorFilesMatch as $file) { + $files[] = $file[0]; + } + + return $files; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php new file mode 100644 index 000000000..a6b40a10b --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/IrreversibleMigrationException.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +/** + * Exception to be thrown in the down() methods of migrations that signifies it + * is an irreversible migration and stops execution. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class IrreversibleMigrationException extends \Exception +{ +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php new file mode 100644 index 000000000..11ee1464c --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Migration.php @@ -0,0 +1,188 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; + +/** + * Class for running migrations to the current version or a manually specified version. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class Migration +{ + /** + * The OutputWriter object instance used for outputting information + * + * @var OutputWriter + */ + private $outputWriter; + + /** + * @var Configuration + */ + private $configuration; + + /** + * @var boolean + */ + private $noMigrationException; + + /** + * Construct a Migration instance + * + * @param Configuration $configuration A migration Configuration instance + */ + public function __construct(Configuration $configuration) + { + $this->configuration = $configuration; + $this->outputWriter = $configuration->getOutputWriter(); + $this->noMigrationException = false; + } + + /** + * Get the array of versions and SQL queries that would be executed for + * each version but do not execute anything. + * + * @param string $to The version to migrate to. + * + * @return array $sql The array of SQL queries. + */ + public function getSql($to = null) + { + return $this->migrate($to, true); + } + + /** + * Write a migration SQL file to the given path + * + * @param string $path The path to write the migration SQL file. + * @param string $to The version to migrate to. + * + * @return boolean $written + */ + public function writeSqlFile($path, $to = null) + { + $sql = $this->getSql($to); + + $from = $this->configuration->getCurrentVersion(); + if ($to === null) { + $to = $this->configuration->getLatestVersion(); + } + + $direction = $from > $to ? Version::DIRECTION_DOWN : Version::DIRECTION_UP; + + $this->outputWriter->write(sprintf("# Migrating from %s to %s\n", $from, $to)); + + $sqlWriter = new SqlFileWriter( + $this->configuration->getMigrationsColumnName(), + $this->configuration->getMigrationsTableName(), + $path, + $this->outputWriter + ); + + return $sqlWriter->write($sql, $direction); + } + + /** + * @param boolean $noMigrationException Throw an exception or not if no migration is found. Mostly for Continuous Integration. + */ + public function setNoMigrationException($noMigrationException = false) + { + $this->noMigrationException = $noMigrationException; + } + + /** + * Run a migration to the current version or the given target version. + * + * @param string $to The version to migrate to. + * @param boolean $dryRun Whether or not to make this a dry run and not execute anything. + * @param boolean $timeAllQueries Measuring or not the execution time of each SQL query. + * + * @return array $sql The array of migration sql statements + * + * @throws MigrationException + */ + public function migrate($to = null, $dryRun = false, $timeAllQueries = false) + { + /** + * If no version to migrate to is given we default to the last available one. + */ + if ($to === null) { + $to = $this->configuration->getLatestVersion(); + } + + $from = (string) $this->configuration->getCurrentVersion(); + $to = (string) $to; + + /** + * Throw an error if we can't find the migration to migrate to in the registered + * migrations. + */ + $migrations = $this->configuration->getMigrations(); + if (!isset($migrations[$to]) && $to > 0) { + throw MigrationException::unknownMigrationVersion($to); + } + + $direction = $from > $to ? Version::DIRECTION_DOWN : Version::DIRECTION_UP; + $migrationsToExecute = $this->configuration->getMigrationsToExecute($direction, $to); + + /** + * If + * there are no migrations to execute + * and there are migrations, + * and the migration from and to are the same + * means we are already at the destination return an empty array() + * to signify that there is nothing left to do. + */ + if ($from === $to && empty($migrationsToExecute) && !empty($migrations)) { + return []; + } + + $output = $dryRun ? 'Executing dry run of migration' : 'Migrating'; + $output .= ' %s to %s from %s'; + $this->outputWriter->write(sprintf($output, $direction, $to, $from)); + + /** + * If there are no migrations to execute throw an exception. + */ + if (empty($migrationsToExecute) && !$this->noMigrationException) { + throw MigrationException::noMigrationsToExecute(); + } + + $sql = []; + $time = 0; + foreach ($migrationsToExecute as $version) { + $versionSql = $version->execute($direction, $dryRun, $timeAllQueries); + $sql[$version->getVersion()] = $versionSql; + $time += $version->getTime(); + } + + $this->outputWriter->write("\n ------------------------\n"); + $this->outputWriter->write(sprintf(" ++ finished in %ss", $time)); + $this->outputWriter->write(sprintf(" ++ %s migrations executed", count($migrationsToExecute))); + $this->outputWriter->write(sprintf(" ++ %s sql queries", count($sql, true) - count($sql))); + + return $sql; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php new file mode 100644 index 000000000..4ea0d26ee --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationException.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +use \Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface; + +/** + * Class for Migrations specific exceptions + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class MigrationException extends \Exception +{ + public static function migrationsNamespaceRequired() + { + return new self('Migrations namespace must be configured in order to use Doctrine migrations.', 2); + } + + public static function migrationsDirectoryRequired() + { + return new self('Migrations directory must be configured in order to use Doctrine migrations.', 3); + } + + public static function noMigrationsToExecute() + { + return new self('Could not find any migrations to execute.', 4); + } + + public static function unknownMigrationVersion($version) + { + return new self(sprintf('Could not find migration version %s', $version), 5); + } + + public static function alreadyAtVersion($version) + { + return new self(sprintf('Database is already at version %s', $version), 6); + } + + public static function duplicateMigrationVersion($version, $class) + { + return new self(sprintf('Migration version %s already registered with class %s', $version, $class), 7); + } + + public static function configurationFileAlreadyLoaded() + { + return new self(sprintf('Migrations configuration file already loaded'), 8); + } + + public static function configurationIncompatibleWithFinder( + $configurationParameterName, + MigrationFinderInterface $finder + ) { + return new self( + sprintf( + 'Configuration-parameter "%s" cannot be used with finder of type "%s"', + $configurationParameterName, + get_class($finder) + ), + 9 + ); + } + + public static function configurationNotValid($msg) + { + return new self($msg, 10); + } + + /** + * @param string $migrationClass + * @param string $migrationNamespace + * @return MigrationException + */ + public static function migrationClassNotFound($migrationClass, $migrationNamespace) + { + return new self( + sprintf( + 'Migration class "%s" was not found. Is it placed in "%s" namespace?', + $migrationClass, + $migrationNamespace + ) + ); + } + + /** + * @param string $migrationClass + * @return MigrationException + */ + public static function migrationNotConvertibleToSql($migrationClass) + { + return new self( + sprintf( + 'Migration class "%s" contains a prepared statement. + Unfortunately there is no cross platform way of outputing it as an sql string. + Do you want to write a PR for it ?', + $migrationClass + ) + ); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php new file mode 100644 index 000000000..a4702268b --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/MigrationsVersion.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +class MigrationsVersion +{ + private static $version = 'v1.4.0'; + + public static function VERSION() { + $gitversion = '@git-version@'; + + if (self::isACustomPharBuild($gitversion)) { + return $gitversion; + } + return self::$version; + } + + /** + * @param $gitversion + * @return bool + * + * Check if doctrine migration is installed by composer or + * in a modified (not tagged) phar version. + */ + private static function isACustomPharBuild($gitversion) { + return $gitversion !== '@' . 'git-version@'; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php new file mode 100644 index 000000000..7801429d7 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/OutputWriter.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +/** + * Simple class for outputting information from migrations. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class OutputWriter +{ + private $closure; + + public function __construct(\Closure $closure = null) + { + if ($closure === null) { + $closure = function($message) {}; + } + $this->closure = $closure; + } + + /** + * Write output using the configured closure. + * + * @param string $message The message to write. + */ + public function write($message) + { + $closure = $this->closure; + $closure($message); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php new file mode 100644 index 000000000..8c05f63d7 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/LazySchemaDiffProvider.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +use Doctrine\DBAL\Migrations\Provider\SchemaDiffProviderInterface; +use Doctrine\DBAL\Schema\Schema; +use ProxyManager\Configuration; +use ProxyManager\Factory\LazyLoadingValueHolderFactory; +use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; +use ProxyManager\Proxy\LazyLoadingInterface; + +class LazySchemaDiffProvider implements SchemaDiffProviderInterface +{ + /** @var LazyLoadingValueHolderFactory */ + private $proxyFactory; + + /** @var SchemaDiffProviderInterface */ + private $originalSchemaManipulator; + + public function __construct(LazyLoadingValueHolderFactory $proxyFactory, SchemaDiffProviderInterface $originalSchemaManipulator) + { + $this->proxyFactory = $proxyFactory; + $this->originalSchemaManipulator = $originalSchemaManipulator; + } + + public static function fromDefaultProxyFacyoryConfiguration(SchemaDiffProviderInterface $originalSchemaManipulator) + { + $proxyConfig = new Configuration(); + $proxyConfig->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); + $proxyFactory = new LazyLoadingValueHolderFactory($proxyConfig); + + return new LazySchemaDiffProvider($proxyFactory, $originalSchemaManipulator); + } + + /** + * @return Schema + */ + public function createFromSchema() + { + $originalSchemaManipulator = $this->originalSchemaManipulator; + + return $this->proxyFactory->createProxy( + Schema::class, + function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) use ($originalSchemaManipulator) { + $initializer = null; + $wrappedObject = $originalSchemaManipulator->createFromSchema(); + + return true; + } + ); + } + + /** + * @param Schema $fromSchema + * @return Schema + */ + public function createToSchema(Schema $fromSchema) + { + $originalSchemaManipulator = $this->originalSchemaManipulator; + + if ($fromSchema instanceof LazyLoadingInterface && ! $fromSchema->isProxyInitialized()) { + return $this->proxyFactory->createProxy( + Schema::class, + function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) use ($originalSchemaManipulator, $fromSchema) { + $initializer = null; + $wrappedObject = $originalSchemaManipulator->createToSchema($fromSchema); + + return true; + } + ); + } + + return $this->originalSchemaManipulator->createToSchema($fromSchema); + } + + /** + * @param Schema $fromSchema + * @param Schema $toSchema + * + * @return string[] + */ + public function getSqlDiffToMigrate(Schema $fromSchema, Schema $toSchema) + { + if ( + $toSchema instanceof LazyLoadingInterface + && ! $toSchema->isProxyInitialized() + ) { + return []; + } + + return $this->originalSchemaManipulator->getSqlDiffToMigrate($fromSchema, $toSchema); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php new file mode 100644 index 000000000..66e86b5db --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/OrmSchemaProvider.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * A schema provider that uses the doctrine ORM to generate schemas. + * + * @since 1.0.0-alpha3 + */ +final class OrmSchemaProvider implements SchemaProviderInterface +{ + /** + * @var EntityManagerInterface + */ + private $entityManager; + + public function __construct($em) + { + if (!$this->isEntityManager($em)) { + throw new \InvalidArgumentException(sprintf( + '$em is not a valid Doctrine ORM Entity Manager, got "%s"', + is_object($em) ? get_class($em) : gettype($em) + )); + } + + $this->entityManager = $em; + } + + /** + * {@inheritdoc} + */ + public function createSchema() + { + $metadata = $this->entityManager->getMetadataFactory()->getAllMetadata(); + if (empty($metadata)) { + throw new \UnexpectedValueException('No mapping information to process'); + } + + $tool = new SchemaTool($this->entityManager); + + return $tool->getSchemaFromMetadata($metadata); + } + + + /** + * Doctrine's EntityManagerInterface was introduced in version 2.4, since this + * library allows those older version we need to be able to check for those + * old ORM versions. Hence the helper method. + * + * No need to check to see if EntityManagerInterface exists first here, PHP + * doesn't care. + * + * @param mixed $manager Hopefully an entity manager, but it may be anything + * @return boolean + */ + private function isEntityManager($manager) + { + return $manager instanceof EntityManagerInterface || $manager instanceof EntityManager; + } + +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php new file mode 100644 index 000000000..3220e64bc --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProvider.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +use Doctrine\DBAL\Migrations\Provider\SchemaDiffProviderInterface; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\Schema; + +class SchemaDiffProvider implements SchemaDiffProviderInterface +{ + /** @var AbstractPlatform */ + private $platform; + + /** @var AbstractSchemaManager */ + private $schemaManager; + + public function __construct(AbstractSchemaManager $schemaManager, AbstractPlatform $platform) + { + $this->schemaManager = $schemaManager; + $this->platform = $platform; + } + + /** + * @return Schema + */ + public function createFromSchema() + { + return $this->schemaManager->createSchema(); + } + + /** + * @param Schema $fromSchema + * @return Schema + */ + public function createToSchema(Schema $fromSchema) + { + return clone $fromSchema; + } + + /** + * @param Schema $fromSchema + * @param Schema $toSchema + * @return string[] + */ + public function getSqlDiffToMigrate(Schema $fromSchema, Schema $toSchema) + { + return $fromSchema->getMigrateToSql($toSchema, $this->platform); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php new file mode 100644 index 000000000..215854582 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaDiffProviderInterface.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +use Doctrine\DBAL\Schema\Schema; + +/** + * Generates `Schema` objects to be passed to the migrations class. + * + * @since 1.3 + */ +interface SchemaDiffProviderInterface +{ + /** + * Create the schema that represent the current state of the database. + * + * @return Schema + */ + public function createFromSchema(); + + /** + * Create the schema that will represent the future state of the database + * + * @param Schema $fromSchema + * @return Schema + */ + public function createToSchema(Schema $fromSchema); + + /** + * Return an array of sql statement that migrate the database state from the + * fromSchema to the toSchema. + * + * @param Schema $fromSchema + * @param Schema $toSchema + * + * @return string[] + */ + public function getSqlDiffToMigrate(Schema $fromSchema, Schema $toSchema); +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php new file mode 100644 index 000000000..d39ee39b2 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/SchemaProviderInterface.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +/** + * Generates `Schema` objects for the diff command. A schema provider should + * return the schema to which the database should be migrated. + * + * @since 1.0.0-alpha3 + */ +interface SchemaProviderInterface +{ + /** + * Create the schema to which the database should be migrated. + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function createSchema(); +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php new file mode 100644 index 000000000..765bc829d --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Provider/StubSchemaProvider.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Provider; + +use Doctrine\DBAL\Schema\Schema; + +/** + * A schemea provider implementation that just returns the schema its given. + * + * @since 1.0.0-alpha3 + */ +final class StubSchemaProvider implements SchemaProviderInterface +{ + /** + * @var Schema + */ + private $toSchema; + + public function __construct(Schema $schema) + { + $this->toSchema = $schema; + } + + /** + * {@inheritdoc} + */ + public function createSchema() + { + return $this->toSchema; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php new file mode 100644 index 000000000..922ec884e --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SkipMigrationException.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +class SkipMigrationException extends MigrationException +{ +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php new file mode 100644 index 000000000..c2aec6424 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/SqlFileWriter.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception\InvalidArgumentException; + +class SqlFileWriter +{ + private $migrationsColumnName; + + private $migrationsTableName; + + private $destPath; + + /** @var null|OutputWriter */ + private $outputWriter; + + /** + * @param string $migrationsColumnName + * @param string $migrationsTableName + * @param string $destPath + * @param \Doctrine\DBAL\Migrations\OutputWriter $outputWriter + */ + public function __construct($migrationsColumnName, $migrationsTableName, $destPath, OutputWriter $outputWriter = null) + { + if (empty($migrationsColumnName)) { + $this->throwInvalidArgumentException('Migrations column name cannot be empty.'); + } + $this->migrationsColumnName = $migrationsColumnName; + if (empty($migrationsTableName)) { + $this->throwInvalidArgumentException('Migrations table name cannot be empty.'); + } + $this->migrationsTableName = $migrationsTableName; + + if (empty($destPath)) { + $this->throwInvalidArgumentException('Destination file must be specified.'); + } + $this->destPath = $destPath; + + $this->outputWriter = $outputWriter; + } + + /** + * @param array $queriesByVersion array Keys are versions and values are arrays of SQL queries (they must be castable to string) + * @param string $direction + * @return int|bool + */ + public function write(array $queriesByVersion, $direction) + { + $path = $this->buildMigrationFilePath(); + $string = $this->buildMigrationFile($queriesByVersion, $direction); + + if ($this->outputWriter) { + $this->outputWriter->write("\n" . sprintf('Writing migration file to "%s"', $path)); + } + + return file_put_contents($path, $string); + } + + private function buildMigrationFile(array $queriesByVersion, $direction) + { + $string = sprintf("# Doctrine Migration File Generated on %s\n", date('Y-m-d H:i:s')); + + foreach ($queriesByVersion as $version => $queries) { + $string .= "\n# Version " . $version . "\n"; + foreach ($queries as $query) { + $string .= $query . ";\n"; + } + + + $string .= $this->getVersionUpdateQuery($version, $direction); + } + + return $string; + } + + private function getVersionUpdateQuery($version, $direction) + { + if ($direction == Version::DIRECTION_DOWN) { + $query = "DELETE FROM %s WHERE %s = '%s';\n"; + } else { + $query = "INSERT INTO %s (%s) VALUES ('%s');\n"; + } + + return sprintf($query, $this->migrationsTableName, $this->migrationsColumnName, $version); + } + + private function buildMigrationFilePath() + { + $path = $this->destPath; + if (is_dir($path)) { + $path = realpath($path); + $path = $path . '/doctrine_migration_' . date('YmdHis') . '.sql'; + } + + return $path; + } + + /** + * This only exists for backwards-compatibiliy with DBAL 2.4 + */ + protected function throwInvalidArgumentException($message) + { + if (class_exists('Doctrine\DBAL\Exception\InvalidArgumentException')) { + throw new InvalidArgumentException($message); + } else { + throw new DBALException($message); + } + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php new file mode 100644 index 000000000..3c1cff17b --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php @@ -0,0 +1,183 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ArrayConnectionConfigurationLoader; +use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionConfigurationLoader; +use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionHelperLoader; +use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionConfigurationChainLoader; +use Doctrine\DBAL\Migrations\OutputWriter; +use Doctrine\DBAL\Migrations\Tools\Console\Helper\ConfigurationHelper; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +/** + * CLI Command for adding and deleting migration versions from the version table. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +abstract class AbstractCommand extends Command +{ + /** + * The configuration property only contains the configuration injected by the setter. + * + * @var Configuration + */ + private $configuration; + + /** + * The migrationConfiguration property contains the configuration + * created taking into account the command line options. + * + * @var Configuration + */ + private $migrationConfiguration; + + /** + * @var OutputWriter + */ + private $outputWriter; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + protected function configure() + { + $this->addOption('configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a migrations configuration file.'); + $this->addOption('db-configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a database connection configuration file.'); + } + + protected function outputHeader(Configuration $configuration, OutputInterface $output) + { + $name = $configuration->getName(); + $name = $name ? $name : 'Doctrine Database Migrations'; + $name = str_repeat(' ', 20) . $name . str_repeat(' ', 20); + $output->writeln('' . str_repeat(' ', strlen($name)) . ''); + $output->writeln('' . $name . ''); + $output->writeln('' . str_repeat(' ', strlen($name)) . ''); + $output->writeln(''); + } + + public function setMigrationConfiguration(Configuration $config) + { + $this->configuration = $config; + } + + /** + * When any (config) command line option is passed to the migration the migrationConfiguration + * property is set with the new generated configuration. + * If no (config) option is passed the migrationConfiguration property is set to the value + * of the configuration one (if any). + * Else a new configuration is created and assigned to the migrationConfiguration property. + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return Configuration + */ + protected function getMigrationConfiguration(InputInterface $input, OutputInterface $output) + { + if (!$this->migrationConfiguration) { + if ($this->getHelperSet()->has('configuration') + && $this->getHelperSet()->get('configuration') instanceof ConfigurationHelper) { + $configHelper = $this->getHelperSet()->get('configuration'); + } else { + $configHelper = new ConfigurationHelper($this->getConnection($input), $this->configuration); + } + $this->migrationConfiguration = $configHelper->getMigrationConfig($input, $this->getOutputWriter($output)); + } + + return $this->migrationConfiguration; + } + + /** + * This method ensure that we stay compatible with symfony console 2.3 by using the deprecated dialog helper + * but use the ConfirmationQuestion when available. + * + * @param $question + * @param InputInterface $input + * @param OutputInterface $output + * @return mixed + */ + protected function askConfirmation($question, InputInterface $input, OutputInterface $output) + { + if (!$this->getHelperSet()->has('question')) { + return $this->getHelper('dialog')->askConfirmation($output, '' . $question . '', false); + } + + return $this->getHelper('question')->ask($input, $output, new ConfirmationQuestion($question)); + } + + /** + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return \Doctrine\DBAL\Migrations\OutputWriter + */ + private function getOutputWriter(OutputInterface $output) + { + if (!$this->outputWriter) { + $this->outputWriter = new OutputWriter(function($message) use ($output) { + return $output->writeln($message); + }); + } + + return $this->outputWriter; + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + * + * @return \Doctrine\DBAL\Connection + * @throws \Doctrine\DBAL\DBALException + */ + private function getConnection(InputInterface $input) + { + if ($this->connection) { + return $this->connection; + } + + $chainLoader = new ConnectionConfigurationChainLoader( + [ + new ArrayConnectionConfigurationLoader($input->getOption('db-configuration')), + new ArrayConnectionConfigurationLoader('migrations-db.php'), + new ConnectionHelperLoader($this->getHelperSet(), 'connection'), + new ConnectionConfigurationLoader($this->configuration), + ] + ); + $connection = $chainLoader->chosen(); + + if ($connection) { + return $this->connection = $connection; + } + + throw new \InvalidArgumentException('You have to specify a --db-configuration file or pass a Database Connection as a dependency to the Migrations.'); + } + +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php new file mode 100644 index 000000000..99d385c42 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/DiffCommand.php @@ -0,0 +1,196 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Version as DbalVersion; +use Doctrine\DBAL\Migrations\Provider\SchemaProviderInterface; +use Doctrine\DBAL\Migrations\Provider\OrmSchemaProvider; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command for generate migration classes by comparing your current database schema + * to your mapping information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class DiffCommand extends GenerateCommand +{ + /** + * @var SchemaProviderInterface + */ + protected $schemaProvider; + + public function __construct(SchemaProviderInterface $schemaProvider=null) + { + $this->schemaProvider = $schemaProvider; + parent::__construct(); + } + + protected function configure() + { + parent::configure(); + + $this + ->setName('migrations:diff') + ->setDescription('Generate a migration by comparing your current database to your mapping information.') + ->setHelp(<<%command.name% command generates a migration by comparing your current database to your mapping information: + + %command.full_name% + +You can optionally specify a --editor-cmd option to open the generated file in your favorite editor: + + %command.full_name% --editor-cmd=mate +EOT + ) + ->addOption('filter-expression', null, InputOption::VALUE_OPTIONAL, 'Tables which are filtered by Regular Expression.') + ->addOption('formatted', null, InputOption::VALUE_NONE, 'Format the generated SQL.') + ->addOption('line-length', null, InputOption::VALUE_OPTIONAL, 'Max line length of unformatted lines.', 120) + ; + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $isDbalOld = (DbalVersion::compare('2.2.0') > 0); + $configuration = $this->getMigrationConfiguration($input, $output); + + $conn = $configuration->getConnection(); + $platform = $conn->getDatabasePlatform(); + + if ($filterExpr = $input->getOption('filter-expression')) { + if ($isDbalOld) { + throw new \InvalidArgumentException('The "--filter-expression" option can only be used as of Doctrine DBAL 2.2'); + } + + $conn->getConfiguration() + ->setFilterSchemaAssetsExpression($filterExpr); + } + + $fromSchema = $conn->getSchemaManager()->createSchema(); + $toSchema = $this->getSchemaProvider()->createSchema(); + + //Not using value from options, because filters can be set from config.yml + if ( ! $isDbalOld && $filterExpr = $conn->getConfiguration()->getFilterSchemaAssetsExpression()) { + foreach ($toSchema->getTables() as $table) { + $tableName = $table->getName(); + if ( ! preg_match($filterExpr, $this->resolveTableName($tableName))) { + $toSchema->dropTable($tableName); + } + } + } + + $up = $this->buildCodeFromSql( + $configuration, + $fromSchema->getMigrateToSql($toSchema, $platform), + $input->getOption('formatted'), + $input->getOption('line-length') + ); + $down = $this->buildCodeFromSql( + $configuration, + $fromSchema->getMigrateFromSql($toSchema, $platform), + $input->getOption('formatted'), + $input->getOption('line-length') + ); + + if (! $up && ! $down) { + $output->writeln('No changes detected in your mapping information.', 'ERROR'); + + return; + } + + $version = date('YmdHis'); + $path = $this->generateMigration($configuration, $input, $version, $up, $down); + + $output->writeln(sprintf('Generated new migration class to "%s" from schema differences.', $path)); + } + + private function buildCodeFromSql(Configuration $configuration, array $sql, $formatted=false, $lineLength=120) + { + $currentPlatform = $configuration->getConnection()->getDatabasePlatform()->getName(); + $code = []; + foreach ($sql as $query) { + if (stripos($query, $configuration->getMigrationsTableName()) !== false) { + continue; + } + + if ($formatted) { + if (!class_exists('\SqlFormatter')) { + throw new \InvalidArgumentException( + 'The "--formatted" option can only be used if the sql formatter is installed.'. + 'Please run "composer require jdorn/sql-formatter".' + ); + } + + $maxLength = $lineLength - 18 - 8; // max - php code length - indentation + + if (strlen($query) > $maxLength) { + $query = \SqlFormatter::format($query, false); + } + } + + $code[] = sprintf("\$this->addSql(%s);", var_export($query, true)); + } + + if (!empty($code)) { + array_unshift( + $code, + sprintf( + "\$this->abortIf(\$this->connection->getDatabasePlatform()->getName() != %s, %s);", + var_export($currentPlatform, true), + var_export(sprintf("Migration can only be executed safely on '%s'.", $currentPlatform), true) + ), + "" + ); + } + + return implode("\n", $code); + } + + private function getSchemaProvider() + { + if (!$this->schemaProvider) { + $this->schemaProvider = new OrmSchemaProvider($this->getHelper('entityManager')->getEntityManager()); + } + + return $this->schemaProvider; + } + + /** + * Resolve a table name from its fully qualified name. The `$name` argument + * comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return + * a namespaced name with the form `{namespace}.{tableName}`. This extracts + * the table name from that. + * + * @param string $name + * @return string + */ + private function resolveTableName($name) + { + $pos = strpos($name, '.'); + + return false === $pos ? $name : substr($name, $pos + 1); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php new file mode 100644 index 000000000..ffd57508c --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/ExecuteCommand.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command for executing single migrations up or down manually. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class ExecuteCommand extends AbstractCommand +{ + protected function configure() + { + $this + ->setName('migrations:execute') + ->setDescription('Execute a single migration version up or down manually.') + ->addArgument('version', InputArgument::REQUIRED, 'The version to execute.', null) + ->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.') + ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.') + ->addOption('up', null, InputOption::VALUE_NONE, 'Execute the migration up.') + ->addOption('down', null, InputOption::VALUE_NONE, 'Execute the migration down.') + ->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.') + ->setHelp(<<%command.name% command executes a single migration version up or down manually: + + %command.full_name% YYYYMMDDHHMMSS + +If no --up or --down option is specified it defaults to up: + + %command.full_name% YYYYMMDDHHMMSS --down + +You can also execute the migration as a --dry-run: + + %command.full_name% YYYYMMDDHHMMSS --dry-run + +You can output the would be executed SQL statements to a file with --write-sql: + + %command.full_name% YYYYMMDDHHMMSS --write-sql + +Or you can also execute the migration without a warning message which you need to interact with: + + %command.full_name% --no-interaction +EOT + ); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $version = $input->getArgument('version'); + $direction = $input->getOption('down') ? 'down' : 'up'; + + $configuration = $this->getMigrationConfiguration($input, $output); + $version = $configuration->getVersion($version); + + $timeAllqueries = $input->getOption('query-time'); + + if ($path = $input->getOption('write-sql')) { + $path = is_bool($path) ? getcwd() : $path; + $version->writeSqlFile($path, $direction); + } else { + if ($input->isInteractive()) { + $question = 'WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)'; + $execute = $this->askConfirmation($question, $input, $output); + } else { + $execute = true; + } + + if ($execute) { + $version->execute($direction, (boolean) $input->getOption('dry-run'), $timeAllqueries); + } else { + $output->writeln('Migration cancelled!'); + } + } + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php new file mode 100644 index 000000000..e716cbd8d --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/GenerateCommand.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Tools\Console\Helper\MigrationDirectoryHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command for generating new blank migration classes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class GenerateCommand extends AbstractCommand +{ + + private static $_template = + '; + +use Doctrine\DBAL\Migrations\AbstractMigration; +use Doctrine\DBAL\Schema\Schema; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +class Version extends AbstractMigration +{ + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + // this up() migration is auto-generated, please modify it to your needs + + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + + } +} +'; + + protected function configure() + { + $this + ->setName('migrations:generate') + ->setDescription('Generate a blank migration class.') + ->addOption('editor-cmd', null, InputOption::VALUE_OPTIONAL, 'Open file with this command upon creation.') + ->setHelp(<<%command.name% command generates a blank migration class: + + %command.full_name% + +You can optionally specify a --editor-cmd option to open the generated file in your favorite editor: + + %command.full_name% --editor-cmd=mate +EOT + ); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $this->getMigrationConfiguration($input, $output); + + $version = date('YmdHis'); + $path = $this->generateMigration($configuration, $input, $version); + + $output->writeln(sprintf('Generated new migration class to "%s"', $path)); + } + + protected function getTemplate() + { + return self::$_template; + } + + protected function generateMigration(Configuration $configuration, InputInterface $input, $version, $up = null, $down = null) + { + $placeHolders = [ + '', + '', + '', + '', + ]; + $replacements = [ + $configuration->getMigrationsNamespace(), + $version, + $up ? " " . implode("\n ", explode("\n", $up)) : null, + $down ? " " . implode("\n ", explode("\n", $down)) : null + ]; + $code = str_replace($placeHolders, $replacements, $this->getTemplate()); + $code = preg_replace('/^ +$/m', '', $code); + $migrationDirectoryHelper = new MigrationDirectoryHelper($configuration); + $dir = $migrationDirectoryHelper->getMigrationDirectory(); + $path = $dir . '/Version' . $version . '.php'; + + file_put_contents($path, $code); + + if ($editorCmd = $input->getOption('editor-cmd')) { + proc_open($editorCmd . ' ' . escapeshellarg($path), [], $pipes); + } + + return $path; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php new file mode 100644 index 000000000..52d4c6e1b --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/LatestCommand.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Outputs the latest version number. + * + * @author Kris Wallsmith + */ +class LatestCommand extends AbstractCommand +{ + protected function configure() + { + $this + ->setName('migrations:latest') + ->setDescription('Outputs the latest version number') + ; + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $this->getMigrationConfiguration($input, $output); + + $output->writeln($configuration->getLatestVersion()); + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php new file mode 100644 index 000000000..5380821cf --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/MigrateCommand.php @@ -0,0 +1,194 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Migration; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command for executing a migration to a specified version or the latest available version. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class MigrateCommand extends AbstractCommand +{ + protected function configure() + { + $this + ->setName('migrations:migrate') + ->setDescription('Execute a migration to a specified version or the latest available version.') + ->addArgument('version', InputArgument::OPTIONAL, 'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.', 'latest') + ->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.') + ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.') + ->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.') + ->addOption('allow-no-migration', null, InputOption::VALUE_NONE, 'Don\'t throw an exception if no migration is available (CI).') + ->setHelp(<<%command.name% command executes a migration to a specified version or the latest available version: + + %command.full_name% + +You can optionally manually specify the version you wish to migrate to: + + %command.full_name% YYYYMMDDHHMMSS + +You can specify the version you wish to migrate to using an alias: + + %command.full_name% prev + +You can also execute the migration as a --dry-run: + + %command.full_name% YYYYMMDDHHMMSS --dry-run + +You can output the would be executed SQL statements to a file with --write-sql: + + %command.full_name% YYYYMMDDHHMMSS --write-sql + +Or you can also execute the migration without a warning message which you need to interact with: + + %command.full_name% --no-interaction + +You can also time all the different queries if you wanna know which one is taking so long: + + %command.full_name% --query-time +EOT + ); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $this->getMigrationConfiguration($input, $output); + $migration = new Migration($configuration); + + $this->outputHeader($configuration, $output); + + $timeAllqueries = $input->getOption('query-time'); + + $executedMigrations = $configuration->getMigratedVersions(); + $availableMigrations = $configuration->getAvailableVersions(); + + $version = $this->getVersionNameFromAlias($input->getArgument('version'), $output, $configuration); + if ($version === false) { + return 1; + } + + $executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations); + if (!empty($executedUnavailableMigrations)) { + $output->writeln(sprintf( + 'WARNING! You have %s previously executed migrations' + . ' in the database that are not registered migrations.', + count($executedUnavailableMigrations) + )); + + foreach ($executedUnavailableMigrations as $executedUnavailableMigration) { + $output->writeln(sprintf( + ' >> %s (%s)', + $configuration->getDateTime($executedUnavailableMigration), + $executedUnavailableMigration + )); + } + + $question = 'Are you sure you wish to continue? (y/n)'; + if (! $this->canExecute($question, $input, $output)) { + $output->writeln('Migration cancelled!'); + + return 1; + } + } + + if ($path = $input->getOption('write-sql')) { + $path = is_bool($path) ? getcwd() : $path; + $migration->writeSqlFile($path, $version); + } else { + $dryRun = (boolean) $input->getOption('dry-run'); + + // warn the user if no dry run and interaction is on + if (! $dryRun) { + $question = 'WARNING! You are about to execute a database migration' + . ' that could result in schema changes and data lost.' + . ' Are you sure you wish to continue? (y/n)'; + if (! $this->canExecute($question, $input, $output)) { + $output->writeln('Migration cancelled!'); + + return 1; + } + } + + $migration->setNoMigrationException($input->getOption('allow-no-migration')); + $sql = $migration->migrate($version, $dryRun, $timeAllqueries); + + if (empty($sql)) { + $output->writeln('No migrations to execute.'); + } + } + } + + /** + * @param string $question + * @param InputInterface $input + * @param OutputInterface $output + * @return bool + */ + private function canExecute($question, InputInterface $input, OutputInterface $output) + { + if ($input->isInteractive() && ! $this->askConfirmation($question, $input, $output)) { + return false; + } + + return true; + } + + /** + * @param string $versionAlias + * @param OutputInterface $output + * @param Configuration $configuration + * @return bool|string + */ + private function getVersionNameFromAlias($versionAlias, OutputInterface $output, Configuration $configuration) + { + $version = $configuration->resolveVersionAlias($versionAlias); + if ($version === null) { + if ($versionAlias == 'prev') { + $output->writeln('Already at first version.'); + return false; + } + if ($versionAlias == 'next') { + $output->writeln('Already at latest version.'); + return false; + } + + $output->writeln(sprintf( + 'Unknown version: %s', + $output->getFormatter()->escape($versionAlias) + )); + return false; + } + + return $version; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php new file mode 100644 index 000000000..370124e55 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/StatusCommand.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\Migrations\Configuration\AbstractFileConfiguration; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Tools\Console\Helper\MigrationStatusInfosHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command to view the status of a set of migrations. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class StatusCommand extends AbstractCommand +{ + protected function configure() + { + $this + ->setName('migrations:status') + ->setDescription('View the status of a set of migrations.') + ->addOption('show-versions', null, InputOption::VALUE_NONE, 'This will display a list of all available migrations and their status') + ->setHelp(<<%command.name% command outputs the status of a set of migrations: + + %command.full_name% + +You can output a list of all available migrations and their status with --show-versions: + + %command.full_name% --show-versions +EOT + ); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $this->getMigrationConfiguration($input, $output); + + $infos = new MigrationStatusInfosHelper($configuration); + + $output->writeln("\n == Configuration\n"); + foreach ($infos->getMigrationsInfos() as $name => $value) { + if ($name == 'New Migrations') { + $value = $value > 0 ? '' . $value . '' : 0; + } + if($name == 'Executed Unavailable Migrations') { + $value = $value > 0 ? '' . $value . '' : 0; + } + $this->writeStatusInfosLineAligned($output, $name, $value); + } + + if ($input->getOption('show-versions')) { + if ($migrations = $configuration->getMigrations()) { + $output->writeln("\n == Available Migration Versions\n"); + + $this->showVersions($migrations, $configuration, $output); + } + + if (count($infos->getExecutedUnavailableMigrations())) { + $output->writeln("\n == Previously Executed Unavailable Migration Versions\n"); + foreach ($infos->getExecutedUnavailableMigrations() as $executedUnavailableMigration) { + $output->writeln(' >> ' . $configuration->getDateTime($executedUnavailableMigration) . + ' (' . $executedUnavailableMigration . ')'); + } + } + } + } + + private function writeStatusInfosLineAligned(OutputInterface $output, $title, $value) + { + $output->writeln(' >> ' . $title . ': ' . str_repeat(' ', 50 - strlen($title)) . $value); + } + + private function showVersions($migrations, Configuration $configuration, OutputInterface $output) + { + $migratedVersions = $configuration->getMigratedVersions(); + + foreach($migrations as $version) { + $isMigrated = in_array($version->getVersion(), $migratedVersions); + $status = $isMigrated ? 'migrated' : 'not migrated'; + + $migrationDescription = $version->getMigration()->getDescription() + ? str_repeat(' ', 5) . $version->getMigration()->getDescription() + : ''; + + $formattedVersion = $configuration->getDateTime($version->getVersion()); + + $output->writeln(' >> ' . $formattedVersion . + ' (' . $version->getVersion() . ')' . + str_repeat(' ', 49 - strlen($formattedVersion) - strlen($version->getVersion())) . + $status . $migrationDescription); + } + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php new file mode 100644 index 000000000..bb1479250 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/VersionCommand.php @@ -0,0 +1,179 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Command; + +use Doctrine\DBAL\Migrations\MigrationException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +/** + * Command for manually adding and deleting migration versions from the version table. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class VersionCommand extends AbstractCommand +{ + /** + * The Migrations Configuration instance + * + * @var \Doctrine\DBAL\Migrations\Configuration\Configuration + */ + private $configuration; + + /** + * Whether or not the versions have to be marked as migrated or not + * + * @var boolean + */ + private $markMigrated; + + protected function configure() + { + $this + ->setName('migrations:version') + ->setDescription('Manually add and delete migration versions from the version table.') + ->addArgument('version', InputArgument::OPTIONAL, 'The version to add or delete.', null) + ->addOption('add', null, InputOption::VALUE_NONE, 'Add the specified version.') + ->addOption('delete', null, InputOption::VALUE_NONE, 'Delete the specified version.') + ->addOption('all', null, InputOption::VALUE_NONE, 'Apply to all the versions.') + ->addOption('range-from', null, InputOption::VALUE_OPTIONAL, 'Apply from specified version.') + ->addOption('range-to', null, InputOption::VALUE_OPTIONAL, 'Apply to specified version.') + ->setHelp(<<%command.name% command allows you to manually add, delete or synchronize migration versions from the version table: + + %command.full_name% YYYYMMDDHHMMSS --add + +If you want to delete a version you can use the --delete option: + + %command.full_name% YYYYMMDDHHMMSS --delete + +If you want to synchronize by adding or deleting all migration versions available in the version table you can use the --all option: + + %command.full_name% --add --all + %command.full_name% --delete --all + +If you want to synchronize by adding or deleting some range of migration versions available in the version table you can use the --range-from/--range-to option: + + %command.full_name% --add --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS + %command.full_name% --delete --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS + +You can also execute this command without a warning message which you need to interact with: + + %command.full_name% --no-interaction +EOT + ); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $this->configuration = $this->getMigrationConfiguration($input, $output); + + if ( ! $input->getOption('add') && ! $input->getOption('delete')) { + throw new \InvalidArgumentException('You must specify whether you want to --add or --delete the specified version.'); + } + + $this->markMigrated = (boolean) $input->getOption('add'); + + if ($input->isInteractive()) { + $question = 'WARNING! You are about to add, delete or synchronize migration versions from the version table that could result in data lost. Are you sure you wish to continue? (y/n)'; + + $confirmation = $this->askConfirmation($question, $input, $output); + + if ($confirmation) { + $this->markVersions($input); + } else { + $output->writeln('Migration cancelled!'); + } + } else { + $this->markVersions($input); + } + + } + + private function markVersions(InputInterface $input) + { + $affectedVersion = $input->getArgument('version'); + + $allOption = $input->getOption('all'); + $rangeFromOption = $input->getOption('range-from'); + $rangeToOption = $input->getOption('range-to'); + + if ($allOption && ($rangeFromOption !== null || $rangeToOption !== null)) { + throw new \InvalidArgumentException('Options --all and --range-to/--range-from both used. You should use only one of them.'); + } + + if ($rangeFromOption !== null ^ $rangeToOption !== null) { + throw new \InvalidArgumentException('Options --range-to and --range-from should be used together.'); + } + + if ($allOption === true) { + $availableVersions = $this->configuration->getAvailableVersions(); + foreach ($availableVersions as $version) { + $this->mark($version, true); + } + } elseif ($rangeFromOption !== null && $rangeToOption !== null) { + $availableVersions = $this->configuration->getAvailableVersions(); + foreach ($availableVersions as $version) { + if ($version >= $rangeFromOption && $version <= $rangeToOption) { + $this->mark($version, true); + } + } + } else { + $this->mark($affectedVersion); + } + } + + private function mark($version, $all = false) + { + if ( ! $this->configuration->hasVersion($version)) { + throw MigrationException::unknownMigrationVersion($version); + } + + $version = $this->configuration->getVersion($version); + if ($this->markMigrated && $this->configuration->hasVersionMigrated($version)) { + if (! $all) { + throw new \InvalidArgumentException(sprintf('The version "%s" already exists in the version table.', $version)); + } + $marked = true; + } + + if ( ! $this->markMigrated && ! $this->configuration->hasVersionMigrated($version)) { + if (! $all) { + throw new \InvalidArgumentException(sprintf('The version "%s" does not exists in the version table.', $version)); + } + $marked = false; + } + + if ( ! isset($marked)) { + if ($this->markMigrated) { + $version->markMigrated(); + } else { + $version->markNotMigrated(); + } + } + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php new file mode 100644 index 000000000..f3171edb4 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/ConfigurationHelper.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Helper; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\OutputWriter; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Helper\Helper; + +/** + * Class ConfigurationHelper + * @package Doctrine\DBAL\Migrations\Tools\Console\Helper + * @internal + */ +class ConfigurationHelper extends Helper +{ + + /** + * @var Connection + */ + private $connection; + + /** + * @var Configuration + */ + private $configuration; + + public function __construct(Connection $connection = null, Configuration $configuration = null) + { + $this->connection = $connection; + $this->configuration = $configuration; + } + + public function getMigrationConfig(InputInterface $input, OutputWriter $outputWriter) + { + /** + * If a configuration option is passed to the command line, use that configuration + * instead of any other one. + */ + if ($input->getOption('configuration')) { + $outputWriter->write("Loading configuration from command option: " . $input->getOption('configuration')); + + return $this->loadConfig($input->getOption('configuration'), $outputWriter); + } + + /** + * If a configuration has already been set using DI or a Setter use it. + */ + if ($this->configuration) { + $outputWriter->write("Loading configuration from the integration code of your framework (setter)."); + + return $this->configuration; + } + + /** + * If no any other config has been found, look for default config file in the path. + */ + $defaultConfig = [ + 'migrations.xml', + 'migrations.yml', + 'migrations.yaml', + 'migrations.json' + ]; + foreach ($defaultConfig as $config) { + if ($this->configExists($config)) { + $outputWriter->write("Loading configuration from file: $config"); + + return $this->loadConfig($config, $outputWriter); + } + } + + return new Configuration($this->connection, $outputWriter); + } + + + private function configExists($config) + { + return file_exists($config); + } + + private function loadConfig($config, OutputWriter $outputWriter) + { + $map = array( + 'xml' => '\XmlConfiguration', + 'yaml' => '\YamlConfiguration', + 'yml' => '\YamlConfiguration', + 'php' => '\ArrayConfiguration', + 'json' => '\JsonConfiguration' + ); + + $info = pathinfo($config); + // check we can support this file type + if (empty($map[$info['extension']])) { + throw new \InvalidArgumentException('Given config file type is not supported'); + } + + $class = 'Doctrine\DBAL\Migrations\Configuration'; + $class .= $map[$info['extension']]; + $configuration = new $class($this->connection, $outputWriter); + $configuration->load($config); + + return $configuration; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'configuration'; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php new file mode 100644 index 000000000..2db0ae64b --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationDirectoryHelper.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Migrations\Tools\Console\Helper; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\OutputWriter; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Helper\Helper; + +/** + * Class ConfigurationHelper + * @package Doctrine\DBAL\Migrations\Tools\Console\Helper + * @internal + */ +class MigrationDirectoryHelper extends Helper +{ + + /** + * @var Configuration + */ + private $configuration; + + public function __construct(Configuration $configuration = null) + { + $this->configuration = $configuration; + } + + public function getMigrationDirectory() + { + $dir = $this->configuration->getMigrationsDirectory(); + $dir = $dir ? $dir : getcwd(); + $dir = rtrim($dir, '/'); + + if (!file_exists($dir)) { + throw new \InvalidArgumentException(sprintf('Migrations directory "%s" does not exist.', $dir)); + } + + if ($this->configuration->areMigrationsOrganizedByYear()) { + $dir .= $this->appendDir(date('Y')); + } + + if ($this->configuration->areMigrationsOrganizedByYearAndMonth()) { + $dir .= $this->appendDir(date('m')); + } + $this->createDirIfNotExists($dir); + + return $dir; + } + + private function appendDir($dir) + { + return DIRECTORY_SEPARATOR . $dir; + } + + private function createDirIfNotExists($dir) + { + if (!file_exists($dir)) { + mkdir($dir, 0755, true); + } + } + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + * + * @api + */ + public function getName() + { + return 'MigrationDirectory'; + } + + +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php new file mode 100644 index 000000000..9d682e348 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Tools/Console/Helper/MigrationStatusInfosHelper.php @@ -0,0 +1,86 @@ +configuration = $configuration; + $this->executedMigrations = $this->configuration->getMigratedVersions(); + $this->availableMigrations = $this->configuration->getAvailableVersions(); + $this->executedUnavailableMigrations = array_diff($this->executedMigrations, $this->availableMigrations); + } + + public function getMigrationsInfos() + { + $numExecutedUnavailableMigrations = count($this->executedUnavailableMigrations); + $numNewMigrations = count(array_diff($this->availableMigrations, $this->executedMigrations)); + + $infos = [ + 'Name' => $this->configuration->getName() ? $this->configuration->getName() : 'Doctrine Database Migrations', + 'Database Driver' => $this->configuration->getConnection()->getDriver()->getName(), + 'Database Name' => $this->configuration->getConnection()->getDatabase(), + 'Configuration Source' => $this->configuration instanceof AbstractFileConfiguration ? $this->configuration->getFile() : 'manually configured', + 'Version Table Name' => $this->configuration->getMigrationsTableName(), + 'Version Column Name' => $this->configuration->getMigrationsColumnName(), + 'Migrations Namespace' => $this->configuration->getMigrationsNamespace(), + 'Migrations Directory' => $this->configuration->getMigrationsDirectory(), + 'Previous Version' => $this->getFormattedVersionAlias('prev'), + 'Current Version' => $this->getFormattedVersionAlias('current'), + 'Next Version' => $this->getFormattedVersionAlias('next'), + 'Latest Version' => $this->getFormattedVersionAlias('latest'), + 'Executed Migrations' => count($this->executedMigrations), + 'Executed Unavailable Migrations' => $numExecutedUnavailableMigrations, + 'Available Migrations' => count($this->availableMigrations), + 'New Migrations' => $numNewMigrations, + ]; + + return $infos; + } + + private function getFormattedVersionAlias($alias) + { + $version = $this->configuration->resolveVersionAlias($alias); + //No version found + if ($version === null) { + if ($alias === 'next') { + return 'Already at latest version'; + } + + if ($alias === 'prev') { + return 'Already at first version'; + } + } + //Before first version "virtual" version number + if ($version === '0') { + return '0'; + } + + //Show normal version number + return $this->configuration->getDateTime($version) . ' (' . $version . ')'; + } + + /** @var Version[] */ + public function getExecutedUnavailableMigrations() + { + return $this->executedUnavailableMigrations; + } +} diff --git a/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php new file mode 100644 index 000000000..0dfdb3121 --- /dev/null +++ b/doctrine/migrations/lib/Doctrine/DBAL/Migrations/Version.php @@ -0,0 +1,428 @@ +. + */ + +namespace Doctrine\DBAL\Migrations; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Provider\LazySchemaDiffProvider; +use Doctrine\DBAL\Migrations\Provider\SchemaDiffProvider; +use Doctrine\DBAL\Migrations\Provider\SchemaDiffProviderInterface; +use ProxyManager\Factory\LazyLoadingValueHolderFactory; + +/** + * Class which wraps a migration version and allows execution of the + * individual migration version up or down method. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage + */ +class Version +{ + const STATE_NONE = 0; + const STATE_PRE = 1; + const STATE_EXEC = 2; + const STATE_POST = 3; + + const DIRECTION_UP = 'up'; + const DIRECTION_DOWN = 'down'; + + /** + * The Migrations Configuration instance for this migration + * + * @var Configuration + */ + private $configuration; + + /** + * The OutputWriter object instance used for outputting information + * + * @var OutputWriter + */ + private $outputWriter; + + /** + * The version in timestamp format (YYYYMMDDHHMMSS) + * + * @param int + */ + private $version; + + /** + * The migration instance for this version + * + * @var AbstractMigration + */ + private $migration; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * @var string + */ + private $class; + + /** The array of collected SQL statements for this version */ + private $sql = []; + + /** The array of collected parameters for SQL statements for this version */ + private $params = []; + + /** The array of collected types for SQL statements for this version */ + private $types = []; + + /** The time in seconds that this migration version took to execute */ + private $time; + + /** + * @var int + */ + private $state = self::STATE_NONE; + + /** @var SchemaDiffProviderInterface */ + private $schemaProvider; + + public function __construct(Configuration $configuration, $version, $class, SchemaDiffProviderInterface $schemaProvider=null) + { + $this->configuration = $configuration; + $this->outputWriter = $configuration->getOutputWriter(); + $this->class = $class; + $this->connection = $configuration->getConnection(); + $this->migration = new $class($this); + $this->version = $version; + + if ($schemaProvider !== null) { + $this->schemaProvider = $schemaProvider; + } + if($schemaProvider === null) { + $schemaProvider = new SchemaDiffProvider($this->connection->getSchemaManager(), + $this->connection->getDatabasePlatform()); + $this->schemaProvider = LazySchemaDiffProvider::fromDefaultProxyFacyoryConfiguration($schemaProvider); + } + } + + /** + * Returns the string version in the format YYYYMMDDHHMMSS + * + * @return string $version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns the Migrations Configuration object instance + * + * @return Configuration $configuration + */ + public function getConfiguration() + { + return $this->configuration; + } + + /** + * Check if this version has been migrated or not. + * + * @return boolean + */ + public function isMigrated() + { + return $this->configuration->hasVersionMigrated($this); + } + + public function markMigrated() + { + $this->markVersion('up'); + } + + private function markVersion($direction) + { + $action = $direction === 'up' ? 'insert' : 'delete'; + + $this->configuration->createMigrationTable(); + $this->connection->$action( + $this->configuration->getMigrationsTableName(), + [$this->configuration->getMigrationsColumnName() => $this->version] + ); + } + + public function markNotMigrated() + { + $this->markVersion('down'); + } + + /** + * Add some SQL queries to this versions migration + * + * @param array|string $sql + * @param array $params + * @param array $types + */ + public function addSql($sql, array $params = [], array $types = []) + { + if (is_array($sql)) { + foreach ($sql as $key => $query) { + $this->sql[] = $query; + if (!empty($params[$key])) { + $queryTypes = isset($types[$key]) ? $types[$key] : []; + $this->addQueryParams($params[$key], $queryTypes); + } + } + } else { + $this->sql[] = $sql; + if (!empty($params)) { + $this->addQueryParams($params, $types); + } + } + } + + /** + * @param mixed[] $params Array of prepared statement parameters + * @param string[] $types Array of the types of each statement parameters + */ + private function addQueryParams($params, $types) + { + $index = count($this->sql) - 1; + $this->params[$index] = $params; + $this->types[$index] = $types; + } + + /** + * Write a migration SQL file to the given path + * + * @param string $path The path to write the migration SQL file. + * @param string $direction The direction to execute. + * + * @return boolean $written + */ + public function writeSqlFile($path, $direction = self::DIRECTION_UP) + { + $queries = $this->execute($direction, true); + + if ( ! empty($this->params)) { + throw MigrationException::migrationNotConvertibleToSql($this->class); + } + + $this->outputWriter->write("\n# Version " . $this->version . "\n"); + + $sqlQueries = [$this->version => $queries]; + $sqlWriter = new SqlFileWriter( + $this->configuration->getMigrationsColumnName(), + $this->configuration->getMigrationsTableName(), + $path, + $this->outputWriter + ); + + return $sqlWriter->write($sqlQueries, $direction); + } + + /** + * @return AbstractMigration + */ + public function getMigration() + { + return $this->migration; + } + + /** + * Execute this migration version up or down and and return the SQL. + * We are only allowing the addSql call and the schema modification to take effect in the up and down call. + * This is necessary to ensure that the migration is revertable. + * The schema is passed to the pre and post method only to be able to test the presence of some table, And the + * connection that can get used trough it allow for the test of the presence of records. + * + * @param string $direction The direction to execute the migration. + * @param boolean $dryRun Whether to not actually execute the migration SQL and just do a dry run. + * @param boolean $timeAllQueries Measuring or not the execution time of each SQL query. + * + * @return array $sql + * + * @throws \Exception when migration fails + */ + public function execute($direction, $dryRun = false, $timeAllQueries = false) + { + $this->sql = []; + + $transaction = $this->migration->isTransactional(); + if ($transaction) { + //only start transaction if in transactional mode + $this->connection->beginTransaction(); + } + + try { + $migrationStart = microtime(true); + + $this->state = self::STATE_PRE; + $fromSchema = $this->schemaProvider->createFromSchema(); + + $this->migration->{'pre' . ucfirst($direction)}($fromSchema); + + if ($direction === self::DIRECTION_UP) { + $this->outputWriter->write("\n" . sprintf(' ++ migrating %s', $this->version) . "\n"); + } else { + $this->outputWriter->write("\n" . sprintf(' -- reverting %s', $this->version) . "\n"); + } + + $this->state = self::STATE_EXEC; + + $toSchema = $this->schemaProvider->createToSchema($fromSchema); + $this->migration->$direction($toSchema); + + $this->addSql($this->schemaProvider->getSqlDiffToMigrate($fromSchema, $toSchema)); + + $this->executeRegisteredSql($dryRun, $timeAllQueries); + + $this->state = self::STATE_POST; + $this->migration->{'post' . ucfirst($direction)}($toSchema); + + if (! $dryRun) { + if ($direction === self::DIRECTION_UP) { + $this->markMigrated(); + } else { + $this->markNotMigrated(); + } + } + + $migrationEnd = microtime(true); + $this->time = round($migrationEnd - $migrationStart, 2); + if ($direction === self::DIRECTION_UP) { + $this->outputWriter->write(sprintf("\n ++ migrated (%ss)", $this->time)); + } else { + $this->outputWriter->write(sprintf("\n -- reverted (%ss)", $this->time)); + } + + if ($transaction) { + //commit only if running in transactional mode + $this->connection->commit(); + } + + $this->state = self::STATE_NONE; + + return $this->sql; + } catch (SkipMigrationException $e) { + if ($transaction) { + //only rollback transaction if in transactional mode + $this->connection->rollback(); + } + + if ($dryRun === false) { + // now mark it as migrated + if ($direction === self::DIRECTION_UP) { + $this->markMigrated(); + } else { + $this->markNotMigrated(); + } + } + + $this->outputWriter->write(sprintf("\n SS skipped (Reason: %s)", $e->getMessage())); + + $this->state = self::STATE_NONE; + + return []; + } catch (\Exception $e) { + + $this->outputWriter->write(sprintf( + 'Migration %s failed during %s. Error %s', + $this->version, $this->getExecutionState(), $e->getMessage() + )); + + if ($transaction) { + //only rollback transaction if in transactional mode + $this->connection->rollback(); + } + + $this->state = self::STATE_NONE; + throw $e; + } + } + + public function getExecutionState() + { + switch ($this->state) { + case self::STATE_PRE: + return 'Pre-Checks'; + case self::STATE_POST: + return 'Post-Checks'; + case self::STATE_EXEC: + return 'Execution'; + default: + return 'No State'; + } + } + + private function outputQueryTime($queryStart, $timeAllQueries = false) + { + if ($timeAllQueries !== false) { + $queryEnd = microtime(true); + $queryTime = round($queryEnd - $queryStart, 4); + + $this->outputWriter->write(sprintf(" %ss", $queryTime)); + } + } + + /** + * Returns the time this migration version took to execute + * + * @return integer $time The time this migration version took to execute + */ + public function getTime() + { + return $this->time; + } + + public function __toString() + { + return $this->version; + } + + private function executeRegisteredSql($dryRun = false, $timeAllQueries = false) + { + if (! $dryRun) { + if (!empty($this->sql)) { + foreach ($this->sql as $key => $query) { + $queryStart = microtime(true); + + if ( ! isset($this->params[$key])) { + $this->outputWriter->write(' -> ' . $query); + $this->connection->executeQuery($query); + } else { + $this->outputWriter->write(sprintf(' - %s (with parameters)', $query)); + $this->connection->executeQuery($query, $this->params[$key], $this->types[$key]); + } + + $this->outputQueryTime($queryStart, $timeAllQueries); + } + } else { + $this->outputWriter->write(sprintf( + 'Migration %s was executed but did not result in any SQL statements.', + $this->version + )); + } + } else { + foreach ($this->sql as $query) { + $this->outputWriter->write(' -> ' . $query); + } + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php b/ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php new file mode 100644 index 000000000..91a6fc282 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Autoloader/Autoloader.php @@ -0,0 +1,69 @@ + + * @license MIT + */ +class Autoloader implements AutoloaderInterface +{ + /** + * @var \ProxyManager\FileLocator\FileLocatorInterface + */ + protected $fileLocator; + + /** + * @var \ProxyManager\Inflector\ClassNameInflectorInterface + */ + protected $classNameInflector; + + /** + * @param \ProxyManager\FileLocator\FileLocatorInterface $fileLocator + * @param \ProxyManager\Inflector\ClassNameInflectorInterface $classNameInflector + */ + public function __construct(FileLocatorInterface $fileLocator, ClassNameInflectorInterface $classNameInflector) + { + $this->fileLocator = $fileLocator; + $this->classNameInflector = $classNameInflector; + } + + /** + * {@inheritDoc} + */ + public function __invoke($className) + { + if (class_exists($className, false) || ! $this->classNameInflector->isProxyClassName($className)) { + return false; + } + + $file = $this->fileLocator->getProxyFileName($className); + + if (! file_exists($file)) { + return false; + } + + return (bool) require_once $file; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php b/ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php new file mode 100644 index 000000000..9670f98b8 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Autoloader/AutoloaderInterface.php @@ -0,0 +1,37 @@ + + * @license MIT + */ +interface AutoloaderInterface +{ + /** + * Callback to allow the object to be handled as autoloader - tries to autoload the given class name + * + * @param string $className + * + * @return bool + */ + public function __invoke($className); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Configuration.php b/ocramius/proxy-manager/src/ProxyManager/Configuration.php new file mode 100644 index 000000000..3bf1d9a72 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Configuration.php @@ -0,0 +1,241 @@ + + * @license MIT + */ +class Configuration +{ + const DEFAULT_PROXY_NAMESPACE = 'ProxyManagerGeneratedProxy'; + + /** + * @var string|null + */ + protected $proxiesTargetDir; + + /** + * @var string + */ + protected $proxiesNamespace = self::DEFAULT_PROXY_NAMESPACE; + + /** + * @var GeneratorStrategyInterface|null + */ + protected $generatorStrategy; + + /** + * @var callable|null + */ + protected $proxyAutoloader; + + /** + * @var ClassNameInflectorInterface|null + */ + protected $classNameInflector; + + /** + * @var SignatureGeneratorInterface|null + */ + protected $signatureGenerator; + + /** + * @var SignatureCheckerInterface|null + */ + protected $signatureChecker; + + /** + * @var ClassSignatureGeneratorInterface|null + */ + protected $classSignatureGenerator; + + /** + * @deprecated deprecated since version 0.5 + * @codeCoverageIgnore + */ + public function setAutoGenerateProxies() + { + } + + /** + * @return bool + * + * @deprecated deprecated since version 0.5 + * @codeCoverageIgnore + */ + public function doesAutoGenerateProxies() + { + return true; + } + + /** + * @param AutoloaderInterface $proxyAutoloader + */ + public function setProxyAutoloader(AutoloaderInterface $proxyAutoloader) + { + $this->proxyAutoloader = $proxyAutoloader; + } + + /** + * @return AutoloaderInterface + */ + public function getProxyAutoloader() + { + return $this->proxyAutoloader + ?: $this->proxyAutoloader = new Autoloader( + new FileLocator($this->getProxiesTargetDir()), + $this->getClassNameInflector() + ); + } + + /** + * @param string $proxiesNamespace + */ + public function setProxiesNamespace($proxiesNamespace) + { + $this->proxiesNamespace = $proxiesNamespace; + } + + /** + * @return string + */ + public function getProxiesNamespace() + { + return $this->proxiesNamespace; + } + + /** + * @param string $proxiesTargetDir + */ + public function setProxiesTargetDir($proxiesTargetDir) + { + $this->proxiesTargetDir = (string) $proxiesTargetDir; + } + + /** + * @return string + */ + public function getProxiesTargetDir() + { + return $this->proxiesTargetDir ?: $this->proxiesTargetDir = sys_get_temp_dir(); + } + + /** + * @param GeneratorStrategyInterface $generatorStrategy + */ + public function setGeneratorStrategy(GeneratorStrategyInterface $generatorStrategy) + { + $this->generatorStrategy = $generatorStrategy; + } + + /** + * @return GeneratorStrategyInterface + */ + public function getGeneratorStrategy() + { + return $this->generatorStrategy + ?: $this->generatorStrategy = new FileWriterGeneratorStrategy( + new FileLocator($this->getProxiesTargetDir()) + ); + } + + /** + * @param ClassNameInflectorInterface $classNameInflector + */ + public function setClassNameInflector(ClassNameInflectorInterface $classNameInflector) + { + $this->classNameInflector = $classNameInflector; + } + + /** + * @return ClassNameInflectorInterface + */ + public function getClassNameInflector() + { + return $this->classNameInflector + ?: $this->classNameInflector = new ClassNameInflector($this->getProxiesNamespace()); + } + + /** + * @param SignatureGeneratorInterface $signatureGenerator + */ + public function setSignatureGenerator(SignatureGeneratorInterface $signatureGenerator) + { + $this->signatureGenerator = $signatureGenerator; + } + + /** + * @return SignatureGeneratorInterface + */ + public function getSignatureGenerator() + { + return $this->signatureGenerator ?: $this->signatureGenerator = new SignatureGenerator(); + } + + /** + * @param SignatureCheckerInterface $signatureChecker + */ + public function setSignatureChecker(SignatureCheckerInterface $signatureChecker) + { + $this->signatureChecker = $signatureChecker; + } + + /** + * @return SignatureCheckerInterface + */ + public function getSignatureChecker() + { + return $this->signatureChecker + ?: $this->signatureChecker = new SignatureChecker($this->getSignatureGenerator()); + } + + /** + * @param ClassSignatureGeneratorInterface $classSignatureGenerator + */ + public function setClassSignatureGenerator(ClassSignatureGeneratorInterface $classSignatureGenerator) + { + $this->classSignatureGenerator = $classSignatureGenerator; + } + + /** + * @return ClassSignatureGeneratorInterface + */ + public function getClassSignatureGenerator() + { + return $this->classSignatureGenerator + ?: new ClassSignatureGenerator($this->getSignatureGenerator()); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php b/ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php new file mode 100644 index 000000000..42f4eda07 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/DisabledMethodException.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class DisabledMethodException extends BadMethodCallException implements ExceptionInterface +{ + const NAME = __CLASS__; + + /** + * @param string $method + * + * @return self + */ + public static function disabledMethod($method) + { + return new self(sprintf('Method "%s" is forcefully disabled', (string) $method)); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php b/ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php new file mode 100644 index 000000000..34c7d8687 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface ExceptionInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php b/ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php new file mode 100644 index 000000000..7d9a99f83 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/FileNotWritableException.php @@ -0,0 +1,69 @@ + + * @license MIT + */ +class FileNotWritableException extends UnexpectedValueException implements ExceptionInterface +{ + /** + * @param string $fromPath + * @param string $toPath + * + * @return self + */ + public static function fromInvalidMoveOperation($fromPath, $toPath) + { + return new self(sprintf( + 'Could not move file "%s" to location "%s": ' + . 'either the source file is not readable, or the destination is not writable', + $fromPath, + $toPath + )); + } + + /** + * @param string $path + * + * @return self + */ + public static function fromNonWritableLocation($path) + { + $messages = array(); + + if (($destination = realpath($path)) && ! is_file($destination)) { + $messages[] = 'exists and is not a file'; + } + + if (! is_writable($destination)) { + $messages[] = 'is not writable'; + } + + return new self(sprintf('Could not write to path "%s": %s', $path, implode(', ', $messages))); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php b/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php new file mode 100644 index 000000000..5dc08d14d --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxiedClassException.php @@ -0,0 +1,79 @@ + + * @license MIT + */ +class InvalidProxiedClassException extends InvalidArgumentException implements ExceptionInterface +{ + /** + * @param ReflectionClass $reflection + * + * @return self + */ + public static function interfaceNotSupported(ReflectionClass $reflection) + { + return new self(sprintf('Provided interface "%s" cannot be proxied', $reflection->getName())); + } + + /** + * @param ReflectionClass $reflection + * + * @return self + */ + public static function finalClassNotSupported(ReflectionClass $reflection) + { + return new self(sprintf('Provided class "%s" is final and cannot be proxied', $reflection->getName())); + } + + /** + * @param ReflectionClass $reflection + * + * @return self + */ + public static function abstractProtectedMethodsNotSupported(ReflectionClass $reflection) + { + return new self(sprintf( + 'Provided class "%s" has following protected abstract methods, and therefore cannot be proxied:' . "\n%s", + $reflection->getName(), + implode( + "\n", + array_map( + function (ReflectionMethod $reflectionMethod) { + return $reflectionMethod->getDeclaringClass()->getName() . '::' . $reflectionMethod->getName(); + }, + array_filter( + $reflection->getMethods(), + function (ReflectionMethod $method) { + return $method->isAbstract() && $method->isProtected(); + } + ) + ) + ) + )); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php b/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php new file mode 100644 index 000000000..cb5d0ea0f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/InvalidProxyDirectoryException.php @@ -0,0 +1,40 @@ + + * @license MIT + */ +class InvalidProxyDirectoryException extends InvalidArgumentException implements ExceptionInterface +{ + /** + * @param string $directory + * + * @return self + */ + public static function proxyDirectoryNotFound($directory) + { + return new self(sprintf('Provided directory "%s" does not exist', (string) $directory)); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php b/ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php new file mode 100644 index 000000000..68f8215f7 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Exception/UnsupportedProxiedClassException.php @@ -0,0 +1,47 @@ + + * @license MIT + */ +class UnsupportedProxiedClassException extends LogicException implements ExceptionInterface +{ + /** + * @param ReflectionProperty $property + * + * @return self + */ + public static function unsupportedLocalizedReflectionProperty(ReflectionProperty $property) + { + return new self( + sprintf( + 'Provided reflection property "%s" of class "%s" is private and cannot be localized in PHP 5.3', + $property->getName(), + $property->getDeclaringClass()->getName() + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php new file mode 100644 index 000000000..cbc7b1a52 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractBaseFactory.php @@ -0,0 +1,112 @@ + + * @license MIT + */ +abstract class AbstractBaseFactory +{ + /** + * @var \ProxyManager\Configuration + */ + protected $configuration; + + /** + * Cached checked class names + * + * @var string[] + */ + private $checkedClasses = array(); + + /** + * @param \ProxyManager\Configuration $configuration + */ + public function __construct(Configuration $configuration = null) + { + $this->configuration = $configuration ?: new Configuration(); + } + + /** + * Generate a proxy from a class name + * @param string $className + * @return string proxy class name + */ + protected function generateProxy($className) + { + if (isset($this->checkedClasses[$className])) { + return $this->checkedClasses[$className]; + } + + $proxyParameters = array( + 'className' => $className, + 'factory' => get_class($this), + 'proxyManagerVersion' => Version::VERSION + ); + $proxyClassName = $this + ->configuration + ->getClassNameInflector() + ->getProxyClassName($className, $proxyParameters); + + if (! class_exists($proxyClassName)) { + $this->generateProxyClass($proxyClassName, $className, $proxyParameters); + } + + $this + ->configuration + ->getSignatureChecker() + ->checkSignature(new ReflectionClass($proxyClassName), $proxyParameters); + + return $this->checkedClasses[$className] = $proxyClassName; + } + + /** + * @return \ProxyManager\ProxyGenerator\ProxyGeneratorInterface + */ + abstract protected function getGenerator(); + + /** + * Generates the provided `$proxyClassName` from the given `$className` and `$proxyParameters` + * @param string $proxyClassName + * @param string $className + * @param array $proxyParameters + * + * @return void + */ + private function generateProxyClass($proxyClassName, $className, array $proxyParameters) + { + $className = $this->configuration->getClassNameInflector()->getUserClassName($className); + $phpClass = new ClassGenerator($proxyClassName); + + $this->getGenerator()->generate(new ReflectionClass($className), $phpClass); + + $phpClass = $this->configuration->getClassSignatureGenerator()->addSignature($phpClass, $proxyParameters); + + $this->configuration->getGeneratorStrategy()->generate($phpClass); + $this->configuration->getProxyAutoloader()->__invoke($proxyClassName); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php new file mode 100644 index 000000000..690bfa15c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/AbstractLazyFactory.php @@ -0,0 +1,46 @@ + + * @license MIT + */ +abstract class AbstractLazyFactory extends AbstractBaseFactory +{ + /** + * Creates a new lazy proxy instance of the given class with + * the given initializer + * + * @param string $className name of the class to be proxied + * @param \Closure $initializer initializer to be passed to the proxy + * + * @return \ProxyManager\Proxy\LazyLoadingInterface + */ + public function createProxy($className, Closure $initializer) + { + $proxyClassName = $this->generateProxy($className); + + return new $proxyClassName($initializer); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php new file mode 100644 index 000000000..57d78e7e2 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorScopeLocalizerFactory.php @@ -0,0 +1,59 @@ + + * @license MIT + */ +class AccessInterceptorScopeLocalizerFactory extends AbstractBaseFactory +{ + /** + * @var \ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator|null + */ + private $generator; + + /** + * @param object $instance the object to be localized within the access interceptor + * @param \Closure[] $prefixInterceptors an array (indexed by method name) of interceptor closures to be called + * before method logic is executed + * @param \Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called + * after method logic is executed + * + * @return \ProxyManager\Proxy\AccessInterceptorInterface + */ + public function createProxy($instance, array $prefixInterceptors = array(), array $suffixInterceptors = array()) + { + $proxyClassName = $this->generateProxy(get_class($instance)); + + return new $proxyClassName($instance, $prefixInterceptors, $suffixInterceptors); + } + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new AccessInterceptorScopeLocalizerGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php new file mode 100644 index 000000000..d5ef5044a --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/AccessInterceptorValueHolderFactory.php @@ -0,0 +1,59 @@ + + * @license MIT + */ +class AccessInterceptorValueHolderFactory extends AbstractBaseFactory +{ + /** + * @var \ProxyManager\ProxyGenerator\AccessInterceptorValueHolderGenerator|null + */ + private $generator; + + /** + * @param object $instance the object to be wrapped within the value holder + * @param \Closure[] $prefixInterceptors an array (indexed by method name) of interceptor closures to be called + * before method logic is executed + * @param \Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called + * after method logic is executed + * + * @return \ProxyManager\Proxy\AccessInterceptorInterface|\ProxyManager\Proxy\ValueHolderInterface + */ + public function createProxy($instance, array $prefixInterceptors = array(), array $suffixInterceptors = array()) + { + $proxyClassName = $this->generateProxy(get_class($instance)); + + return new $proxyClassName($instance, $prefixInterceptors, $suffixInterceptors); + } + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new AccessInterceptorValueHolderGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php new file mode 100644 index 000000000..73e030c78 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingGhostFactory.php @@ -0,0 +1,45 @@ + + * @license MIT + * + * @method \ProxyManager\Proxy\GhostObjectInterface createProxy($className, \Closure $initializer) + */ +class LazyLoadingGhostFactory extends AbstractLazyFactory +{ + /** + * @var \ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator|null + */ + private $generator; + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new LazyLoadingGhostGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php new file mode 100644 index 000000000..c1449be73 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php @@ -0,0 +1,45 @@ + + * @license MIT + * + * @method \ProxyManager\Proxy\VirtualProxyInterface createProxy($className, \Closure $initializer) + */ +class LazyLoadingValueHolderFactory extends AbstractLazyFactory +{ + /** + * @var \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator|null + */ + private $generator; + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new LazyLoadingValueHolderGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php new file mode 100644 index 000000000..9c605cbaf --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/NullObjectFactory.php @@ -0,0 +1,56 @@ + + * @license MIT + */ +class NullObjectFactory extends AbstractBaseFactory +{ + /** + * @var \ProxyManager\ProxyGenerator\NullObjectGenerator|null + */ + private $generator; + + /** + * @param object $instanceOrClassName the object to be wrapped or interface to transform to null object + * + * @return \ProxyManager\Proxy\NullobjectInterface + */ + public function createProxy($instanceOrClassName) + { + $className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName; + $proxyClassName = $this->generateProxy($className); + + return new $proxyClassName(); + } + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new NullObjectGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php new file mode 100644 index 000000000..814d327ea --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/BaseAdapter.php @@ -0,0 +1,81 @@ + + * @license MIT + */ +abstract class BaseAdapter implements AdapterInterface +{ + /** + * Adapter client + * + * @var \Zend\Server\Client + */ + protected $client; + + /** + * Service name mapping + * + * @var string[] + */ + protected $map = array(); + + /** + * Constructor + * + * @param Client $client + * @param array $map map of service names to their aliases + */ + public function __construct(Client $client, array $map = array()) + { + $this->client = $client; + $this->map = $map; + } + + /** + * {@inheritDoc} + */ + public function call($wrappedClass, $method, array $params = array()) + { + $serviceName = $this->getServiceName($wrappedClass, $method); + + if (isset($this->map[$serviceName])) { + $serviceName = $this->map[$serviceName]; + } + + return $this->client->call($serviceName, $params); + } + + /** + * Get the service name will be used by the adapter + * + * @param string $wrappedClass + * @param string $method + * + * @return string Service name + */ + abstract protected function getServiceName($wrappedClass, $method); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php new file mode 100644 index 000000000..aff27002c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/JsonRpc.php @@ -0,0 +1,36 @@ + + * @license MIT + */ +class JsonRpc extends BaseAdapter +{ + /** + * {@inheritDoc} + */ + protected function getServiceName($wrappedClass, $method) + { + return $wrappedClass . '.' . $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php new file mode 100644 index 000000000..69de38623 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/Soap.php @@ -0,0 +1,36 @@ + + * @license MIT + */ +class Soap extends BaseAdapter +{ + /** + * {@inheritDoc} + */ + protected function getServiceName($wrappedClass, $method) + { + return (string) $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php new file mode 100644 index 000000000..0485ccda5 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/Adapter/XmlRpc.php @@ -0,0 +1,36 @@ + + * @license MIT + */ +class XmlRpc extends BaseAdapter +{ + /** + * {@inheritDoc} + */ + protected function getServiceName($wrappedClass, $method) + { + return $wrappedClass . '.' . $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php new file mode 100644 index 000000000..4168f9d78 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObject/AdapterInterface.php @@ -0,0 +1,37 @@ + + * @license MIT + */ +interface AdapterInterface +{ + /** + * Call remote object + * + * @param string $wrappedClass + * @param string $method + * @param array $params + */ + public function call($wrappedClass, $method, array $params = array()); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php new file mode 100644 index 000000000..52778d3ce --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Factory/RemoteObjectFactory.php @@ -0,0 +1,76 @@ + + * @license MIT + */ +class RemoteObjectFactory extends AbstractBaseFactory +{ + /** + * @var AdapterInterface + */ + protected $adapter; + + /** + * @var \ProxyManager\ProxyGenerator\RemoteObjectGenerator|null + */ + private $generator; + + /** + * {@inheritDoc} + * + * @param AdapterInterface $adapter + * @param Configuration $configuration + */ + public function __construct(AdapterInterface $adapter, Configuration $configuration = null) + { + parent::__construct($configuration); + + $this->adapter = $adapter; + } + + /** + * @param string|object $instanceOrClassName + * + * @return \ProxyManager\Proxy\RemoteObjectInterface + */ + public function createProxy($instanceOrClassName) + { + $className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName; + $proxyClassName = $this->generateProxy($className); + + return new $proxyClassName($this->adapter); + } + + /** + * {@inheritDoc} + */ + protected function getGenerator() + { + return $this->generator ?: $this->generator = new RemoteObjectGenerator(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php b/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php new file mode 100644 index 000000000..197595ead --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocator.php @@ -0,0 +1,57 @@ + + * @license MIT + */ +class FileLocator implements FileLocatorInterface +{ + /** + * @var string + */ + protected $proxiesDirectory; + + /** + * @param string $proxiesDirectory + * + * @throws \ProxyManager\Exception\InvalidProxyDirectoryException + */ + public function __construct($proxiesDirectory) + { + $this->proxiesDirectory = realpath($proxiesDirectory); + + if (false === $this->proxiesDirectory) { + throw InvalidProxyDirectoryException::proxyDirectoryNotFound($proxiesDirectory); + } + } + + /** + * {@inheritDoc} + */ + public function getProxyFileName($className) + { + return $this->proxiesDirectory . DIRECTORY_SEPARATOR . str_replace('\\', '', $className) . '.php'; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php b/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php new file mode 100644 index 000000000..c52ac0281 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/FileLocator/FileLocatorInterface.php @@ -0,0 +1,37 @@ + + * @license MIT + */ +interface FileLocatorInterface +{ + /** + * Retrieves the file name for the given proxy + * + * @param string $className + * + * @return string + */ + public function getProxyFileName($className); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php new file mode 100644 index 000000000..0304c42f2 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/ClassGenerator.php @@ -0,0 +1,54 @@ + + * @license MIT + */ +class ClassGenerator extends ZendClassGenerator +{ + /** + * {@inheritDoc} + */ + public function setExtendedClass($extendedClass) + { + if ($extendedClass) { + $extendedClass = '\\' . trim($extendedClass, '\\'); + } + + return parent::setExtendedClass($extendedClass); + } + + /** + * {@inheritDoc} + */ + public function setImplementedInterfaces(array $interfaces) + { + foreach ($interfaces as & $interface) { + $interface = '\\' . trim($interface, '\\'); + } + + return parent::setImplementedInterfaces($interfaces); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php new file mode 100644 index 000000000..462aa8f8e --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/MagicMethodGenerator.php @@ -0,0 +1,52 @@ + + * @license MIT + */ +class MagicMethodGenerator extends MethodGenerator +{ + /** + * @param ReflectionClass $originalClass + * @param string $name + * @param array $parameters + */ + public function __construct(ReflectionClass $originalClass, $name, array $parameters = array()) + { + parent::__construct( + $name, + $parameters, + static::FLAG_PUBLIC, + null, + $originalClass->hasMethod($name) ? '{@inheritDoc}' : null + ); + + $this->setReturnsReference(strtolower($name) === '__get'); + + if ($originalClass->hasMethod($name)) { + $this->setReturnsReference($originalClass->getMethod($name)->returnsReference()); + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php new file mode 100644 index 000000000..cffd6afdf --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/MethodGenerator.php @@ -0,0 +1,161 @@ + + * @license MIT + */ +class MethodGenerator extends ZendMethodGenerator +{ + /** + * @var bool + */ + protected $returnsReference = false; + + /** + * @param boolean $returnsReference + */ + public function setReturnsReference($returnsReference) + { + $this->returnsReference = (bool) $returnsReference; + } + + /** + * @return boolean + */ + public function returnsReference() + { + return $this->returnsReference; + } + + /** + * @override enforces generation of \ProxyManager\Generator\MethodGenerator + * + * {@inheritDoc} + */ + public static function fromReflection(MethodReflection $reflectionMethod) + { + /* @var $method self */ + $method = new static(); + + $method->setSourceContent($reflectionMethod->getContents(false)); + $method->setSourceDirty(false); + + if ($reflectionMethod->getDocComment() != '') { + $method->setDocBlock(DocBlockGenerator::fromReflection($reflectionMethod->getDocBlock())); + } + + $method->setFinal($reflectionMethod->isFinal()); + $method->setVisibility(self::extractVisibility($reflectionMethod)); + + foreach ($reflectionMethod->getParameters() as $reflectionParameter) { + $method->setParameter(ParameterGenerator::fromReflection($reflectionParameter)); + } + + $method->setStatic($reflectionMethod->isStatic()); + $method->setName($reflectionMethod->getName()); + $method->setBody($reflectionMethod->getBody()); + $method->setReturnsReference($reflectionMethod->returnsReference()); + + return $method; + } + + /** + * Retrieves the visibility for the given method reflection + * + * @param MethodReflection $reflectionMethod + * + * @return string + */ + private static function extractVisibility(MethodReflection $reflectionMethod) + { + if ($reflectionMethod->isPrivate()) { + return static::VISIBILITY_PRIVATE; + } + + if ($reflectionMethod->isProtected()) { + return static::VISIBILITY_PROTECTED; + } + + return static::VISIBILITY_PUBLIC; + } + + /** + * @override fixes by-reference return value in zf2's method generator + * + * {@inheritDoc} + */ + public function generate() + { + $output = ''; + $indent = $this->getIndentation(); + + if (null !== ($docBlock = $this->getDocBlock())) { + $docBlock->setIndentation($indent); + + $output .= $docBlock->generate(); + } + + $output .= $indent . $this->generateMethodDeclaration() . self::LINE_FEED . $indent . '{' . self::LINE_FEED; + + if ($this->body) { + $output .= preg_replace('#^(.+?)$#m', $indent . $indent . '$1', trim($this->body)) + . self::LINE_FEED; + } + + $output .= $indent . '}' . self::LINE_FEED; + + return $output; + } + + /** + * @return string + */ + private function generateMethodDeclaration() + { + $output = $this->generateVisibility() + . ' function ' + . (($this->returnsReference()) ? '& ' : '') + . $this->getName() . '('; + + $parameterOutput = array(); + + foreach ($this->getParameters() as $parameter) { + $parameterOutput[] = $parameter->generate(); + } + + return $output . implode(', ', $parameterOutput) . ')'; + } + + /** + * @return string + */ + private function generateVisibility() + { + return $this->isAbstract() ? 'abstract ' : (($this->isFinal()) ? 'final ' : '') + . ($this->getVisibility() . (($this->isStatic()) ? ' static' : '')); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php new file mode 100644 index 000000000..7960d42d1 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/ParameterGenerator.php @@ -0,0 +1,149 @@ + + * @license MIT + */ +class ParameterGenerator extends ZendParameterGenerator +{ + /** + * @override - uses `static` to instantiate the parameter + * + * {@inheritDoc} + */ + public static function fromReflection(ParameterReflection $reflectionParameter) + { + /* @var $param self */ + $param = new static(); + + $param->setName($reflectionParameter->getName()); + $param->setPosition($reflectionParameter->getPosition()); + + $type = self::extractParameterType($reflectionParameter); + + if (null !== $type) { + $param->setType($type); + } + + self::setOptionalParameter($param, $reflectionParameter); + + $param->setPassedByReference($reflectionParameter->isPassedByReference()); + + return $param; + } + + /** + * Retrieves the type of a reflection parameter (null if none is found) + * + * @param ParameterReflection $reflectionParameter + * + * @return string|null + */ + private static function extractParameterType(ParameterReflection $reflectionParameter) + { + if ($reflectionParameter->isArray()) { + return 'array'; + } + + if (method_exists($reflectionParameter, 'isCallable') && $reflectionParameter->isCallable()) { + return 'callable'; + } + + if ($typeClass = $reflectionParameter->getClass()) { + return $typeClass->getName(); + } + + return null; + } + + /** + * @return string + */ + public function generate() + { + return $this->getGeneratedType() + . (true === $this->passedByReference ? '&' : '') + . '$' . $this->name + . $this->generateDefaultValue(); + } + + /** + * @return string + */ + private function generateDefaultValue() + { + if (null === $this->defaultValue) { + return ''; + } + + $defaultValue = $this->defaultValue instanceof ValueGenerator + ? $this->defaultValue + : new ValueGenerator($this->defaultValue); + + $defaultValue->setOutputMode(ValueGenerator::OUTPUT_SINGLE_LINE); + + return ' = ' . $defaultValue; + } + + /** + * Retrieves the generated parameter type + * + * @return string + */ + private function getGeneratedType() + { + if (! $this->type || in_array($this->type, static::$simple)) { + return ''; + } + + if ('array' === $this->type || 'callable' === $this->type) { + return $this->type . ' '; + } + + return '\\' . trim($this->type, '\\') . ' '; + } + + /** + * Set the default value for a parameter (if it is optional) + * + * @param ZendParameterGenerator $parameterGenerator + * @param ParameterReflection $reflectionParameter + */ + private static function setOptionalParameter( + ZendParameterGenerator $parameterGenerator, + ParameterReflection $reflectionParameter + ) { + if ($reflectionParameter->isOptional()) { + try { + $parameterGenerator->setDefaultValue($reflectionParameter->getDefaultValue()); + } catch (ReflectionException $e) { + $parameterGenerator->setDefaultValue(null); + } + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php b/ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php new file mode 100644 index 000000000..62e130c13 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/Util/ClassGeneratorUtils.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +final class ClassGeneratorUtils +{ + /** + * @param ReflectionClass $originalClass + * @param ClassGenerator $classGenerator + * @param MethodGenerator $generatedMethod + * + * @return void|false + */ + public static function addMethodIfNotFinal( + ReflectionClass $originalClass, + ClassGenerator $classGenerator, + MethodGenerator $generatedMethod + ) { + $methodName = $generatedMethod->getName(); + + if ($originalClass->hasMethod($methodName) && $originalClass->getMethod($methodName)->isFinal()) { + return false; + } + + $classGenerator->addMethodFromGenerator($generatedMethod); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php new file mode 100644 index 000000000..4bb2b7711 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Generator/Util/UniqueIdentifierGenerator.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +abstract class UniqueIdentifierGenerator +{ + const VALID_IDENTIFIER_FORMAT = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]+$/'; + const DEFAULT_IDENTIFIER = 'g'; + + /** + * Generates a valid unique identifier from the given name + * + * @param string $name + * + * @return string + */ + public static function getIdentifier($name) + { + return str_replace( + '.', + '', + uniqid( + preg_match(static::VALID_IDENTIFIER_FORMAT, $name) + ? $name + : static::DEFAULT_IDENTIFIER, + true + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php b/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php new file mode 100644 index 000000000..1e7712080 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/BaseGeneratorStrategy.php @@ -0,0 +1,38 @@ + + * @license MIT + */ +class BaseGeneratorStrategy implements GeneratorStrategyInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ClassGenerator $classGenerator) + { + return $classGenerator->generate(); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php b/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php new file mode 100644 index 000000000..bdb4688c0 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php @@ -0,0 +1,69 @@ + + * @license MIT + */ +class EvaluatingGeneratorStrategy implements GeneratorStrategyInterface +{ + /** + * @var bool flag indicating whether {@see eval} can be used + */ + private $canEval = true; + + /** + * Constructor + */ + public function __construct() + { + $this->canEval = ! ini_get('suhosin.executor.disable_eval'); + } + + /** + * Evaluates the generated code before returning it + * + * {@inheritDoc} + */ + public function generate(ClassGenerator $classGenerator) + { + $code = $classGenerator->generate(); + + if (! $this->canEval) { + // @codeCoverageIgnoreStart + $fileName = sys_get_temp_dir() . '/EvaluatingGeneratorStrategy.php.tmp.' . uniqid('', true); + + file_put_contents($fileName, " + * @license MIT + */ +class FileWriterGeneratorStrategy implements GeneratorStrategyInterface +{ + /** + * @var \ProxyManager\FileLocator\FileLocatorInterface + */ + protected $fileLocator; + + /** + * @var callable + */ + private $emptyErrorHandler; + + /** + * @param \ProxyManager\FileLocator\FileLocatorInterface $fileLocator + */ + public function __construct(FileLocatorInterface $fileLocator) + { + $this->fileLocator = $fileLocator; + $this->emptyErrorHandler = function () { + }; + } + + /** + * Write generated code to disk and return the class code + * + * {@inheritDoc} + */ + public function generate(ClassGenerator $classGenerator) + { + $className = trim($classGenerator->getNamespaceName(), '\\') + . '\\' . trim($classGenerator->getName(), '\\'); + $generatedCode = $classGenerator->generate(); + $fileName = $this->fileLocator->getProxyFileName($className); + + set_error_handler($this->emptyErrorHandler); + + try { + $this->writeFile(" + * @license MIT + */ +interface GeneratorStrategyInterface +{ + /** + * Generate the provided class + * + * @param ClassGenerator $classGenerator + * + * @return string the class body + */ + public function generate(ClassGenerator $classGenerator); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php b/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php new file mode 100644 index 000000000..934d82c82 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflector.php @@ -0,0 +1,98 @@ + + * @license MIT + */ +final class ClassNameInflector implements ClassNameInflectorInterface +{ + /** + * @var string + */ + protected $proxyNamespace; + + /** + * @var int + */ + private $proxyMarkerLength; + + /** + * @var string + */ + private $proxyMarker; + + /** + * @var \ProxyManager\Inflector\Util\ParameterHasher + */ + private $parameterHasher; + + /** + * @param string $proxyNamespace + */ + public function __construct($proxyNamespace) + { + $this->proxyNamespace = (string) $proxyNamespace; + $this->proxyMarker = '\\' . static::PROXY_MARKER . '\\'; + $this->proxyMarkerLength = strlen($this->proxyMarker); + $this->parameterHasher = new ParameterHasher(); + } + + /** + * {@inheritDoc} + */ + public function getUserClassName($className) + { + $className = ltrim($className, '\\'); + + if (false === $position = strrpos($className, $this->proxyMarker)) { + return $className; + } + + return substr( + $className, + $this->proxyMarkerLength + $position, + strrpos($className, '\\') - ($position + $this->proxyMarkerLength) + ); + } + + /** + * {@inheritDoc} + */ + public function getProxyClassName($className, array $options = array()) + { + return $this->proxyNamespace + . $this->proxyMarker + . $this->getUserClassName($className) + . '\\Generated' . $this->parameterHasher->hashParameters($options); + } + + /** + * {@inheritDoc} + */ + public function isProxyClassName($className) + { + return false !== strrpos($className, $this->proxyMarker); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php b/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php new file mode 100644 index 000000000..71fc43275 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Inflector/ClassNameInflectorInterface.php @@ -0,0 +1,61 @@ + + * @license MIT + */ +interface ClassNameInflectorInterface +{ + /** + * Marker for proxy classes - classes containing this marker are considered proxies + */ + const PROXY_MARKER = '__PM__'; + + /** + * Retrieve the class name of a user-defined class + * + * @param string $className + * + * @return string + */ + public function getUserClassName($className); + + /** + * Retrieve the class name of the proxy for the given user-defined class name + * + * @param string $className + * @param array $options arbitrary options to be used for the generated class name + * + * @return string + */ + public function getProxyClassName($className, array $options = array()); + + /** + * Retrieve whether the provided class name is a proxy + * + * @param string $className + * + * @return bool + */ + public function isProxyClassName($className); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php b/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php new file mode 100644 index 000000000..a1a4b172c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterEncoder.php @@ -0,0 +1,41 @@ + + * @license MIT + */ +class ParameterEncoder +{ + /** + * Converts the given parameters into a set of characters that are safe to + * use in a class name + * + * @param array $parameters + * + * @return string + */ + public function encodeParameters(array $parameters) + { + return base64_encode(serialize($parameters)); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php b/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php new file mode 100644 index 000000000..970b1583c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Inflector/Util/ParameterHasher.php @@ -0,0 +1,40 @@ + + * @license MIT + */ +class ParameterHasher +{ + /** + * Converts the given parameters into a likely-unique hash + * + * @param array $parameters + * + * @return string + */ + public function hashParameters(array $parameters) + { + return md5(serialize($parameters)); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php new file mode 100644 index 000000000..49d70f221 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/AccessInterceptorInterface.php @@ -0,0 +1,64 @@ + + * @license MIT + */ +interface AccessInterceptorInterface extends ProxyInterface +{ + /** + * Set or remove the prefix interceptor for a method + * + * @link https://github.com/Ocramius/ProxyManager/blob/master/docs/access-interceptor-value-holder.md + * + * A prefix interceptor should have a signature like following: + * + * + * $prefixInterceptor = function ($proxy, $instance, $method, $params, & $returnEarly) {}; + * + * + * @param string $methodName name of the intercepted method + * @param \Closure|null $prefixInterceptor interceptor closure or null to unset the currently active interceptor + * + * @return void + */ + public function setMethodPrefixInterceptor($methodName, \Closure $prefixInterceptor = null); + + /** + * Set or remove the suffix interceptor for a method + * + * @link https://github.com/Ocramius/ProxyManager/blob/master/docs/access-interceptor-value-holder.md + * + * A prefix interceptor should have a signature like following: + * + * + * $suffixInterceptor = function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) {}; + * + * + * @param string $methodName name of the intercepted method + * @param \Closure|null $suffixInterceptor interceptor closure or null to unset the currently active interceptor + * + * @return void + */ + public function setMethodSuffixInterceptor($methodName, \Closure $suffixInterceptor = null); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php new file mode 100644 index 000000000..636a0291c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/Exception/RemoteObjectException.php @@ -0,0 +1,31 @@ + + * @license MIT + */ +class RemoteObjectException extends RuntimeException +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php new file mode 100644 index 000000000..bf4bc2a8d --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/FallbackValueHolderInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface FallbackValueHolderInterface extends ProxyInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php new file mode 100644 index 000000000..77df518a6 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/GhostObjectInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface GhostObjectInterface extends LazyLoadingInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php new file mode 100644 index 000000000..1da90e650 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/LazyLoadingInterface.php @@ -0,0 +1,64 @@ + + * @license MIT + */ +interface LazyLoadingInterface extends ProxyInterface +{ + /** + * Set or unset the initializer for the proxy instance + * + * @link https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md#lazy-initialization + * + * An initializer should have a signature like following: + * + * + * $initializer = function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {}; + * + * + * @param \Closure|null $initializer + * + * @return mixed + */ + public function setProxyInitializer(\Closure $initializer = null); + + /** + * @return \Closure|null + */ + public function getProxyInitializer(); + + /** + * Force initialization of the proxy + * + * @return bool true if the proxy could be initialized + */ + public function initializeProxy(); + + /** + * Retrieves current initialization status of the proxy + * + * @return bool + */ + public function isProxyInitialized(); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php new file mode 100644 index 000000000..cd411154f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/NullObjectInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface NullObjectInterface extends ProxyInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php new file mode 100644 index 000000000..7d402b455 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/ProxyInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface ProxyInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php new file mode 100644 index 000000000..264fa5ec9 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/RemoteObjectInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface RemoteObjectInterface extends ProxyInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php new file mode 100644 index 000000000..2380b32b6 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/SmartReferenceInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface SmartReferenceInterface extends ProxyInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php new file mode 100644 index 000000000..126d54390 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/ValueHolderInterface.php @@ -0,0 +1,33 @@ + + * @license MIT + */ +interface ValueHolderInterface extends ProxyInterface +{ + /** + * @return object|null the wrapped value + */ + public function getWrappedValueHolderValue(); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php b/ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php new file mode 100644 index 000000000..fc108c7fb --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Proxy/VirtualProxyInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface VirtualProxyInterface extends LazyLoadingInterface, ValueHolderInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php new file mode 100644 index 000000000..5b7c19d15 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/MagicWakeup.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class MagicWakeup extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct(ReflectionClass $originalClass) + { + parent::__construct($originalClass, '__wakeup'); + + /* @var $publicProperties \ReflectionProperty[] */ + $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); + $unsetProperties = array(); + + foreach ($publicProperties as $publicProperty) { + $unsetProperties[] = '$this->' . $publicProperty->getName(); + } + + $this->setBody($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");" : ''); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php new file mode 100644 index 000000000..85ba61359 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodPrefixInterceptor.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class SetMethodPrefixInterceptor extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $prefixInterceptor) + { + parent::__construct('setMethodPrefixInterceptor'); + + $interceptor = new ParameterGenerator('prefixInterceptor'); + + $interceptor->setType('Closure'); + $interceptor->setDefaultValue(null); + $this->setParameter(new ParameterGenerator('methodName')); + $this->setParameter($interceptor); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('$this->' . $prefixInterceptor->getName() . '[$methodName] = $prefixInterceptor;'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php new file mode 100644 index 000000000..74cf81f23 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/MethodGenerator/SetMethodSuffixInterceptor.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class SetMethodSuffixInterceptor extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $suffixInterceptor) + { + parent::__construct('setMethodSuffixInterceptor'); + + $interceptor = new ParameterGenerator('suffixInterceptor'); + + $interceptor->setType('Closure'); + $interceptor->setDefaultValue(null); + $this->setParameter(new ParameterGenerator('methodName')); + $this->setParameter($interceptor); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('$this->' . $suffixInterceptor->getName() . '[$methodName] = $suffixInterceptor;'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php new file mode 100644 index 000000000..b9c44a20c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodPrefixInterceptors.php @@ -0,0 +1,43 @@ + + * @license MIT + */ +class MethodPrefixInterceptors extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodPrefixInterceptors')); + + $this->setDefaultValue(array()); + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\Closure[] map of interceptors to be called per-method before execution'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php new file mode 100644 index 000000000..a11d6c24c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptor/PropertyGenerator/MethodSuffixInterceptors.php @@ -0,0 +1,43 @@ + + * @license MIT + */ +class MethodSuffixInterceptors extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodSuffixInterceptors')); + + $this->setDefaultValue(array()); + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\Closure[] map of interceptors to be called per-method after execution'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php new file mode 100644 index 000000000..3770a16f3 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Constructor.php @@ -0,0 +1,92 @@ + + * @license MIT + */ +class Constructor extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct('__construct'); + + $localizedObject = new ParameterGenerator('localizedObject'); + $prefix = new ParameterGenerator('prefixInterceptors'); + $suffix = new ParameterGenerator('suffixInterceptors'); + + $localizedObject->setType($originalClass->getName()); + $prefix->setDefaultValue(array()); + $suffix->setDefaultValue(array()); + $prefix->setType('array'); + $suffix->setType('array'); + + $this->setParameter($localizedObject); + $this->setParameter($prefix); + $this->setParameter($suffix); + + $localizedProperties = array(); + + foreach ($originalClass->getProperties() as $originalProperty) { + if ((! method_exists('Closure', 'bind')) && $originalProperty->isPrivate()) { + // @codeCoverageIgnoreStart + throw UnsupportedProxiedClassException::unsupportedLocalizedReflectionProperty($originalProperty); + // @codeCoverageIgnoreEnd + } + + $propertyName = $originalProperty->getName(); + + if ($originalProperty->isPrivate()) { + $localizedProperties[] = "\\Closure::bind(function () use (\$localizedObject) {\n " + . '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ";\n" + . '}, $this, ' . var_export($originalProperty->getDeclaringClass()->getName(), true) + . ')->__invoke();'; + } else { + $localizedProperties[] = '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ";"; + } + } + + $this->setDocblock( + "@override constructor to setup interceptors\n\n" + . "@param \\" . $originalClass->getName() . " \$localizedObject\n" + . "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n" + . "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic" + ); + $this->setBody( + (empty($localizedProperties) ? '' : implode("\n\n", $localizedProperties) . "\n\n") + . '$this->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n" + . '$this->' . $suffixInterceptors->getName() . " = \$suffixInterceptors;" + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php new file mode 100644 index 000000000..22068f7e7 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/InterceptedMethod.php @@ -0,0 +1,67 @@ + + * @license MIT + */ +class InterceptedMethod extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors + * @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors + * + * @return self + */ + public static function generateMethod( + MethodReflection $originalMethod, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + $forwardedParams = array(); + + foreach ($originalMethod->getParameters() as $parameter) { + $forwardedParams[] = '$' . $parameter->getName(); + } + + $method->setDocblock('{@inheritDoc}'); + $method->setBody( + InterceptorGenerator::createInterceptedMethodBody( + '$returnValue = parent::' + . $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');', + $method, + $prefixInterceptors, + $suffixInterceptors + ) + ); + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php new file mode 100644 index 000000000..98e9b19aa --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicClone.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +class MagicClone extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__clone'); + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $originalClass->hasMethod('__clone') ? '$returnValue = parent::__clone();' : '$returnValue = null;', + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php new file mode 100644 index 000000000..930b9d7e4 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php @@ -0,0 +1,73 @@ + + * @license MIT + */ +class MagicGet extends MagicMethodGenerator +{ + /** + * @param ReflectionClass $originalClass + * @param PropertyGenerator $prefixInterceptors + * @param PropertyGenerator $suffixInterceptors + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__get', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__get'); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if ($override) { + $callParent = '$returnValue = & parent::__get($name);'; + } else { + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_GET, + 'name', + null, + null, + 'returnValue' + ); + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php new file mode 100644 index 000000000..26eb275ae --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php @@ -0,0 +1,73 @@ + + * @license MIT + */ +class MagicIsset extends MagicMethodGenerator +{ + /** + * @param ReflectionClass $originalClass + * @param PropertyGenerator $prefixInterceptors + * @param PropertyGenerator $suffixInterceptors + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__isset'); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if ($override) { + $callParent = '$returnValue = & parent::__isset($name);'; + } else { + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_ISSET, + 'name', + null, + null, + 'returnValue' + ); + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php new file mode 100644 index 000000000..e7562075b --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php @@ -0,0 +1,77 @@ + + * @license MIT + */ +class MagicSet extends MagicMethodGenerator +{ + /** + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors + * @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct( + $originalClass, + '__set', + array(new ParameterGenerator('name'), new ParameterGenerator('value')) + ); + + $override = $originalClass->hasMethod('__set'); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if ($override) { + $callParent = '$returnValue = & parent::__set($name, $value);'; + } else { + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_SET, + 'name', + 'value', + null, + 'returnValue' + ); + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php new file mode 100644 index 000000000..3ea4a2589 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSleep.php @@ -0,0 +1,57 @@ + + * @license MIT + */ +class MagicSleep extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__sleep'); + + $callParent = $originalClass->hasMethod('__sleep') + ? '$returnValue = & parent::__sleep();' + : '$returnValue = array_keys((array) $this);'; + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php new file mode 100644 index 000000000..3d404202f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php @@ -0,0 +1,73 @@ + + * @license MIT + */ +class MagicUnset extends MagicMethodGenerator +{ + /** + * @param ReflectionClass $originalClass + * @param PropertyGenerator $prefixInterceptors + * @param PropertyGenerator $suffixInterceptors + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__unset'); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if ($override) { + $callParent = '$returnValue = & parent::__unset($name);'; + } else { + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_UNSET, + 'name', + null, + null, + 'returnValue' + ); + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php new file mode 100644 index 000000000..c43463506 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/Util/InterceptorGenerator.php @@ -0,0 +1,82 @@ + + * @license MIT + * + * @internal - this class is just here as a small utility for this component, + * don't use it in your own code + */ +class InterceptorGenerator +{ + /** + * @param string $methodBody the body of the previously generated code. + * It MUST assign the return value to a variable + * `$returnValue` instead of directly returning + * @param \ProxyManager\Generator\MethodGenerator $method + * @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors + * @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors + * + * @return string + */ + public static function createInterceptedMethodBody( + $methodBody, + MethodGenerator $method, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + $name = var_export($method->getName(), true); + $prefixInterceptors = $prefixInterceptors->getName(); + $suffixInterceptors = $suffixInterceptors->getName(); + $params = array(); + + foreach ($method->getParameters() as $parameter) { + $parameterName = $parameter->getName(); + $params[] = var_export($parameterName, true) . ' => $' . $parameter->getName(); + } + + $paramsString = 'array(' . implode(', ', $params) . ')'; + + return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n" + . " \$returnEarly = false;\n" + . " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke(" + . "\$this, \$this, $name, $paramsString, \$returnEarly);\n\n" + . " if (\$returnEarly) {\n" + . " return \$prefixReturnValue;\n" + . " }\n" + . "}\n\n" + . $methodBody . "\n\n" + . "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n" + . " \$returnEarly = false;\n" + . " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke(" + . "\$this, \$this, $name, $paramsString, \$returnValue, \$returnEarly);\n\n" + . " if (\$returnEarly) {\n" + . " return \$suffixReturnValue;\n" + . " }\n" + . "}\n\n" + . "return \$returnValue;"; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php new file mode 100644 index 000000000..b6edee35a --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizerGenerator.php @@ -0,0 +1,96 @@ + + * @license MIT + */ +class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass, false); + + $classGenerator->setExtendedClass($originalClass->getName()); + $classGenerator->setImplementedInterfaces(array('ProxyManager\\Proxy\\AccessInterceptorInterface')); + $classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors()); + $classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodPrefixInterceptors()); + + array_map( + function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); + }, + array_merge( + array_map( + function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) { + return InterceptedMethod::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()), + $prefixInterceptors, + $suffixInterceptors + ); + }, + ProxiedMethodsFilter::getProxiedMethods( + $originalClass, + array('__get', '__set', '__isset', '__unset', '__clone', '__sleep') + ) + ), + array( + new Constructor($originalClass, $prefixInterceptors, $suffixInterceptors), + new SetMethodPrefixInterceptor($prefixInterceptors), + new SetMethodSuffixInterceptor($suffixInterceptors), + new MagicGet($originalClass, $prefixInterceptors, $suffixInterceptors), + new MagicSet($originalClass, $prefixInterceptors, $suffixInterceptors), + new MagicIsset($originalClass, $prefixInterceptors, $suffixInterceptors), + new MagicUnset($originalClass, $prefixInterceptors, $suffixInterceptors), + new MagicSleep($originalClass, $prefixInterceptors, $suffixInterceptors), + new MagicClone($originalClass, $prefixInterceptors, $suffixInterceptors), + ) + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php new file mode 100644 index 000000000..925ee6b51 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Constructor.php @@ -0,0 +1,79 @@ + + * @license MIT + */ +class Constructor extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct('__construct'); + + $prefix = new ParameterGenerator('prefixInterceptors'); + $suffix = new ParameterGenerator('suffixInterceptors'); + + $prefix->setDefaultValue(array()); + $suffix->setDefaultValue(array()); + $prefix->setType('array'); + $suffix->setType('array'); + + $this->setParameter(new ParameterGenerator('wrappedObject')); + $this->setParameter($prefix); + $this->setParameter($suffix); + + /* @var $publicProperties \ReflectionProperty[] */ + $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); + $unsetProperties = array(); + + foreach ($publicProperties as $publicProperty) { + $unsetProperties[] = '$this->' . $publicProperty->getName(); + } + + $this->setDocblock( + "@override constructor to setup interceptors\n\n" + . "@param \\" . $originalClass->getName() . " \$wrappedObject\n" + . "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n" + . "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic" + ); + $this->setBody( + ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') + . '$this->' . $valueHolder->getName() . " = \$wrappedObject;\n" + . '$this->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n" + . '$this->' . $suffixInterceptors->getName() . " = \$suffixInterceptors;" + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php new file mode 100644 index 000000000..e882e3291 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/InterceptedMethod.php @@ -0,0 +1,70 @@ + + * @license MIT + */ +class InterceptedMethod extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * @param \Zend\Code\Generator\PropertyGenerator $valueHolderProperty + * @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors + * @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors + * + * @return self + */ + public static function generateMethod( + MethodReflection $originalMethod, + PropertyGenerator $valueHolderProperty, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + $forwardedParams = array(); + + foreach ($originalMethod->getParameters() as $parameter) { + $forwardedParams[] = '$' . $parameter->getName(); + } + + $method->setDocblock('{@inheritDoc}'); + $method->setBody( + InterceptorGenerator::createInterceptedMethodBody( + '$returnValue = $this->' . $valueHolderProperty->getName() . '->' + . $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');', + $method, + $valueHolderProperty, + $prefixInterceptors, + $suffixInterceptors + ) + ); + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php new file mode 100644 index 000000000..764ba77fa --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicClone.php @@ -0,0 +1,58 @@ + + * @license MIT + */ +class MagicClone extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolderProperty, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + parent::__construct($originalClass, '__clone'); + + $valueHolder = $valueHolderProperty->getName(); + $prefix = $prefixInterceptors->getName(); + $suffix = $suffixInterceptors->getName(); + + $this->setBody( + "\$this->$valueHolder = clone \$this->$valueHolder;\n\n" + . "foreach (\$this->$prefix as \$key => \$value) {\n" + . " \$this->$prefix" . "[\$key] = clone \$value;\n" + . "}\n\n" + . "foreach (\$this->$suffix as \$key => \$value) {\n" + . " \$this->$suffix" . "[\$key] = clone \$value;\n" + . "}" + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php new file mode 100644 index 000000000..80962811e --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php @@ -0,0 +1,78 @@ + + * @license MIT + */ +class MagicGet extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__get', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__get'); + $valueHolderName = $valueHolder->getName(); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_GET, + 'name', + 'value', + $valueHolder, + 'returnValue' + ); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' $returnValue = & $this->' . $valueHolderName . '->$name;' + . "\n} else {\n $callParent\n}\n\n"; + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php new file mode 100644 index 000000000..2647b3529 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php @@ -0,0 +1,78 @@ + + * @license MIT + */ +class MagicIsset extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__isset'); + $valueHolderName = $valueHolder->getName(); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_ISSET, + 'name', + 'value', + $valueHolder, + 'returnValue' + ); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' $returnValue = isset($this->' . $valueHolderName . '->$name);' + . "\n} else {\n $callParent\n}\n\n"; + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php new file mode 100644 index 000000000..b443b770c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php @@ -0,0 +1,82 @@ + + * @license MIT + */ +class MagicSet extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors, + PublicPropertiesMap $publicProperties + ) { + parent::__construct( + $originalClass, + '__set', + array(new ParameterGenerator('name'), new ParameterGenerator('value')) + ); + + $override = $originalClass->hasMethod('__set'); + $valueHolderName = $valueHolder->getName(); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_SET, + 'name', + 'value', + $valueHolder, + 'returnValue' + ); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' $returnValue = ($this->' . $valueHolderName . '->$name = $value);' + . "\n} else {\n $callParent\n}\n\n"; + } + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php new file mode 100644 index 000000000..bdf3a1aa0 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php @@ -0,0 +1,80 @@ + + * @license MIT + */ +class MagicUnset extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__unset'); + $valueHolderName = $valueHolder->getName(); + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_UNSET, + 'name', + 'value', + $valueHolder, + 'returnValue' + ); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' unset($this->' . $valueHolderName . '->$name);' + . "\n} else {\n $callParent\n}\n\n"; + } + + $callParent .= '$returnValue = false;'; + + $this->setBody( + InterceptorGenerator::createInterceptedMethodBody( + $callParent, + $this, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php new file mode 100644 index 000000000..0a6f58acc --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/Util/InterceptorGenerator.php @@ -0,0 +1,85 @@ + + * @license MIT + * + * @internal - this class is just here as a small utility for this component, + * don't use it in your own code + */ +class InterceptorGenerator +{ + /** + * @param string $methodBody the body of the previously generated code. + * It MUST assign the return value to a variable + * `$returnValue` instead of directly returning + * @param \ProxyManager\Generator\MethodGenerator $method + * @param \Zend\Code\Generator\PropertyGenerator $valueHolder + * @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors + * @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors + * + * @return string + */ + public static function createInterceptedMethodBody( + $methodBody, + MethodGenerator $method, + PropertyGenerator $valueHolder, + PropertyGenerator $prefixInterceptors, + PropertyGenerator $suffixInterceptors + ) { + $name = var_export($method->getName(), true); + $valueHolder = $valueHolder->getName(); + $prefixInterceptors = $prefixInterceptors->getName(); + $suffixInterceptors = $suffixInterceptors->getName(); + $params = array(); + + foreach ($method->getParameters() as $parameter) { + $parameterName = $parameter->getName(); + $params[] = var_export($parameterName, true) . ' => $' . $parameter->getName(); + } + + $paramsString = 'array(' . implode(', ', $params) . ')'; + + return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n" + . " \$returnEarly = false;\n" + . " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke(" + . "\$this, \$this->$valueHolder, $name, $paramsString, \$returnEarly);\n\n" + . " if (\$returnEarly) {\n" + . " return \$prefixReturnValue;\n" + . " }\n" + . "}\n\n" + . $methodBody . "\n\n" + . "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n" + . " \$returnEarly = false;\n" + . " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke(" + . "\$this, \$this->$valueHolder, $name, $paramsString, \$returnValue, \$returnEarly);\n\n" + . " if (\$returnEarly) {\n" + . " return \$suffixReturnValue;\n" + . " }\n" + . "}\n\n" + . "return \$returnValue;"; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php new file mode 100644 index 000000000..4b055495e --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolderGenerator.php @@ -0,0 +1,138 @@ + + * @license MIT + */ +class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass); + + $publicProperties = new PublicPropertiesMap($originalClass); + $interfaces = array( + 'ProxyManager\\Proxy\\AccessInterceptorInterface', + 'ProxyManager\\Proxy\\ValueHolderInterface', + ); + + if ($originalClass->isInterface()) { + $interfaces[] = $originalClass->getName(); + } else { + $classGenerator->setExtendedClass($originalClass->getName()); + } + + $classGenerator->setImplementedInterfaces($interfaces); + $classGenerator->addPropertyFromGenerator($valueHolder = new ValueHolderProperty()); + $classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors()); + $classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors()); + $classGenerator->addPropertyFromGenerator($publicProperties); + + array_map( + function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); + }, + array_merge( + array_map( + function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors, $valueHolder) { + return InterceptedMethod::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()), + $valueHolder, + $prefixInterceptors, + $suffixInterceptors + ); + }, + ProxiedMethodsFilter::getProxiedMethods($originalClass) + ), + array( + new Constructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors), + new GetWrappedValueHolderValue($valueHolder), + new SetMethodPrefixInterceptor($prefixInterceptors), + new SetMethodSuffixInterceptor($suffixInterceptors), + new MagicGet( + $originalClass, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors, + $publicProperties + ), + new MagicSet( + $originalClass, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors, + $publicProperties + ), + new MagicIsset( + $originalClass, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors, + $publicProperties + ), + new MagicUnset( + $originalClass, + $valueHolder, + $prefixInterceptors, + $suffixInterceptors, + $publicProperties + ), + new MagicClone($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors), + new MagicSleep($originalClass, $valueHolder), + new MagicWakeup($originalClass, $valueHolder), + ) + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php new file mode 100644 index 000000000..213e15d9d --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Assertion/CanProxyAssertion.php @@ -0,0 +1,102 @@ + + * @license MIT + */ +final class CanProxyAssertion +{ + /** + * Disabled constructor: not meant to be instantiated + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Unsupported constructor.'); + } + + /** + * @param ReflectionClass $originalClass + * @param bool $allowInterfaces + * + * @throws InvalidProxiedClassException + */ + public static function assertClassCanBeProxied(ReflectionClass $originalClass, $allowInterfaces = true) + { + self::isNotFinal($originalClass); + self::hasNoAbstractProtectedMethods($originalClass); + + if (! $allowInterfaces) { + self::isNotInterface($originalClass); + } + } + + /** + * @param ReflectionClass $originalClass + * + * @throws InvalidProxiedClassException + */ + private static function isNotFinal(ReflectionClass $originalClass) + { + if ($originalClass->isFinal()) { + throw InvalidProxiedClassException::finalClassNotSupported($originalClass); + } + } + + /** + * @param ReflectionClass $originalClass + * + * @throws InvalidProxiedClassException + */ + private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass) + { + $protectedAbstract = array_filter( + $originalClass->getMethods(), + function (ReflectionMethod $method) { + return $method->isAbstract() && $method->isProtected(); + } + ); + + if ($protectedAbstract) { + throw InvalidProxiedClassException::abstractProtectedMethodsNotSupported($originalClass); + } + } + + /** + * @param ReflectionClass $originalClass + * + * @throws InvalidProxiedClassException + */ + private static function isNotInterface(ReflectionClass $originalClass) + { + if ($originalClass->isInterface()) { + throw InvalidProxiedClassException::interfaceNotSupported($originalClass); + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php new file mode 100644 index 000000000..224370b33 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php @@ -0,0 +1,58 @@ + + * @license MIT + */ +class Constructor extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $initializerProperty) + { + parent::__construct('__construct'); + + $this->setParameter(new ParameterGenerator('initializer')); + + /* @var $publicProperties \ReflectionProperty[] */ + $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); + $unsetProperties = array(); + + foreach ($publicProperties as $publicProperty) { + $unsetProperties[] = '$this->' . $publicProperty->getName(); + } + + $this->setDocblock("@override constructor for lazy initialization\n\n@param \\Closure|null \$initializer"); + $this->setBody( + ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') + . '$this->' . $initializerProperty->getName() . ' = $initializer;' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php new file mode 100644 index 000000000..36f2794fd --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -0,0 +1,67 @@ + + * @license MIT + */ +class CallInitializer extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct( + PropertyGenerator $initializerProperty, + PropertyGenerator $publicPropsDefaults, + PropertyGenerator $initTracker + ) { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('callInitializer')); + $this->setDocblock("Triggers initialization logic for this ghost object"); + + $this->setParameters(array( + new ParameterGenerator('methodName'), + new ParameterGenerator('parameters', 'array'), + )); + + $this->setVisibility(static::VISIBILITY_PRIVATE); + + $initializer = $initializerProperty->getName(); + $initialization = $initTracker->getName(); + + $this->setBody( + 'if ($this->' . $initialization . ' || ! $this->' . $initializer . ') {' . "\n return;\n}\n\n" + . "\$this->" . $initialization . " = true;\n\n" + . "foreach (self::\$" . $publicPropsDefaults->getName() . " as \$key => \$default) {\n" + . " \$this->\$key = \$default;\n" + . "}\n\n" + . '$this->' . $initializer . '->__invoke' + . '($this, $methodName, $parameters, $this->' . $initializer . ');' . "\n\n" + . "\$this->" . $initialization . " = false;" + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php new file mode 100644 index 000000000..5b1d75a4f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/GetProxyInitializer.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class GetProxyInitializer extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty) + { + parent::__construct('getProxyInitializer'); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('return $this->' . $initializerProperty->getName() . ';'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php new file mode 100644 index 000000000..90723c31c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/InitializeProxy.php @@ -0,0 +1,47 @@ + + * @license MIT + */ +class InitializeProxy extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty, ZendMethodGenerator $callInitializer) + { + parent::__construct('initializeProxy'); + $this->setDocblock('{@inheritDoc}'); + + $this->setBody( + 'return $this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'initializeProxy\', array());' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php new file mode 100644 index 000000000..110ef3173 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/IsProxyInitialized.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class IsProxyInitialized extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty) + { + parent::__construct('isProxyInitialized'); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('return ! $this->' . $initializerProperty->getName() . ';'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php new file mode 100644 index 000000000..d511a4829 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/LazyLoadingMethodInterceptor.php @@ -0,0 +1,71 @@ + + * @license MIT + */ +class LazyLoadingMethodInterceptor extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\MethodGenerator $callInitializer + * + * @return LazyLoadingMethodInterceptor|static + */ + public static function generateMethod( + MethodReflection $originalMethod, + PropertyGenerator $initializerProperty, + ZendMethodGenerator $callInitializer + ) { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + $parameters = $originalMethod->getParameters(); + $methodName = $originalMethod->getName(); + $initializerParams = array(); + $forwardedParams = array(); + + foreach ($parameters as $parameter) { + $parameterName = $parameter->getName(); + $initializerParams[] = var_export($parameterName, true) . ' => $' . $parameterName; + $forwardedParams[] = '$' . $parameterName; + } + + $method->setBody( + '$this->' . $initializerProperty->getName() + . ' && $this->' . $callInitializer->getName() + . '(' . var_export($methodName, true) + . ', array(' . implode(', ', $initializerParams) . "));\n\n" + . 'return parent::' + . $methodName . '(' . implode(', ', $forwardedParams) . ');' + ); + $method->setDocblock('{@inheritDoc}'); + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php new file mode 100644 index 000000000..34d999b7b --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicClone.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class MagicClone extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer + ) { + parent::__construct($originalClass, '__clone'); + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__clone\', array());' + . ($originalClass->hasMethod('__clone') ? "\n\nparent::__clone();" : '') + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php new file mode 100644 index 000000000..007b65974 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -0,0 +1,77 @@ + + * @license MIT + */ +class MagicGet extends MagicMethodGenerator +{ + /** + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\MethodGenerator $callInitializer + * @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__get', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__get'); + $callParent = ''; + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return $this->$name;' + . "\n}\n\n"; + } + + if ($override) { + $callParent .= 'return parent::__get($name);'; + } else { + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_GET, + 'name' + ); + } + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__get\', array(\'name\' => $name));' + . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php new file mode 100644 index 000000000..84c77fa9c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -0,0 +1,77 @@ + + * @license MIT + */ +class MagicIsset extends MagicMethodGenerator +{ + /** + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\MethodGenerator $callInitializer + * @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__isset'); + $callParent = ''; + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return isset($this->$name);' + . "\n}\n\n"; + } + + if ($override) { + $callParent .= 'return parent::__isset($name);'; + } else { + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_ISSET, + 'name' + ); + } + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__isset\', array(\'name\' => $name));' + . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php new file mode 100644 index 000000000..902a16d84 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -0,0 +1,81 @@ + + * @license MIT + */ +class MagicSet extends MagicMethodGenerator +{ + /** + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\MethodGenerator $callInitializer + * @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer, + PublicPropertiesMap $publicProperties + ) { + parent::__construct( + $originalClass, + '__set', + array(new ParameterGenerator('name'), new ParameterGenerator('value')) + ); + + $override = $originalClass->hasMethod('__set'); + $callParent = ''; + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return ($this->$name = $value);' + . "\n}\n\n"; + } + + if ($override) { + $callParent .= 'return parent::__set($name, $value);'; + } else { + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_SET, + 'name', + 'value' + ); + } + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__set\', array(\'name\' => $name, \'value\' => $value));' . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php new file mode 100644 index 000000000..a8036b2dd --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSleep.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class MagicSleep extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer + ) { + parent::__construct($originalClass, '__sleep'); + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__sleep\', array());' . "\n\n" + . ($originalClass->hasMethod('__sleep') ? 'return parent::__sleep();' : 'return array_keys((array) $this);') + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php new file mode 100644 index 000000000..5a5bd23cb --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -0,0 +1,78 @@ + + * @license MIT + */ +class MagicUnset extends MagicMethodGenerator +{ + /** + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\MethodGenerator $callInitializer + * @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + MethodGenerator $callInitializer, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name'))); + + $override = $originalClass->hasMethod('__unset'); + $callParent = ''; + + $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' unset($this->$name);' + . "\n\n return;" + . "\n}\n\n"; + } + + if ($override) { + $callParent .= "return parent::__unset(\$name);"; + } else { + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_UNSET, + 'name' + ); + } + + $this->setBody( + '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() + . '(\'__unset\', array(\'name\' => $name));' + . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php new file mode 100644 index 000000000..30e37bbb7 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/SetProxyInitializer.php @@ -0,0 +1,49 @@ + + * @license MIT + */ +class SetProxyInitializer extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty) + { + parent::__construct('setProxyInitializer'); + + $initializerParameter = new ParameterGenerator('initializer'); + + $initializerParameter->setType('Closure'); + $initializerParameter->setDefaultValue(null); + $this->setParameter($initializerParameter); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('$this->' . $initializerProperty->getName() . ' = $initializer;'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php new file mode 100644 index 000000000..c303f1c5f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializationTracker.php @@ -0,0 +1,43 @@ + + * @license MIT + */ +class InitializationTracker extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializationTracker')); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var bool tracks initialization status - true while the object is initializing'); + $this->setDefaultValue(false); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php new file mode 100644 index 000000000..554889fd8 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/InitializerProperty.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class InitializerProperty extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializer')); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php new file mode 100644 index 000000000..1f9d88e5a --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -0,0 +1,114 @@ + + * @license MIT + */ +class LazyLoadingGhostGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass); + + $interfaces = array('ProxyManager\\Proxy\\GhostObjectInterface'); + $publicProperties = new PublicPropertiesMap($originalClass); + $publicPropsDefaults = new PublicPropertiesDefaults($originalClass); + + if ($originalClass->isInterface()) { + $interfaces[] = $originalClass->getName(); + } else { + $classGenerator->setExtendedClass($originalClass->getName()); + } + + $classGenerator->setImplementedInterfaces($interfaces); + $classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty()); + $classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker()); + $classGenerator->addPropertyFromGenerator($publicProperties); + $classGenerator->addPropertyFromGenerator($publicPropsDefaults); + + $init = new CallInitializer($initializer, $publicPropsDefaults, $initializationTracker); + + array_map( + function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); + }, + array_merge( + array_map( + function (ReflectionMethod $method) use ($initializer, $init) { + return LazyLoadingMethodInterceptor::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()), + $initializer, + $init + ); + }, + ProxiedMethodsFilter::getProxiedMethods($originalClass) + ), + array( + $init, + new Constructor($originalClass, $initializer), + new MagicGet($originalClass, $initializer, $init, $publicProperties), + new MagicSet($originalClass, $initializer, $init, $publicProperties), + new MagicIsset($originalClass, $initializer, $init, $publicProperties), + new MagicUnset($originalClass, $initializer, $init, $publicProperties), + new MagicClone($originalClass, $initializer, $init, $publicProperties), + new MagicSleep($originalClass, $initializer, $init, $publicProperties), + new SetProxyInitializer($initializer), + new GetProxyInitializer($initializer), + new InitializeProxy($initializer, $init), + new IsProxyInitialized($initializer), + ) + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php new file mode 100644 index 000000000..3dd085484 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/GetProxyInitializer.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class GetProxyInitializer extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty) + { + parent::__construct('getProxyInitializer'); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('return $this->' . $initializerProperty->getName() . ';'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php new file mode 100644 index 000000000..4c358b100 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/InitializeProxy.php @@ -0,0 +1,49 @@ + + * @license MIT + */ +class InitializeProxy extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty, PropertyGenerator $valueHolderProperty) + { + parent::__construct('initializeProxy'); + $this->setDocblock('{@inheritDoc}'); + + $initializer = $initializerProperty->getName(); + + $this->setBody( + 'return $this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolderProperty->getName() + . ', $this, \'initializeProxy\', array(), $this->' . $initializer . ');' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php new file mode 100644 index 000000000..34fdd0bd8 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/IsProxyInitialized.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class IsProxyInitialized extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $valueHolderProperty) + { + parent::__construct('isProxyInitialized'); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('return null !== $this->' . $valueHolderProperty->getName() . ';'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php new file mode 100644 index 000000000..b4ce370a3 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/LazyLoadingMethodInterceptor.php @@ -0,0 +1,72 @@ + + * @license MIT + */ +class LazyLoadingMethodInterceptor extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * @param \Zend\Code\Generator\PropertyGenerator $initializerProperty + * @param \Zend\Code\Generator\PropertyGenerator $valueHolderProperty + * + * @return LazyLoadingMethodInterceptor|static + */ + public static function generateMethod( + MethodReflection $originalMethod, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty + ) { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + $initializerName = $initializerProperty->getName(); + $valueHolderName = $valueHolderProperty->getName(); + $parameters = $originalMethod->getParameters(); + $methodName = $originalMethod->getName(); + $initializerParams = array(); + $forwardedParams = array(); + + foreach ($parameters as $parameter) { + $parameterName = $parameter->getName(); + $initializerParams[] = var_export($parameterName, true) . ' => $' . $parameterName; + $forwardedParams[] = '$' . $parameterName; + } + + $method->setBody( + '$this->' . $initializerName + . ' && $this->' . $initializerName + . '->__invoke($this->' . $valueHolderName . ', $this, ' . var_export($methodName, true) + . ', array(' . implode(', ', $initializerParams) . '), $this->' . $initializerName . ");\n\n" + . 'return $this->' . $valueHolderName . '->' + . $methodName . '(' . implode(', ', $forwardedParams) . ');' + ); + $method->setDocblock('{@inheritDoc}'); + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php new file mode 100644 index 000000000..d62c46ab1 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicClone.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +class MagicClone extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty + ) { + parent::__construct($originalClass, '__clone'); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder + . ', $this, \'__clone\', array(), $this->' . $initializer . ');' . "\n\n" + . '$this->' . $valueHolder . ' = clone $this->' . $valueHolder . ';' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php new file mode 100644 index 000000000..43b3ec51a --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php @@ -0,0 +1,69 @@ + + * @license MIT + */ +class MagicGet extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__get', array(new ParameterGenerator('name'))); + + $this->setDocblock(($originalClass->hasMethod('__get') ? "{@inheritDoc}\n" : '') . '@param string $name'); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return $this->' . $valueHolder . '->$name;' + . "\n}\n\n"; + + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_GET, + 'name', + null, + $valueHolderProperty + ); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder . ', $this, \'__get\', array(\'name\' => $name), $this->' + . $initializer . ');' + . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php new file mode 100644 index 000000000..11607a4ce --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php @@ -0,0 +1,72 @@ + + * @license MIT + */ +class MagicIsset extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name'))); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + $callParent = ''; + + $this->setDocblock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return isset($this->' . $valueHolder . '->$name);' + . "\n}\n\n"; + } + + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_ISSET, + 'name', + null, + $valueHolderProperty + ); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder . ', $this, \'__isset\', array(\'name\' => $name), $this->' + . $initializer . ');' . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php new file mode 100644 index 000000000..453031917 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php @@ -0,0 +1,79 @@ + + * @license MIT + */ +class MagicSet extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty, + PublicPropertiesMap $publicProperties + ) { + parent::__construct( + $originalClass, + '__set', + array(new ParameterGenerator('name'), new ParameterGenerator('value')) + ); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + $callParent = ''; + + $this->setDocblock( + ($originalClass->hasMethod('__set') ? "{@inheritDoc}\n" : '') . "@param string \$name\n@param mixed \$value" + ); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return ($this->' . $valueHolder . '->$name = $value);' + . "\n}\n\n"; + } + + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_SET, + 'name', + 'value', + $valueHolderProperty + ); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder . ', $this, ' + . '\'__set\', array(\'name\' => $name, \'value\' => $value), $this->' . $initializer . ');' + . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php new file mode 100644 index 000000000..20051c8f2 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSleep.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +class MagicSleep extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty + ) { + parent::__construct($originalClass, '__sleep'); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder . ', $this, \'__sleep\', array(), $this->' + . $initializer . ');' . "\n\n" + . 'return array(' . var_export($valueHolder, true) . ');' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php new file mode 100644 index 000000000..0c339541f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php @@ -0,0 +1,72 @@ + + * @license MIT + */ +class MagicUnset extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct( + ReflectionClass $originalClass, + PropertyGenerator $initializerProperty, + PropertyGenerator $valueHolderProperty, + PublicPropertiesMap $publicProperties + ) { + parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name'))); + + $initializer = $initializerProperty->getName(); + $valueHolder = $valueHolderProperty->getName(); + $callParent = ''; + + $this->setDocblock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name'); + + if (! $publicProperties->isEmpty()) { + $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' unset($this->' . $valueHolder . '->$name);' . "\n\n return;" + . "\n}\n\n"; + } + + $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + PublicScopeSimulator::OPERATION_UNSET, + 'name', + null, + $valueHolderProperty + ); + + $this->setBody( + '$this->' . $initializer . ' && $this->' . $initializer + . '->__invoke($this->' . $valueHolder . ', $this, \'__unset\', array(\'name\' => $name), $this->' + . $initializer . ');' . "\n\n" . $callParent + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php new file mode 100644 index 000000000..db3400f5b --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/SetProxyInitializer.php @@ -0,0 +1,49 @@ + + * @license MIT + */ +class SetProxyInitializer extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $initializerProperty) + { + parent::__construct('setProxyInitializer'); + + $initializerParameter = new ParameterGenerator('initializer'); + + $initializerParameter->setType('Closure'); + $initializerParameter->setDefaultValue(null); + $this->setParameter($initializerParameter); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('$this->' . $initializerProperty->getName() . ' = $initializer;'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php new file mode 100644 index 000000000..e7b436bac --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/InitializerProperty.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class InitializerProperty extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializer')); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php new file mode 100644 index 000000000..71895dca6 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/PropertyGenerator/ValueHolderProperty.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class ValueHolderProperty extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('valueHolder')); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php new file mode 100644 index 000000000..0a9ab363a --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php @@ -0,0 +1,111 @@ + + * @license MIT + */ +class LazyLoadingValueHolderGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass); + + $interfaces = array('ProxyManager\\Proxy\\VirtualProxyInterface'); + $publicProperties = new PublicPropertiesMap($originalClass); + + if ($originalClass->isInterface()) { + $interfaces[] = $originalClass->getName(); + } else { + $classGenerator->setExtendedClass($originalClass->getName()); + } + + $classGenerator->setImplementedInterfaces($interfaces); + $classGenerator->addPropertyFromGenerator($valueHolder = new ValueHolderProperty()); + $classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty()); + $classGenerator->addPropertyFromGenerator($publicProperties); + + array_map( + function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); + }, + array_merge( + array_map( + function (ReflectionMethod $method) use ($initializer, $valueHolder) { + return LazyLoadingMethodInterceptor::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()), + $initializer, + $valueHolder + ); + }, + ProxiedMethodsFilter::getProxiedMethods($originalClass) + ), + array( + new Constructor($originalClass, $initializer), + new MagicGet($originalClass, $initializer, $valueHolder, $publicProperties), + new MagicSet($originalClass, $initializer, $valueHolder, $publicProperties), + new MagicIsset($originalClass, $initializer, $valueHolder, $publicProperties), + new MagicUnset($originalClass, $initializer, $valueHolder, $publicProperties), + new MagicClone($originalClass, $initializer, $valueHolder), + new MagicSleep($originalClass, $initializer, $valueHolder), + new MagicWakeup($originalClass), + new SetProxyInitializer($initializer), + new GetProxyInitializer($initializer), + new InitializeProxy($initializer, $valueHolder), + new IsProxyInitialized($valueHolder), + new GetWrappedValueHolderValue($valueHolder), + ) + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php new file mode 100644 index 000000000..7f4c4cec1 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/Constructor.php @@ -0,0 +1,55 @@ + + * @license MIT + */ +class Constructor extends MethodGenerator +{ + /** + * Constructor + * + * @param ReflectionClass $originalClass Reflection of the class to proxy + */ + public function __construct(ReflectionClass $originalClass) + { + parent::__construct('__construct'); + + /* @var $publicProperties \ReflectionProperty[] */ + $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); + $nullableProperties = array(); + + foreach ($publicProperties as $publicProperty) { + $nullableProperties[] = '$this->' . $publicProperty->getName() . ' = null;'; + } + + $this->setDocblock("@override constructor for null object initialization"); + if ($nullableProperties) { + $this->setBody(implode("\n", $nullableProperties)); + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php new file mode 100644 index 000000000..2b63e5042 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObject/MethodGenerator/NullObjectMethodInterceptor.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +class NullObjectMethodInterceptor extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * + * @return NullObjectMethodInterceptor|static + */ + public static function generateMethod(MethodReflection $originalMethod) + { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + + if ($originalMethod->returnsReference()) { + $reference = UniqueIdentifierGenerator::getIdentifier('ref'); + + $method->setBody("\$$reference = null;\nreturn \$$reference;"); + } else { + $method->setBody(''); + } + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php new file mode 100644 index 000000000..db177ac4f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/NullObjectGenerator.php @@ -0,0 +1,65 @@ + + * @license MIT + */ +class NullObjectGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass); + + $interfaces = array('ProxyManager\\Proxy\\NullObjectInterface'); + + if ($originalClass->isInterface()) { + $interfaces[] = $originalClass->getName(); + } + + $classGenerator->setImplementedInterfaces($interfaces); + + foreach (ProxiedMethodsFilter::getProxiedMethods($originalClass) as $method) { + $classGenerator->addMethodFromGenerator( + NullObjectMethodInterceptor::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()) + ) + ); + } + + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, new Constructor($originalClass)); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php new file mode 100644 index 000000000..55822f369 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php @@ -0,0 +1,58 @@ + + * @license MIT + */ +class PublicPropertiesDefaults extends PropertyGenerator +{ + /** + * @var bool[] + */ + private $publicProperties = array(); + + /** + * @param \ReflectionClass $originalClass + */ + public function __construct(ReflectionClass $originalClass) + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('publicPropertiesDefaults')); + + $defaults = $originalClass->getDefaultProperties(); + + foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) { + $name = $publicProperty->getName(); + $this->publicProperties[$name] = $defaults[$name]; + } + + $this->setDefaultValue($this->publicProperties); + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setStatic(true); + $this->setDocblock('@var mixed[] map of default property values of the parent class'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php new file mode 100644 index 000000000..5682a4a6d --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesMap.php @@ -0,0 +1,63 @@ + + * @license MIT + */ +class PublicPropertiesMap extends PropertyGenerator +{ + /** + * @var bool[] + */ + private $publicProperties = array(); + + /** + * @param \ReflectionClass $originalClass + */ + public function __construct(ReflectionClass $originalClass) + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('publicProperties')); + + foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) { + $this->publicProperties[$publicProperty->getName()] = true; + } + + $this->setDefaultValue($this->publicProperties); + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setStatic(true); + $this->setDocblock('@var bool[] map of public properties of the parent class'); + } + + /** + * @return bool whether there are no public properties + */ + public function isEmpty() + { + return empty($this->publicProperties); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php new file mode 100644 index 000000000..df88aa887 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ProxyGeneratorInterface.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +interface ProxyGeneratorInterface +{ + /** + * Apply modifications to the provided $classGenerator to proxy logic from $originalClass + * + * @param \ReflectionClass $originalClass + * @param \Zend\Code\Generator\ClassGenerator $classGenerator + * + * @return void + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php new file mode 100644 index 000000000..d1fcb8926 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/Constructor.php @@ -0,0 +1,63 @@ + + * @license MIT + */ +class Constructor extends MethodGenerator +{ + /** + * Constructor + * + * @param ReflectionClass $originalClass Reflection of the class to proxy + * @param PropertyGenerator $adapter Adapter property + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapter) + { + parent::__construct('__construct'); + + $adapterName = $adapter->getName(); + + $this->setParameter(new ParameterGenerator($adapterName, 'ProxyManager\Factory\RemoteObject\AdapterInterface')); + + $this->setDocblock( + '@override constructor for remote object control\n\n' + . '@param \\ProxyManager\\Factory\\RemoteObject\\AdapterInterface \$adapter' + ); + + $body = '$this->' . $adapterName . ' = $' . $adapterName . ';'; + + foreach ($originalClass->getProperties() as $property) { + if ($property->isPublic() && ! $property->isStatic()) { + $body .= "\nunset(\$this->" . $property->getName() . ');'; + } + } + + $this->setBody($body); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php new file mode 100644 index 000000000..c1841dc26 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicGet.php @@ -0,0 +1,49 @@ + + * @license MIT + */ +class MagicGet extends MagicMethodGenerator +{ + /** + * Constructor + * @param ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $adapterProperty + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty) + { + parent::__construct($originalClass, '__get', array(new ParameterGenerator('name'))); + + $this->setDocblock('@param string $name'); + $this->setBody( + '$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true) + . ', \'__get\', array($name));' . "\n\n" . 'return $return;' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php new file mode 100644 index 000000000..254977c33 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicIsset.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class MagicIsset extends MagicMethodGenerator +{ + /** + * Constructor + * @param ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $adapterProperty + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty) + { + parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name'))); + + $this->setDocblock('@param string $name'); + $this->setBody( + '$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true) + . ', \'__isset\', array($name));' . "\n\n" + . 'return $return;' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php new file mode 100644 index 000000000..2b3c917a5 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicSet.php @@ -0,0 +1,54 @@ + + * @license MIT + */ +class MagicSet extends MagicMethodGenerator +{ + /** + * Constructor + * @param ReflectionClass $originalClass + * @param \Zend\Code\Generator\PropertyGenerator $adapterProperty + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty) + { + parent::__construct( + $originalClass, + '__set', + array(new ParameterGenerator('name'), new ParameterGenerator('value')) + ); + + $this->setDocblock('@param string \$name\n@param mixed \$value'); + $this->setBody( + '$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true) + . ', \'__set\', array($name, $value));' . "\n\n" + . 'return $return;' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php new file mode 100644 index 000000000..fa7aba869 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/MagicUnset.php @@ -0,0 +1,51 @@ + + * @license MIT + */ +class MagicUnset extends MagicMethodGenerator +{ + /** + * Constructor + * + * @param ReflectionClass $originalClass + * @param PropertyGenerator $adapterProperty + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty) + { + parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name'))); + + $this->setDocblock('@param string $name'); + $this->setBody( + '$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true) + . ', \'__unset\', array($name));' . "\n\n" + . 'return $return;' + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php new file mode 100644 index 000000000..e8476a69b --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php @@ -0,0 +1,63 @@ + + * @license MIT + */ +class RemoteObjectMethod extends MethodGenerator +{ + /** + * @param \Zend\Code\Reflection\MethodReflection $originalMethod + * @param \Zend\Code\Generator\PropertyGenerator $adapterProperty + * @param \ReflectionClass $originalClass + * + * @return RemoteObjectMethod|static + */ + public static function generateMethod( + MethodReflection $originalMethod, + PropertyGenerator $adapterProperty, + ReflectionClass $originalClass + ) { + /* @var $method self */ + $method = static::fromReflection($originalMethod); + $parameters = $originalMethod->getParameters(); + $list = array(); + + foreach ($parameters as $parameter) { + $list[] = '$' . $parameter->getName(); + } + + $method->setBody( + '$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true) + . ', ' . var_export($originalMethod->getName(), true) . ', array('. implode(', ', $list) .'));' . "\n\n" + . 'return $return;' + ); + + return $method; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php new file mode 100644 index 000000000..5ab1dd133 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObject/PropertyGenerator/AdapterProperty.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class AdapterProperty extends PropertyGenerator +{ + /** + * Constructor + */ + public function __construct() + { + parent::__construct(UniqueIdentifierGenerator::getIdentifier('adapter')); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setDocblock('@var \\ProxyManager\\Factory\\RemoteObject\\AdapterInterface Remote web service adapter'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php new file mode 100644 index 000000000..8a4a4e97b --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/RemoteObjectGenerator.php @@ -0,0 +1,93 @@ + + * @license MIT + */ +class RemoteObjectGenerator implements ProxyGeneratorInterface +{ + /** + * {@inheritDoc} + */ + public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + CanProxyAssertion::assertClassCanBeProxied($originalClass); + + $interfaces = array('ProxyManager\\Proxy\\RemoteObjectInterface'); + + if ($originalClass->isInterface()) { + $interfaces[] = $originalClass->getName(); + } else { + $classGenerator->setExtendedClass($originalClass->getName()); + } + + $classGenerator->setImplementedInterfaces($interfaces); + $classGenerator->addPropertyFromGenerator($adapter = new AdapterProperty()); + + array_map( + function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { + ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); + }, + array_merge( + array_map( + function (ReflectionMethod $method) use ($adapter, $originalClass) { + return RemoteObjectMethod::generateMethod( + new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()), + $adapter, + $originalClass + ); + }, + ProxiedMethodsFilter::getProxiedMethods( + $originalClass, + array('__get', '__set', '__isset', '__unset') + ) + ), + array( + new Constructor($originalClass, $adapter), + new MagicGet($originalClass, $adapter), + new MagicSet($originalClass, $adapter), + new MagicIsset($originalClass, $adapter), + new MagicUnset($originalClass, $adapter), + ) + ) + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php new file mode 100644 index 000000000..6024e3344 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -0,0 +1,56 @@ + + * @license MIT + */ +class ProxiedMethodsFilter +{ + /** + * @param ReflectionClass $class reflection class from which methods should be extracted + * @param string[] $excluded methods to be ignored + * + * @return ReflectionMethod[] + */ + public static function getProxiedMethods( + ReflectionClass $class, + array $excluded = array('__get', '__set', '__isset', '__unset', '__clone', '__sleep', '__wakeup') + ) { + $ignored = array_flip(array_map('strtolower', $excluded)); + + return array_filter( + $class->getMethods(ReflectionMethod::IS_PUBLIC), + function (ReflectionMethod $method) use ($ignored) { + return ! ( + $method->isConstructor() + || isset($ignored[strtolower($method->getName())]) + || $method->isFinal() + || $method->isStatic() + ); + } + ); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php new file mode 100644 index 000000000..a908e58bc --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php @@ -0,0 +1,190 @@ + + * @license MIT + */ +class PublicScopeSimulator +{ + const OPERATION_SET = 'set'; + const OPERATION_GET = 'get'; + const OPERATION_ISSET = 'isset'; + const OPERATION_UNSET = 'unset'; + + /** + * Generates code for simulating access to a property from the scope that is accessing a proxy. + * This is done by introspecting `debug_backtrace()` and then binding a closure to the scope + * of the parent caller. + * + * @param string $operationType operation to execute: one of 'get', 'set', 'isset' or 'unset' + * @param string $nameParameter name of the `name` parameter of the magic method + * @param string|null $valueParameter name of the `value` parameter of the magic method + * @param PropertyGenerator $valueHolder name of the property containing the target object from which + * to read the property. `$this` if none provided + * @param string|null $returnPropertyName name of the property to which we want to assign the result of + * the operation. Return directly if none provided + * + * @return string + * + * @throws \InvalidArgumentException + */ + public static function getPublicAccessSimulationCode( + $operationType, + $nameParameter, + $valueParameter = null, + PropertyGenerator $valueHolder = null, + $returnPropertyName = null + ) { + $byRef = self::getByRefReturnValue($operationType); + $value = static::OPERATION_SET === $operationType ? ', $value' : ''; + $target = '$this'; + + if ($valueHolder) { + $target = '$this->' . $valueHolder->getName(); + } + + return '$realInstanceReflection = new \\ReflectionClass(get_parent_class($this));' . "\n\n" + . 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {' . "\n" + . ' $targetObject = ' . $target . ';' . "\n\n" + . self::getUndefinedPropertyNotice($operationType, $nameParameter) + . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . ";\n" + . " return;\n" + . '}' . "\n\n" + . '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n" + . '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n" + . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n" + . "};\n" + . self::getScopeReBind() + . ( + $returnPropertyName + ? '$' . $returnPropertyName . ' = ' . $byRef . '$accessor();' + : '$returnValue = ' . $byRef . '$accessor();' . "\n\n" . 'return $returnValue;' + ); + } + + /** + * This will generate code that triggers a notice if access is attempted on a non-existing property + * + * @param string $operationType + * @param string $nameParameter + * + * @return string + */ + private static function getUndefinedPropertyNotice($operationType, $nameParameter) + { + if (static::OPERATION_GET !== $operationType) { + return ''; + } + + // + return ' $backtrace = debug_backtrace(false);' . "\n" + . ' trigger_error(\'Undefined property: \' . get_parent_class($this) . \'::$\' . $' + . $nameParameter + . ' . \' in \' . $backtrace[0][\'file\'] . \' on line \' . $backtrace[0][\'line\'], \E_USER_NOTICE);' + . "\n"; + } + + /** + * Defines whether the given operation produces a reference. + * + * Note: if the object is a wrapper, the wrapped instance is accessed directly. If the object + * is a ghost or the proxy has no wrapper, then an instance of the parent class is created via + * on-the-fly unserialization + * + * @param string $operationType + * + * @return string + */ + private static function getByRefReturnValue($operationType) + { + return (static::OPERATION_GET === $operationType || static::OPERATION_SET === $operationType) ? '& ' : ''; + } + + /** + * Retrieves the logic to fetch the object on which access should be attempted + * + * @param PropertyGenerator $valueHolder + * + * @return string + */ + private static function getTargetObject(PropertyGenerator $valueHolder = null) + { + if ($valueHolder) { + return '$this->' . $valueHolder->getName(); + } + + return 'unserialize(sprintf(\'O:%d:"%s":0:{}\', strlen(get_parent_class($this)), get_parent_class($this)))'; + } + + /** + * @param string $operationType + * @param string $nameParameter + * @param string|null $valueParameter + * + * @return string + * + * @throws \InvalidArgumentException + */ + private static function getOperation($operationType, $nameParameter, $valueParameter) + { + switch ($operationType) { + case static::OPERATION_GET: + return 'return $targetObject->$' . $nameParameter . ";"; + case static::OPERATION_SET: + if (! $valueParameter) { + throw new \InvalidArgumentException('Parameter $valueParameter not provided'); + } + + return 'return $targetObject->$' . $nameParameter . ' = $' . $valueParameter . ';'; + case static::OPERATION_ISSET: + return 'return isset($targetObject->$' . $nameParameter . ');'; + case static::OPERATION_UNSET: + return 'unset($targetObject->$' . $nameParameter . ');'; + } + + throw new \InvalidArgumentException(sprintf('Invalid operation "%s" provided', $operationType)); + } + + /** + * Generates code to bind operations to the parent scope if supported by the current PHP implementation + * + * @return string + */ + private static function getScopeReBind() + { + if (! method_exists('Closure', 'bind')) { + // @codeCoverageIgnoreStart + return ''; + // @codeCoverageIgnoreEnd + } + + return ' $backtrace = debug_backtrace(true);' . "\n" + . ' $scopeObject = isset($backtrace[1][\'object\'])' + . ' ? $backtrace[1][\'object\'] : new \stdClass();' . "\n" + . ' $accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n"; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php new file mode 100644 index 000000000..7db81d375 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/GetWrappedValueHolderValue.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class GetWrappedValueHolderValue extends MethodGenerator +{ + /** + * Constructor + */ + public function __construct(PropertyGenerator $valueHolderProperty) + { + parent::__construct('getWrappedValueHolderValue'); + $this->setDocblock('{@inheritDoc}'); + $this->setBody('return $this->' . $valueHolderProperty->getName() . ';'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php new file mode 100644 index 000000000..17aa0fd63 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/ProxyGenerator/ValueHolder/MethodGenerator/MagicSleep.php @@ -0,0 +1,42 @@ + + * @license MIT + */ +class MagicSleep extends MagicMethodGenerator +{ + /** + * Constructor + */ + public function __construct(ReflectionClass $originalClass, PropertyGenerator $valueHolderProperty) + { + parent::__construct($originalClass, '__sleep'); + + $this->setBody('return array(' . var_export($valueHolderProperty->getName(), true) . ');'); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php new file mode 100644 index 000000000..70e90acd9 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGenerator.php @@ -0,0 +1,58 @@ + + * @license MIT + */ +final class ClassSignatureGenerator implements ClassSignatureGeneratorInterface +{ + /** + * @var SignatureGeneratorInterface + */ + private $signatureGenerator; + + /** + * @param SignatureGeneratorInterface $signatureGenerator + */ + public function __construct(SignatureGeneratorInterface $signatureGenerator) + { + $this->signatureGenerator = $signatureGenerator; + } + + /** + * {@inheritDoc} + */ + public function addSignature(ClassGenerator $classGenerator, array $parameters) + { + $classGenerator->addPropertyFromGenerator(new PropertyGenerator( + 'signature' . $this->signatureGenerator->generateSignatureKey($parameters), + $this->signatureGenerator->generateSignature($parameters), + PropertyGenerator::FLAG_STATIC | PropertyGenerator::FLAG_PRIVATE + )); + + return $classGenerator; + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php b/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php new file mode 100644 index 000000000..a5553ed23 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/ClassSignatureGeneratorInterface.php @@ -0,0 +1,40 @@ + + * @license MIT + */ +interface ClassSignatureGeneratorInterface +{ + /** + * Applies a signature to a given class generator + * + * @param ClassGenerator $classGenerator + * @param array $parameters + * + * @return ClassGenerator + */ + public function addSignature(ClassGenerator $classGenerator, array $parameters); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php new file mode 100644 index 000000000..c103153f7 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ + + * @license MIT + */ +interface ExceptionInterface +{ +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php new file mode 100644 index 000000000..7e974fcb9 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/InvalidSignatureException.php @@ -0,0 +1,50 @@ + + * @license MIT + */ +class InvalidSignatureException extends UnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $class + * @param array $parameters + * @param string $signature + * @param string $expected + * + * @return self + */ + public static function fromInvalidSignature(ReflectionClass $class, array $parameters, $signature, $expected) + { + return new self(sprintf( + 'Found signature "%s" for class "%s" does not correspond to expected signature "%s" for %d parameters', + $signature, + $class->getName(), + $expected, + count($parameters) + )); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php new file mode 100644 index 000000000..e53f5277f --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/Exception/MissingSignatureException.php @@ -0,0 +1,48 @@ + + * @license MIT + */ +class MissingSignatureException extends UnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $class + * @param array $parameters + * @param string $expected + * + * @return self + */ + public static function fromMissingSignature(ReflectionClass $class, array $parameters, $expected) + { + return new self(sprintf( + 'No signature found for class "%s", expected signature "%s" for %d parameters', + $class->getName(), + $expected, + count($parameters) + )); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php new file mode 100644 index 000000000..0a674eca6 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureChecker.php @@ -0,0 +1,68 @@ + + * @license MIT + */ +final class SignatureChecker implements SignatureCheckerInterface +{ + /** + * @var SignatureGeneratorInterface + */ + private $signatureGenerator; + + /** + * @param SignatureGeneratorInterface $signatureGenerator + */ + public function __construct(SignatureGeneratorInterface $signatureGenerator) + { + $this->signatureGenerator = $signatureGenerator; + } + + /** + * {@inheritDoc} + */ + public function checkSignature(ReflectionClass $class, array $parameters) + { + $propertyName = 'signature' . $this->signatureGenerator->generateSignatureKey($parameters); + $signature = $this->signatureGenerator->generateSignature($parameters); + $defaultProperties = $class->getDefaultProperties(); + + if (! isset($defaultProperties[$propertyName])) { + throw MissingSignatureException::fromMissingSignature($class, $parameters, $signature); + } + + if ($defaultProperties[$propertyName] !== $signature) { + throw InvalidSignatureException::fromInvalidSignature( + $class, + $parameters, + $defaultProperties[$propertyName], + $signature + ); + } + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php new file mode 100644 index 000000000..ded656140 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureCheckerInterface.php @@ -0,0 +1,43 @@ + + * @license MIT + */ +interface SignatureCheckerInterface +{ + /** + * Checks whether the given signature is valid or not + * + * @param ReflectionClass $class + * @param array $parameters + * + * @return void + * + * @throws \ProxyManager\Signature\Exception\InvalidSignatureException + * @throws \ProxyManager\Signature\Exception\MissingSignatureException + */ + public function checkSignature(ReflectionClass $class, array $parameters); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php new file mode 100644 index 000000000..473d3eab7 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGenerator.php @@ -0,0 +1,66 @@ + + * @license MIT + */ +final class SignatureGenerator implements SignatureGeneratorInterface +{ + /** + * @var ParameterEncoder + */ + private $parameterEncoder; + + /** + * @var ParameterHasher + */ + private $parameterHasher; + + /** + * Constructor. + */ + public function __construct() + { + $this->parameterEncoder = new ParameterEncoder(); + $this->parameterHasher = new ParameterHasher(); + } + + /** + * {@inheritDoc} + */ + public function generateSignature(array $parameters) + { + return $this->parameterEncoder->encodeParameters($parameters); + } + + /** + * {@inheritDoc} + */ + public function generateSignatureKey(array $parameters) + { + return $this->parameterHasher->hashParameters($parameters); + } +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php new file mode 100644 index 000000000..68ad30bd0 --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Signature/SignatureGeneratorInterface.php @@ -0,0 +1,46 @@ + + * @license MIT + */ +interface SignatureGeneratorInterface +{ + /** + * Generates a signature to be used to verify generated code validity + * + * @param array $parameters + * + * @return string + */ + public function generateSignature(array $parameters); + + /** + * Generates a signature key to be looked up when verifying generated code validity + * + * @param array $parameters + * + * @return string + */ + public function generateSignatureKey(array $parameters); +} diff --git a/ocramius/proxy-manager/src/ProxyManager/Version.php b/ocramius/proxy-manager/src/ProxyManager/Version.php new file mode 100644 index 000000000..a0367114c --- /dev/null +++ b/ocramius/proxy-manager/src/ProxyManager/Version.php @@ -0,0 +1,39 @@ + + * @license MIT + */ +final class Version +{ + const VERSION = '1.0.0'; + + /** + * Private constructor - this class is not meant to be instantiated + */ + private function __construct() + { + } +} diff --git a/symfony/yaml/Command/LintCommand.php b/symfony/yaml/Command/LintCommand.php new file mode 100644 index 000000000..ea86a1f8c --- /dev/null +++ b/symfony/yaml/Command/LintCommand.php @@ -0,0 +1,234 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Command; + +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 Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser; + +/** + * Validates YAML files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + */ +class LintCommand extends Command +{ + private $parser; + private $format; + private $displayCorrectFiles; + private $directoryIteratorProvider; + private $isReadableProvider; + + public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + { + parent::__construct($name); + + $this->directoryIteratorProvider = $directoryIteratorProvider; + $this->isReadableProvider = $isReadableProvider; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('lint:yaml') + ->setDescription('Lints a file and outputs encountered errors') + ->addArgument('filename', null, 'A file or a directory or STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt') + ->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT +the first encountered syntax error. + +You can validates YAML contents passed from STDIN: + + cat filename | php %command.full_name% + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $filename = $input->getArgument('filename'); + $this->format = $input->getOption('format'); + $this->displayCorrectFiles = $output->isVerbose(); + + if (!$filename) { + if (!$stdin = $this->getStdin()) { + throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.'); + } + + return $this->display($io, array($this->validate($stdin))); + } + + if (!$this->isReadable($filename)) { + throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + } + + $filesInfo = array(); + foreach ($this->getFiles($filename) as $file) { + $filesInfo[] = $this->validate(file_get_contents($file), $file); + } + + return $this->display($io, $filesInfo); + } + + private function validate($content, $file = null) + { + try { + $this->getParser()->parse($content); + } catch (ParseException $e) { + return array('file' => $file, 'valid' => false, 'message' => $e->getMessage()); + } + + return array('file' => $file, 'valid' => true); + } + + private function display(SymfonyStyle $io, array $files) + { + switch ($this->format) { + case 'txt': + return $this->displayTxt($io, $files); + case 'json': + return $this->displayJson($io, $files); + default: + throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); + } + } + + private function displayTxt(SymfonyStyle $io, array $filesInfo) + { + $countFiles = count($filesInfo); + $erroredFiles = 0; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $this->displayCorrectFiles) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$erroredFiles; + $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->text(sprintf(' >> %s', $info['message'])); + } + } + + if ($erroredFiles === 0) { + $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles)); + } else { + $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles)); + } + + return min($erroredFiles, 1); + } + + private function displayJson(SymfonyStyle $io, array $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + if (!$v['valid']) { + ++$errors; + } + }); + + $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + return min($errors, 1); + } + + private function getFiles($fileOrDirectory) + { + if (is_file($fileOrDirectory)) { + yield new \SplFileInfo($fileOrDirectory); + + return; + } + + foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { + if (!in_array($file->getExtension(), array('yml', 'yaml'))) { + continue; + } + + yield $file; + } + } + + private function getStdin() + { + if (0 !== ftell(STDIN)) { + return; + } + + $inputs = ''; + while (!feof(STDIN)) { + $inputs .= fread(STDIN, 1024); + } + + return $inputs; + } + + private function getParser() + { + if (!$this->parser) { + $this->parser = new Parser(); + } + + return $this->parser; + } + + private function getDirectoryIterator($directory) + { + $default = function ($directory) { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + }; + + if (null !== $this->directoryIteratorProvider) { + return call_user_func($this->directoryIteratorProvider, $directory, $default); + } + + return $default($directory); + } + + private function isReadable($fileOrDirectory) + { + $default = function ($fileOrDirectory) { + return is_readable($fileOrDirectory); + }; + + if (null !== $this->isReadableProvider) { + return call_user_func($this->isReadableProvider, $fileOrDirectory, $default); + } + + return $default($fileOrDirectory); + } +} diff --git a/symfony/yaml/Dumper.php b/symfony/yaml/Dumper.php new file mode 100644 index 000000000..98d82434c --- /dev/null +++ b/symfony/yaml/Dumper.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Dumper dumps PHP variables to YAML strings. + * + * @author Fabien Potencier + */ +class Dumper +{ + /** + * The amount of spaces to use for indentation of nested nodes. + * + * @var int + */ + protected $indentation; + + /** + * @param int $indentation + */ + public function __construct($indentation = 4) + { + if ($indentation < 1) { + throw new \InvalidArgumentException('The indentation must be greater than zero.'); + } + + $this->indentation = $indentation; + } + + /** + * Sets the indentation. + * + * @param int $num The amount of spaces to use for indentation of nested nodes + */ + public function setIndentation($num) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED); + + $this->indentation = (int) $num; + } + + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The level of indentation (used internally) + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0, $flags = 0) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 5) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(4)) { + $flags |= Yaml::DUMP_OBJECT; + } + } + + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) { + $output .= $prefix.Inline::dump($input, $flags); + } else { + $isAHash = Inline::isHash($input); + + foreach ($input as $key => $value) { + if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) { + $output .= sprintf("%s%s%s |\n", $prefix, $isAHash ? Inline::dump($key, $flags).':' : '-', ''); + + foreach (preg_split('/\n|\r\n/', $value) as $row) { + $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row); + } + + continue; + } + + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? Inline::dump($key, $flags).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} diff --git a/symfony/yaml/Escaper.php b/symfony/yaml/Escaper.php new file mode 100644 index 000000000..a74f14dd9 --- /dev/null +++ b/symfony/yaml/Escaper.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Escaper encapsulates escaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + * + * @internal + */ +class Escaper +{ + // Characters that would cause a dumped string to require double quoting. + const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; + + // Mapping arrays for escaping a double quoted string. The backslash is + // first to ensure proper escaping because str_replace operates iteratively + // on the input arrays. This ordering of the characters avoids the use of strtr, + // which performs more slowly. + private static $escapees = array('\\', '\\\\', '\\"', '"', + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"); + private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', + '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', + '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', + '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', + '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', + '\\N', '\\_', '\\L', '\\P'); + + /** + * Determines if a PHP value would require double quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require double quotes + */ + public static function requiresDoubleQuoting($value) + { + return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); + } + + /** + * Escapes and surrounds a PHP value with double quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithDoubleQuotes($value) + { + return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); + } + + /** + * Determines if a PHP value would require single quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require single quotes + */ + public static function requiresSingleQuoting($value) + { + // Determines if a PHP value is entirely composed of a value that would + // require single quoting in YAML. + if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) { + return true; + } + + // Determines if the PHP value contains any single characters that would + // cause it to require single quoting in YAML. + return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); + } + + /** + * Escapes and surrounds a PHP value with single quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithSingleQuotes($value) + { + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + } +} diff --git a/symfony/yaml/Exception/DumpException.php b/symfony/yaml/Exception/DumpException.php new file mode 100644 index 000000000..cce972f24 --- /dev/null +++ b/symfony/yaml/Exception/DumpException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during dumping. + * + * @author Fabien Potencier + */ +class DumpException extends RuntimeException +{ +} diff --git a/symfony/yaml/Exception/ExceptionInterface.php b/symfony/yaml/Exception/ExceptionInterface.php new file mode 100644 index 000000000..ad850eea1 --- /dev/null +++ b/symfony/yaml/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Fabien Potencier + */ +interface ExceptionInterface +{ +} diff --git a/symfony/yaml/Exception/ParseException.php b/symfony/yaml/Exception/ParseException.php new file mode 100644 index 000000000..ba3be7d4f --- /dev/null +++ b/symfony/yaml/Exception/ParseException.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Fabien Potencier + */ +class ParseException extends RuntimeException +{ + private $parsedFile; + private $parsedLine; + private $snippet; + private $rawMessage; + + /** + * Constructor. + * + * @param string $message The error message + * @param int $parsedLine The line where the error occurred + * @param int $snippet The snippet of code near the problem + * @param string $parsedFile The file name where the error occurred + * @param \Exception $previous The previous exception + */ + public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) + { + $this->parsedFile = $parsedFile; + $this->parsedLine = $parsedLine; + $this->snippet = $snippet; + $this->rawMessage = $message; + + $this->updateRepr(); + + parent::__construct($this->message, 0, $previous); + } + + /** + * Gets the snippet of code near the error. + * + * @return string The snippet of code + */ + public function getSnippet() + { + return $this->snippet; + } + + /** + * Sets the snippet of code near the error. + * + * @param string $snippet The code snippet + */ + public function setSnippet($snippet) + { + $this->snippet = $snippet; + + $this->updateRepr(); + } + + /** + * Gets the filename where the error occurred. + * + * This method returns null if a string is parsed. + * + * @return string The filename + */ + public function getParsedFile() + { + return $this->parsedFile; + } + + /** + * Sets the filename where the error occurred. + * + * @param string $parsedFile The filename + */ + public function setParsedFile($parsedFile) + { + $this->parsedFile = $parsedFile; + + $this->updateRepr(); + } + + /** + * Gets the line where the error occurred. + * + * @return int The file line + */ + public function getParsedLine() + { + return $this->parsedLine; + } + + /** + * Sets the line where the error occurred. + * + * @param int $parsedLine The file line + */ + public function setParsedLine($parsedLine) + { + $this->parsedLine = $parsedLine; + + $this->updateRepr(); + } + + private function updateRepr() + { + $this->message = $this->rawMessage; + + $dot = false; + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if (null !== $this->parsedFile) { + $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + if ($this->parsedLine >= 0) { + $this->message .= sprintf(' at line %d', $this->parsedLine); + } + + if ($this->snippet) { + $this->message .= sprintf(' (near "%s")', $this->snippet); + } + + if ($dot) { + $this->message .= '.'; + } + } +} diff --git a/symfony/yaml/Exception/RuntimeException.php b/symfony/yaml/Exception/RuntimeException.php new file mode 100644 index 000000000..3f36b73be --- /dev/null +++ b/symfony/yaml/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Romain Neutron + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/symfony/yaml/Inline.php b/symfony/yaml/Inline.php new file mode 100644 index 000000000..8ad3b1e1b --- /dev/null +++ b/symfony/yaml/Inline.php @@ -0,0 +1,728 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Exception\DumpException; + +/** + * Inline implements a YAML parser/dumper for the YAML inline syntax. + * + * @author Fabien Potencier + * + * @internal + */ +class Inline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + public static $parsedLineNumber; + + private static $exceptionOnInvalidType = false; + private static $objectSupport = false; + private static $objectForMap = false; + private static $constantSupport = false; + + /** + * Converts a YAML string to a PHP value. + * + * @param string $value A YAML string + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * @param array $references Mapping of variable names to values + * + * @return mixed A PHP value + * + * @throws ParseException + */ + public static function parse($value, $flags = 0, $references = array()) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 3 && !is_array($references)) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); + + if ($references) { + $flags |= Yaml::PARSE_OBJECT; + } + + if (func_num_args() >= 4) { + @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(3)) { + $flags |= Yaml::PARSE_OBJECT_FOR_MAP; + } + } + + if (func_num_args() >= 5) { + $references = func_get_arg(4); + } else { + $references = array(); + } + } + + self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); + self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); + self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); + self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags); + + $value = trim($value); + + if ('' === $value) { + return ''; + } + + if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $i = 0; + switch ($value[0]) { + case '[': + $result = self::parseSequence($value, $flags, $i, $references); + ++$i; + break; + case '{': + $result = self::parseMapping($value, $flags, $i, $references); + ++$i; + break; + default: + $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references); + } + + // some comments are allowed at the end + if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + * + * @return string The YAML string representing the PHP value + * + * @throws DumpException When trying to dump PHP resource + */ + public static function dump($value, $flags = 0) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 3) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(2)) { + $flags |= Yaml::DUMP_OBJECT; + } + } + + switch (true) { + case is_resource($value): + if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { + throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); + } + + return 'null'; + case $value instanceof \DateTimeInterface: + return $value->format('c'); + case is_object($value): + if (Yaml::DUMP_OBJECT & $flags) { + return '!php/object:'.serialize($value); + } + + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { + return self::dumpArray((array) $value, $flags); + } + + if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { + throw new DumpException('Object support when dumping a YAML file has been disabled.'); + } + + return 'null'; + case is_array($value): + return self::dumpArray($value, $flags); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + $locale = setlocale(LC_NUMERIC, 0); + if (false !== $locale) { + setlocale(LC_NUMERIC, 'C'); + } + if (is_float($value)) { + $repr = (string) $value; + if (is_infinite($value)) { + $repr = str_ireplace('INF', '.Inf', $repr); + } elseif (floor($value) == $value && $repr == $value) { + // Preserve float data type since storing a whole number will result in integer value. + $repr = '!!float '.$repr; + } + } else { + $repr = is_string($value) ? "'$value'" : (string) $value; + } + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + + return $repr; + case '' == $value: + return "''"; + case self::isBinaryString($value): + return '!!binary '.base64_encode($value); + case Escaper::requiresDoubleQuoting($value): + return Escaper::escapeWithDoubleQuotes($value); + case Escaper::requiresSingleQuoting($value): + case preg_match('{^[0-9]+[_0-9]*$}', $value): + case preg_match(self::getHexRegex(), $value): + case preg_match(self::getTimestampRegex(), $value): + return Escaper::escapeWithSingleQuotes($value); + default: + return $value; + } + } + + /** + * Check if given array is hash or just normal indexed array. + * + * @internal + * + * @param array $value The PHP array to check + * + * @return bool true if value is hash array, false otherwise + */ + public static function isHash(array $value) + { + $expectedKey = 0; + + foreach ($value as $key => $val) { + if ($key !== $expectedKey++) { + return true; + } + } + + return false; + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + * + * @return string The YAML string representing the PHP array + */ + private static function dumpArray($value, $flags) + { + // array + if ($value && !self::isHash($value)) { + $output = array(); + foreach ($value as $val) { + $output[] = self::dump($val, $flags); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // hash + $output = array(); + foreach ($value as $key => $val) { + $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a YAML scalar. + * + * @param string $scalar + * @param int $flags + * @param string $delimiters + * @param array $stringDelimiters + * @param int &$i + * @param bool $evaluate + * @param array $references + * + * @return string + * + * @throws ParseException When malformed inline YAML string is parsed + * + * @internal + */ + public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) + { + if (in_array($scalar[$i], $stringDelimiters)) { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + + if (null !== $delimiters) { + $tmp = ltrim(substr($scalar, $i), ' '); + if (!in_array($tmp[0], $delimiters)) { + throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); + } + } + } else { + // "normal" string + if (!$delimiters) { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { + $output = substr($output, 0, $match[0][1]); + } + } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + $output = $match[1]; + $i += strlen($output); + } else { + throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar)); + } + + // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) + if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) { + throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0])); + } + + if ($output && '%' === $output[0]) { + @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output), E_USER_DEPRECATED); + } + + if ($evaluate) { + $output = self::evaluateScalar($output, $flags, $references); + } + } + + return $output; + } + + /** + * Parses a YAML quoted scalar. + * + * @param string $scalar + * @param int &$i + * + * @return string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + $unescaper = new Unescaper(); + if ('"' == $scalar[$i]) { + $output = $unescaper->unescapeDoubleQuotedString($output); + } else { + $output = $unescaper->unescapeSingleQuotedString($output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a YAML sequence. + * + * @param string $sequence + * @param int $flags + * @param int &$i + * @param array $references + * + * @return array + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseSequence($sequence, $flags, &$i = 0, $references = array()) + { + $output = array(); + $len = strlen($sequence); + ++$i; + + // [foo, bar, ...] + while ($i < $len) { + switch ($sequence[$i]) { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $flags, $i, $references); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $flags, $i, $references); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references); + + // the value can be an array if a reference has been resolved to an array var + if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { + // embedded mapping? + try { + $pos = 0; + $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); + } catch (\InvalidArgumentException $e) { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence)); + } + + /** + * Parses a YAML mapping. + * + * @param string $mapping + * @param int $flags + * @param int &$i + * @param array $references + * + * @return array|\stdClass + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseMapping($mapping, $flags, &$i = 0, $references = array()) + { + $output = array(); + $len = strlen($mapping); + ++$i; + + // {foo: bar, bar:foo, ...} + while ($i < $len) { + switch ($mapping[$i]) { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + if (self::$objectForMap) { + return (object) $output; + } + + return $output; + } + + // key + $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false); + + if (false === $i = strpos($mapping, ':', $i)) { + break; + } + + if (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', '[', ']', '{', '}'), true)) { + @trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED); + } + + // value + $done = false; + + while ($i < $len) { + switch ($mapping[$i]) { + case '[': + // nested sequence + $value = self::parseSequence($mapping, $flags, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); + } + $done = true; + break; + case '{': + // nested mapping + $value = self::parseMapping($mapping, $flags, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); + } + $done = true; + break; + case ':': + case ' ': + break; + default: + $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); + } + $done = true; + --$i; + } + + ++$i; + + if ($done) { + continue 2; + } + } + } + + throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * @param int $flags + * @param array $references + * + * @return string A YAML string + * + * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved + */ + private static function evaluateScalar($scalar, $flags, $references = array()) + { + $scalar = trim($scalar); + $scalarLower = strtolower($scalar); + + if (0 === strpos($scalar, '*')) { + if (false !== $pos = strpos($scalar, '#')) { + $value = substr($scalar, 1, $pos - 2); + } else { + $value = substr($scalar, 1); + } + + // an unquoted * + if (false === $value || '' === $value) { + throw new ParseException('A reference must contain at least one character.'); + } + + if (!array_key_exists($value, $references)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); + } + + return $references[$value]; + } + + switch (true) { + case 'null' === $scalarLower: + case '' === $scalar: + case '~' === $scalar: + return; + case 'true' === $scalarLower: + return true; + case 'false' === $scalarLower: + return false; + // Optimise for returning strings. + case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]): + switch (true) { + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return (int) self::parseScalar(substr($scalar, 2), $flags); + case 0 === strpos($scalar, '!php/object:'): + if (self::$objectSupport) { + return unserialize(substr($scalar, 12)); + } + + if (self::$exceptionOnInvalidType) { + throw new ParseException('Object support when parsing a YAML file has been disabled.'); + } + + return; + case 0 === strpos($scalar, '!!php/object:'): + if (self::$objectSupport) { + @trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED); + + return unserialize(substr($scalar, 13)); + } + + if (self::$exceptionOnInvalidType) { + throw new ParseException('Object support when parsing a YAML file has been disabled.'); + } + + return; + case 0 === strpos($scalar, '!php/const:'): + if (self::$constantSupport) { + if (defined($const = substr($scalar, 11))) { + return constant($const); + } + + throw new ParseException(sprintf('The constant "%s" is not defined.', $const)); + } + if (self::$exceptionOnInvalidType) { + throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar)); + } + + return; + case 0 === strpos($scalar, '!!float '): + return (float) substr($scalar, 8); + case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar): + $scalar = str_replace('_', '', (string) $scalar); + // omitting the break / return as integers are handled in the next case + case ctype_digit($scalar): + $raw = $scalar; + $cast = (int) $scalar; + + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): + $raw = $scalar; + $cast = (int) $scalar; + + return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); + case is_numeric($scalar): + case preg_match(self::getHexRegex(), $scalar): + $scalar = str_replace('_', '', $scalar); + + return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; + case '.inf' === $scalarLower: + case '.nan' === $scalarLower: + return -log(0); + case '-.inf' === $scalarLower: + return log(0); + case 0 === strpos($scalar, '!!binary '): + return self::evaluateBinaryScalar(substr($scalar, 9)); + case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar): + case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): + if (false !== strpos($scalar, ',')) { + @trigger_error('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED); + } + + return (float) str_replace(array(',', '_'), '', $scalar); + case preg_match(self::getTimestampRegex(), $scalar): + if (Yaml::PARSE_DATETIME & $flags) { + // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. + return new \DateTime($scalar, new \DateTimeZone('UTC')); + } + + $timeZone = date_default_timezone_get(); + date_default_timezone_set('UTC'); + $time = strtotime($scalar); + date_default_timezone_set($timeZone); + + return $time; + } + default: + return (string) $scalar; + } + } + + /** + * @param string $scalar + * + * @return string + * + * @internal + */ + public static function evaluateBinaryScalar($scalar) + { + $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); + + if (0 !== (strlen($parsedBinaryData) % 4)) { + throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData))); + } + + if (!preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { + throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData)); + } + + return base64_decode($parsedBinaryData, true); + } + + private static function isBinaryString($value) + { + return !preg_match('//u', $value) || preg_match('/[^\x09-\x0d\x20-\xff]/', $value); + } + + /** + * Gets a regex that matches a YAML date. + * + * @return string The regular expression + * + * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 + */ + private static function getTimestampRegex() + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } + + /** + * Gets a regex that matches a YAML number in hexadecimal notation. + * + * @return string + */ + private static function getHexRegex() + { + return '~^0x[0-9a-f_]++$~i'; + } +} diff --git a/symfony/yaml/Parser.php b/symfony/yaml/Parser.php new file mode 100644 index 000000000..cb886adac --- /dev/null +++ b/symfony/yaml/Parser.php @@ -0,0 +1,879 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Parser parses YAML strings to convert them to PHP arrays. + * + * @author Fabien Potencier + */ +class Parser +{ + const TAG_PATTERN = '((?P![\w!.\/:-]+) +)?'; + const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; + + private $offset = 0; + private $totalNumberOfLines; + private $lines = array(); + private $currentLineNb = -1; + private $currentLine = ''; + private $refs = array(); + private $skippedLineNumbers = array(); + private $locallySkippedLineNumbers = array(); + + /** + * Constructor. + * + * @param int $offset The offset of YAML document (used for line numbers in error messages) + * @param int|null $totalNumberOfLines The overall number of lines being parsed + * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser + */ + public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array()) + { + $this->offset = $offset; + $this->totalNumberOfLines = $totalNumberOfLines; + $this->skippedLineNumbers = $skippedLineNumbers; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * + * @return mixed A PHP value + * + * @throws ParseException If the YAML is not valid + */ + public function parse($value, $flags = 0) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 3) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(2)) { + $flags |= Yaml::PARSE_OBJECT; + } + } + + if (func_num_args() >= 4) { + @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(3)) { + $flags |= Yaml::PARSE_OBJECT_FOR_MAP; + } + } + + if (!preg_match('//u', $value)) { + throw new ParseException('The YAML value does not appear to be valid UTF-8.'); + } + $this->currentLineNb = -1; + $this->currentLine = ''; + $value = $this->cleanup($value); + $this->lines = explode("\n", $value); + + if (null === $this->totalNumberOfLines) { + $this->totalNumberOfLines = count($this->lines); + } + + if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + } + + $data = array(); + $context = null; + $allowOverwrite = false; + while ($this->moveToNextLine()) { + if ($this->isCurrentLineEmpty()) { + continue; + } + + // tab? + if ("\t" === $this->currentLine[0]) { + throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $isRef = $mergeNode = false; + if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { + if ($context && 'mapping' == $context) { + throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + $context = 'sequence'; + + if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); + } else { + if (isset($values['leadspaces']) + && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) + ) { + // this is a compact notation element, add to next block and parse + $block = $values['value']; + if ($this->isNextLineIndented()) { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); + } + + $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); + } else { + $data[] = $this->parseValue($values['value'], $flags, $context); + } + } + if ($isRef) { + $this->refs[$isRef] = end($data); + } + } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { + if ($context && 'sequence' == $context) { + throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); + } + $context = 'mapping'; + + // force correct settings + Inline::parse(null, $flags, $this->refs); + try { + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $key = Inline::parseScalar($values['key']); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + // Convert float keys to strings, to avoid being converted to integers by PHP + if (is_float($key)) { + $key = (string) $key; + } + + if ('<<' === $key) { + $mergeNode = true; + $allowOverwrite = true; + if (isset($values['value']) && 0 === strpos($values['value'], '*')) { + $refName = substr($values['value'], 1); + if (!array_key_exists($refName, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $refValue = $this->refs[$refName]; + + if (!is_array($refValue)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + foreach ($refValue as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } else { + if (isset($values['value']) && $values['value'] !== '') { + $value = $values['value']; + } else { + $value = $this->getNextEmbedBlock(); + } + $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); + + if (!is_array($parsed)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + if (isset($parsed[0])) { + // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes + // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier + // in the sequence override keys specified in later mapping nodes. + foreach ($parsed as $parsedItem) { + if (!is_array($parsedItem)) { + throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); + } + + foreach ($parsedItem as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } + } else { + // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the + // current mapping, unless the key already exists in it. + foreach ($parsed as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } + } + } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($mergeNode) { + // Merge keys + } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + // hash + // if next line is less indented or equal, then it means that the current value is null + if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = null; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); + } + } else { + // remember the parsed line number here in case we need it to provide some contexts in error messages below + $realCurrentLineNbKey = $this->getRealCurrentLineNb(); + $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = $value; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED); + } + } + } else { + $value = $this->parseValue($values['value'], $flags, $context); + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = $value; + } else { + @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); + } + } + if ($isRef) { + $this->refs[$isRef] = $data[$key]; + } + } else { + // multiple documents are not supported + if ('---' === $this->currentLine) { + throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); + } + + // 1-liner optionally followed by newline(s) + if (is_string($value) && $this->lines[0] === trim($value)) { + try { + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $value = Inline::parse($this->lines[0], $flags, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error.'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached.'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached.'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data.'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; + break; + default: + $error = 'Unable to parse.'; + } + + throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) { + $object = new \stdClass(); + + foreach ($data as $key => $value) { + $object->$key = $value; + } + + $data = $object; + } + + return empty($data) ? null : $data; + } + + private function parseBlock($offset, $yaml, $flags) + { + $skippedLineNumbers = $this->skippedLineNumbers; + + foreach ($this->locallySkippedLineNumbers as $lineNumber) { + if ($lineNumber < $offset) { + continue; + } + + $skippedLineNumbers[] = $lineNumber; + } + + $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers); + $parser->refs = &$this->refs; + + return $parser->parse($yaml, $flags); + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return int The current line number + */ + private function getRealCurrentLineNb() + { + $realCurrentLineNumber = $this->currentLineNb + $this->offset; + + foreach ($this->skippedLineNumbers as $skippedLineNumber) { + if ($skippedLineNumber > $realCurrentLineNumber) { + break; + } + + ++$realCurrentLineNumber; + } + + return $realCurrentLineNumber; + } + + /** + * Returns the current line indentation. + * + * @return int The current line indentation + */ + private function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param int $indentation The indent level at which the block is to be read, or null for default + * @param bool $inSequence True if the enclosing data structure is a sequence + * + * @return string A YAML string + * + * @throws ParseException When indentation problem are detected + */ + private function getNextEmbedBlock($indentation = null, $inSequence = false) + { + $oldLineIndentation = $this->getCurrentLineIndentation(); + $blockScalarIndentations = array(); + + if ($this->isBlockScalarHeader()) { + $blockScalarIndentations[] = $this->getCurrentLineIndentation(); + } + + if (!$this->moveToNextLine()) { + return; + } + + if (null === $indentation) { + $newIndent = $this->getCurrentLineIndentation(); + + $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); + + if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } else { + $newIndent = $indentation; + } + + $data = array(); + if ($this->getCurrentLineIndentation() >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent); + } else { + $this->moveToPreviousLine(); + + return; + } + + if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { + // the previous line contained a dash but no item content, this line is a sequence item with the same indentation + // and therefore no nested list or mapping + $this->moveToPreviousLine(); + + return; + } + + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); + + if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) { + $blockScalarIndentations[] = $this->getCurrentLineIndentation(); + } + + $previousLineIndentation = $this->getCurrentLineIndentation(); + + while ($this->moveToNextLine()) { + $indent = $this->getCurrentLineIndentation(); + + // terminate all block scalars that are more indented than the current line + if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') { + foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { + if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) { + unset($blockScalarIndentations[$key]); + } + } + } + + if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { + $blockScalarIndentations[] = $this->getCurrentLineIndentation(); + } + + $previousLineIndentation = $indent; + + if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { + $this->moveToPreviousLine(); + break; + } + + if ($this->isCurrentLineBlank()) { + $data[] = substr($this->currentLine, $newIndent); + continue; + } + + // we ignore "comment" lines only when we are not inside a scalar block + if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { + // remember ignored comment lines (they are used later in nested + // parser calls to determine real line numbers) + // + // CAUTION: beware to not populate the global property here as it + // will otherwise influence the getRealCurrentLineNb() call here + // for consecutive comment lines and subsequent embedded blocks + $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); + + continue; + } + + if ($indent >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent); + } elseif (0 == $indent) { + $this->moveToPreviousLine(); + + break; + } else { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + * + * @return bool + */ + private function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + * + * @return bool + */ + private function moveToPreviousLine() + { + if ($this->currentLineNb < 1) { + return false; + } + + $this->currentLine = $this->lines[--$this->currentLineNb]; + + return true; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * @param string $context The parser context (either sequence or mapping) + * + * @return mixed A PHP value + * + * @throws ParseException When reference does not exist + */ + private function parseValue($value, $flags, $context) + { + if (0 === strpos($value, '*')) { + if (false !== $pos = strpos($value, '#')) { + $value = substr($value, 1, $pos - 2); + } else { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); + } + + return $this->refs[$value]; + } + + if (preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); + + if (isset($matches['tag']) && '!!binary' === $matches['tag']) { + return Inline::evaluateBinaryScalar($data); + } + + return $data; + } + + try { + $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; + + // do not take following lines into account when the current line is a quoted single line value + if (null !== $quotation && preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { + return Inline::parse($value, $flags, $this->refs); + } + + while ($this->moveToNextLine()) { + // unquoted strings end before the first unindented line + if (null === $quotation && $this->getCurrentLineIndentation() === 0) { + $this->moveToPreviousLine(); + + break; + } + + $value .= ' '.trim($this->currentLine); + + // quoted string values end with a line that is terminated with the quotation character + if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { + break; + } + } + + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $parsedValue = Inline::parse($value, $flags, $this->refs); + + if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { + throw new ParseException('A colon cannot be used in an unquoted mapping value.'); + } + + return $parsedValue; + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } + + /** + * Parses a block scalar. + * + * @param string $style The style indicator that was used to begin this block scalar (| or >) + * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) + * @param int $indentation The indentation indicator that was used to begin this block scalar + * + * @return string The text value + */ + private function parseBlockScalar($style, $chomping = '', $indentation = 0) + { + $notEOF = $this->moveToNextLine(); + if (!$notEOF) { + return ''; + } + + $isCurrentLineBlank = $this->isCurrentLineBlank(); + $blockLines = array(); + + // leading blank lines are consumed before determining indentation + while ($notEOF && $isCurrentLineBlank) { + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $blockLines[] = ''; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + + // determine indentation if not specified + if (0 === $indentation) { + if (preg_match('/^ +/', $this->currentLine, $matches)) { + $indentation = strlen($matches[0]); + } + } + + if ($indentation > 0) { + $pattern = sprintf('/^ {%d}(.*)$/', $indentation); + + while ( + $notEOF && ( + $isCurrentLineBlank || + preg_match($pattern, $this->currentLine, $matches) + ) + ) { + if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) { + $blockLines[] = substr($this->currentLine, $indentation); + } elseif ($isCurrentLineBlank) { + $blockLines[] = ''; + } else { + $blockLines[] = $matches[1]; + } + + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + } elseif ($notEOF) { + $blockLines[] = ''; + } + + if ($notEOF) { + $blockLines[] = ''; + $this->moveToPreviousLine(); + } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { + $blockLines[] = ''; + } + + // folded style + if ('>' === $style) { + $text = ''; + $previousLineIndented = false; + $previousLineBlank = false; + + for ($i = 0; $i < count($blockLines); ++$i) { + if ('' === $blockLines[$i]) { + $text .= "\n"; + $previousLineIndented = false; + $previousLineBlank = true; + } elseif (' ' === $blockLines[$i][0]) { + $text .= "\n".$blockLines[$i]; + $previousLineIndented = true; + $previousLineBlank = false; + } elseif ($previousLineIndented) { + $text .= "\n".$blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } elseif ($previousLineBlank || 0 === $i) { + $text .= $blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } else { + $text .= ' '.$blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } + } + } else { + $text = implode("\n", $blockLines); + } + + // deal with trailing newlines + if ('' === $chomping) { + $text = preg_replace('/\n+$/', "\n", $text); + } elseif ('-' === $chomping) { + $text = preg_replace('/\n+$/', '', $text); + } + + return $text; + } + + /** + * Returns true if the next line is indented. + * + * @return bool Returns true if the next line is indented, false otherwise + */ + private function isNextLineIndented() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $EOF = !$this->moveToNextLine(); + + while (!$EOF && $this->isCurrentLineEmpty()) { + $EOF = !$this->moveToNextLine(); + } + + if ($EOF) { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() > $currentIndentation) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise + */ + private function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return bool Returns true if the current line is blank, false otherwise + */ + private function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return bool Returns true if the current line is a comment line, false otherwise + */ + private function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + + return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#'; + } + + private function isCurrentLineLastLineInDocument() + { + return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + private function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#', '', $value); + } + + return $value; + } + + /** + * Returns true if the next line starts unindented collection. + * + * @return bool Returns true if the next line starts unindented collection, false otherwise + */ + private function isNextLineUnIndentedCollection() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) { + return false; + } + + $ret = false; + if ( + $this->getCurrentLineIndentation() == $currentIndentation + && + $this->isStringUnIndentedCollectionItem() + ) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the string is un-indented collection item. + * + * @return bool Returns true if the string is un-indented collection item, false otherwise + */ + private function isStringUnIndentedCollectionItem() + { + return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); + } + + /** + * Tests whether or not the current line is the header of a block scalar. + * + * @return bool + */ + private function isBlockScalarHeader() + { + return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); + } +} diff --git a/symfony/yaml/Unescaper.php b/symfony/yaml/Unescaper.php new file mode 100644 index 000000000..6e863e12f --- /dev/null +++ b/symfony/yaml/Unescaper.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Unescaper encapsulates unescaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + * + * @internal + */ +class Unescaper +{ + /** + * Regex fragment that matches an escaped character in a double quoted string. + */ + const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; + + /** + * Unescapes a single quoted string. + * + * @param string $value A single quoted string + * + * @return string The unescaped string + */ + public function unescapeSingleQuotedString($value) + { + return str_replace('\'\'', '\'', $value); + } + + /** + * Unescapes a double quoted string. + * + * @param string $value A double quoted string + * + * @return string The unescaped string + */ + public function unescapeDoubleQuotedString($value) + { + $callback = function ($match) { + return $this->unescapeCharacter($match[0]); + }; + + // evaluate the string + return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); + } + + /** + * Unescapes a character that was found in a double-quoted string. + * + * @param string $value An escaped character + * + * @return string The unescaped character + */ + private function unescapeCharacter($value) + { + switch ($value[1]) { + case '0': + return "\x0"; + case 'a': + return "\x7"; + case 'b': + return "\x8"; + case 't': + return "\t"; + case "\t": + return "\t"; + case 'n': + return "\n"; + case 'v': + return "\xB"; + case 'f': + return "\xC"; + case 'r': + return "\r"; + case 'e': + return "\x1B"; + case ' ': + return ' '; + case '"': + return '"'; + case '/': + return '/'; + case '\\': + return '\\'; + case 'N': + // U+0085 NEXT LINE + return "\xC2\x85"; + case '_': + // U+00A0 NO-BREAK SPACE + return "\xC2\xA0"; + case 'L': + // U+2028 LINE SEPARATOR + return "\xE2\x80\xA8"; + case 'P': + // U+2029 PARAGRAPH SEPARATOR + return "\xE2\x80\xA9"; + case 'x': + return self::utf8chr(hexdec(substr($value, 2, 2))); + case 'u': + return self::utf8chr(hexdec(substr($value, 2, 4))); + case 'U': + return self::utf8chr(hexdec(substr($value, 2, 8))); + default: + throw new ParseException(sprintf('Found unknown escape character "%s".', $value)); + } + } + + /** + * Get the UTF-8 character for the given code point. + * + * @param int $c The unicode code point + * + * @return string The corresponding UTF-8 character + */ + private static function utf8chr($c) + { + if (0x80 > $c %= 0x200000) { + return chr($c); + } + if (0x800 > $c) { + return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } + + return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } +} diff --git a/symfony/yaml/Yaml.php b/symfony/yaml/Yaml.php new file mode 100644 index 000000000..46e6ef5c1 --- /dev/null +++ b/symfony/yaml/Yaml.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Yaml offers convenience methods to load and dump YAML. + * + * @author Fabien Potencier + */ +class Yaml +{ + const DUMP_OBJECT = 1; + const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; + const PARSE_OBJECT = 4; + const PARSE_OBJECT_FOR_MAP = 8; + const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; + const PARSE_DATETIME = 32; + const DUMP_OBJECT_AS_MAP = 64; + const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; + const PARSE_CONSTANT = 256; + + /** + * Parses YAML into a PHP value. + * + * Usage: + * + * $array = Yaml::parse(file_get_contents('config.yml')); + * print_r($array); + * + * + * @param string $input A string containing YAML + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * + * @return mixed The YAML converted to a PHP value + * + * @throws ParseException If the YAML is not valid + */ + public static function parse($input, $flags = 0) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 3) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(2)) { + $flags |= self::PARSE_OBJECT; + } + } + + if (func_num_args() >= 4) { + @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(3)) { + $flags |= self::PARSE_OBJECT_FOR_MAP; + } + } + + $yaml = new Parser(); + + return $yaml->parse($input, $flags); + } + + /** + * Dumps a PHP value to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The amount of spaces to use for indentation of nested nodes + * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string + * + * @return string A YAML string representing the original PHP value + */ + public static function dump($input, $inline = 2, $indent = 4, $flags = 0) + { + if (is_bool($flags)) { + @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); + + if ($flags) { + $flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE; + } else { + $flags = 0; + } + } + + if (func_num_args() >= 5) { + @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED); + + if (func_get_arg(4)) { + $flags |= self::DUMP_OBJECT; + } + } + + $yaml = new Dumper($indent); + + return $yaml->dump($input, $inline, 0, $flags); + } +} diff --git a/zendframework/zend-code/src/Annotation/AnnotationCollection.php b/zendframework/zend-code/src/Annotation/AnnotationCollection.php new file mode 100644 index 000000000..7dbd45f61 --- /dev/null +++ b/zendframework/zend-code/src/Annotation/AnnotationCollection.php @@ -0,0 +1,32 @@ +setIdentifiers([ + __CLASS__, + get_class($this), + ]); + $this->events = $events; + + return $this; + } + + /** + * Retrieve event manager + * + * Lazy loads an instance if none registered. + * + * @return EventManagerInterface + */ + public function getEventManager() + { + if (null === $this->events) { + $this->setEventManager(new EventManager()); + } + + return $this->events; + } + + /** + * Attach a parser to listen to the createAnnotation event + * + * @param ParserInterface $parser + * @return AnnotationManager + */ + public function attach(ParserInterface $parser) + { + $this->getEventManager() + ->attach(self::EVENT_CREATE_ANNOTATION, [$parser, 'onCreateAnnotation']); + + return $this; + } + + /** + * Create Annotation + * + * @param string[] $annotationData + * @return false|\stdClass + */ + public function createAnnotation(array $annotationData) + { + $event = new Event(); + $event->setName(self::EVENT_CREATE_ANNOTATION); + $event->setTarget($this); + $event->setParams([ + 'class' => $annotationData[0], + 'content' => $annotationData[1], + 'raw' => $annotationData[2], + ]); + + $eventManager = $this->getEventManager(); + $results = $eventManager->triggerEventUntil(function ($r) { + return (is_object($r)); + }, $event); + + $annotation = $results->last(); + + return (is_object($annotation) ? $annotation : false); + } +} diff --git a/zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php b/zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php new file mode 100644 index 000000000..adef137c9 --- /dev/null +++ b/zendframework/zend-code/src/Annotation/Parser/DoctrineAnnotationParser.php @@ -0,0 +1,153 @@ +docParser = $docParser; + return $this; + } + + /** + * Retrieve the DocParser instance + * + * If none is registered, lazy-loads a new instance. + * + * @return DocParser + */ + public function getDocParser() + { + if (!$this->docParser instanceof DocParser) { + $this->setDocParser(new DocParser()); + } + + return $this->docParser; + } + + /** + * Handle annotation creation + * + * @param EventInterface $e + * @return false|\stdClass + */ + public function onCreateAnnotation(EventInterface $e) + { + $annotationClass = $e->getParam('class', false); + if (!$annotationClass) { + return false; + } + + if (!isset($this->allowedAnnotations[$annotationClass])) { + return false; + } + + $annotationString = $e->getParam('raw', false); + if (!$annotationString) { + return false; + } + + // Annotation classes provided by the AnnotationScanner are already + // resolved to fully-qualified class names. Adding the global namespace + // prefix allows the Doctrine annotation parser to locate the annotation + // class correctly. + $annotationString = preg_replace('/^(@)/', '$1\\', $annotationString); + + $parser = $this->getDocParser(); + $annotations = $parser->parse($annotationString); + if (empty($annotations)) { + return false; + } + + $annotation = array_shift($annotations); + if (!is_object($annotation)) { + return false; + } + + return $annotation; + } + + /** + * Specify an allowed annotation class + * + * @param string $annotation + * @return DoctrineAnnotationParser + */ + public function registerAnnotation($annotation) + { + $this->allowedAnnotations[$annotation] = true; + return $this; + } + + /** + * Set many allowed annotations at once + * + * @param array|Traversable $annotations Array or traversable object of + * annotation class names + * @throws Exception\InvalidArgumentException + * @return DoctrineAnnotationParser + */ + public function registerAnnotations($annotations) + { + if (!is_array($annotations) && !$annotations instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array or Traversable; received "%s"', + __METHOD__, + (is_object($annotations) ? get_class($annotations) : gettype($annotations)) + )); + } + + foreach ($annotations as $annotation) { + $this->allowedAnnotations[$annotation] = true; + } + + return $this; + } +} diff --git a/zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php b/zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php new file mode 100644 index 000000000..6592e4f47 --- /dev/null +++ b/zendframework/zend-code/src/Annotation/Parser/GenericAnnotationParser.php @@ -0,0 +1,224 @@ +getParam('class', false); + if (!$class || !$this->hasAnnotation($class)) { + return false; + } + + $content = $e->getParam('content', ''); + $content = trim($content, '()'); + + if ($this->hasAlias($class)) { + $class = $this->resolveAlias($class); + } + + $index = array_search($class, $this->annotationNames); + $annotation = $this->annotations[$index]; + + $newAnnotation = clone $annotation; + if ($content) { + $newAnnotation->initialize($content); + } + + return $newAnnotation; + } + + /** + * Register annotations + * + * @param string|AnnotationInterface $annotation String class name of an + * AnnotationInterface implementation, or actual instance + * @return GenericAnnotationParser + * @throws Exception\InvalidArgumentException + */ + public function registerAnnotation($annotation) + { + $class = false; + if (is_string($annotation) && class_exists($annotation)) { + $class = $annotation; + $annotation = new $annotation(); + } + + if (!$annotation instanceof AnnotationInterface) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an instance of %s\AnnotationInterface; received "%s"', + __METHOD__, + __NAMESPACE__, + (is_object($annotation) ? get_class($annotation) : gettype($annotation)) + )); + } + + $class = $class ?: get_class($annotation); + + if (in_array($class, $this->annotationNames)) { + throw new Exception\InvalidArgumentException(sprintf( + 'An annotation for this class %s already exists', + $class + )); + } + + $this->annotations[] = $annotation; + $this->annotationNames[] = $class; + } + + /** + * Register many annotations at once + * + * @param array|Traversable $annotations + * @throws Exception\InvalidArgumentException + * @return GenericAnnotationParser + */ + public function registerAnnotations($annotations) + { + if (!is_array($annotations) && !$annotations instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array or Traversable; received "%s"', + __METHOD__, + (is_object($annotations) ? get_class($annotations) : gettype($annotations)) + )); + } + + foreach ($annotations as $annotation) { + $this->registerAnnotation($annotation); + } + + return $this; + } + + /** + * Checks if the manager has annotations for a class + * + * @param string $class + * @return bool + */ + public function hasAnnotation($class) + { + if (in_array($class, $this->annotationNames)) { + return true; + } + + if ($this->hasAlias($class)) { + return true; + } + + return false; + } + + /** + * Alias an annotation name + * + * @param string $alias + * @param string $class May be either a registered annotation name or another alias + * @throws Exception\InvalidArgumentException + * @return GenericAnnotationParser + */ + public function setAlias($alias, $class) + { + if (!in_array($class, $this->annotationNames) && !$this->hasAlias($class)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: Cannot alias "%s" to "%s", as class "%s" is not currently a registered annotation or alias', + __METHOD__, + $alias, + $class, + $class + )); + } + + $alias = $this->normalizeAlias($alias); + $this->aliases[$alias] = $class; + + return $this; + } + + /** + * Normalize an alias name + * + * @param string $alias + * @return string + */ + protected function normalizeAlias($alias) + { + return strtolower(str_replace(['-', '_', ' ', '\\', '/'], '', $alias)); + } + + /** + * Do we have an alias by the provided name? + * + * @param string $alias + * @return bool + */ + protected function hasAlias($alias) + { + $alias = $this->normalizeAlias($alias); + + return (isset($this->aliases[$alias])); + } + + /** + * Resolve an alias to a class name + * + * @param string $alias + * @return string + */ + protected function resolveAlias($alias) + { + do { + $normalized = $this->normalizeAlias($alias); + $class = $this->aliases[$normalized]; + } while ($this->hasAlias($class)); + + return $class; + } +} diff --git a/zendframework/zend-code/src/Annotation/Parser/ParserInterface.php b/zendframework/zend-code/src/Annotation/Parser/ParserInterface.php new file mode 100644 index 000000000..f27ea471f --- /dev/null +++ b/zendframework/zend-code/src/Annotation/Parser/ParserInterface.php @@ -0,0 +1,39 @@ +setOptions($options); + } + } + + /** + * @param bool $isSourceDirty + * @return AbstractGenerator + */ + public function setSourceDirty($isSourceDirty = true) + { + $this->isSourceDirty = (bool) $isSourceDirty; + return $this; + } + + /** + * @return bool + */ + public function isSourceDirty() + { + return $this->isSourceDirty; + } + + /** + * @param string $indentation + * @return AbstractGenerator + */ + public function setIndentation($indentation) + { + $this->indentation = (string) $indentation; + return $this; + } + + /** + * @return string + */ + public function getIndentation() + { + return $this->indentation; + } + + /** + * @param string $sourceContent + * @return AbstractGenerator + */ + public function setSourceContent($sourceContent) + { + $this->sourceContent = (string) $sourceContent; + return $this; + } + + /** + * @return string + */ + public function getSourceContent() + { + return $this->sourceContent; + } + + /** + * @param array|Traversable $options + * @throws Exception\InvalidArgumentException + * @return AbstractGenerator + */ + public function setOptions($options) + { + if (!is_array($options) && !$options instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects an array or Traversable object; received "%s"', + __METHOD__, + (is_object($options) ? get_class($options) : gettype($options)) + )); + } + + foreach ($options as $optionName => $optionValue) { + $methodName = 'set' . $optionName; + if (method_exists($this, $methodName)) { + $this->{$methodName}($optionValue); + } + } + + return $this; + } +} diff --git a/zendframework/zend-code/src/Generator/AbstractMemberGenerator.php b/zendframework/zend-code/src/Generator/AbstractMemberGenerator.php new file mode 100644 index 000000000..110461286 --- /dev/null +++ b/zendframework/zend-code/src/Generator/AbstractMemberGenerator.php @@ -0,0 +1,242 @@ +flags = $flags; + + return $this; + } + + /** + * @param int $flag + * @return AbstractMemberGenerator + */ + public function addFlag($flag) + { + $this->setFlags($this->flags | $flag); + return $this; + } + + /** + * @param int $flag + * @return AbstractMemberGenerator + */ + public function removeFlag($flag) + { + $this->setFlags($this->flags & ~$flag); + return $this; + } + + /** + * @param bool $isAbstract + * @return AbstractMemberGenerator + */ + public function setAbstract($isAbstract) + { + return (($isAbstract) ? $this->addFlag(self::FLAG_ABSTRACT) : $this->removeFlag(self::FLAG_ABSTRACT)); + } + + /** + * @return bool + */ + public function isAbstract() + { + return (bool) ($this->flags & self::FLAG_ABSTRACT); + } + + /** + * @param bool $isInterface + * @return AbstractMemberGenerator + */ + public function setInterface($isInterface) + { + return (($isInterface) ? $this->addFlag(self::FLAG_INTERFACE) : $this->removeFlag(self::FLAG_INTERFACE)); + } + + /** + * @return bool + */ + public function isInterface() + { + return (bool) ($this->flags & self::FLAG_INTERFACE); + } + + /** + * @param bool $isFinal + * @return AbstractMemberGenerator + */ + public function setFinal($isFinal) + { + return (($isFinal) ? $this->addFlag(self::FLAG_FINAL) : $this->removeFlag(self::FLAG_FINAL)); + } + + /** + * @return bool + */ + public function isFinal() + { + return (bool) ($this->flags & self::FLAG_FINAL); + } + + /** + * @param bool $isStatic + * @return AbstractMemberGenerator + */ + public function setStatic($isStatic) + { + return (($isStatic) ? $this->addFlag(self::FLAG_STATIC) : $this->removeFlag(self::FLAG_STATIC)); + } + + /** + * @return bool + */ + public function isStatic() + { + return (bool) ($this->flags & self::FLAG_STATIC); // is FLAG_STATIC in flags + } + + /** + * @param string $visibility + * @return AbstractMemberGenerator + */ + public function setVisibility($visibility) + { + switch ($visibility) { + case self::VISIBILITY_PUBLIC: + $this->removeFlag(self::FLAG_PRIVATE | self::FLAG_PROTECTED); // remove both + $this->addFlag(self::FLAG_PUBLIC); + break; + case self::VISIBILITY_PROTECTED: + $this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PRIVATE); // remove both + $this->addFlag(self::FLAG_PROTECTED); + break; + case self::VISIBILITY_PRIVATE: + $this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PROTECTED); // remove both + $this->addFlag(self::FLAG_PRIVATE); + break; + } + + return $this; + } + + /** + * @return string + */ + public function getVisibility() + { + switch (true) { + case ($this->flags & self::FLAG_PROTECTED): + return self::VISIBILITY_PROTECTED; + case ($this->flags & self::FLAG_PRIVATE): + return self::VISIBILITY_PRIVATE; + default: + return self::VISIBILITY_PUBLIC; + } + } + + /** + * @param string $name + * @return AbstractMemberGenerator + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param DocBlockGenerator|string $docBlock + * @throws Exception\InvalidArgumentException + * @return AbstractMemberGenerator + */ + public function setDocBlock($docBlock) + { + if (is_string($docBlock)) { + $docBlock = new DocBlockGenerator($docBlock); + } elseif (!$docBlock instanceof DocBlockGenerator) { + throw new Exception\InvalidArgumentException(sprintf( + '%s is expecting either a string, array or an instance of %s\DocBlockGenerator', + __METHOD__, + __NAMESPACE__ + )); + } + + $this->docBlock = $docBlock; + + return $this; + } + + /** + * @return DocBlockGenerator + */ + public function getDocBlock() + { + return $this->docBlock; + } +} diff --git a/zendframework/zend-code/src/Generator/BodyGenerator.php b/zendframework/zend-code/src/Generator/BodyGenerator.php new file mode 100644 index 000000000..b199fe817 --- /dev/null +++ b/zendframework/zend-code/src/Generator/BodyGenerator.php @@ -0,0 +1,44 @@ +content = (string) $content; + return $this; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return string + */ + public function generate() + { + return $this->getContent(); + } +} diff --git a/zendframework/zend-code/src/Generator/ClassGenerator.php b/zendframework/zend-code/src/Generator/ClassGenerator.php new file mode 100644 index 000000000..259febf11 --- /dev/null +++ b/zendframework/zend-code/src/Generator/ClassGenerator.php @@ -0,0 +1,1012 @@ +getName()); + + $cg->setSourceContent($cg->getSourceContent()); + $cg->setSourceDirty(false); + + if ($classReflection->getDocComment() != '') { + $cg->setDocBlock(DocBlockGenerator::fromReflection($classReflection->getDocBlock())); + } + + $cg->setAbstract($classReflection->isAbstract()); + + // set the namespace + if ($classReflection->inNamespace()) { + $cg->setNamespaceName($classReflection->getNamespaceName()); + } + + /* @var \Zend\Code\Reflection\ClassReflection $parentClass */ + $parentClass = $classReflection->getParentClass(); + $interfaces = $classReflection->getInterfaces(); + + if ($parentClass) { + $cg->setExtendedClass($parentClass->getName()); + + $interfaces = array_diff($interfaces, $parentClass->getInterfaces()); + } + + $interfaceNames = []; + foreach ($interfaces as $interface) { + /* @var \Zend\Code\Reflection\ClassReflection $interface */ + $interfaceNames[] = $interface->getName(); + } + + $cg->setImplementedInterfaces($interfaceNames); + + $properties = []; + + foreach ($classReflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->getDeclaringClass()->getName() == $classReflection->getName()) { + $properties[] = PropertyGenerator::fromReflection($reflectionProperty); + } + } + + $cg->addProperties($properties); + + $constants = []; + + foreach ($classReflection->getConstants() as $name => $value) { + $constants[] = [ + 'name' => $name, + 'value' => $value + ]; + } + + $cg->addConstants($constants); + + $methods = []; + + foreach ($classReflection->getMethods() as $reflectionMethod) { + $className = ($cg->getNamespaceName()) ? $cg->getNamespaceName() . "\\" . $cg->getName() : $cg->getName(); + + if ($reflectionMethod->getDeclaringClass()->getName() == $className) { + $methods[] = MethodGenerator::fromReflection($reflectionMethod); + } + } + + $cg->addMethods($methods); + + return $cg; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey filegenerator FileGenerator File generator that holds this class + * @configkey namespacename string The namespace for this class + * @configkey docblock string The docblock information + * @configkey flags int Flags, one of ClassGenerator::FLAG_ABSTRACT ClassGenerator::FLAG_FINAL + * @configkey extendedclass string Class which this class is extending + * @configkey implementedinterfaces + * @configkey properties + * @configkey methods + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return ClassGenerator + */ + public static function fromArray(array $array) + { + if (!isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Class generator requires that a name is provided for this object' + ); + } + + $cg = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'containingfile': + $cg->setContainingFileGenerator($value); + break; + case 'namespacename': + $cg->setNamespaceName($value); + break; + case 'docblock': + $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value); + $cg->setDocBlock($docBlock); + break; + case 'flags': + $cg->setFlags($value); + break; + case 'extendedclass': + $cg->setExtendedClass($value); + break; + case 'implementedinterfaces': + $cg->setImplementedInterfaces($value); + break; + case 'properties': + $cg->addProperties($value); + break; + case 'methods': + $cg->addMethods($value); + break; + } + } + + return $cg; + } + + /** + * @param string $name + * @param string $namespaceName + * @param array|string $flags + * @param string $extends + * @param array $interfaces + * @param array $properties + * @param array $methods + * @param DocBlockGenerator $docBlock + */ + public function __construct( + $name = null, + $namespaceName = null, + $flags = null, + $extends = null, + $interfaces = [], + $properties = [], + $methods = [], + $docBlock = null + ) { + $this->traitUsageGenerator = new TraitUsageGenerator($this); + + if ($name !== null) { + $this->setName($name); + } + if ($namespaceName !== null) { + $this->setNamespaceName($namespaceName); + } + if ($flags !== null) { + $this->setFlags($flags); + } + if ($properties !== []) { + $this->addProperties($properties); + } + if ($extends !== null) { + $this->setExtendedClass($extends); + } + if (is_array($interfaces)) { + $this->setImplementedInterfaces($interfaces); + } + if ($methods !== []) { + $this->addMethods($methods); + } + if ($docBlock !== null) { + $this->setDocBlock($docBlock); + } + } + + /** + * @param string $name + * @return ClassGenerator + */ + public function setName($name) + { + if (strstr($name, '\\')) { + $namespace = substr($name, 0, strrpos($name, '\\')); + $name = substr($name, strrpos($name, '\\') + 1); + $this->setNamespaceName($namespace); + } + + $this->name = $name; + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $namespaceName + * @return ClassGenerator + */ + public function setNamespaceName($namespaceName) + { + $this->namespaceName = $namespaceName; + return $this; + } + + /** + * @return string + */ + public function getNamespaceName() + { + return $this->namespaceName; + } + + /** + * @param FileGenerator $fileGenerator + * @return ClassGenerator + */ + public function setContainingFileGenerator(FileGenerator $fileGenerator) + { + $this->containingFileGenerator = $fileGenerator; + return $this; + } + + /** + * @return FileGenerator + */ + public function getContainingFileGenerator() + { + return $this->containingFileGenerator; + } + + /** + * @param DocBlockGenerator $docBlock + * @return ClassGenerator + */ + public function setDocBlock(DocBlockGenerator $docBlock) + { + $this->docBlock = $docBlock; + return $this; + } + + /** + * @return DocBlockGenerator + */ + public function getDocBlock() + { + return $this->docBlock; + } + + /** + * @param array|string $flags + * @return ClassGenerator + */ + public function setFlags($flags) + { + if (is_array($flags)) { + $flagsArray = $flags; + $flags = 0x00; + foreach ($flagsArray as $flag) { + $flags |= $flag; + } + } + // check that visibility is one of three + $this->flags = $flags; + + return $this; + } + + /** + * @param string $flag + * @return ClassGenerator + */ + public function addFlag($flag) + { + $this->setFlags($this->flags | $flag); + return $this; + } + + /** + * @param string $flag + * @return ClassGenerator + */ + public function removeFlag($flag) + { + $this->setFlags($this->flags & ~$flag); + return $this; + } + + /** + * @param bool $isAbstract + * @return ClassGenerator + */ + public function setAbstract($isAbstract) + { + return (($isAbstract) ? $this->addFlag(self::FLAG_ABSTRACT) : $this->removeFlag(self::FLAG_ABSTRACT)); + } + + /** + * @return bool + */ + public function isAbstract() + { + return (bool) ($this->flags & self::FLAG_ABSTRACT); + } + + /** + * @param bool $isFinal + * @return ClassGenerator + */ + public function setFinal($isFinal) + { + return (($isFinal) ? $this->addFlag(self::FLAG_FINAL) : $this->removeFlag(self::FLAG_FINAL)); + } + + /** + * @return bool + */ + public function isFinal() + { + return ($this->flags & self::FLAG_FINAL); + } + + /** + * @param string $extendedClass + * @return ClassGenerator + */ + public function setExtendedClass($extendedClass) + { + $this->extendedClass = $extendedClass; + return $this; + } + + /** + * @return string + */ + public function getExtendedClass() + { + return $this->extendedClass; + } + + /** + * @param array $implementedInterfaces + * @return ClassGenerator + */ + public function setImplementedInterfaces(array $implementedInterfaces) + { + $this->implementedInterfaces = $implementedInterfaces; + return $this; + } + + /** + * @return array + */ + public function getImplementedInterfaces() + { + return $this->implementedInterfaces; + } + + /** + * @param string $constantName + * + * @return PropertyGenerator|false + */ + public function getConstant($constantName) + { + if (isset($this->constants[$constantName])) { + return $this->constants[$constantName]; + } + + return false; + } + + /** + * @return PropertyGenerator[] indexed by constant name + */ + public function getConstants() + { + return $this->constants; + } + + /** + * @param string $constantName + * @return bool + */ + public function hasConstant($constantName) + { + return isset($this->constants[$constantName]); + } + + /** + * Add constant from PropertyGenerator + * + * @param PropertyGenerator $constant + * @throws Exception\InvalidArgumentException + * @return ClassGenerator + */ + public function addConstantFromGenerator(PropertyGenerator $constant) + { + $constantName = $constant->getName(); + + if (isset($this->constants[$constantName])) { + throw new Exception\InvalidArgumentException(sprintf( + 'A constant by name %s already exists in this class.', + $constantName + )); + } + + if (! $constant->isConst()) { + throw new Exception\InvalidArgumentException(sprintf( + 'The value %s is not defined as a constant.', + $constantName + )); + } + + $this->constants[$constantName] = $constant; + + return $this; + } + + /** + * Add Constant + * + * @param string $name non-empty string + * @param string|int|null|float|array $value scalar + * + * @throws Exception\InvalidArgumentException + * + * @return ClassGenerator + */ + public function addConstant($name, $value) + { + if (empty($name) || !is_string($name)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects string for name', + __METHOD__ + )); + } + + $this->validateConstantValue($value); + + return $this->addConstantFromGenerator( + new PropertyGenerator($name, new PropertyValueGenerator($value), PropertyGenerator::FLAG_CONSTANT) + ); + } + + /** + * @param PropertyGenerator[]|array[] $constants + * + * @return ClassGenerator + */ + public function addConstants(array $constants) + { + foreach ($constants as $constant) { + if ($constant instanceof PropertyGenerator) { + $this->addPropertyFromGenerator($constant); + } else { + if (is_array($constant)) { + call_user_func_array([$this, 'addConstant'], $constant); + } + } + } + + return $this; + } + + /** + * @param array $properties + * @return ClassGenerator + */ + public function addProperties(array $properties) + { + foreach ($properties as $property) { + if ($property instanceof PropertyGenerator) { + $this->addPropertyFromGenerator($property); + } else { + if (is_string($property)) { + $this->addProperty($property); + } elseif (is_array($property)) { + call_user_func_array([$this, 'addProperty'], $property); + } + } + } + + return $this; + } + + /** + * Add Property from scalars + * + * @param string $name + * @param string|array $defaultValue + * @param int $flags + * @throws Exception\InvalidArgumentException + * @return ClassGenerator + */ + public function addProperty($name, $defaultValue = null, $flags = PropertyGenerator::FLAG_PUBLIC) + { + if (!is_string($name)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s::%s expects string for name', + get_class($this), + __FUNCTION__ + )); + } + + // backwards compatibility + // @todo remove this on next major version + if ($flags === PropertyGenerator::FLAG_CONSTANT) { + return $this->addConstant($name, $defaultValue); + } + + return $this->addPropertyFromGenerator(new PropertyGenerator($name, $defaultValue, $flags)); + } + + /** + * Add property from PropertyGenerator + * + * @param PropertyGenerator $property + * @throws Exception\InvalidArgumentException + * @return ClassGenerator + */ + public function addPropertyFromGenerator(PropertyGenerator $property) + { + $propertyName = $property->getName(); + + if (isset($this->properties[$propertyName])) { + throw new Exception\InvalidArgumentException(sprintf( + 'A property by name %s already exists in this class.', + $propertyName + )); + } + + // backwards compatibility + // @todo remove this on next major version + if ($property->isConst()) { + return $this->addConstantFromGenerator($property); + } + + $this->properties[$propertyName] = $property; + return $this; + } + + /** + * @return PropertyGenerator[] + */ + public function getProperties() + { + return $this->properties; + } + + /** + * @param string $propertyName + * @return PropertyGenerator|false + */ + public function getProperty($propertyName) + { + foreach ($this->getProperties() as $property) { + if ($property->getName() == $propertyName) { + return $property; + } + } + + return false; + } + + /** + * Add a class to "use" classes + * + * @param string $use + * @param string|null $useAlias + * @return ClassGenerator + */ + public function addUse($use, $useAlias = null) + { + $this->traitUsageGenerator->addUse($use, $useAlias); + return $this; + } + + /** + * Returns the "use" classes + * + * @return array + */ + public function getUses() + { + return $this->traitUsageGenerator->getUses(); + } + + /** + * @param string $propertyName + * @return bool + */ + public function hasProperty($propertyName) + { + return isset($this->properties[$propertyName]); + } + + /** + * @param array $methods + * @return ClassGenerator + */ + public function addMethods(array $methods) + { + foreach ($methods as $method) { + if ($method instanceof MethodGenerator) { + $this->addMethodFromGenerator($method); + } else { + if (is_string($method)) { + $this->addMethod($method); + } elseif (is_array($method)) { + call_user_func_array([$this, 'addMethod'], $method); + } + } + } + + return $this; + } + + /** + * Add Method from scalars + * + * @param string $name + * @param array $parameters + * @param int $flags + * @param string $body + * @param string $docBlock + * @throws Exception\InvalidArgumentException + * @return ClassGenerator + */ + public function addMethod( + $name = null, + array $parameters = [], + $flags = MethodGenerator::FLAG_PUBLIC, + $body = null, + $docBlock = null + ) { + if (!is_string($name)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s::%s expects string for name', + get_class($this), + __FUNCTION__ + )); + } + + return $this->addMethodFromGenerator(new MethodGenerator($name, $parameters, $flags, $body, $docBlock)); + } + + /** + * Add Method from MethodGenerator + * + * @param MethodGenerator $method + * @throws Exception\InvalidArgumentException + * @return ClassGenerator + */ + public function addMethodFromGenerator(MethodGenerator $method) + { + $methodName = $method->getName(); + + if ($this->hasMethod($methodName)) { + throw new Exception\InvalidArgumentException(sprintf( + 'A method by name %s already exists in this class.', + $methodName + )); + } + + $this->methods[strtolower($methodName)] = $method; + return $this; + } + + /** + * @return MethodGenerator[] + */ + public function getMethods() + { + return $this->methods; + } + + /** + * @param string $methodName + * @return MethodGenerator|false + */ + public function getMethod($methodName) + { + return $this->hasMethod($methodName) ? $this->methods[strtolower($methodName)] : false; + } + + /** + * @param string $methodName + * @return ClassGenerator + */ + public function removeMethod($methodName) + { + if ($this->hasMethod($methodName)) { + unset($this->methods[strtolower($methodName)]); + } + + return $this; + } + + /** + * @param string $methodName + * @return bool + */ + public function hasMethod($methodName) + { + return isset($this->methods[strtolower($methodName)]); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTrait($trait) + { + $this->traitUsageGenerator->addTrait($trait); + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraits(array $traits) + { + $this->traitUsageGenerator->addTraits($traits); + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function hasTrait($traitName) + { + return $this->traitUsageGenerator->hasTrait($traitName); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraits() + { + return $this->traitUsageGenerator->getTraits(); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function removeTrait($traitName) + { + return $this->traitUsageGenerator->removeTrait($traitName); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraitAlias($method, $alias, $visibility = null) + { + $this->traitUsageGenerator->addTraitAlias($method, $alias, $visibility); + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraitAliases() + { + return $this->traitUsageGenerator->getTraitAliases(); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraitOverride($method, $traitsToReplace) + { + $this->traitUsageGenerator->addTraitOverride($method, $traitsToReplace); + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function removeTraitOverride($method, $overridesToRemove = null) + { + $this->traitUsageGenerator->removeTraitOverride($method, $overridesToRemove); + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraitOverrides() + { + return $this->traitUsageGenerator->getTraitOverrides(); + } + + /** + * @return bool + */ + public function isSourceDirty() + { + if (($docBlock = $this->getDocBlock()) && $docBlock->isSourceDirty()) { + return true; + } + + foreach ($this->getProperties() as $property) { + if ($property->isSourceDirty()) { + return true; + } + } + + foreach ($this->getMethods() as $method) { + if ($method->isSourceDirty()) { + return true; + } + } + + return parent::isSourceDirty(); + } + + /** + * @inherit Zend\Code\Generator\GeneratorInterface + */ + public function generate() + { + if (!$this->isSourceDirty()) { + $output = $this->getSourceContent(); + if (!empty($output)) { + return $output; + } + } + + $indent = $this->getIndentation(); + $output = ''; + + if (null !== ($namespace = $this->getNamespaceName())) { + $output .= 'namespace ' . $namespace . ';' . self::LINE_FEED . self::LINE_FEED; + } + + $uses = $this->getUses(); + + if (!empty($uses)) { + foreach ($uses as $use) { + $output .= 'use ' . $use . ';' . self::LINE_FEED; + } + + $output .= self::LINE_FEED; + } + + if (null !== ($docBlock = $this->getDocBlock())) { + $docBlock->setIndentation(''); + $output .= $docBlock->generate(); + } + + if ($this->isAbstract()) { + $output .= 'abstract '; + } elseif ($this->isFinal()) { + $output .= 'final '; + } + + $output .= static::OBJECT_TYPE . ' ' . $this->getName(); + + if (!empty($this->extendedClass)) { + $output .= ' extends ' . $this->extendedClass; + } + + $implemented = $this->getImplementedInterfaces(); + + if (!empty($implemented)) { + $output .= ' ' . static::IMPLEMENTS_KEYWORD . ' ' . implode(', ', $implemented); + } + + $output .= self::LINE_FEED . '{' . self::LINE_FEED . self::LINE_FEED; + $output .= $this->traitUsageGenerator->generate(); + + $constants = $this->getConstants(); + + foreach ($constants as $constant) { + $output .= $constant->generate() . self::LINE_FEED . self::LINE_FEED; + } + + $properties = $this->getProperties(); + + foreach ($properties as $property) { + $output .= $property->generate() . self::LINE_FEED . self::LINE_FEED; + } + + $methods = $this->getMethods(); + + foreach ($methods as $method) { + $output .= $method->generate() . self::LINE_FEED; + } + + $output .= self::LINE_FEED . '}' . self::LINE_FEED; + + return $output; + } + + /** + * @param mixed $value + * + * @return void + * + * @throws Exception\InvalidArgumentException + */ + private function validateConstantValue($value) + { + if (null === $value || is_scalar($value)) { + return; + } + + if (is_array($value)) { + array_walk($value, [$this, 'validateConstantValue']); + + return; + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Expected value for constant, value must be a "scalar" or "null", "%s" found', + gettype($value) + )); + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag.php new file mode 100644 index 000000000..1fb500bc6 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag.php @@ -0,0 +1,50 @@ +initializeDefaultTags(); + return $tagManager->createTagFromReflection($reflectionTag); + } + + /** + * @param string $description + * @return Tag + * @deprecated Deprecated in 2.3. Use GenericTag::setContent() instead + */ + public function setDescription($description) + { + return $this->setContent($description); + } + + /** + * @return string + * @deprecated Deprecated in 2.3. Use GenericTag::getContent() instead + */ + public function getDescription() + { + return $this->getContent(); + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php new file mode 100644 index 000000000..490e049cb --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/AbstractTypeableTag.php @@ -0,0 +1,96 @@ +setTypes($types); + } + + if (!empty($description)) { + $this->setDescription($description); + } + } + + /** + * @param string $description + * @return ReturnTag + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Array of types or string with types delimited by pipe (|) + * e.g. array('int', 'null') or "int|null" + * + * @param array|string $types + * @return ReturnTag + */ + public function setTypes($types) + { + if (is_string($types)) { + $types = explode('|', $types); + } + $this->types = $types; + return $this; + } + + /** + * @return array + */ + public function getTypes() + { + return $this->types; + } + + /** + * @param string $delimiter + * @return string + */ + public function getTypesAsString($delimiter = '|') + { + return implode($delimiter, $this->types); + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php new file mode 100644 index 000000000..b97a5cb68 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/AuthorTag.php @@ -0,0 +1,110 @@ +setAuthorName($authorName); + } + + if (!empty($authorEmail)) { + $this->setAuthorEmail($authorEmail); + } + } + + /** + * @param ReflectionTagInterface $reflectionTag + * @return ReturnTag + * @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead + */ + public static function fromReflection(ReflectionTagInterface $reflectionTag) + { + $tagManager = new TagManager(); + $tagManager->initializeDefaultTags(); + return $tagManager->createTagFromReflection($reflectionTag); + } + + /** + * @return string + */ + public function getName() + { + return 'author'; + } + + /** + * @param string $authorEmail + * @return AuthorTag + */ + public function setAuthorEmail($authorEmail) + { + $this->authorEmail = $authorEmail; + return $this; + } + + /** + * @return string + */ + public function getAuthorEmail() + { + return $this->authorEmail; + } + + /** + * @param string $authorName + * @return AuthorTag + */ + public function setAuthorName($authorName) + { + $this->authorName = $authorName; + return $this; + } + + /** + * @return string + */ + public function getAuthorName() + { + return $this->authorName; + } + + /** + * @return string + */ + public function generate() + { + $output = '@author' + . ((!empty($this->authorName)) ? ' ' . $this->authorName : '') + . ((!empty($this->authorEmail)) ? ' <' . $this->authorEmail . '>' : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php new file mode 100644 index 000000000..173e9272f --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/GenericTag.php @@ -0,0 +1,88 @@ +setName($name); + } + + if (!empty($content)) { + $this->setContent($content); + } + } + + /** + * @param string $name + * @return GenericTag + */ + public function setName($name) + { + $this->name = ltrim($name, '@'); + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $content + * @return GenericTag + */ + public function setContent($content) + { + $this->content = $content; + return $this; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return string + */ + public function generate() + { + $output = '@' . $this->name + . ((!empty($this->content)) ? ' ' . $this->content : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php new file mode 100644 index 000000000..e41fec38b --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/LicenseTag.php @@ -0,0 +1,110 @@ +setUrl($url); + } + + if (!empty($licenseName)) { + $this->setLicenseName($licenseName); + } + } + + /** + * @param ReflectionTagInterface $reflectionTag + * @return ReturnTag + * @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead + */ + public static function fromReflection(ReflectionTagInterface $reflectionTag) + { + $tagManager = new TagManager(); + $tagManager->initializeDefaultTags(); + return $tagManager->createTagFromReflection($reflectionTag); + } + + /** + * @return string + */ + public function getName() + { + return 'license'; + } + + /** + * @param string $url + * @return LicenseTag + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * @param string $name + * @return LicenseTag + */ + public function setLicenseName($name) + { + $this->licenseName = $name; + return $this; + } + + /** + * @return string + */ + public function getLicenseName() + { + return $this->licenseName; + } + + /** + * @return string + */ + public function generate() + { + $output = '@license' + . ((!empty($this->url)) ? ' ' . $this->url : '') + . ((!empty($this->licenseName)) ? ' ' . $this->licenseName : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php new file mode 100644 index 000000000..0e4c94386 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/MethodTag.php @@ -0,0 +1,98 @@ +setMethodName($methodName); + } + + $this->setIsStatic((bool) $isStatic); + + parent::__construct($types, $description); + } + + /** + * @return string + */ + public function getName() + { + return 'method'; + } + + /** + * @param boolean $isStatic + * @return MethodTag + */ + public function setIsStatic($isStatic) + { + $this->isStatic = $isStatic; + return $this; + } + + /** + * @return boolean + */ + public function isStatic() + { + return $this->isStatic; + } + + /** + * @param string $methodName + * @return MethodTag + */ + public function setMethodName($methodName) + { + $this->methodName = rtrim($methodName, ')('); + return $this; + } + + /** + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * @return string + */ + public function generate() + { + $output = '@method' + . (($this->isStatic) ? ' static' : '') + . ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '') + . ((!empty($this->methodName)) ? ' ' . $this->methodName . '()' : '') + . ((!empty($this->description)) ? ' ' . $this->description : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php new file mode 100644 index 000000000..04f413eea --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/ParamTag.php @@ -0,0 +1,124 @@ +setVariableName($variableName); + } + + parent::__construct($types, $description); + } + + /** + * @param ReflectionTagInterface $reflectionTag + * @return ReturnTag + * @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead + */ + public static function fromReflection(ReflectionTagInterface $reflectionTag) + { + $tagManager = new TagManager(); + $tagManager->initializeDefaultTags(); + return $tagManager->createTagFromReflection($reflectionTag); + } + + /** + * @return string + */ + public function getName() + { + return 'param'; + } + + /** + * @param string $variableName + * @return ParamTag + */ + public function setVariableName($variableName) + { + $this->variableName = ltrim($variableName, '$'); + return $this; + } + + /** + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * @param string $datatype + * @return ReturnTag + * @deprecated Deprecated in 2.3. Use setTypes() instead + */ + public function setDatatype($datatype) + { + return $this->setTypes($datatype); + } + + /** + * @return string + * @deprecated Deprecated in 2.3. Use getTypes() or getTypesAsString() instead + */ + public function getDatatype() + { + return $this->getTypesAsString(); + } + + /** + * @param string $paramName + * @return ParamTag + * @deprecated Deprecated in 2.3. Use setVariableName() instead + */ + public function setParamName($paramName) + { + return $this->setVariableName($paramName); + } + + /** + * @return string + * @deprecated Deprecated in 2.3. Use getVariableName() instead + */ + public function getParamName() + { + return $this->getVariableName(); + } + + /** + * @return string + */ + public function generate() + { + $output = '@param' + . ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '') + . ((!empty($this->variableName)) ? ' $' . $this->variableName : '') + . ((!empty($this->description)) ? ' ' . $this->description : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php new file mode 100644 index 000000000..def0368c2 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/PropertyTag.php @@ -0,0 +1,71 @@ +setPropertyName($propertyName); + } + + parent::__construct($types, $description); + } + + /** + * @return string + */ + public function getName() + { + return 'property'; + } + + /** + * @param string $propertyName + * @return self + */ + public function setPropertyName($propertyName) + { + $this->propertyName = ltrim($propertyName, '$'); + return $this; + } + + /** + * @return string + */ + public function getPropertyName() + { + return $this->propertyName; + } + + /** + * @return string + */ + public function generate() + { + $output = '@property' + . ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '') + . ((!empty($this->propertyName)) ? ' $' . $this->propertyName : '') + . ((!empty($this->description)) ? ' ' . $this->description : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php new file mode 100644 index 000000000..f7796708b --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/ReturnTag.php @@ -0,0 +1,67 @@ +initializeDefaultTags(); + return $tagManager->createTagFromReflection($reflectionTag); + } + + /** + * @return string + */ + public function getName() + { + return 'return'; + } + + /** + * @param string $datatype + * @return ReturnTag + * @deprecated Deprecated in 2.3. Use setTypes() instead + */ + public function setDatatype($datatype) + { + return $this->setTypes($datatype); + } + + /** + * @return string + * @deprecated Deprecated in 2.3. Use getTypes() or getTypesAsString() instead + */ + public function getDatatype() + { + return $this->getTypesAsString(); + } + + /** + * @return string + */ + public function generate() + { + $output = '@return ' + . $this->getTypesAsString() + . ((!empty($this->description)) ? ' ' . $this->description : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php b/zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php new file mode 100644 index 000000000..1b37e1057 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/Tag/TagInterface.php @@ -0,0 +1,16 @@ +types)) ? ' ' . $this->getTypesAsString() : '') + . ((!empty($this->description)) ? ' ' . $this->description : ''); + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlock/TagManager.php b/zendframework/zend-code/src/Generator/DocBlock/TagManager.php new file mode 100644 index 000000000..b88266691 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlock/TagManager.php @@ -0,0 +1,69 @@ +addPrototype(new Tag\ParamTag()); + $this->addPrototype(new Tag\ReturnTag()); + $this->addPrototype(new Tag\MethodTag()); + $this->addPrototype(new Tag\PropertyTag()); + $this->addPrototype(new Tag\AuthorTag()); + $this->addPrototype(new Tag\LicenseTag()); + $this->addPrototype(new Tag\ThrowsTag()); + $this->setGenericPrototype(new Tag\GenericTag()); + } + + /** + * @param ReflectionTagInterface $reflectionTag + * @return TagInterface + */ + public function createTagFromReflection(ReflectionTagInterface $reflectionTag) + { + $tagName = $reflectionTag->getName(); + + /* @var TagInterface $newTag */ + $newTag = $this->getClonedPrototype($tagName); + + // transport any properties via accessors and mutators from reflection to codegen object + $reflectionClass = new \ReflectionClass($reflectionTag); + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (substr($method->getName(), 0, 3) == 'get') { + $propertyName = substr($method->getName(), 3); + if (method_exists($newTag, 'set' . $propertyName)) { + $newTag->{'set' . $propertyName}($reflectionTag->{'get' . $propertyName}()); + } + } elseif (substr($method->getName(), 0, 2) == 'is') { + $propertyName = ucfirst($method->getName()); + if (method_exists($newTag, 'set' . $propertyName)) { + $newTag->{'set' . $propertyName}($reflectionTag->{$method->getName()}()); + } + } + } + return $newTag; + } +} diff --git a/zendframework/zend-code/src/Generator/DocBlockGenerator.php b/zendframework/zend-code/src/Generator/DocBlockGenerator.php new file mode 100644 index 000000000..218bbfb41 --- /dev/null +++ b/zendframework/zend-code/src/Generator/DocBlockGenerator.php @@ -0,0 +1,274 @@ +setSourceContent($reflectionDocBlock->getContents()); + $docBlock->setSourceDirty(false); + + $docBlock->setShortDescription($reflectionDocBlock->getShortDescription()); + $docBlock->setLongDescription($reflectionDocBlock->getLongDescription()); + + foreach ($reflectionDocBlock->getTags() as $tag) { + $docBlock->setTag(self::getTagManager()->createTagFromReflection($tag)); + } + + return $docBlock; + } + + /** + * Generate from array + * + * @configkey shortdescription string The short description for this doc block + * @configkey longdescription string The long description for this doc block + * @configkey tags array + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return DocBlockGenerator + */ + public static function fromArray(array $array) + { + $docBlock = new static(); + + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'shortdescription': + $docBlock->setShortDescription($value); + break; + case 'longdescription': + $docBlock->setLongDescription($value); + break; + case 'tags': + $docBlock->setTags($value); + break; + } + } + + return $docBlock; + } + + protected static function getTagManager() + { + if (!isset(static::$tagManager)) { + static::$tagManager = new TagManager(); + static::$tagManager->initializeDefaultTags(); + } + return static::$tagManager; + } + + /** + * @param string $shortDescription + * @param string $longDescription + * @param array $tags + */ + public function __construct($shortDescription = null, $longDescription = null, array $tags = []) + { + if ($shortDescription) { + $this->setShortDescription($shortDescription); + } + if ($longDescription) { + $this->setLongDescription($longDescription); + } + if (is_array($tags) && $tags) { + $this->setTags($tags); + } + } + + /** + * @param string $shortDescription + * @return DocBlockGenerator + */ + public function setShortDescription($shortDescription) + { + $this->shortDescription = $shortDescription; + return $this; + } + + /** + * @return string + */ + public function getShortDescription() + { + return $this->shortDescription; + } + + /** + * @param string $longDescription + * @return DocBlockGenerator + */ + public function setLongDescription($longDescription) + { + $this->longDescription = $longDescription; + return $this; + } + + /** + * @return string + */ + public function getLongDescription() + { + return $this->longDescription; + } + + /** + * @param array $tags + * @return DocBlockGenerator + */ + public function setTags(array $tags) + { + foreach ($tags as $tag) { + $this->setTag($tag); + } + + return $this; + } + + /** + * @param array|TagInterface $tag + * @throws Exception\InvalidArgumentException + * @return DocBlockGenerator + */ + public function setTag($tag) + { + if (is_array($tag)) { + // use deprecated Tag class for backward compatiblity to old array-keys + $genericTag = new Tag(); + $genericTag->setOptions($tag); + $tag = $genericTag; + } elseif (!$tag instanceof TagInterface) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects either an array of method options or an instance of %s\DocBlock\Tag\TagInterface', + __METHOD__, + __NAMESPACE__ + )); + } + + $this->tags[] = $tag; + return $this; + } + + /** + * @return TagInterface[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * @param bool $value + * @return DocBlockGenerator + */ + public function setWordWrap($value) + { + $this->wordwrap = (bool) $value; + return $this; + } + + /** + * @return bool + */ + public function getWordWrap() + { + return $this->wordwrap; + } + + /** + * @return string + */ + public function generate() + { + if (!$this->isSourceDirty()) { + return $this->docCommentize(trim($this->getSourceContent())); + } + + $output = ''; + if (null !== ($sd = $this->getShortDescription())) { + $output .= $sd . self::LINE_FEED . self::LINE_FEED; + } + if (null !== ($ld = $this->getLongDescription())) { + $output .= $ld . self::LINE_FEED . self::LINE_FEED; + } + + /* @var $tag GeneratorInterface */ + foreach ($this->getTags() as $tag) { + $output .= $tag->generate() . self::LINE_FEED; + } + + return $this->docCommentize(trim($output)); + } + + /** + * @param string $content + * @return string + */ + protected function docCommentize($content) + { + $indent = $this->getIndentation(); + $output = $indent . '/**' . self::LINE_FEED; + $content = $this->getWordWrap() == true ? wordwrap($content, 80, self::LINE_FEED) : $content; + $lines = explode(self::LINE_FEED, $content); + foreach ($lines as $line) { + $output .= $indent . ' *'; + if ($line) { + $output .= " $line"; + } + $output .= self::LINE_FEED; + } + $output .= $indent . ' */' . self::LINE_FEED; + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php b/zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php new file mode 100644 index 000000000..968f0249f --- /dev/null +++ b/zendframework/zend-code/src/Generator/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ +setOptions($options); + } + } + + /** + * Use this if you intend on generating code generation objects based on the same file. + * This will keep previous changes to the file in tact during the same PHP process + * + * @param string $filePath + * @param bool $includeIfNotAlreadyIncluded + * @throws ReflectionException\InvalidArgumentException If file does not exists + * @throws ReflectionException\RuntimeException If file exists but is not included or required + * @return FileGenerator + */ + public static function fromReflectedFileName($filePath, $includeIfNotAlreadyIncluded = true) + { + $fileReflector = new FileReflection($filePath, $includeIfNotAlreadyIncluded); + $codeGenerator = static::fromReflection($fileReflector); + + return $codeGenerator; + } + + /** + * @param FileReflection $fileReflection + * @return FileGenerator + */ + public static function fromReflection(FileReflection $fileReflection) + { + $file = new static(); + + $file->setSourceContent($fileReflection->getContents()); + $file->setSourceDirty(false); + + $uses = $fileReflection->getUses(); + + foreach ($fileReflection->getClasses() as $class) { + $phpClass = ClassGenerator::fromReflection($class); + $phpClass->setContainingFileGenerator($file); + + foreach ($uses as $fileUse) { + $phpClass->addUse($fileUse['use'], $fileUse['as']); + } + + $file->setClass($phpClass); + } + + $namespace = $fileReflection->getNamespace(); + + if ($namespace != '') { + $file->setNamespace($namespace); + } + + if ($uses) { + $file->setUses($uses); + } + + if (($fileReflection->getDocComment() != '')) { + $docBlock = $fileReflection->getDocBlock(); + $file->setDocBlock(DocBlockGenerator::fromReflection($docBlock)); + } + + return $file; + } + + /** + * @param array $values + * @return FileGenerator + */ + public static function fromArray(array $values) + { + $fileGenerator = new static; + foreach ($values as $name => $value) { + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'filename': + $fileGenerator->setFilename($value); + continue; + case 'class': + $fileGenerator->setClass(($value instanceof ClassGenerator) ? $value : ClassGenerator::fromArray($value)); + continue; + case 'requiredfiles': + $fileGenerator->setRequiredFiles($value); + continue; + default: + if (property_exists($fileGenerator, $name)) { + $fileGenerator->{$name} = $value; + } elseif (method_exists($fileGenerator, 'set' . $name)) { + $fileGenerator->{'set' . $name}($value); + } + } + } + + return $fileGenerator; + } + + /** + * @param DocBlockGenerator|array|string $docBlock + * @throws Exception\InvalidArgumentException + * @return FileGenerator + */ + public function setDocBlock($docBlock) + { + if (is_string($docBlock)) { + $docBlock = ['shortDescription' => $docBlock]; + } + + if (is_array($docBlock)) { + $docBlock = new DocBlockGenerator($docBlock); + } elseif (!$docBlock instanceof DocBlockGenerator) { + throw new Exception\InvalidArgumentException(sprintf( + '%s is expecting either a string, array or an instance of %s\DocBlockGenerator', + __METHOD__, + __NAMESPACE__ + )); + } + + $this->docBlock = $docBlock; + return $this; + } + + /** + * @return DocBlockGenerator + */ + public function getDocBlock() + { + return $this->docBlock; + } + + /** + * @param array $requiredFiles + * @return FileGenerator + */ + public function setRequiredFiles(array $requiredFiles) + { + $this->requiredFiles = $requiredFiles; + return $this; + } + + /** + * @return array + */ + public function getRequiredFiles() + { + return $this->requiredFiles; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @param string $namespace + * @return FileGenerator + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + return $this; + } + + /** + * Returns an array with the first element the use statement, second is the as part. + * If $withResolvedAs is set to true, there will be a third element that is the + * "resolved" as statement, as the second part is not required in use statements + * + * @param bool $withResolvedAs + * @return array + */ + public function getUses($withResolvedAs = false) + { + $uses = $this->uses; + if ($withResolvedAs) { + for ($useIndex = 0, $count = count($uses); $useIndex < $count; $useIndex++) { + if ($uses[$useIndex][1] == '') { + if (($lastSeparator = strrpos($uses[$useIndex][0], '\\')) !== false) { + $uses[$useIndex][2] = substr($uses[$useIndex][0], $lastSeparator + 1); + } else { + $uses[$useIndex][2] = $uses[$useIndex][0]; + } + } else { + $uses[$useIndex][2] = $uses[$useIndex][1]; + } + } + } + + return $uses; + } + + /** + * @param array $uses + * @return FileGenerator + */ + public function setUses(array $uses) + { + foreach ($uses as $use) { + $use = (array) $use; + if (array_key_exists('use', $use) && array_key_exists('as', $use)) { + $import = $use['use']; + $alias = $use['as']; + } elseif (count($use) == 2) { + list($import, $alias) = $use; + } else { + $import = current($use); + $alias = null; + } + $this->setUse($import, $alias); + } + return $this; + } + + /** + * @param string $use + * @param null|string $as + * @return FileGenerator + */ + public function setUse($use, $as = null) + { + if (!in_array([$use, $as], $this->uses)) { + $this->uses[] = [$use, $as]; + } + return $this; + } + + /** + * @param array $classes + * @return FileGenerator + */ + public function setClasses(array $classes) + { + foreach ($classes as $class) { + $this->setClass($class); + } + + return $this; + } + + /** + * @param string $name + * @return ClassGenerator + */ + public function getClass($name = null) + { + if ($name === null) { + reset($this->classes); + + return current($this->classes); + } + + return $this->classes[(string) $name]; + } + + /** + * @param array|string|ClassGenerator $class + * @throws Exception\InvalidArgumentException + * @return FileGenerator + */ + public function setClass($class) + { + if (is_array($class)) { + $class = ClassGenerator::fromArray($class); + } elseif (is_string($class)) { + $class = new ClassGenerator($class); + } elseif (!$class instanceof ClassGenerator) { + throw new Exception\InvalidArgumentException(sprintf( + '%s is expecting either a string, array or an instance of %s\ClassGenerator', + __METHOD__, + __NAMESPACE__ + )); + } + + // @todo check for dup here + $className = $class->getName(); + $this->classes[$className] = $class; + + return $this; + } + + /** + * @param string $filename + * @return FileGenerator + */ + public function setFilename($filename) + { + $this->filename = (string) $filename; + return $this; + } + + /** + * @return string + */ + public function getFilename() + { + return $this->filename; + } + + /** + * @return ClassGenerator[] + */ + public function getClasses() + { + return $this->classes; + } + + /** + * @param string $body + * @return FileGenerator + */ + public function setBody($body) + { + $this->body = (string) $body; + return $this; + } + + /** + * @return string + */ + public function getBody() + { + return $this->body; + } + + /** + * @return bool + */ + public function isSourceDirty() + { + $docBlock = $this->getDocBlock(); + if ($docBlock && $docBlock->isSourceDirty()) { + return true; + } + + foreach ($this->classes as $class) { + if ($class->isSourceDirty()) { + return true; + } + } + + return parent::isSourceDirty(); + } + + /** + * @return string + */ + public function generate() + { + if ($this->isSourceDirty() === false) { + return $this->sourceContent; + } + + $output = ''; + + // @note body gets populated when FileGenerator created + // from a file. @see fromReflection and may also be set + // via FileGenerator::setBody + $body = $this->getBody(); + + // start with the body (if there), or open tag + if (preg_match('#(?:\s*)<\?php#', $body) == false) { + $output = 'getDocBlock())) { + $docBlock->setIndentation(''); + + if (preg_match('#/\* Zend_Code_Generator_FileGenerator-DocBlockMarker \*/#m', $output)) { + $output = preg_replace('#/\* Zend_Code_Generator_FileGenerator-DocBlockMarker \*/#m', $docBlock->generate(), $output, 1); + } else { + $output .= $docBlock->generate() . self::LINE_FEED; + } + } + + // newline + $output .= self::LINE_FEED; + + // namespace, if any + $namespace = $this->getNamespace(); + if ($namespace) { + $namespace = sprintf('namespace %s;%s', $namespace, str_repeat(self::LINE_FEED, 2)); + if (preg_match('#/\* Zend_Code_Generator_FileGenerator-NamespaceMarker \*/#m', $output)) { + $output = preg_replace( + '#/\* Zend_Code_Generator_FileGenerator-NamespaceMarker \*/#m', + $namespace, + $output, + 1 + ); + } else { + $output .= $namespace; + } + } + + // process required files + // @todo marker replacement for required files + $requiredFiles = $this->getRequiredFiles(); + if (!empty($requiredFiles)) { + foreach ($requiredFiles as $requiredFile) { + $output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED; + } + + $output .= self::LINE_FEED; + } + + $classes = $this->getClasses(); + $classUses = []; + //build uses array + foreach ($classes as $class) { + //check for duplicate use statements + $uses = $class->getUses(); + if (!empty($uses) && is_array($uses)) { + $classUses = array_merge($classUses, $uses); + } + } + + // process import statements + $uses = $this->getUses(); + if (!empty($uses)) { + $useOutput = ''; + + foreach ($uses as $use) { + list($import, $alias) = $use; + if (null === $alias) { + $tempOutput = sprintf('%s', $import); + } else { + $tempOutput = sprintf('%s as %s', $import, $alias); + } + + //don't duplicate use statements + if (!in_array($tempOutput, $classUses)) { + $useOutput .= "use ". $tempOutput .";"; + $useOutput .= self::LINE_FEED; + } + } + $useOutput .= self::LINE_FEED; + + if (preg_match('#/\* Zend_Code_Generator_FileGenerator-UseMarker \*/#m', $output)) { + $output = preg_replace( + '#/\* Zend_Code_Generator_FileGenerator-UseMarker \*/#m', + $useOutput, + $output, + 1 + ); + } else { + $output .= $useOutput; + } + } + + // process classes + if (!empty($classes)) { + foreach ($classes as $class) { + $regex = str_replace('&', $class->getName(), '/\* Zend_Code_Generator_Php_File-ClassMarker: \{[A-Za-z0-9\\\]+?&\} \*/'); + if (preg_match('#' . $regex . '#m', $output)) { + $output = preg_replace('#' . $regex . '#', $class->generate(), $output, 1); + } else { + if ($namespace) { + $class->setNamespaceName(null); + } + $output .= $class->generate() . self::LINE_FEED; + } + } + } + + if (!empty($body)) { + // add an extra space between classes and + if (!empty($classes)) { + $output .= self::LINE_FEED; + } + + $output .= $body; + } + + return $output; + } + + /** + * @return FileGenerator + * @throws Exception\RuntimeException + */ + public function write() + { + if ($this->filename == '' || !is_writable(dirname($this->filename))) { + throw new Exception\RuntimeException('This code generator object is not writable.'); + } + file_put_contents($this->filename, $this->generate()); + + return $this; + } +} diff --git a/zendframework/zend-code/src/Generator/FileGeneratorRegistry.php b/zendframework/zend-code/src/Generator/FileGeneratorRegistry.php new file mode 100644 index 000000000..74ae50195 --- /dev/null +++ b/zendframework/zend-code/src/Generator/FileGeneratorRegistry.php @@ -0,0 +1,44 @@ +getFilename(); + } + + if ($fileName == '') { + throw new RuntimeException('FileName does not exist.'); + } + + // cannot use realpath since the file might not exist, but we do need to have the index + // in the same DIRECTORY_SEPARATOR that realpath would use: + $fileName = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fileName); + + static::$fileCodeGenerators[$fileName] = $fileCodeGenerator; + } +} diff --git a/zendframework/zend-code/src/Generator/GeneratorInterface.php b/zendframework/zend-code/src/Generator/GeneratorInterface.php new file mode 100644 index 000000000..c8c3be64a --- /dev/null +++ b/zendframework/zend-code/src/Generator/GeneratorInterface.php @@ -0,0 +1,15 @@ +isInterface()) { + throw new Exception\InvalidArgumentException(sprintf( + 'Class %s is not a interface', + $classReflection->getName() + )); + } + + // class generator + $cg = new static($classReflection->getName()); + $methods = []; + + $cg->setSourceContent($cg->getSourceContent()); + $cg->setSourceDirty(false); + + if ($classReflection->getDocComment() != '') { + $cg->setDocBlock(DocBlockGenerator::fromReflection($classReflection->getDocBlock())); + } + + // set the namespace + if ($classReflection->inNamespace()) { + $cg->setNamespaceName($classReflection->getNamespaceName()); + } + + foreach ($classReflection->getMethods() as $reflectionMethod) { + $className = ($cg->getNamespaceName()) + ? $cg->getNamespaceName() . '\\' . $cg->getName() + : $cg->getName(); + + if ($reflectionMethod->getDeclaringClass()->getName() == $className) { + $methods[] = MethodGenerator::fromReflection($reflectionMethod); + } + } + + foreach ($classReflection->getConstants() as $name => $value) { + $cg->addConstant($name, $value); + } + + $cg->addMethods($methods); + + return $cg; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey filegenerator FileGenerator File generator that holds this class + * @configkey namespacename string The namespace for this class + * @configkey docblock string The docblock information + * @configkey constants + * @configkey methods + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return InterfaceGenerator + */ + public static function fromArray(array $array) + { + if (! isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Class generator requires that a name is provided for this object' + ); + } + + $cg = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'containingfile': + $cg->setContainingFileGenerator($value); + break; + case 'namespacename': + $cg->setNamespaceName($value); + break; + case 'docblock': + $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value); + $cg->setDocBlock($docBlock); + break; + case 'methods': + $cg->addMethods($value); + break; + case 'constants': + $cg->addConstants($value); + break; + } + } + + return $cg; + } + + /** + * {@inheritdoc} + */ + public function addPropertyFromGenerator(PropertyGenerator $property) + { + return $this; + } + + /** + * {@inheritdoc} + */ + public function addMethodFromGenerator(MethodGenerator $method) + { + $method->setInterface(true); + + return parent::addMethodFromGenerator($method); + } + + /** + * {@inheritdoc} + */ + public function setExtendedClass($extendedClass) + { + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAbstract($isAbstract) + { + return $this; + } +} diff --git a/zendframework/zend-code/src/Generator/MethodGenerator.php b/zendframework/zend-code/src/Generator/MethodGenerator.php new file mode 100644 index 000000000..6f0a625a8 --- /dev/null +++ b/zendframework/zend-code/src/Generator/MethodGenerator.php @@ -0,0 +1,322 @@ +getDeclaringClass(); + + $method->setSourceContent($reflectionMethod->getContents(false)); + $method->setSourceDirty(false); + + if ($reflectionMethod->getDocComment() != '') { + $method->setDocBlock(DocBlockGenerator::fromReflection($reflectionMethod->getDocBlock())); + } + + $method->setFinal($reflectionMethod->isFinal()); + + if ($reflectionMethod->isPrivate()) { + $method->setVisibility(self::VISIBILITY_PRIVATE); + } elseif ($reflectionMethod->isProtected()) { + $method->setVisibility(self::VISIBILITY_PROTECTED); + } else { + $method->setVisibility(self::VISIBILITY_PUBLIC); + } + + $method->setInterface($declaringClass->isInterface()); + $method->setStatic($reflectionMethod->isStatic()); + + $method->setName($reflectionMethod->getName()); + + foreach ($reflectionMethod->getParameters() as $reflectionParameter) { + $method->setParameter(ParameterGenerator::fromReflection($reflectionParameter)); + } + + $method->setBody(static::clearBodyIndention($reflectionMethod->getBody())); + + return $method; + } + + /** + * Identify the space indention from the first line and remove this indention + * from all lines + * + * @param string $body + * + * @return string + */ + protected static function clearBodyIndention($body) + { + if (empty($body)) { + return $body; + } + + $lines = explode(PHP_EOL, $body); + + $indention = str_replace(trim($lines[1]), '', $lines[1]); + + foreach ($lines as $key => $line) { + if (substr($line, 0, strlen($indention)) == $indention) { + $lines[$key] = substr($line, strlen($indention)); + } + } + + $body = implode(PHP_EOL, $lines); + + return $body; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey docblock string The docblock information + * @configkey flags int Flags, one of MethodGenerator::FLAG_ABSTRACT MethodGenerator::FLAG_FINAL + * @configkey parameters string Class which this class is extending + * @configkey body string + * @configkey abstract bool + * @configkey final bool + * @configkey static bool + * @configkey visibility string + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return MethodGenerator + */ + public static function fromArray(array $array) + { + if (!isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Method generator requires that a name is provided for this object' + ); + } + + $method = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'docblock': + $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value); + $method->setDocBlock($docBlock); + break; + case 'flags': + $method->setFlags($value); + break; + case 'parameters': + $method->setParameters($value); + break; + case 'body': + $method->setBody($value); + break; + case 'abstract': + $method->setAbstract($value); + break; + case 'final': + $method->setFinal($value); + break; + case 'interface': + $method->setInterface($value); + break; + case 'static': + $method->setStatic($value); + break; + case 'visibility': + $method->setVisibility($value); + break; + } + } + + return $method; + } + + /** + * @param string $name + * @param array $parameters + * @param int $flags + * @param string $body + * @param DocBlockGenerator|string $docBlock + */ + public function __construct( + $name = null, + array $parameters = [], + $flags = self::FLAG_PUBLIC, + $body = null, + $docBlock = null + ) { + if ($name) { + $this->setName($name); + } + if ($parameters) { + $this->setParameters($parameters); + } + if ($flags !== self::FLAG_PUBLIC) { + $this->setFlags($flags); + } + if ($body) { + $this->setBody($body); + } + if ($docBlock) { + $this->setDocBlock($docBlock); + } + } + + /** + * @param array $parameters + * @return MethodGenerator + */ + public function setParameters(array $parameters) + { + foreach ($parameters as $parameter) { + $this->setParameter($parameter); + } + + return $this; + } + + /** + * @param ParameterGenerator|array|string $parameter + * @throws Exception\InvalidArgumentException + * @return MethodGenerator + */ + public function setParameter($parameter) + { + if (is_string($parameter)) { + $parameter = new ParameterGenerator($parameter); + } + + if (is_array($parameter)) { + $parameter = ParameterGenerator::fromArray($parameter); + } + + if (!$parameter instanceof ParameterGenerator) { + throw new Exception\InvalidArgumentException(sprintf( + '%s is expecting either a string, array or an instance of %s\ParameterGenerator', + __METHOD__, + __NAMESPACE__ + )); + } + + $this->parameters[$parameter->getName()] = $parameter; + + return $this; + } + + /** + * @return ParameterGenerator[] + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @param string $body + * @return MethodGenerator + */ + public function setBody($body) + { + $this->body = $body; + return $this; + } + + /** + * @return string + */ + public function getBody() + { + return $this->body; + } + + /** + * @return string + */ + public function generate() + { + $output = ''; + + $indent = $this->getIndentation(); + + if (($docBlock = $this->getDocBlock()) !== null) { + $docBlock->setIndentation($indent); + $output .= $docBlock->generate(); + } + + $output .= $indent; + + if ($this->isAbstract()) { + $output .= 'abstract '; + } else { + $output .= (($this->isFinal()) ? 'final ' : ''); + } + + $output .= $this->getVisibility() + . (($this->isStatic()) ? ' static' : '') + . ' function ' . $this->getName() . '('; + + $parameters = $this->getParameters(); + if (!empty($parameters)) { + foreach ($parameters as $parameter) { + $parameterOutput[] = $parameter->generate(); + } + + $output .= implode(', ', $parameterOutput); + } + + $output .= ')'; + + if ($this->isAbstract()) { + return $output . ';'; + } + + if ($this->isInterface()) { + return $output . ';'; + } + + $output .= self::LINE_FEED . $indent . '{' . self::LINE_FEED; + + if ($this->body) { + $output .= preg_replace('#^((?![a-zA-Z0-9_-]+;).+?)$#m', $indent . $indent . '$1', trim($this->body)) + . self::LINE_FEED; + } + + $output .= $indent . '}' . self::LINE_FEED; + + return $output; + } + + public function __toString() + { + return $this->generate(); + } +} diff --git a/zendframework/zend-code/src/Generator/ParameterGenerator.php b/zendframework/zend-code/src/Generator/ParameterGenerator.php new file mode 100644 index 000000000..16f938cab --- /dev/null +++ b/zendframework/zend-code/src/Generator/ParameterGenerator.php @@ -0,0 +1,300 @@ +setName($reflectionParameter->getName()); + + if ($reflectionParameter->isArray()) { + $param->setType('array'); + } elseif (method_exists($reflectionParameter, 'isCallable') && $reflectionParameter->isCallable()) { + $param->setType('callable'); + } else { + $typeClass = $reflectionParameter->getClass(); + if ($typeClass) { + $parameterType = $typeClass->getName(); + $currentNamespace = $reflectionParameter->getDeclaringClass()->getNamespaceName(); + + if (!empty($currentNamespace) && substr($parameterType, 0, strlen($currentNamespace)) == $currentNamespace) { + $parameterType = substr($parameterType, strlen($currentNamespace) + 1); + } else { + $parameterType = '\\' . trim($parameterType, '\\'); + } + + $param->setType($parameterType); + } + } + + $param->setPosition($reflectionParameter->getPosition()); + + if ($reflectionParameter->isOptional()) { + $param->setDefaultValue($reflectionParameter->getDefaultValue()); + } + $param->setPassedByReference($reflectionParameter->isPassedByReference()); + + return $param; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey type string + * @configkey defaultvalue null|bool|string|int|float|array|ValueGenerator + * @configkey passedbyreference bool + * @configkey position int + * @configkey sourcedirty bool + * @configkey indentation string + * @configkey sourcecontent string + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return ParameterGenerator + */ + public static function fromArray(array $array) + { + if (!isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Paramerer generator requires that a name is provided for this object' + ); + } + + $param = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'type': + $param->setType($value); + break; + case 'defaultvalue': + $param->setDefaultValue($value); + break; + case 'passedbyreference': + $param->setPassedByReference($value); + break; + case 'position': + $param->setPosition($value); + break; + case 'sourcedirty': + $param->setSourceDirty($value); + break; + case 'indentation': + $param->setIndentation($value); + break; + case 'sourcecontent': + $param->setSourceContent($value); + break; + } + } + + return $param; + } + + /** + * @param string $name + * @param string $type + * @param mixed $defaultValue + * @param int $position + * @param bool $passByReference + */ + public function __construct( + $name = null, + $type = null, + $defaultValue = null, + $position = null, + $passByReference = false + ) { + if (null !== $name) { + $this->setName($name); + } + if (null !== $type) { + $this->setType($type); + } + if (null !== $defaultValue) { + $this->setDefaultValue($defaultValue); + } + if (null !== $position) { + $this->setPosition($position); + } + if (false !== $passByReference) { + $this->setPassedByReference(true); + } + } + + /** + * @param string $type + * @return ParameterGenerator + */ + public function setType($type) + { + $this->type = (string) $type; + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $name + * @return ParameterGenerator + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set the default value of the parameter. + * + * Certain variables are difficult to express + * + * @param null|bool|string|int|float|array|ValueGenerator $defaultValue + * @return ParameterGenerator + */ + public function setDefaultValue($defaultValue) + { + if (!($defaultValue instanceof ValueGenerator)) { + $defaultValue = new ValueGenerator($defaultValue); + } + $this->defaultValue = $defaultValue; + + return $this; + } + + /** + * @return string + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * @param int $position + * @return ParameterGenerator + */ + public function setPosition($position) + { + $this->position = (int) $position; + return $this; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return bool + */ + public function getPassedByReference() + { + return $this->passedByReference; + } + + /** + * @param bool $passedByReference + * @return ParameterGenerator + */ + public function setPassedByReference($passedByReference) + { + $this->passedByReference = (bool) $passedByReference; + return $this; + } + + /** + * @return string + */ + public function generate() + { + $output = ''; + + if ($this->type && !in_array($this->type, static::$simple)) { + $output .= $this->type . ' '; + } + + if (true === $this->passedByReference) { + $output .= '&'; + } + + $output .= '$' . $this->name; + + if ($this->defaultValue !== null) { + $output .= ' = '; + if (is_string($this->defaultValue)) { + $output .= ValueGenerator::escape($this->defaultValue); + } elseif ($this->defaultValue instanceof ValueGenerator) { + $this->defaultValue->setOutputMode(ValueGenerator::OUTPUT_SINGLE_LINE); + $output .= $this->defaultValue; + } else { + $output .= $this->defaultValue; + } + } + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/PropertyGenerator.php b/zendframework/zend-code/src/Generator/PropertyGenerator.php new file mode 100644 index 000000000..657b6f42e --- /dev/null +++ b/zendframework/zend-code/src/Generator/PropertyGenerator.php @@ -0,0 +1,226 @@ +setName($reflectionProperty->getName()); + + $allDefaultProperties = $reflectionProperty->getDeclaringClass()->getDefaultProperties(); + + $property->setDefaultValue($allDefaultProperties[$reflectionProperty->getName()]); + + if ($reflectionProperty->getDocComment() != '') { + $property->setDocBlock(DocBlockGenerator::fromReflection($reflectionProperty->getDocBlock())); + } + + if ($reflectionProperty->isStatic()) { + $property->setStatic(true); + } + + if ($reflectionProperty->isPrivate()) { + $property->setVisibility(self::VISIBILITY_PRIVATE); + } elseif ($reflectionProperty->isProtected()) { + $property->setVisibility(self::VISIBILITY_PROTECTED); + } else { + $property->setVisibility(self::VISIBILITY_PUBLIC); + } + + $property->setSourceDirty(false); + + return $property; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey const bool + * @configkey defaultvalue null|bool|string|int|float|array|ValueGenerator + * @configkey flags int + * @configkey abstract bool + * @configkey final bool + * @configkey static bool + * @configkey visibility string + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return PropertyGenerator + */ + public static function fromArray(array $array) + { + if (!isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Property generator requires that a name is provided for this object' + ); + } + + $property = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'const': + $property->setConst($value); + break; + case 'defaultvalue': + $property->setDefaultValue($value); + break; + case 'docblock': + $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value); + $property->setDocBlock($docBlock); + break; + case 'flags': + $property->setFlags($value); + break; + case 'abstract': + $property->setAbstract($value); + break; + case 'final': + $property->setFinal($value); + break; + case 'static': + $property->setStatic($value); + break; + case 'visibility': + $property->setVisibility($value); + break; + } + } + + return $property; + } + + /** + * @param string $name + * @param PropertyValueGenerator|string|array $defaultValue + * @param int $flags + */ + public function __construct($name = null, $defaultValue = null, $flags = self::FLAG_PUBLIC) + { + if (null !== $name) { + $this->setName($name); + } + if (null !== $defaultValue) { + $this->setDefaultValue($defaultValue); + } + if ($flags !== self::FLAG_PUBLIC) { + $this->setFlags($flags); + } + } + + /** + * @param bool $const + * @return PropertyGenerator + */ + public function setConst($const) + { + if ($const) { + $this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PRIVATE | self::FLAG_PROTECTED); + $this->setFlags(self::FLAG_CONSTANT); + } else { + $this->removeFlag(self::FLAG_CONSTANT); + } + + return $this; + } + + /** + * @return bool + */ + public function isConst() + { + return (bool) ($this->flags & self::FLAG_CONSTANT); + } + + /** + * @param PropertyValueGenerator|mixed $defaultValue + * @param string $defaultValueType + * @param string $defaultValueOutputMode + * + * @return PropertyGenerator + */ + public function setDefaultValue($defaultValue, $defaultValueType = PropertyValueGenerator::TYPE_AUTO, $defaultValueOutputMode = PropertyValueGenerator::OUTPUT_MULTIPLE_LINE) + { + if (!($defaultValue instanceof PropertyValueGenerator)) { + $defaultValue = new PropertyValueGenerator($defaultValue, $defaultValueType, $defaultValueOutputMode); + } + + $this->defaultValue = $defaultValue; + + return $this; + } + + /** + * @return PropertyValueGenerator + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * @throws Exception\RuntimeException + * @return string + */ + public function generate() + { + $name = $this->getName(); + $defaultValue = $this->getDefaultValue(); + + $output = ''; + + if (($docBlock = $this->getDocBlock()) !== null) { + $docBlock->setIndentation(' '); + $output .= $docBlock->generate(); + } + + if ($this->isConst()) { + if ($defaultValue !== null && !$defaultValue->isValidConstantType()) { + throw new Exception\RuntimeException(sprintf( + 'The property %s is said to be ' + . 'constant but does not have a valid constant value.', + $this->name + )); + } + $output .= $this->indentation . 'const ' . $name . ' = ' + . (($defaultValue !== null) ? $defaultValue->generate() : 'null;'); + } else { + $output .= $this->indentation + . $this->getVisibility() + . (($this->isStatic()) ? ' static' : '') + . ' $' . $name . ' = ' + . (($defaultValue !== null) ? $defaultValue->generate() : 'null;'); + } + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/PropertyValueGenerator.php b/zendframework/zend-code/src/Generator/PropertyValueGenerator.php new file mode 100644 index 000000000..19bd475bc --- /dev/null +++ b/zendframework/zend-code/src/Generator/PropertyValueGenerator.php @@ -0,0 +1,23 @@ +getName()); + + $cg->setSourceContent($cg->getSourceContent()); + $cg->setSourceDirty(false); + + if ($classReflection->getDocComment() != '') { + $cg->setDocBlock(DocBlockGenerator::fromReflection($classReflection->getDocBlock())); + } + + // set the namespace + if ($classReflection->inNamespace()) { + $cg->setNamespaceName($classReflection->getNamespaceName()); + } + + $properties = []; + foreach ($classReflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->getDeclaringClass()->getName() == $classReflection->getName()) { + $properties[] = PropertyGenerator::fromReflection($reflectionProperty); + } + } + $cg->addProperties($properties); + + $methods = []; + foreach ($classReflection->getMethods() as $reflectionMethod) { + $className = ($cg->getNamespaceName()) + ? $cg->getNamespaceName() . '\\' . $cg->getName() + : $cg->getName(); + if ($reflectionMethod->getDeclaringClass()->getName() == $className) { + $methods[] = MethodGenerator::fromReflection($reflectionMethod); + } + } + $cg->addMethods($methods); + + return $cg; + } + + /** + * Generate from array + * + * @configkey name string [required] Class Name + * @configkey filegenerator FileGenerator File generator that holds this class + * @configkey namespacename string The namespace for this class + * @configkey docblock string The docblock information + * @configkey properties + * @configkey methods + * + * @throws Exception\InvalidArgumentException + * @param array $array + * @return TraitGenerator + */ + public static function fromArray(array $array) + { + if (! isset($array['name'])) { + throw new Exception\InvalidArgumentException( + 'Class generator requires that a name is provided for this object' + ); + } + + $cg = new static($array['name']); + foreach ($array as $name => $value) { + // normalize key + switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { + case 'containingfile': + $cg->setContainingFileGenerator($value); + break; + case 'namespacename': + $cg->setNamespaceName($value); + break; + case 'docblock': + $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value); + $cg->setDocBlock($docBlock); + break; + case 'properties': + $cg->addProperties($value); + break; + case 'methods': + $cg->addMethods($value); + break; + } + } + + return $cg; + } + + /** + * @param array|string $flags + * @return self + */ + public function setFlags($flags) + { + return $this; + } + + /** + * @param string $flag + * @return self + */ + public function addFlag($flag) + { + return $this; + } + + /** + * @param string $flag + * @return self + */ + public function removeFlag($flag) + { + return $this; + } + + /** + * @param bool $isFinal + * @return self + */ + public function setFinal($isFinal) + { + return $this; + } + + /** + * @param string $extendedClass + * @return self + */ + public function setExtendedClass($extendedClass) + { + return $this; + } + + /** + * @param array $implementedInterfaces + * @return self + */ + public function setImplementedInterfaces(array $implementedInterfaces) + { + return $this; + } + + /** + * @param bool $isAbstract + * @return self + */ + public function setAbstract($isAbstract) + { + return $this; + } +} diff --git a/zendframework/zend-code/src/Generator/TraitUsageGenerator.php b/zendframework/zend-code/src/Generator/TraitUsageGenerator.php new file mode 100644 index 000000000..23cf764b6 --- /dev/null +++ b/zendframework/zend-code/src/Generator/TraitUsageGenerator.php @@ -0,0 +1,353 @@ +classGenerator = $classGenerator; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addUse($use, $useAlias = null) + { + if (! empty($useAlias)) { + $use .= ' as ' . $useAlias; + } + + $this->uses[$use] = $use; + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getUses() + { + return array_values($this->uses); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTrait($trait) + { + $traitName = $trait; + if (is_array($trait)) { + if (! array_key_exists('traitName', $trait)) { + throw new Exception\InvalidArgumentException('Missing required value for traitName'); + } + $traitName = $trait['traitName']; + + if (array_key_exists('aliases', $trait)) { + foreach ($trait['aliases'] as $alias) { + $this->addAlias($alias); + } + } + + if (array_key_exists('insteadof', $trait)) { + foreach ($trait['insteadof'] as $insteadof) { + $this->addTraitOverride($insteadof); + } + } + } + + if (! $this->hasTrait($traitName)) { + $this->traits[] = $traitName; + } + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraits(array $traits) + { + foreach ($traits as $trait) { + $this->addTrait($trait); + } + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function hasTrait($traitName) + { + return in_array($traitName, $this->traits); + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraits() + { + return $this->traits; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function removeTrait($traitName) + { + $key = array_search($traitName, $this->traits); + if (false !== $key) { + unset($this->traits[$key]); + } + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraitAlias($method, $alias, $visibility = null) + { + $traitAndMethod = $method; + if (is_array($method)) { + if (! array_key_exists('traitName', $method)) { + throw new Exception\InvalidArgumentException('Missing required argument "traitName" for $method'); + } + + if (! array_key_exists('method', $method)) { + throw new Exception\InvalidArgumentException('Missing required argument "method" for $method'); + } + + $traitAndMethod = $method['traitName'] . '::' . $method['method']; + } + + // Validations + if (false === strpos($traitAndMethod, "::")) { + throw new Exception\InvalidArgumentException( + 'Invalid Format: $method must be in the format of trait::method' + ); + } + if (! is_string($alias)) { + throw new Exception\InvalidArgumentException('Invalid Alias: $alias must be a string or array.'); + } + if ($this->classGenerator->hasMethod($alias)) { + throw new Exception\InvalidArgumentException('Invalid Alias: Method name already exists on this class.'); + } + if (null !== $visibility + && $visibility !== ReflectionMethod::IS_PUBLIC + && $visibility !== ReflectionMethod::IS_PRIVATE + && $visibility !== ReflectionMethod::IS_PROTECTED + ) { + throw new Exception\InvalidArgumentException( + 'Invalid Type: $visibility must of ReflectionMethod::IS_PUBLIC,' + . ' ReflectionMethod::IS_PRIVATE or ReflectionMethod::IS_PROTECTED' + ); + } + + list($trait, $method) = explode('::', $traitAndMethod); + if (! $this->hasTrait($trait)) { + throw new Exception\InvalidArgumentException('Invalid trait: Trait does not exists on this class'); + } + + $this->traitAliases[$traitAndMethod] = [ + 'alias' => $alias, + 'visibility' => $visibility + ]; + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraitAliases() + { + return $this->traitAliases; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function addTraitOverride($method, $traitsToReplace) + { + if (false === is_array($traitsToReplace)) { + $traitsToReplace = [$traitsToReplace]; + } + + $traitAndMethod = $method; + if (is_array($method)) { + if (! array_key_exists('traitName', $method)) { + throw new Exception\InvalidArgumentException('Missing required argument "traitName" for $method'); + } + + if (! array_key_exists('method', $method)) { + throw new Exception\InvalidArgumentException('Missing required argument "method" for $method'); + } + + $traitAndMethod = (string) $method['traitName'] . '::' . (string) $method['method']; + } + + // Validations + if (false === strpos($traitAndMethod, "::")) { + throw new Exception\InvalidArgumentException( + 'Invalid Format: $method must be in the format of trait::method' + ); + } + + list($trait, $method) = explode("::", $traitAndMethod); + if (! $this->hasTrait($trait)) { + throw new Exception\InvalidArgumentException('Invalid trait: Trait does not exists on this class'); + } + + if (! array_key_exists($traitAndMethod, $this->traitOverrides)) { + $this->traitOverrides[$traitAndMethod] = []; + } + + foreach ($traitsToReplace as $traitToReplace) { + if (! is_string($traitToReplace)) { + throw new Exception\InvalidArgumentException( + 'Invalid Argument: $traitToReplace must be a string or array of strings' + ); + } + + if (! in_array($traitToReplace, $this->traitOverrides[$traitAndMethod])) { + $this->traitOverrides[$traitAndMethod][] = $traitToReplace; + } + } + + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function removeTraitOverride($method, $overridesToRemove = null) + { + if (! array_key_exists($method, $this->traitOverrides)) { + return $this; + } + + if (null === $overridesToRemove) { + unset($this->traitOverrides[$method]); + return $this; + } + + $overridesToRemove = (! is_array($overridesToRemove)) + ? [$overridesToRemove] + : $overridesToRemove; + foreach ($overridesToRemove as $traitToRemove) { + $key = array_search($traitToRemove, $this->traitOverrides[$method]); + if (false !== $key) { + unset($this->traitOverrides[$method][$key]); + } + } + return $this; + } + + /** + * @inherit Zend\Code\Generator\TraitUsageInterface + */ + public function getTraitOverrides() + { + return $this->traitOverrides; + } + + /** + * @inherit Zend\Code\Generator\GeneratorInterface + */ + public function generate() + { + $output = ''; + $indent = $this->getIndentation(); + $traits = $this->getTraits(); + + if (empty($traits)) { + return $output; + } + + $output .= $indent . 'use ' . implode(', ', $traits); + + $aliases = $this->getTraitAliases(); + $overrides = $this->getTraitOverrides(); + if (empty($aliases) && empty($overrides)) { + $output .= ";" . self::LINE_FEED . self::LINE_FEED; + return $output; + } + + $output .= ' {' . self::LINE_FEED; + foreach ($aliases as $method => $alias) { + $visibility = (null !== $alias['visibility']) + ? current(Reflection::getModifierNames($alias['visibility'])) . ' ' + : ''; + + // validation check + if ($this->classGenerator->hasMethod($alias['alias'])) { + throw new Exception\RuntimeException(sprintf( + 'Generation Error: Aliased method %s already exists on this class', + $alias['alias'] + )); + } + + $output .= + $indent + . $indent + . $method + . ' as ' + . $visibility + . $alias['alias'] + . ';' + . self::LINE_FEED; + } + + foreach ($overrides as $method => $insteadofTraits) { + foreach ($insteadofTraits as $insteadofTrait) { + $output .= + $indent + . $indent + . $method + . ' insteadof ' + . $insteadofTrait + . ';' + . self::LINE_FEED; + } + } + + $output .= self::LINE_FEED . $indent . '}' . self::LINE_FEED . self::LINE_FEED; + + return $output; + } +} diff --git a/zendframework/zend-code/src/Generator/TraitUsageInterface.php b/zendframework/zend-code/src/Generator/TraitUsageInterface.php new file mode 100644 index 000000000..560f0530d --- /dev/null +++ b/zendframework/zend-code/src/Generator/TraitUsageInterface.php @@ -0,0 +1,159 @@ +:: + * Option 2: Array + * key: traitName value: trait name + * key: method value: method name + * + * $traitToReplace: + * The name of the trait that you wish to supersede. + * + * This method provides 2 ways for defining the trait method. + * Option 1: String of trait to replace + * Option 2: Array of strings of traits to replace + + * @param mixed $method + * @param mixed $traitToReplace + */ + public function addTraitOverride($method, $traitsToReplace); + + /** + * Remove an override for a given trait::method + * + * $method: + * This method provides 2 ways for defining the trait method. + * Option 1: String Format: :: + * Option 2: Array + * key: traitName value: trait name + * key: method value: method name + * + * $overridesToRemove: + * The name of the trait that you wish to remove. + * + * This method provides 2 ways for defining the trait method. + * Option 1: String of trait to replace + * Option 2: Array of strings of traits to replace + * + * @param $traitAndMethod + * @param null $overridesToRemove + * @return $this + */ + public function removeTraitOverride($method, $overridesToRemove = null); + + /** + * Return trait overrides + * + * @return array + */ + public function getTraitOverrides(); +} diff --git a/zendframework/zend-code/src/Generator/ValueGenerator.php b/zendframework/zend-code/src/Generator/ValueGenerator.php new file mode 100644 index 000000000..13b8ee561 --- /dev/null +++ b/zendframework/zend-code/src/Generator/ValueGenerator.php @@ -0,0 +1,463 @@ +setValue($value); + } + if ($type !== self::TYPE_AUTO) { + $this->setType($type); + } + if ($outputMode !== self::OUTPUT_MULTIPLE_LINE) { + $this->setOutputMode($outputMode); + } + if ($constants === null) { + $constants = new SplArrayObject(); + } elseif (!(($constants instanceof SplArrayObject) || ($constants instanceof StdlibArrayObject))) { + throw new InvalidArgumentException( + '$constants must be an instance of ArrayObject or Zend\Stdlib\ArrayObject' + ); + } + $this->constants = $constants; + } + + /** + * Init constant list by defined and magic constants + */ + public function initEnvironmentConstants() + { + $constants = [ + '__DIR__', + '__FILE__', + '__LINE__', + '__CLASS__', + '__TRAIT__', + '__METHOD__', + '__FUNCTION__', + '__NAMESPACE__', + '::' + ]; + $constants = array_merge($constants, array_keys(get_defined_constants()), $this->constants->getArrayCopy()); + $this->constants->exchangeArray($constants); + } + + /** + * Add constant to list + * + * @param string $constant + * + * @return $this + */ + public function addConstant($constant) + { + $this->constants->append($constant); + + return $this; + } + + /** + * Delete constant from constant list + * + * @param string $constant + * + * @return bool + */ + public function deleteConstant($constant) + { + if (($index = array_search($constant, $this->constants->getArrayCopy())) !== false) { + $this->constants->offsetUnset($index); + } + + return $index !== false; + } + + /** + * Return constant list + * + * @return SplArrayObject|StdlibArrayObject + */ + public function getConstants() + { + return $this->constants; + } + + /** + * @return bool + */ + public function isValidConstantType() + { + if ($this->type == self::TYPE_AUTO) { + $type = $this->getAutoDeterminedType($this->value); + } else { + $type = $this->type; + } + + // valid types for constants + $scalarTypes = [ + self::TYPE_BOOLEAN, + self::TYPE_BOOL, + self::TYPE_NUMBER, + self::TYPE_INTEGER, + self::TYPE_INT, + self::TYPE_FLOAT, + self::TYPE_DOUBLE, + self::TYPE_STRING, + self::TYPE_CONSTANT, + self::TYPE_NULL + ]; + + return in_array($type, $scalarTypes); + } + + /** + * @param mixed $value + * @return ValueGenerator + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * @param string $type + * @return ValueGenerator + */ + public function setType($type) + { + $this->type = (string) $type; + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param int $arrayDepth + * @return ValueGenerator + */ + public function setArrayDepth($arrayDepth) + { + $this->arrayDepth = (int) $arrayDepth; + return $this; + } + + /** + * @return int + */ + public function getArrayDepth() + { + return $this->arrayDepth; + } + + /** + * @param string $type + * @return string + */ + protected function getValidatedType($type) + { + $types = [ + self::TYPE_AUTO, + self::TYPE_BOOLEAN, + self::TYPE_BOOL, + self::TYPE_NUMBER, + self::TYPE_INTEGER, + self::TYPE_INT, + self::TYPE_FLOAT, + self::TYPE_DOUBLE, + self::TYPE_STRING, + self::TYPE_ARRAY, + self::TYPE_ARRAY_SHORT, + self::TYPE_ARRAY_LONG, + self::TYPE_CONSTANT, + self::TYPE_NULL, + self::TYPE_OBJECT, + self::TYPE_OTHER + ]; + + if (in_array($type, $types)) { + return $type; + } + + return self::TYPE_AUTO; + } + + /** + * @param mixed $value + * @return string + */ + public function getAutoDeterminedType($value) + { + switch (gettype($value)) { + case 'boolean': + return self::TYPE_BOOLEAN; + case 'string': + foreach ($this->constants as $constant) { + if (strpos($value, $constant) !== false) { + return self::TYPE_CONSTANT; + } + } + return self::TYPE_STRING; + case 'double': + case 'float': + case 'integer': + return self::TYPE_NUMBER; + case 'array': + return self::TYPE_ARRAY; + case 'NULL': + return self::TYPE_NULL; + case 'object': + case 'resource': + case 'unknown type': + default: + return self::TYPE_OTHER; + } + } + + /** + * @throws Exception\RuntimeException + * @return string + */ + public function generate() + { + $type = $this->type; + + if ($type != self::TYPE_AUTO) { + $type = $this->getValidatedType($type); + } + + $value = $this->value; + + if ($type == self::TYPE_AUTO) { + $type = $this->getAutoDeterminedType($value); + } + + $isArrayType = in_array($type, [self::TYPE_ARRAY, self::TYPE_ARRAY_LONG, self::TYPE_ARRAY_SHORT]); + + if ($isArrayType) { + foreach ($value as &$curValue) { + if ($curValue instanceof self) { + continue; + } + + if (is_array($curValue)) { + $newType = $type; + } else { + $newType = self::TYPE_AUTO; + } + + $curValue = new self($curValue, $newType, self::OUTPUT_MULTIPLE_LINE, $this->getConstants()); + } + } + + $output = ''; + + switch ($type) { + case self::TYPE_BOOLEAN: + case self::TYPE_BOOL: + $output .= ($value ? 'true' : 'false'); + break; + case self::TYPE_STRING: + $output .= self::escape($value); + break; + case self::TYPE_NULL: + $output .= 'null'; + break; + case self::TYPE_NUMBER: + case self::TYPE_INTEGER: + case self::TYPE_INT: + case self::TYPE_FLOAT: + case self::TYPE_DOUBLE: + case self::TYPE_CONSTANT: + $output .= $value; + break; + case self::TYPE_ARRAY: + case self::TYPE_ARRAY_LONG: + case self::TYPE_ARRAY_SHORT: + if ($type == self::TYPE_ARRAY_SHORT) { + $startArray = '['; + $endArray = ']'; + } else { + $startArray = 'array('; + $endArray = ')'; + } + + $output .= $startArray; + if ($this->outputMode == self::OUTPUT_MULTIPLE_LINE) { + $output .= self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth + 1); + } + $outputParts = []; + $noKeyIndex = 0; + foreach ($value as $n => $v) { + /* @var $v ValueGenerator */ + $v->setArrayDepth($this->arrayDepth + 1); + $partV = $v->generate(); + $short = false; + if (is_int($n)) { + if ($n === $noKeyIndex) { + $short = true; + $noKeyIndex++; + } else { + $noKeyIndex = max($n + 1, $noKeyIndex); + } + } + + if ($short) { + $outputParts[] = $partV; + } else { + $outputParts[] = (is_int($n) ? $n : self::escape($n)) . ' => ' . $partV; + } + } + $padding = ($this->outputMode == self::OUTPUT_MULTIPLE_LINE) + ? self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth + 1) + : ' '; + $output .= implode(',' . $padding, $outputParts); + if ($this->outputMode == self::OUTPUT_MULTIPLE_LINE) { + if (count($outputParts) > 0) { + $output .= ','; + } + $output .= self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth); + } + $output .= $endArray; + break; + case self::TYPE_OTHER: + default: + throw new Exception\RuntimeException( + sprintf('Type "%s" is unknown or cannot be used as property default value.', get_class($value)) + ); + } + + return $output; + } + + /** + * Quotes value for PHP code. + * + * @param string $input Raw string. + * @param bool $quote Whether add surrounding quotes or not. + * @return string PHP-ready code. + */ + public static function escape($input, $quote = true) + { + $output = addcslashes($input, "\\'"); + + // adds quoting strings + if ($quote) { + $output = "'" . $output . "'"; + } + + return $output; + } + + /** + * @param string $outputMode + * @return ValueGenerator + */ + public function setOutputMode($outputMode) + { + $this->outputMode = (string) $outputMode; + return $this; + } + + /** + * @return string + */ + public function getOutputMode() + { + return $this->outputMode; + } + + public function __toString() + { + return $this->generate(); + } +} diff --git a/zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php b/zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php new file mode 100644 index 000000000..58c28e073 --- /dev/null +++ b/zendframework/zend-code/src/Generic/Prototype/PrototypeClassFactory.php @@ -0,0 +1,121 @@ +addPrototype($prototype); + } + + if ($genericPrototype) { + $this->setGenericPrototype($genericPrototype); + } + } + + /** + * @param PrototypeInterface $prototype + * @throws Exception\InvalidArgumentException + */ + public function addPrototype(PrototypeInterface $prototype) + { + $prototypeName = $this->normalizeName($prototype->getName()); + + if (isset($this->prototypes[$prototypeName])) { + throw new Exception\InvalidArgumentException('A prototype with this name already exists in this manager'); + } + + $this->prototypes[$prototypeName] = $prototype; + } + + /** + * @param PrototypeGenericInterface $prototype + * @throws Exception\InvalidArgumentException + */ + public function setGenericPrototype(PrototypeGenericInterface $prototype) + { + if (isset($this->genericPrototype)) { + throw new Exception\InvalidArgumentException('A default prototype is already set'); + } + + $this->genericPrototype = $prototype; + } + + /** + * @param string $name + * @return string + */ + protected function normalizeName($name) + { + return str_replace(['-', '_'], '', $name); + } + + /** + * @param string $name + * @return bool + */ + public function hasPrototype($name) + { + $name = $this->normalizeName($name); + return isset($this->prototypes[$name]); + } + + /** + * @param string $prototypeName + * @return PrototypeInterface + * @throws Exception\RuntimeException + */ + public function getClonedPrototype($prototypeName) + { + $prototypeName = $this->normalizeName($prototypeName); + + if (!$this->hasPrototype($prototypeName) && !isset($this->genericPrototype)) { + throw new Exception\RuntimeException('This tag name is not supported by this tag manager'); + } + + if (!$this->hasPrototype($prototypeName)) { + $newPrototype = clone $this->genericPrototype; + $newPrototype->setName($prototypeName); + } else { + $newPrototype = clone $this->prototypes[$prototypeName]; + } + + return $newPrototype; + } +} diff --git a/zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php b/zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php new file mode 100644 index 000000000..e9e5a0c28 --- /dev/null +++ b/zendframework/zend-code/src/Generic/Prototype/PrototypeGenericInterface.php @@ -0,0 +1,18 @@ +setNamespace($namespace); + } + if ($uses) { + $this->setUses($uses); + } + } + + /** + * @param string $namespace + * @return NameInformation + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + return $this; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return bool + */ + public function hasNamespace() + { + return ($this->namespace !== null); + } + + /** + * @param array $uses + * @return NameInformation + */ + public function setUses(array $uses) + { + $this->uses = []; + $this->addUses($uses); + + return $this; + } + + /** + * @param array $uses + * @return NameInformation + */ + public function addUses(array $uses) + { + foreach ($uses as $use => $as) { + if (is_int($use)) { + $this->addUse($as); + } elseif (is_string($use)) { + $this->addUse($use, $as); + } + } + + return $this; + } + + /** + * @param array|string $use + * @param string $as + */ + public function addUse($use, $as = null) + { + if (is_array($use) && array_key_exists('use', $use) && array_key_exists('as', $use)) { + $uses = $use; + $use = $uses['use']; + $as = $uses['as']; + } + + $use = trim($use, '\\'); + if ($as === null) { + $as = trim($use, '\\'); + $nsSeparatorPosition = strrpos($as, '\\'); + if ($nsSeparatorPosition !== false && $nsSeparatorPosition !== 0 && $nsSeparatorPosition != strlen($as)) { + $as = substr($as, $nsSeparatorPosition + 1); + } + } + + $this->uses[$use] = $as; + } + + /** + * @return array + */ + public function getUses() + { + return $this->uses; + } + + /** + * @param string $name + * @return string + */ + public function resolveName($name) + { + if ($this->namespace && !$this->uses && strlen($name) > 0 && $name{0} != '\\') { + return $this->namespace . '\\' . $name; + } + + if (!$this->uses || strlen($name) <= 0 || $name{0} == '\\') { + return ltrim($name, '\\'); + } + + if ($this->namespace || $this->uses) { + $firstPart = $name; + if (($firstPartEnd = strpos($firstPart, '\\')) !== false) { + $firstPart = substr($firstPart, 0, $firstPartEnd); + } else { + $firstPartEnd = strlen($firstPart); + } + if (($fqns = array_search($firstPart, $this->uses)) !== false) { + return substr_replace($name, $fqns, 0, $firstPartEnd); + } + if ($this->namespace) { + return $this->namespace . '\\' . $name; + } + } + + return $name; + } +} diff --git a/zendframework/zend-code/src/Reflection/ClassReflection.php b/zendframework/zend-code/src/Reflection/ClassReflection.php new file mode 100644 index 000000000..42f122c26 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/ClassReflection.php @@ -0,0 +1,283 @@ +getFileName()); + + return $instance; + } + + /** + * Return the classes DocBlock reflection object + * + * @return DocBlockReflection + * @throws Exception\ExceptionInterface for missing DocBock or invalid reflection class + */ + public function getDocBlock() + { + if (isset($this->docBlock)) { + return $this->docBlock; + } + + if ('' == $this->getDocComment()) { + return false; + } + + $this->docBlock = new DocBlockReflection($this); + + return $this->docBlock; + } + + /** + * @param AnnotationManager $annotationManager + * @return AnnotationCollection + */ + public function getAnnotations(AnnotationManager $annotationManager) + { + $docComment = $this->getDocComment(); + + if ($docComment == '') { + return false; + } + + if ($this->annotations) { + return $this->annotations; + } + + $fileScanner = $this->createFileScanner($this->getFileName()); + $nameInformation = $fileScanner->getClassNameInformation($this->getName()); + + if (!$nameInformation) { + return false; + } + + $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation); + + return $this->annotations; + } + + /** + * Return the start line of the class + * + * @param bool $includeDocComment + * @return int + */ + public function getStartLine($includeDocComment = false) + { + if ($includeDocComment && $this->getDocComment() != '') { + return $this->getDocBlock()->getStartLine(); + } + + return parent::getStartLine(); + } + + /** + * Return the contents of the class + * + * @param bool $includeDocBlock + * @return string + */ + public function getContents($includeDocBlock = true) + { + $fileName = $this->getFileName(); + + if (false === $fileName || ! file_exists($fileName)) { + return ''; + } + + $filelines = file($fileName); + $startnum = $this->getStartLine($includeDocBlock); + $endnum = $this->getEndLine() - $this->getStartLine(); + + // Ensure we get between the open and close braces + $lines = array_slice($filelines, $startnum, $endnum); + array_unshift($lines, $filelines[$startnum-1]); + + return strstr(implode('', $lines), '{'); + } + + /** + * Get all reflection objects of implemented interfaces + * + * @return ClassReflection[] + */ + public function getInterfaces() + { + $phpReflections = parent::getInterfaces(); + $zendReflections = []; + while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { + $instance = new ClassReflection($phpReflection->getName()); + $zendReflections[] = $instance; + unset($phpReflection); + } + unset($phpReflections); + + return $zendReflections; + } + + /** + * Return method reflection by name + * + * @param string $name + * @return MethodReflection + */ + public function getMethod($name) + { + $method = new MethodReflection($this->getName(), parent::getMethod($name)->getName()); + + return $method; + } + + /** + * Get reflection objects of all methods + * + * @param int $filter + * @return MethodReflection[] + */ + public function getMethods($filter = -1) + { + $methods = []; + foreach (parent::getMethods($filter) as $method) { + $instance = new MethodReflection($this->getName(), $method->getName()); + $methods[] = $instance; + } + + return $methods; + } + + /** + * Returns an array of reflection classes of traits used by this class. + * + * @return array|null + */ + public function getTraits() + { + $vals = []; + $traits = parent::getTraits(); + if ($traits === null) { + return; + } + + foreach ($traits as $trait) { + $vals[] = new ClassReflection($trait->getName()); + } + + return $vals; + } + + /** + * Get parent reflection class of reflected class + * + * @return ClassReflection|bool + */ + public function getParentClass() + { + $phpReflection = parent::getParentClass(); + if ($phpReflection) { + $zendReflection = new ClassReflection($phpReflection->getName()); + unset($phpReflection); + + return $zendReflection; + } + + return false; + } + + /** + * Return reflection property of this class by name + * + * @param string $name + * @return PropertyReflection + */ + public function getProperty($name) + { + $phpReflection = parent::getProperty($name); + $zendReflection = new PropertyReflection($this->getName(), $phpReflection->getName()); + unset($phpReflection); + + return $zendReflection; + } + + /** + * Return reflection properties of this class + * + * @param int $filter + * @return PropertyReflection[] + */ + public function getProperties($filter = -1) + { + $phpReflections = parent::getProperties($filter); + $zendReflections = []; + while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { + $instance = new PropertyReflection($this->getName(), $phpReflection->getName()); + $zendReflections[] = $instance; + unset($phpReflection); + } + unset($phpReflections); + + return $zendReflections; + } + + /** + * @return string + */ + public function toString() + { + return parent::__toString(); + } + + /** + * @return string + */ + public function __toString() + { + return parent::__toString(); + } + + /** + * Creates a new FileScanner instance. + * + * By having this as a seperate method it allows the method to be overridden + * if a different FileScanner is needed. + * + * @param string $filename + * + * @return FileScanner + */ + protected function createFileScanner($filename) + { + return new FileScanner($filename); + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php new file mode 100644 index 000000000..ec0619141 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/AuthorTag.php @@ -0,0 +1,74 @@ +]*)\>)?(.*)$/u', $tagDocblockLine, $match)) { + return; + } + + if ($match[1] !== '') { + $this->authorName = rtrim($match[1]); + } + + if (isset($match[3]) && $match[3] !== '') { + $this->authorEmail = $match[3]; + } + } + + /** + * @return null|string + */ + public function getAuthorName() + { + return $this->authorName; + } + + /** + * @return null|string + */ + public function getAuthorEmail() + { + return $this->authorEmail; + } + + public function __toString() + { + return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . PHP_EOL; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php new file mode 100644 index 000000000..1c83e57ae --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/GenericTag.php @@ -0,0 +1,109 @@ +contentSplitCharacter = $contentSplitCharacter; + } + + /** + * @param string $tagDocBlockLine + * @return void + */ + public function initialize($tagDocBlockLine) + { + $this->parse($tagDocBlockLine); + } + + /** + * Get annotation tag name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @param int $position + * @return string + */ + public function returnValue($position) + { + return $this->values[$position]; + } + + /** + * Serialize to string + * + * Required by Reflector + * + * @todo What should this do? + * @return string + */ + public function __toString() + { + return 'DocBlock Tag [ * @' . $this->name . ' ]' . PHP_EOL; + } + + /** + * @param string $docBlockLine + */ + protected function parse($docBlockLine) + { + $this->content = trim($docBlockLine); + $this->values = explode($this->contentSplitCharacter, $docBlockLine); + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php new file mode 100644 index 000000000..2880844da --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/LicenseTag.php @@ -0,0 +1,74 @@ +url = trim($match[1]); + } + + if (isset($match[2]) && $match[2] !== '') { + $this->licenseName = $match[2]; + } + } + + /** + * @return null|string + */ + public function getUrl() + { + return $this->url; + } + + /** + * @return null|string + */ + public function getLicenseName() + { + return $this->licenseName; + } + + public function __toString() + { + return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . PHP_EOL; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php new file mode 100644 index 000000000..cf74e4d32 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/MethodTag.php @@ -0,0 +1,122 @@ +isStatic = true; + } + + if ($match[2] !== '') { + $this->types = explode('|', rtrim($match[2])); + } + + $this->methodName = $match[3]; + + if ($match[4] !== '') { + $this->description = $match[4]; + } + } + + /** + * Get return value type + * + * @return null|string + * @deprecated 2.0.4 use getTypes instead + */ + public function getReturnType() + { + if (empty($this->types)) { + return; + } + + return $this->types[0]; + } + + public function getTypes() + { + return $this->types; + } + + /** + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * @return null|string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @return bool + */ + public function isStatic() + { + return $this->isStatic; + } + + public function __toString() + { + return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . PHP_EOL; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php new file mode 100644 index 000000000..1d3ccaaef --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/ParamTag.php @@ -0,0 +1,98 @@ +types = explode('|', $matches[1]); + + if (isset($matches[2])) { + $this->variableName = $matches[2]; + } + + if (isset($matches[3])) { + $this->description = trim(preg_replace('#\s+#', ' ', $matches[3])); + } + } + + /** + * Get parameter variable type + * + * @return string + * @deprecated 2.0.4 use getTypes instead + */ + public function getType() + { + if (empty($this->types)) { + return ''; + } + + return $this->types[0]; + } + + public function getTypes() + { + return $this->types; + } + + /** + * Get parameter name + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php new file mode 100644 index 000000000..415ae9f14 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/PhpDocTypedTagInterface.php @@ -0,0 +1,20 @@ +types = explode('|', rtrim($match[1])); + } + + if ($match[2] !== '') { + $this->propertyName = $match[2]; + } + + if ($match[3] !== '') { + $this->description = $match[3]; + } + } + + /** + * @return null|string + * @deprecated 2.0.4 use getTypes instead + */ + public function getType() + { + if (empty($this->types)) { + return; + } + + return $this->types[0]; + } + + public function getTypes() + { + return $this->types; + } + + /** + * @return null|string + */ + public function getPropertyName() + { + return $this->propertyName; + } + + /** + * @return null|string + */ + public function getDescription() + { + return $this->description; + } + + public function __toString() + { + return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . PHP_EOL; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php new file mode 100644 index 000000000..9da77d1a9 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/ReturnTag.php @@ -0,0 +1,75 @@ +types = explode('|', $matches[1]); + + if (isset($matches[2])) { + $this->description = trim(preg_replace('#\s+#', ' ', $matches[2])); + } + } + + /** + * @return string + * @deprecated 2.0.4 use getTypes instead + */ + public function getType() + { + if (empty($this->types)) { + return ''; + } + + return $this->types[0]; + } + + public function getTypes() + { + return $this->types; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php b/zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php new file mode 100644 index 000000000..c4c30cf62 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/Tag/TagInterface.php @@ -0,0 +1,21 @@ +types = explode('|', $matches[1]); + + if (isset($matches[2])) { + $this->description = $matches[2]; + } + } + + /** + * Get return variable type + * + * @return string + * @deprecated 2.0.4 use getTypes instead + */ + public function getType() + { + return implode('|', $this->getTypes()); + } + + /** + * @return array + */ + public function getTypes() + { + return $this->types; + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlock/TagManager.php b/zendframework/zend-code/src/Reflection/DocBlock/TagManager.php new file mode 100644 index 000000000..4e07760ef --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlock/TagManager.php @@ -0,0 +1,48 @@ +addPrototype(new Tag\ParamTag()); + $this->addPrototype(new Tag\ReturnTag()); + $this->addPrototype(new Tag\MethodTag()); + $this->addPrototype(new Tag\PropertyTag()); + $this->addPrototype(new Tag\AuthorTag()); + $this->addPrototype(new Tag\LicenseTag()); + $this->addPrototype(new Tag\ThrowsTag()); + $this->setGenericPrototype(new Tag\GenericTag()); + } + + /** + * @param string $tagName + * @param string $content + * @return TagInterface + */ + public function createTag($tagName, $content = null) + { + /* @var TagInterface $newTag */ + $newTag = $this->getClonedPrototype($tagName); + + if ($content) { + $newTag->initialize($content); + } + + return $newTag; + } +} diff --git a/zendframework/zend-code/src/Reflection/DocBlockReflection.php b/zendframework/zend-code/src/Reflection/DocBlockReflection.php new file mode 100644 index 000000000..c258a2fe8 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/DocBlockReflection.php @@ -0,0 +1,296 @@ +initializeDefaultTags(); + } + $this->tagManager = $tagManager; + + if ($commentOrReflector instanceof Reflector) { + $this->reflector = $commentOrReflector; + if (!method_exists($commentOrReflector, 'getDocComment')) { + throw new Exception\InvalidArgumentException('Reflector must contain method "getDocComment"'); + } + /* @var MethodReflection $commentOrReflector */ + $this->docComment = $commentOrReflector->getDocComment(); + + // determine line numbers + $lineCount = substr_count($this->docComment, "\n"); + $this->startLine = $this->reflector->getStartLine() - $lineCount - 1; + $this->endLine = $this->reflector->getStartLine() - 1; + } elseif (is_string($commentOrReflector)) { + $this->docComment = $commentOrReflector; + } else { + throw new Exception\InvalidArgumentException(sprintf( + '%s must have a (string) DocComment or a Reflector in the constructor', + get_class($this) + )); + } + + if ($this->docComment == '') { + throw new Exception\InvalidArgumentException('DocComment cannot be empty'); + } + + $this->reflect(); + } + + /** + * Retrieve contents of DocBlock + * + * @return string + */ + public function getContents() + { + $this->reflect(); + + return $this->cleanDocComment; + } + + /** + * Get start line (position) of DocBlock + * + * @return int + */ + public function getStartLine() + { + $this->reflect(); + + return $this->startLine; + } + + /** + * Get last line (position) of DocBlock + * + * @return int + */ + public function getEndLine() + { + $this->reflect(); + + return $this->endLine; + } + + /** + * Get DocBlock short description + * + * @return string + */ + public function getShortDescription() + { + $this->reflect(); + + return $this->shortDescription; + } + + /** + * Get DocBlock long description + * + * @return string + */ + public function getLongDescription() + { + $this->reflect(); + + return $this->longDescription; + } + + /** + * Does the DocBlock contain the given annotation tag? + * + * @param string $name + * @return bool + */ + public function hasTag($name) + { + $this->reflect(); + foreach ($this->tags as $tag) { + if ($tag->getName() == $name) { + return true; + } + } + + return false; + } + + /** + * Retrieve the given DocBlock tag + * + * @param string $name + * @return DocBlockTagInterface|false + */ + public function getTag($name) + { + $this->reflect(); + foreach ($this->tags as $tag) { + if ($tag->getName() == $name) { + return $tag; + } + } + + return false; + } + + /** + * Get all DocBlock annotation tags + * + * @param string $filter + * @return DocBlockTagInterface[] + */ + public function getTags($filter = null) + { + $this->reflect(); + if ($filter === null || !is_string($filter)) { + return $this->tags; + } + + $returnTags = []; + foreach ($this->tags as $tag) { + if ($tag->getName() == $filter) { + $returnTags[] = $tag; + } + } + + return $returnTags; + } + + /** + * Parse the DocBlock + * + * @return void + */ + protected function reflect() + { + if ($this->isReflected) { + return; + } + + $docComment = preg_replace('#[ ]{0,1}\*/$#', '', $this->docComment); + + // create a clean docComment + $this->cleanDocComment = preg_replace("#[ \t]*(?:/\*\*|\*/|\*)[ ]{0,1}(.*)?#", '$1', $docComment); + $this->cleanDocComment = ltrim($this->cleanDocComment, "\r\n"); // @todo should be changed to remove first and last empty line + + $scanner = new DocBlockScanner($docComment); + $this->shortDescription = ltrim($scanner->getShortDescription()); + $this->longDescription = ltrim($scanner->getLongDescription()); + + foreach ($scanner->getTags() as $tag) { + $this->tags[] = $this->tagManager->createTag(ltrim($tag['name'], '@'), ltrim($tag['value'])); + } + + $this->isReflected = true; + } + + /** + * @return string + */ + public function toString() + { + $str = "DocBlock [ /* DocBlock */ ] {" . PHP_EOL . PHP_EOL; + $str .= " - Tags [" . count($this->tags) . "] {" . PHP_EOL; + + foreach ($this->tags as $tag) { + $str .= " " . $tag; + } + + $str .= " }" . PHP_EOL; + $str .= "}" . PHP_EOL; + + return $str; + } + + /** + * Serialize to string + * + * Required by the Reflector interface + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } +} diff --git a/zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php b/zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php new file mode 100644 index 000000000..db54c19f5 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/Exception/BadMethodCallException.php @@ -0,0 +1,17 @@ +filePath = $fileRealPath; + $this->reflect(); + } + + /** + * Required by the Reflector interface. + * + * @todo What should this do? + * @return null + */ + public static function export() + { + return; + } + + /** + * Return the file name of the reflected file + * + * @return string + */ + public function getFileName() + { + return basename($this->filePath); + } + + /** + * Get the start line - Always 1, staying consistent with the Reflection API + * + * @return int + */ + public function getStartLine() + { + return $this->startLine; + } + + /** + * Get the end line / number of lines + * + * @return int + */ + public function getEndLine() + { + return $this->endLine; + } + + /** + * @return string + */ + public function getDocComment() + { + return $this->docComment; + } + + /** + * @return DocBlockReflection + */ + public function getDocBlock() + { + if (!($docComment = $this->getDocComment())) { + return false; + } + + $instance = new DocBlockReflection($docComment); + + return $instance; + } + + /** + * @return string[] + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * @return string + */ + public function getNamespace() + { + if (count($this->namespaces) == 0) { + return; + } + + return $this->namespaces[0]; + } + + /** + * @return array + */ + public function getUses() + { + return $this->uses; + } + + /** + * Return the reflection classes of the classes found inside this file + * + * @return ClassReflection[] + */ + public function getClasses() + { + $classes = []; + foreach ($this->classes as $class) { + $classes[] = new ClassReflection($class); + } + + return $classes; + } + + /** + * Return the reflection functions of the functions found inside this file + * + * @return FunctionReflection[] + */ + public function getFunctions() + { + $functions = []; + foreach ($this->functions as $function) { + $functions[] = new FunctionReflection($function); + } + + return $functions; + } + + /** + * Retrieve the reflection class of a given class found in this file + * + * @param null|string $name + * @return ClassReflection + * @throws Exception\InvalidArgumentException for invalid class name or invalid reflection class + */ + public function getClass($name = null) + { + if (null === $name) { + reset($this->classes); + $selected = current($this->classes); + + return new ClassReflection($selected); + } + + if (in_array($name, $this->classes)) { + return new ClassReflection($name); + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Class by name %s not found.', + $name + )); + } + + /** + * Return the full contents of file + * + * @return string + */ + public function getContents() + { + return file_get_contents($this->filePath); + } + + public function toString() + { + return ''; // @todo + } + + /** + * Serialize to string + * + * Required by the Reflector interface + * + * @todo What should this serialization look like? + * @return string + */ + public function __toString() + { + return ''; + } + + /** + * This method does the work of "reflecting" the file + * + * Uses Zend\Code\Scanner\FileScanner to gather file information + * + * @return void + */ + protected function reflect() + { + $scanner = new CachingFileScanner($this->filePath); + $this->docComment = $scanner->getDocComment(); + $this->requiredFiles = $scanner->getIncludes(); + $this->classes = $scanner->getClassNames(); + $this->namespaces = $scanner->getNamespaces(); + $this->uses = $scanner->getUses(); + } + + /** + * Validate / check a file level DocBlock + * + * @param array $tokens Array of tokenizer tokens + * @return void + */ + protected function checkFileDocBlock($tokens) + { + foreach ($tokens as $token) { + $type = $token[0]; + $value = $token[1]; + $lineNum = $token[2]; + if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) { + continue; + } elseif ($type == T_DOC_COMMENT) { + $this->docComment = $value; + $this->startLine = $lineNum + substr_count($value, "\n") + 1; + + return; + } else { + // Only whitespace is allowed before file DocBlocks + return; + } + } + } +} diff --git a/zendframework/zend-code/src/Reflection/FunctionReflection.php b/zendframework/zend-code/src/Reflection/FunctionReflection.php new file mode 100644 index 000000000..443962e21 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/FunctionReflection.php @@ -0,0 +1,269 @@ +getDocComment())) { + throw new Exception\InvalidArgumentException(sprintf( + '%s does not have a DocBlock', + $this->getName() + )); + } + + $instance = new DocBlockReflection($comment); + + return $instance; + } + + /** + * Get start line (position) of function + * + * @param bool $includeDocComment + * @return int + */ + public function getStartLine($includeDocComment = false) + { + if ($includeDocComment) { + if ($this->getDocComment() != '') { + return $this->getDocBlock()->getStartLine(); + } + } + + return parent::getStartLine(); + } + + /** + * Get contents of function + * + * @param bool $includeDocBlock + * @return string + */ + public function getContents($includeDocBlock = true) + { + $fileName = $this->getFileName(); + if (false === $fileName) { + return ''; + } + + $startLine = $this->getStartLine(); + $endLine = $this->getEndLine(); + + // eval'd protect + if (preg_match('#\((\d+)\) : eval\(\)\'d code$#', $fileName, $matches)) { + $fileName = preg_replace('#\(\d+\) : eval\(\)\'d code$#', '', $fileName); + $startLine = $endLine = $matches[1]; + } + + $lines = array_slice( + file($fileName, FILE_IGNORE_NEW_LINES), + $startLine - 1, + ($endLine - ($startLine - 1)), + true + ); + + $functionLine = implode("\n", $lines); + + $content = ''; + if ($this->isClosure()) { + preg_match('#function\s*\([^\)]*\)\s*(use\s*\([^\)]+\))?\s*\{(.*\;)?\s*\}#s', $functionLine, $matches); + if (isset($matches[0])) { + $content = $matches[0]; + } + } else { + $name = substr($this->getName(), strrpos($this->getName(), '\\')+1); + preg_match('#function\s+' . preg_quote($name) . '\s*\([^\)]*\)\s*{([^{}]+({[^}]+})*[^}]+)?}#', $functionLine, $matches); + if (isset($matches[0])) { + $content = $matches[0]; + } + } + + $docComment = $this->getDocComment(); + + return $includeDocBlock && $docComment ? $docComment . "\n" . $content : $content; + } + + /** + * Get method prototype + * + * @return array + */ + public function getPrototype($format = FunctionReflection::PROTOTYPE_AS_ARRAY) + { + $returnType = 'mixed'; + $docBlock = $this->getDocBlock(); + if ($docBlock) { + $return = $docBlock->getTag('return'); + $returnTypes = $return->getTypes(); + $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0]; + } + + $prototype = [ + 'namespace' => $this->getNamespaceName(), + 'name' => substr($this->getName(), strlen($this->getNamespaceName()) + 1), + 'return' => $returnType, + 'arguments' => [], + ]; + + $parameters = $this->getParameters(); + foreach ($parameters as $parameter) { + $prototype['arguments'][$parameter->getName()] = [ + 'type' => $parameter->getType(), + 'required' => !$parameter->isOptional(), + 'by_ref' => $parameter->isPassedByReference(), + 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, + ]; + } + + if ($format == FunctionReflection::PROTOTYPE_AS_STRING) { + $line = $prototype['return'] . ' ' . $prototype['name'] . '('; + $args = []; + foreach ($prototype['arguments'] as $name => $argument) { + $argsLine = ($argument['type'] ? $argument['type'] . ' ' : '') . ($argument['by_ref'] ? '&' : '') . '$' . $name; + if (!$argument['required']) { + $argsLine .= ' = ' . var_export($argument['default'], true); + } + $args[] = $argsLine; + } + $line .= implode(', ', $args); + $line .= ')'; + + return $line; + } + + return $prototype; + } + + /** + * Get function parameters + * + * @return ParameterReflection[] + */ + public function getParameters() + { + $phpReflections = parent::getParameters(); + $zendReflections = []; + while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { + $instance = new ParameterReflection($this->getName(), $phpReflection->getName()); + $zendReflections[] = $instance; + unset($phpReflection); + } + unset($phpReflections); + + return $zendReflections; + } + + /** + * Get return type tag + * + * @throws Exception\InvalidArgumentException + * @return DocBlockReflection + */ + public function getReturn() + { + $docBlock = $this->getDocBlock(); + if (!$docBlock->hasTag('return')) { + throw new Exception\InvalidArgumentException( + 'Function does not specify an @return annotation tag; cannot determine return type' + ); + } + + $tag = $docBlock->getTag('return'); + + return new DocBlockReflection('@return ' . $tag->getDescription()); + } + + /** + * Get method body + * + * @return string|false + */ + public function getBody() + { + $fileName = $this->getFileName(); + if (false === $fileName) { + throw new Exception\InvalidArgumentException( + 'Cannot determine internals functions body' + ); + } + + $startLine = $this->getStartLine(); + $endLine = $this->getEndLine(); + + // eval'd protect + if (preg_match('#\((\d+)\) : eval\(\)\'d code$#', $fileName, $matches)) { + $fileName = preg_replace('#\(\d+\) : eval\(\)\'d code$#', '', $fileName); + $startLine = $endLine = $matches[1]; + } + + $lines = array_slice( + file($fileName, FILE_IGNORE_NEW_LINES), + $startLine - 1, + ($endLine - ($startLine - 1)), + true + ); + + $functionLine = implode("\n", $lines); + + $body = false; + if ($this->isClosure()) { + preg_match('#function\s*\([^\)]*\)\s*(use\s*\([^\)]+\))?\s*\{(.*\;)\s*\}#s', $functionLine, $matches); + if (isset($matches[2])) { + $body = $matches[2]; + } + } else { + $name = substr($this->getName(), strrpos($this->getName(), '\\')+1); + preg_match('#function\s+' . $name . '\s*\([^\)]*\)\s*{([^{}]+({[^}]+})*[^}]+)}#', $functionLine, $matches); + if (isset($matches[1])) { + $body = $matches[1]; + } + } + + return $body; + } + + /** + * @return string + */ + public function toString() + { + return $this->__toString(); + } + + /** + * Required due to bug in php + * + * @return string + */ + public function __toString() + { + return parent::__toString(); + } +} diff --git a/zendframework/zend-code/src/Reflection/MethodReflection.php b/zendframework/zend-code/src/Reflection/MethodReflection.php new file mode 100644 index 000000000..ecd654fc7 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/MethodReflection.php @@ -0,0 +1,494 @@ +getDocComment()) { + return false; + } + + $instance = new DocBlockReflection($this); + + return $instance; + } + + /** + * @param AnnotationManager $annotationManager + * @return AnnotationScanner + */ + public function getAnnotations(AnnotationManager $annotationManager) + { + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + if ($this->annotations) { + return $this->annotations; + } + + $cachingFileScanner = $this->createFileScanner($this->getFileName()); + $nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringClass()->getName()); + + if (!$nameInformation) { + return false; + } + + $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation); + + return $this->annotations; + } + + /** + * Get start line (position) of method + * + * @param bool $includeDocComment + * @return int + */ + public function getStartLine($includeDocComment = false) + { + if ($includeDocComment) { + if ($this->getDocComment() != '') { + return $this->getDocBlock()->getStartLine(); + } + } + + return parent::getStartLine(); + } + + /** + * Get reflection of declaring class + * + * @return ClassReflection + */ + public function getDeclaringClass() + { + $phpReflection = parent::getDeclaringClass(); + $zendReflection = new ClassReflection($phpReflection->getName()); + unset($phpReflection); + + return $zendReflection; + } + + /** + * Get method prototype + * + * @return array + */ + public function getPrototype($format = MethodReflection::PROTOTYPE_AS_ARRAY) + { + $returnType = 'mixed'; + $docBlock = $this->getDocBlock(); + if ($docBlock) { + $return = $docBlock->getTag('return'); + $returnTypes = $return->getTypes(); + $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0]; + } + + $declaringClass = $this->getDeclaringClass(); + $prototype = [ + 'namespace' => $declaringClass->getNamespaceName(), + 'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1), + 'name' => $this->getName(), + 'visibility' => ($this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected')), + 'return' => $returnType, + 'arguments' => [], + ]; + + $parameters = $this->getParameters(); + foreach ($parameters as $parameter) { + $prototype['arguments'][$parameter->getName()] = [ + 'type' => $parameter->getType(), + 'required' => !$parameter->isOptional(), + 'by_ref' => $parameter->isPassedByReference(), + 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, + ]; + } + + if ($format == MethodReflection::PROTOTYPE_AS_STRING) { + $line = $prototype['visibility'] . ' ' . $prototype['return'] . ' ' . $prototype['name'] . '('; + $args = []; + foreach ($prototype['arguments'] as $name => $argument) { + $argsLine = ($argument['type'] ? $argument['type'] . ' ' : '') . ($argument['by_ref'] ? '&' : '') . '$' . $name; + if (!$argument['required']) { + $argsLine .= ' = ' . var_export($argument['default'], true); + } + $args[] = $argsLine; + } + $line .= implode(', ', $args); + $line .= ')'; + + return $line; + } + + return $prototype; + } + + /** + * Get all method parameter reflection objects + * + * @return ParameterReflection[] + */ + public function getParameters() + { + $phpReflections = parent::getParameters(); + $zendReflections = []; + while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { + $instance = new ParameterReflection( + [$this->getDeclaringClass()->getName(), $this->getName()], + $phpReflection->getName() + ); + $zendReflections[] = $instance; + unset($phpReflection); + } + unset($phpReflections); + + return $zendReflections; + } + + /** + * Get method contents + * + * @param bool $includeDocBlock + * @return string + */ + public function getContents($includeDocBlock = true) + { + $docComment = $this->getDocComment(); + $content = ($includeDocBlock && !empty($docComment)) ? $docComment . "\n" : ''; + $content .= $this->extractMethodContents(); + + return $content; + } + + /** + * Get method body + * + * @return string + */ + public function getBody() + { + return $this->extractMethodContents(true); + } + + /** + * Tokenize method string and return concatenated body + * + * @param bool $bodyOnly + * @return string + */ + protected function extractMethodContents($bodyOnly = false) + { + $fileName = $this->getFileName(); + + if ((class_exists($this->class) && false === $fileName) || ! file_exists($fileName)) { + return ''; + } + + $lines = array_slice( + file($fileName, FILE_IGNORE_NEW_LINES), + $this->getStartLine() - 1, + ($this->getEndLine() - ($this->getStartLine() - 1)), + true + ); + + $functionLine = implode("\n", $lines); + $tokens = token_get_all(" $token) { + $tokenType = (is_array($token)) ? token_name($token[0]) : $token; + $tokenValue = (is_array($token)) ? $token[1] : $token; + + switch ($tokenType) { + case "T_FINAL": + case "T_ABSTRACT": + case "T_PUBLIC": + case "T_PROTECTED": + case "T_PRIVATE": + case "T_STATIC": + case "T_FUNCTION": + // check to see if we have a valid function + // then check if we are inside function and have a closure + if ($this->isValidFunction($tokens, $key, $this->getName())) { + if ($bodyOnly === false) { + //if first instance of tokenType grab prefixed whitespace + //and append to body + if ($capture === false) { + $body .= $this->extractPrefixedWhitespace($tokens, $key); + } + $body .= $tokenValue; + } + + $capture = true; + } else { + //closure test + if ($firstBrace && $tokenType == "T_FUNCTION") { + $body .= $tokenValue; + continue; + } + $capture = false; + continue; + } + break; + + case "{": + if ($capture === false) { + continue; + } + + if ($firstBrace === false) { + $firstBrace = true; + if ($bodyOnly === true) { + continue; + } + } + + $body .= $tokenValue; + break; + + case "}": + if ($capture === false) { + continue; + } + + //check to see if this is the last brace + if ($this->isEndingBrace($tokens, $key)) { + //capture the end brace if not bodyOnly + if ($bodyOnly === false) { + $body .= $tokenValue; + } + + break 2; + } + + $body .= $tokenValue; + break; + + default: + if ($capture === false) { + continue; + } + + // if returning body only wait for first brace before capturing + if ($bodyOnly === true && $firstBrace !== true) { + continue; + } + + $body .= $tokenValue; + break; + } + } + + //remove ending whitespace and return + return rtrim($body); + } + + /** + * Take current position and find any whitespace + * + * @param array $haystack + * @param int $position + * @return string + */ + protected function extractPrefixedWhitespace($haystack, $position) + { + $content = ''; + $count = count($haystack); + if ($position+1 == $count) { + return $content; + } + + for ($i = $position-1;$i >= 0;$i--) { + $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; + $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i]; + + //search only for whitespace + if ($tokenType == "T_WHITESPACE") { + $content .= $tokenValue; + } else { + break; + } + } + + return $content; + } + + /** + * Test for ending brace + * + * @param array $haystack + * @param int $position + * @return bool + */ + protected function isEndingBrace($haystack, $position) + { + $count = count($haystack); + + //advance one position + $position = $position+1; + + if ($position == $count) { + return true; + } + + for ($i = $position;$i < $count; $i++) { + $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; + switch ($tokenType) { + case "T_FINAL": + case "T_ABSTRACT": + case "T_PUBLIC": + case "T_PROTECTED": + case "T_PRIVATE": + case "T_STATIC": + return true; + + case "T_FUNCTION": + // If a function is encountered and that function is not a closure + // then return true. otherwise the function is a closure, return false + if ($this->isValidFunction($haystack, $i)) { + return true; + } + return false; + + case "}": + case ";"; + case "T_BREAK": + case "T_CATCH": + case "T_DO": + case "T_ECHO": + case "T_ELSE": + case "T_ELSEIF": + case "T_EVAL": + case "T_EXIT": + case "T_FINALLY": + case "T_FOR": + case "T_FOREACH": + case "T_GOTO": + case "T_IF": + case "T_INCLUDE": + case "T_INCLUDE_ONCE": + case "T_PRINT": + case "T_STRING": + case "T_STRING_VARNAME": + case "T_THROW": + case "T_USE": + case "T_VARIABLE": + case "T_WHILE": + case "T_YIELD": + + return false; + } + } + } + + /** + * Test to see if current position is valid function or + * closure. Returns true if it's a function and NOT a closure + * + * @param array $haystack + * @param int $position + * @param string $functionName + * @return bool + */ + protected function isValidFunction($haystack, $position, $functionName = null) + { + $isValid = false; + $count = count($haystack); + for ($i = $position+1; $i < $count; $i++) { + $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; + $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i]; + + //check for occurance of ( or + if ($tokenType == "T_STRING") { + //check to see if function name is passed, if so validate against that + if ($functionName !== null && $tokenValue != $functionName) { + $isValid = false; + break; + } + + $isValid = true; + break; + } elseif ($tokenValue == "(") { + break; + } + } + + return $isValid; + } + + /** + * @return string + */ + public function toString() + { + return parent::__toString(); + } + + /** + * @return string + */ + public function __toString() + { + return parent::__toString(); + } + + /** + * Creates a new FileScanner instance. + * + * By having this as a seperate method it allows the method to be overridden + * if a different FileScanner is needed. + * + * @param string $filename + * + * @return CachingFileScanner + */ + protected function createFileScanner($filename) + { + return new CachingFileScanner($filename); + } +} diff --git a/zendframework/zend-code/src/Reflection/ParameterReflection.php b/zendframework/zend-code/src/Reflection/ParameterReflection.php new file mode 100644 index 000000000..984c56b08 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/ParameterReflection.php @@ -0,0 +1,116 @@ +getName()); + unset($phpReflection); + + return $zendReflection; + } + + /** + * Get class reflection object + * + * @return ClassReflection + */ + public function getClass() + { + $phpReflection = parent::getClass(); + if ($phpReflection === null) { + return; + } + + $zendReflection = new ClassReflection($phpReflection->getName()); + unset($phpReflection); + + return $zendReflection; + } + + /** + * Get declaring function reflection object + * + * @return FunctionReflection|MethodReflection + */ + public function getDeclaringFunction() + { + $phpReflection = parent::getDeclaringFunction(); + if ($phpReflection instanceof \ReflectionMethod) { + $zendReflection = new MethodReflection($this->getDeclaringClass()->getName(), $phpReflection->getName()); + } else { + $zendReflection = new FunctionReflection($phpReflection->getName()); + } + unset($phpReflection); + + return $zendReflection; + } + + /** + * Get parameter type + * + * @return string + */ + public function getType() + { + if ($this->isArray()) { + return 'array'; + } elseif (method_exists($this, 'isCallable') && $this->isCallable()) { + return 'callable'; + } + + if (($class = $this->getClass()) instanceof \ReflectionClass) { + return $class->getName(); + } + + $docBlock = $this->getDeclaringFunction()->getDocBlock(); + if (!$docBlock instanceof DocBlockReflection) { + return; + } + + $params = $docBlock->getTags('param'); + if (isset($params[$this->getPosition()])) { + return $params[$this->getPosition()]->getType(); + } + + return; + } + + /** + * @return string + */ + public function toString() + { + return parent::__toString(); + } + + /** + * @return string + */ + public function __toString() + { + return parent::__toString(); + } +} diff --git a/zendframework/zend-code/src/Reflection/PropertyReflection.php b/zendframework/zend-code/src/Reflection/PropertyReflection.php new file mode 100644 index 000000000..6cadc0270 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/PropertyReflection.php @@ -0,0 +1,114 @@ +getName()); + unset($phpReflection); + + return $zendReflection; + } + + /** + * Get DocBlock comment + * + * @return string|false False if no DocBlock defined + */ + public function getDocComment() + { + return parent::getDocComment(); + } + + /** + * @return false|DocBlockReflection + */ + public function getDocBlock() + { + if (!($docComment = $this->getDocComment())) { + return false; + } + + $docBlockReflection = new DocBlockReflection($docComment); + + return $docBlockReflection; + } + + /** + * @param AnnotationManager $annotationManager + * @return AnnotationScanner + */ + public function getAnnotations(AnnotationManager $annotationManager) + { + if (null !== $this->annotations) { + return $this->annotations; + } + + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + $class = $this->getDeclaringClass(); + $cachingFileScanner = $this->createFileScanner($class->getFileName()); + $nameInformation = $cachingFileScanner->getClassNameInformation($class->getName()); + + if (!$nameInformation) { + return false; + } + + $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation); + + return $this->annotations; + } + + /** + * @return string + */ + public function toString() + { + return $this->__toString(); + } + + /** + * Creates a new FileScanner instance. + * + * By having this as a seperate method it allows the method to be overridden + * if a different FileScanner is needed. + * + * @param string $filename + * + * @return CachingFileScanner + */ + protected function createFileScanner($filename) + { + return new CachingFileScanner($filename); + } +} diff --git a/zendframework/zend-code/src/Reflection/ReflectionInterface.php b/zendframework/zend-code/src/Reflection/ReflectionInterface.php new file mode 100644 index 000000000..8c848c8a4 --- /dev/null +++ b/zendframework/zend-code/src/Reflection/ReflectionInterface.php @@ -0,0 +1,20 @@ +directories as $scanner) { + $classes += $scanner->getClasses(); + } + if ($returnScannerClass) { + foreach ($classes as $index => $class) { + $classes[$index] = $this->getClass($class, $returnScannerClass, $returnDerivedScannerClass); + } + } + + return $classes; + } + + /** + * @param string $class + * @return bool + */ + public function hasClass($class) + { + foreach ($this->directories as $scanner) { + if ($scanner->hasClass($class)) { + break; + } else { + unset($scanner); + } + } + + return (isset($scanner)); + } + + /** + * @param string $class + * @param bool $returnScannerClass + * @param bool $returnDerivedScannerClass + * @return ClassScanner|DerivedClassScanner + * @throws Exception\RuntimeException + */ + public function getClass($class, $returnScannerClass = true, $returnDerivedScannerClass = false) + { + foreach ($this->directories as $scanner) { + if ($scanner->hasClass($class)) { + break; + } else { + unset($scanner); + } + } + + if (!isset($scanner)) { + throw new Exception\RuntimeException('Class by that name was not found.'); + } + + $classScanner = $scanner->getClass($class); + + return new DerivedClassScanner($classScanner, $this); + } + + /** + * @param bool $returnScannerClass + */ + public function getFunctions($returnScannerClass = false) + { + $this->scan(); + + if (!$returnScannerClass) { + $functions = []; + foreach ($this->infos as $info) { + if ($info['type'] == 'function') { + $functions[] = $info['name']; + } + } + + return $functions; + } + $scannerClass = new FunctionScanner(); + // @todo + } +} diff --git a/zendframework/zend-code/src/Scanner/AnnotationScanner.php b/zendframework/zend-code/src/Scanner/AnnotationScanner.php new file mode 100644 index 000000000..22216be85 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/AnnotationScanner.php @@ -0,0 +1,353 @@ +annotationManager = $annotationManager; + $this->docComment = $docComment; + $this->nameInformation = $nameInformation; + $this->scan($this->tokenize()); + } + + /** + * @param NameInformation $nameInformation + */ + public function setNameInformation(NameInformation $nameInformation) + { + $this->nameInformation = $nameInformation; + } + + /** + * @param array $tokens + */ + protected function scan(array $tokens) + { + $annotations = []; + $annotationIndex = -1; + $contentEnd = false; + + reset($tokens); + + SCANNER_TOP: + $token = current($tokens); + + switch ($token[0]) { + + case 'ANNOTATION_CLASS': + + $contentEnd = false; + $annotationIndex++; + $class = substr($token[1], 1); + $class = $this->nameInformation->resolveName($class); + $annotations[$annotationIndex] = [$class, null]; + goto SCANNER_CONTINUE; + // goto no break needed + + case 'ANNOTATION_CONTENT_START': + + $annotations[$annotationIndex][1] = ''; + //fall-through + + case 'ANNOTATION_CONTENT_END': + case 'ANNOTATION_CONTENT': + case 'ANNOTATION_WHITESPACE': + case 'ANNOTATION_NEWLINE': + + if (!$contentEnd && isset($annotations[$annotationIndex]) && is_string($annotations[$annotationIndex][1])) { + $annotations[$annotationIndex][1] .= $token[1]; + } + + if ($token[0] === 'ANNOTATION_CONTENT_END') { + $contentEnd = true; + } + + goto SCANNER_CONTINUE; + } + + SCANNER_CONTINUE: + if (next($tokens) === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + foreach ($annotations as $annotation) { + $annotation[] = '@' . $annotation[0] . $annotation[1]; + $annotationObject = $this->annotationManager->createAnnotation($annotation); + if ($annotationObject) { + $this->append($annotationObject); + } + } + } + + /** + * @return array + */ + protected function tokenize() + { + static $CONTEXT_DOCBLOCK = 0x01; + static $CONTEXT_ASTERISK = 0x02; + static $CONTEXT_CLASS = 0x04; + static $CONTEXT_CONTENT = 0x08; + + $context = 0x00; + $stream = $this->docComment; + $streamIndex = null; + $tokens = []; + $tokenIndex = null; + $currentChar = null; + $currentWord = null; + $currentLine = null; + + $annotationParentCount = 0; + + $MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (&$stream, &$streamIndex, &$currentChar, &$currentWord, &$currentLine) { + $positionsForward = ($positionsForward > 0) ? $positionsForward : 1; + $streamIndex = ($streamIndex === null) ? 0 : $streamIndex + $positionsForward; + if (!isset($stream[$streamIndex])) { + $currentChar = false; + + return false; + } + $currentChar = $stream[$streamIndex]; + $matches = []; + $currentLine = (preg_match('#(.*?)(?:\n|\r\n?)#', $stream, $matches, null, $streamIndex) === 1) ? $matches[1] : substr($stream, $streamIndex); + if ($currentChar === ' ') { + $currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine; + } else { + $currentWord = (($matches = strpos($currentLine, ' ')) !== false) ? substr($currentLine, 0, $matches) : $currentLine; + } + + return $currentChar; + }; + $MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) { + return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord)); + }; + $MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) { + return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine)); + }; + $MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) { + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + $tokens[$tokenIndex] = ['ANNOTATION_UNKNOWN', '']; + }; + $MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) { + $tokens[$tokenIndex][0] = $type; + }; + $MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentChar; + }; + $MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentWord; + }; + $MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentLine; + }; + $MACRO_HAS_CONTEXT = function ($which) use (&$context) { + return (($context & $which) === $which); + }; + + $MACRO_STREAM_ADVANCE_CHAR(); + $MACRO_TOKEN_ADVANCE(); + + TOKENIZER_TOP: + + if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTSTART'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + $context |= $CONTEXT_DOCBLOCK; + $context |= $CONTEXT_ASTERISK; + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($MACRO_HAS_CONTEXT($CONTEXT_CLASS)) { + if (in_array($currentChar, [' ', '(', "\n", "\r"])) { + $context &= ~$CONTEXT_CLASS; + $MACRO_TOKEN_ADVANCE(); + } else { + $MACRO_TOKEN_APPEND_CHAR(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + } + + // Since we don't know what line endings are used in the file, we check for all scenarios. If we find a + // cariage return (\r), we check the next character for a line feed (\n). If so we consume it and act as + // if the cariage return was a line feed. + $lineEnded = $currentChar === "\n"; + if ($currentChar === "\r") { + $lineEnded = true; + + $nextChar = $MACRO_STREAM_ADVANCE_CHAR(); + if ($nextChar !== "\n") { + $streamIndex--; + } + + $currentChar = "\n"; + } + + if ($lineEnded) { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_NEWLINE'); + $MACRO_TOKEN_APPEND_CHAR(); + $MACRO_TOKEN_ADVANCE(); + $context &= ~$CONTEXT_ASTERISK; + $context &= ~$CONTEXT_CLASS; + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === ' ') { + $MACRO_TOKEN_SET_TYPE(($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) ? 'ANNOTATION_WHITESPACE' : 'ANNOTATION_WHITESPACE_INDENT'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($MACRO_HAS_CONTEXT($CONTEXT_CONTENT) && $MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT'); + $annotationParentCount += substr_count($currentWord, '('); + $annotationParentCount -= substr_count($currentWord, ')'); + + if ($annotationParentCount === 0) { + $context &= ~$CONTEXT_CONTENT; + $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_END'); + } + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === '(' && $tokens[$tokenIndex - 1][0] === 'ANNOTATION_CLASS') { + $context |= $CONTEXT_CONTENT; + $annotationParentCount = 1; + $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_START'); + $MACRO_TOKEN_APPEND_CHAR(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && $currentWord === '*/') { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTEND'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + $context &= ~$CONTEXT_DOCBLOCK; + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === '*') { + if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && ($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK))) { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE'); + } else { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_ASTERISK'); + $context |= $CONTEXT_ASTERISK; + } + $MACRO_TOKEN_APPEND_CHAR(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === '@') { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_CLASS'); + $context |= $CONTEXT_CLASS; + $MACRO_TOKEN_APPEND_CHAR(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + TOKENIZER_CONTINUE: + + if ($context && $CONTEXT_CONTENT) { + $MACRO_TOKEN_APPEND_CHAR(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + } else { + $MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE'); + $MACRO_TOKEN_APPEND_LINE(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_LINE() === false) { + goto TOKENIZER_END; + } + } + goto TOKENIZER_TOP; + + TOKENIZER_END: + + array_pop($tokens); + + return $tokens; + } +} diff --git a/zendframework/zend-code/src/Scanner/CachingFileScanner.php b/zendframework/zend-code/src/Scanner/CachingFileScanner.php new file mode 100644 index 000000000..4fe248275 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/CachingFileScanner.php @@ -0,0 +1,160 @@ +fileScanner = static::$cache[$cacheId]; + } else { + $this->fileScanner = new FileScanner($file, $annotationManager); + static::$cache[$cacheId] = $this->fileScanner; + } + } + + /** + * @return void + */ + public static function clearCache() + { + static::$cache = []; + } + + /** + * @return AnnotationManager + */ + public function getAnnotationManager() + { + return $this->fileScanner->getAnnotationManager(); + } + + /** + * @return array|null|string + */ + public function getFile() + { + return $this->fileScanner->getFile(); + } + + /** + * @return null|string + */ + public function getDocComment() + { + return $this->fileScanner->getDocComment(); + } + + /** + * @return array + */ + public function getNamespaces() + { + return $this->fileScanner->getNamespaces(); + } + + /** + * @param null|string $namespace + * @return array|null + */ + public function getUses($namespace = null) + { + return $this->fileScanner->getUses($namespace); + } + + /** + * @return array + */ + public function getIncludes() + { + return $this->fileScanner->getIncludes(); + } + + /** + * @return array + */ + public function getClassNames() + { + return $this->fileScanner->getClassNames(); + } + + /** + * @return array + */ + public function getClasses() + { + return $this->fileScanner->getClasses(); + } + + /** + * @param int|string $className + * @return ClassScanner + */ + public function getClass($className) + { + return $this->fileScanner->getClass($className); + } + + /** + * @param string $className + * @return bool|null|NameInformation + */ + public function getClassNameInformation($className) + { + return $this->fileScanner->getClassNameInformation($className); + } + + /** + * @return array + */ + public function getFunctionNames() + { + return $this->fileScanner->getFunctionNames(); + } + + /** + * @return array + */ + public function getFunctions() + { + return $this->fileScanner->getFunctions(); + } +} diff --git a/zendframework/zend-code/src/Scanner/ClassScanner.php b/zendframework/zend-code/src/Scanner/ClassScanner.php new file mode 100644 index 000000000..de45bd473 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/ClassScanner.php @@ -0,0 +1,1344 @@ +tokens = $classTokens; + $this->nameInformation = $nameInformation; + } + + /** + * Get annotations + * + * @param Annotation\AnnotationManager $annotationManager + * @return Annotation\AnnotationCollection + */ + public function getAnnotations(Annotation\AnnotationManager $annotationManager) + { + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation); + } + + /** + * Return documentation comment + * + * @return null|string + */ + public function getDocComment() + { + $this->scan(); + + return $this->docComment; + } + + /** + * Return documentation block + * + * @return false|DocBlockScanner + */ + public function getDocBlock() + { + if (!$docComment = $this->getDocComment()) { + return false; + } + + return new DocBlockScanner($docComment); + } + + /** + * Return a name of class + * + * @return null|string + */ + public function getName() + { + $this->scan(); + return $this->name; + } + + /** + * Return short name of class + * + * @return null|string + */ + public function getShortName() + { + $this->scan(); + return $this->shortName; + } + + /** + * Return number of first line + * + * @return int|null + */ + public function getLineStart() + { + $this->scan(); + return $this->lineStart; + } + + /** + * Return number of last line + * + * @return int|null + */ + public function getLineEnd() + { + $this->scan(); + return $this->lineEnd; + } + + /** + * Verify if class is final + * + * @return bool + */ + public function isFinal() + { + $this->scan(); + return $this->isFinal; + } + + /** + * Verify if class is a trait + * @return bool + */ + public function isTrait() + { + $this->scan(); + return $this->isTrait; + } + + /** + * Verify if class is instantiable + * + * @return bool + */ + public function isInstantiable() + { + $this->scan(); + return (!$this->isAbstract && !$this->isInterface && !$this->isTrait); + } + + /** + * Verify if class is an abstract class + * + * @return bool + */ + public function isAbstract() + { + $this->scan(); + return $this->isAbstract; + } + + /** + * Verify if class is an interface + * + * @return bool + */ + public function isInterface() + { + $this->scan(); + return $this->isInterface; + } + + /** + * Verify if class has parent + * + * @return bool + */ + public function hasParentClass() + { + $this->scan(); + return ($this->parentClass !== null); + } + + /** + * Return a name of parent class + * + * @return null|string + */ + public function getParentClass() + { + $this->scan(); + return $this->parentClass; + } + + /** + * Return a list of interface names + * + * @return array + */ + public function getInterfaces() + { + $this->scan(); + return $this->interfaces; + } + + /** + * Return a list of constant names + * + * @return array + */ + public function getConstantNames() + { + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'constant') { + continue; + } + + $return[] = $info['name']; + } + + return $return; + } + + /** + * Return a list of constants + * + * @param bool $namesOnly Set false to return instances of ConstantScanner + * @return array|ConstantScanner[] + */ + public function getConstants($namesOnly = true) + { + if (true === $namesOnly) { + trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED); + return $this->getConstantNames(); + } + + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'constant') { + continue; + } + + $return[] = $this->getConstant($info['name']); + } + + return $return; + } + + /** + * Return a single constant by given name or index of info + * + * @param string|int $constantNameOrInfoIndex + * @throws Exception\InvalidArgumentException + * @return bool|ConstantScanner + */ + public function getConstant($constantNameOrInfoIndex) + { + $this->scan(); + + if (is_int($constantNameOrInfoIndex)) { + $info = $this->infos[$constantNameOrInfoIndex]; + if ($info['type'] != 'constant') { + throw new Exception\InvalidArgumentException('Index of info offset is not about a constant'); + } + } elseif (is_string($constantNameOrInfoIndex)) { + $constantFound = false; + foreach ($this->infos as $info) { + if ($info['type'] === 'constant' && $info['name'] === $constantNameOrInfoIndex) { + $constantFound = true; + break; + } + } + if (!$constantFound) { + return false; + } + } else { + throw new Exception\InvalidArgumentException( + 'Invalid constant name of info index type. Must be of type int or string' + ); + } + if (!isset($info)) { + return false; + } + $p = new ConstantScanner( + array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1), + $this->nameInformation + ); + $p->setClass($this->name); + $p->setScannerClass($this); + return $p; + } + + /** + * Verify if class has constant + * + * @param string $name + * @return bool + */ + public function hasConstant($name) + { + $this->scan(); + + foreach ($this->infos as $info) { + if ($info['type'] === 'constant' && $info['name'] === $name) { + return true; + } + } + + return false; + } + + /** + * Return a list of property names + * + * @return array + */ + public function getPropertyNames() + { + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'property') { + continue; + } + + $return[] = $info['name']; + } + + return $return; + } + + /** + * Return a list of properties + * + * @return PropertyScanner[] + */ + public function getProperties() + { + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'property') { + continue; + } + + $return[] = $this->getProperty($info['name']); + } + + return $return; + } + + /** + * Return a single property by given name or index of info + * + * @param string|int $propertyNameOrInfoIndex + * @throws Exception\InvalidArgumentException + * @return bool|PropertyScanner + */ + public function getProperty($propertyNameOrInfoIndex) + { + $this->scan(); + + if (is_int($propertyNameOrInfoIndex)) { + $info = $this->infos[$propertyNameOrInfoIndex]; + if ($info['type'] != 'property') { + throw new Exception\InvalidArgumentException('Index of info offset is not about a property'); + } + } elseif (is_string($propertyNameOrInfoIndex)) { + $propertyFound = false; + foreach ($this->infos as $info) { + if ($info['type'] === 'property' && $info['name'] === $propertyNameOrInfoIndex) { + $propertyFound = true; + break; + } + } + if (!$propertyFound) { + return false; + } + } else { + throw new Exception\InvalidArgumentException( + 'Invalid property name of info index type. Must be of type int or string' + ); + } + if (!isset($info)) { + return false; + } + $p = new PropertyScanner( + array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart'] + 1), + $this->nameInformation + ); + $p->setClass($this->name); + $p->setScannerClass($this); + return $p; + } + + /** + * Verify if class has property + * + * @param string $name + * @return bool + */ + public function hasProperty($name) + { + $this->scan(); + + foreach ($this->infos as $info) { + if ($info['type'] === 'property' && $info['name'] === $name) { + return true; + } + } + + return false; + } + + /** + * Retrieve any traits used by the class. + * + * @return ClassScanner[] + */ + public function getTraits() + { + if (! empty($this->traits)) { + return $this->traits; + } + + // get list of trait names + $traitNames = $this->getTraitNames(); + foreach ($traitNames as $traitName) { + $r = new ReflectionClass($traitName); + if (! $r->isTrait()) { + throw new Exception\RuntimeException(sprintf( + 'Non-trait class detected as a trait: %s', + $traitName + )); + } + $fileName = $r->getFileName(); + + $file = new FileScanner($fileName); + $this->traits[] = $file->getClass($traitName); + } + + return $this->traits; + } + + /** + * Retrieve a list of trait names used by this class. + * + * @return array + */ + public function getTraitNames() + { + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] !== 'use') { + continue; + } + + if (is_array($info['use_statements'])) { + foreach ($info['use_statements'] as $trait) { + $traitName = $trait; + if ($this->nameInformation instanceof NameInformation) { + $traitName = $this->nameInformation->resolveName($traitName); + } + $return[] = $traitName; + } + } + break; + } + + return $return; + } + + /** + * Retrieve a list of aliased traits used by the class. + * + * @return array + */ + public function getTraitAliases() + { + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] !== 'use') { + continue; + } + + if (is_array($info['aliases'])) { + foreach ($info['aliases'] as $alias) { + if (null === $alias + || (! empty($alias['type']) && $alias['type'] !== 'as') + ) { + continue; + } + + // attempt to get fqcn + list($trait, $method) = explode('::', $alias['original']); + if ($this->nameInformation instanceof NameInformation) { + $trait = $this->nameInformation->resolveName($trait); + } + + $return[$alias['alias']] = $trait . '::' . $method; + } + } + break; + } + + return $return; + } + + /** + * Retrieve visibility for a given alias. + * + * @param mixed $aliasName + * @return string + */ + protected function getVisibilityForAlias($aliasName) + { + $return = null; + foreach ($this->infos as $info) { + if ($info['type'] !== 'use') { + continue; + } + + if (is_array($info['aliases'])) { + foreach ($info['aliases'] as $alias) { + if (null === $alias + && (! empty($alias['type']) && $alias['type'] !== 'as') + ) { + continue; + } + + if ($alias['alias'] === $aliasName) { + $return = $alias['visibility']; + break 2; + } + } + } + break; + } + + return $return; + } + + /** + * Return an array of key = trait to keep, value = trait::method to ignore + * + * @return array + */ + protected function getBlockedTraitMethods() + { + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] !== 'use') { + continue; + } + + if (is_array($info['aliases'])) { + foreach ($info['aliases'] as $alias) { + if (null === $alias + || (! empty($alias['type']) && $alias['type'] !== 'insteadof') + ) { + continue; + } + + // attempt to get fqcn + list($trait, $method) = explode('::', $alias['original']); + if ($this->nameInformation instanceof NameInformation) { + $trait = $this->nameInformation->resolveName($alias['alias']); + } + + $return[] = $trait . '::' . $method; + } + } + break; + } + + return $return; + } + + /** + * Return a list of method names + * + * @return array + */ + public function getMethodNames() + { + $this->scan(); + + $methods = $this->getMethods(); + $return = []; + foreach ($methods as $method) { + $return[] = $method->getName(); + } + + return $return; + } + + /** + * Return a list of methods + * + * @return MethodScanner[] + */ + public function getMethods() + { + $this->scan(); + + if (! empty($this->methods)) { + return $this->methods; + } + + foreach ($this->infos as $info) { + if ($info['type'] !== 'method' && $info['type'] !== 'use') { + continue; + } + + // Merge in trait methods + if ($info['type'] === "use") { + $traitMethods = []; + $traits = $this->getTraits(); + $insteadof = $this->getBlockedTraitMethods(); + $aliases = $this->getTraitAliases(); + + foreach ($traits as $trait) { + $tempMethods = $trait->getMethods(); + foreach ($tempMethods as $tempMethod) { + $methodFullName = $trait->getName() . '::' . $tempMethod->getName(); + $methodAlias = array_search($methodFullName, $aliases); + + if (false !== $methodAlias) { + // trait::method is aliased + // clone the tempMethod as we need to change + // the name and possibly the visibility of the + // scanned method. + // + // @todo setName and setVisibility were added to + // MethodScanner to accomplish this, may not be the + // best option, could use ReflectionClass instead? + $newMethod = clone $tempMethod; + $newMethod->setName($methodAlias); + + // if visibility exists, change it on the MethodScanner + $visibility = $this->getVisibilityForAlias($methodAlias); + if (null !== $visibility) { + $newMethod->setVisibility($visibility); + } + $traitMethods[$methodAlias] = $newMethod; + } elseif (in_array($methodFullName, $insteadof)) { + // ignore overridden methods + continue; + } else { + if (array_key_exists($tempMethod->getName(), $traitMethods)) { + throw new Exception\RuntimeException(sprintf( + 'Trait method %s has not been applied because there are' + . ' collisions with other trait methods see: (insteadof OR as)', + $tempMethod->getName() + )); + } + + $traitMethods[$tempMethod->getName()] = $tempMethod; + } + } + } + + $this->methods = array_merge($this->methods, array_values($traitMethods)); + continue; + } + + $m = new MethodScanner( + array_slice( + $this->tokens, + $info['tokenStart'], + $info['tokenEnd'] - $info['tokenStart'] + 1 + ), + $this->nameInformation + ); + $m->setClass($this->name); + $m->setScannerClass($this); + + $this->methods[] = $m; + } + + return $this->methods; + } + + /** + * Return a single method by given name or index of info + * + * @param string|int $methodNameOrInfoIndex + * @throws Exception\InvalidArgumentException + * @return MethodScanner + */ + public function getMethod($methodNameOrInfoIndex) + { + $this->scan(); + + if (is_int($methodNameOrInfoIndex)) { + $info = $this->infos[$methodNameOrInfoIndex]; + if ($info['type'] != 'method') { + throw new Exception\InvalidArgumentException('Index of info offset is not about a method'); + } + $methodNameOrInfoIndex = $info['name']; + } + + $returnMethod = false; + $methods = $this->getMethods(); + foreach ($methods as $method) { + if ($method->getName() === $methodNameOrInfoIndex) { + $returnMethod = $method; + break; + } + } + + return $returnMethod; + } + + /** + * Verify if class has method by given name + * + * @param string $name + * @return bool + */ + public function hasMethod($name) + { + $this->scan(); + + return is_object($this->getMethod($name)); + } + + public static function export() + { + // @todo + } + + public function __toString() + { + // @todo + } + + /** + * Scan tokens + * + * @return void + * @throws Exception\RuntimeException + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new Exception\RuntimeException('No tokens were provided'); + } + + /** + * Variables & Setup + */ + + $tokens = &$this->tokens; // localize + $infos = &$this->infos; // localize + $tokenIndex = null; + $token = null; + $tokenType = null; + $tokenContent = null; + $tokenLine = null; + $namespace = null; + $infoIndex = 0; + $braceCount = 0; + + /* + * MACRO creation + */ + $MACRO_TOKEN_ADVANCE = function () use ( + &$tokens, + &$tokenIndex, + &$token, + &$tokenType, + &$tokenContent, + &$tokenLine + ) { + static $lastTokenArray = null; + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + if (!isset($tokens[$tokenIndex])) { + $token = false; + $tokenContent = false; + $tokenType = false; + $tokenLine = false; + + return false; + } + $token = $tokens[$tokenIndex]; + + if (is_string($token)) { + $tokenType = null; + $tokenContent = $token; + $tokenLine = $tokenLine + substr_count( + $lastTokenArray[1], + "\n" + ); // adjust token line by last known newline count + } else { + $lastTokenArray = $token; + list($tokenType, $tokenContent, $tokenLine) = $token; + } + + return $tokenIndex; + }; + $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { + $infos[$infoIndex]['tokenEnd'] = $tokenIndex; + $infos[$infoIndex]['lineEnd'] = $tokenLine; + $infoIndex++; + + return $infoIndex; + }; + + /** + * START FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + // Initialize token + $MACRO_TOKEN_ADVANCE(); + + SCANNER_TOP: + + switch ($tokenType) { + + case T_DOC_COMMENT: + + $this->docComment = $tokenContent; + goto SCANNER_CONTINUE; + //goto no break needed + + case T_FINAL: + case T_ABSTRACT: + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + + // CLASS INFORMATION + + $classContext = null; + $classInterfaceIndex = 0; + + SCANNER_CLASS_INFO_TOP: + + if (is_string($tokens[$tokenIndex + 1]) && $tokens[$tokenIndex + 1] === '{') { + goto SCANNER_CLASS_INFO_END; + } + + $this->lineStart = $tokenLine; + + switch ($tokenType) { + + case T_FINAL: + $this->isFinal = true; + goto SCANNER_CLASS_INFO_CONTINUE; + // goto no break needed + + case T_ABSTRACT: + $this->isAbstract = true; + goto SCANNER_CLASS_INFO_CONTINUE; + // goto no break needed + + case T_TRAIT: + $this->isTrait = true; + $this->shortName = $tokens[$tokenIndex + 2][1]; + if ($this->nameInformation && $this->nameInformation->hasNamespace()) { + $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName; + } else { + $this->name = $this->shortName; + } + goto SCANNER_CLASS_INFO_CONTINUE; + + case T_INTERFACE: + $this->isInterface = true; + //fall-through + case T_CLASS: + $this->shortName = $tokens[$tokenIndex + 2][1]; + if ($this->nameInformation && $this->nameInformation->hasNamespace()) { + $this->name = $this->nameInformation->getNamespace() . '\\' . $this->shortName; + } else { + $this->name = $this->shortName; + } + goto SCANNER_CLASS_INFO_CONTINUE; + //goto no break needed + + case T_NS_SEPARATOR: + case T_STRING: + switch ($classContext) { + case T_EXTENDS: + $this->shortParentClass .= $tokenContent; + break; + case T_IMPLEMENTS: + $this->shortInterfaces[$classInterfaceIndex] .= $tokenContent; + break; + } + goto SCANNER_CLASS_INFO_CONTINUE; + //goto no break needed + + case T_EXTENDS: + case T_IMPLEMENTS: + $classContext = $tokenType; + if (($this->isInterface && $classContext === T_EXTENDS) || $classContext === T_IMPLEMENTS) { + $this->shortInterfaces[$classInterfaceIndex] = ''; + } elseif (!$this->isInterface && $classContext === T_EXTENDS) { + $this->shortParentClass = ''; + } + goto SCANNER_CLASS_INFO_CONTINUE; + //goto no break needed + + case null: + if ($classContext == T_IMPLEMENTS && $tokenContent == ',') { + $classInterfaceIndex++; + $this->shortInterfaces[$classInterfaceIndex] = ''; + } + + } + + SCANNER_CLASS_INFO_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_CLASS_INFO_TOP; + + SCANNER_CLASS_INFO_END: + + goto SCANNER_CONTINUE; + + } + + if ($tokenType === null && $tokenContent === '{' && $braceCount === 0) { + $braceCount++; + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_CLASS_BODY_TOP: + + if ($braceCount === 0) { + goto SCANNER_CLASS_BODY_END; + } + + switch ($tokenType) { + + case T_CONST: + + $infos[$infoIndex] = [ + 'type' => 'constant', + 'tokenStart' => $tokenIndex, + 'tokenEnd' => null, + 'lineStart' => $tokenLine, + 'lineEnd' => null, + 'name' => null, + 'value' => null, + ]; + + SCANNER_CLASS_BODY_CONST_TOP: + + if ($tokenContent === ';') { + goto SCANNER_CLASS_BODY_CONST_END; + } + + if ($tokenType === T_STRING && null === $infos[$infoIndex]['name']) { + $infos[$infoIndex]['name'] = $tokenContent; + } + + SCANNER_CLASS_BODY_CONST_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_CLASS_BODY_CONST_TOP; + + SCANNER_CLASS_BODY_CONST_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CLASS_BODY_CONTINUE; + // goto no break needed + + case T_USE: + // ensure php backwards compatibility + if (! defined('T_INSTEADOF')) { + define('T_INSTEADOF', 24000); + } + + $infos[$infoIndex] = [ + 'type' => 'use', + 'tokenStart' => $tokenIndex, + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'name' => $namespace, + 'use_statements' => [0 => null], + 'aliases' => [0 => null], + ]; + + $isOriginalName = [T_STRING, T_DOUBLE_COLON]; + $isAlias = [T_STRING]; + $isVisibility = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC]; + $isAliasType = [T_AS, T_INSTEADOF]; + $isValidAlias = array_merge($isOriginalName, $isAlias, $isVisibility, $isAliasType); + + $useStatementIndex = 0; + $aliasStatementIndex = 0; + $useAliasContext = false; + $useAsContext = false; + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_USE_TOP: + + if ($tokenType === null) { + if ($tokenContent === "{") { + $useStatementIndex = 0; + $useAliasContext = true; + $infos[$infoIndex]['aliases'][$useStatementIndex] = [ + 'original' => null, + 'alias' => null, + 'visibility' => null, + 'type' => 'as' + ]; + } elseif ($tokenContent === "}") { + $useAliasContext = false; + goto SCANNER_USE_END; + } elseif ($tokenContent === ';') { + if ($useAliasContext === true) { + $useStatementIndex++; + $useAsContext = false; + } + // only end if we aren't inside braces + if (false === $useAliasContext) { + goto SCANNER_USE_END; + } + } elseif ($tokenContent === ',') { + $useStatementIndex++; + $infos[$infoIndex]['use_statements'][$useStatementIndex] = ''; + } + } + + // ANALYZE + if ($tokenType !== null) { + // use context + if (false === $useAliasContext) { + if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) { + $infos[$infoIndex]['use_statements'][$useStatementIndex] .= $tokenContent; + } + } else { + if (in_array($tokenType, $isValidAlias) + && empty($infos[$infoIndex]['aliases'][$useStatementIndex]) + ) { + $infos[$infoIndex]['aliases'][$useStatementIndex] = [ + 'original' => null, + 'visibility' => null, + 'alias' => null, + 'type' => null + ]; + } + + if ($tokenType == T_AS || $tokenType == T_INSTEADOF) { + $useAsContext = true; + $infos[$infoIndex]['aliases'][$useStatementIndex]['type'] = ($tokenType == T_INSTEADOF) + ? 'insteadof' + : 'as'; + goto SCANNER_USE_CONTINUE; + } + + // in alias context + if ($useAsContext === true && in_array($tokenType, $isAlias)) { + $infos[$infoIndex]['aliases'][$useStatementIndex]['alias'] = $tokenContent; + } elseif (in_array($tokenType, $isOriginalName)) { + $infos[$infoIndex]['aliases'][$useStatementIndex]['original'] .= $tokenContent; + } elseif (in_array($tokenType, $isVisibility)) { + //add whitespace (will trim later) + $infos[$infoIndex]['aliases'][$useStatementIndex]['visibility'] = $tokenType; + } + } + } + + SCANNER_USE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_USE_TOP; + + SCANNER_USE_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CLASS_BODY_CONTINUE; + + case T_DOC_COMMENT: + case T_PUBLIC: + case T_PROTECTED: + case T_PRIVATE: + case T_ABSTRACT: + case T_FINAL: + case T_VAR: + case T_FUNCTION: + + $infos[$infoIndex] = [ + 'type' => null, + 'tokenStart' => $tokenIndex, + 'tokenEnd' => null, + 'lineStart' => $tokenLine, + 'lineEnd' => null, + 'name' => null, + ]; + + $memberContext = null; + $methodBodyStarted = false; + + SCANNER_CLASS_BODY_MEMBER_TOP: + + if ($memberContext === 'method') { + switch ($tokenContent) { + case '{': + $methodBodyStarted = true; + $braceCount++; + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + // goto no break needed + case '}': + $braceCount--; + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + + case ';': + $infos[$infoIndex]['tokenEnd'] = $tokenIndex; + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + } + } + + if ($memberContext !== null) { + if ( + ($memberContext === 'property' && $tokenContent === ';') + || ($memberContext === 'method' && $methodBodyStarted && $braceCount === 1) + || ($memberContext === 'method' && $this->isInterface && $tokenContent === ';') + ) { + goto SCANNER_CLASS_BODY_MEMBER_END; + } + } + + switch ($tokenType) { + + case T_CONST: + $memberContext = 'constant'; + $infos[$infoIndex]['type'] = 'constant'; + goto SCANNER_CLASS_BODY_CONST_CONTINUE; + //goto no break needed + + case T_VARIABLE: + if ($memberContext === null) { + $memberContext = 'property'; + $infos[$infoIndex]['type'] = 'property'; + $infos[$infoIndex]['name'] = ltrim($tokenContent, '$'); + } + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + // goto no break needed + + case T_FUNCTION: + $memberContext = 'method'; + $infos[$infoIndex]['type'] = 'method'; + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + // goto no break needed + + case T_STRING: + if ($memberContext === 'method' && null === $infos[$infoIndex]['name']) { + $infos[$infoIndex]['name'] = $tokenContent; + } + goto SCANNER_CLASS_BODY_MEMBER_CONTINUE; + // goto no break needed + } + + SCANNER_CLASS_BODY_MEMBER_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_CLASS_BODY_MEMBER_TOP; + + SCANNER_CLASS_BODY_MEMBER_END: + + $memberContext = null; + $MACRO_INFO_ADVANCE(); + goto SCANNER_CLASS_BODY_CONTINUE; + // goto no break needed + + case null: // no type, is a string + + switch ($tokenContent) { + case '{': + $braceCount++; + goto SCANNER_CLASS_BODY_CONTINUE; + // goto no break needed + + case '}': + $braceCount--; + goto SCANNER_CLASS_BODY_CONTINUE; + } + } + + SCANNER_CLASS_BODY_CONTINUE: + + if ($braceCount === 0 || $MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_CONTINUE; + } + goto SCANNER_CLASS_BODY_TOP; + + SCANNER_CLASS_BODY_END: + + goto SCANNER_CONTINUE; + } + + SCANNER_CONTINUE: + + if ($tokenContent === '}') { + $this->lineEnd = $tokenLine; + } + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + // process short names + if ($this->nameInformation) { + if ($this->shortParentClass) { + $this->parentClass = $this->nameInformation->resolveName($this->shortParentClass); + } + if ($this->shortInterfaces) { + foreach ($this->shortInterfaces as $siIndex => $si) { + $this->interfaces[$siIndex] = $this->nameInformation->resolveName($si); + } + } + } else { + $this->parentClass = $this->shortParentClass; + $this->interfaces = $this->shortInterfaces; + } + + $this->isScanned = true; + + return; + } +} diff --git a/zendframework/zend-code/src/Scanner/ConstantScanner.php b/zendframework/zend-code/src/Scanner/ConstantScanner.php new file mode 100644 index 000000000..31e245c9d --- /dev/null +++ b/zendframework/zend-code/src/Scanner/ConstantScanner.php @@ -0,0 +1,236 @@ +tokens = $constantTokens; + $this->nameInformation = $nameInformation; + } + + /** + * @param string $class + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * @param ClassScanner $scannerClass + */ + public function setScannerClass(ClassScanner $scannerClass) + { + $this->scannerClass = $scannerClass; + } + + /** + * @return ClassScanner + */ + public function getClassScanner() + { + return $this->scannerClass; + } + + /** + * @return string + */ + public function getName() + { + $this->scan(); + return $this->name; + } + + /** + * @return string + */ + public function getValue() + { + $this->scan(); + return $this->value; + } + + /** + * @return string + */ + public function getDocComment() + { + $this->scan(); + return $this->docComment; + } + + /** + * @param Annotation\AnnotationManager $annotationManager + * @return AnnotationScanner + */ + public function getAnnotations(Annotation\AnnotationManager $annotationManager) + { + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation); + } + + /** + * @return string + */ + public function __toString() + { + $this->scan(); + return var_export($this, true); + } + + /** + * Scan tokens + * + * @throws Exception\RuntimeException + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new Exception\RuntimeException('No tokens were provided'); + } + + /** + * Variables & Setup + */ + $tokens = &$this->tokens; + + reset($tokens); + + SCANNER_TOP: + + $token = current($tokens); + + if (!is_string($token)) { + list($tokenType, $tokenContent, $tokenLine) = $token; + + switch ($tokenType) { + case T_DOC_COMMENT: + if ($this->docComment === null && $this->name === null) { + $this->docComment = $tokenContent; + } + goto SCANNER_CONTINUE; + // fall-through + + case T_STRING: + $string = (is_string($token)) ? $token : $tokenContent; + + if (null === $this->name) { + $this->name = $string; + } else { + if ('self' == strtolower($string)) { + list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens); + + if ('::' == $tokenNextContent) { + list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens); + + if ($this->getClassScanner()->getConstant($tokenNextContent)) { + $this->value = $this->getClassScanner()->getConstant($tokenNextContent)->getValue(); + } + } + } + } + + goto SCANNER_CONTINUE; + // fall-through + + case T_CONSTANT_ENCAPSED_STRING: + case T_DNUMBER: + case T_LNUMBER: + $string = (is_string($token)) ? $token : $tokenContent; + + if (substr($string, 0, 1) === '"' || substr($string, 0, 1) === "'") { + $this->value = substr($string, 1, -1); // Remove quotes + } else { + $this->value = $string; + } + goto SCANNER_CONTINUE; + // fall-trough + + default: + goto SCANNER_CONTINUE; + } + } + + SCANNER_CONTINUE: + + if (next($this->tokens) === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + $this->isScanned = true; + } +} diff --git a/zendframework/zend-code/src/Scanner/DerivedClassScanner.php b/zendframework/zend-code/src/Scanner/DerivedClassScanner.php new file mode 100644 index 000000000..cb32172b6 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/DerivedClassScanner.php @@ -0,0 +1,381 @@ +classScanner = $classScanner; + $this->directoryScanner = $directoryScanner; + + $currentScannerClass = $classScanner; + + while ($currentScannerClass && $currentScannerClass->hasParentClass()) { + $currentParentClassName = $currentScannerClass->getParentClass(); + if ($directoryScanner->hasClass($currentParentClassName)) { + $currentParentClass = $directoryScanner->getClass($currentParentClassName); + $this->parentClassScanners[$currentParentClassName] = $currentParentClass; + $currentScannerClass = $currentParentClass; + } else { + $currentScannerClass = false; + } + } + + foreach ($interfaces = $this->classScanner->getInterfaces() as $iName) { + if ($directoryScanner->hasClass($iName)) { + $this->interfaceClassScanners[$iName] = $directoryScanner->getClass($iName); + } + } + } + + /** + * @return null|string + */ + public function getName() + { + return $this->classScanner->getName(); + } + + /** + * @return null|string + */ + public function getShortName() + { + return $this->classScanner->getShortName(); + } + + /** + * @return bool + */ + public function isInstantiable() + { + return $this->classScanner->isInstantiable(); + } + + /** + * @return bool + */ + public function isFinal() + { + return $this->classScanner->isFinal(); + } + + /** + * @return bool + */ + public function isAbstract() + { + return $this->classScanner->isAbstract(); + } + + /** + * @return bool + */ + public function isInterface() + { + return $this->classScanner->isInterface(); + } + + /** + * @return array + */ + public function getParentClasses() + { + return array_keys($this->parentClassScanners); + } + + /** + * @return bool + */ + public function hasParentClass() + { + return ($this->classScanner->getParentClass() !== null); + } + + /** + * @return null|string + */ + public function getParentClass() + { + return $this->classScanner->getParentClass(); + } + + /** + * @param bool $returnClassScanners + * @return array + */ + public function getInterfaces($returnClassScanners = false) + { + if ($returnClassScanners) { + return $this->interfaceClassScanners; + } + + $interfaces = $this->classScanner->getInterfaces(); + foreach ($this->parentClassScanners as $pClassScanner) { + $interfaces = array_merge($interfaces, $pClassScanner->getInterfaces()); + } + + return $interfaces; + } + + /** + * Return a list of constant names + * + * @return array + */ + public function getConstantNames() + { + $constants = $this->classScanner->getConstantNames(); + foreach ($this->parentClassScanners as $pClassScanner) { + $constants = array_merge($constants, $pClassScanner->getConstantNames()); + } + + return $constants; + } + + /** + * Return a list of constants + * + * @param bool $namesOnly Set false to return instances of ConstantScanner + * @return array|ConstantScanner[] + */ + public function getConstants($namesOnly = true) + { + if (true === $namesOnly) { + trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED); + return $this->getConstantNames(); + } + + $constants = $this->classScanner->getConstants(); + foreach ($this->parentClassScanners as $pClassScanner) { + $constants = array_merge($constants, $pClassScanner->getConstants($namesOnly)); + } + + return $constants; + } + + /** + * Return a single constant by given name or index of info + * + * @param string|int $constantNameOrInfoIndex + * @throws Exception\InvalidArgumentException + * @return bool|ConstantScanner + */ + public function getConstant($constantNameOrInfoIndex) + { + if ($this->classScanner->hasConstant($constantNameOrInfoIndex)) { + return $this->classScanner->getConstant($constantNameOrInfoIndex); + } + + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasConstant($constantNameOrInfoIndex)) { + return $pClassScanner->getConstant($constantNameOrInfoIndex); + } + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Constant %s not found in %s', + $constantNameOrInfoIndex, + $this->classScanner->getName() + )); + } + + /** + * Verify if class or parent class has constant + * + * @param string $name + * @return bool + */ + public function hasConstant($name) + { + if ($this->classScanner->hasConstant($name)) { + return true; + } + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasConstant($name)) { + return true; + } + } + + return false; + } + + /** + * Return a list of property names + * + * @return array + */ + public function getPropertyNames() + { + $properties = $this->classScanner->getPropertyNames(); + foreach ($this->parentClassScanners as $pClassScanner) { + $properties = array_merge($properties, $pClassScanner->getPropertyNames()); + } + + return $properties; + } + + /** + * @param bool $returnScannerProperty + * @return array + */ + public function getProperties($returnScannerProperty = false) + { + $properties = $this->classScanner->getProperties($returnScannerProperty); + foreach ($this->parentClassScanners as $pClassScanner) { + $properties = array_merge($properties, $pClassScanner->getProperties($returnScannerProperty)); + } + + return $properties; + } + + /** + * Return a single property by given name or index of info + * + * @param string|int $propertyNameOrInfoIndex + * @throws Exception\InvalidArgumentException + * @return bool|PropertyScanner + */ + public function getProperty($propertyNameOrInfoIndex) + { + if ($this->classScanner->hasProperty($propertyNameOrInfoIndex)) { + return $this->classScanner->getProperty($propertyNameOrInfoIndex); + } + + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasProperty($propertyNameOrInfoIndex)) { + return $pClassScanner->getProperty($propertyNameOrInfoIndex); + } + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Property %s not found in %s', + $propertyNameOrInfoIndex, + $this->classScanner->getName() + )); + } + + /** + * Verify if class or parent class has property + * + * @param string $name + * @return bool + */ + public function hasProperty($name) + { + if ($this->classScanner->hasProperty($name)) { + return true; + } + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasProperty($name)) { + return true; + } + } + + return false; + } + + /** + * @return array + */ + public function getMethodNames() + { + $methods = $this->classScanner->getMethodNames(); + foreach ($this->parentClassScanners as $pClassScanner) { + $methods = array_merge($methods, $pClassScanner->getMethodNames()); + } + + return $methods; + } + + /** + * @return MethodScanner[] + */ + public function getMethods() + { + $methods = $this->classScanner->getMethods(); + foreach ($this->parentClassScanners as $pClassScanner) { + $methods = array_merge($methods, $pClassScanner->getMethods()); + } + + return $methods; + } + + /** + * @param int|string $methodNameOrInfoIndex + * @return MethodScanner + * @throws Exception\InvalidArgumentException + */ + public function getMethod($methodNameOrInfoIndex) + { + if ($this->classScanner->hasMethod($methodNameOrInfoIndex)) { + return $this->classScanner->getMethod($methodNameOrInfoIndex); + } + + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasMethod($methodNameOrInfoIndex)) { + return $pClassScanner->getMethod($methodNameOrInfoIndex); + } + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Method %s not found in %s', + $methodNameOrInfoIndex, + $this->classScanner->getName() + )); + } + + /** + * Verify if class or parent class has method by given name + * + * @param string $name + * @return bool + */ + public function hasMethod($name) + { + if ($this->classScanner->hasMethod($name)) { + return true; + } + foreach ($this->parentClassScanners as $pClassScanner) { + if ($pClassScanner->hasMethod($name)) { + return true; + } + } + + return false; + } +} diff --git a/zendframework/zend-code/src/Scanner/DirectoryScanner.php b/zendframework/zend-code/src/Scanner/DirectoryScanner.php new file mode 100644 index 000000000..e561be0d9 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/DirectoryScanner.php @@ -0,0 +1,272 @@ +addDirectory($directory); + } elseif (is_array($directory)) { + foreach ($directory as $d) { + $this->addDirectory($d); + } + } + } + } + + /** + * @param DirectoryScanner|string $directory + * @return void + * @throws Exception\InvalidArgumentException + */ + public function addDirectory($directory) + { + if ($directory instanceof DirectoryScanner) { + $this->directories[] = $directory; + } elseif (is_string($directory)) { + $realDir = realpath($directory); + if (!$realDir || !is_dir($realDir)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Directory "%s" does not exist', + $realDir + )); + } + $this->directories[] = $realDir; + } else { + throw new Exception\InvalidArgumentException( + 'The argument provided was neither a DirectoryScanner or directory path' + ); + } + } + + /** + * @param DirectoryScanner $directoryScanner + * @return void + */ + public function addDirectoryScanner(DirectoryScanner $directoryScanner) + { + $this->addDirectory($directoryScanner); + } + + /** + * @param FileScanner $fileScanner + * @return void + */ + public function addFileScanner(FileScanner $fileScanner) + { + $this->fileScanners[] = $fileScanner; + } + + /** + * @return void + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + // iterate directories creating file scanners + foreach ($this->directories as $directory) { + if ($directory instanceof DirectoryScanner) { + $directory->scan(); + if ($directory->fileScanners) { + $this->fileScanners = array_merge($this->fileScanners, $directory->fileScanners); + } + } else { + $rdi = new RecursiveDirectoryIterator($directory); + foreach (new RecursiveIteratorIterator($rdi) as $item) { + if ($item->isFile() && pathinfo($item->getRealPath(), PATHINFO_EXTENSION) == 'php') { + $this->fileScanners[] = new FileScanner($item->getRealPath()); + } + } + } + } + + $this->isScanned = true; + } + + /** + * @todo implement method + */ + public function getNamespaces() + { + // @todo + } + + /** + * @param bool $returnFileScanners + * @return array + */ + public function getFiles($returnFileScanners = false) + { + $this->scan(); + + $return = []; + foreach ($this->fileScanners as $fileScanner) { + $return[] = ($returnFileScanners) ? $fileScanner : $fileScanner->getFile(); + } + + return $return; + } + + /** + * @return array + */ + public function getClassNames() + { + $this->scan(); + + if ($this->classToFileScanner === null) { + $this->createClassToFileScannerCache(); + } + + return array_keys($this->classToFileScanner); + } + + /** + * @param bool $returnDerivedScannerClass + * @return array + */ + public function getClasses($returnDerivedScannerClass = false) + { + $this->scan(); + + if ($this->classToFileScanner === null) { + $this->createClassToFileScannerCache(); + } + + $returnClasses = []; + foreach ($this->classToFileScanner as $className => $fsIndex) { + $classScanner = $this->fileScanners[$fsIndex]->getClass($className); + if ($returnDerivedScannerClass) { + $classScanner = new DerivedClassScanner($classScanner, $this); + } + $returnClasses[] = $classScanner; + } + + return $returnClasses; + } + + /** + * @param string $class + * @return bool + */ + public function hasClass($class) + { + $this->scan(); + + if ($this->classToFileScanner === null) { + $this->createClassToFileScannerCache(); + } + + return (isset($this->classToFileScanner[$class])); + } + + /** + * @param string $class + * @param bool $returnDerivedScannerClass + * @return ClassScanner|DerivedClassScanner + * @throws Exception\InvalidArgumentException + */ + public function getClass($class, $returnDerivedScannerClass = false) + { + $this->scan(); + + if ($this->classToFileScanner === null) { + $this->createClassToFileScannerCache(); + } + + if (!isset($this->classToFileScanner[$class])) { + throw new Exception\InvalidArgumentException('Class not found.'); + } + + /** @var FileScanner $fs */ + $fs = $this->fileScanners[$this->classToFileScanner[$class]]; + $returnClass = $fs->getClass($class); + + if (($returnClass instanceof ClassScanner) && $returnDerivedScannerClass) { + return new DerivedClassScanner($returnClass, $this); + } + + return $returnClass; + } + + /** + * Create class to file scanner cache + * + * @return void + */ + protected function createClassToFileScannerCache() + { + if ($this->classToFileScanner !== null) { + return; + } + + $this->classToFileScanner = []; + /** @var FileScanner $fileScanner */ + foreach ($this->fileScanners as $fsIndex => $fileScanner) { + $fsClasses = $fileScanner->getClassNames(); + foreach ($fsClasses as $fsClassName) { + $this->classToFileScanner[$fsClassName] = $fsIndex; + } + } + } + + /** + * Export + * + * @todo implement method + */ + public static function export() + { + // @todo + } + + /** + * __ToString + * + * @todo implement method + */ + public function __toString() + { + // @todo + } +} diff --git a/zendframework/zend-code/src/Scanner/DocBlockScanner.php b/zendframework/zend-code/src/Scanner/DocBlockScanner.php new file mode 100644 index 000000000..ba63f96c6 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/DocBlockScanner.php @@ -0,0 +1,326 @@ +docComment = $docComment; + $this->nameInformation = $nameInformation; + } + + /** + * @return string + */ + public function getShortDescription() + { + $this->scan(); + + return $this->shortDescription; + } + + /** + * @return string + */ + public function getLongDescription() + { + $this->scan(); + + return $this->longDescription; + } + + /** + * @return array + */ + public function getTags() + { + $this->scan(); + + return $this->tags; + } + + /** + * @return array + */ + public function getAnnotations() + { + $this->scan(); + + return $this->annotations; + } + + /** + * @return void + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + $mode = 1; + + $tokens = $this->tokenize(); + $tagIndex = null; + reset($tokens); + + SCANNER_TOP: + $token = current($tokens); + + switch ($token[0]) { + case 'DOCBLOCK_NEWLINE': + if ($this->shortDescription != '' && $tagIndex === null) { + $mode = 2; + } else { + $this->longDescription .= $token[1]; + } + goto SCANNER_CONTINUE; + //goto no break needed + + case 'DOCBLOCK_WHITESPACE': + case 'DOCBLOCK_TEXT': + if ($tagIndex !== null) { + $this->tags[$tagIndex]['value'] .= ($this->tags[$tagIndex]['value'] == '') ? $token[1] : ' ' . $token[1]; + goto SCANNER_CONTINUE; + } elseif ($mode <= 2) { + if ($mode == 1) { + $this->shortDescription .= $token[1]; + } else { + $this->longDescription .= $token[1]; + } + goto SCANNER_CONTINUE; + } + //gotos no break needed + case 'DOCBLOCK_TAG': + array_push($this->tags, ['name' => $token[1], + 'value' => '']); + end($this->tags); + $tagIndex = key($this->tags); + $mode = 3; + goto SCANNER_CONTINUE; + //goto no break needed + + case 'DOCBLOCK_COMMENTEND': + goto SCANNER_END; + + } + + SCANNER_CONTINUE: + if (next($tokens) === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + $this->shortDescription = trim($this->shortDescription); + $this->longDescription = trim($this->longDescription); + $this->isScanned = true; + } + + /** + * @return array + */ + protected function tokenize() + { + static $CONTEXT_INSIDE_DOCBLOCK = 0x01; + static $CONTEXT_INSIDE_ASTERISK = 0x02; + + $context = 0x00; + $stream = $this->docComment; + $streamIndex = null; + $tokens = []; + $tokenIndex = null; + $currentChar = null; + $currentWord = null; + $currentLine = null; + + $MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (&$stream, &$streamIndex, &$currentChar, &$currentWord, &$currentLine) { + $positionsForward = ($positionsForward > 0) ? $positionsForward : 1; + $streamIndex = ($streamIndex === null) ? 0 : $streamIndex + $positionsForward; + if (!isset($stream[$streamIndex])) { + $currentChar = false; + + return false; + } + $currentChar = $stream[$streamIndex]; + $matches = []; + $currentLine = (preg_match('#(.*?)\r?\n#', $stream, $matches, null, $streamIndex) === 1) ? $matches[1] : substr($stream, $streamIndex); + if ($currentChar === ' ') { + $currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine; + } else { + $currentWord = (($matches = strpos($currentLine, ' ')) !== false) ? substr($currentLine, 0, $matches) : $currentLine; + } + + return $currentChar; + }; + $MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) { + return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord)); + }; + $MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) { + return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine)); + }; + $MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) { + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + $tokens[$tokenIndex] = ['DOCBLOCK_UNKNOWN', '']; + }; + $MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) { + $tokens[$tokenIndex][0] = $type; + }; + $MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentChar; + }; + $MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentWord; + }; + $MACRO_TOKEN_APPEND_WORD_PARTIAL = function ($length) use (&$currentWord, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= substr($currentWord, 0, $length); + }; + $MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) { + $tokens[$tokenIndex][1] .= $currentLine; + }; + + $MACRO_STREAM_ADVANCE_CHAR(); + $MACRO_TOKEN_ADVANCE(); + + TOKENIZER_TOP: + + if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTSTART'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + $context |= $CONTEXT_INSIDE_DOCBLOCK; + $context |= $CONTEXT_INSIDE_ASTERISK; + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($context & $CONTEXT_INSIDE_DOCBLOCK && $currentWord === '*/') { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTEND'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + $context &= ~$CONTEXT_INSIDE_DOCBLOCK; + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === ' ' || $currentChar === "\t") { + $MACRO_TOKEN_SET_TYPE(($context & $CONTEXT_INSIDE_ASTERISK) ? 'DOCBLOCK_WHITESPACE' : 'DOCBLOCK_WHITESPACE_INDENT'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === '*') { + if (($context & $CONTEXT_INSIDE_DOCBLOCK) && ($context & $CONTEXT_INSIDE_ASTERISK)) { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT'); + } else { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_ASTERISK'); + $context |= $CONTEXT_INSIDE_ASTERISK; + } + $MACRO_TOKEN_APPEND_CHAR(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === '@') { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TAG'); + $MACRO_TOKEN_APPEND_WORD(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_WORD() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + if ($currentChar === "\n") { + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_NEWLINE'); + $MACRO_TOKEN_APPEND_CHAR(); + $MACRO_TOKEN_ADVANCE(); + $context &= ~$CONTEXT_INSIDE_ASTERISK; + if ($MACRO_STREAM_ADVANCE_CHAR() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + } + + $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT'); + $MACRO_TOKEN_APPEND_LINE(); + $MACRO_TOKEN_ADVANCE(); + if ($MACRO_STREAM_ADVANCE_LINE() === false) { + goto TOKENIZER_END; + } + goto TOKENIZER_TOP; + + TOKENIZER_END: + + array_pop($tokens); + + return $tokens; + } +} diff --git a/zendframework/zend-code/src/Scanner/FileScanner.php b/zendframework/zend-code/src/Scanner/FileScanner.php new file mode 100644 index 000000000..fed937006 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/FileScanner.php @@ -0,0 +1,46 @@ +file = $file; + if (!file_exists($file)) { + throw new Exception\InvalidArgumentException(sprintf( + 'File "%s" not found', + $file + )); + } + parent::__construct(token_get_all(file_get_contents($file)), $annotationManager); + } + + /** + * @return string + */ + public function getFile() + { + return $this->file; + } +} diff --git a/zendframework/zend-code/src/Scanner/FunctionScanner.php b/zendframework/zend-code/src/Scanner/FunctionScanner.php new file mode 100644 index 000000000..ef9176349 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/FunctionScanner.php @@ -0,0 +1,16 @@ +tokens = $methodTokens; + $this->nameInformation = $nameInformation; + } + + /** + * @param string $class + * @return MethodScanner + */ + public function setClass($class) + { + $this->class = (string) $class; + return $this; + } + + /** + * @param ClassScanner $scannerClass + * @return MethodScanner + */ + public function setScannerClass(ClassScanner $scannerClass) + { + $this->scannerClass = $scannerClass; + return $this; + } + + /** + * @return MethodScanner + */ + public function getClassScanner() + { + return $this->scannerClass; + } + + /** + * @return string + */ + public function getName() + { + $this->scan(); + + return $this->name; + } + + /** + * @return int + */ + public function getLineStart() + { + $this->scan(); + + return $this->lineStart; + } + + /** + * @return int + */ + public function getLineEnd() + { + $this->scan(); + + return $this->lineEnd; + } + + /** + * @return string + */ + public function getDocComment() + { + $this->scan(); + + return $this->docComment; + } + + /** + * @param AnnotationManager $annotationManager + * @return AnnotationScanner + */ + public function getAnnotations(AnnotationManager $annotationManager) + { + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation); + } + + /** + * @return bool + */ + public function isFinal() + { + $this->scan(); + + return $this->isFinal; + } + + /** + * @return bool + */ + public function isAbstract() + { + $this->scan(); + + return $this->isAbstract; + } + + /** + * @return bool + */ + public function isPublic() + { + $this->scan(); + + return $this->isPublic; + } + + /** + * @return bool + */ + public function isProtected() + { + $this->scan(); + + return $this->isProtected; + } + + /** + * @return bool + */ + public function isPrivate() + { + $this->scan(); + + return $this->isPrivate; + } + + /** + * @return bool + */ + public function isStatic() + { + $this->scan(); + + return $this->isStatic; + } + + /** + * Override the given name for a method, this is necessary to + * support traits. + * + * @param $name + * @return self + */ + public function setName($name) + { + $this->name = $name; + return $this; + } + + /** + * Visibility must be of T_PUBLIC, T_PRIVATE or T_PROTECTED + * Needed to support traits + * + * @param $visibility T_PUBLIC | T_PRIVATE | T_PROTECTED + * @return self + * @throws \Zend\Code\Exception + */ + public function setVisibility($visibility) + { + switch (strtolower($visibility)) { + case T_PUBLIC: + $this->isPublic = true; + $this->isPrivate = false; + $this->isProtected = false; + break; + + case T_PRIVATE: + $this->isPublic = false; + $this->isPrivate = true; + $this->isProtected = false; + break; + + case T_PROTECTED: + $this->isPublic = false; + $this->isPrivate = false; + $this->isProtected = true; + break; + + default: + throw new Exception("Invalid visibility argument passed to setVisibility."); + } + + return $this; + } + + /** + * @return int + */ + public function getNumberOfParameters() + { + return count($this->getParameters()); + } + + /** + * @param bool $returnScanner + * @return array + */ + public function getParameters($returnScanner = false) + { + $this->scan(); + + $return = []; + + foreach ($this->infos as $info) { + if ($info['type'] != 'parameter') { + continue; + } + + if (!$returnScanner) { + $return[] = $info['name']; + } else { + $return[] = $this->getParameter($info['name']); + } + } + + return $return; + } + + /** + * @param int|string $parameterNameOrInfoIndex + * @return ParameterScanner + * @throws Exception\InvalidArgumentException + */ + public function getParameter($parameterNameOrInfoIndex) + { + $this->scan(); + + if (is_int($parameterNameOrInfoIndex)) { + $info = $this->infos[$parameterNameOrInfoIndex]; + if ($info['type'] != 'parameter') { + throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter'); + } + } elseif (is_string($parameterNameOrInfoIndex)) { + foreach ($this->infos as $info) { + if ($info['type'] === 'parameter' && $info['name'] === $parameterNameOrInfoIndex) { + break; + } + unset($info); + } + if (!isset($info)) { + throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter'); + } + } + + $p = new ParameterScanner( + array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart']), + $this->nameInformation + ); + $p->setDeclaringFunction($this->name); + $p->setDeclaringScannerFunction($this); + $p->setDeclaringClass($this->class); + $p->setDeclaringScannerClass($this->scannerClass); + $p->setPosition($info['position']); + + return $p; + } + + /** + * @return string + */ + public function getBody() + { + $this->scan(); + + return $this->body; + } + + public static function export() + { + // @todo + } + + public function __toString() + { + $this->scan(); + + return var_export($this, true); + } + + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new Exception\RuntimeException('No tokens were provided'); + } + + /** + * Variables & Setup + */ + + $tokens = &$this->tokens; // localize + $infos = &$this->infos; // localize + $tokenIndex = null; + $token = null; + $tokenType = null; + $tokenContent = null; + $tokenLine = null; + $infoIndex = 0; + $parentCount = 0; + + /* + * MACRO creation + */ + $MACRO_TOKEN_ADVANCE = function () use ( + &$tokens, + &$tokenIndex, + &$token, + &$tokenType, + &$tokenContent, + &$tokenLine + ) { + static $lastTokenArray = null; + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + if (!isset($tokens[$tokenIndex])) { + $token = false; + $tokenContent = false; + $tokenType = false; + $tokenLine = false; + + return false; + } + $token = $tokens[$tokenIndex]; + if (is_string($token)) { + $tokenType = null; + $tokenContent = $token; + $tokenLine = $tokenLine + substr_count( + $lastTokenArray[1], + "\n" + ); // adjust token line by last known newline count + } else { + list($tokenType, $tokenContent, $tokenLine) = $token; + } + + return $tokenIndex; + }; + $MACRO_INFO_START = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { + $infos[$infoIndex] = [ + 'type' => 'parameter', + 'tokenStart' => $tokenIndex, + 'tokenEnd' => null, + 'lineStart' => $tokenLine, + 'lineEnd' => $tokenLine, + 'name' => null, + 'position' => $infoIndex + 1, // position is +1 of infoIndex + ]; + }; + $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { + $infos[$infoIndex]['tokenEnd'] = $tokenIndex; + $infos[$infoIndex]['lineEnd'] = $tokenLine; + $infoIndex++; + + return $infoIndex; + }; + + /** + * START FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + // Initialize token + $MACRO_TOKEN_ADVANCE(); + + SCANNER_TOP: + + $this->lineStart = ($this->lineStart) ? : $tokenLine; + + switch ($tokenType) { + case T_DOC_COMMENT: + $this->lineStart = null; + if ($this->docComment === null && $this->name === null) { + $this->docComment = $tokenContent; + } + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_FINAL: + $this->isFinal = true; + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_ABSTRACT: + $this->isAbstract = true; + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_PUBLIC: + // use defaults + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_PROTECTED: + $this->setVisibility(T_PROTECTED); + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_PRIVATE: + $this->setVisibility(T_PRIVATE); + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_STATIC: + $this->isStatic = true; + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case T_VARIABLE: + case T_STRING: + + if ($tokenType === T_STRING && $parentCount === 0) { + $this->name = $tokenContent; + } + + if ($parentCount === 1) { + if (!isset($infos[$infoIndex])) { + $MACRO_INFO_START(); + } + if ($tokenType === T_VARIABLE) { + $infos[$infoIndex]['name'] = ltrim($tokenContent, '$'); + } + } + + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + + case null: + + switch ($tokenContent) { + case '&': + if (!isset($infos[$infoIndex])) { + $MACRO_INFO_START(); + } + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + case '(': + $parentCount++; + goto SCANNER_CONTINUE_SIGNATURE; + //goto (no break needed); + case ')': + $parentCount--; + if ($parentCount > 0) { + goto SCANNER_CONTINUE_SIGNATURE; + } + if ($parentCount === 0) { + if ($infos) { + $MACRO_INFO_ADVANCE(); + } + $context = 'body'; + } + goto SCANNER_CONTINUE_BODY; + //goto (no break needed); + case ',': + if ($parentCount === 1) { + $MACRO_INFO_ADVANCE(); + } + goto SCANNER_CONTINUE_SIGNATURE; + } + } + + SCANNER_CONTINUE_SIGNATURE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_CONTINUE_BODY: + + $braceCount = 0; + while ($MACRO_TOKEN_ADVANCE() !== false) { + if ($tokenContent == '}') { + $braceCount--; + } + if ($braceCount > 0) { + $this->body .= $tokenContent; + } + if ($tokenContent == '{') { + $braceCount++; + } + $this->lineEnd = $tokenLine; + } + + SCANNER_END: + + $this->isScanned = true; + + return; + } +} diff --git a/zendframework/zend-code/src/Scanner/ParameterScanner.php b/zendframework/zend-code/src/Scanner/ParameterScanner.php new file mode 100644 index 000000000..573247cf5 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/ParameterScanner.php @@ -0,0 +1,352 @@ +tokens = $parameterTokens; + $this->nameInformation = $nameInformation; + } + + /** + * Set declaring class + * + * @param string $class + * @return void + */ + public function setDeclaringClass($class) + { + $this->declaringClass = (string) $class; + } + + /** + * Set declaring scanner class + * + * @param ClassScanner $scannerClass + * @return void + */ + public function setDeclaringScannerClass(ClassScanner $scannerClass) + { + $this->declaringScannerClass = $scannerClass; + } + + /** + * Set declaring function + * + * @param string $function + * @return void + */ + public function setDeclaringFunction($function) + { + $this->declaringFunction = $function; + } + + /** + * Set declaring scanner function + * + * @param MethodScanner $scannerFunction + * @return void + */ + public function setDeclaringScannerFunction(MethodScanner $scannerFunction) + { + $this->declaringScannerFunction = $scannerFunction; + } + + /** + * Set position + * + * @param int $position + * @return void + */ + public function setPosition($position) + { + $this->position = $position; + } + + /** + * Scan + * + * @return void + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + $tokens = &$this->tokens; + + reset($tokens); + + SCANNER_TOP: + + $token = current($tokens); + + if (is_string($token)) { + // check pass by ref + if ($token === '&') { + $this->isPassedByReference = true; + goto SCANNER_CONTINUE; + } + if ($token === '=') { + $this->isOptional = true; + $this->isDefaultValueAvailable = true; + goto SCANNER_CONTINUE; + } + } else { + if ($this->name === null && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) { + $this->class .= $token[1]; + goto SCANNER_CONTINUE; + } + if ($token[0] === T_VARIABLE) { + $this->name = ltrim($token[1], '$'); + goto SCANNER_CONTINUE; + } + } + + if ($this->name !== null) { + $this->defaultValue .= trim((is_string($token)) ? $token : $token[1]); + } + + SCANNER_CONTINUE: + + if (next($this->tokens) === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + if ($this->class && $this->nameInformation) { + $this->class = $this->nameInformation->resolveName($this->class); + } + + $this->isScanned = true; + } + + /** + * Get declaring scanner class + * + * @return ClassScanner + */ + public function getDeclaringScannerClass() + { + return $this->declaringScannerClass; + } + + /** + * Get declaring class + * + * @return string + */ + public function getDeclaringClass() + { + return $this->declaringClass; + } + + /** + * Get declaring scanner function + * + * @return MethodScanner + */ + public function getDeclaringScannerFunction() + { + return $this->declaringScannerFunction; + } + + /** + * Get declaring function + * + * @return string + */ + public function getDeclaringFunction() + { + return $this->declaringFunction; + } + + /** + * Get default value + * + * @return string + */ + public function getDefaultValue() + { + $this->scan(); + + return $this->defaultValue; + } + + /** + * Get class + * + * @return string + */ + public function getClass() + { + $this->scan(); + + return $this->class; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + $this->scan(); + + return $this->name; + } + + /** + * Get position + * + * @return int + */ + public function getPosition() + { + $this->scan(); + + return $this->position; + } + + /** + * Check if is array + * + * @return bool + */ + public function isArray() + { + $this->scan(); + + return $this->isArray; + } + + /** + * Check if default value is available + * + * @return bool + */ + public function isDefaultValueAvailable() + { + $this->scan(); + + return $this->isDefaultValueAvailable; + } + + /** + * Check if is optional + * + * @return bool + */ + public function isOptional() + { + $this->scan(); + + return $this->isOptional; + } + + /** + * Check if is passed by reference + * + * @return bool + */ + public function isPassedByReference() + { + $this->scan(); + + return $this->isPassedByReference; + } +} diff --git a/zendframework/zend-code/src/Scanner/PropertyScanner.php b/zendframework/zend-code/src/Scanner/PropertyScanner.php new file mode 100644 index 000000000..5920196cc --- /dev/null +++ b/zendframework/zend-code/src/Scanner/PropertyScanner.php @@ -0,0 +1,317 @@ +tokens = $propertyTokens; + $this->nameInformation = $nameInformation; + } + + /** + * @param string $class + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * @param ClassScanner $scannerClass + */ + public function setScannerClass(ClassScanner $scannerClass) + { + $this->scannerClass = $scannerClass; + } + + /** + * @return ClassScanner + */ + public function getClassScanner() + { + return $this->scannerClass; + } + + /** + * @return string + */ + public function getName() + { + $this->scan(); + return $this->name; + } + + /** + * @return string + */ + public function getValueType() + { + $this->scan(); + return $this->valueType; + } + + /** + * @return bool + */ + public function isPublic() + { + $this->scan(); + return $this->isPublic; + } + + /** + * @return bool + */ + public function isPrivate() + { + $this->scan(); + return $this->isPrivate; + } + + /** + * @return bool + */ + public function isProtected() + { + $this->scan(); + return $this->isProtected; + } + + /** + * @return bool + */ + public function isStatic() + { + $this->scan(); + return $this->isStatic; + } + + /** + * @return string + */ + public function getValue() + { + $this->scan(); + return $this->value; + } + + /** + * @return string + */ + public function getDocComment() + { + $this->scan(); + return $this->docComment; + } + + /** + * @param Annotation\AnnotationManager $annotationManager + * @return AnnotationScanner + */ + public function getAnnotations(Annotation\AnnotationManager $annotationManager) + { + if (($docComment = $this->getDocComment()) == '') { + return false; + } + + return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation); + } + + /** + * @return string + */ + public function __toString() + { + $this->scan(); + return var_export($this, true); + } + + /** + * Scan tokens + * + * @throws \Zend\Code\Exception\RuntimeException + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new Exception\RuntimeException('No tokens were provided'); + } + + /** + * Variables & Setup + */ + $value = ''; + $concatenateValue = false; + + $tokens = &$this->tokens; + reset($tokens); + + foreach ($tokens as $token) { + $tempValue = $token; + if (!is_string($token)) { + list($tokenType, $tokenContent, $tokenLine) = $token; + + switch ($tokenType) { + case T_DOC_COMMENT: + if ($this->docComment === null && $this->name === null) { + $this->docComment = $tokenContent; + } + break; + + case T_VARIABLE: + $this->name = ltrim($tokenContent, '$'); + break; + + case T_PUBLIC: + // use defaults + break; + + case T_PROTECTED: + $this->isProtected = true; + $this->isPublic = false; + break; + + case T_PRIVATE: + $this->isPrivate = true; + $this->isPublic = false; + break; + + case T_STATIC: + $this->isStatic = true; + break; + default: + $tempValue = trim($tokenContent); + break; + } + } + + //end value concatenation + if (!is_array($token) && trim($token) == ";") { + $concatenateValue = false; + } + + if (true === $concatenateValue) { + $value .= $tempValue; + } + + //start value concatenation + if (!is_array($token) && trim($token) == "=") { + $concatenateValue = true; + } + } + + $this->valueType = self::T_UNKNOWN; + if ($value == "false" || $value == "true") { + $this->valueType = self::T_BOOLEAN; + } elseif (is_numeric($value)) { + $this->valueType = self::T_INTEGER; + } elseif (0 === strpos($value, 'array') || 0 === strpos($value, "[")) { + $this->valueType = self::T_ARRAY; + } elseif (substr($value, 0, 1) === '"' || substr($value, 0, 1) === "'") { + $value = substr($value, 1, -1); // Remove quotes + $this->valueType = self::T_STRING; + } + + $this->value = empty($value) ? null : $value; + $this->isScanned = true; + } +} diff --git a/zendframework/zend-code/src/Scanner/ScannerInterface.php b/zendframework/zend-code/src/Scanner/ScannerInterface.php new file mode 100644 index 000000000..cb7d0ea4a --- /dev/null +++ b/zendframework/zend-code/src/Scanner/ScannerInterface.php @@ -0,0 +1,16 @@ +tokens = $tokens; + $this->annotationManager = $annotationManager; + } + + /** + * @return AnnotationManager + */ + public function getAnnotationManager() + { + return $this->annotationManager; + } + + /** + * Get doc comment + * + * @todo Assignment of $this->docComment should probably be done in scan() + * and then $this->getDocComment() just retrieves it. + * + * @return string + */ + public function getDocComment() + { + foreach ($this->tokens as $token) { + $type = $token[0]; + $value = $token[1]; + if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) { + continue; + } elseif ($type == T_DOC_COMMENT) { + $this->docComment = $value; + + return $this->docComment; + } else { + // Only whitespace is allowed before file docblocks + return; + } + } + } + + /** + * @return array + */ + public function getNamespaces() + { + $this->scan(); + + $namespaces = []; + foreach ($this->infos as $info) { + if ($info['type'] == 'namespace') { + $namespaces[] = $info['namespace']; + } + } + + return $namespaces; + } + + /** + * @param null|string $namespace + * @return array|null + */ + public function getUses($namespace = null) + { + $this->scan(); + + return $this->getUsesNoScan($namespace); + } + + /** + * @return array + */ + public function getIncludes() + { + $this->scan(); + // @todo Implement getIncludes() in TokenArrayScanner + } + + /** + * @return array + */ + public function getClassNames() + { + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'class') { + continue; + } + + $return[] = $info['name']; + } + + return $return; + } + + /** + * @return ClassScanner[] + */ + public function getClasses() + { + $this->scan(); + + $return = []; + foreach ($this->infos as $info) { + if ($info['type'] != 'class') { + continue; + } + + $return[] = $this->getClass($info['name']); + } + + return $return; + } + + /** + * Return the class object from this scanner + * + * @param string|int $name + * @throws Exception\InvalidArgumentException + * @return ClassScanner + */ + public function getClass($name) + { + $this->scan(); + + if (is_int($name)) { + $info = $this->infos[$name]; + if ($info['type'] != 'class') { + throw new Exception\InvalidArgumentException('Index of info offset is not about a class'); + } + } elseif (is_string($name)) { + $classFound = false; + foreach ($this->infos as $info) { + if ($info['type'] === 'class' && $info['name'] === $name) { + $classFound = true; + break; + } + } + + if (!$classFound) { + return false; + } + } + + return new ClassScanner( + array_slice( + $this->tokens, + $info['tokenStart'], + ($info['tokenEnd'] - $info['tokenStart'] + 1) + ), // zero indexed array + new NameInformation($info['namespace'], $info['uses']) + ); + } + + /** + * @param string $className + * @return bool|null|NameInformation + */ + public function getClassNameInformation($className) + { + $this->scan(); + + $classFound = false; + foreach ($this->infos as $info) { + if ($info['type'] === 'class' && $info['name'] === $className) { + $classFound = true; + break; + } + } + + if (!$classFound) { + return false; + } + + if (!isset($info)) { + return; + } + + return new NameInformation($info['namespace'], $info['uses']); + } + + /** + * @return array + */ + public function getFunctionNames() + { + $this->scan(); + $functionNames = []; + foreach ($this->infos as $info) { + if ($info['type'] == 'function') { + $functionNames[] = $info['name']; + } + } + + return $functionNames; + } + + /** + * @return array + */ + public function getFunctions() + { + $this->scan(); + + $functions = []; + foreach ($this->infos as $info) { + if ($info['type'] == 'function') { + // @todo $functions[] = new FunctionScanner($info['name']); + } + } + + return $functions; + } + + /** + * Export + * + * @param $tokens + */ + public static function export($tokens) + { + // @todo + } + + public function __toString() + { + // @todo + } + + /** + * Scan + * + * @todo: $this->docComment should be assigned for valid docblock during + * the scan instead of $this->getDocComment() (starting with + * T_DOC_COMMENT case) + * + * @throws Exception\RuntimeException + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new Exception\RuntimeException('No tokens were provided'); + } + + /** + * Variables & Setup + */ + + $tokens = &$this->tokens; // localize + $infos = &$this->infos; // localize + $tokenIndex = null; + $token = null; + $tokenType = null; + $tokenContent = null; + $tokenLine = null; + $namespace = null; + $docCommentIndex = false; + $infoIndex = 0; + + /* + * MACRO creation + */ + $MACRO_TOKEN_ADVANCE = function () use (&$tokens, &$tokenIndex, &$token, &$tokenType, &$tokenContent, &$tokenLine) { + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + if (!isset($tokens[$tokenIndex])) { + $token = false; + $tokenContent = false; + $tokenType = false; + $tokenLine = false; + + return false; + } + if (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"') { + do { + $tokenIndex++; + } while (!(is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"')); + } + $token = $tokens[$tokenIndex]; + if (is_array($token)) { + list($tokenType, $tokenContent, $tokenLine) = $token; + } else { + $tokenType = null; + $tokenContent = $token; + } + + return $tokenIndex; + }; + $MACRO_TOKEN_LOGICAL_START_INDEX = function () use (&$tokenIndex, &$docCommentIndex) { + return ($docCommentIndex === false) ? $tokenIndex : $docCommentIndex; + }; + $MACRO_DOC_COMMENT_START = function () use (&$tokenIndex, &$docCommentIndex) { + $docCommentIndex = $tokenIndex; + + return $docCommentIndex; + }; + $MACRO_DOC_COMMENT_VALIDATE = function () use (&$tokenType, &$docCommentIndex) { + static $validTrailingTokens = null; + if ($validTrailingTokens === null) { + $validTrailingTokens = [T_WHITESPACE, T_FINAL, T_ABSTRACT, T_INTERFACE, T_CLASS, T_FUNCTION]; + } + if ($docCommentIndex !== false && !in_array($tokenType, $validTrailingTokens)) { + $docCommentIndex = false; + } + + return $docCommentIndex; + }; + $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { + $infos[$infoIndex]['tokenEnd'] = $tokenIndex; + $infos[$infoIndex]['lineEnd'] = $tokenLine; + $infoIndex++; + + return $infoIndex; + }; + + /** + * START FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + // Initialize token + $MACRO_TOKEN_ADVANCE(); + + SCANNER_TOP: + + if ($token === false) { + goto SCANNER_END; + } + + // Validate current doc comment index + $MACRO_DOC_COMMENT_VALIDATE(); + + switch ($tokenType) { + + case T_DOC_COMMENT: + + $MACRO_DOC_COMMENT_START(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_NAMESPACE: + + $infos[$infoIndex] = [ + 'type' => 'namespace', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $token[2], + 'lineEnd' => null, + 'namespace' => null, + ]; + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_NAMESPACE_TOP: + + if ($tokenType === null && $tokenContent === ';' || $tokenContent === '{') { + goto SCANNER_NAMESPACE_END; + } + + if ($tokenType === T_WHITESPACE) { + goto SCANNER_NAMESPACE_CONTINUE; + } + + if ($tokenType === T_NS_SEPARATOR || $tokenType === T_STRING) { + $infos[$infoIndex]['namespace'] .= $tokenContent; + } + + SCANNER_NAMESPACE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_NAMESPACE_TOP; + + SCANNER_NAMESPACE_END: + + $namespace = $infos[$infoIndex]['namespace']; + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_USE: + + $infos[$infoIndex] = [ + 'type' => 'use', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'namespace' => $namespace, + 'statements' => [0 => ['use' => null, + 'as' => null]], + ]; + + $useStatementIndex = 0; + $useAsContext = false; + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_USE_TOP: + + if ($tokenType === null) { + if ($tokenContent === ';') { + goto SCANNER_USE_END; + } elseif ($tokenContent === ',') { + $useAsContext = false; + $useStatementIndex++; + $infos[$infoIndex]['statements'][$useStatementIndex] = ['use' => null, + 'as' => null]; + } + } + + // ANALYZE + if ($tokenType !== null) { + if ($tokenType == T_AS) { + $useAsContext = true; + goto SCANNER_USE_CONTINUE; + } + + if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) { + if ($useAsContext == false) { + $infos[$infoIndex]['statements'][$useStatementIndex]['use'] .= $tokenContent; + } else { + $infos[$infoIndex]['statements'][$useStatementIndex]['as'] = $tokenContent; + } + } + } + + SCANNER_USE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_USE_TOP; + + SCANNER_USE_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_INCLUDE: + case T_INCLUDE_ONCE: + case T_REQUIRE: + case T_REQUIRE_ONCE: + + // Static for performance + static $includeTypes = [ + T_INCLUDE => 'include', + T_INCLUDE_ONCE => 'include_once', + T_REQUIRE => 'require', + T_REQUIRE_ONCE => 'require_once' + ]; + + $infos[$infoIndex] = [ + 'type' => 'include', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'includeType' => $includeTypes[$tokens[$tokenIndex][0]], + 'path' => '', + ]; + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_INCLUDE_TOP: + + if ($tokenType === null && $tokenContent === ';') { + goto SCANNER_INCLUDE_END; + } + + $infos[$infoIndex]['path'] .= $tokenContent; + + SCANNER_INCLUDE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_INCLUDE_TOP; + + SCANNER_INCLUDE_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_FUNCTION: + case T_FINAL: + case T_ABSTRACT: + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + + $infos[$infoIndex] = [ + 'type' => ($tokenType === T_FUNCTION) ? 'function' : 'class', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'namespace' => $namespace, + 'uses' => $this->getUsesNoScan($namespace), + 'name' => null, + 'shortName' => null, + ]; + + $classBraceCount = 0; + + // start processing with current token + + SCANNER_CLASS_TOP: + + // process the name + if ($infos[$infoIndex]['shortName'] == '' + && (($tokenType === T_CLASS || $tokenType === T_INTERFACE || $tokenType === T_TRAIT) && $infos[$infoIndex]['type'] === 'class' + || ($tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function')) + ) { + $infos[$infoIndex]['shortName'] = $tokens[$tokenIndex + 2][1]; + $infos[$infoIndex]['name'] = (($namespace !== null) ? $namespace . '\\' : '') . $infos[$infoIndex]['shortName']; + } + + if ($tokenType === null) { + if ($tokenContent == '{') { + $classBraceCount++; + } + if ($tokenContent == '}') { + $classBraceCount--; + if ($classBraceCount === 0) { + goto SCANNER_CLASS_END; + } + } + } + + SCANNER_CLASS_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_CLASS_TOP; + + SCANNER_CLASS_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + + } + + SCANNER_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + /** + * END FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + $this->isScanned = true; + } + + /** + * Check for namespace + * + * @param string $namespace + * @return bool + */ + public function hasNamespace($namespace) + { + $this->scan(); + + foreach ($this->infos as $info) { + if ($info['type'] == 'namespace' && $info['namespace'] == $namespace) { + return true; + } + } + return false; + } + + /** + * @param string $namespace + * @return null|array + * @throws Exception\InvalidArgumentException + */ + protected function getUsesNoScan($namespace) + { + $namespaces = []; + foreach ($this->infos as $info) { + if ($info['type'] == 'namespace') { + $namespaces[] = $info['namespace']; + } + } + + if ($namespace === null) { + $namespace = array_shift($namespaces); + } elseif (!is_string($namespace)) { + throw new Exception\InvalidArgumentException('Invalid namespace provided'); + } elseif (!in_array($namespace, $namespaces)) { + return; + } + + $uses = []; + foreach ($this->infos as $info) { + if ($info['type'] !== 'use') { + continue; + } + foreach ($info['statements'] as $statement) { + if ($info['namespace'] == $namespace) { + $uses[] = $statement; + } + } + } + + return $uses; + } +} diff --git a/zendframework/zend-code/src/Scanner/Util.php b/zendframework/zend-code/src/Scanner/Util.php new file mode 100644 index 000000000..dd942f527 --- /dev/null +++ b/zendframework/zend-code/src/Scanner/Util.php @@ -0,0 +1,74 @@ +namespace && !$data->uses && strlen($value) > 0 && $value{0} != '\\') { + $value = $data->namespace . '\\' . $value; + + return; + } + + if (!$data->uses || strlen($value) <= 0 || $value{0} == '\\') { + $value = ltrim($value, '\\'); + + return; + } + + if ($data->namespace || $data->uses) { + $firstPart = $value; + if (($firstPartEnd = strpos($firstPart, '\\')) !== false) { + $firstPart = substr($firstPart, 0, $firstPartEnd); + } else { + $firstPartEnd = strlen($firstPart); + } + + if (array_key_exists($firstPart, $data->uses)) { + $value = substr_replace($value, $data->uses[$firstPart], 0, $firstPartEnd); + + return; + } + + if ($data->namespace) { + $value = $data->namespace . '\\' . $value; + + return; + } + } + } +} diff --git a/zendframework/zend-code/src/Scanner/ValueScanner.php b/zendframework/zend-code/src/Scanner/ValueScanner.php new file mode 100644 index 000000000..ec8825e5a --- /dev/null +++ b/zendframework/zend-code/src/Scanner/ValueScanner.php @@ -0,0 +1,15 @@ +listeners as $index => $callback) { + $events->detach($callback); + unset($this->listeners[$index]); + } + } +} diff --git a/zendframework/zend-eventmanager/src/Event.php b/zendframework/zend-eventmanager/src/Event.php new file mode 100644 index 000000000..e92865f0a --- /dev/null +++ b/zendframework/zend-eventmanager/src/Event.php @@ -0,0 +1,201 @@ +setName($name); + } + + if (null !== $target) { + $this->setTarget($target); + } + + if (null !== $params) { + $this->setParams($params); + } + } + + /** + * Get event name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the event target + * + * This may be either an object, or the name of a static method. + * + * @return string|object + */ + public function getTarget() + { + return $this->target; + } + + /** + * Set parameters + * + * Overwrites parameters + * + * @param array|ArrayAccess|object $params + * @throws Exception\InvalidArgumentException + */ + public function setParams($params) + { + if (! is_array($params) && ! is_object($params)) { + throw new Exception\InvalidArgumentException( + sprintf('Event parameters must be an array or object; received "%s"', gettype($params)) + ); + } + + $this->params = $params; + } + + /** + * Get all parameters + * + * @return array|object|ArrayAccess + */ + public function getParams() + { + return $this->params; + } + + /** + * Get an individual parameter + * + * If the parameter does not exist, the $default value will be returned. + * + * @param string|int $name + * @param mixed $default + * @return mixed + */ + public function getParam($name, $default = null) + { + // Check in params that are arrays or implement array access + if (is_array($this->params) || $this->params instanceof ArrayAccess) { + if (! isset($this->params[$name])) { + return $default; + } + + return $this->params[$name]; + } + + // Check in normal objects + if (! isset($this->params->{$name})) { + return $default; + } + return $this->params->{$name}; + } + + /** + * Set the event name + * + * @param string $name + */ + public function setName($name) + { + $this->name = (string) $name; + } + + /** + * Set the event target/context + * + * @param null|string|object $target + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Set an individual parameter to a value + * + * @param string|int $name + * @param mixed $value + */ + public function setParam($name, $value) + { + if (is_array($this->params) || $this->params instanceof ArrayAccess) { + // Arrays or objects implementing array access + $this->params[$name] = $value; + return; + } + + // Objects + $this->params->{$name} = $value; + } + + /** + * Stop further event propagation + * + * @param bool $flag + */ + public function stopPropagation($flag = true) + { + $this->stopPropagation = (bool) $flag; + } + + /** + * Is propagation stopped? + * + * @return bool + */ + public function propagationIsStopped() + { + return $this->stopPropagation; + } +} diff --git a/zendframework/zend-eventmanager/src/EventInterface.php b/zendframework/zend-eventmanager/src/EventInterface.php new file mode 100644 index 000000000..76f2d4878 --- /dev/null +++ b/zendframework/zend-eventmanager/src/EventInterface.php @@ -0,0 +1,96 @@ + => [ + * => [ + * 0 => [, ...] + * ], + * ... + * ], + * ... + * ] + * + * NOTE: + * This structure helps us to reuse the list of listeners + * instead of first iterating over it and generating a new one + * -> In result it improves performance by up to 25% even if it looks a bit strange + * + * @var array[] + */ + protected $events = []; + + /** + * @var EventInterface Prototype to use when creating an event at trigger(). + */ + protected $eventPrototype; + + /** + * Identifiers, used to pull shared signals from SharedEventManagerInterface instance + * + * @var array + */ + protected $identifiers = []; + + /** + * Shared event manager + * + * @var null|SharedEventManagerInterface + */ + protected $sharedManager = null; + + /** + * Constructor + * + * Allows optionally specifying identifier(s) to use to pull signals from a + * SharedEventManagerInterface. + * + * @param SharedEventManagerInterface $sharedEventManager + * @param array $identifiers + */ + public function __construct(SharedEventManagerInterface $sharedEventManager = null, array $identifiers = []) + { + if ($sharedEventManager) { + $this->sharedManager = $sharedEventManager; + $this->setIdentifiers($identifiers); + } + + $this->eventPrototype = new Event(); + } + + /** + * @inheritDoc + */ + public function setEventPrototype(EventInterface $prototype) + { + $this->eventPrototype = $prototype; + } + + /** + * Retrieve the shared event manager, if composed. + * + * @return null|SharedEventManagerInterface $sharedEventManager + */ + public function getSharedManager() + { + return $this->sharedManager; + } + + /** + * @inheritDoc + */ + public function getIdentifiers() + { + return $this->identifiers; + } + + /** + * @inheritDoc + */ + public function setIdentifiers(array $identifiers) + { + $this->identifiers = array_unique($identifiers); + } + + /** + * @inheritDoc + */ + public function addIdentifiers(array $identifiers) + { + $this->identifiers = array_unique(array_merge( + $this->identifiers, + $identifiers + )); + } + + /** + * @inheritDoc + */ + public function trigger($eventName, $target = null, $argv = []) + { + $event = clone $this->eventPrototype; + $event->setName($eventName); + + if ($target !== null) { + $event->setTarget($target); + } + + if ($argv) { + $event->setParams($argv); + } + + return $this->triggerListeners($event); + } + + /** + * @inheritDoc + */ + public function triggerUntil(callable $callback, $eventName, $target = null, $argv = []) + { + $event = clone $this->eventPrototype; + $event->setName($eventName); + + if ($target !== null) { + $event->setTarget($target); + } + + if ($argv) { + $event->setParams($argv); + } + + return $this->triggerListeners($event, $callback); + } + + /** + * @inheritDoc + */ + public function triggerEvent(EventInterface $event) + { + return $this->triggerListeners($event); + } + + /** + * @inheritDoc + */ + public function triggerEventUntil(callable $callback, EventInterface $event) + { + return $this->triggerListeners($event, $callback); + } + + /** + * @inheritDoc + */ + public function attach($eventName, callable $listener, $priority = 1) + { + if (! is_string($eventName)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a string for the event; received %s', + __METHOD__, + (is_object($eventName) ? get_class($eventName) : gettype($eventName)) + )); + } + + $this->events[$eventName][(int) $priority][0][] = $listener; + return $listener; + } + + /** + * @inheritDoc + * @throws Exception\InvalidArgumentException for invalid event types. + */ + public function detach(callable $listener, $eventName = null, $force = false) + { + + // If event is wildcard, we need to iterate through each listeners + if (null === $eventName || ('*' === $eventName && ! $force)) { + foreach (array_keys($this->events) as $eventName) { + $this->detach($listener, $eventName, true); + } + return; + } + + if (! is_string($eventName)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a string for the event; received %s', + __METHOD__, + (is_object($eventName) ? get_class($eventName) : gettype($eventName)) + )); + } + + if (! isset($this->events[$eventName])) { + return; + } + + foreach ($this->events[$eventName] as $priority => $listeners) { + foreach ($listeners[0] as $index => $evaluatedListener) { + if ($evaluatedListener !== $listener) { + continue; + } + + // Found the listener; remove it. + unset($this->events[$eventName][$priority][0][$index]); + + // If the queue for the given priority is empty, remove it. + if (empty($this->events[$eventName][$priority][0])) { + unset($this->events[$eventName][$priority]); + break; + } + } + } + + // If the queue for the given event is empty, remove it. + if (empty($this->events[$eventName])) { + unset($this->events[$eventName]); + } + } + + /** + * @inheritDoc + */ + public function clearListeners($eventName) + { + if (isset($this->events[$eventName])) { + unset($this->events[$eventName]); + } + } + + /** + * Prepare arguments + * + * Use this method if you want to be able to modify arguments from within a + * listener. It returns an ArrayObject of the arguments, which may then be + * passed to trigger(). + * + * @param array $args + * @return ArrayObject + */ + public function prepareArgs(array $args) + { + return new ArrayObject($args); + } + + /** + * Trigger listeners + * + * Actual functionality for triggering listeners, to which trigger() delegate. + * + * @param EventInterface $event + * @param null|callable $callback + * @return ResponseCollection + */ + protected function triggerListeners(EventInterface $event, callable $callback = null) + { + $name = $event->getName(); + + if (empty($name)) { + throw new Exception\RuntimeException('Event is missing a name; cannot trigger!'); + } + + if (isset($this->events[$name])) { + $listOfListenersByPriority = $this->events[$name]; + + if (isset($this->events['*'])) { + foreach ($this->events['*'] as $priority => $listOfListeners) { + $listOfListenersByPriority[$priority][] = $listOfListeners[0]; + } + } + } elseif (isset($this->events['*'])) { + $listOfListenersByPriority = $this->events['*']; + } else { + $listOfListenersByPriority = []; + } + + if ($this->sharedManager) { + foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) { + $listOfListenersByPriority[$priority][] = $listeners; + } + } + + // Sort by priority in reverse order + krsort($listOfListenersByPriority); + + // Initial value of stop propagation flag should be false + $event->stopPropagation(false); + + // Execute listeners + $responses = new ResponseCollection(); + foreach ($listOfListenersByPriority as $listOfListeners) { + foreach ($listOfListeners as $listeners) { + foreach ($listeners as $listener) { + $response = $listener($event); + $responses->push($response); + + // If the event was asked to stop propagating, do so + if ($event->propagationIsStopped()) { + $responses->setStopped(true); + return $responses; + } + + // If the result causes our validation callback to return true, + // stop propagation + if ($callback && $callback($response)) { + $responses->setStopped(true); + return $responses; + } + } + } + } + + return $responses; + } +} diff --git a/zendframework/zend-eventmanager/src/EventManagerAwareInterface.php b/zendframework/zend-eventmanager/src/EventManagerAwareInterface.php new file mode 100644 index 000000000..42ccbcf90 --- /dev/null +++ b/zendframework/zend-eventmanager/src/EventManagerAwareInterface.php @@ -0,0 +1,24 @@ +eventIdentifier property. + * + * @param EventManagerInterface $events + */ + public function setEventManager(EventManagerInterface $events) + { + $identifiers = [__CLASS__, get_class($this)]; + if (isset($this->eventIdentifier)) { + if ((is_string($this->eventIdentifier)) + || (is_array($this->eventIdentifier)) + || ($this->eventIdentifier instanceof Traversable) + ) { + $identifiers = array_unique(array_merge($identifiers, (array) $this->eventIdentifier)); + } elseif (is_object($this->eventIdentifier)) { + $identifiers[] = $this->eventIdentifier; + } + // silently ignore invalid eventIdentifier types + } + $events->setIdentifiers($identifiers); + $this->events = $events; + if (method_exists($this, 'attachDefaultListeners')) { + $this->attachDefaultListeners(); + } + } + + /** + * Retrieve the event manager + * + * Lazy-loads an EventManager instance if none registered. + * + * @return EventManagerInterface + */ + public function getEventManager() + { + if (! $this->events instanceof EventManagerInterface) { + $this->setEventManager(new EventManager()); + } + return $this->events; + } +} diff --git a/zendframework/zend-eventmanager/src/EventManagerInterface.php b/zendframework/zend-eventmanager/src/EventManagerInterface.php new file mode 100644 index 000000000..b61312d67 --- /dev/null +++ b/zendframework/zend-eventmanager/src/EventManagerInterface.php @@ -0,0 +1,164 @@ +setExtractFlags(self::EXTR_BOTH); + + // Iterate and remove any matches + $removed = false; + $items = []; + $this->rewind(); + while (! $this->isEmpty()) { + $item = $this->extract(); + if ($item['data'] === $datum) { + $removed = true; + continue; + } + $items[] = $item; + } + + // Repopulate + foreach ($items as $item) { + $this->insert($item['data'], $item['priority']); + } + + $this->setExtractFlags(self::EXTR_DATA); + return $removed; + } + + /** + * Iterate the next filter in the chain + * + * Iterates and calls the next filter in the chain. + * + * @param mixed $context + * @param array $params + * @param FilterIterator $chain + * @return mixed + */ + public function next($context = null, array $params = [], $chain = null) + { + if (empty($context) || ($chain instanceof FilterIterator && $chain->isEmpty())) { + return; + } + + //We can't extract from an empty heap + if ($this->isEmpty()) { + return; + } + + $next = $this->extract(); + return $next($context, $params, $chain); + } +} diff --git a/zendframework/zend-eventmanager/src/FilterChain.php b/zendframework/zend-eventmanager/src/FilterChain.php new file mode 100644 index 000000000..85e0423e3 --- /dev/null +++ b/zendframework/zend-eventmanager/src/FilterChain.php @@ -0,0 +1,112 @@ +filters = new Filter\FilterIterator(); + } + + /** + * Apply the filters + * + * Begins iteration of the filters. + * + * @param mixed $context Object under observation + * @param mixed $argv Associative array of arguments + * @return mixed + */ + public function run($context, array $argv = []) + { + $chain = clone $this->getFilters(); + + if ($chain->isEmpty()) { + return; + } + + $next = $chain->extract(); + + return $next($context, $argv, $chain); + } + + /** + * Connect a filter to the chain + * + * @param callable $callback PHP Callback + * @param int $priority Priority in the queue at which to execute; + * defaults to 1 (higher numbers == higher priority) + * @return CallbackHandler (to allow later unsubscribe) + * @throws Exception\InvalidCallbackException + */ + public function attach(callable $callback, $priority = 1) + { + $this->filters->insert($callback, $priority); + return $callback; + } + + /** + * Detach a filter from the chain + * + * @param callable $filter + * @return bool Returns true if filter found and unsubscribed; returns false otherwise + */ + public function detach(callable $filter) + { + return $this->filters->remove($filter); + } + + /** + * Retrieve all filters + * + * @return Filter\FilterIterator + */ + public function getFilters() + { + return $this->filters; + } + + /** + * Clear all filters + * + * @return void + */ + public function clearFilters() + { + $this->filters = new Filter\FilterIterator(); + } + + /** + * Return current responses + * + * Only available while the chain is still being iterated. Returns the + * current ResponseCollection. + * + * @return null|ResponseCollection + */ + public function getResponses() + { + return; + } +} diff --git a/zendframework/zend-eventmanager/src/LazyEventListener.php b/zendframework/zend-eventmanager/src/LazyEventListener.php new file mode 100644 index 000000000..be5cd7cd8 --- /dev/null +++ b/zendframework/zend-eventmanager/src/LazyEventListener.php @@ -0,0 +1,75 @@ +event = $definition['event']; + $this->priority = isset($definition['priority']) ? (int) $definition['priority'] : null; + } + + /** + * @return string + */ + public function getEvent() + { + return $this->event; + } + + /** + * @return int + */ + public function getPriority($default = 1) + { + return (null !== $this->priority) ? $this->priority : $default; + } +} diff --git a/zendframework/zend-eventmanager/src/LazyListener.php b/zendframework/zend-eventmanager/src/LazyListener.php new file mode 100644 index 000000000..b1e7d92eb --- /dev/null +++ b/zendframework/zend-eventmanager/src/LazyListener.php @@ -0,0 +1,122 @@ +service = $definition['listener']; + $this->method = $definition['method']; + $this->container = $container; + $this->env = $env; + } + + /** + * Use the listener as an invokable, allowing direct attachment to an event manager. + * + * @param EventInterface $event + * @return callable + */ + public function __invoke(EventInterface $event) + { + $listener = $this->fetchListener(); + $method = $this->method; + return $listener->{$method}($event); + } + + /** + * @return callable + */ + private function fetchListener() + { + if ($this->listener) { + return $this->listener; + } + + // In the future, typehint against Zend\ServiceManager\ServiceLocatorInterface, + // which defines this message starting in v3. + if (method_exists($this->container, 'build') && ! empty($this->env)) { + $this->listener = $this->container->build($this->service, $this->env); + return $this->listener; + } + + $this->listener = $this->container->get($this->service); + return $this->listener; + } +} diff --git a/zendframework/zend-eventmanager/src/LazyListenerAggregate.php b/zendframework/zend-eventmanager/src/LazyListenerAggregate.php new file mode 100644 index 000000000..146287078 --- /dev/null +++ b/zendframework/zend-eventmanager/src/LazyListenerAggregate.php @@ -0,0 +1,110 @@ + + * $events->attachAggregate(new LazyListenerAggregate( + * $lazyEventListenersOrDefinitions, + * $container + * )); + * + */ +class LazyListenerAggregate implements ListenerAggregateInterface +{ + use ListenerAggregateTrait; + + /** + * @var ContainerInterface Container from which to pull lazy listeners. + */ + private $container; + + /** + * @var array Additional environment/option variables to use when creating listener. + */ + private $env; + + /** + * Generated LazyEventListener instances. + * + * @var LazyEventListener[] + */ + private $lazyListeners = []; + + /** + * Constructor + * + * Accepts the composed $listeners, as well as the $container and $env in + * order to create a listener aggregate that defers listener creation until + * the listener is triggered. + * + * Listeners may be either LazyEventListener instances, or lazy event + * listener definitions that can be provided to a LazyEventListener + * constructor in order to create a new instance; in the latter case, the + * $container and $env will be passed at instantiation as well. + * + * @var array $listeners LazyEventListener instances or array definitions + * to pass to the LazyEventListener constructor. + * @var ContainerInterface $container + * @var array $env + * @throws Exception\InvalidArgumentException for invalid listener items. + */ + public function __construct(array $listeners, ContainerInterface $container, array $env = []) + { + $this->container = $container; + $this->env = $env; + + // This would raise an exception for invalid structs + foreach ($listeners as $listener) { + if (is_array($listener)) { + $listener = new LazyEventListener($listener, $container, $env); + } + + if (! $listener instanceof LazyEventListener) { + throw new Exception\InvalidArgumentException(sprintf( + 'All listeners must be LazyEventListener instances or definitions; received %s', + (is_object($listener) ? get_class($listener) : gettype($listener)) + )); + } + + $this->lazyListeners[] = $listener; + } + } + + /** + * Attach the aggregate to the event manager. + * + * Loops through all composed lazy listeners, and attaches them to the + * event manager. + * + * @var EventManagerInterface $events + * @var int $priority + */ + public function attach(EventManagerInterface $events, $priority = 1) + { + foreach ($this->lazyListeners as $lazyListener) { + $this->listeners[] = $events->attach( + $lazyListener->getEvent(), + $lazyListener, + $lazyListener->getPriority($priority) + ); + } + } +} diff --git a/zendframework/zend-eventmanager/src/ListenerAggregateInterface.php b/zendframework/zend-eventmanager/src/ListenerAggregateInterface.php new file mode 100644 index 000000000..910fbb5ea --- /dev/null +++ b/zendframework/zend-eventmanager/src/ListenerAggregateInterface.php @@ -0,0 +1,41 @@ +listeners as $index => $callback) { + $events->detach($callback); + unset($this->listeners[$index]); + } + } +} diff --git a/zendframework/zend-eventmanager/src/ResponseCollection.php b/zendframework/zend-eventmanager/src/ResponseCollection.php new file mode 100644 index 000000000..57c5bd3fb --- /dev/null +++ b/zendframework/zend-eventmanager/src/ResponseCollection.php @@ -0,0 +1,82 @@ +stopped; + } + + /** + * Mark the collection as stopped (or its opposite) + * + * @param bool $flag + */ + public function setStopped($flag) + { + $this->stopped = (bool) $flag; + } + + /** + * Convenient access to the first handler return value. + * + * @return mixed The first handler return value + */ + public function first() + { + return parent::bottom(); + } + + /** + * Convenient access to the last handler return value. + * + * If the collection is empty, returns null. Otherwise, returns value + * returned by last handler. + * + * @return mixed The last handler return value + */ + public function last() + { + if (count($this) === 0) { + return; + } + return parent::top(); + } + + /** + * Check if any of the responses match the given value. + * + * @param mixed $value The value to look for among responses + * @return bool + */ + public function contains($value) + { + foreach ($this as $response) { + if ($response === $value) { + return true; + } + } + return false; + } +} diff --git a/zendframework/zend-eventmanager/src/SharedEventManager.php b/zendframework/zend-eventmanager/src/SharedEventManager.php new file mode 100644 index 000000000..b61701bbd --- /dev/null +++ b/zendframework/zend-eventmanager/src/SharedEventManager.php @@ -0,0 +1,237 @@ + + * $sharedEventManager = new SharedEventManager(); + * foreach (['My\Resource\AbstractResource', 'My\Resource\EntityResource'] as $identifier) { + * $sharedEventManager->attach( + * $identifier, + * 'getAll', + * function ($e) use ($cache) { + * if (!$id = $e->getParam('id', false)) { + * return; + * } + * if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) { + * return; + * } + * return $data; + * } + * ); + * } + * + * + * @param string $identifier Identifier for event emitting component. + * @param string $event + * @param callable $listener Listener that will handle the event. + * @param int $priority Priority at which listener should execute + * @return void + * @throws Exception\InvalidArgumentException for invalid identifier arguments. + * @throws Exception\InvalidArgumentException for invalid event arguments. + */ + public function attach($identifier, $event, callable $listener, $priority = 1) + { + if (! is_string($identifier) || empty($identifier)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid identifier provided; must be a string; received "%s"', + (is_object($identifier) ? get_class($identifier) : gettype($identifier)) + )); + } + + if (! is_string($event) || empty($event)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid event provided; must be a non-empty string; received "%s"', + (is_object($event) ? get_class($event) : gettype($event)) + )); + } + + $this->identifiers[$identifier][$event][(int) $priority][] = $listener; + } + + /** + * @inheritDoc + */ + public function detach(callable $listener, $identifier = null, $eventName = null, $force = false) + { + // No identifier or wildcard identifier: loop through all identifiers and detach + if (null === $identifier || ('*' === $identifier && ! $force)) { + foreach (array_keys($this->identifiers) as $identifier) { + $this->detach($listener, $identifier, $eventName, true); + } + return; + } + + if (! is_string($identifier) || empty($identifier)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid identifier provided; must be a string, received %s', + (is_object($identifier) ? get_class($identifier) : gettype($identifier)) + )); + } + + // Do we have any listeners on the provided identifier? + if (! isset($this->identifiers[$identifier])) { + return; + } + + if (null === $eventName || ('*' === $eventName && ! $force)) { + foreach (array_keys($this->identifiers[$identifier]) as $eventName) { + $this->detach($listener, $identifier, $eventName, true); + } + return; + } + + if (! is_string($eventName) || empty($eventName)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid event name provided; must be a string, received %s', + (is_object($eventName) ? get_class($eventName) : gettype($eventName)) + )); + } + + if (! isset($this->identifiers[$identifier][$eventName])) { + return; + } + + foreach ($this->identifiers[$identifier][$eventName] as $priority => $listeners) { + foreach ($listeners as $index => $evaluatedListener) { + if ($evaluatedListener !== $listener) { + continue; + } + + // Found the listener; remove it. + unset($this->identifiers[$identifier][$eventName][$priority][$index]); + + // Is the priority queue empty? + if (empty($this->identifiers[$identifier][$eventName][$priority])) { + unset($this->identifiers[$identifier][$eventName][$priority]); + break; + } + } + + // Is the event queue empty? + if (empty($this->identifiers[$identifier][$eventName])) { + unset($this->identifiers[$identifier][$eventName]); + break; + } + } + + // Is the identifier queue now empty? Remove it. + if (empty($this->identifiers[$identifier])) { + unset($this->identifiers[$identifier]); + } + } + + /** + * Retrieve all listeners for a given identifier and event + * + * @param string[] $identifiers + * @param string $eventName + * @return array[] + * @throws Exception\InvalidArgumentException + */ + public function getListeners(array $identifiers, $eventName) + { + if ('*' === $eventName || ! is_string($eventName) || empty($eventName)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Event name passed to %s must be a non-empty, non-wildcard string', + __METHOD__ + )); + } + + $returnListeners = []; + + foreach ($identifiers as $identifier) { + if ('*' === $identifier || ! is_string($identifier) || empty($identifier)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Identifier names passed to %s must be non-empty, non-wildcard strings', + __METHOD__ + )); + } + + if (isset($this->identifiers[$identifier])) { + $listenersByIdentifier = $this->identifiers[$identifier]; + if (isset($listenersByIdentifier[$eventName])) { + foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) { + $returnListeners[$priority][] = $listeners; + } + } + if (isset($listenersByIdentifier['*'])) { + foreach ($listenersByIdentifier['*'] as $priority => $listeners) { + $returnListeners[$priority][] = $listeners; + } + } + } + } + + if (isset($this->identifiers['*'])) { + $wildcardIdentifier = $this->identifiers['*']; + if (isset($wildcardIdentifier[$eventName])) { + foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) { + $returnListeners[$priority][] = $listeners; + } + } + if (isset($wildcardIdentifier['*'])) { + foreach ($wildcardIdentifier['*'] as $priority => $listeners) { + $returnListeners[$priority][] = $listeners; + } + } + } + + foreach ($returnListeners as $priority => $listOfListeners) { + // Argument unpacking requires PHP-5.6 + // $listeners[$priority] = array_merge(...$listOfListeners); + $returnListeners[$priority] = call_user_func_array('array_merge', $listOfListeners); + } + + return $returnListeners; + } + + /** + * @inheritDoc + */ + public function clearListeners($identifier, $eventName = null) + { + if (! isset($this->identifiers[$identifier])) { + return false; + } + + if (null === $eventName) { + unset($this->identifiers[$identifier]); + return; + } + + if (! isset($this->identifiers[$identifier][$eventName])) { + return; + } + + unset($this->identifiers[$identifier][$eventName]); + } +} diff --git a/zendframework/zend-eventmanager/src/SharedEventManagerInterface.php b/zendframework/zend-eventmanager/src/SharedEventManagerInterface.php new file mode 100644 index 000000000..c2457041a --- /dev/null +++ b/zendframework/zend-eventmanager/src/SharedEventManagerInterface.php @@ -0,0 +1,59 @@ +setAccessible(true); + $listeners = $r->getValue($events); + return array_keys($listeners); + } + + /** + * Retrieve an interable list of listeners for an event. + * + * Given an event and an event manager, returns an iterator with the + * listeners for that event, in priority order. + * + * If $withPriority is true, the key values will be the priority at which + * the given listener is attached. + * + * Do not pass $withPriority if you want to cast the iterator to an array, + * as many listeners will likely have the same priority, and thus casting + * will collapse to the last added. + * + * @param string $event + * @param EventManager $events + * @param bool $withPriority + * @return \Traversable + */ + private function getListenersForEvent($event, EventManager $events, $withPriority = false) + { + $r = new ReflectionProperty($events, 'events'); + $r->setAccessible(true); + $internal = $r->getValue($events); + + $listeners = []; + foreach (isset($internal[$event]) ? $internal[$event] : [] as $p => $listOfListeners) { + foreach ($listOfListeners as $l) { + $listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l; + } + } + + return $this->traverseListeners($listeners, $withPriority); + } + + /** + * Assert that a given listener exists at the specified priority. + * + * @param callable $expectedListener + * @param int $expectedPriority + * @param string $event + * @param EventManager $events + * @param string $message Failure message to use, if any. + */ + private function assertListenerAtPriority( + callable $expectedListener, + $expectedPriority, + $event, + EventManager $events, + $message = '' + ) { + $message = $message ?: sprintf( + 'Listener not found for event "%s" and priority %d', + $event, + $expectedPriority + ); + $listeners = $this->getListenersForEvent($event, $events, true); + $found = false; + foreach ($listeners as $priority => $listener) { + if ($listener === $expectedListener + && $priority === $expectedPriority + ) { + $found = true; + break; + } + } + Assert::assertTrue($found, $message); + } + + /** + * Returns an indexed array of listeners for an event. + * + * Returns an indexed array of listeners for an event, in priority order. + * Priority values will not be included; use this only for testing if + * specific listeners are present, or for a count of listeners. + * + * @param string $event + * @param EventManager $events + * @return callable[] + */ + private function getArrayOfListenersForEvent($event, EventManager $events) + { + return iterator_to_array($this->getListenersForEvent($event, $events)); + } + + /** + * Generator for traversing listeners in priority order. + * + * @param array $listeners + * @param bool $withPriority When true, yields priority as key. + */ + public function traverseListeners(array $queue, $withPriority = false) + { + krsort($queue, SORT_NUMERIC); + + foreach ($queue as $priority => $listeners) { + $priority = (int) $priority; + foreach ($listeners as $listener) { + if ($withPriority) { + yield $priority => $listener; + } else { + yield $listener; + } + } + } + } +}