Added PHP-DI in order to phase out SingletonFactory
authorAlexander Ebert <ebert@woltlab.com>
Tue, 17 Nov 2015 12:58:43 +0000 (13:58 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 17 Nov 2015 12:58:43 +0000 (13:58 +0100)
137 files changed:
wcfsetup/install/files/lib/system/SingletonFactory.class.php
wcfsetup/install/files/lib/system/WCF.class.php
wcfsetup/install/files/lib/system/api/autoload.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer.lock [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/ClassLoader.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/LICENSE [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_files.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_namespaces.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_real.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/installed.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/.gitignore [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/LICENSE [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/README.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/composer.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface-meta.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup-meta.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/interoperating_containers.png [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/priority.png [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/side_by_side_containers.png [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/ContainerInterface.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/CONTRIBUTING.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/LICENSE [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/README.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/composer.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/doc/parameter-resolvers.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/CallableResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/InvocationException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotCallableException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotEnoughParametersException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/Invoker.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/InvokerInterface.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ParameterResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ResolverChain.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/invoker/src/Reflection/CallableReflection.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/.coveralls.yml [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/.gitattributes [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/.gitignore [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/.travis.yml [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/404.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/CONTRIBUTING.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/LICENSE [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/README.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/change-log.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/composer.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/couscous.yml [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/phpunit.xml.dist [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Inject.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Injectable.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Cache/ArrayCache.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Container.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/ContainerBuilder.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Debug.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/AliasDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinitionExtension.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/CacheableDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/DecoratorDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Definition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/AliasDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ArrayDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DecoratorDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumperDispatcher.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/EnvironmentVariableDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/FactoryDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ObjectDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/StringDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ValueDefinitionDumper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EntryReference.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EnvironmentVariableDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/AnnotationException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/DefinitionException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/FactoryDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/HasSubDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ArrayDefinitionExtensionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/DefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/EnvironmentVariableDefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/FactoryDefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ObjectDefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/StringDefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ValueDefinitionHelper.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/InstanceDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/MethodInjection.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/PropertyInjection.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/AliasResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ArrayResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DecoratorResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DefinitionResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/EnvironmentVariableResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/FactoryResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/InstanceInjector.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ObjectCreator.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ParameterResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ResolverDispatcher.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/StringResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ValueResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/AnnotationReader.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/Autowiring.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/CachedDefinitionSource.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionArray.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionFile.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionSource.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/MutableDefinitionSource.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/SourceChain.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/StringDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ValueDefinition.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/DependencyException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/FactoryInterface.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Invoker/DefinitionParameterResolver.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/InvokerInterface.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/NotFoundException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Proxy/ProxyFactory.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Reflection/CallableReflectionFactory.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Scope.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/functions.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitattributes [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitignore [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/LICENSE [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/README.md [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/composer.json [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/AnnotationException.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpDocReader.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/TokenParser.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/UseStatementParser.php [new file with mode: 0644]

index 2c90207a0e7d6f749868249d618193b81cff31d8..d15fc3f2f662a215c6962c94ac2c0f36155fe8f3 100644 (file)
@@ -13,17 +13,11 @@ use wcf\system\exception\SystemException;
  * @category   Community Framework
  */
 abstract class SingletonFactory {
-       /**
-        * list of singletons
-        * @var array<SingletonFactory>
-        */
-       protected static $__singletonObjects = array();
-       
        /**
         * Singletons do not support a public constructor. Override init() if
         * your class needs to initialize components on creation.
         */
-       protected final function __construct() {
+       public final function __construct() {
                $this->init();
        }
        
@@ -50,26 +44,6 @@ abstract class SingletonFactory {
         * @return      static
         */
        public static final function getInstance() {
-               $className = get_called_class();
-               if (!array_key_exists($className, self::$__singletonObjects)) {
-                       self::$__singletonObjects[$className] = null;
-                       self::$__singletonObjects[$className] = new $className();
-               }
-               else if (self::$__singletonObjects[$className] === null) {
-                       throw new SystemException("Infinite loop detected while trying to retrieve object for '".$className."'");
-               }
-               
-               return self::$__singletonObjects[$className];
-       }
-       
-       /**
-        * Returns whether this singleton is already initialized.
-        * 
-        * @return      boolean
-        */
-       public static final function isInitialized() {
-               $className = get_called_class();
-               
-               return isset(self::$__singletonObjects[$className]);
+               return WCF::getDIContainer()->get(get_called_class());
        }
 }
index 92be155773367fcbb280e9793d4bfdd40d535d36..dd4209d835e6a7607ce9f7c4eb4bd5a8b830c266 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\system;
+use DI\ContainerBuilder;
 use wcf\data\application\Application;
 use wcf\data\option\OptionEditor;
 use wcf\data\package\Package;
@@ -44,6 +45,7 @@ define('TIME_NOW', time());
 // wcf imports
 if (!defined('NO_IMPORTS')) {
        require_once(WCF_DIR.'lib/core.functions.php');
+       require_once(WCF_DIR.'lib/system/api/autoload.php');
 }
 
 /**
@@ -94,6 +96,12 @@ class WCF {
         */
        protected static $dbObj = null;
        
+       /**
+        * dependency injection container
+        * @var \DI\Container
+        */
+       protected static $diContainer = null;
+       
        /**
         * language object
         * @var \wcf\data\language\Language
@@ -134,6 +142,9 @@ class WCF {
                // define tmp directory
                if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder());
                
+               $builder = new ContainerBuilder();
+               self::$diContainer = $builder->build();
+               
                // start initialization
                $this->initDB();
                $this->loadOptions();
@@ -148,6 +159,15 @@ class WCF {
                EventHandler::getInstance()->fireAction($this, 'initialized');
        }
        
+       /**
+        * Returns the dependency injection container.
+        * 
+        * @return \DI\Container
+        */
+       public static final function getDIContainer() {
+               return self::$diContainer;
+       }
+       
        /**
         * Flushes the output, closes the session, performs background tasks and more.
         * 
@@ -308,7 +328,7 @@ class WCF {
                $factory = new SessionFactory();
                $factory->load();
                
-               self::$sessionObj = SessionHandler::getInstance();
+               self::$sessionObj = self::$diContainer->get(SessionHandler::class);
                self::$sessionObj->setHasValidCookie($factory->hasValidCookie());
        }
        
@@ -333,7 +353,7 @@ class WCF {
         * Initialises the template engine.
         */
        protected function initTPL() {
-               self::$tplObj = TemplateEngine::getInstance();
+               self::$tplObj = self::$diContainer->get(TemplateEngine::class);
                self::getTPL()->setLanguageID(self::getLanguage()->languageID);
                $this->assignDefaultTemplateVariables();
                
@@ -348,7 +368,9 @@ class WCF {
                        self::getSession()->setStyleID(intval($_REQUEST['styleID']));
                }
                
-               StyleHandler::getInstance()->changeStyle(self::getSession()->getStyleID());
+               /** @var $styleHandler \wcf\system\style\StyleHandler */
+               $styleHandler = self::$diContainer->get(StyleHandler::class);
+               $styleHandler->changeStyle(self::getSession()->getStyleID());
        }
        
        /**
diff --git a/wcfsetup/install/files/lib/system/api/autoload.php b/wcfsetup/install/files/lib/system/api/autoload.php
new file mode 100644 (file)
index 0000000..4694b27
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer' . '/autoload_real.php';
+
+return ComposerAutoloaderInitbf0ab6890db693133cef0043fae5b370::getLoader();
diff --git a/wcfsetup/install/files/lib/system/api/composer.json b/wcfsetup/install/files/lib/system/api/composer.json
new file mode 100644 (file)
index 0000000..9506cbb
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "config": {
+        "vendor-dir": "./"
+    },
+    "require": {
+        "php-di/php-di": "5.1.*"
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/composer.lock b/wcfsetup/install/files/lib/system/api/composer.lock
new file mode 100644 (file)
index 0000000..f4c57f4
--- /dev/null
@@ -0,0 +1,183 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "hash": "42cea85fb0771ad2e63dbf2fd651fcc9",
+    "content-hash": "85555ff71b5f67266b9d82865ad103e5",
+    "packages": [
+        {
+            "name": "container-interop/container-interop",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/container-interop/container-interop.git",
+                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
+                "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Interop\\Container\\": "src/Interop/Container/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+            "time": "2014-12-30 15:22:37"
+        },
+        {
+            "name": "php-di/invoker",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHP-DI/Invoker.git",
+                "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/9949fff87fcf14e8f2ccfbe36dac1e5921944c48",
+                "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48",
+                "shasum": ""
+            },
+            "require": {
+                "container-interop/container-interop": "~1.1"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "phpunit/phpunit": "~4.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Invoker\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Generic and extensible callable invoker",
+            "homepage": "https://github.com/PHP-DI/Invoker",
+            "keywords": [
+                "callable",
+                "dependency",
+                "dependency-injection",
+                "injection",
+                "invoke",
+                "invoker"
+            ],
+            "time": "2015-10-22 19:49:23"
+        },
+        {
+            "name": "php-di/php-di",
+            "version": "5.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHP-DI/PHP-DI.git",
+                "reference": "f4a8088fa4eb480ee66c51b5ee4e4e30cce78489"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f4a8088fa4eb480ee66c51b5ee4e4e30cce78489",
+                "reference": "f4a8088fa4eb480ee66c51b5ee4e4e30cce78489",
+                "shasum": ""
+            },
+            "require": {
+                "container-interop/container-interop": "~1.0",
+                "php": ">=5.4.0",
+                "php-di/invoker": "^1.0.1",
+                "php-di/phpdoc-reader": "~2.0"
+            },
+            "replace": {
+                "mnapoli/php-di": "*"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.2",
+                "doctrine/cache": "~1.4",
+                "mnapoli/phpunit-easymock": "~0.1.4",
+                "ocramius/proxy-manager": "~1.0",
+                "phpunit/phpunit": "~4.5"
+            },
+            "suggest": {
+                "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)",
+                "doctrine/cache": "Install it if you want to use the cache (version ~1.4)",
+                "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~1.0)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DI\\": "src/DI/"
+                },
+                "files": [
+                    "src/DI/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "The dependency injection container for humans",
+            "homepage": "http://php-di.org/",
+            "keywords": [
+                "container",
+                "dependency injection",
+                "di"
+            ],
+            "time": "2015-09-08 08:56:29"
+        },
+        {
+            "name": "php-di/phpdoc-reader",
+            "version": "2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHP-DI/PhpDocReader.git",
+                "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/21dce5e29f640d655e7b4583ecfb7d166127a5da",
+                "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpDocReader\\": "src/PhpDocReader"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)",
+            "keywords": [
+                "phpdoc",
+                "reflection"
+            ],
+            "time": "2015-06-01 14:23:20"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
diff --git a/wcfsetup/install/files/lib/system/api/composer/ClassLoader.php b/wcfsetup/install/files/lib/system/api/composer/ClassLoader.php
new file mode 100644 (file)
index 0000000..5e1469e
--- /dev/null
@@ -0,0 +1,413 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0 class loader
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+
+    private $classMapAuthoritative = false;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-0 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+        if ('\\' == $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative) {
+            return false;
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if ($file === null && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if ($file === null) {
+            // Remember that this class does not exist.
+            return $this->classMap[$class] = false;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}
diff --git a/wcfsetup/install/files/lib/system/api/composer/LICENSE b/wcfsetup/install/files/lib/system/api/composer/LICENSE
new file mode 100644 (file)
index 0000000..c8d57af
--- /dev/null
@@ -0,0 +1,21 @@
+
+Copyright (c) 2015 Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php b/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php
new file mode 100644 (file)
index 0000000..1bd6482
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = $vendorDir;
+
+return array(
+);
diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_files.php b/wcfsetup/install/files/lib/system/api/composer/autoload_files.php
new file mode 100644 (file)
index 0000000..0e0a9ea
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = $vendorDir;
+
+return array(
+    $vendorDir . '/php-di/php-di/src/DI/functions.php',
+);
diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_namespaces.php b/wcfsetup/install/files/lib/system/api/composer/autoload_namespaces.php
new file mode 100644 (file)
index 0000000..71c9e91
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = $vendorDir;
+
+return array(
+);
diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php b/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php
new file mode 100644 (file)
index 0000000..127166e
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = $vendorDir;
+
+return array(
+    'PhpDocReader\\' => array($vendorDir . '/php-di/phpdoc-reader/src/PhpDocReader'),
+    'Invoker\\' => array($vendorDir . '/php-di/invoker/src'),
+    'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'),
+    'DI\\' => array($vendorDir . '/php-di/php-di/src/DI'),
+);
diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_real.php b/wcfsetup/install/files/lib/system/api/composer/autoload_real.php
new file mode 100644 (file)
index 0000000..cf860b9
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitbf0ab6890db693133cef0043fae5b370
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInitbf0ab6890db693133cef0043fae5b370', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInitbf0ab6890db693133cef0043fae5b370', 'loadClassLoader'));
+
+        $map = require __DIR__ . '/autoload_namespaces.php';
+        foreach ($map as $namespace => $path) {
+            $loader->set($namespace, $path);
+        }
+
+        $map = require __DIR__ . '/autoload_psr4.php';
+        foreach ($map as $namespace => $path) {
+            $loader->setPsr4($namespace, $path);
+        }
+
+        $classMap = require __DIR__ . '/autoload_classmap.php';
+        if ($classMap) {
+            $loader->addClassMap($classMap);
+        }
+
+        $loader->register(true);
+
+        $includeFiles = require __DIR__ . '/autoload_files.php';
+        foreach ($includeFiles as $file) {
+            composerRequirebf0ab6890db693133cef0043fae5b370($file);
+        }
+
+        return $loader;
+    }
+}
+
+function composerRequirebf0ab6890db693133cef0043fae5b370($file)
+{
+    require $file;
+}
diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.json b/wcfsetup/install/files/lib/system/api/composer/installed.json
new file mode 100644 (file)
index 0000000..6b6c205
--- /dev/null
@@ -0,0 +1,174 @@
+[
+    {
+        "name": "php-di/phpdoc-reader",
+        "version": "2.0.0",
+        "version_normalized": "2.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/PHP-DI/PhpDocReader.git",
+            "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/21dce5e29f640d655e7b4583ecfb7d166127a5da",
+            "reference": "21dce5e29f640d655e7b4583ecfb7d166127a5da",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "~4.6"
+        },
+        "time": "2015-06-01 14:23:20",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "PhpDocReader\\": "src/PhpDocReader"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)",
+        "keywords": [
+            "phpdoc",
+            "reflection"
+        ]
+    },
+    {
+        "name": "container-interop/container-interop",
+        "version": "1.1.0",
+        "version_normalized": "1.1.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/container-interop/container-interop.git",
+            "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
+            "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
+            "shasum": ""
+        },
+        "time": "2014-12-30 15:22:37",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Interop\\Container\\": "src/Interop/Container/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "description": "Promoting the interoperability of container objects (DIC, SL, etc.)"
+    },
+    {
+        "name": "php-di/invoker",
+        "version": "1.2.0",
+        "version_normalized": "1.2.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/PHP-DI/Invoker.git",
+            "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/9949fff87fcf14e8f2ccfbe36dac1e5921944c48",
+            "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48",
+            "shasum": ""
+        },
+        "require": {
+            "container-interop/container-interop": "~1.1"
+        },
+        "require-dev": {
+            "athletic/athletic": "~0.1.8",
+            "phpunit/phpunit": "~4.5"
+        },
+        "time": "2015-10-22 19:49:23",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Invoker\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "description": "Generic and extensible callable invoker",
+        "homepage": "https://github.com/PHP-DI/Invoker",
+        "keywords": [
+            "callable",
+            "dependency",
+            "dependency-injection",
+            "injection",
+            "invoke",
+            "invoker"
+        ]
+    },
+    {
+        "name": "php-di/php-di",
+        "version": "5.1.0",
+        "version_normalized": "5.1.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/PHP-DI/PHP-DI.git",
+            "reference": "f4a8088fa4eb480ee66c51b5ee4e4e30cce78489"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f4a8088fa4eb480ee66c51b5ee4e4e30cce78489",
+            "reference": "f4a8088fa4eb480ee66c51b5ee4e4e30cce78489",
+            "shasum": ""
+        },
+        "require": {
+            "container-interop/container-interop": "~1.0",
+            "php": ">=5.4.0",
+            "php-di/invoker": "^1.0.1",
+            "php-di/phpdoc-reader": "~2.0"
+        },
+        "replace": {
+            "mnapoli/php-di": "*"
+        },
+        "require-dev": {
+            "doctrine/annotations": "~1.2",
+            "doctrine/cache": "~1.4",
+            "mnapoli/phpunit-easymock": "~0.1.4",
+            "ocramius/proxy-manager": "~1.0",
+            "phpunit/phpunit": "~4.5"
+        },
+        "suggest": {
+            "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)",
+            "doctrine/cache": "Install it if you want to use the cache (version ~1.4)",
+            "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~1.0)"
+        },
+        "time": "2015-09-08 08:56:29",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "DI\\": "src/DI/"
+            },
+            "files": [
+                "src/DI/functions.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "description": "The dependency injection container for humans",
+        "homepage": "http://php-di.org/",
+        "keywords": [
+            "container",
+            "dependency injection",
+            "di"
+        ]
+    }
+]
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/.gitignore b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/.gitignore
new file mode 100644 (file)
index 0000000..b2395aa
--- /dev/null
@@ -0,0 +1,3 @@
+composer.lock
+composer.phar
+/vendor/
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/LICENSE b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/LICENSE
new file mode 100644 (file)
index 0000000..7671d90
--- /dev/null
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 container-interop
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/README.md b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/README.md
new file mode 100644 (file)
index 0000000..ec434d0
--- /dev/null
@@ -0,0 +1,85 @@
+# Container Interoperability
+
+[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop)
+
+*container-interop* tries to identify and standardize features in *container* objects (service locators,
+dependency injection containers, etc.) to achieve interopererability.
+
+Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations. 
+
+If PHP projects that provide container implementations begin to adopt these common standards, then PHP
+applications and projects that use containers can depend on the common interfaces instead of specific
+implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume
+*any* container implementation that can be adapted to these interfaces.
+
+The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being
+worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope
+this project will pave the way for one or more future PSRs.
+
+
+## Installation
+
+You can install this package through Composer:
+
+```json
+{
+    "require": {
+        "container-interop/container-interop": "~1.0"
+    }
+}
+```
+
+The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility
+between minor versions.
+
+## Standards
+
+### Available
+
+- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php).
+[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md).
+Describes the interface of a container that exposes methods to read its entries.
+- [*Delegate lookup feature*](docs/Delegate-lookup.md).
+[Meta Document](docs/Delegate-lookup-meta.md).
+Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This 
+feature lets several containers work together in a single application.
+
+### Proposed
+
+View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC)
+
+## Compatible projects
+
+### Projects implementing `ContainerInterface`
+
+- [Acclimate](https://github.com/jeremeamia/acclimate-container)
+- [dcp-di](https://github.com/estelsmith/dcp-di)
+- [Mouf](http://mouf-php.com)
+- [Njasm Container](https://github.com/njasm/container)
+- [PHP-DI](http://php-di.org)
+- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
+- [XStatic](https://github.com/jeremeamia/xstatic)
+
+### Projects implementing the *delegate lookup* feature
+
+- [Mouf](http://mouf-php.com)
+- [PHP-DI](http://php-di.org)
+- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
+
+## Workflow
+
+Everyone is welcome to join and contribute.
+
+The general workflow looks like this:
+
+1. Someone opens a discussion (GitHub issue) to suggest an interface
+1. Feedback is gathered
+1. The interface is added to a development branch
+1. We release alpha versions so that the interface can be experimented with
+1. Discussions and edits ensue until the interface is deemed stable by a general consensus
+1. A new minor version of the package is released
+
+We try to not break BC by creating new interfaces instead of editing existing ones.
+
+While we currently work on interfaces, we are open to anything that might help towards interoperability, may that
+be code, best practices, etc.
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/composer.json b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/composer.json
new file mode 100644 (file)
index 0000000..84f3875
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "name": "container-interop/container-interop",
+    "type": "library",
+    "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "Interop\\Container\\": "src/Interop/Container/"
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface-meta.md b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface-meta.md
new file mode 100644 (file)
index 0000000..90711c9
--- /dev/null
@@ -0,0 +1,114 @@
+# ContainerInterface Meta Document
+
+## Introduction
+
+This document describes the process and discussions that lead to the `ContainerInterface`.
+Its goal is to explain the reasons behind each decision.
+
+## Goal
+
+The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
+container to obtain objects and parameters.
+
+By standardizing such a behavior, frameworks and libraries using the `ContainerInterface`
+could work with any compatible container.
+That would allow end users to choose their own container based on their own preferences.
+
+It is important to distinguish the two usages of a container:
+
+- configuring entries
+- fetching entries
+
+Most of the time, those two sides are not used by the same party.
+While it is often end users who tend to configure entries, it is generally the framework that fetch
+entries to build the application.
+
+This is why this interface focuses only on how entries can be fetched from a container.
+
+## Interface name
+
+The interface name has been thoroughly discussed and was decided by a vote.
+
+The list of options considered with their respective votes are:
+
+- `ContainerInterface`: +8
+- `ProviderInterface`: +2
+- `LocatorInterface`: 0
+- `ReadableContainerInterface`: -5
+- `ServiceLocatorInterface`: -6
+- `ObjectFactory`: -6
+- `ObjectStore`: -8
+- `ConsumerInterface`: -9
+
+[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)
+
+The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1).
+
+## Interface methods
+
+The choice of which methods the interface would contain was made after a statistical analysis of existing containers.
+The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681).
+
+The summary of the analysis showed that:
+
+- all containers offer a method to get an entry by its id
+- a large majority name such method `get()`
+- for all containers, the `get()` method has 1 mandatory parameter of type string
+- some containers have an optional additional argument for `get()`, but it doesn't same the same purpose between containers
+- a large majority of the containers offer a method to test if it can return an entry by its id
+- a majority name such method `has()`
+- for all containers offering `has()`, the method has exactly 1 parameter of type string
+- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()`
+- a large majority of the containers don't implement `ArrayAccess`
+
+The question of whether to include methods to define entries has been discussed in
+[issue #1](https://github.com/container-interop/container-interop/issues/1).
+It has been judged that such methods do not belong in the interface described here because it is out of its scope
+(see the "Goal" section).
+
+As a result, the `ContainerInterface` contains two methods:
+
+- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found.
+- `has()`, returning a boolean, with one mandatory string parameter.
+
+### Number of parameters in `get()` method
+
+While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with
+existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters
+as long as they are optional, because the implementation *does* satisfy the interface.
+
+This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6).
+
+### Type of the `$id` parameter
+
+The type of the `$id` parameter in `get()` and `has()` has been discussed in
+[issue #6](https://github.com/container-interop/container-interop/issues/6).
+While `string` is used in all the containers that were analyzed, it was suggested that allowing
+anything (such as objects) could allow containers to offer a more advanced query API.
+
+An example given was to use the container as an object builder. The `$id` parameter would then be an
+object that would describe how to create an instance.
+
+The conclusion of the discussion was that this was beyond the scope of getting entries from a container without
+knowing how the container provided them, and it was more fit for a factory.
+
+## Contributors
+
+Are listed here all people that contributed in the discussions or votes, by alphabetical order:
+
+- [Amy Stephen](https://github.com/AmyStephen)
+- [David Négrier](https://github.com/moufmouf)
+- [Don Gilbert](https://github.com/dongilbert)
+- [Jason Judge](https://github.com/judgej)
+- [Jeremy Lindblom](https://github.com/jeremeamia)
+- [Marco Pivetta](https://github.com/Ocramius)
+- [Matthieu Napoli](https://github.com/mnapoli)
+- [Paul M. Jones](https://github.com/pmjones)
+- [Stephan Hochdörfer](https://github.com/shochdoerfer)
+- [Taylor Otwell](https://github.com/taylorotwell)
+
+## Relevant links
+
+- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php)
+- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed)
+- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface.md b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/ContainerInterface.md
new file mode 100644 (file)
index 0000000..9f60967
--- /dev/null
@@ -0,0 +1,153 @@
+Container interface
+===================
+
+This document describes a common interface for dependency injection containers.
+
+The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
+container to obtain objects and parameters (called *entries* in the rest of this document).
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The word `implementor` in this document is to be interpreted as someone
+implementing the `ContainerInterface` in a depency injection-related library or framework.
+Users of dependency injections containers (DIC) are refered to as `user`.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Specification
+-----------------
+
+### 1.1 Basics
+
+- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`.
+
+- `get` takes one mandatory parameter: an entry identifier. It MUST be a string.
+  A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier
+  is not known to the container. Two successive calls to `get` with the same
+  identifier SHOULD return the same value. However, depending on the `implementor`
+  design and/or `user` configuration, different values might be returned, so
+  `user` SHOULD NOT rely on getting the same value on 2 successive calls.
+  While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations
+  MAY accept additional optional parameters.
+  
+- `has` takes one unique parameter: an entry identifier. It MUST return `true`
+  if an entry identifier is known to the container and `false` if it is not.
+  
+### 1.2 Exceptions
+
+Exceptions directly thrown by the container MUST implement the 
+[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php).
+
+A call to the `get` method with a non-existing id should throw a
+[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php).
+
+### 1.3 Additional features
+
+This section describes additional features that MAY be added to a container. Containers are not 
+required to implement these features to respect the ContainerInterface.
+
+#### 1.3.1 Delegate lookup feature
+
+The goal of the *delegate lookup* feature is to allow several containers to share entries. 
+Containers implementing this feature can perform dependency lookups in other containers.
+
+Containers implementing this feature will offer a greater lever of interoperability 
+with other containers. Implementation of this feature is therefore RECOMMENDED.
+
+A container implementing this feature:
+
+- MUST implement the `ContainerInterface`
+- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, 
+  or any possible way). The delegate container MUST implement the `ContainerInterface`.
+
+When a container is configured to use a delegate container for dependencies:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container. 
+  If the entry is not part of the container, an exception should be thrown 
+  (as requested by the `ContainerInterface`).
+- Calls to the `has` method should only return `true` if the entry is part of the container.
+  If the entry is not part of the container, `false` should be returned.
+- If the fetched entry has dependencies, **instead** of performing 
+  the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup 
+into the same container (or another container) instead of the delegate container.
+
+2. Package
+----------
+
+The interfaces and classes described as well as relevant exception are provided as part of the
+[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package.
+
+3. `Interop\Container\ContainerInterface`
+-----------------------------------------
+
+```php
+<?php
+namespace Interop\Container;
+
+use Interop\Container\Exception\ContainerException;
+use Interop\Container\Exception\NotFoundException;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface
+{
+    /**
+     * Finds an entry of the container by its identifier and returns it.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @throws NotFoundException  No entry was found for this identifier.
+     * @throws ContainerException Error while retrieving the entry.
+     *
+     * @return mixed Entry.
+     */
+    public function get($id);
+
+    /**
+     * Returns true if the container can return an entry for the given identifier.
+     * Returns false otherwise.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @return boolean
+     */
+    public function has($id);
+}
+```
+
+4. `Interop\Container\Exception\ContainerException`
+---------------------------------------------------
+
+```php
+<?php
+namespace Interop\Container\Exception;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerException
+{
+}
+```
+
+5. `Interop\Container\Exception\NotFoundException`
+---------------------------------------------------
+
+```php
+<?php
+namespace Interop\Container\Exception;
+
+/**
+ * No entry was found in the container.
+ */
+interface NotFoundException extends ContainerException
+{
+}
+```
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup-meta.md b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup-meta.md
new file mode 100644 (file)
index 0000000..d21ebf9
--- /dev/null
@@ -0,0 +1,259 @@
+Delegate lookup feature Meta Document
+=====================================
+
+1. Summary
+----------
+
+This document describes the *delegate lookup feature*.
+Containers are not required to implement this feature to respect the `ContainerInterface`.
+However, containers implementing this feature will offer a greater lever of interoperability
+with other containers, allowing multiple containers to share entries in the same application.
+Implementation of this feature is therefore recommanded.
+
+2. Why Bother?
+--------------
+
+The [`ContainerInterface`](../src/Interop/Container/ContainerInterface.php) ([meta doc](ContainerInterface.md))
+standardizes how frameworks and libraries make use of a container to obtain objects and parameters.
+
+By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface`
+could work with any compatible container.
+That would allow end users to choose their own container based on their own preferences.
+
+The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same
+application. For instance, this is what the [CompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php) 
+class of [Acclimate](https://github.com/jeremeamia/acclimate-container) is designed for:
+
+![Side by side containers](images/side_by_side_containers.png)
+
+However, an instance in container 1 cannot reference an instance in container 2.
+
+It would be better if an instance of container 1 could reference an instance in container 2,
+and the opposite should be true. 
+
+![Interoperating containers](images/interoperating_containers.png)
+
+In the sample above, entry 1 in container 1 is referencing entry 3 in container 2.
+
+3. Scope
+--------
+
+### 3.1 Goals
+
+The goal of the *delegate lookup* feature is to allow several containers to share entries.
+
+4. Approaches
+-------------
+
+### 4.1 Chosen Approach
+
+Containers implementing this feature can perform dependency lookups in other containers.
+
+A container implementing this feature:
+
+- must implement the `ContainerInterface`
+- must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any
+possible way). The *delegate container* must implement the `ContainerInterface`.
+
+When a *delegate container* is configured on a container:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container.
+If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`).
+- Calls to the `has` method should only return *true* if the entry is part of the container.
+If the entry is not part of the container, *false* should be returned.
+ - Finally, the important part: if the entry we are fetching has dependencies,
+**instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup into 
+the same container (or another container) instead of the delegate container.
+
+### 4.2 Typical usage
+
+The *delegate container* will usually be a composite container. A composite container is a container that
+contains several other containers. When performing a lookup on a composite container, the inner containers are 
+queried until one container returns an entry.
+An inner container implementing the *delegate lookup feature* will return entries it contains, but if these
+entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving
+a chance to all containers to answer.
+
+Interestingly enough, the order in which containers are added in the composite container matters. Indeed,
+the first containers to be added in the composite container can "override" the entries of containers with
+lower priority.
+
+![Containers priority](images/priority.png)
+
+In the example above, "container 2" contains a controller "myController" and the controller is referencing an 
+"entityManager" entry. "Container 1" contains also an entry named "entityManager".
+Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take 
+in charge the instanciation of both entries.
+
+However, using the *delegate lookup* feature, here is what happens when we ask the composite controller for the 
+"myController" instance:
+
+- The composite controller asks container 1 if if contains the "myController" instance. The answer is no.
+- The composite controller asks container 2 if if contains the "myController" instance. The answer is yes.
+- The composite controller performs a `get` call on container 2 for the "myController" instance.
+- Container 2 sees that "myController" has a dependency on "entityManager".
+- Container 2 delegates the lookup of "entityManager" to the composite controller.
+- The composite controller asks container 1 if if contains the "entityManager" instance. The answer is yes.
+- The composite controller performs a `get` call on container 1 for the "entityManager" instance.
+
+In the end, we get a controller instanciated by container 2 that references an entityManager instanciated
+by container 1.
+
+### 4.3 Alternative: the fallback strategy
+
+The first proposed approach we tried was to perform all the lookups in the "local" container,
+and if a lookup fails in the container, to use the delegate container. In this scenario, the
+delegate container is used in "fallback" mode.
+
+This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1).
+It was also discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and
+[here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631).
+
+Problems with this strategy:
+
+- Heavy problem regarding infinite loops
+- Unable to overload a container entry with the delegate container entry
+
+### 4.4 Alternative: force implementing an interface
+
+The first proposed approach was to develop a `ParentAwareContainerInterface` interface.
+It was proposed here: https://github.com/container-interop/container-interop/pull/8
+
+The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of
+a `setParentContainter` method:
+
+```php
+interface ParentAwareContainerInterface extends ReadableContainerInterface {
+    /**
+     * Sets the parent container associated to that container. This container will call
+     * the parent container to fetch dependencies.
+     *
+     * @param ContainerInterface $container
+     */
+    public function setParentContainer(ContainerInterface $container);
+}
+```
+
+The interface idea was first questioned by @Ocramius [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
+@Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation
+details on the class implementing the interface. 
+Then @mnapoli made a proposal for a "convention" [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079),
+this idea was further discussed until all participants in the discussion agreed to remove the interface idea
+and replace it with a "standard" feature.
+
+**Pros:**
+
+If we had had an interface, we could have delegated the registration of the delegate/composite container to the
+the delegate/composite container itself.
+For instance:
+
+```php
+$containerA = new ContainerA();
+$containerB = new ContainerB();
+
+$compositeContainer = new CompositeContainer([$containerA, $containerB]);
+
+// The call to 'setParentContainer' is delegated to the CompositeContainer
+// It is not the responsibility of the user anymore.
+class CompositeContainer {
+       ...
+       
+       public function __construct($containers) {
+               foreach ($containers as $container) {
+                       if ($container instanceof ParentAwareContainerInterface) {
+                               $container->setParentContainer($this);
+                       }
+               }
+               ...
+       }
+}
+
+``` 
+
+**Cons:**
+
+Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
+Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments,
+and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface.
+
+### 4.4 Alternative: no exception case for delegate lookups
+
+Originally, the proposed wording for delegate lookup calls was:
+
+> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself.
+
+This was later replaced by:
+
+> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
+>
+> It is however allowed for containers to provide exception cases for special entries, and a way to lookup 
+> into the same container (or another container) instead of the delegate container.
+
+Exception cases have been allowed to avoid breaking dependencies with some services that must be provided
+by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235
+
+### 4.5 Alternative: having one of the containers act as the composite container
+
+In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to
+add another DI container to this container. Most of the time, the "big" framework will be responsible for
+creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted,
+the "big" framework will not be aware of the existence of a composite container that it should use instead
+of its own container.
+
+For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container
+to make it act as a composite container.
+
+This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194) 
+and [here](http://mouf-php.com/container-interop-whats-next#solution4).
+
+This was implemented in Symfony 2 using:
+
+- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0)
+- [framework interop](https://github.com/mnapoli/framework-interop/)
+
+This was implemented in Silex using:
+
+- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di)
+
+Having a container act as the composite container is not part of the delegate lookup standard because it is
+simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop
+play nice with other DI containers.
+
+
+5. Implementations
+------------------
+
+The following projects already implement the delegate lookup feature:
+
+- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120)
+- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72)
+- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62)
+
+6. People
+---------
+
+Are listed here all people that contributed in the discussions, by alphabetical order:
+
+- [Alexandru Pătrănescu](https://github.com/drealecs)
+- [Ben Peachey](https://github.com/potherca)
+- [David Négrier](https://github.com/moufmouf)
+- [Jeremy Lindblom](https://github.com/jeremeamia)
+- [Marco Pivetta](https://github.com/Ocramius)
+- [Matthieu Napoli](https://github.com/mnapoli)
+- [Nelson J Morais](https://github.com/njasm)
+- [Phil Sturgeon](https://github.com/philsturgeon)
+- [Stephan Hochdörfer](https://github.com/shochdoerfer)
+
+7. Relevant Links
+-----------------
+
+_**Note:** Order descending chronologically._
+
+- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20)
+- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8)
+- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next)
+
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup.md b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/Delegate-lookup.md
new file mode 100644 (file)
index 0000000..04eb3ae
--- /dev/null
@@ -0,0 +1,60 @@
+Delegate lookup feature
+=======================
+
+This document describes a standard for dependency injection containers.
+
+The goal set by the *delegate lookup* feature is to allow several containers to share entries. 
+Containers implementing this feature can perform dependency lookups in other containers.
+
+Containers implementing this feature will offer a greater lever of interoperability 
+with other containers. Implementation of this feature is therefore RECOMMENDED.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The word `implementor` in this document is to be interpreted as someone
+implementing the delegate lookup feature in a dependency injection-related library or framework.
+Users of dependency injections containers (DIC) are refered to as `user`.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Vocabulary
+-------------
+
+In a dependency injection container, the container is used to fetch entries.
+Entries can have dependencies on other entries. Usually, these other entries are fetched by the container.
+
+The *delegate lookup* feature is the ability for a container to fetch dependencies in
+another container. In the rest of the document, the word "container" will reference the container
+implemented by the implementor. The word "delegate container" will reference the container we are
+fetching the dependencies from.
+
+2. Specification
+----------------
+
+A container implementing the *delegate lookup* feature:
+
+- MUST implement the [`ContainerInterface`](ContainerInterface.md)
+- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, 
+  or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md).
+
+When a container is configured to use a delegate container for dependencies:
+
+- Calls to the `get` method should only return an entry if the entry is part of the container.
+  If the entry is not part of the container, an exception should be thrown 
+  (as requested by the [`ContainerInterface`](ContainerInterface.md)).
+- Calls to the `has` method should only return `true` if the entry is part of the container.
+  If the entry is not part of the container, `false` should be returned.
+- If the fetched entry has dependencies, **instead** of performing 
+  the dependency lookup in the container, the lookup is performed on the *delegate container*.
+
+Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself.
+
+It is however allowed for containers to provide exception cases for special entries, and a way to lookup 
+into the same container (or another container) instead of the delegate container.
+
+3. Package / Interface
+----------------------
+
+This feature is not tied to any code, interface or package.
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/interoperating_containers.png b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/interoperating_containers.png
new file mode 100644 (file)
index 0000000..9c672e1
Binary files /dev/null and b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/interoperating_containers.png differ
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/priority.png b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/priority.png
new file mode 100644 (file)
index 0000000..5760dc7
Binary files /dev/null and b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/priority.png differ
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/side_by_side_containers.png b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/side_by_side_containers.png
new file mode 100644 (file)
index 0000000..24ca03c
Binary files /dev/null and b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/docs/images/side_by_side_containers.png differ
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/ContainerInterface.php b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/ContainerInterface.php
new file mode 100644 (file)
index 0000000..dee5ffa
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Interop\Container;
+
+use Interop\Container\Exception\ContainerException;
+use Interop\Container\Exception\NotFoundException;
+
+/**
+ * Describes the interface of a container that exposes methods to read its entries.
+ */
+interface ContainerInterface
+{
+    /**
+     * Finds an entry of the container by its identifier and returns it.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @throws NotFoundException  No entry was found for this identifier.
+     * @throws ContainerException Error while retrieving the entry.
+     *
+     * @return mixed Entry.
+     */
+    public function get($id);
+
+    /**
+     * Returns true if the container can return an entry for the given identifier.
+     * Returns false otherwise.
+     *
+     * @param string $id Identifier of the entry to look for.
+     *
+     * @return boolean
+     */
+    public function has($id);
+}
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php
new file mode 100644 (file)
index 0000000..d867434
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Interop\Container\Exception;
+
+/**
+ * Base interface representing a generic exception in a container.
+ */
+interface ContainerException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php b/wcfsetup/install/files/lib/system/api/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php
new file mode 100644 (file)
index 0000000..714d7a9
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace Interop\Container\Exception;
+
+/**
+ * No entry was found in the container.
+ */
+interface NotFoundException extends ContainerException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/CONTRIBUTING.md b/wcfsetup/install/files/lib/system/api/php-di/invoker/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..852fd1a
--- /dev/null
@@ -0,0 +1,15 @@
+# Contributing
+
+First of all, **thank you** for contributing!
+
+Here are a few rules to follow in order to ease code reviews and merging:
+
+- follow [PSR-1](http://www.php-fig.org/psr/1/) and [PSR-2](http://www.php-fig.org/psr/2/)
+- run the test suite
+- write (or update) unit tests when applicable
+- write documentation for new features
+- use [commit messages that make sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
+
+One may ask you to [squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) too. This is used to "clean" your pull request before merging it (we don't want commits such as `fix tests`, `fix 2`, `fix 3`, etc.).
+
+When creating your pull request on GitHub, please write a description which gives the context and/or explains why you are creating it.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/LICENSE b/wcfsetup/install/files/lib/system/api/php-di/invoker/LICENSE
new file mode 100644 (file)
index 0000000..b2b0416
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Matthieu Napoli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/README.md b/wcfsetup/install/files/lib/system/api/php-di/invoker/README.md
new file mode 100644 (file)
index 0000000..19adfe5
--- /dev/null
@@ -0,0 +1,234 @@
+# Invoker
+
+Generic and extensible callable invoker.
+
+[![Build Status](https://img.shields.io/travis/PHP-DI/Invoker.svg?style=flat-square)](https://travis-ci.org/PHP-DI/Invoker)
+[![Coverage Status](https://img.shields.io/coveralls/PHP-DI/Invoker/master.svg?style=flat-square)](https://coveralls.io/r/PHP-DI/Invoker?branch=master)
+[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/PHP-DI/Invoker.svg?style=flat-square)](https://scrutinizer-ci.com/g/PHP-DI/Invoker/?branch=master)
+[![Latest Version](https://img.shields.io/github/release/PHP-DI/invoker.svg?style=flat-square)](https://packagist.org/packages/PHP-DI/invoker)
+
+## Why?
+
+Who doesn't need an over-engineered `call_user_func()`?
+
+### Named parameters
+
+Does this [Silex](http://silex.sensiolabs.org) example look familiar:
+
+```php
+$app->get('/project/{project}/issue/{issue}', function ($project, $issue) {
+    // ...
+});
+```
+
+Or this command defined with [Silly](https://github.com/mnapoli/silly#usage):
+
+```php
+$app->command('greet [name] [--yell]', function ($name, $yell) {
+    // ...
+});
+```
+
+Same pattern in [Slim](http://www.slimframework.com):
+
+```php
+$app->get('/hello/:name', function ($name) {
+    // ...
+});
+```
+
+You get the point. These frameworks invoke the controller/command/handler using something akin to named parameters: whatever the order of the parameters, they are matched by their name.
+
+**This library allows to invoke callables with named parameters in a generic and extensible way.**
+
+### Dependency injection
+
+Anyone familiar with AngularJS is familiar with how dependency injection is performed:
+
+```js
+angular.controller('MyController', ['dep1', 'dep2', function(dep1, dep2) {
+    // ...
+}]);
+```
+
+In PHP we find this pattern again in some frameworks and DI containers with partial to full support. For example in Silex you can type-hint the application to get it injected, but it only works with `Silex\Application`:
+
+```php
+$app->get('/hello/{name}', function (Silex\Application $app, $name) {
+    // ...
+});
+```
+
+In Silly, it only works with `OutputInterface` to inject the application output:
+
+```php
+$app->command('greet [name]', function ($name, OutputInterface $output) {
+    // ...
+});
+```
+
+[PHP-DI](http://php-di.org/doc/container.html) provides a way to invoke a callable and resolve all dependencies from the container using type-hints:
+
+```php
+$container->call(function (Logger $logger, EntityManager $em) {
+    // ...
+});
+```
+
+**This library provides clear extension points to let frameworks implement any kind of dependency injection support they want.**
+
+### TL/DR
+
+In short, this library is meant to be a base building block for calling a function with named parameters and/or dependency injection.
+
+## Installation
+
+```sh
+$ composer require PHP-DI/invoker
+```
+
+## Usage
+
+### Default behavior
+
+By default the `Invoker` can call using named parameters:
+
+```php
+$invoker = new Invoker\Invoker;
+
+$invoker->call(function () {
+    echo 'Hello world!';
+});
+
+// Simple parameter array
+$invoker->call(function ($name) {
+    echo 'Hello ' . $name;
+}, ['John']);
+
+// Named parameters
+$invoker->call(function ($name) {
+    echo 'Hello ' . $name;
+}, [
+    'name' => 'John'
+]);
+
+// Use the default value
+$invoker->call(function ($name = 'world') {
+    echo 'Hello ' . $name;
+});
+
+// Invoke any PHP callable
+$invoker->call(['MyClass', 'myStaticMethod']);
+
+// Using Class::method syntax
+$invoker->call('MyClass::myStaticMethod');
+```
+
+Dependency injection in parameters is supported but needs to be configured with your container. Read on or jump to [*Built-in support for dependency injection*](#built-in-support-for-dependency-injection) if you are impatient.
+
+Additionally, callables can also be resolved from your container. Read on or jump to [*Resolving callables from a container*](#resolving-callables-from-a-container) if you are impatient.
+
+### Parameter resolvers
+
+Extending the behavior of the `Invoker` is easy and is done by implementing a [`ParameterResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php).
+
+This is explained in details the [Parameter resolvers documentation](doc/parameter-resolvers.md).
+
+#### Built-in support for dependency injection
+
+Rather than have you re-implement support for dependency injection with different containers every time, this package ships with 2 optional resolvers:
+
+- [`TypeHintContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/TypeHintContainerResolver.php)
+
+    This resolver will inject container entries by searching for the class name using the type-hint:
+
+    ```php
+    $invoker->call(function (Psr\Logger\LoggerInterface $logger) {
+        // ...
+    });
+    ```
+
+    In this example it will `->get('Psr\Logger\LoggerInterface')` from the container and inject it.
+
+    This resolver is only useful if you store objects in your container using the class (or interface) name. Silex or Symfony for example store services under a custom name (e.g. `twig`, `db`, etc.) instead of the class name: in that case use the resolver shown below.
+
+- [`ParameterNameContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/ParameterNameContainerResolver.php)
+
+    This resolver will inject container entries by searching for the name of the parameter:
+
+    ```php
+    $invoker->call(function ($twig) {
+        // ...
+    });
+    ```
+
+    In this example it will `->get('twig')` from the container and inject it.
+
+These resolvers can work with any dependency injection container compliant with [container-interop](https://github.com/container-interop/container-interop). If you container is not compliant you can use the [Acclimate](https://github.com/jeremeamia/acclimate-container) package.
+
+Setting up those resolvers is simple:
+
+```php
+// $container must be an instance of Interop\Container\ContainerInterface
+$container = ...
+
+$containerResolver = new TypeHintContainerResolver($container);
+// or
+$containerResolver = new ParameterNameContainerResolver($container);
+
+$invoker = new Invoker\Invoker;
+// Register it before all the other parameter resolvers
+$invoker->getParameterResolver()->prependResolver($containerResolver);
+```
+
+You can also register both resolvers at the same time if you wish by prepending both. Implementing support for more tricky things is easy and up to you!
+
+### Resolving callables from a container
+
+The `Invoker` can be wired to your DI container to resolve the callables.
+
+For example with an invokable class:
+
+```php
+class MyHandler
+{
+    public function __invoke()
+    {
+        // ...
+    }
+}
+
+// By default this doesn't work: an instance of the class should be provided
+$invoker->call('MyHandler');
+
+// If we set up the container to use
+$invoker = new Invoker\Invoker(null, $container);
+// Now 'MyHandler' is resolved using the container!
+$invoker->call('MyHandler');
+```
+
+The same works for a class method:
+
+```php
+class WelcomeController
+{
+    public function home()
+    {
+        // ...
+    }
+}
+
+// By default this doesn't work: home() is not a static method
+$invoker->call(['WelcomeController', 'home']);
+
+// If we set up the container to use
+$invoker = new Invoker\Invoker(null, $container);
+// Now 'WelcomeController' is resolved using the container!
+$invoker->call(['WelcomeController', 'home']);
+// Alternatively we can use the Class::method syntax
+$invoker->call('WelcomeController::home');
+```
+
+That feature can be used as the base building block for a framework's dispatcher.
+
+Again, any [container-interop](https://github.com/container-interop/container-interop) compliant container can be provided, and [Acclimate](https://github.com/jeremeamia/acclimate-container) can be used for incompatible containers.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/composer.json b/wcfsetup/install/files/lib/system/api/php-di/invoker/composer.json
new file mode 100644 (file)
index 0000000..3f0c041
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    "name": "php-di/invoker",
+    "description": "Generic and extensible callable invoker",
+    "keywords": ["invoker", "dependency-injection", "dependency", "injection", "callable", "invoke"],
+    "homepage": "https://github.com/PHP-DI/Invoker",
+    "license": "MIT",
+    "type": "library",
+    "autoload": {
+        "psr-4": {
+            "Invoker\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Invoker\\Test\\": "tests/"
+        }
+    },
+    "require": {
+        "container-interop/container-interop": "~1.1"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.5",
+        "athletic/athletic": "~0.1.8"
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/doc/parameter-resolvers.md b/wcfsetup/install/files/lib/system/api/php-di/invoker/doc/parameter-resolvers.md
new file mode 100644 (file)
index 0000000..bfccb7f
--- /dev/null
@@ -0,0 +1,109 @@
+# Parameter resolvers
+
+Extending the behavior of the `Invoker` is easy and is done by implementing a [`ParameterResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php):
+
+```php
+interface ParameterResolver
+{
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    );
+}
+```
+
+- `$providedParameters` contains the parameters provided by the user when calling `$invoker->call($callable, $parameters)`
+- `$resolvedParameters` contains parameters that have already been resolved by other parameter resolvers
+
+An `Invoker` can chain multiple parameter resolvers to mix behaviors, e.g. you can mix "named parameters" support with "dependency injection" support. This is why a `ParameterResolver` should skip parameters that are already resolved in `$resolvedParameters`.
+
+Here is an implementation example for dumb dependency injection that creates a new instance of the classes type-hinted:
+
+```php
+class MyParameterResolver implements ParameterResolver
+{
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        foreach ($reflection->getParameters() as $index => $parameter) {
+            if (array_key_exists($index, $resolvedParameters)) {
+                // Skip already resolved parameters
+                continue;
+            }
+
+            $class = $parameter->getClass();
+
+            if ($class) {
+                $resolvedParameters[$index] = $class->newInstance();
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
+```
+
+To use it:
+
+```php
+$invoker = new Invoker\Invoker(new MyParameterResolver);
+
+$invoker->call(function (ArticleManager $articleManager) {
+    $articleManager->publishArticle('Hello world', 'This is the article content.');
+});
+```
+
+A new instance of `ArticleManager` will be created by our parameter resolver.
+
+## Chaining parameter resolvers
+
+The fun starts to happen when we want to add support for many things:
+
+- named parameters
+- dependency injection for type-hinted parameters
+- ...
+
+This is where we should use the [`ResolverChain`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ResolverChain.php). This resolver implements the [Chain of responsibility](http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) design pattern.
+
+For example the default chain is:
+
+```php
+$parameterResolver = new ResolverChain([
+    new NumericArrayResolver,
+    new AssociativeArrayResolver,
+    new DefaultValueResolver,
+]);
+```
+
+It allows to support even the weirdest use cases like:
+
+```php
+$parameters = [];
+
+// First parameter will receive "Welcome"
+$parameters[] = 'Welcome';
+
+// Parameter named "content" will receive "Hello world!"
+$parameters['content'] = 'Hello world!';
+
+// $published is not defined so it will use its default value
+$invoker->call(function ($title, $content, $published = true) {
+    // ...
+}, $parameters);
+```
+
+We can put our custom parameter resolver in the list and created a super-duper invoker that also supports basic dependency injection:
+
+```php
+$parameterResolver = new ResolverChain([
+    new MyParameterResolver, // Our resolver is at the top for highest priority
+    new NumericArrayResolver,
+    new AssociativeArrayResolver,
+    new DefaultValueResolver,
+]);
+
+$invoker = new Invoker\Invoker($parameterResolver);
+```
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/CallableResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/CallableResolver.php
new file mode 100644 (file)
index 0000000..eab8c0e
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+
+namespace Invoker;
+
+use Interop\Container\ContainerInterface;
+use Invoker\Exception\NotCallableException;
+
+/**
+ * Resolves a callable from a container.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class CallableResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Resolve the given callable into a real PHP callable.
+     *
+     * @param callable|string|array $callable
+     *
+     * @return callable Real PHP callable.
+     *
+     * @throws NotCallableException
+     */
+    public function resolve($callable)
+    {
+        if (is_string($callable) && strpos($callable, '::') !== false) {
+            $callable = explode('::', $callable, 2);
+        }
+
+        $callable = $this->resolveFromContainer($callable);
+
+        if (! is_callable($callable)) {
+            throw new NotCallableException(sprintf(
+                '%s is not a callable',
+                is_object($callable) ? 'Instance of ' . get_class($callable) : var_export($callable, true)
+            ));
+        }
+
+        return $callable;
+    }
+
+    /**
+     * @param callable|string|array $callable
+     * @return callable
+     * @throws NotCallableException
+     */
+    private function resolveFromContainer($callable)
+    {
+        // Shortcut for a very common use case
+        if ($callable instanceof \Closure) {
+            return $callable;
+        }
+
+        $isStaticCallToNonStaticMethod = false;
+
+        // If it's already a callable there is nothing to do
+        if (is_callable($callable)) {
+            $isStaticCallToNonStaticMethod = $this->isStaticCallToNonStaticMethod($callable);
+            if (! $isStaticCallToNonStaticMethod) {
+                return $callable;
+            }
+        }
+
+        // The callable is a container entry name
+        if (is_string($callable)) {
+            if ($this->container->has($callable)) {
+                return $this->container->get($callable);
+            } else {
+                throw new NotCallableException(sprintf(
+                    '"%s" is neither a callable nor a valid container entry',
+                    $callable
+                ));
+            }
+        }
+
+        // The callable is an array whose first item is a container entry name
+        // e.g. ['some-container-entry', 'methodToCall']
+        if (is_array($callable) && is_string($callable[0])) {
+            if ($this->container->has($callable[0])) {
+                // Replace the container entry name by the actual object
+                $callable[0] = $this->container->get($callable[0]);
+                return $callable;
+            } elseif ($isStaticCallToNonStaticMethod) {
+                throw new NotCallableException(sprintf(
+                    'Cannot call %s::%s() because %s() is not a static method and "%s" is not a container entry',
+                    $callable[0],
+                    $callable[1],
+                    $callable[1],
+                    $callable[0]
+                ));
+            } else {
+                throw new NotCallableException(sprintf(
+                    'Cannot call %s on %s because it is not a class nor a valid container entry',
+                    $callable[1],
+                    $callable[0]
+                ));
+            }
+        }
+
+        // Unrecognized stuff, we let it fail later
+        return $callable;
+    }
+
+    /**
+     * Check if the callable represents a static call to a non-static method.
+     *
+     * @param mixed $callable
+     * @return bool
+     */
+    private function isStaticCallToNonStaticMethod($callable)
+    {
+        if (is_array($callable) && is_string($callable[0])) {
+            list($class, $method) = $callable;
+            $reflection = new \ReflectionMethod($class, $method);
+
+            return ! $reflection->isStatic();
+        }
+
+        return false;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/InvocationException.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/InvocationException.php
new file mode 100644 (file)
index 0000000..a5f6ffc
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace Invoker\Exception;
+
+/**
+ * Impossible to invoke the callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class InvocationException extends \Exception
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotCallableException.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotCallableException.php
new file mode 100644 (file)
index 0000000..1f3e352
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace Invoker\Exception;
+
+/**
+ * The given callable is not actually callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class NotCallableException extends InvocationException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotEnoughParametersException.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Exception/NotEnoughParametersException.php
new file mode 100644 (file)
index 0000000..1be4eb9
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace Invoker\Exception;
+
+/**
+ * Not enough parameters could be resolved to invoke the callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class NotEnoughParametersException extends InvocationException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Invoker.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Invoker.php
new file mode 100644 (file)
index 0000000..ffdcd0b
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+namespace Invoker;
+
+use Interop\Container\ContainerInterface;
+use Invoker\Exception\NotCallableException;
+use Invoker\Exception\NotEnoughParametersException;
+use Invoker\ParameterResolver\AssociativeArrayResolver;
+use Invoker\ParameterResolver\DefaultValueResolver;
+use Invoker\ParameterResolver\NumericArrayResolver;
+use Invoker\ParameterResolver\ParameterResolver;
+use Invoker\ParameterResolver\ResolverChain;
+use Invoker\Reflection\CallableReflection;
+
+/**
+ * Invoke a callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class Invoker implements InvokerInterface
+{
+    /**
+     * @var CallableResolver|null
+     */
+    private $callableResolver;
+
+    /**
+     * @var ParameterResolver
+     */
+    private $parameterResolver;
+
+    /**
+     * @var ContainerInterface|null
+     */
+    private $container;
+
+    public function __construct(ParameterResolver $parameterResolver = null, ContainerInterface $container = null)
+    {
+        $this->parameterResolver = $parameterResolver ?: $this->createParameterResolver();
+        $this->container = $container;
+
+        if ($container) {
+            $this->callableResolver = new CallableResolver($container);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function call($callable, array $parameters = array())
+    {
+        if ($this->callableResolver) {
+            $callable = $this->callableResolver->resolve($callable);
+        }
+
+        if (! is_callable($callable)) {
+            throw new NotCallableException(sprintf(
+                '%s is not a callable',
+                is_object($callable) ? 'Instance of ' . get_class($callable) : var_export($callable, true)
+            ));
+        }
+
+        $callableReflection = CallableReflection::create($callable);
+
+        $args = $this->parameterResolver->getParameters($callableReflection, $parameters, array());
+
+        // Sort by array key because call_user_func_array ignores numeric keys
+        ksort($args);
+
+        // Check all parameters are resolved
+        $diff = array_diff_key($callableReflection->getParameters(), $args);
+        if (! empty($diff)) {
+            /** @var \ReflectionParameter $parameter */
+            $parameter = reset($diff);
+            throw new NotEnoughParametersException(sprintf(
+                'Unable to invoke the callable because no value was given for parameter %d ($%s)',
+                $parameter->getPosition() + 1,
+                $parameter->name
+            ));
+        }
+
+        return call_user_func_array($callable, $args);
+    }
+
+    /**
+     * Create the default parameter resolver.
+     *
+     * @return ParameterResolver
+     */
+    private function createParameterResolver()
+    {
+        return new ResolverChain(array(
+            new NumericArrayResolver,
+            new AssociativeArrayResolver,
+            new DefaultValueResolver,
+        ));
+    }
+
+    /**
+     * @return ParameterResolver By default it's a ResolverChain
+     */
+    public function getParameterResolver()
+    {
+        return $this->parameterResolver;
+    }
+
+    /**
+     * @return ContainerInterface|null
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+
+    /**
+     * @return CallableResolver|null Returns null if no container was given in the constructor.
+     */
+    public function getCallableResolver()
+    {
+        return $this->callableResolver;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/InvokerInterface.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/InvokerInterface.php
new file mode 100644 (file)
index 0000000..cd6980e
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace Invoker;
+
+use Invoker\Exception\InvocationException;
+use Invoker\Exception\NotCallableException;
+use Invoker\Exception\NotEnoughParametersException;
+
+/**
+ * Invoke a callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface InvokerInterface
+{
+    /**
+     * Call the given function using the given parameters.
+     *
+     * @param callable $callable   Function to call.
+     * @param array    $parameters Parameters to use.
+     *
+     * @return mixed Result of the function.
+     *
+     * @throws InvocationException Base exception class for all the sub-exceptions below.
+     * @throws NotCallableException
+     * @throws NotEnoughParametersException
+     */
+    public function call($callable, array $parameters = array());
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php
new file mode 100644 (file)
index 0000000..d51f2c7
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+namespace Invoker\ParameterResolver;
+
+use ReflectionFunctionAbstract;
+
+/**
+ * Tries to map an associative array (string-indexed) to the parameter names.
+ *
+ * E.g. `->call($callable, ['foo' => 'bar'])` will inject the string `'bar'`
+ * in the parameter named `$foo`.
+ *
+ * Parameters that are not indexed by a string are ignored.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AssociativeArrayResolver implements ParameterResolver
+{
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        $parameters = $reflection->getParameters();
+
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $parameters = array_diff_key($parameters, $resolvedParameters);
+        }
+
+        foreach ($parameters as $index => $parameter) {
+            if (array_key_exists($parameter->name, $providedParameters)) {
+                $resolvedParameters[$index] = $providedParameters[$parameter->name];
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php
new file mode 100644 (file)
index 0000000..983dea4
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+namespace Invoker\ParameterResolver\Container;
+
+use Interop\Container\ContainerInterface;
+use Invoker\ParameterResolver\ParameterResolver;
+use ReflectionFunctionAbstract;
+
+/**
+ * Inject entries from a DI container using the parameter names.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ParameterNameContainerResolver implements ParameterResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @param ContainerInterface $container The container to get entries from.
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        $parameters = $reflection->getParameters();
+
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $parameters = array_diff_key($parameters, $resolvedParameters);
+        }
+
+        foreach ($parameters as $index => $parameter) {
+            $name = $parameter->name;
+
+            if ($name && $this->container->has($name)) {
+                $resolvedParameters[$index] = $this->container->get($name);
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php
new file mode 100644 (file)
index 0000000..65a4079
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+namespace Invoker\ParameterResolver\Container;
+
+use Interop\Container\ContainerInterface;
+use Invoker\ParameterResolver\ParameterResolver;
+use ReflectionFunctionAbstract;
+
+/**
+ * Inject entries from a DI container using the type-hints.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class TypeHintContainerResolver implements ParameterResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @param ContainerInterface $container The container to get entries from.
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        $parameters = $reflection->getParameters();
+
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $parameters = array_diff_key($parameters, $resolvedParameters);
+        }
+
+        foreach ($parameters as $index => $parameter) {
+            $parameterClass = $parameter->getClass();
+
+            if ($parameterClass && $this->container->has($parameterClass->name)) {
+                $resolvedParameters[$index] = $this->container->get($parameterClass->name);
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php
new file mode 100644 (file)
index 0000000..6fa8f8c
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+namespace Invoker\ParameterResolver;
+
+use ReflectionException;
+use ReflectionFunctionAbstract;
+
+/**
+ * Finds the default value for a parameter, *if it exists*.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefaultValueResolver implements ParameterResolver
+{
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        $parameters = $reflection->getParameters();
+
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $parameters = array_diff_key($parameters, $resolvedParameters);
+        }
+
+        foreach ($parameters as $index => $parameter) {
+            /** @var \ReflectionParameter $parameter */
+            if ($parameter->isOptional()) {
+                try {
+                    $resolvedParameters[$index] = $parameter->getDefaultValue();
+                } catch (ReflectionException $e) {
+                    // Can't get default values from PHP internal classes and functions
+                }
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php
new file mode 100644 (file)
index 0000000..dc5f02c
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+namespace Invoker\ParameterResolver;
+
+use ReflectionFunctionAbstract;
+
+/**
+ * Simply returns all the values of the $providedParameters array that are
+ * indexed by the parameter position (i.e. a number).
+ *
+ * E.g. `->call($callable, ['foo', 'bar'])` will simply resolve the parameters
+ * to `['foo', 'bar']`.
+ *
+ * Parameters that are not indexed by a number (i.e. parameter position)
+ * will be ignored.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class NumericArrayResolver implements ParameterResolver
+{
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $providedParameters = array_diff_key($providedParameters, $resolvedParameters);
+        }
+
+        foreach ($providedParameters as $key => $value) {
+            if (is_int($key)) {
+                $resolvedParameters[$key] = $value;
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ParameterResolver.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ParameterResolver.php
new file mode 100644 (file)
index 0000000..39c7abc
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+namespace Invoker\ParameterResolver;
+
+use ReflectionFunctionAbstract;
+
+/**
+ * Resolves the parameters to use to call the callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface ParameterResolver
+{
+    /**
+     * Resolves the parameters to use to call the callable.
+     *
+     * `$resolvedParameters` contains parameters that have already been resolved.
+     *
+     * Each ParameterResolver must resolve parameters that are not already
+     * in `$resolvedParameters`. That allows to chain multiple ParameterResolver.
+     *
+     * @param ReflectionFunctionAbstract $reflection Reflection object for the callable.
+     * @param array $providedParameters Parameters provided by the caller.
+     * @param array $resolvedParameters Parameters resolved (indexed by parameter position).
+     *
+     * @return array
+     */
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    );
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ResolverChain.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/ParameterResolver/ResolverChain.php
new file mode 100644 (file)
index 0000000..d524746
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+namespace Invoker\ParameterResolver;
+
+use ReflectionFunctionAbstract;
+
+/**
+ * Dispatches the call to other resolvers until all parameters are resolved.
+ *
+ * Chain of responsibility pattern.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ResolverChain implements ParameterResolver
+{
+    /**
+     * @var ParameterResolver[]
+     */
+    private $resolvers = array();
+
+    public function __construct(array $resolvers = array())
+    {
+        $this->resolvers = $resolvers;
+    }
+
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        $reflectionParameters = $reflection->getParameters();
+
+        foreach ($this->resolvers as $resolver) {
+            $resolvedParameters = $resolver->getParameters(
+                $reflection,
+                $providedParameters,
+                $resolvedParameters
+            );
+
+            $diff = array_diff_key($reflectionParameters, $resolvedParameters);
+            if (empty($diff)) {
+                // Stop traversing: all parameters are resolved
+                return $resolvedParameters;
+            }
+        }
+
+        return $resolvedParameters;
+    }
+
+    /**
+     * Push a parameter resolver after the ones already registered.
+     *
+     * @param ParameterResolver $resolver
+     */
+    public function appendResolver(ParameterResolver $resolver)
+    {
+        $this->resolvers[] = $resolver;
+    }
+
+    /**
+     * Insert a parameter resolver before the ones already registered.
+     *
+     * @param ParameterResolver $resolver
+     */
+    public function prependResolver(ParameterResolver $resolver)
+    {
+        array_unshift($this->resolvers, $resolver);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Reflection/CallableReflection.php b/wcfsetup/install/files/lib/system/api/php-di/invoker/src/Reflection/CallableReflection.php
new file mode 100644 (file)
index 0000000..7835f44
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+namespace Invoker\Reflection;
+
+use Invoker\Exception\NotCallableException;
+
+/**
+ * Create a reflection object from a callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class CallableReflection
+{
+    /**
+     * @param callable $callable
+     *
+     * @return \ReflectionFunctionAbstract
+     *
+     * @throws NotCallableException
+     *
+     * TODO Use the `callable` type-hint once support for PHP 5.4 and up.
+     */
+    public static function create($callable)
+    {
+        // Closure
+        if ($callable instanceof \Closure) {
+            return new \ReflectionFunction($callable);
+        }
+
+        // Array callable
+        if (is_array($callable)) {
+            list($class, $method) = $callable;
+
+            return new \ReflectionMethod($class, $method);
+        }
+
+        // Callable object (i.e. implementing __invoke())
+        if (is_object($callable) && method_exists($callable, '__invoke')) {
+            return new \ReflectionMethod($callable, '__invoke');
+        }
+
+        // Callable class (i.e. implementing __invoke())
+        if (is_string($callable) && class_exists($callable) && method_exists($callable, '__invoke')) {
+            return new \ReflectionMethod($callable, '__invoke');
+        }
+
+        // Standard function
+        if (is_string($callable) && function_exists($callable)) {
+            return new \ReflectionFunction($callable);
+        }
+
+        throw new NotCallableException(sprintf(
+            '%s is not a callable',
+            is_string($callable) ? $callable : 'Instance of ' . get_class($callable)
+        ));
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/.coveralls.yml b/wcfsetup/install/files/lib/system/api/php-di/php-di/.coveralls.yml
new file mode 100644 (file)
index 0000000..bc71b62
--- /dev/null
@@ -0,0 +1,2 @@
+coverage_clover: clover.xml
+json_path: coveralls-upload.json
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/.gitattributes b/wcfsetup/install/files/lib/system/api/php-di/php-di/.gitattributes
new file mode 100644 (file)
index 0000000..362238c
--- /dev/null
@@ -0,0 +1,8 @@
+# .gitattributes
+tests/ export-ignore
+website/ export-ignore
+doc/ export-ignore
+news/ export-ignore
+
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/.gitignore b/wcfsetup/install/files/lib/system/api/php-di/php-di/.gitignore
new file mode 100644 (file)
index 0000000..b39e0cd
--- /dev/null
@@ -0,0 +1,9 @@
+/.idea/
+/vendor/
+/composer.phar
+/composer.lock
+/theme/
+/.couscous/
+/website/bower_components/
+/website/css/all.min.css
+/logo/
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/.travis.yml b/wcfsetup/install/files/lib/system/api/php-di/php-di/.travis.yml
new file mode 100644 (file)
index 0000000..1fed6a3
--- /dev/null
@@ -0,0 +1,26 @@
+language: php
+
+php:
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+  - hhvm
+
+matrix:
+  include:
+    - php: 5.4
+      env: dependencies=lowest
+
+before_script:
+  - composer self-update
+  - if [[ $(phpenv version-name) == '5.6' ]]; then composer require satooshi/php-coveralls:dev-master -n ; fi
+  - if [[ $(phpenv version-name) != '5.6' ]]; then composer install -n ; fi
+  - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --prefer-stable -n; fi;
+
+script:
+  - if [[ $(phpenv version-name) == '5.6' ]]; then phpunit --coverage-clover clover.xml ; fi
+  - if [[ $(phpenv version-name) != '5.6' ]]; then phpunit ; fi
+
+after_script:
+  - if [[ $(phpenv version-name) == '5.6' ]]; then php vendor/bin/coveralls -v ; fi
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/404.md b/wcfsetup/install/files/lib/system/api/php-di/php-di/404.md
new file mode 100644 (file)
index 0000000..c4e870f
--- /dev/null
@@ -0,0 +1,3 @@
+---
+layout: 404
+---
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/CONTRIBUTING.md b/wcfsetup/install/files/lib/system/api/php-di/php-di/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..6e3bd24
--- /dev/null
@@ -0,0 +1,52 @@
+# Contributing
+
+[![Build Status](https://travis-ci.org/PHP-DI/PHP-DI.png?branch=master)](https://travis-ci.org/PHP-DI/PHP-DI) [![Coverage Status](https://coveralls.io/repos/PHP-DI/PHP-DI/badge.png?branch=master)](https://coveralls.io/r/PHP-DI/PHP-DI?branch=master)
+
+PHP-DI is licensed under the MIT License.
+
+
+## Set up
+
+* Check out the sources using git or download them
+
+```bash
+$ git clone https://github.com/PHP-DI/PHP-DI.git
+```
+
+* Install the libraries using composer:
+
+```bash
+$ curl -s http://getcomposer.org/installer | php
+$ php composer.phar install
+```
+
+If you are running Windows or are having trouble, read [the official documentation](http://getcomposer.org/doc/00-intro.md#installation).
+
+
+## Run the tests
+
+The tests are run with [PHPUnit](http://www.phpunit.de/manual/current/en/installation.html):
+
+```bash
+$ phpunit
+```
+
+
+## Learning the internals
+
+Read the [How it works](doc/how-it-works.md) documentation.
+
+
+## What to do?
+
+- Add tests: pick up uncovered situations in the [code coverage report](https://coveralls.io/r/PHP-DI/PHP-DI)
+- Resolve issues: [issue list](https://github.com/PHP-DI/PHP-DI/issues)
+- Improve the documentation
+- â€¦
+
+
+## Coding style
+
+The code follows PSR0, PSR1 and [PSR2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
+
+Also, do not hesitate to add your name to the author list of a class in the docblock if you improve it.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/LICENSE b/wcfsetup/install/files/lib/system/api/php-di/php-di/LICENSE
new file mode 100644 (file)
index 0000000..f24e183
--- /dev/null
@@ -0,0 +1,18 @@
+PHP-DI - PHP Dependency Injection
+
+Copyright (C) Matthieu Napoli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/README.md b/wcfsetup/install/files/lib/system/api/php-di/php-di/README.md
new file mode 100644 (file)
index 0000000..7e40802
--- /dev/null
@@ -0,0 +1,21 @@
+---
+layout: home
+---
+
+PHP-DI is a Dependency Injection Container made for humans.
+
+[![Build Status](https://img.shields.io/travis/PHP-DI/PHP-DI.svg?style=flat-square)](https://travis-ci.org/PHP-DI/PHP-DI)
+[![HHVM Status](https://img.shields.io/hhvm/php-di/php-di.svg?style=flat-square)](http://hhvm.h4cc.de/package/php-di/php-di)
+[![Coverage Status](https://img.shields.io/coveralls/PHP-DI/PHP-DI/master.svg?style=flat-square)](https://coveralls.io/r/PHP-DI/PHP-DI?branch=master)
+[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/PHP-DI/PHP-DI.svg?style=flat-square)](https://scrutinizer-ci.com/g/PHP-DI/PHP-DI/?branch=master)
+[![Latest Version](https://img.shields.io/github/release/PHP-DI/PHP-DI.svg?style=flat-square)](https://packagist.org/packages/php-di/php-di)
+[![Total Downloads](https://img.shields.io/packagist/dt/mnapoli/PHP-DI.svg?style=flat-square)](https://packagist.org/packages/mnapoli/php-di)
+
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/PHP-DI/PHP-DI.svg)](http://isitmaintained.com/project/PHP-DI/PHP-DI "Average time to resolve an issue")
+[![Percentage of issues still open](http://isitmaintained.com/badge/open/PHP-DI/PHP-DI.svg)](http://isitmaintained.com/project/PHP-DI/PHP-DI "Percentage of issues still open")
+
+It is meant to be practical, powerful, and framework-agnostic.
+
+Read more on the website: **[php-di.org](http://php-di.org)**
+
+Join us in the Gitter chat room: [![Gitter chat](https://badges.gitter.im/PHP-DI/PHP-DI.png)](https://gitter.im/PHP-DI/PHP-DI)
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/change-log.md b/wcfsetup/install/files/lib/system/api/php-di/php-di/change-log.md
new file mode 100644 (file)
index 0000000..3a7ba86
--- /dev/null
@@ -0,0 +1,303 @@
+# Change log
+
+## 5.1
+
+Read the [news entry](news/16-php-di-5-1-released.md).
+
+Improvements:
+
+- [Zend Framework 2 integration](https://github.com/PHP-DI/ZF2-Bridge) (by @Rastusik)
+- [#308](https://github.com/PHP-DI/PHP-DI/pull/308): Instantiate factories using the container (`DI\factory(['FooFactory', 'create'])`)
+- Many performances improvements - some benchmarks show up to 35% performance improvements, real results may vary of course
+- Many documentation improvements (@jdreesen, @mindplay-dk, @mnapoli, @holtkamp, @Rastusik)
+- [#296](https://github.com/PHP-DI/PHP-DI/issues/296): Provide a faster `ArrayCache` implementation, mostly useful in micro-benchmarks
+
+Bugfixes:
+
+- [#257](https://github.com/PHP-DI/PHP-DI/issues/257) & [#274](https://github.com/PHP-DI/PHP-DI/issues/274): Private properties of parent classes are not injected when using annotations
+- [#300](https://github.com/PHP-DI/PHP-DI/pull/300): Exception if object definition extends an incompatible definition
+- [#306](https://github.com/PHP-DI/PHP-DI/issues/306): Errors when using parameters passed by reference (fixed by @bradynpoulsen)
+- [#318](https://github.com/PHP-DI/PHP-DI/issues/318): `Container::call()` ignores parameter's default value
+
+Internal changes:
+
+- [#276](https://github.com/PHP-DI/PHP-DI/pull/276): Tests now pass on Windows (@bgaillard)
+
+## 5.0
+
+This is the complete change log. You can also read the [migration guide](doc/migration/5.0.md) for upgrading, or [the news article](news/15-php-di-5-0-released.md) for a nicer introduction to this new version.
+
+Improvements:
+
+- Moved to an organization on GitHub: [github.com/PHP-DI/PHP-DI](https://github.com/PHP-DI/PHP-DI)
+- The package has been renamed to: from `mnapoli/php-di` to [`php-di/php-di`](https://packagist.org/packages/php-di/php-di)
+- New [Silex integration](doc/frameworks/silex.md)
+- Lighter package: from 10 to 3 Composer dependencies!
+- [#235](https://github.com/PHP-DI/PHP-DI/issues/235): `DI\link()` is now deprecated in favor of `DI\get()`. There is no BC break as `DI\link()` still works.
+- [#207](https://github.com/PHP-DI/PHP-DI/issues/207): Support for `DI\link()` in arrays
+- [#203](https://github.com/PHP-DI/PHP-DI/issues/203): New `DI\string()` helper ([documentation](doc/php-definitions.md))
+- [#208](https://github.com/PHP-DI/PHP-DI/issues/208): Support for nested definitions
+- [#226](https://github.com/PHP-DI/PHP-DI/pull/226): `DI\factory()` can now be omitted with closures:
+
+    ```php
+    // before
+    'My\Class' => DI\factory(function () { ... })
+    // now (optional shortcut)
+    'My\Class' => function () { ... }
+    ```
+- [#193](https://github.com/PHP-DI/PHP-DI/issues/193): `DI\object()->method()` now supports calling the same method twice (or more).
+- [#248](https://github.com/PHP-DI/PHP-DI/issues/248): New `DI\decorate()` helper to decorate a previously defined entry ([documentation](doc/definition-overriding.md))
+- [#215](https://github.com/PHP-DI/PHP-DI/pull/215): New `DI\add()` helper to add entries to an existing array ([documentation](doc/definition-overriding.md))
+- [#218](https://github.com/PHP-DI/PHP-DI/issues/218): `ContainerBuilder::addDefinitions()` can now take an array of definitions
+- [#211](https://github.com/PHP-DI/PHP-DI/pull/211): `ContainerBuilder::addDefinitions()` is now fluent (return `$this`)
+- [#250](https://github.com/PHP-DI/PHP-DI/issues/250): `Container::call()` now also accepts parameters not indexed by name as well as embedded definitions ([documentation](doc/container.md))
+- Various performance improvements, e.g. lower the number of files loaded, simpler architecture, â€¦
+
+BC breaks:
+
+- PHP-DI now requires a version of PHP >= 5.4.0
+- The package is lighter by default:
+    - [#251](https://github.com/PHP-DI/PHP-DI/issues/251): Annotations are disabled by default, if you use annotations enable them with `$containerBuilder->useAnnotations(true)`. Additionally the `doctrine/annotations` package isn't required by default anymore, so you also need to run `composer require doctrine/annotations`.
+    - `doctrine/cache` is not installed by default anymore, you need to require it in `composer.json` (`~1.0`) if you want to configure a cache for PHP-DI
+    - [#198](https://github.com/PHP-DI/PHP-DI/issues/198): `ocramius/proxy-manager` is not installed by default anymore, you need to require it in `composer.json` (`~1.0`) if you want to use **lazy injection**
+- Closures are now converted into factory definitions automatically. If you ever defined a closure as a value (e.g. to have the closure injected in a class), you need to wrap the closure with the new `DI\value()` helper.
+- [#223](https://github.com/PHP-DI/PHP-DI/issues/223): `DI\ContainerInterface` was deprecated since v4.1 and has been removed
+
+Internal changes in case you were replacing/extending some parts:
+
+- the definition sources architecture has been refactored, if you defined custom definition sources you will need to update your code (it should be much easier now)
+- [#252](https://github.com/PHP-DI/PHP-DI/pull/252): `DI\Scope` internal implementation has changed. You are encouraged to use the constants (`DI\Scope::SINGLETON` and `DI\Scope::PROTOTYPE`) instead of the static methods, but backward compatibility is kept (static methods still work).
+- [#241](https://github.com/PHP-DI/PHP-DI/issues/241): `Container::call()` now uses the *Invoker* external library
+
+## 4.4
+
+Read the [news entry](news/13-php-di-4-4-released.md).
+
+- [#185](https://github.com/PHP-DI/PHP-DI/issues/185) Support for invokable objects in `Container::call()`
+- [#192](https://github.com/PHP-DI/PHP-DI/pull/192) Support for invokable classes in `Container::call()` (will instantiate the class)
+- [#184](https://github.com/PHP-DI/PHP-DI/pull/184) Option to ignore phpdoc errors
+
+## 4.3
+
+Read the [news entry](news/11-php-di-4-3-released.md).
+
+- [#176](https://github.com/PHP-DI/PHP-DI/pull/176) New definition type for reading environment variables: `DI\env()`
+- [#181](https://github.com/PHP-DI/PHP-DI/pull/181) `DI\FactoryInterface` and `DI\InvokerInterface` are now auto-registered inside the container so that you can inject them without any configuration needed
+- [#173](https://github.com/PHP-DI/PHP-DI/pull/173) `$container->call(['MyClass', 'method]);` will get `MyClass` from the container if `method()` is not a static method
+
+## 4.2.2
+
+- Fixed [#180](https://github.com/PHP-DI/PHP-DI/pull/180): `Container::call()` with object methods (`[$object, 'method']`) is now supported
+
+## 4.2.1
+
+- Support for PHP 5.3.3, which was previously incomplete because of a bug in the reflection (there is now a workaround for this bug)
+
+But if you can, seriously avoid this (really old) PHP version and upgrade.
+
+## 4.2
+
+Read the [news entry](news/10-php-di-4-2-released.md).
+
+**Minor BC-break**: Optional parameters (that were not configured) were injected, they are now ignored, which is what naturally makes sense since they are optional.
+Example:
+
+```php
+    public function __construct(Bar $bar = null)
+    {
+        $this->bar = $bar ?: $this->createDefaultBar();
+    }
+```
+
+Before 4.2, PHP-DI would try to inject a `Bar` instance. From 4.2 and onwards, it will inject `null`.
+
+Of course, you can still explicitly define an injection for the optional parameters and that will work.
+
+All changes:
+
+* [#162](https://github.com/PHP-DI/PHP-DI/pull/162) Added `Container::call()` to call functions with dependency injection
+* [#156](https://github.com/PHP-DI/PHP-DI/issues/156) Wildcards (`*`) in definitions
+* [#164](https://github.com/PHP-DI/PHP-DI/issues/164) Prototype scope is now available for `factory()` definitions too
+* FIXED [#168](https://github.com/PHP-DI/PHP-DI/pull/168) `Container::has()` now returns false for interfaces and abstract classes that are not mapped in the definitions
+* FIXED [#171](https://github.com/PHP-DI/PHP-DI/issues/171) Optional parameters are now ignored (not injected) if not set in the definitions (see the BC-break warning above)
+
+## 4.1
+
+Read the [news entry](news/09-php-di-4-1-released.md).
+
+BC-breaks: None.
+
+* [#138](https://github.com/PHP-DI/PHP-DI/issues/138) [Container-interop](https://github.com/container-interop/container-interop) compliance
+* [#143](https://github.com/PHP-DI/PHP-DI/issues/143) Much more explicit exception messages
+* [#157](https://github.com/PHP-DI/PHP-DI/issues/157) HHVM support
+* [#158](https://github.com/PHP-DI/PHP-DI/issues/158) Improved the documentation for [Symfony 2 integration](http://php-di.org/doc/frameworks/symfony2.html)
+
+## 4.0
+
+Major changes:
+
+* The configuration format has changed ([read more here to understand why](news/06-php-di-4-0-new-definitions.md))
+
+Read the migration guide if you are using 3.x: [Migration guide from 3.x to 4.0](doc/migration/4.0.md).
+
+BC-breaks:
+
+* YAML, XML and JSON definitions have been removed, and the PHP definition format has changed (see above)
+* `ContainerSingleton` has been removed
+* You cannot configure an injection as lazy anymore, you can only configure a container entry as lazy
+* The Container constructor now takes mandatory parameters. Use the ContainerBuilder to create a Container.
+* Removed `ContainerBuilder::setDefinitionsValidation()` (no definition validation anymore)
+* `ContainerBuilder::useReflection()` is now named: `ContainerBuilder::useAutowiring()`
+* `ContainerBuilder::addDefinitionsFromFile()` is now named: `ContainerBuilder::addDefinitions()`
+* The `$proxy` parameter in `Container::get($name, $proxy = true)` hase been removed. To get a proxy, you now need to define an entry as "lazy".
+
+Other changes:
+
+* Added `ContainerInterface` and `FactoryInterface`, both implemented by the container.
+* [#115](https://github.com/PHP-DI/PHP-DI/issues/115) Added `Container::has()`
+* [#142](https://github.com/PHP-DI/PHP-DI/issues/142) Added `Container::make()` to resolve an entry
+* [#127](https://github.com/PHP-DI/PHP-DI/issues/127) Added support for cases where PHP-DI is wrapped by another container (like Acclimate): PHP-DI can now use the wrapping container to perform injections
+* [#128](https://github.com/PHP-DI/PHP-DI/issues/128) Configure entry aliases
+* [#110](https://github.com/PHP-DI/PHP-DI/issues/110) XML definitions are not supported anymore
+* [#122](https://github.com/PHP-DI/PHP-DI/issues/122) JSON definitions are not supported anymore
+* `ContainerSingleton` has finally been removed
+* Added `ContainerBuilder::buildDevContainer()` to get started with a default container very easily.
+* [#99](https://github.com/PHP-DI/PHP-DI/issues/99) Fixed "`@param` with PHP internal type throws exception"
+
+## 3.5.1
+
+* FIXED [#126](https://github.com/PHP-DI/PHP-DI/issues/126): `Container::set` without effect if a value has already been set and retrieved
+
+## 3.5
+
+Read the [news entry](news/05-php-di-3-5.md).
+
+* Importing `@Inject` and `@Injectable` annotations is now optional! It means that you don't have to write `use DI\Annotation\Inject` anymore
+* FIXED [#124](https://github.com/PHP-DI/PHP-DI/issues/124): `@Injects` annotation conflicts with other annotations
+
+## 3.4
+
+Read the [news entry](news/04-php-di-3-4.md).
+
+* [#106](https://github.com/PHP-DI/PHP-DI/pull/106) You can now define arrays of values (in YAML, PHP, â€¦) thanks to [@unkind](https://github.com/unkind)
+* [#98](https://github.com/PHP-DI/PHP-DI/issues/98) `ContainerBuilder` is now fluent thanks to [@drdamour](https://github.com/drdamour)
+* [#101](https://github.com/PHP-DI/PHP-DI/pull/101) Optional parameters are now supported: if you don't define a value to inject, their default value will be used
+* XML definitions have been deprecated, there weren't even documented and were not maintained. They will be removed in 4.0.
+* FIXED [#100](https://github.com/PHP-DI/PHP-DI/issues/100): bug for lazy injection in constructors
+
+## 3.3
+
+Read the [news entry](news/03-php-di-3-3.md).
+
+* Inject dependencies on an existing instance with `Container::injectOn` (work from [Jeff Flitton](https://github.com/jflitton): [#89](https://github.com/PHP-DI/PHP-DI/pull/89)).
+* [#86](https://github.com/PHP-DI/PHP-DI/issues/86): Optimized definition lookup (faster)
+* FIXED [#87](https://github.com/PHP-DI/PHP-DI/issues/87): Rare bug in the `PhpDocParser`, fixed by [drdamour](https://github.com/drdamour)
+
+## 3.2
+
+Read the [news entry](news/02-php-di-3-2.md).
+
+Small BC-break: PHP-DI 3.0 and 3.1 injected properties before calling the constructor. This was confusing and [not supported for internal classes](https://github.com/PHP-DI/PHP-DI/issues/74).
+From 3.2 and on, properties are injected after calling the constructor.
+
+* **[Lazy injection](doc/lazy-injection.md)**: it is now possible to use lazy injection on properties and methods (setters and constructors).
+* Lazy dependencies are now proxies that extend the class they proxy, so type-hinting works.
+* Addition of the **`ContainerBuilder`** object, that helps to [create and configure a `Container`](doc/container-configuration.md).
+* Some methods for configuring the Container have gone **deprecated** in favor of the `ContainerBuilder`. Fear not, these deprecated methods will remain until next major version (4.0).
+    * `Container::useReflection`, use ContainerBuilder::useReflection instead
+    * `Container::useAnnotations`, use ContainerBuilder::useAnnotations instead
+    * `Container::setDefinitionCache`, use ContainerBuilder::setDefinitionCache instead
+    * `Container::setDefinitionsValidation`, use ContainerBuilder::setDefinitionsValidation instead
+* The container is now auto-registered (as 'DI\Container'). You can now inject the container without registering it.
+
+## 3.1.1
+
+* Value definitions (`$container->set('foo', 80)`) are not cached anymore
+* FIXED [#82](https://github.com/PHP-DI/PHP-DI/issues/82): Serialization error when using a cache
+
+## 3.1
+
+Read the [news entry](news/01-php-di-3-1.md).
+
+* Zend Framework 1 integration through the [PHP-DI-ZF1 project](https://github.com/PHP-DI/PHP-DI-ZF1)
+* Fixed the order of priorities when you mix different definition sources (reflection, annotations, files, â€¦). See [Definition overriding](doc/definition-overriding.md)
+* Now possible to define null values with  `$container->set('foo', null)` (see [#79](https://github.com/PHP-DI/PHP-DI/issues/79)).
+* Deprecated usage of `ContainerSingleton`, will be removed in next major version (4.0)
+
+## 3.0.6
+
+* FIXED [#76](https://github.com/PHP-DI/PHP-DI/issues/76): Definition conflict when setting a closure for a class name
+
+## 3.0.5
+
+* FIXED [#70](https://github.com/PHP-DI/PHP-DI/issues/70): Definition conflict when setting a value for a class name
+
+## 3.0.4
+
+* FIXED [#69](https://github.com/PHP-DI/PHP-DI/issues/69): YamlDefinitionFileLoader crashes if YAML file is empty
+
+## 3.0.3
+
+* Fixed over-restrictive dependencies in composer.json
+
+## 3.0.2
+
+* [#64](https://github.com/PHP-DI/PHP-DI/issues/64): Non PHP-DI exceptions are not captured-rethrown anymore when injecting dependencies (cleaner stack trace)
+
+## 3.0.1
+
+* [#62](https://github.com/PHP-DI/PHP-DI/issues/62): When using aliases, definitions are now merged
+
+## 3.0
+
+Major compatibility breaks with 2.x.
+
+* The container is no longer a Singleton (but `ContainerSingleton::getInstance()` is available for fools who like it)
+* Setter injection
+* Constructor injection
+* Scopes: singleton (share the same instance of the class) or prototype (create a new instance each time it is fetched). Defined at class level.
+* Configuration is reworked from scratch. Now every configuration backend can do 100% of the job.
+* Provided configuration backends:
+    * Reflection
+    * Annotations: @Inject, @Injectable
+    * PHP code (`Container::set()`)
+    * PHP array
+    * YAML file
+* As a consequence, annotations are not mandatory anymore, all functionalities can be used with or without annotations.
+* Renamed `DI\Annotations\` to `DI\Annotation\`
+* `Container` no longer implements ArrayAccess, use only `$container->get($key)` now
+* ZF1 integration broken and removed (work in progress for next releases)
+* Code now follows PSR1 and PSR2 coding styles
+* FIXED: [#58](https://github.com/PHP-DI/PHP-DI/issues/58) Getting a proxy of an alias didn't work
+
+## 2.1
+
+* `use` statements to import classes from other namespaces are now taken into account with the `@var` annotation
+* Updated and lightened the dependencies : `doctrine/common` has been replaced with more specific `doctrine/annotations` and `doctrine/cache`
+
+## 2.0
+
+Major compatibility breaks with 1.x.
+
+* `Container::resolveDependencies()` has been renamed to `Container::injectAll()`
+* Dependencies are now injected **before** the constructor is called, and thus are available in the constructor
+* Merged `@Value` annotation with `@Inject`: no difference between value and bean injection anymore
+* Container implements ArrayAccess for get() and set() (`$container['db.host'] = 'localhost';`)
+* Ini configuration files removed: configuration is done in PHP
+* Allow to define beans within closures for lazy-loading
+* Switched to MIT License
+
+Warning:
+
+* If you use PHP 5.3 and __wakeup() methods, they will be called when PHP-DI creates new instances of those classes.
+
+## 1.1
+
+* Caching of annotations based on Doctrine caches
+
+## 1.0
+
+* DependencyManager renamed to Container
+* Refactored basic Container usage with `get` and `set`
+* Allow named injection `@Inject(name="")`
+* Zend Framework integration
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/composer.json b/wcfsetup/install/files/lib/system/api/php-di/php-di/composer.json
new file mode 100644 (file)
index 0000000..3111f93
--- /dev/null
@@ -0,0 +1,43 @@
+{
+    "name": "php-di/php-di",
+    "type": "library",
+    "description": "The dependency injection container for humans",
+    "keywords": ["di", "dependency injection", "container"],
+    "homepage": "http://php-di.org/",
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "DI\\": "src/DI/"
+        },
+        "files": [
+            "src/DI/functions.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "DI\\Test\\IntegrationTest\\": "tests/IntegrationTest/",
+            "DI\\Test\\UnitTest\\": "tests/UnitTest/"
+        }
+    },
+    "require": {
+        "php": ">=5.4.0",
+        "container-interop/container-interop": "~1.0",
+        "php-di/invoker": "^1.0.1",
+        "php-di/phpdoc-reader": "~2.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.5",
+        "mnapoli/phpunit-easymock": "~0.1.4",
+        "doctrine/cache": "~1.4",
+        "doctrine/annotations": "~1.2",
+        "ocramius/proxy-manager": "~1.0"
+    },
+    "replace": {
+        "mnapoli/php-di": "*"
+    },
+    "suggest": {
+        "doctrine/cache": "Install it if you want to use the cache (version ~1.4)",
+        "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)",
+        "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~1.0)"
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/couscous.yml b/wcfsetup/install/files/lib/system/api/php-di/php-di/couscous.yml
new file mode 100644 (file)
index 0000000..c18e3cf
--- /dev/null
@@ -0,0 +1,110 @@
+baseUrl: http://php-di.org
+
+scripts:
+    before:
+        - lessc --clean-css website/less/main.less website/css/all.min.css
+
+menu:
+    items:
+        introduction:
+            section: Introduction
+            items:
+                getting-started:
+                    text: Getting started
+                    url: doc/getting-started.html
+                understanding-di:
+                    text: Understanding dependency injection
+                    url: doc/understanding-di.html
+                best-practices:
+                    text: "\"Best practices\" guide"
+                    url: doc/best-practices.html
+        usage:
+            section: Usage
+            items:
+                container-configuration:
+                    text: Configuring the container
+                    url: doc/container-configuration.html
+                container:
+                    text: Using the container
+                    url: doc/container.html
+        definition:
+            section: Definitions
+            items:
+                introduction:
+                    text: Introduction
+                    url: doc/definition.html
+                autowiring:
+                    text: Autowiring
+                    url: doc/autowiring.html
+                php:
+                    text: PHP definitions
+                    url: doc/php-definitions.html
+                annotations:
+                    text: Annotations
+                    url: doc/annotations.html
+                definition-overriding:
+                    text: Definition extensions and overriding
+                    url: doc/definition-overriding.html
+        frameworks:
+            section: Frameworks
+            items:
+                symfony2:
+                    text: Symfony 2
+                    url: doc/frameworks/symfony2.html
+                silex:
+                    text: Silex
+                    url: doc/frameworks/silex.html
+                zf2:
+                    text: Zend Framework 2
+                    url: doc/frameworks/zf2.html
+                zf1:
+                    text: Zend Framework 1
+                    url: doc/frameworks/zf1.html
+                silly:
+                    text: Silly
+                    url: doc/frameworks/silly.html
+        advanced:
+            section: Advanced topics
+            items:
+                performances:
+                    text: Performances
+                    url: doc/performances.html
+                scopes:
+                    text: Scopes
+                    url: doc/scopes.html
+                lazy-injection:
+                    text: Lazy injection
+                    url: doc/lazy-injection.html
+                inject-on-instance:
+                    text: Inject on an existing instance
+                    url: doc/inject-on-instance.html
+                environments:
+                    text: Injections depending on the environment
+                    url: doc/environments.html
+        migration:
+            section: Migration guides
+            items:
+                4:
+                    text: From PHP-DI 3.x to 4.0
+                    url: doc/migration/4.0.html
+                5:
+                    text: From PHP-DI 4.x to 5.0
+                    url: doc/migration/5.0.html
+        internals:
+            section: Internals
+            items:
+                contributing:
+                    text: Contributing
+                    url: contributing.html
+                how-it-works:
+                    text: How PHP-DI works
+                    url: doc/how-it-works.html
+        versions:
+            section: Old documentation
+            items:
+                v3:
+                    text: PHP-DI 3.x
+                    absoluteUrl: https://github.com/PHP-DI/PHP-DI/tree/3.x/doc
+                v4:
+                    text: PHP-DI 4.x
+                    absoluteUrl: https://github.com/PHP-DI/PHP-DI/tree/4.x/doc
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/phpunit.xml.dist b/wcfsetup/install/files/lib/system/api/php-di/php-di/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..81c7f73
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    phpunit -c phpunit.xml
+-->
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         syntaxCheck="true"
+         forceCoversAnnotation="true"
+         bootstrap="./vendor/autoload.php">
+
+    <testsuites>
+        <testsuite name="unit">
+            <directory>./tests/UnitTest/</directory>
+        </testsuite>
+        <testsuite name="integration">
+            <directory>./tests/IntegrationTest/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist processUncoveredFilesFromWhitelist="true">
+            <directory suffix=".php">src</directory>
+        </whitelist>
+    </filter>
+
+</phpunit>
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Inject.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Inject.php
new file mode 100644 (file)
index 0000000..d0776a6
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Annotation;
+
+use DI\Definition\Exception\AnnotationException;
+
+/**
+ * "Inject" annotation
+ *
+ * Marks a property or method as an injection point
+ *
+ * @Annotation
+ * @Target({"METHOD","PROPERTY"})
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+final class Inject
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * Parameters, indexed by the parameter number (index) or name
+     *
+     * Used if the annotation is set on a method
+     * @var array
+     */
+    private $parameters = [];
+
+    /**
+     * @param array $values
+     */
+    public function __construct(array $values)
+    {
+        // Process the parameters as a list AND as a parameter array (we don't know on what the annotation is)
+
+        // @Inject(name="foo")
+        if (isset($values['name']) && is_string($values['name'])) {
+            $this->name = $values['name'];
+            return;
+        }
+
+        // @Inject
+        if (! isset($values['value'])) {
+            return;
+        }
+
+        $values = $values['value'];
+
+        // @Inject("foo")
+        if (is_string($values)) {
+            $this->name = $values;
+        }
+
+        // @Inject({...}) on a method
+        if (is_array($values)) {
+            foreach ($values as $key => $value) {
+                if (! is_string($value)) {
+                    throw new AnnotationException(sprintf(
+                        '@Inject({"param" = "value"}) expects "value" to be a string, %s given.',
+                        json_encode($value)
+                    ));
+                }
+
+                $this->parameters[$key] = $value;
+            }
+        }
+    }
+
+    /**
+     * @return string Name of the entry to inject
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return array Parameters, indexed by the parameter number (index) or name
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Injectable.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Annotation/Injectable.php
new file mode 100644 (file)
index 0000000..2176b67
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Annotation;
+
+use DI\Scope;
+use UnexpectedValueException;
+
+/**
+ * "Injectable" annotation
+ *
+ * Marks a class as injectable
+ *
+ * @Annotation
+ * @Target("CLASS")
+ *
+ * @author Domenic Muskulus <domenic@muskulus.eu>
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+final class Injectable
+{
+    /**
+     * The scope of an class: prototype, singleton
+     * @var string|null
+     */
+    private $scope;
+
+    /**
+     * Should the object be lazy-loaded
+     * @var boolean|null
+     */
+    private $lazy;
+
+    /**
+     * @param array $values
+     */
+    public function __construct(array $values)
+    {
+        if (isset($values['scope'])) {
+            if ($values['scope'] === 'prototype') {
+                $this->scope = Scope::PROTOTYPE;
+            } elseif ($values['scope'] === 'singleton') {
+                $this->scope = Scope::SINGLETON;
+            } else {
+                throw new UnexpectedValueException(sprintf("Value '%s' is not a valid scope", $values['scope']));
+            }
+        }
+        if (isset($values['lazy'])) {
+            $this->lazy = (boolean) $values['lazy'];
+        }
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getScope()
+    {
+        return $this->scope;
+    }
+
+    /**
+     * @return boolean|null
+     */
+    public function isLazy()
+    {
+        return $this->lazy;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Cache/ArrayCache.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Cache/ArrayCache.php
new file mode 100644 (file)
index 0000000..dcd6c0f
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Cache;
+
+use Doctrine\Common\Cache\Cache;
+use Doctrine\Common\Cache\ClearableCache;
+use Doctrine\Common\Cache\FlushableCache;
+
+/**
+ * Simple implementation of a cache based on an array.
+ *
+ * This implementation can be used instead of Doctrine's ArrayCache for
+ * better performances (because simpler implementation).
+ *
+ * The code is based on Doctrine's ArrayCache provider:
+ * @see \Doctrine\Common\Cache\ArrayCache
+ * @link   www.doctrine-project.org
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author David Abdemoulaie <dave@hobodave.com>
+ */
+class ArrayCache implements Cache, FlushableCache, ClearableCache
+{
+    /**
+     * @var array $data
+     */
+    private $data = [];
+
+    public function fetch($id)
+    {
+        return $this->contains($id) ? $this->data[$id] : false;
+    }
+
+    public function contains($id)
+    {
+        // isset() is required for performance optimizations, to avoid unnecessary function calls to array_key_exists.
+        return isset($this->data[$id]) || array_key_exists($id, $this->data);
+    }
+
+    public function save($id, $data, $lifeTime = 0)
+    {
+        $this->data[$id] = $data;
+
+        return true;
+    }
+
+    public function delete($id)
+    {
+        unset($this->data[$id]);
+
+        return true;
+    }
+
+    public function getStats()
+    {
+        return null;
+    }
+
+    public function flushAll()
+    {
+        $this->data = array();
+
+        return true;
+    }
+
+    public function deleteAll()
+    {
+        return $this->flushAll();
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Container.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Container.php
new file mode 100644 (file)
index 0000000..642e56b
--- /dev/null
@@ -0,0 +1,341 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\Definition;
+use DI\Definition\FactoryDefinition;
+use DI\Definition\InstanceDefinition;
+use DI\Definition\Resolver\ResolverDispatcher;
+use DI\Definition\Source\CachedDefinitionSource;
+use DI\Definition\Source\DefinitionSource;
+use DI\Definition\Source\MutableDefinitionSource;
+use DI\Definition\Helper\DefinitionHelper;
+use DI\Definition\Resolver\DefinitionResolver;
+use DI\Invoker\DefinitionParameterResolver;
+use DI\Proxy\ProxyFactory;
+use Exception;
+use Interop\Container\ContainerInterface;
+use InvalidArgumentException;
+use Invoker\Invoker;
+use Invoker\ParameterResolver\AssociativeArrayResolver;
+use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
+use Invoker\ParameterResolver\DefaultValueResolver;
+use Invoker\ParameterResolver\NumericArrayResolver;
+use Invoker\ParameterResolver\ResolverChain;
+
+/**
+ * Dependency Injection Container.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class Container implements ContainerInterface, FactoryInterface, \DI\InvokerInterface
+{
+    /**
+     * Map of entries with Singleton scope that are already resolved.
+     * @var array
+     */
+    private $singletonEntries = [];
+
+    /**
+     * @var DefinitionSource
+     */
+    private $definitionSource;
+
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * Array of entries being resolved. Used to avoid circular dependencies and infinite loops.
+     * @var array
+     */
+    private $entriesBeingResolved = [];
+
+    /**
+     * @var \Invoker\InvokerInterface|null
+     */
+    private $invoker;
+
+    /**
+     * Container that wraps this container. If none, points to $this.
+     *
+     * @var ContainerInterface
+     */
+    private $wrapperContainer;
+
+    /**
+     * Use the ContainerBuilder to ease constructing the Container.
+     *
+     * @see ContainerBuilder
+     *
+     * @param DefinitionSource   $definitionSource
+     * @param ProxyFactory       $proxyFactory
+     * @param ContainerInterface $wrapperContainer If the container is wrapped by another container.
+     */
+    public function __construct(
+        DefinitionSource $definitionSource,
+        ProxyFactory $proxyFactory,
+        ContainerInterface $wrapperContainer = null
+    ) {
+        $this->wrapperContainer = $wrapperContainer ?: $this;
+
+        $this->definitionSource = $definitionSource;
+        $this->definitionResolver = new ResolverDispatcher($this->wrapperContainer, $proxyFactory);
+
+        // Auto-register the container
+        $this->singletonEntries['DI\Container'] = $this;
+        $this->singletonEntries['DI\FactoryInterface'] = $this;
+        $this->singletonEntries['DI\InvokerInterface'] = $this;
+    }
+
+    /**
+     * Returns an entry of the container by its name.
+     *
+     * @param string $name Entry name or a class name.
+     *
+     * @throws InvalidArgumentException The name parameter must be of type string.
+     * @throws DependencyException Error while resolving the entry.
+     * @throws NotFoundException No entry found for the given name.
+     * @return mixed
+     */
+    public function get($name)
+    {
+        if (! is_string($name)) {
+            throw new InvalidArgumentException(sprintf(
+                'The name parameter must be of type string, %s given',
+                is_object($name) ? get_class($name) : gettype($name)
+            ));
+        }
+
+        // Try to find the entry in the singleton map
+        if (array_key_exists($name, $this->singletonEntries)) {
+            return $this->singletonEntries[$name];
+        }
+
+        $definition = $this->definitionSource->getDefinition($name);
+        if (! $definition) {
+            throw new NotFoundException("No entry or class found for '$name'");
+        }
+
+        $value = $this->resolveDefinition($definition);
+
+        // If the entry is singleton, we store it to always return it without recomputing it
+        if ($definition->getScope() === Scope::SINGLETON) {
+            $this->singletonEntries[$name] = $value;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Build an entry of the container by its name.
+     *
+     * This method behave like get() except it forces the scope to "prototype",
+     * which means the definition of the entry will be re-evaluated each time.
+     * For example, if the entry is a class, then a new instance will be created each time.
+     *
+     * This method makes the container behave like a factory.
+     *
+     * @param string $name       Entry name or a class name.
+     * @param array  $parameters Optional parameters to use to build the entry. Use this to force specific parameters
+     *                           to specific values. Parameters not defined in this array will be resolved using
+     *                           the container.
+     *
+     * @throws InvalidArgumentException The name parameter must be of type string.
+     * @throws DependencyException Error while resolving the entry.
+     * @throws NotFoundException No entry found for the given name.
+     * @return mixed
+     */
+    public function make($name, array $parameters = [])
+    {
+        if (! is_string($name)) {
+            throw new InvalidArgumentException(sprintf(
+                'The name parameter must be of type string, %s given',
+                is_object($name) ? get_class($name) : gettype($name)
+            ));
+        }
+
+        $definition = $this->definitionSource->getDefinition($name);
+        if (! $definition) {
+            // Try to find the entry in the singleton map
+            if (array_key_exists($name, $this->singletonEntries)) {
+                return $this->singletonEntries[$name];
+            }
+
+            throw new NotFoundException("No entry or class found for '$name'");
+        }
+
+        return $this->resolveDefinition($definition, $parameters);
+    }
+
+    /**
+     * Test if the container can provide something for the given name.
+     *
+     * @param string $name Entry name or a class name.
+     *
+     * @throws InvalidArgumentException The name parameter must be of type string.
+     * @return bool
+     */
+    public function has($name)
+    {
+        if (! is_string($name)) {
+            throw new InvalidArgumentException(sprintf(
+                'The name parameter must be of type string, %s given',
+                is_object($name) ? get_class($name) : gettype($name)
+            ));
+        }
+
+        if (array_key_exists($name, $this->singletonEntries)) {
+            return true;
+        }
+
+        $definition = $this->definitionSource->getDefinition($name);
+        if ($definition === null) {
+            return false;
+        }
+
+        return $this->definitionResolver->isResolvable($definition);
+    }
+
+    /**
+     * Inject all dependencies on an existing instance
+     *
+     * @param object $instance Object to perform injection upon
+     * @throws InvalidArgumentException
+     * @throws DependencyException Error while injecting dependencies
+     * @return object $instance Returns the same instance
+     */
+    public function injectOn($instance)
+    {
+        $objectDefinition = $this->definitionSource->getDefinition(get_class($instance));
+        if (! $objectDefinition instanceof ObjectDefinition) {
+            return $instance;
+        }
+
+        $definition = new InstanceDefinition($instance, $objectDefinition);
+
+        $this->definitionResolver->resolve($definition);
+
+        return $instance;
+    }
+
+    /**
+     * Call the given function using the given parameters.
+     *
+     * Missing parameters will be resolved from the container.
+     *
+     * @param callable $callable   Function to call.
+     * @param array    $parameters Parameters to use. Can be indexed by the parameter names
+     *                             or not indexed (same order as the parameters).
+     *                             The array can also contain DI definitions, e.g. DI\get().
+     *
+     * @return mixed Result of the function.
+     */
+    public function call($callable, array $parameters = [])
+    {
+        return $this->getInvoker()->call($callable, $parameters);
+    }
+
+    /**
+     * Define an object or a value in the container.
+     *
+     * @param string                 $name  Entry name
+     * @param mixed|DefinitionHelper $value Value, use definition helpers to define objects
+     */
+    public function set($name, $value)
+    {
+        if ($value instanceof DefinitionHelper) {
+            $value = $value->getDefinition($name);
+        } elseif ($value instanceof \Closure) {
+            $value = new FactoryDefinition($name, $value);
+        }
+
+        if ($value instanceof Definition) {
+            $this->setDefinition($name, $value);
+        } else {
+            $this->singletonEntries[$name] = $value;
+        }
+    }
+
+    /**
+     * Resolves a definition.
+     *
+     * Checks for circular dependencies while resolving the definition.
+     *
+     * @param Definition $definition
+     * @param array      $parameters
+     *
+     * @throws DependencyException Error while resolving the entry.
+     * @return mixed
+     */
+    private function resolveDefinition(Definition $definition, array $parameters = [])
+    {
+        $entryName = $definition->getName();
+
+        // Check if we are already getting this entry -> circular dependency
+        if (isset($this->entriesBeingResolved[$entryName])) {
+            throw new DependencyException("Circular dependency detected while trying to resolve entry '$entryName'");
+        }
+        $this->entriesBeingResolved[$entryName] = true;
+
+        // Resolve the definition
+        try {
+            $value = $this->definitionResolver->resolve($definition, $parameters);
+        } catch (Exception $exception) {
+            unset($this->entriesBeingResolved[$entryName]);
+            throw $exception;
+        }
+
+        unset($this->entriesBeingResolved[$entryName]);
+
+        return $value;
+    }
+
+    private function setDefinition($name, Definition $definition)
+    {
+        if ($this->definitionSource instanceof CachedDefinitionSource) {
+            throw new \LogicException('You cannot set a definition at runtime on a container that has a cache configured. Doing so would risk caching the definition for the next execution, where it might be different. You can either put your definitions in a file, remove the cache or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.');
+        }
+
+        if (! $this->definitionSource instanceof MutableDefinitionSource) {
+            // This can happen if you instantiate the container yourself
+            throw new \LogicException('The container has not been initialized correctly');
+        }
+
+        // Clear existing entry if it exists
+        if (array_key_exists($name, $this->singletonEntries)) {
+            unset($this->singletonEntries[$name]);
+        }
+
+        $this->definitionSource->addDefinition($definition);
+    }
+
+    /**
+     * @return \Invoker\InvokerInterface
+     */
+    private function getInvoker()
+    {
+        if (! $this->invoker) {
+            $parameterResolver = new ResolverChain([
+                new DefinitionParameterResolver($this->definitionResolver),
+                new NumericArrayResolver,
+                new AssociativeArrayResolver,
+                new DefaultValueResolver,
+                new TypeHintContainerResolver($this->wrapperContainer),
+            ]);
+
+            $this->invoker = new Invoker($parameterResolver, $this);
+        }
+
+        return $this->invoker;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/ContainerBuilder.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/ContainerBuilder.php
new file mode 100644 (file)
index 0000000..53871e2
--- /dev/null
@@ -0,0 +1,258 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use DI\Definition\Source\AnnotationReader;
+use DI\Definition\Source\DefinitionArray;
+use DI\Definition\Source\CachedDefinitionSource;
+use DI\Definition\Source\DefinitionSource;
+use DI\Definition\Source\DefinitionFile;
+use DI\Definition\Source\Autowiring;
+use DI\Definition\Source\SourceChain;
+use DI\Proxy\ProxyFactory;
+use Doctrine\Common\Cache\Cache;
+use Interop\Container\ContainerInterface;
+use InvalidArgumentException;
+
+/**
+ * Helper to create and configure a Container.
+ *
+ * With the default options, the container created is appropriate for the development environment.
+ *
+ * Example:
+ *
+ *     $builder = new ContainerBuilder();
+ *     $container = $builder->build();
+ *
+ * @since  3.2
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ContainerBuilder
+{
+    /**
+     * Name of the container class, used to create the container.
+     * @var string
+     */
+    private $containerClass;
+
+    /**
+     * @var boolean
+     */
+    private $useAutowiring = true;
+
+    /**
+     * @var boolean
+     */
+    private $useAnnotations = false;
+
+    /**
+     * @var boolean
+     */
+    private $ignorePhpDocErrors = false;
+
+    /**
+     * @var Cache
+     */
+    private $cache;
+
+    /**
+     * If true, write the proxies to disk to improve performances.
+     * @var boolean
+     */
+    private $writeProxiesToFile = false;
+
+    /**
+     * Directory where to write the proxies (if $writeProxiesToFile is enabled).
+     * @var string
+     */
+    private $proxyDirectory;
+
+    /**
+     * If PHP-DI is wrapped in another container, this references the wrapper.
+     * @var ContainerInterface
+     */
+    private $wrapperContainer;
+
+    /**
+     * @var DefinitionSource[]
+     */
+    private $definitionSources = [];
+
+    /**
+     * Build a container configured for the dev environment.
+     *
+     * @return Container
+     */
+    public static function buildDevContainer()
+    {
+        $builder = new self();
+        return $builder->build();
+    }
+
+    /**
+     * @param string $containerClass Name of the container class, used to create the container.
+     */
+    public function __construct($containerClass = 'DI\Container')
+    {
+        $this->containerClass = $containerClass;
+    }
+
+    /**
+     * Build and return a container.
+     *
+     * @return Container
+     */
+    public function build()
+    {
+        $sources = array_reverse($this->definitionSources);
+        if ($this->useAnnotations) {
+            $sources[] = new AnnotationReader($this->ignorePhpDocErrors);
+        } elseif ($this->useAutowiring) {
+            $sources[] = new Autowiring();
+        }
+
+        $chain = new SourceChain($sources);
+
+        if ($this->cache) {
+            $source = new CachedDefinitionSource($chain, $this->cache);
+            $chain->setRootDefinitionSource($source);
+        } else {
+            $source = $chain;
+            // Mutable definition source
+            $source->setMutableDefinitionSource(new DefinitionArray());
+        }
+
+        $proxyFactory = new ProxyFactory($this->writeProxiesToFile, $this->proxyDirectory);
+
+        $containerClass = $this->containerClass;
+
+        return new $containerClass($source, $proxyFactory, $this->wrapperContainer);
+    }
+
+    /**
+     * Enable or disable the use of autowiring to guess injections.
+     *
+     * Enabled by default.
+     *
+     * @param boolean $bool
+     * @return ContainerBuilder
+     */
+    public function useAutowiring($bool)
+    {
+        $this->useAutowiring = $bool;
+        return $this;
+    }
+
+    /**
+     * Enable or disable the use of annotations to guess injections.
+     *
+     * Disabled by default.
+     *
+     * @param boolean $bool
+     * @return ContainerBuilder
+     */
+    public function useAnnotations($bool)
+    {
+        $this->useAnnotations = $bool;
+        return $this;
+    }
+
+    /**
+     * Enable or disable ignoring phpdoc errors (non-existent classes in `@param` or `@var`)
+     * 
+     * @param boolean $bool
+     * @return ContainerBuilder
+     */
+    public function ignorePhpDocErrors($bool)
+    {
+        $this->ignorePhpDocErrors = $bool;
+        return $this;
+    }
+
+    /**
+     * Enables the use of a cache for the definitions.
+     *
+     * @param Cache $cache Cache backend to use
+     * @return ContainerBuilder
+     */
+    public function setDefinitionCache(Cache $cache)
+    {
+        $this->cache = $cache;
+        return $this;
+    }
+
+    /**
+     * Configure the proxy generation
+     *
+     * For dev environment, use writeProxiesToFile(false) (default configuration)
+     * For production environment, use writeProxiesToFile(true, 'tmp/proxies')
+     *
+     * @param boolean     $writeToFile    If true, write the proxies to disk to improve performances
+     * @param string|null $proxyDirectory Directory where to write the proxies
+     * @return ContainerBuilder
+     *
+     * @throws InvalidArgumentException when writeToFile is set to true and the proxy directory is null
+     */
+    public function writeProxiesToFile($writeToFile, $proxyDirectory = null)
+    {
+        $this->writeProxiesToFile = $writeToFile;
+
+        if ($writeToFile && $proxyDirectory === null) {
+            throw new InvalidArgumentException(
+                "The proxy directory must be specified if you want to write proxies on disk"
+            );
+        }
+        $this->proxyDirectory = $proxyDirectory;
+
+        return $this;
+    }
+
+    /**
+     * If PHP-DI's container is wrapped by another container, we can
+     * set this so that PHP-DI will use the wrapper rather than itself for building objects.
+     *
+     * @param ContainerInterface $otherContainer
+     * @return $this
+     */
+    public function wrapContainer(ContainerInterface $otherContainer)
+    {
+        $this->wrapperContainer = $otherContainer;
+
+        return $this;
+    }
+
+    /**
+     * Add definitions to the container.
+     *
+     * @param string|array|DefinitionSource $definitions Can be an array of definitions, the
+     *                                                   name of a file containing definitions
+     *                                                   or a DefinitionSource object.
+     * @return $this
+     */
+    public function addDefinitions($definitions)
+    {
+        if (is_string($definitions)) {
+            // File
+            $definitions = new DefinitionFile($definitions);
+        } elseif (is_array($definitions)) {
+            $definitions = new DefinitionArray($definitions);
+        } elseif (! $definitions instanceof DefinitionSource) {
+            throw new InvalidArgumentException(sprintf(
+                '%s parameter must be a string, an array or a DefinitionSource object, %s given',
+                'ContainerBuilder::addDefinitions()',
+                is_object($definitions) ? get_class($definitions) : gettype($definitions)
+            ));
+        }
+
+        $this->definitionSources[] = $definitions;
+
+        return $this;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Debug.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Debug.php
new file mode 100644 (file)
index 0000000..a8b3ae2
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use DI\Definition\Definition;
+use DI\Definition\Dumper\DefinitionDumperDispatcher;
+
+/**
+ * Debug utilities.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class Debug
+{
+    /**
+     * Dump the definition to a string.
+     *
+     * @param Definition $definition
+     *
+     * @return string
+     */
+    public static function dumpDefinition(Definition $definition)
+    {
+        static $dumper;
+
+        if (! $dumper) {
+            $dumper = new DefinitionDumperDispatcher();
+        }
+
+        return $dumper->dump($definition);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/AliasDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/AliasDefinition.php
new file mode 100644 (file)
index 0000000..ea26506
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Defines an alias from an entry to another.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AliasDefinition implements CacheableDefinition
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * Name of the target entry
+     * @var string
+     */
+    private $targetEntryName;
+
+    /**
+     * @param string $name            Entry name
+     * @param string $targetEntryName Name of the target entry
+     */
+    public function __construct($name, $targetEntryName)
+    {
+        $this->name = $name;
+        $this->targetEntryName = $targetEntryName;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::PROTOTYPE;
+    }
+
+    /**
+     * @return string
+     */
+    public function getTargetEntryName()
+    {
+        return $this->targetEntryName;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinition.php
new file mode 100644 (file)
index 0000000..f225f89
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Definition of an array containing values or references.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ArrayDefinition implements Definition
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var array
+     */
+    private $values;
+
+    /**
+     * @param string $name   Entry name
+     * @param array  $values
+     */
+    public function __construct($name, array $values)
+    {
+        $this->name = $name;
+        $this->values = $values;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::SINGLETON;
+    }
+
+    /**
+     * @return array
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinitionExtension.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ArrayDefinitionExtension.php
new file mode 100644 (file)
index 0000000..e2f2b76
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Definition\Exception\DefinitionException;
+
+/**
+ * Extends an array definition by adding new elements into it.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ArrayDefinitionExtension extends ArrayDefinition implements HasSubDefinition
+{
+    /**
+     * @var ArrayDefinition
+     */
+    private $subDefinition;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValues()
+    {
+        if (! $this->subDefinition) {
+            return parent::getValues();
+        }
+
+        return array_merge($this->subDefinition->getValues(), parent::getValues());
+    }
+
+    /**
+     * @return string
+     */
+    public function getSubDefinitionName()
+    {
+        return $this->getName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSubDefinition(Definition $definition)
+    {
+        if (! $definition instanceof ArrayDefinition) {
+            throw new DefinitionException(sprintf(
+                'Definition %s tries to add array entries but the previous definition is not an array',
+                $this->getName()
+            ));
+        }
+
+        $this->subDefinition = $definition;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/CacheableDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/CacheableDefinition.php
new file mode 100644 (file)
index 0000000..eca8e3a
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+/**
+ * Cacheable definition
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface CacheableDefinition extends Definition
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/DecoratorDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/DecoratorDefinition.php
new file mode 100644 (file)
index 0000000..68b4b60
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+/**
+ * Factory that decorates a sub-definition.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DecoratorDefinition extends FactoryDefinition implements Definition, HasSubDefinition
+{
+    /**
+     * @var Definition
+     */
+    private $decorated;
+
+    /**
+     * @return string
+     */
+    public function getSubDefinitionName()
+    {
+        return $this->getName();
+    }
+
+    /**
+     * @param Definition $definition
+     */
+    public function setSubDefinition(Definition $definition)
+    {
+        $this->decorated = $definition;
+    }
+
+    /**
+     * @return Definition
+     */
+    public function getDecoratedDefinition()
+    {
+        return $this->decorated;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Definition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Definition.php
new file mode 100644 (file)
index 0000000..849c382
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+/**
+ * Definition
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface Definition
+{
+    /**
+     * Returns the name of the entry in the container
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Returns the scope of the entry
+     *
+     * @return string
+     */
+    public function getScope();
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/AliasDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/AliasDefinitionDumper.php
new file mode 100644 (file)
index 0000000..c50c6a7
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\AliasDefinition;
+use DI\Definition\Definition;
+
+/**
+ * Dumps alias definitions.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AliasDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof AliasDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with AliasDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        if ($definition->getName()) {
+            return sprintf(
+                "get(%s => %s)",
+                $definition->getName(),
+                $definition->getTargetEntryName()
+            );
+        }
+
+        return sprintf(
+            "get(%s)",
+            $definition->getTargetEntryName()
+        );
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ArrayDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ArrayDefinitionDumper.php
new file mode 100644 (file)
index 0000000..c31b7b1
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Debug;
+use DI\Definition\ArrayDefinition;
+use DI\Definition\Definition;
+use DI\Definition\Helper\DefinitionHelper;
+
+/**
+ * Dumps array definitions.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ArrayDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof ArrayDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with ArrayDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        $str = '[' . PHP_EOL;
+
+        foreach ($definition->getValues() as $key => $value) {
+            if (is_string($key)) {
+                $key = "'" . $key . "'";
+            }
+
+            $str .= '    ' . $key . ' => ';
+
+            if ($value instanceof DefinitionHelper) {
+                $nestedDefinition = Debug::dumpDefinition($value->getDefinition(''));
+                $str .= $this->indent($nestedDefinition);
+            } else {
+                $str .= var_export($value, true);
+            }
+
+            $str .= ',' . PHP_EOL;
+        }
+
+        $str .= ']';
+
+        return $str;
+    }
+
+    private function indent($str)
+    {
+        return str_replace(PHP_EOL, PHP_EOL . "    ", $str);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DecoratorDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DecoratorDefinitionDumper.php
new file mode 100644 (file)
index 0000000..9aaa55f
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\DecoratorDefinition;
+use DI\Definition\Definition;
+
+/**
+ * Dumps decorator definitions.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DecoratorDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof DecoratorDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with DecoratorDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        return 'Decorate(' . $definition->getSubDefinitionName() . ')';
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumper.php
new file mode 100644 (file)
index 0000000..7083c97
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\Definition;
+
+/**
+ * Dumps definitions to help debugging.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface DefinitionDumper
+{
+    /**
+     * Returns the given definition as string representation.
+     *
+     * @param Definition $definition
+     *
+     * @return string
+     */
+    public function dump(Definition $definition);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumperDispatcher.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/DefinitionDumperDispatcher.php
new file mode 100644 (file)
index 0000000..89add31
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\Definition;
+
+/**
+ * Dispatch a definition to the appropriate dumper.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefinitionDumperDispatcher implements DefinitionDumper
+{
+    /**
+     * Definition dumpers, indexed by the class of the definition they can dump.
+     *
+     * @var DefinitionDumper[]|null
+     */
+    private $dumpers = [];
+
+    public function __construct($dumpers = null)
+    {
+        $this->dumpers = $dumpers;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        $this->initialize();
+
+        $class = get_class($definition);
+
+        if (! array_key_exists($class, $this->dumpers)) {
+            throw new \RuntimeException(sprintf(
+                'There is no DefinitionDumper capable of dumping this definition of type %s',
+                $class
+            ));
+        }
+
+        $dumper = $this->dumpers[$class];
+
+        return $dumper->dump($definition);
+    }
+
+    private function initialize()
+    {
+        if ($this->dumpers === null) {
+            $this->dumpers = [
+                'DI\Definition\ValueDefinition'               => new ValueDefinitionDumper(),
+                'DI\Definition\FactoryDefinition'             => new FactoryDefinitionDumper(),
+                'DI\Definition\DecoratorDefinition'           => new DecoratorDefinitionDumper(),
+                'DI\Definition\AliasDefinition'               => new AliasDefinitionDumper(),
+                'DI\Definition\ObjectDefinition'              => new ObjectDefinitionDumper(),
+                'DI\Definition\EnvironmentVariableDefinition' => new EnvironmentVariableDefinitionDumper(),
+            ];
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/EnvironmentVariableDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/EnvironmentVariableDefinitionDumper.php
new file mode 100644 (file)
index 0000000..9886731
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Debug;
+use DI\Definition\Definition;
+use DI\Definition\EntryReference;
+use DI\Definition\EnvironmentVariableDefinition;
+use DI\Definition\Helper\DefinitionHelper;
+
+/**
+ * Dumps environment variable definitions.
+ *
+ * @author James Harris <james.harris@icecave.com.au>
+ */
+class EnvironmentVariableDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof EnvironmentVariableDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with EnvironmentVariableDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        $str = "    variable = " . $definition->getVariableName();
+        $str .= PHP_EOL . "    optional = " . ($definition->isOptional() ? 'yes' : 'no');
+
+        if ($definition->isOptional()) {
+            $defaultValue = $definition->getDefaultValue();
+
+            if ($defaultValue instanceof DefinitionHelper) {
+                $nestedDefinition = Debug::dumpDefinition($defaultValue->getDefinition(''));
+                $defaultValueStr = $this->indent($nestedDefinition);
+            } else {
+                $defaultValueStr = var_export($defaultValue, true);
+            }
+
+            $str .= PHP_EOL . "    default = " . $defaultValueStr;
+        }
+
+        return sprintf(
+            "Environment variable (" . PHP_EOL . "%s" . PHP_EOL . ")",
+            $str
+        );
+    }
+
+    private function indent($str)
+    {
+        return str_replace(PHP_EOL, PHP_EOL . "    ", $str);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/FactoryDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/FactoryDefinitionDumper.php
new file mode 100644 (file)
index 0000000..8477f5d
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\Definition;
+use DI\Definition\FactoryDefinition;
+
+/**
+ * Dumps factory definitions.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class FactoryDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof FactoryDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with FactoryDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        return 'Factory';
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ObjectDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ObjectDefinitionDumper.php
new file mode 100644 (file)
index 0000000..66d87d7
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\ObjectDefinition\MethodInjection;
+use DI\Definition\Definition;
+use DI\Definition\EntryReference;
+use ReflectionException;
+use ReflectionMethod;
+
+/**
+ * Dumps object definitions.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ObjectDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof ObjectDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with ObjectDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        $className = $definition->getClassName();
+        $classExist = class_exists($className) || interface_exists($className);
+
+        // Class
+        if (! $classExist) {
+            $warning = '#UNKNOWN# ';
+        } else {
+            $class = new \ReflectionClass($className);
+            $warning = $class->isInstantiable() ? '' : '#NOT INSTANTIABLE# ';
+        }
+        $str = sprintf('    class = %s%s', $warning, $className);
+
+        // Scope
+        $str .= PHP_EOL . "    scope = " . $definition->getScope();
+
+        // Lazy
+        $str .= PHP_EOL . "    lazy = " . var_export($definition->isLazy(), true);
+
+        if ($classExist) {
+            // Constructor
+            $str .= $this->dumpConstructor($className, $definition);
+
+            // Properties
+            $str .= $this->dumpProperties($definition);
+
+            // Methods
+            $str .= $this->dumpMethods($className, $definition);
+        }
+
+        return sprintf("Object (" . PHP_EOL . "%s" . PHP_EOL . ")", $str);
+    }
+
+    private function dumpConstructor($className, ObjectDefinition $definition)
+    {
+        $str = '';
+
+        $constructorInjection = $definition->getConstructorInjection();
+
+        if ($constructorInjection !== null) {
+            $parameters = $this->dumpMethodParameters($className, $constructorInjection);
+
+            $str .= sprintf(PHP_EOL . "    __construct(" . PHP_EOL . "        %s" . PHP_EOL . "    )", $parameters);
+        }
+
+        return $str;
+    }
+
+    private function dumpProperties(ObjectDefinition $definition)
+    {
+        $str = '';
+
+        foreach ($definition->getPropertyInjections() as $propertyInjection) {
+            $value = $propertyInjection->getValue();
+            if ($value instanceof EntryReference) {
+                $valueStr = sprintf('get(%s)', $value->getName());
+            } else {
+                $valueStr = var_export($value, true);
+            }
+
+            $str .= sprintf(PHP_EOL . "    $%s = %s", $propertyInjection->getPropertyName(), $valueStr);
+        }
+
+        return $str;
+    }
+
+    private function dumpMethods($className, ObjectDefinition $definition)
+    {
+        $str = '';
+
+        foreach ($definition->getMethodInjections() as $methodInjection) {
+            $parameters = $this->dumpMethodParameters($className, $methodInjection);
+
+            $str .= sprintf(PHP_EOL . "    %s(" . PHP_EOL . "        %s" . PHP_EOL . "    )", $methodInjection->getMethodName(), $parameters);
+        }
+
+        return $str;
+    }
+
+    private function dumpMethodParameters($className, MethodInjection $methodInjection)
+    {
+        $methodReflection = new \ReflectionMethod($className, $methodInjection->getMethodName());
+
+        $args = [];
+
+        $definitionParameters = $methodInjection->getParameters();
+
+        foreach ($methodReflection->getParameters() as $index => $parameter) {
+            if (array_key_exists($index, $definitionParameters)) {
+                $value = $definitionParameters[$index];
+
+                if ($value instanceof EntryReference) {
+                    $args[] = sprintf('$%s = get(%s)', $parameter->getName(), $value->getName());
+                } else {
+                    $args[] = sprintf('$%s = %s', $parameter->getName(), var_export($value, true));
+                }
+                continue;
+            }
+
+            // If the parameter is optional and wasn't specified, we take its default value
+            if ($parameter->isOptional()) {
+                try {
+                    $value = $parameter->getDefaultValue();
+
+                    $args[] = sprintf(
+                        '$%s = (default value) %s',
+                        $parameter->getName(),
+                        var_export($value, true)
+                    );
+                    continue;
+                } catch (ReflectionException $e) {
+                    // The default value can't be read through Reflection because it is a PHP internal class
+                }
+            }
+
+            $args[] = sprintf('$%s = #UNDEFINED#', $parameter->getName());
+        }
+
+        return implode(PHP_EOL . '        ', $args);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/StringDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/StringDefinitionDumper.php
new file mode 100644 (file)
index 0000000..b7267d7
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\Definition;
+use DI\Definition\StringDefinition;
+
+/**
+ * Dumps string definitions.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class StringDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof StringDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with StringDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        return $definition->getExpression();
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ValueDefinitionDumper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Dumper/ValueDefinitionDumper.php
new file mode 100644 (file)
index 0000000..c6decdc
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Dumper;
+
+use DI\Definition\Definition;
+use DI\Definition\ValueDefinition;
+
+/**
+ * Dumps value definitions.
+ *
+ * @since 4.1
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ValueDefinitionDumper implements DefinitionDumper
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function dump(Definition $definition)
+    {
+        if (! $definition instanceof ValueDefinition) {
+            throw new \InvalidArgumentException(sprintf(
+                'This definition dumper is only compatible with ValueDefinition objects, %s given',
+                get_class($definition)
+            ));
+        }
+
+        ob_start();
+
+        var_dump($definition->getValue());
+
+        return sprintf(
+            "Value (" . PHP_EOL . "    %s" . PHP_EOL . ")",
+            trim(ob_get_clean())
+        );
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EntryReference.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EntryReference.php
new file mode 100644 (file)
index 0000000..3a13604
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Definition\Helper\DefinitionHelper;
+
+/**
+ * Represents a reference to a container entry.
+ *
+ * TODO should EntryReference and AliasDefinition be merged into a ReferenceDefinition?
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class EntryReference implements DefinitionHelper
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @param string $entryName Entry name
+     */
+    public function __construct($entryName)
+    {
+        $this->name = $entryName;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($entryName)
+    {
+        return new AliasDefinition($entryName, $this->name);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EnvironmentVariableDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/EnvironmentVariableDefinition.php
new file mode 100644 (file)
index 0000000..1cc0997
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Defines a reference to an environment variable, with fallback to a default
+ * value if the environment variable is not defined.
+ *
+ * @author James Harris <james.harris@icecave.com.au>
+ */
+class EnvironmentVariableDefinition implements CacheableDefinition
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * The name of the environment variable
+     * @var string
+     */
+    private $variableName;
+
+    /**
+     * Whether or not the environment variable definition is optional
+     *
+     * If true and the environment variable given by $variableName has not been
+     * defined, $defaultValue is used.
+     *
+     * @var boolean
+     */
+    private $isOptional;
+
+    /**
+     * The default value to use if the environment variable is optional and not provided
+     * @var mixed
+     */
+    private $defaultValue;
+
+    /**
+     * @var string|null
+     */
+    private $scope;
+
+    /**
+     * @param string $name Entry name
+     * @param string $variableName The name of the environment variable
+     * @param boolean $isOptional Whether or not the environment variable definition is optional
+     * @param mixed $defaultValue The default value to use if the environment variable is optional and not provided
+     */
+    public function __construct($name, $variableName, $isOptional = false, $defaultValue = null)
+    {
+        $this->name = $name;
+        $this->variableName = $variableName;
+        $this->isOptional = $isOptional;
+        $this->defaultValue = $defaultValue;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return string The name of the environment variable
+     */
+    public function getVariableName()
+    {
+        return $this->variableName;
+    }
+
+    /**
+     * @return boolean Whether or not the environment variable definition is optional
+     */
+    public function isOptional()
+    {
+        return $this->isOptional;
+    }
+
+    /**
+     * @return mixed The default value to use if the environment variable is optional and not provided
+     */
+    public function getDefaultValue()
+    {
+        return $this->defaultValue;
+    }
+
+    /**
+     * @param string $scope
+     */
+    public function setScope($scope)
+    {
+        $this->scope = $scope;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return $this->scope ?: Scope::SINGLETON;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/AnnotationException.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/AnnotationException.php
new file mode 100644 (file)
index 0000000..3657c20
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Exception;
+
+/**
+ * Exception in the definitions using annotations
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AnnotationException extends DefinitionException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/DefinitionException.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Exception/DefinitionException.php
new file mode 100644 (file)
index 0000000..cfa806a
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Exception;
+
+use DI\Debug;
+use DI\Definition\Definition;
+
+/**
+ * Invalid DI definitions
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefinitionException extends \Exception
+{
+    public static function create(Definition $definition, $message)
+    {
+        return new self(sprintf(
+            "%s" . PHP_EOL . "Full definition:" . PHP_EOL . "%s",
+            $message,
+            Debug::dumpDefinition($definition)
+        ));
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/FactoryDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/FactoryDefinition.php
new file mode 100644 (file)
index 0000000..6c007a0
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Definition of a value or class with a factory.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class FactoryDefinition implements Definition
+{
+    /**
+     * Entry name.
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var string
+     */
+    private $scope;
+
+    /**
+     * Callable that returns the value.
+     * @var callable
+     */
+    private $factory;
+
+    /**
+     * @param string      $name    Entry name
+     * @param callable    $factory Callable that returns the value associated to the entry name.
+     * @param string|null $scope
+     */
+    public function __construct($name, $factory, $scope = null)
+    {
+        $this->name = $name;
+        $this->factory = $factory;
+        $this->scope = $scope;
+    }
+
+    /**
+     * @return string Entry name.
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Default scope is singleton: the callable is called once and the result is shared.
+     *
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return $this->scope ?: Scope::SINGLETON;
+    }
+
+    /**
+     * @return callable Callable that returns the value associated to the entry name.
+     */
+    public function getCallable()
+    {
+        return $this->factory;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/HasSubDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/HasSubDefinition.php
new file mode 100644 (file)
index 0000000..7639120
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+/**
+ * A definition that has a sub-definition.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface HasSubDefinition extends Definition
+{
+    /**
+     * @return string
+     */
+    public function getSubDefinitionName();
+
+    /**
+     * @param Definition $definition
+     */
+    public function setSubDefinition(Definition $definition);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ArrayDefinitionExtensionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ArrayDefinitionExtensionHelper.php
new file mode 100644 (file)
index 0000000..6961b4c
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\ArrayDefinitionExtension;
+
+/**
+ * Helps extending the definition of an array.
+ *
+ * For example you can add new entries to the array.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ArrayDefinitionExtensionHelper implements DefinitionHelper
+{
+    /**
+     * @var array
+     */
+    private $values = [];
+
+    /**
+     * @param array $values Values to add to the array.
+     */
+    public function __construct(array $values)
+    {
+        $this->values = $values;
+    }
+
+    /**
+     * @param string $entryName Container entry name
+     *
+     * @return ArrayDefinitionExtension
+     */
+    public function getDefinition($entryName)
+    {
+        return new ArrayDefinitionExtension($entryName, $this->values);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/DefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/DefinitionHelper.php
new file mode 100644 (file)
index 0000000..2493b2f
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+/**
+ * Helps defining container entries.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface DefinitionHelper
+{
+    /**
+     * @param string $entryName Container entry name
+     * @return \DI\Definition\Definition
+     */
+    public function getDefinition($entryName);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/EnvironmentVariableDefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/EnvironmentVariableDefinitionHelper.php
new file mode 100644 (file)
index 0000000..8e525bd
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\EnvironmentVariableDefinition;
+
+/**
+ * Helps defining how to create an instance of an environment variable definition.
+ *
+ * @author James Harris <james.harris@icecave.com.au>
+ */
+class EnvironmentVariableDefinitionHelper implements DefinitionHelper
+{
+    /**
+     * The name of the environment variable
+     * @var string
+     */
+    private $variableName;
+
+    /**
+     * Whether or not the environment variable definition is optional
+     *
+     * If true and the environment variable given by $variableName has not been
+     * defined, $defaultValue is used.
+     *
+     * @var boolean
+     */
+    private $isOptional;
+
+    /**
+     * The default value to use if the environment variable is optional and not provided
+     * @var mixed
+     */
+    private $defaultValue;
+
+    /**
+     * @param string  $variableName The name of the environment variable
+     * @param boolean $isOptional   Whether or not the environment variable definition is optional
+     * @param mixed   $defaultValue The default value to use if the environment variable is optional and not provided
+     */
+    public function __construct($variableName, $isOptional, $defaultValue = null)
+    {
+        $this->variableName = $variableName;
+        $this->isOptional = $isOptional;
+        $this->defaultValue = $defaultValue;
+    }
+
+    /**
+     * @param string $entryName Container entry name
+     *
+     * @return EnvironmentVariableDefinition
+     */
+    public function getDefinition($entryName)
+    {
+        return new EnvironmentVariableDefinition($entryName, $this->variableName, $this->isOptional, $this->defaultValue);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/FactoryDefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/FactoryDefinitionHelper.php
new file mode 100644 (file)
index 0000000..0278023
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\DecoratorDefinition;
+use DI\Definition\FactoryDefinition;
+
+/**
+ * Helps defining how to create an instance of a class using a factory (callable).
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class FactoryDefinitionHelper implements DefinitionHelper
+{
+    /**
+     * @var callable
+     */
+    private $factory;
+
+    /**
+     * @var string|null
+     */
+    private $scope;
+
+    /**
+     * @var bool
+     */
+    private $decorate;
+
+    /**
+     * @param callable $factory
+     * @param bool     $decorate Is the factory decorating a previous definition?
+     */
+    public function __construct($factory, $decorate = false)
+    {
+        $this->factory = $factory;
+        $this->decorate = $decorate;
+    }
+
+    /**
+     * Defines the scope of the entry.
+     *
+     * @param string $scope
+     *
+     * @return FactoryDefinitionHelper
+     */
+    public function scope($scope)
+    {
+        $this->scope = $scope;
+        return $this;
+    }
+
+    /**
+     * @param string $entryName Container entry name
+     * @return FactoryDefinition
+     */
+    public function getDefinition($entryName)
+    {
+        if ($this->decorate) {
+            return new DecoratorDefinition($entryName, $this->factory, $this->scope);
+        }
+
+        return new FactoryDefinition($entryName, $this->factory, $this->scope);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ObjectDefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ObjectDefinitionHelper.php
new file mode 100644 (file)
index 0000000..a05725b
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\ObjectDefinition\MethodInjection;
+use DI\Definition\ObjectDefinition\PropertyInjection;
+use DI\Definition\Exception\DefinitionException;
+
+/**
+ * Helps defining how to create an instance of a class.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ObjectDefinitionHelper implements DefinitionHelper
+{
+    /**
+     * @var string|null
+     */
+    private $className;
+
+    /**
+     * @var boolean|null
+     */
+    private $lazy;
+
+    /**
+     * @var string|null
+     */
+    private $scope;
+
+    /**
+     * Array of constructor parameters.
+     * @var array
+     */
+    private $constructor = [];
+
+    /**
+     * Array of properties and their value.
+     * @var array
+     */
+    private $properties = [];
+
+    /**
+     * Array of methods and their parameters.
+     * @var array
+     */
+    private $methods = [];
+
+    /**
+     * Helper for defining an object.
+     *
+     * @param string|null $className Class name of the object.
+     *                               If null, the name of the entry (in the container) will be used as class name.
+     */
+    public function __construct($className = null)
+    {
+        $this->className = $className;
+    }
+
+    /**
+     * Define the entry as lazy.
+     *
+     * A lazy entry is created only when it is used, a proxy is injected instead.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function lazy()
+    {
+        $this->lazy = true;
+        return $this;
+    }
+
+    /**
+     * Defines the scope of the entry.
+     *
+     * @param string $scope
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function scope($scope)
+    {
+        $this->scope = $scope;
+        return $this;
+    }
+
+    /**
+     * Defines the arguments to use to call the constructor.
+     *
+     * This method takes a variable number of arguments, example:
+     *     ->constructor($param1, $param2, $param3)
+     *
+     * @param mixed ... Parameters to use for calling the constructor of the class.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function constructor()
+    {
+        $this->constructor = func_get_args();
+        return $this;
+    }
+
+    /**
+     * Defines a value for a specific argument of the constructor.
+     *
+     * This method is usually used together with annotations or autowiring, when a parameter
+     * is not (or cannot be) type-hinted. Using this method instead of constructor() allows to
+     * avoid defining all the parameters (letting them being resolved using annotations or autowiring)
+     * and only define one.
+     *
+     * @param string $parameter Parameter for which the value will be given.
+     * @param mixed  $value     Value to give to this parameter.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function constructorParameter($parameter, $value)
+    {
+        $this->constructor[$parameter] = $value;
+        return $this;
+    }
+
+    /**
+     * Defines a value to inject in a property of the object.
+     *
+     * @param string $property Entry in which to inject the value.
+     * @param mixed  $value    Value to inject in the property.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function property($property, $value)
+    {
+        $this->properties[$property] = $value;
+        return $this;
+    }
+
+    /**
+     * Defines a method to call and the arguments to use.
+     *
+     * This method takes a variable number of arguments after the method name, example:
+     *
+     *     ->method('myMethod', $param1, $param2)
+     *
+     * Can be used multiple times to declare multiple calls.
+     *
+     * @param string $method Name of the method to call.
+     * @param mixed  ...     Parameters to use for calling the method.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function method($method)
+    {
+        $args = func_get_args();
+        array_shift($args);
+
+        if (! isset($this->methods[$method])) {
+            $this->methods[$method] = [];
+        }
+
+        $this->methods[$method][] = $args;
+
+        return $this;
+    }
+
+    /**
+     * Defines a method to call and a value for a specific argument.
+     *
+     * This method is usually used together with annotations or autowiring, when a parameter
+     * is not (or cannot be) type-hinted. Using this method instead of method() allows to
+     * avoid defining all the parameters (letting them being resolved using annotations or
+     * autowiring) and only define one.
+     *
+     * If multiple calls to the method have been configured already (e.g. in a previous definition)
+     * then this method only overrides the parameter for the *first* call.
+     *
+     * @param string $method    Name of the method to call.
+     * @param string $parameter Name or index of the parameter for which the value will be given.
+     * @param mixed  $value     Value to give to this parameter.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    public function methodParameter($method, $parameter, $value)
+    {
+        // Special case for the constructor
+        if ($method === '__construct') {
+            $this->constructor[$parameter] = $value;
+            return $this;
+        }
+
+        if (! isset($this->methods[$method])) {
+            $this->methods[$method] = [0 => []];
+        }
+
+        $this->methods[$method][0][$parameter] = $value;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($entryName)
+    {
+        $definition = new ObjectDefinition($entryName, $this->className);
+
+        if ($this->lazy !== null) {
+            $definition->setLazy($this->lazy);
+        }
+        if ($this->scope !== null) {
+            $definition->setScope($this->scope);
+        }
+
+        if (! empty($this->constructor)) {
+            $parameters = $this->fixParameters($definition, '__construct', $this->constructor);
+            $constructorInjection = MethodInjection::constructor($parameters);
+            $definition->setConstructorInjection($constructorInjection);
+        }
+
+        if (! empty($this->properties)) {
+            foreach ($this->properties as $property => $value) {
+                $definition->addPropertyInjection(
+                    new PropertyInjection($property, $value)
+                );
+            }
+        }
+
+        if (! empty($this->methods)) {
+            foreach ($this->methods as $method => $calls) {
+                foreach ($calls as $parameters) {
+                    $parameters = $this->fixParameters($definition, $method, $parameters);
+                    $methodInjection = new MethodInjection($method, $parameters);
+                    $definition->addMethodInjection($methodInjection);
+                }
+            }
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Fixes parameters indexed by the parameter name -> reindex by position.
+     *
+     * This is necessary so that merging definitions between sources is possible.
+     *
+     * @param ObjectDefinition $definition
+     * @param string          $method
+     * @param array           $parameters
+     * @throws DefinitionException
+     * @return array
+     */
+    private function fixParameters(ObjectDefinition $definition, $method, $parameters)
+    {
+        $fixedParameters = [];
+
+        foreach ($parameters as $index => $parameter) {
+            // Parameter indexed by the parameter name, we reindex it with its position
+            if (is_string($index)) {
+                $callable = [$definition->getClassName(), $method];
+                try {
+                    $reflectionParameter = new \ReflectionParameter($callable, $index);
+                } catch (\ReflectionException $e) {
+                    throw DefinitionException::create($definition, "Parameter with name '$index' could not be found");
+                }
+
+                $index = $reflectionParameter->getPosition();
+            }
+
+            $fixedParameters[$index] = $parameter;
+        }
+
+        return $fixedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/StringDefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/StringDefinitionHelper.php
new file mode 100644 (file)
index 0000000..b694952
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\StringDefinition;
+
+/**
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class StringDefinitionHelper implements DefinitionHelper
+{
+    /**
+     * @var string
+     */
+    private $expression;
+
+    public function __construct($expression)
+    {
+        $this->expression = $expression;
+    }
+
+    /**
+     * @param string $entryName Container entry name
+     *
+     * @return StringDefinition
+     */
+    public function getDefinition($entryName)
+    {
+        return new StringDefinition($entryName, $this->expression);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ValueDefinitionHelper.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Helper/ValueDefinitionHelper.php
new file mode 100644 (file)
index 0000000..436ebe3
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Helper;
+
+use DI\Definition\ValueDefinition;
+
+/**
+ * Helps defining a value.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ValueDefinitionHelper implements DefinitionHelper
+{
+    /**
+     * @var mixed
+     */
+    private $value;
+
+    /**
+     * @param mixed $value
+     */
+    public function __construct($value)
+    {
+        $this->value = $value;
+    }
+
+    /**
+     * @param string $entryName Container entry name
+     * @return ValueDefinition
+     */
+    public function getDefinition($entryName)
+    {
+        return new ValueDefinition($entryName, $this->value);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/InstanceDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/InstanceDefinition.php
new file mode 100644 (file)
index 0000000..7be8e65
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Defines injections on an existing class instance.
+ *
+ * @since  5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class InstanceDefinition implements Definition
+{
+    /**
+     * Instance on which to inject dependencies.
+     *
+     * @var object
+     */
+    private $instance;
+
+    /**
+     * @var ObjectDefinition
+     */
+    private $objectDefinition;
+
+    /**
+     * @param object          $instance
+     * @param ObjectDefinition $objectDefinition
+     */
+    public function __construct($instance, ObjectDefinition $objectDefinition)
+    {
+        $this->instance = $instance;
+        $this->objectDefinition = $objectDefinition;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        // Name are superfluous for instance definitions
+        return '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::PROTOTYPE;
+    }
+
+    /**
+     * @return object
+     */
+    public function getInstance()
+    {
+        return $this->instance;
+    }
+
+    /**
+     * @return ObjectDefinition
+     */
+    public function getObjectDefinition()
+    {
+        return $this->objectDefinition;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition.php
new file mode 100644 (file)
index 0000000..03cbf99
--- /dev/null
@@ -0,0 +1,337 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Definition\ObjectDefinition\MethodInjection;
+use DI\Definition\ObjectDefinition\PropertyInjection;
+use DI\Scope;
+use ReflectionClass;
+
+/**
+ * Defines how an object can be instantiated.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ObjectDefinition implements Definition, CacheableDefinition, HasSubDefinition
+{
+    /**
+     * Entry name (most of the time, same as $classname)
+     * @var string
+     */
+    private $name;
+
+    /**
+     * Class name (if null, then the class name is $name)
+     * @var string|null
+     */
+    private $className;
+
+    /**
+     * Constructor parameter injection
+     * @var MethodInjection|null
+     */
+    private $constructorInjection;
+
+    /**
+     * Property injections
+     * @var PropertyInjection[]
+     */
+    private $propertyInjections = [];
+
+    /**
+     * Method calls
+     * @var MethodInjection[][]
+     */
+    private $methodInjections = [];
+
+    /**
+     * @var string|null
+     */
+    private $scope;
+
+    /**
+     * @var boolean|null
+     */
+    private $lazy;
+
+    /**
+     * Store if the class exists. Storing it (in cache) avoids recomputing this.
+     *
+     * @var bool
+     */
+    private $classExists;
+
+    /**
+     * Store if the class is instantiable. Storing it (in cache) avoids recomputing this.
+     *
+     * @var bool
+     */
+    private $isInstantiable;
+
+    /**
+     * @param string $name Class name
+     * @param string $className
+     */
+    public function __construct($name, $className = null)
+    {
+        $this->name = (string) $name;
+        $this->setClassName($className);
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @param string|null $className
+     */
+    public function setClassName($className)
+    {
+        $this->className = $className;
+
+        $this->updateCache();
+    }
+
+    /**
+     * @return string Class name
+     */
+    public function getClassName()
+    {
+        if ($this->className !== null) {
+            return $this->className;
+        }
+        return $this->name;
+    }
+
+    /**
+     * @return MethodInjection|null
+     */
+    public function getConstructorInjection()
+    {
+        return $this->constructorInjection;
+    }
+
+    /**
+     * @param MethodInjection $constructorInjection
+     */
+    public function setConstructorInjection(MethodInjection $constructorInjection)
+    {
+        $this->constructorInjection = $constructorInjection;
+    }
+
+    /**
+     * @return PropertyInjection[] Property injections
+     */
+    public function getPropertyInjections()
+    {
+        return $this->propertyInjections;
+    }
+
+    /**
+     * @param string $propertyName
+     * @return PropertyInjection
+     */
+    public function getPropertyInjection($propertyName)
+    {
+        return isset($this->propertyInjections[$propertyName]) ? $this->propertyInjections[$propertyName] : null;
+    }
+
+    /**
+     * @param PropertyInjection $propertyInjection
+     */
+    public function addPropertyInjection(PropertyInjection $propertyInjection)
+    {
+        $this->propertyInjections[$propertyInjection->getPropertyName()] = $propertyInjection;
+    }
+
+    /**
+     * @return MethodInjection[] Method injections
+     */
+    public function getMethodInjections()
+    {
+        // Return array leafs
+        $injections = [];
+        array_walk_recursive($this->methodInjections, function ($injection) use (&$injections) {
+            $injections[] = $injection;
+        });
+        return $injections;
+    }
+
+    /**
+     * @param MethodInjection $methodInjection
+     */
+    public function addMethodInjection(MethodInjection $methodInjection)
+    {
+        $method = $methodInjection->getMethodName();
+        if (! isset($this->methodInjections[$method])) {
+            $this->methodInjections[$method] = [];
+        }
+        $this->methodInjections[$method][] = $methodInjection;
+    }
+
+    /**
+     * @param string $scope
+     */
+    public function setScope($scope)
+    {
+        $this->scope = $scope;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return $this->scope ?: Scope::SINGLETON;
+    }
+
+    /**
+     * @param boolean|null $lazy
+     */
+    public function setLazy($lazy)
+    {
+        $this->lazy = $lazy;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isLazy()
+    {
+        if ($this->lazy !== null) {
+            return $this->lazy;
+        } else {
+            // Default value
+            return false;
+        }
+    }
+
+    /**
+     * @return bool
+     */
+    public function classExists()
+    {
+        return $this->classExists;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isInstantiable()
+    {
+        return $this->isInstantiable;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSubDefinitionName()
+    {
+        return $this->getClassName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSubDefinition(Definition $definition)
+    {
+        if (! $definition instanceof ObjectDefinition) {
+            return;
+        }
+
+        // The current prevails
+        if ($this->className === null) {
+            $this->setClassName($definition->className);
+        }
+        if ($this->scope === null) {
+            $this->scope = $definition->scope;
+        }
+        if ($this->lazy === null) {
+            $this->lazy = $definition->lazy;
+        }
+
+        // Merge constructor injection
+        $this->mergeConstructorInjection($definition);
+
+        // Merge property injections
+        $this->mergePropertyInjections($definition);
+
+        // Merge method injections
+        $this->mergeMethodInjections($definition);
+    }
+
+    private function mergeConstructorInjection(ObjectDefinition $definition)
+    {
+        if ($definition->getConstructorInjection() !== null) {
+            if ($this->constructorInjection !== null) {
+                // Merge
+                $this->constructorInjection->merge($definition->getConstructorInjection());
+            } else {
+                // Set
+                $this->constructorInjection = $definition->getConstructorInjection();
+            }
+        }
+    }
+
+    private function mergePropertyInjections(ObjectDefinition $definition)
+    {
+        foreach ($definition->getPropertyInjections() as $propertyName => $propertyInjection) {
+            if (! array_key_exists($propertyName, $this->propertyInjections)) {
+                // Add
+                $this->propertyInjections[$propertyName] = $propertyInjection;
+            }
+        }
+    }
+
+    private function mergeMethodInjections(ObjectDefinition $definition)
+    {
+        foreach ($definition->methodInjections as $methodName => $calls) {
+            if (array_key_exists($methodName, $this->methodInjections)) {
+                $this->mergeMethodCalls($calls, $methodName);
+            } else {
+                // Add
+                $this->methodInjections[$methodName] = $calls;
+            }
+        }
+    }
+
+    private function mergeMethodCalls(array $calls, $methodName)
+    {
+        foreach ($calls as $index => $methodInjection) {
+            // Merge
+            if (array_key_exists($index, $this->methodInjections[$methodName])) {
+                // Merge
+                $this->methodInjections[$methodName][$index]->merge($methodInjection);
+            } else {
+                // Add
+                $this->methodInjections[$methodName][$index] = $methodInjection;
+            }
+        }
+    }
+
+    private function updateCache()
+    {
+        $className = $this->getClassName();
+
+        $this->classExists = class_exists($className) || interface_exists($className);
+
+        if (! $this->classExists) {
+            $this->isInstantiable = false;
+            return;
+        }
+
+        $class = new ReflectionClass($className);
+        $this->isInstantiable = $class->isInstantiable();
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/MethodInjection.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/MethodInjection.php
new file mode 100644 (file)
index 0000000..5861641
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\ObjectDefinition;
+
+use DI\Definition\Definition;
+use DI\Scope;
+
+/**
+ * Describe an injection in an object method.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class MethodInjection implements Definition
+{
+    /**
+     * @var string
+     */
+    private $methodName;
+
+    /**
+     * @var array
+     */
+    private $parameters = [];
+
+    /**
+     * @param string $methodName
+     * @param array  $parameters
+     */
+    public function __construct($methodName, array $parameters = [])
+    {
+        $this->methodName = (string) $methodName;
+        $this->parameters = $parameters;
+    }
+
+    public static function constructor(array $parameters = [])
+    {
+        return new self('__construct', $parameters);
+    }
+
+    /**
+     * @return string Method name
+     */
+    public function getMethodName()
+    {
+        return $this->methodName;
+    }
+
+    /**
+     * @return array
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Replace the parameters of the definition by a new array of parameters.
+     *
+     * @param array $parameters
+     */
+    public function replaceParameters(array $parameters)
+    {
+        $this->parameters = $parameters;
+    }
+
+    public function merge(MethodInjection $definition)
+    {
+        // In case of conflicts, the current definition prevails.
+        $this->parameters = $this->parameters + $definition->parameters;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::PROTOTYPE;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/PropertyInjection.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ObjectDefinition/PropertyInjection.php
new file mode 100644 (file)
index 0000000..07ecf9e
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\ObjectDefinition;
+
+/**
+ * Describe an injection in a class property.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class PropertyInjection
+{
+    /**
+     * Property name
+     * @var string
+     */
+    private $propertyName;
+
+    /**
+     * Value that should be injected in the property
+     * @var mixed
+     */
+    private $value;
+
+    /**
+     * Use for injecting in properties of parent classes: the class name
+     * must be the name of the parent class because private properties
+     * can be attached to the parent classes, not the one we are resolving.
+     * @var string|null
+     */
+    private $className;
+
+    /**
+     * @param string      $propertyName Property name
+     * @param mixed       $value        Value that should be injected in the property
+     * @param string|null $className
+     */
+    public function __construct($propertyName, $value, $className = null)
+    {
+        $this->propertyName = (string) $propertyName;
+        $this->value = $value;
+        $this->className = $className;
+    }
+
+    /**
+     * @return string Property name
+     */
+    public function getPropertyName()
+    {
+        return $this->propertyName;
+    }
+
+    /**
+     * @return string Value that should be injected in the property
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getClassName()
+    {
+        return $this->className;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/AliasResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/AliasResolver.php
new file mode 100644 (file)
index 0000000..b92772d
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\AliasDefinition;
+use DI\Definition\Definition;
+use Interop\Container\ContainerInterface;
+
+/**
+ * Resolves an alias definition to a value.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AliasResolver implements DefinitionResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * The resolver needs a container.
+     * This container will be used to get the entry to which the alias points to.
+     *
+     * @param ContainerInterface $container
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Resolve an alias definition to a value.
+     *
+     * This will return the entry the alias points to.
+     *
+     * @param AliasDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        return $this->container->get($definition->getTargetEntryName());
+    }
+
+    /**
+     * @param AliasDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return $this->container->has($definition->getTargetEntryName());
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ArrayResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ArrayResolver.php
new file mode 100644 (file)
index 0000000..dcc7682
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\ArrayDefinition;
+use DI\Definition\Definition;
+use DI\Definition\Helper\DefinitionHelper;
+use DI\DependencyException;
+use Exception;
+
+/**
+ * Resolves an array definition to a value.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ArrayResolver implements DefinitionResolver
+{
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * @param DefinitionResolver $definitionResolver Used to resolve nested definitions.
+     */
+    public function __construct(DefinitionResolver $definitionResolver)
+    {
+        $this->definitionResolver = $definitionResolver;
+    }
+
+    /**
+     * Resolve an array definition to a value.
+     *
+     * An array definition can contain simple values or references to other entries.
+     *
+     * @param ArrayDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $values = $definition->getValues();
+
+        $values = $this->resolveNestedDefinitions($definition, $values);
+
+        return $values;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+
+    private function resolveNestedDefinitions(ArrayDefinition $definition, array $values)
+    {
+        foreach ($values as $key => $value) {
+            if ($value instanceof DefinitionHelper) {
+                $values[$key] = $this->resolveDefinition($value, $definition, $key);
+            }
+        }
+
+        return $values;
+    }
+
+    private function resolveDefinition(DefinitionHelper $value, ArrayDefinition $definition, $key)
+    {
+        try {
+            return $this->definitionResolver->resolve($value->getDefinition(''));
+        } catch (DependencyException $e) {
+            throw $e;
+        } catch (Exception $e) {
+            throw new DependencyException(sprintf(
+                "Error while resolving %s[%s]. %s",
+                $definition->getName(),
+                $key,
+                $e->getMessage()
+            ), 0, $e);
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DecoratorResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DecoratorResolver.php
new file mode 100644 (file)
index 0000000..db19a60
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\DecoratorDefinition;
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\Definition;
+use Interop\Container\ContainerInterface;
+
+/**
+ * Resolves a decorator definition to a value.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DecoratorResolver implements DefinitionResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * The resolver needs a container. This container will be passed to the factory as a parameter
+     * so that the factory can access other entries of the container.
+     *
+     * @param ContainerInterface $container
+     * @param DefinitionResolver $definitionResolver Used to resolve nested definitions.
+     */
+    public function __construct(ContainerInterface $container, DefinitionResolver $definitionResolver)
+    {
+        $this->container = $container;
+        $this->definitionResolver = $definitionResolver;
+    }
+
+    /**
+     * Resolve a decorator definition to a value.
+     *
+     * This will call the callable of the definition and pass it the decorated entry.
+     *
+     * @param DecoratorDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $callable = $definition->getCallable();
+
+        if (! is_callable($callable)) {
+            throw new DefinitionException(sprintf(
+                'The decorator "%s" is not callable',
+                $definition->getName()
+            ));
+        }
+
+        $decoratedDefinition = $definition->getDecoratedDefinition();
+
+        if (! $decoratedDefinition instanceof Definition) {
+            if (! $definition->getSubDefinitionName()) {
+                throw new DefinitionException('Decorators cannot be nested in another definition');
+            }
+
+            throw new DefinitionException(sprintf(
+                'Entry "%s" decorates nothing: no previous definition with the same name was found',
+                $definition->getName()
+            ));
+        }
+
+        $decorated = $this->definitionResolver->resolve($decoratedDefinition);
+
+        return call_user_func($callable, $decorated, $this->container);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DefinitionResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/DefinitionResolver.php
new file mode 100644 (file)
index 0000000..a4358b1
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\Exception\DefinitionException;
+
+/**
+ * Resolves a definition to a value.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface DefinitionResolver
+{
+    /**
+     * Resolve a definition to a value.
+     *
+     * @param Definition $definition Object that defines how the value should be obtained.
+     * @param array      $parameters Optional parameters to use to build the entry.
+     *
+     * @throws DefinitionException If the definition cannot be resolved.
+     *
+     * @return mixed Value obtained from the definition.
+     */
+    public function resolve(Definition $definition, array $parameters = []);
+
+    /**
+     * Check if a definition can be resolved.
+     *
+     * @param Definition $definition Object that defines how the value should be obtained.
+     * @param array      $parameters Optional parameters to use to build the entry.
+     *
+     * @return bool
+     */
+    public function isResolvable(Definition $definition, array $parameters = []);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/EnvironmentVariableResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/EnvironmentVariableResolver.php
new file mode 100644 (file)
index 0000000..0a9849c
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\EnvironmentVariableDefinition;
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\Helper\DefinitionHelper;
+
+/**
+ * Resolves a environment variable definition to a value.
+ *
+ * @author James Harris <james.harris@icecave.com.au>
+ */
+class EnvironmentVariableResolver implements DefinitionResolver
+{
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * @var callable
+     */
+    private $variableReader;
+
+    public function __construct(DefinitionResolver $definitionResolver, $variableReader = 'getenv')
+    {
+        $this->definitionResolver = $definitionResolver;
+        $this->variableReader = $variableReader;
+    }
+
+    /**
+     * Resolve an environment variable definition to a value.
+     *
+     * @param EnvironmentVariableDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $value = call_user_func($this->variableReader, $definition->getVariableName());
+
+        if (false !== $value) {
+            return $value;
+        }
+
+        if (!$definition->isOptional()) {
+            throw new DefinitionException(sprintf(
+                "The environment variable '%s' has not been defined",
+                $definition->getVariableName()
+            ));
+        }
+
+        $value = $definition->getDefaultValue();
+
+        // Nested definition
+        if ($value instanceof DefinitionHelper) {
+            return $this->definitionResolver->resolve($value->getDefinition(''));
+        }
+
+        return $value;
+    }
+
+    /**
+     * @param EnvironmentVariableDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/FactoryResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/FactoryResolver.php
new file mode 100644 (file)
index 0000000..28c797b
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\FactoryDefinition;
+use DI\Definition\Definition;
+use Interop\Container\ContainerInterface;
+use Invoker\Invoker;
+use Invoker\ParameterResolver\NumericArrayResolver;
+
+/**
+ * Resolves a factory definition to a value.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class FactoryResolver implements DefinitionResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @var Invoker|null
+     */
+    private $invoker;
+
+    /**
+     * The resolver needs a container. This container will be passed to the factory as a parameter
+     * so that the factory can access other entries of the container.
+     *
+     * @param ContainerInterface $container
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Resolve a factory definition to a value.
+     *
+     * This will call the callable of the definition.
+     *
+     * @param FactoryDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $callable = $definition->getCallable();
+
+        if (! is_callable($callable)) {
+            throw new DefinitionException(sprintf(
+                'The factory definition "%s" is not callable',
+                $definition->getName()
+            ));
+        }
+
+        if (! $this->invoker) {
+            $this->invoker = new Invoker(new NumericArrayResolver, $this->container);
+        }
+
+        return $this->invoker->call($callable, [$this->container]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/InstanceInjector.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/InstanceInjector.php
new file mode 100644 (file)
index 0000000..eb825ba
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\InstanceDefinition;
+use DI\DependencyException;
+use Interop\Container\Exception\NotFoundException;
+
+/**
+ * Injects dependencies on an existing instance.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class InstanceInjector extends ObjectCreator
+{
+    /**
+     * Injects dependencies on an existing instance.
+     *
+     * @param InstanceDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        try {
+            $this->injectMethodsAndProperties($definition->getInstance(), $definition->getObjectDefinition());
+        } catch (NotFoundException $e) {
+            $message = sprintf(
+                "Error while injecting dependencies into %s: %s",
+                get_class($definition->getInstance()),
+                $e->getMessage()
+            );
+            throw new DependencyException($message, 0, $e);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ObjectCreator.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ObjectCreator.php
new file mode 100644 (file)
index 0000000..cc2169a
--- /dev/null
@@ -0,0 +1,258 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\Definition;
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\ObjectDefinition\PropertyInjection;
+use DI\Definition\Helper\DefinitionHelper;
+use DI\DependencyException;
+use DI\Proxy\ProxyFactory;
+use Exception;
+use Interop\Container\Exception\NotFoundException;
+use ProxyManager\Proxy\LazyLoadingInterface;
+use ReflectionClass;
+use ReflectionProperty;
+
+/**
+ * Create objects based on an object definition.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ObjectCreator implements DefinitionResolver
+{
+    /**
+     * @var ProxyFactory
+     */
+    private $proxyFactory;
+
+    /**
+     * @var ParameterResolver
+     */
+    private $parameterResolver;
+
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * @param DefinitionResolver $definitionResolver Used to resolve nested definitions.
+     * @param ProxyFactory       $proxyFactory       Used to create proxies for lazy injections.
+     */
+    public function __construct(
+        DefinitionResolver $definitionResolver,
+        ProxyFactory $proxyFactory
+    ) {
+        $this->definitionResolver = $definitionResolver;
+        $this->proxyFactory = $proxyFactory;
+        $this->parameterResolver = new ParameterResolver($definitionResolver);
+    }
+
+    /**
+     * Resolve a class definition to a value.
+     *
+     * This will create a new instance of the class using the injections points defined.
+     *
+     * @param ObjectDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        // Lazy?
+        if ($definition->isLazy()) {
+            return $this->createProxy($definition, $parameters);
+        }
+
+        return $this->createInstance($definition, $parameters);
+    }
+
+    /**
+     * The definition is not resolvable if the class is not instantiable (interface or abstract)
+     * or if the class doesn't exist.
+     *
+     * @param ObjectDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return $definition->isInstantiable();
+    }
+
+    /**
+     * Returns a proxy instance
+     *
+     * @param ObjectDefinition $definition
+     * @param array           $parameters
+     *
+     * @return LazyLoadingInterface Proxy instance
+     */
+    private function createProxy(ObjectDefinition $definition, array $parameters)
+    {
+        /** @noinspection PhpUnusedParameterInspection */
+        $proxy = $this->proxyFactory->createProxy(
+            $definition->getClassName(),
+            function (& $wrappedObject, $proxy, $method, $params, & $initializer) use ($definition, $parameters) {
+                $wrappedObject = $this->createInstance($definition, $parameters);
+                $initializer = null; // turning off further lazy initialization
+                return true;
+            }
+        );
+
+        return $proxy;
+    }
+
+    /**
+     * Creates an instance of the class and injects dependencies..
+     *
+     * @param ObjectDefinition $definition
+     * @param array            $parameters      Optional parameters to use to create the instance.
+     *
+     * @throws DefinitionException
+     * @throws DependencyException
+     * @return object
+     */
+    private function createInstance(ObjectDefinition $definition, array $parameters)
+    {
+        $this->assertClassExists($definition);
+
+        $classname = $definition->getClassName();
+        $classReflection = new ReflectionClass($classname);
+
+        $this->assertClassIsInstantiable($definition);
+
+        $constructorInjection = $definition->getConstructorInjection();
+
+        try {
+            $args = $this->parameterResolver->resolveParameters(
+                $constructorInjection,
+                $classReflection->getConstructor(),
+                $parameters
+            );
+
+            if (count($args) > 0) {
+                $object = $classReflection->newInstanceArgs($args);
+            } else {
+                $object = new $classname;
+            }
+
+            $this->injectMethodsAndProperties($object, $definition);
+        } catch (NotFoundException $e) {
+            throw new DependencyException(sprintf(
+                "Error while injecting dependencies into %s: %s",
+                $classReflection->getName(),
+                $e->getMessage()
+            ), 0, $e);
+        } catch (DefinitionException $e) {
+            throw DefinitionException::create($definition, sprintf(
+                "Entry %s cannot be resolved: %s",
+                $definition->getName(),
+                $e->getMessage()
+            ));
+        }
+
+        if (! $object) {
+            throw new DependencyException(sprintf(
+                "Entry %s cannot be resolved: %s could not be constructed",
+                $definition->getName(),
+                $classReflection->getName()
+            ));
+        }
+
+        return $object;
+    }
+
+    protected function injectMethodsAndProperties($object, ObjectDefinition $objectDefinition)
+    {
+        // Property injections
+        foreach ($objectDefinition->getPropertyInjections() as $propertyInjection) {
+            $this->injectProperty($object, $propertyInjection);
+        }
+
+        // Method injections
+        foreach ($objectDefinition->getMethodInjections() as $methodInjection) {
+            $methodReflection = new \ReflectionMethod($object, $methodInjection->getMethodName());
+            $args = $this->parameterResolver->resolveParameters($methodInjection, $methodReflection);
+
+            $methodReflection->invokeArgs($object, $args);
+        }
+    }
+
+    /**
+     * Inject dependencies into properties.
+     *
+     * @param object            $object            Object to inject dependencies into
+     * @param PropertyInjection $propertyInjection Property injection definition
+     *
+     * @throws DependencyException
+     * @throws DefinitionException
+     */
+    private function injectProperty($object, PropertyInjection $propertyInjection)
+    {
+        $propertyName = $propertyInjection->getPropertyName();
+
+        $className = $propertyInjection->getClassName();
+        $className = $className ?: get_class($object);
+        $property = new ReflectionProperty($className, $propertyName);
+
+        $value = $propertyInjection->getValue();
+
+        if ($value instanceof DefinitionHelper) {
+            /** @var Definition $nestedDefinition */
+            $nestedDefinition = $value->getDefinition('');
+
+            try {
+                $value = $this->definitionResolver->resolve($nestedDefinition);
+            } catch (DependencyException $e) {
+                throw $e;
+            } catch (Exception $e) {
+                throw new DependencyException(sprintf(
+                    "Error while injecting in %s::%s. %s",
+                    get_class($object),
+                    $propertyName,
+                    $e->getMessage()
+                ), 0, $e);
+            }
+        }
+
+        if (! $property->isPublic()) {
+            $property->setAccessible(true);
+        }
+        $property->setValue($object, $value);
+    }
+
+    private function assertClassExists(ObjectDefinition $definition)
+    {
+        if (! $definition->classExists()) {
+            throw DefinitionException::create($definition,
+            sprintf(
+                "Entry %s cannot be resolved: class %s doesn't exist",
+                $definition->getName(),
+                $definition->getClassName()
+            ));
+        }
+    }
+
+    private function assertClassIsInstantiable(ObjectDefinition $definition)
+    {
+        if (! $definition->isInstantiable()) {
+            throw DefinitionException::create($definition,
+            sprintf(
+                "Entry %s cannot be resolved: class %s is not instantiable",
+                $definition->getName(),
+                $definition->getClassName()
+            ));
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ParameterResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ParameterResolver.php
new file mode 100644 (file)
index 0000000..63b0ba9
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\Helper\DefinitionHelper;
+use DI\Definition\ObjectDefinition\MethodInjection;
+
+/**
+ * Resolves parameters for a function call.
+ *
+ * @since 4.2
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ParameterResolver
+{
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    /**
+     * @param DefinitionResolver $definitionResolver Will be used to resolve nested definitions.
+     */
+    public function __construct(DefinitionResolver $definitionResolver)
+    {
+        $this->definitionResolver = $definitionResolver;
+    }
+
+    /**
+     * @param MethodInjection             $definition
+     * @param \ReflectionFunctionAbstract $functionReflection
+     * @param array                       $parameters
+     *
+     * @throws DefinitionException A parameter has no value defined or guessable.
+     * @return array Parameters to use to call the function.
+     */
+    public function resolveParameters(
+        MethodInjection $definition = null,
+        \ReflectionFunctionAbstract $functionReflection = null,
+        array $parameters = []
+    ) {
+        $args = [];
+
+        if (! $functionReflection) {
+            return $args;
+        }
+
+        $definitionParameters = $definition ? $definition->getParameters() : array();
+
+        foreach ($functionReflection->getParameters() as $index => $parameter) {
+            if (array_key_exists($parameter->getName(), $parameters)) {
+                // Look in the $parameters array
+                $value = &$parameters[$parameter->getName()];
+            } elseif (array_key_exists($index, $definitionParameters)) {
+                // Look in the definition
+                $value = &$definitionParameters[$index];
+            } else {
+                // If the parameter is optional and wasn't specified, we take its default value
+                if ($parameter->isOptional()) {
+                    $args[] = $this->getParameterDefaultValue($parameter, $functionReflection);
+                    continue;
+                }
+
+                throw new DefinitionException(sprintf(
+                    "The parameter '%s' of %s has no value defined or guessable",
+                    $parameter->getName(),
+                    $this->getFunctionName($functionReflection)
+                ));
+            }
+
+            if ($value instanceof DefinitionHelper) {
+                $nestedDefinition = $value->getDefinition('');
+
+                // If the container cannot produce the entry, we can use the default parameter value
+                if ($parameter->isOptional() && !$this->definitionResolver->isResolvable($nestedDefinition)) {
+                    $value = $this->getParameterDefaultValue($parameter, $functionReflection);
+                } else {
+                    $value = $this->definitionResolver->resolve($nestedDefinition);
+                }
+            }
+
+            $args[] = &$value;
+        }
+
+        return $args;
+    }
+
+    /**
+     * Returns the default value of a function parameter.
+     *
+     * @param \ReflectionParameter        $parameter
+     * @param \ReflectionFunctionAbstract $function
+     *
+     * @throws DefinitionException Can't get default values from PHP internal classes and functions
+     * @return mixed
+     */
+    private function getParameterDefaultValue(
+        \ReflectionParameter $parameter,
+        \ReflectionFunctionAbstract $function
+    ) {
+        try {
+            return $parameter->getDefaultValue();
+        } catch (\ReflectionException $e) {
+            throw new DefinitionException(sprintf(
+                "The parameter '%s' of %s has no type defined or guessable. It has a default value, "
+                . "but the default value can't be read through Reflection because it is a PHP internal class.",
+                $parameter->getName(),
+                $this->getFunctionName($function)
+            ));
+        }
+    }
+
+    private function getFunctionName(\ReflectionFunctionAbstract $reflectionFunction)
+    {
+        if ($reflectionFunction instanceof \ReflectionMethod) {
+            return sprintf(
+                '%s::%s',
+                $reflectionFunction->getDeclaringClass()->getName(),
+                $reflectionFunction->getName()
+            );
+        } elseif ($reflectionFunction->isClosure()) {
+            return sprintf(
+                'closure defined in %s at line %d',
+                $reflectionFunction->getFileName(),
+                $reflectionFunction->getStartLine()
+            );
+        }
+
+        return $reflectionFunction->getName();
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ResolverDispatcher.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ResolverDispatcher.php
new file mode 100644 (file)
index 0000000..d93d57c
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\Exception\DefinitionException;
+use DI\Proxy\ProxyFactory;
+use Interop\Container\ContainerInterface;
+
+/**
+ * Dispatches to more specific resolvers.
+ *
+ * Dynamic dispatch pattern.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ResolverDispatcher implements DefinitionResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * @var ProxyFactory
+     */
+    private $proxyFactory;
+
+    private $valueResolver;
+    private $arrayResolver;
+    private $factoryResolver;
+    private $decoratorResolver;
+    private $aliasResolver;
+    private $objectResolver;
+    private $instanceResolver;
+    private $envVariableResolver;
+    private $stringResolver;
+
+    public function __construct(ContainerInterface $container, ProxyFactory $proxyFactory)
+    {
+        $this->container = $container;
+        $this->proxyFactory = $proxyFactory;
+    }
+
+    /**
+     * Resolve a definition to a value.
+     *
+     * @param Definition $definition Object that defines how the value should be obtained.
+     * @param array      $parameters Optional parameters to use to build the entry.
+     *
+     * @throws DefinitionException If the definition cannot be resolved.
+     *
+     * @return mixed Value obtained from the definition.
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $definitionResolver = $this->getDefinitionResolver($definition);
+
+        return $definitionResolver->resolve($definition, $parameters);
+    }
+
+    /**
+     * Check if a definition can be resolved.
+     *
+     * @param Definition $definition Object that defines how the value should be obtained.
+     * @param array      $parameters Optional parameters to use to build the entry.
+     *
+     * @return bool
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        $definitionResolver = $this->getDefinitionResolver($definition);
+
+        return $definitionResolver->isResolvable($definition, $parameters);
+    }
+
+    /**
+     * Returns a resolver capable of handling the given definition.
+     *
+     * @param Definition $definition
+     *
+     * @throws \RuntimeException No definition resolver was found for this type of definition.
+     * @return DefinitionResolver
+     */
+    private function getDefinitionResolver(Definition $definition)
+    {
+        switch (true) {
+            case ($definition instanceof \DI\Definition\ObjectDefinition):
+                if (! $this->objectResolver) {
+                    $this->objectResolver = new ObjectCreator($this, $this->proxyFactory);
+                }
+                return $this->objectResolver;
+            case ($definition instanceof \DI\Definition\ValueDefinition):
+                if (! $this->valueResolver) {
+                    $this->valueResolver = new ValueResolver();
+                }
+                return $this->valueResolver;
+            case ($definition instanceof \DI\Definition\AliasDefinition):
+                if (! $this->aliasResolver) {
+                    $this->aliasResolver = new AliasResolver($this->container);
+                }
+                return $this->aliasResolver;
+            case ($definition instanceof \DI\Definition\DecoratorDefinition):
+                if (! $this->decoratorResolver) {
+                    $this->decoratorResolver = new DecoratorResolver($this->container, $this);
+                }
+                return $this->decoratorResolver;
+            case ($definition instanceof \DI\Definition\FactoryDefinition):
+                if (! $this->factoryResolver) {
+                    $this->factoryResolver = new FactoryResolver($this->container);
+                }
+                return $this->factoryResolver;
+            case ($definition instanceof \DI\Definition\ArrayDefinition):
+                if (! $this->arrayResolver) {
+                    $this->arrayResolver = new ArrayResolver($this);
+                }
+                return $this->arrayResolver;
+            case ($definition instanceof \DI\Definition\EnvironmentVariableDefinition):
+                if (! $this->envVariableResolver) {
+                    $this->envVariableResolver = new EnvironmentVariableResolver($this);
+                }
+                return $this->envVariableResolver;
+            case ($definition instanceof \DI\Definition\StringDefinition):
+                if (! $this->stringResolver) {
+                    $this->stringResolver = new StringResolver($this->container);
+                }
+                return $this->stringResolver;
+            case ($definition instanceof \DI\Definition\InstanceDefinition):
+                if (! $this->instanceResolver) {
+                    $this->instanceResolver = new InstanceInjector($this, $this->proxyFactory);
+                }
+                return $this->instanceResolver;
+            default:
+                throw new \RuntimeException('No definition resolver was configured for definition of type ' . get_class($definition));
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/StringResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/StringResolver.php
new file mode 100644 (file)
index 0000000..96854d4
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\StringDefinition;
+use DI\DependencyException;
+use DI\NotFoundException;
+use Interop\Container\ContainerInterface;
+
+/**
+ * Resolves a string expression.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class StringResolver implements DefinitionResolver
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    /**
+     * The resolver needs a container.
+     * This container will be used to get the entry to which the alias points to.
+     *
+     * @param ContainerInterface $container
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->container = $container;
+    }
+
+    /**
+     * Resolve a value definition to a value.
+     *
+     * A value definition is simple, so this will just return the value of the ValueDefinition.
+     *
+     * @param StringDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        $expression = $definition->getExpression();
+
+        $result = preg_replace_callback('#\{([^\{\}]+)\}#', function (array $matches) use ($definition) {
+            try {
+                return $this->container->get($matches[1]);
+            } catch (NotFoundException $e) {
+                throw new DependencyException(sprintf(
+                    "Error while parsing string expression for entry '%s': %s",
+                    $definition->getName(),
+                    $e->getMessage()
+                ), 0, $e);
+            }
+        }, $expression);
+
+        if ($result === null) {
+            throw new \RuntimeException(sprintf('An unknown error occurred while parsing the string definition: \'%s\'', $expression));
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ValueResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Resolver/ValueResolver.php
new file mode 100644 (file)
index 0000000..04d98fc
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://mnapoli.github.com/PHP-DI/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Resolver;
+
+use DI\Definition\Definition;
+use DI\Definition\ValueDefinition;
+
+/**
+ * Resolves a value definition to a value.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ValueResolver implements DefinitionResolver
+{
+    /**
+     * Resolve a value definition to a value.
+     *
+     * A value definition is simple, so this will just return the value of the ValueDefinition.
+     *
+     * @param ValueDefinition $definition
+     *
+     * {@inheritdoc}
+     */
+    public function resolve(Definition $definition, array $parameters = [])
+    {
+        return $definition->getValue();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isResolvable(Definition $definition, array $parameters = [])
+    {
+        return true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/AnnotationReader.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/AnnotationReader.php
new file mode 100644 (file)
index 0000000..e12ab86
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Annotation\Inject;
+use DI\Annotation\Injectable;
+use DI\Definition\ObjectDefinition;
+use DI\Definition\EntryReference;
+use DI\Definition\Exception\AnnotationException;
+use DI\Definition\Exception\DefinitionException;
+use DI\Definition\ObjectDefinition\MethodInjection;
+use DI\Definition\ObjectDefinition\PropertyInjection;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Annotations\Reader;
+use Doctrine\Common\Annotations\SimpleAnnotationReader;
+use InvalidArgumentException;
+use PhpDocReader\PhpDocReader;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionParameter;
+use ReflectionProperty;
+use UnexpectedValueException;
+
+/**
+ * Provides DI definitions by reading annotations such as @ Inject and @ var annotations.
+ *
+ * Uses Autowiring, Doctrine's Annotations and regex docblock parsing.
+ * This source automatically includes the reflection source.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AnnotationReader implements DefinitionSource
+{
+    /**
+     * @var Reader
+     */
+    private $annotationReader;
+
+    /**
+     * @var PhpDocReader
+     */
+    private $phpDocReader;
+
+    /**
+     * @var bool
+     */
+    private $ignorePhpDocErrors;
+
+    public function __construct($ignorePhpDocErrors = false)
+    {
+        $this->ignorePhpDocErrors = (bool) $ignorePhpDocErrors;
+    }
+
+    /**
+     * {@inheritdoc}
+     * @throws AnnotationException
+     * @throws InvalidArgumentException The class doesn't exist
+     */
+    public function getDefinition($name)
+    {
+        if (!class_exists($name) && !interface_exists($name)) {
+            return null;
+        }
+
+        $class = new ReflectionClass($name);
+        $definition = new ObjectDefinition($name);
+
+        $this->readInjectableAnnotation($class, $definition);
+
+        // Browse the class properties looking for annotated properties
+        $this->readProperties($class, $definition);
+
+        // Browse the object's methods looking for annotated methods
+        $this->readMethods($class, $definition);
+
+        return $definition;
+    }
+
+    /**
+     * Browse the class properties looking for annotated properties.
+     */
+    private function readProperties(ReflectionClass $class, ObjectDefinition $definition)
+    {
+        foreach ($class->getProperties() as $property) {
+            if ($property->isStatic()) {
+                continue;
+            }
+            $this->readProperty($property, $definition);
+        }
+
+        // Read also the *private* properties of the parent classes
+        /** @noinspection PhpAssignmentInConditionInspection */
+        while ($class = $class->getParentClass()) {
+            foreach ($class->getProperties(ReflectionProperty::IS_PRIVATE) as $property) {
+                if ($property->isStatic()) {
+                    continue;
+                }
+                $this->readProperty($property, $definition, $class->getName());
+            }
+        }
+    }
+
+    private function readProperty(ReflectionProperty $property, ObjectDefinition $definition, $classname = null)
+    {
+        // Look for @Inject annotation
+        /** @var $annotation Inject */
+        $annotation = $this->getAnnotationReader()->getPropertyAnnotation($property, 'DI\Annotation\Inject');
+        if ($annotation === null) {
+            return null;
+        }
+
+        // @Inject("name") or look for @var content
+        $entryName = $annotation->getName() ?: $this->getPhpDocReader()->getPropertyClass($property);
+
+        if ($entryName === null) {
+            throw new AnnotationException(sprintf(
+                '@Inject found on property %s::%s but unable to guess what to inject, use a @var annotation',
+                $property->getDeclaringClass()->getName(),
+                $property->getName()
+            ));
+        }
+
+        $definition->addPropertyInjection(
+            new PropertyInjection($property->getName(), new EntryReference($entryName), $classname)
+        );
+    }
+
+    /**
+     * Browse the object's methods looking for annotated methods.
+     */
+    private function readMethods(ReflectionClass $class, ObjectDefinition $objectDefinition)
+    {
+        // This will look in all the methods, including those of the parent classes
+        foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
+            if ($method->isStatic()) {
+                continue;
+            }
+
+            $methodInjection = $this->getMethodInjection($method);
+
+            if (! $methodInjection) {
+                continue;
+            }
+
+            if ($method->isConstructor()) {
+                $objectDefinition->setConstructorInjection($methodInjection);
+            } else {
+                $objectDefinition->addMethodInjection($methodInjection);
+            }
+        }
+    }
+
+    private function getMethodInjection(ReflectionMethod $method)
+    {
+        // Look for @Inject annotation
+        /** @var $annotation Inject|null */
+        try {
+            $annotation = $this->getAnnotationReader()->getMethodAnnotation($method, 'DI\Annotation\Inject');
+        } catch (AnnotationException $e) {
+            throw new AnnotationException(sprintf(
+                '@Inject annotation on %s::%s is malformed. %s',
+                $method->getDeclaringClass()->getName(),
+                $method->getName(),
+                $e->getMessage()
+            ), 0, $e);
+        }
+        $annotationParameters = $annotation ? $annotation->getParameters() : [];
+
+        // @Inject on constructor is implicit
+        if (! ($annotation || $method->isConstructor())) {
+            return null;
+        }
+
+        $parameters = [];
+        foreach ($method->getParameters() as $index => $parameter) {
+            $entryName = $this->getMethodParameter($index, $parameter, $annotationParameters);
+
+            if ($entryName !== null) {
+                $parameters[$index] = new EntryReference($entryName);
+            }
+        }
+
+        if ($method->isConstructor()) {
+            return MethodInjection::constructor($parameters);
+        } else {
+            return new MethodInjection($method->getName(), $parameters);
+        }
+    }
+
+    /**
+     * @param int                 $parameterIndex
+     * @param ReflectionParameter $parameter
+     * @param array               $annotationParameters
+     *
+     * @return string|null Entry name or null if not found.
+     */
+    private function getMethodParameter($parameterIndex, ReflectionParameter $parameter, array $annotationParameters)
+    {
+        // @Inject has definition for this parameter (by index, or by name)
+        if (isset($annotationParameters[$parameterIndex])) {
+            return $annotationParameters[$parameterIndex];
+        }
+        if (isset($annotationParameters[$parameter->getName()])) {
+            return $annotationParameters[$parameter->getName()];
+        }
+
+        // Skip optional parameters if not explicitly defined
+        if ($parameter->isOptional()) {
+            return null;
+        }
+
+        // Try to use the type-hinting
+        $parameterClass = $parameter->getClass();
+        if ($parameterClass) {
+            return $parameterClass->getName();
+        }
+
+        // Last resort, look for @param tag
+        return $this->getPhpDocReader()->getParameterClass($parameter);
+    }
+
+    /**
+     * @return Reader The annotation reader
+     */
+    public function getAnnotationReader()
+    {
+        if ($this->annotationReader === null) {
+            AnnotationRegistry::registerAutoloadNamespace('DI\Annotation', __DIR__ . '/../../../');
+            $this->annotationReader = new SimpleAnnotationReader();
+            $this->annotationReader->addNamespace('DI\Annotation');
+        }
+
+        return $this->annotationReader;
+    }
+
+    /**
+     * @return PhpDocReader
+     */
+    private function getPhpDocReader()
+    {
+        if ($this->phpDocReader === null) {
+            $this->phpDocReader = new PhpDocReader($this->ignorePhpDocErrors);
+        }
+
+        return $this->phpDocReader;
+    }
+
+    private function readInjectableAnnotation(ReflectionClass $class, ObjectDefinition $definition)
+    {
+        try {
+            /** @var $annotation Injectable|null */
+            $annotation = $this->getAnnotationReader()
+                ->getClassAnnotation($class, 'DI\Annotation\Injectable');
+        } catch (UnexpectedValueException $e) {
+            throw new DefinitionException(sprintf(
+                'Error while reading @Injectable on %s: %s',
+                $class->getName(),
+                $e->getMessage()
+            ), 0, $e);
+        }
+
+        if (! $annotation) {
+            return;
+        }
+
+        if ($annotation->getScope()) {
+            $definition->setScope($annotation->getScope());
+        }
+        if ($annotation->isLazy() !== null) {
+            $definition->setLazy($annotation->isLazy());
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/Autowiring.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/Autowiring.php
new file mode 100644 (file)
index 0000000..730799b
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\ObjectDefinition;
+use DI\Definition\EntryReference;
+use DI\Definition\ObjectDefinition\MethodInjection;
+
+/**
+ * Reads DI class definitions using reflection.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class Autowiring implements DefinitionSource
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($name)
+    {
+        if (!class_exists($name) && !interface_exists($name)) {
+            return null;
+        }
+
+        $definition = new ObjectDefinition($name);
+
+        // Constructor
+        $class = new \ReflectionClass($name);
+        $constructor = $class->getConstructor();
+        if ($constructor && $constructor->isPublic()) {
+            $definition->setConstructorInjection(
+                MethodInjection::constructor($this->getParametersDefinition($constructor))
+            );
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Read the type-hinting from the parameters of the function.
+     */
+    private function getParametersDefinition(\ReflectionFunctionAbstract $constructor)
+    {
+        $parameters = [];
+
+        foreach ($constructor->getParameters() as $index => $parameter) {
+            // Skip optional parameters
+            if ($parameter->isOptional()) {
+                continue;
+            }
+
+            $parameterClass = $parameter->getClass();
+
+            if ($parameterClass) {
+                $parameters[$index] = new EntryReference($parameterClass->getName());
+            }
+        }
+
+        return $parameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/CachedDefinitionSource.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/CachedDefinitionSource.php
new file mode 100644 (file)
index 0000000..19bc7c0
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\CacheableDefinition;
+use DI\Definition\Definition;
+use Doctrine\Common\Cache\Cache;
+
+/**
+ * Caches another definition source.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class CachedDefinitionSource implements DefinitionSource
+{
+    /**
+     * Prefix for cache key, to avoid conflicts with other systems using the same cache
+     * @var string
+     */
+    const CACHE_PREFIX = 'DI\\Definition\\';
+
+    /**
+     * @var DefinitionSource
+     */
+    private $source;
+
+    /**
+     * @var Cache
+     */
+    private $cache;
+
+    public function __construct(DefinitionSource $source, Cache $cache)
+    {
+        $this->source = $source;
+        $this->cache = $cache;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($name)
+    {
+        // Look in cache
+        $definition = $this->fetchFromCache($name);
+
+        if ($definition === false) {
+            $definition = $this->source->getDefinition($name);
+
+            // Save to cache
+            if ($definition === null || ($definition instanceof CacheableDefinition)) {
+                $this->saveToCache($name, $definition);
+            }
+        }
+
+        return $definition;
+    }
+
+    /**
+     * @return Cache
+     */
+    public function getCache()
+    {
+        return $this->cache;
+    }
+
+    /**
+     * Fetches a definition from the cache
+     *
+     * @param string $name Entry name
+     * @return Definition|null|boolean The cached definition, null or false if the value is not already cached
+     */
+    private function fetchFromCache($name)
+    {
+        $cacheKey = self::CACHE_PREFIX . $name;
+
+        $data = $this->cache->fetch($cacheKey);
+
+        if ($data !== false) {
+            return $data;
+        }
+
+        return false;
+    }
+
+    /**
+     * Saves a definition to the cache
+     *
+     * @param string          $name Entry name
+     * @param Definition|null $definition
+     */
+    private function saveToCache($name, Definition $definition = null)
+    {
+        $cacheKey = self::CACHE_PREFIX . $name;
+
+        $this->cache->save($cacheKey, $definition);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionArray.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionArray.php
new file mode 100644 (file)
index 0000000..21b6eeb
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\ArrayDefinition;
+use DI\Definition\ObjectDefinition;
+use DI\Definition\Definition;
+use DI\Definition\FactoryDefinition;
+use DI\Definition\ValueDefinition;
+use DI\Definition\Helper\DefinitionHelper;
+
+/**
+ * Reads DI definitions from a PHP array.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefinitionArray implements DefinitionSource, MutableDefinitionSource
+{
+    const WILDCARD = '*';
+    /**
+     * Matches anything except "\"
+     */
+    const WILDCARD_PATTERN = '([^\\\\]+)';
+
+    /**
+     * DI definitions in a PHP array
+     * @var array
+     */
+    private $definitions = [];
+
+    /**
+     * @param array $definitions
+     */
+    public function __construct(array $definitions = [])
+    {
+        $this->definitions = $definitions;
+    }
+
+    /**
+     * @param array $definitions DI definitions in a PHP array indexed by the definition name.
+     */
+    public function addDefinitions(array $definitions)
+    {
+        // The newly added data prevails
+        // "for keys that exist in both arrays, the elements from the left-hand array will be used"
+        $this->definitions = $definitions + $this->definitions;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addDefinition(Definition $definition)
+    {
+        $this->definitions[$definition->getName()] = $definition;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($name)
+    {
+        // Look for the definition by name
+        if (array_key_exists($name, $this->definitions)) {
+            return $this->castDefinition($this->definitions[$name], $name);
+        }
+
+        // Look if there are wildcards definitions
+        foreach ($this->definitions as $key => $definition) {
+            if (strpos($key, self::WILDCARD) === false) {
+                continue;
+            }
+
+            // Turn the pattern into a regex
+            $key = addslashes($key);
+            $key = '#' . str_replace(self::WILDCARD, self::WILDCARD_PATTERN, $key) . '#';
+            if (preg_match($key, $name, $matches) === 1) {
+                $definition = $this->castDefinition($definition, $name);
+
+                // For a class definition, we replace * in the class name with the matches
+                // *Interface -> *Impl => FooInterface -> FooImpl
+                if ($definition instanceof ObjectDefinition) {
+                    array_shift($matches);
+                    $definition->setClassName(
+                        $this->replaceWildcards($definition->getClassName(), $matches)
+                    );
+                }
+
+                return $definition;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param mixed  $definition
+     * @param string $name
+     * @return Definition
+     */
+    private function castDefinition($definition, $name)
+    {
+        if ($definition instanceof DefinitionHelper) {
+            $definition = $definition->getDefinition($name);
+        }
+        if (! $definition instanceof Definition && is_array($definition)) {
+            $definition = new ArrayDefinition($name, $definition);
+        }
+        if ($definition instanceof \Closure) {
+            $definition = new FactoryDefinition($name, $definition);
+        }
+        if (! $definition instanceof Definition) {
+            $definition = new ValueDefinition($name, $definition);
+        }
+
+        return $definition;
+    }
+
+    /**
+     * Replaces all the wildcards in the string with the given replacements.
+     * @param string   $string
+     * @param string[] $replacements
+     * @return string
+     */
+    private function replaceWildcards($string, array $replacements)
+    {
+        foreach ($replacements as $replacement) {
+            $pos = strpos($string, self::WILDCARD);
+            if ($pos !== false) {
+                $string = substr_replace($string, $replacement, $pos, 1);
+            }
+        }
+
+        return $string;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionFile.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionFile.php
new file mode 100644 (file)
index 0000000..a79ccf8
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\Definition;
+use DI\Definition\Exception\DefinitionException;
+
+/**
+ * Reads DI definitions from a file returning a PHP array.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefinitionFile extends DefinitionArray
+{
+    /**
+     * @var bool
+     */
+    private $initialized = false;
+
+    /**
+     * File containing definitions, or null if the definitions are given as a PHP array.
+     * @var string|null
+     */
+    private $file;
+
+    /**
+     * @param string $file File in which the definitions are returned as an array.
+     */
+    public function __construct($file)
+    {
+        // Lazy-loading to improve performances
+        $this->file = $file;
+
+        parent::__construct([]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition($name)
+    {
+        $this->initialize();
+
+        return parent::getDefinition($name);
+    }
+
+    /**
+     * Lazy-loading of the definitions.
+     * @throws DefinitionException
+     */
+    private function initialize()
+    {
+        if ($this->initialized === true) {
+            return;
+        }
+
+        $definitions = require $this->file;
+
+        if (! is_array($definitions)) {
+            throw new DefinitionException("File {$this->file} should return an array of definitions");
+        }
+
+        $this->addDefinitions($definitions);
+
+        $this->initialized = true;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionSource.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/DefinitionSource.php
new file mode 100644 (file)
index 0000000..7aa5836
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\Definition;
+use DI\Definition\Exception\DefinitionException;
+
+/**
+ * Source of definitions for entries of the container.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface DefinitionSource
+{
+    /**
+     * Returns the DI definition for the entry name.
+     *
+     * @param string $name
+     *
+     * @throws DefinitionException An invalid definition was found.
+     * @return Definition|null
+     */
+    public function getDefinition($name);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/MutableDefinitionSource.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/MutableDefinitionSource.php
new file mode 100644 (file)
index 0000000..287cc32
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+namespace DI\Definition\Source;
+
+use DI\Definition\Definition;
+
+/**
+ * Describes a definition source to which we can add new definitions.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface MutableDefinitionSource extends DefinitionSource
+{
+    public function addDefinition(Definition $definition);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/SourceChain.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/Source/SourceChain.php
new file mode 100644 (file)
index 0000000..962d6f4
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition\Source;
+
+use DI\Definition\Definition;
+use DI\Definition\HasSubDefinition;
+
+/**
+ * Manages a chain of other definition sources.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class SourceChain implements DefinitionSource, MutableDefinitionSource
+{
+    /**
+     * @var DefinitionSource[]
+     */
+    private $sources;
+
+    /**
+     * @var DefinitionSource
+     */
+    private $rootSource;
+
+    /**
+     * @var MutableDefinitionSource|null
+     */
+    private $mutableSource;
+
+    /**
+     * @param DefinitionSource[] $sources
+     */
+    public function __construct(array $sources)
+    {
+        // We want a numerically indexed array to ease the traversal later
+        $this->sources = array_values($sources);
+        $this->rootSource = $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     * @param int $startIndex Use this parameter to start looking from a specific
+     *                        point in the source chain.
+     */
+    public function getDefinition($name, $startIndex = 0)
+    {
+        $count = count($this->sources);
+        for ($i = $startIndex; $i < $count; $i++) {
+            $source = $this->sources[$i];
+
+            $definition = $source->getDefinition($name);
+
+            if ($definition) {
+                if ($definition instanceof HasSubDefinition) {
+                    $this->resolveSubDefinition($definition, $i);
+                }
+                return $definition;
+            }
+        }
+
+        return null;
+    }
+
+    public function addDefinition(Definition $definition)
+    {
+        if (! $this->mutableSource) {
+            throw new \LogicException("The container's definition source has not been initialized correctly");
+        }
+
+        $this->mutableSource->addDefinition($definition);
+    }
+
+    public function setRootDefinitionSource(DefinitionSource $rootSource)
+    {
+        $this->rootSource = $rootSource;
+    }
+
+    private function resolveSubDefinition(HasSubDefinition $definition, $currentIndex)
+    {
+        $subDefinitionName = $definition->getSubDefinitionName();
+
+        if ($subDefinitionName === $definition->getName()) {
+            // Extending itself: look in the next sources only (else infinite recursion)
+            $subDefinition = $this->getDefinition($subDefinitionName, $currentIndex + 1);
+        } else {
+            // Extending another definition: look from the root
+            $subDefinition = $this->rootSource->getDefinition($subDefinitionName);
+        }
+
+        if ($subDefinition) {
+            $definition->setSubDefinition($subDefinition);
+        }
+    }
+
+    public function setMutableDefinitionSource(MutableDefinitionSource $mutableSource)
+    {
+        $this->mutableSource = $mutableSource;
+
+        array_unshift($this->sources, $mutableSource);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/StringDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/StringDefinition.php
new file mode 100644 (file)
index 0000000..27d2811
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Definition of a string composed of other strings.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class StringDefinition implements Definition
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var string
+     */
+    private $expression;
+
+    /**
+     * @param string $name       Entry name
+     * @param string $expression
+     */
+    public function __construct($name, $expression)
+    {
+        $this->name = $name;
+        $this->expression = $expression;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::SINGLETON;
+    }
+
+    /**
+     * @return string
+     */
+    public function getExpression()
+    {
+        return $this->expression;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ValueDefinition.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Definition/ValueDefinition.php
new file mode 100644 (file)
index 0000000..b0123f9
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Definition;
+
+use DI\Scope;
+
+/**
+ * Definition of a value for dependency injection.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ValueDefinition implements Definition
+{
+    /**
+     * Entry name
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var mixed
+     */
+    private $value;
+
+    /**
+     * @param string $name Entry name
+     * @param mixed $value
+     */
+    public function __construct($name, $value)
+    {
+        $this->name = $name;
+        $this->value = $value;
+    }
+
+    /**
+     * @return string Entry name
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * A value definition is like a constant, there is nothing to compute, the value is the same for everyone.
+     *
+     * {@inheritdoc}
+     */
+    public function getScope()
+    {
+        return Scope::SINGLETON;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/DependencyException.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/DependencyException.php
new file mode 100644 (file)
index 0000000..f410f38
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use Interop\Container\Exception\ContainerException;
+
+/**
+ * Exception for the Container
+ */
+class DependencyException extends \Exception implements ContainerException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/FactoryInterface.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/FactoryInterface.php
new file mode 100644 (file)
index 0000000..5627398
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+/**
+ * Describes the basic interface of a factory.
+ *
+ * @since 4.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface FactoryInterface
+{
+    /**
+     * Resolves an entry by its name. If given a class name, it will return a new instance of that class.
+     *
+     * @param string $name       Entry name or a class name.
+     * @param array  $parameters Optional parameters to use to build the entry. Use this to force specific
+     *                           parameters to specific values. Parameters not defined in this array will
+     *                           be automatically resolved.
+     *
+     * @throws \InvalidArgumentException The name parameter must be of type string.
+     * @throws DependencyException       Error while resolving the entry.
+     * @throws NotFoundException         No entry or class found for the given name.
+     * @return mixed
+     */
+    public function make($name, array $parameters = []);
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Invoker/DefinitionParameterResolver.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Invoker/DefinitionParameterResolver.php
new file mode 100644 (file)
index 0000000..f9718f2
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+namespace DI\Invoker;
+
+use DI\Definition\Helper\DefinitionHelper;
+use DI\Definition\Resolver\DefinitionResolver;
+use Invoker\ParameterResolver\ParameterResolver;
+use ReflectionFunctionAbstract;
+
+/**
+ * Resolves callable parameters using definitions.
+ *
+ * @since 5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class DefinitionParameterResolver implements ParameterResolver
+{
+    /**
+     * @var DefinitionResolver
+     */
+    private $definitionResolver;
+
+    public function __construct(DefinitionResolver $definitionResolver)
+    {
+        $this->definitionResolver = $definitionResolver;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getParameters(
+        ReflectionFunctionAbstract $reflection,
+        array $providedParameters,
+        array $resolvedParameters
+    ) {
+        // Skip parameters already resolved
+        if (! empty($resolvedParameters)) {
+            $providedParameters = array_diff_key($providedParameters, $resolvedParameters);
+        }
+
+        foreach ($providedParameters as $key => $value) {
+            if (! $value instanceof DefinitionHelper) {
+                continue;
+            }
+
+            $definition = $value->getDefinition('');
+            $value = $this->definitionResolver->resolve($definition);
+
+            if (is_int($key)) {
+                // Indexed by position
+                $resolvedParameters[$key] = $value;
+            } else {
+                // Indexed by parameter name
+                // TODO optimize?
+                $reflectionParameters = $reflection->getParameters();
+                foreach ($reflectionParameters as $reflectionParameter) {
+                    if ($key === $reflectionParameter->name) {
+                        $resolvedParameters[$reflectionParameter->getPosition()] = $value;
+                    }
+                }
+            }
+        }
+
+        return $resolvedParameters;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/InvokerInterface.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/InvokerInterface.php
new file mode 100644 (file)
index 0000000..65d0746
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+/**
+ * Invoke a callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+interface InvokerInterface extends \Invoker\InvokerInterface
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/NotFoundException.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/NotFoundException.php
new file mode 100644 (file)
index 0000000..0119578
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use Interop\Container\Exception\NotFoundException as BaseNotFoundException;
+
+/**
+ * Exception thrown when a class or a value is not found in the container
+ */
+class NotFoundException extends \Exception implements BaseNotFoundException
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Proxy/ProxyFactory.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Proxy/ProxyFactory.php
new file mode 100644 (file)
index 0000000..a937222
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Proxy;
+
+use ProxyManager\Configuration;
+use ProxyManager\Factory\LazyLoadingValueHolderFactory;
+use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
+
+/**
+ * Creates proxy classes.
+ *
+ * Wraps Ocramius/ProxyManager LazyLoadingValueHolderFactory.
+ *
+ * @see ProxyManager\Factory\LazyLoadingValueHolderFactory
+ *
+ * @since  5.0
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class ProxyFactory
+{
+    /**
+     * If true, write the proxies to disk to improve performances.
+     * @var boolean
+     */
+    private $writeProxiesToFile;
+
+    /**
+     * Directory where to write the proxies (if $writeProxiesToFile is enabled).
+     * @var string
+     */
+    private $proxyDirectory;
+
+    /**
+     * @var LazyLoadingValueHolderFactory|null
+     */
+    private $proxyManager;
+
+    public function __construct($writeProxiesToFile, $proxyDirectory = null)
+    {
+        $this->writeProxiesToFile = $writeProxiesToFile;
+        $this->proxyDirectory = $proxyDirectory;
+    }
+
+    /**
+     * 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)
+    {
+        $this->createProxyManager();
+
+        return $this->proxyManager->createProxy($className, $initializer);
+    }
+
+    private function createProxyManager()
+    {
+        if ($this->proxyManager !== null) {
+            return;
+        }
+
+        if (! class_exists('ProxyManager\Configuration')) {
+            throw new \RuntimeException('The ocramius/proxy-manager library is not installed. Lazy injection requires that library to be installed with Composer in order to work. Run "composer require ocramius/proxy-manager:~0.3".');
+        }
+
+        $config = new Configuration();
+
+        if ($this->writeProxiesToFile) {
+            $config->setProxiesTargetDir($this->proxyDirectory);
+            spl_autoload_register($config->getProxyAutoloader());
+        } else {
+            $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
+        }
+
+        $this->proxyManager = new LazyLoadingValueHolderFactory($config);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Reflection/CallableReflectionFactory.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Reflection/CallableReflectionFactory.php
new file mode 100644 (file)
index 0000000..f6062d3
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI\Reflection;
+
+/**
+ * Create a reflection object from a callable.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class CallableReflectionFactory
+{
+    /**
+     * @param callable $callable
+     *
+     * @return \ReflectionFunctionAbstract
+     */
+    public static function fromCallable($callable)
+    {
+        // Array callable
+        if (is_array($callable)) {
+            list($class, $method) = $callable;
+
+            return new \ReflectionMethod($class, $method);
+        }
+
+        // Closure
+        if ($callable instanceof \Closure) {
+            return new \ReflectionFunction($callable);
+        }
+
+        // Callable object (i.e. implementing __invoke())
+        if (is_object($callable) && method_exists($callable, '__invoke')) {
+            return new \ReflectionMethod($callable, '__invoke');
+        }
+
+        // Callable class (i.e. implementing __invoke())
+        if (is_string($callable) && class_exists($callable) && method_exists($callable, '__invoke')) {
+            return new \ReflectionMethod($callable, '__invoke');
+        }
+
+        // Standard function
+        return new \ReflectionFunction($callable);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Scope.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/Scope.php
new file mode 100644 (file)
index 0000000..cddd7d5
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+/**
+ * Scope enum.
+ *
+ * The scope defines the lifecycle of an entry.
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class Scope
+{
+    /**
+     * A singleton entry will be computed once and shared.
+     *
+     * For a class, only a single instance of the class will be created.
+     */
+    const SINGLETON = 'singleton';
+
+    /**
+     * A prototype entry will be recomputed each time it is asked.
+     *
+     * For a class, this will create a new instance each time.
+     */
+    const PROTOTYPE = 'prototype';
+
+    /**
+     * Method kept for backward compatibility, use the constant instead.
+     *
+     * @return string
+     */
+    public static function SINGLETON()
+    {
+        return self::SINGLETON;
+    }
+
+    /**
+     * Method kept for backward compatibility, use the constant instead.
+     *
+     * @return string
+     */
+    public static function PROTOTYPE()
+    {
+        return self::PROTOTYPE;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/functions.php b/wcfsetup/install/files/lib/system/api/php-di/php-di/src/DI/functions.php
new file mode 100644 (file)
index 0000000..313c71f
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * PHP-DI
+ *
+ * @link      http://php-di.org/
+ * @copyright Matthieu Napoli (http://mnapoli.fr/)
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
+ */
+
+namespace DI;
+
+use DI\Definition\EntryReference;
+use DI\Definition\Helper\ArrayDefinitionExtensionHelper;
+use DI\Definition\Helper\FactoryDefinitionHelper;
+use DI\Definition\Helper\ObjectDefinitionHelper;
+use DI\Definition\Helper\EnvironmentVariableDefinitionHelper;
+use DI\Definition\Helper\ValueDefinitionHelper;
+use DI\Definition\Helper\StringDefinitionHelper;
+
+if (! function_exists('DI\value')) {
+    /**
+     * Helper for defining an object.
+     *
+     * @param mixed $value
+     *
+     * @return ValueDefinitionHelper
+     */
+    function value($value)
+    {
+        return new ValueDefinitionHelper($value);
+    }
+}
+
+if (! function_exists('DI\object')) {
+    /**
+     * Helper for defining an object.
+     *
+     * @param string|null $className Class name of the object.
+     *                               If null, the name of the entry (in the container) will be used as class name.
+     *
+     * @return ObjectDefinitionHelper
+     */
+    function object($className = null)
+    {
+        return new ObjectDefinitionHelper($className);
+    }
+}
+
+if (! function_exists('DI\factory')) {
+    /**
+     * Helper for defining a container entry using a factory function/callable.
+     *
+     * @param callable $factory The factory is a callable that takes the container as parameter
+     *                          and returns the value to register in the container.
+     *
+     * @return FactoryDefinitionHelper
+     */
+    function factory($factory)
+    {
+        return new FactoryDefinitionHelper($factory);
+    }
+}
+
+if (! function_exists('DI\decorate')) {
+    /**
+     * Decorate the previous definition using a callable.
+     *
+     * Example:
+     *
+     *     'foo' => decorate(function ($foo, $container) {
+     *         return new CachedFoo($foo, $container->get('cache'));
+     *     })
+     *
+     * @param callable $callable The callable takes the decorated object as first parameter and
+     *                           the container as second.
+     *
+     * @return FactoryDefinitionHelper
+     */
+    function decorate($callable)
+    {
+        return new FactoryDefinitionHelper($callable, true);
+    }
+}
+
+if (! function_exists('DI\get')) {
+    /**
+     * Helper for referencing another container entry in an object definition.
+     *
+     * @param string $entryName
+     *
+     * @return EntryReference
+     */
+    function get($entryName)
+    {
+        return new EntryReference($entryName);
+    }
+}
+
+if (! function_exists('DI\link')) {
+    /**
+     * Helper for referencing another container entry in an object definition.
+     *
+     * @deprecated \DI\link() has been replaced by \DI\get()
+     *
+     * @param string $entryName
+     *
+     * @return EntryReference
+     */
+    function link($entryName)
+    {
+        return new EntryReference($entryName);
+    }
+}
+
+if (! function_exists('DI\env')) {
+    /**
+     * Helper for referencing environment variables.
+     *
+     * @param string $variableName The name of the environment variable.
+     * @param mixed $defaultValue The default value to be used if the environment variable is not defined.
+     *
+     * @return EnvironmentVariableDefinitionHelper
+     */
+    function env($variableName, $defaultValue = null)
+    {
+        // Only mark as optional if the default value was *explicitly* provided.
+        $isOptional = 2 === func_num_args();
+
+        return new EnvironmentVariableDefinitionHelper($variableName, $isOptional, $defaultValue);
+    }
+}
+
+if (! function_exists('DI\add')) {
+    /**
+     * Helper for extending another definition.
+     *
+     * Example:
+     *
+     *     'log.backends' => DI\add(DI\get('My\Custom\LogBackend'))
+     *
+     * or:
+     *
+     *     'log.backends' => DI\add([
+     *         DI\get('My\Custom\LogBackend')
+     *     ])
+     *
+     * @param mixed|array $values A value or an array of values to add to the array.
+     *
+     * @return ArrayDefinitionExtensionHelper
+     *
+     * @since 5.0
+     */
+    function add($values)
+    {
+        if (! is_array($values)) {
+            $values = [$values];
+        }
+
+        return new ArrayDefinitionExtensionHelper($values);
+    }
+}
+
+if (! function_exists('DI\string')) {
+    /**
+     * Helper for concatenating strings.
+     *
+     * Example:
+     *
+     *     'log.filename' => DI\string('{app.path}/app.log')
+     *
+     * @param string $expression A string expression. Use the `{}` placeholders to reference other container entries.
+     *
+     * @return StringDefinitionHelper
+     *
+     * @since 5.0
+     */
+    function string($expression)
+    {
+        return new StringDefinitionHelper((string) $expression);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitattributes b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitattributes
new file mode 100644 (file)
index 0000000..912292c
--- /dev/null
@@ -0,0 +1,7 @@
+# .gitattributes
+tests/ export-ignore
+phpunit.xml.dist export-ignore
+.travis.yml export-ignore
+
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitignore b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/.gitignore
new file mode 100644 (file)
index 0000000..f7be360
--- /dev/null
@@ -0,0 +1,5 @@
+.DS_Store
+.idea/*
+vendor/*
+composer.phar
+composer.lock
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/LICENSE b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/LICENSE
new file mode 100644 (file)
index 0000000..f859a31
--- /dev/null
@@ -0,0 +1,16 @@
+Copyright (C) 2015 Matthieu Napoli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/README.md b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/README.md
new file mode 100644 (file)
index 0000000..9a59545
--- /dev/null
@@ -0,0 +1,59 @@
+# PhpDocReader
+
+[![Build Status](https://img.shields.io/travis/PHP-DI/PhpDocReader.svg)](https://travis-ci.org/mnapoli/PhpDocReader)
+[![Coverage Status](https://img.shields.io/coveralls/PHP-DI/PhpDocReader.svg)](https://coveralls.io/r/mnapoli/PhpDocReader)
+![](https://img.shields.io/packagist/dt/PHP-DI/phpdoc-reader.svg)
+
+This project is used by:
+
+- [PHP-DI](http://php-di.org/)
+- [phockito-unit-php-di](https://github.com/balihoo/phockito-unit-php-di)
+
+Fork the README to add your project here.
+
+## Features
+
+PhpDocReader parses `@var` and `@param` values in PHP docblocks:
+
+```php
+
+use My\Cache\Backend;
+
+class Cache
+{
+    /**
+     * @var Backend
+     */
+    protected $backend;
+
+    /**
+     * @param Backend $backend
+     */
+    public function __construct($backend)
+    {
+    }
+}
+```
+
+It supports namespaced class names with the same resolution rules as PHP:
+
+- fully qualified name (starting with `\`)
+- imported class name (eg. `use My\Cache\Backend;`)
+- relative class name (from the current namespace, like `SubNamespace\MyClass`)
+- aliased class name  (eg. `use My\Cache\Backend as FooBar;`)
+
+Primitive types (`@var string`) are ignored (returns null), only valid class names are returned.
+
+## Usage
+
+```php
+$reader = new PhpDocReader();
+
+// Read a property type (@var phpdoc)
+$property = new ReflectionProperty($className, $propertyName);
+$propertyClass = $reader->getPropertyClass($property);
+
+// Read a parameter type (@param phpdoc)
+$parameter = new ReflectionParameter(array($className, $methodName), $parameterName);
+$parameterClass = $reader->getParameterClass($parameter);
+```
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/composer.json b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/composer.json
new file mode 100644 (file)
index 0000000..11da6a5
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    "name": "php-di/phpdoc-reader",
+    "type": "library",
+    "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)",
+    "keywords": ["phpdoc", "reflection"],
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "PhpDocReader\\": "src/PhpDocReader"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "UnitTest\\PhpDocReader\\": "tests/"
+        }
+    },
+    "require": {
+        "php": ">=5.3.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.6"
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/AnnotationException.php b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/AnnotationException.php
new file mode 100644 (file)
index 0000000..577d73f
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+namespace PhpDocReader;
+
+/**
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class AnnotationException extends \Exception
+{
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpDocReader.php b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpDocReader.php
new file mode 100644 (file)
index 0000000..6251371
--- /dev/null
@@ -0,0 +1,270 @@
+<?php
+
+namespace PhpDocReader;
+
+use PhpDocReader\PhpParser\UseStatementParser;
+use ReflectionParameter;
+use ReflectionProperty;
+
+/**
+ * PhpDoc reader
+ *
+ * @author Matthieu Napoli <matthieu@mnapoli.fr>
+ */
+class PhpDocReader
+{
+    /**
+     * @var UseStatementParser
+     */
+    private $parser;
+
+    private $ignoredTypes = array(
+        'bool',
+        'boolean',
+        'string',
+        'int',
+        'integer',
+        'float',
+        'double',
+        'array',
+        'object',
+        'callable',
+        'resource',
+    );
+
+    /**
+     * Enable or disable throwing errors when PhpDoc Errors occur (when parsing annotations)
+     * 
+     * @var bool
+     */
+    private $ignorePhpDocErrors;
+
+    /**
+     * 
+     * @param bool $ignorePhpDocErrors
+     */
+    public function __construct($ignorePhpDocErrors = false)
+    {
+        $this->parser = new UseStatementParser();
+        $this->ignorePhpDocErrors = $ignorePhpDocErrors;
+    }
+
+    /**
+     * Parse the docblock of the property to get the class of the var annotation.
+     *
+     * @param ReflectionProperty $property
+     *
+     * @throws AnnotationException
+     * @return string|null Type of the property (content of var annotation)
+     *
+     * @deprecated Use getPropertyClass instead.
+     */
+    public function getPropertyType(ReflectionProperty $property)
+    {
+        return $this->getPropertyClass($property);
+    }
+
+    /**
+     * Parse the docblock of the property to get the class of the var annotation.
+     *
+     * @param ReflectionProperty $property
+     *
+     * @throws AnnotationException
+     * @return string|null Type of the property (content of var annotation)
+     */
+    public function getPropertyClass(ReflectionProperty $property)
+    {
+        // Get the content of the @var annotation
+        if (preg_match('/@var\s+([^\s]+)/', $property->getDocComment(), $matches)) {
+            list(, $type) = $matches;
+        } else {
+            return null;
+        }
+
+        // Ignore primitive types
+        if (in_array($type, $this->ignoredTypes)) {
+            return null;
+        }
+
+        // Ignore types containing special characters ([], <> ...)
+        if (! preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) {
+            return null;
+        }
+
+        $class = $property->getDeclaringClass();
+
+        // If the class name is not fully qualified (i.e. doesn't start with a \)
+        if ($type[0] !== '\\') {
+            $alias = (false === $pos = strpos($type, '\\')) ? $type : substr($type, 0, $pos);
+            $loweredAlias = strtolower($alias);
+
+            // Retrieve "use" statements
+            $uses = $this->parser->parseUseStatements($property->getDeclaringClass());
+
+            $found = false;
+
+            if (isset($uses[$loweredAlias])) {
+                // Imported classes
+                if (false !== $pos) {
+                    $type = $uses[$loweredAlias] . substr($type, $pos);
+                } else {
+                    $type = $uses[$loweredAlias];
+                }
+                $found = true;
+            } elseif ($this->classExists($class->getNamespaceName() . '\\' . $type)) {
+                $type = $class->getNamespaceName() . '\\' . $type;
+                $found = true;
+            } elseif (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) {
+                // Class namespace
+                $type = $uses['__NAMESPACE__'] . '\\' . $type;
+                $found = true;
+            } elseif ($this->classExists($type)) {
+                // No namespace
+                $found = true;
+            }
+
+            if (!$found && !$this->ignorePhpDocErrors) {
+                throw new AnnotationException(sprintf(
+                    'The @var annotation on %s::%s contains a non existent class "%s". '
+                        . 'Did you maybe forget to add a "use" statement for this annotation?',
+                    $class->name,
+                    $property->getName(),
+                    $type
+                ));
+            }
+        }
+
+        if (!$this->classExists($type) && !$this->ignorePhpDocErrors) {
+            throw new AnnotationException(sprintf(
+                'The @var annotation on %s::%s contains a non existent class "%s"',
+                $class->name,
+                $property->getName(),
+                $type
+            ));
+        }
+
+        // Remove the leading \ (FQN shouldn't contain it)
+        $type = ltrim($type, '\\');
+
+        return $type;
+    }
+
+    /**
+     * Parse the docblock of the property to get the class of the param annotation.
+     *
+     * @param ReflectionParameter $parameter
+     *
+     * @throws AnnotationException
+     * @return string|null Type of the property (content of var annotation)
+     *
+     * @deprecated Use getParameterClass instead.
+     */
+    public function getParameterType(ReflectionParameter $parameter)
+    {
+        return $this->getParameterClass($parameter);
+    }
+
+    /**
+     * Parse the docblock of the property to get the class of the param annotation.
+     *
+     * @param ReflectionParameter $parameter
+     *
+     * @throws AnnotationException
+     * @return string|null Type of the property (content of var annotation)
+     */
+    public function getParameterClass(ReflectionParameter $parameter)
+    {
+        // Use reflection
+        $parameterClass = $parameter->getClass();
+        if ($parameterClass !== null) {
+            return $parameterClass->name;
+        }
+
+        $parameterName = $parameter->name;
+        // Get the content of the @param annotation
+        $method = $parameter->getDeclaringFunction();
+        if (preg_match('/@param\s+([^\s]+)\s+\$' . $parameterName . '/', $method->getDocComment(), $matches)) {
+            list(, $type) = $matches;
+        } else {
+            return null;
+        }
+
+        // Ignore primitive types
+        if (in_array($type, $this->ignoredTypes)) {
+            return null;
+        }
+
+        // Ignore types containing special characters ([], <> ...)
+        if (! preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) {
+            return null;
+        }
+
+        $class = $parameter->getDeclaringClass();
+
+        // If the class name is not fully qualified (i.e. doesn't start with a \)
+        if ($type[0] !== '\\') {
+            $alias = (false === $pos = strpos($type, '\\')) ? $type : substr($type, 0, $pos);
+            $loweredAlias = strtolower($alias);
+
+            // Retrieve "use" statements
+            $uses = $this->parser->parseUseStatements($class);
+
+            $found = false;
+
+            if (isset($uses[$loweredAlias])) {
+                // Imported classes
+                if (false !== $pos) {
+                    $type = $uses[$loweredAlias] . substr($type, $pos);
+                } else {
+                    $type = $uses[$loweredAlias];
+                }
+                $found = true;
+            } elseif ($this->classExists($class->getNamespaceName() . '\\' . $type)) {
+                $type = $class->getNamespaceName() . '\\' . $type;
+                $found = true;
+            } elseif (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) {
+                // Class namespace
+                $type = $uses['__NAMESPACE__'] . '\\' . $type;
+                $found = true;
+            } elseif ($this->classExists($type)) {
+                // No namespace
+                $found = true;
+            }
+
+            if (!$found && !$this->ignorePhpDocErrors) {
+                throw new AnnotationException(sprintf(
+                    'The @param annotation for parameter "%s" of %s::%s contains a non existent class "%s". '
+                        . 'Did you maybe forget to add a "use" statement for this annotation?',
+                    $parameterName,
+                    $class->name,
+                    $method->name,
+                    $type
+                ));
+            }
+        }
+
+        if (!$this->classExists($type) && !$this->ignorePhpDocErrors) {
+            throw new AnnotationException(sprintf(
+                'The @param annotation for parameter "%s" of %s::%s contains a non existent class "%s"',
+                $parameterName,
+                $class->name,
+                $method->name,
+                $type
+            ));
+        }
+
+        // Remove the leading \ (FQN shouldn't contain it)
+        $type = ltrim($type, '\\');
+
+        return $type;
+    }
+
+    /**
+     * @param string $class
+     * @return bool
+     */
+    private function classExists($class)
+    {
+        return class_exists($class) || interface_exists($class);
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/TokenParser.php b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/TokenParser.php
new file mode 100644 (file)
index 0000000..d3965d7
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+namespace PhpDocReader\PhpParser;
+
+/**
+ * Parses a file for namespaces/use/class declarations.
+ *
+ * Class taken and adapted from doctrine/annotations to avoid pulling the whole package.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Christian Kaps <christian.kaps@mohiva.com>
+ */
+class TokenParser
+{
+    /**
+     * The token list.
+     *
+     * @var array
+     */
+    private $tokens;
+
+    /**
+     * The number of tokens.
+     *
+     * @var int
+     */
+    private $numTokens;
+
+    /**
+     * The current array pointer.
+     *
+     * @var int
+     */
+    private $pointer = 0;
+
+    /**
+     * @param string $contents
+     */
+    public function __construct($contents)
+    {
+        $this->tokens = token_get_all($contents);
+
+        // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it
+        // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored
+        // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a
+        // docblock. If the first thing in the file is a class without a doc block this would cause calls to
+        // getDocBlock() on said class to return our long lost doc_comment. Argh.
+        // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least
+        // it's harmless to us.
+        token_get_all("<?php\n/**\n *\n */");
+
+        $this->numTokens = count($this->tokens);
+    }
+
+    /**
+     * Gets all use statements.
+     *
+     * @param string $namespaceName The namespace name of the reflected class.
+     *
+     * @return array A list with all found use statements.
+     */
+    public function parseUseStatements($namespaceName)
+    {
+        $statements = array();
+        while (($token = $this->next())) {
+            if ($token[0] === T_USE) {
+                $statements = array_merge($statements, $this->parseUseStatement());
+                continue;
+            }
+            if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
+                continue;
+            }
+
+            // Get fresh array for new namespace. This is to prevent the parser to collect the use statements
+            // for a previous namespace with the same name. This is the case if a namespace is defined twice
+            // or if a namespace with the same name is commented out.
+            $statements = array();
+        }
+
+        return $statements;
+    }
+
+    /**
+     * Gets the next non whitespace and non comment token.
+     *
+     * @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
+     *                                     If FALSE then only whitespace and normal comments are skipped.
+     *
+     * @return array|null The token if exists, null otherwise.
+     */
+    private function next($docCommentIsComment = true)
+    {
+        for ($i = $this->pointer; $i < $this->numTokens; $i++) {
+            $this->pointer++;
+            if ($this->tokens[$i][0] === T_WHITESPACE ||
+                $this->tokens[$i][0] === T_COMMENT ||
+                ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
+
+                continue;
+            }
+
+            return $this->tokens[$i];
+        }
+
+        return null;
+    }
+
+    /**
+     * Parses a single use statement.
+     *
+     * @return array A list with all found class names for a use statement.
+     */
+    private function parseUseStatement()
+    {
+        $class = '';
+        $alias = '';
+        $statements = array();
+        $explicitAlias = false;
+        while (($token = $this->next())) {
+            $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
+            if (!$explicitAlias && $isNameToken) {
+                $class .= $token[1];
+                $alias = $token[1];
+            } elseif ($explicitAlias && $isNameToken) {
+                $alias .= $token[1];
+            } elseif ($token[0] === T_AS) {
+                $explicitAlias = true;
+                $alias = '';
+            } elseif ($token === ',') {
+                $statements[strtolower($alias)] = $class;
+                $class = '';
+                $alias = '';
+                $explicitAlias = false;
+            } elseif ($token === ';') {
+                $statements[strtolower($alias)] = $class;
+                break;
+            } else {
+                break;
+            }
+        }
+
+        return $statements;
+    }
+
+    /**
+     * Gets the namespace.
+     *
+     * @return string The found namespace.
+     */
+    private function parseNamespace()
+    {
+        $name = '';
+        while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
+            $name .= $token[1];
+        }
+
+        return $name;
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/UseStatementParser.php b/wcfsetup/install/files/lib/system/api/php-di/phpdoc-reader/src/PhpDocReader/PhpParser/UseStatementParser.php
new file mode 100644 (file)
index 0000000..1b0d63d
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+namespace PhpDocReader\PhpParser;
+
+use SplFileObject;
+
+/**
+ * Parses a file for "use" declarations.
+ *
+ * Class taken and adapted from doctrine/annotations to avoid pulling the whole package.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Christian Kaps <christian.kaps@mohiva.com>
+ */
+class UseStatementParser
+{
+    /**
+     * @return array A list with use statements in the form (Alias => FQN).
+     */
+    public function parseUseStatements(\ReflectionClass $class)
+    {
+        if (false === $filename = $class->getFilename()) {
+            return array();
+        }
+
+        $content = $this->getFileContent($filename, $class->getStartLine());
+
+        if (null === $content) {
+            return array();
+        }
+
+        $namespace = preg_quote($class->getNamespaceName());
+        $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
+        $tokenizer = new TokenParser('<?php ' . $content);
+
+        $statements = $tokenizer->parseUseStatements($class->getNamespaceName());
+
+        return $statements;
+    }
+
+    /**
+     * Gets the content of the file right up to the given line number.
+     *
+     * @param string  $filename   The name of the file to load.
+     * @param integer $lineNumber The number of lines to read from file.
+     *
+     * @return string The content of the file.
+     */
+    private function getFileContent($filename, $lineNumber)
+    {
+        if ( ! is_file($filename)) {
+            return null;
+        }
+
+        $content = '';
+        $lineCnt = 0;
+        $file = new SplFileObject($filename);
+        while (!$file->eof()) {
+            if ($lineCnt++ == $lineNumber) {
+                break;
+            }
+
+            $content .= $file->fgets();
+        }
+
+        return $content;
+    }
+}