getMessage(), "\n"; exit -1; } spl_autoload_register(function ($class) { $class = str_replace(array('_', '\\'), '/', $class); if (file_exists('phar://' . __FILE__ . '/Zend_Di-2.0.0dev20110916/php/' . $class . '.php')) { include 'phar://' . __FILE__ . '/Zend_Di-2.0.0dev20110916/php/' . $class . '.php'; } }); $phar = new Phar(__FILE__); $sig = $phar->getSignature(); define('Zend_Di_SIG', $sig['hash']); define('Zend_Di_SIGTYPE', $sig['hash_type']); __HALT_COMPILER(); ?> º "os:102:"Zend_Di-2.0.0dev20110916/.xmlregistry/packages/zfcampus.org!packages/Zend_Di/2.0.0dev20110916-info.xml";fZend_Di-2.0.0dev20110916/.xmlregistry/packages/zfcampus.org!packages/Zend_Di/2.0.0dev20110916-info.xml~ dÌsN~ ­pB¶ package.xmlŸdÌsNŸJ*—¶6Zend_Di-2.0.0dev20110916/php/Zend/Di/Configuration.phpódÌsNóÒ­õ0¶GZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/AggregateDefinition.phpÜ dÌsNÜ ˆ³U¶CZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/ArrayDefinition.php@ dÌsN@ ÀèÛ¶KZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/Builder/InjectionMethod.php,dÌsN,#/T¶DZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/Builder/PhpClass.phpm dÌsNm  [¶EZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/BuilderDefinition.phpódÌsNó“G+¶<Zend_Di-2.0.0dev20110916/php/Zend/Di/Definition/Compiler.php7&dÌsN7&Õj ¶HZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/IntrospectionRuleset.php×dÌsNפ˭ݶEZend_Di-2.0.0dev20110916/php/Zend/Di/Definition/RuntimeDefinition.php³$dÌsN³$ï ~n¶3Zend_Di-2.0.0dev20110916/php/Zend/Di/Definition.php dÌsN pï¶<Zend_Di-2.0.0dev20110916/php/Zend/Di/DependencyInjection.php»dÌsN»JÓ0¶;Zend_Di-2.0.0dev20110916/php/Zend/Di/DependencyInjector.phpFTdÌsNFT{wx¶NZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/CircularDependencyException.php¤dÌsN¤¦ª„˶IZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/ClassNotFoundException.phpŸdÌsNŸÏ+GJ¶KZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/InvalidArgumentException.php dÌsN gç¦!¶KZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/InvalidCallbackException.phpidÌsNiRlm¶LZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/InvalidParamNameException.php dÌsN Š6÷]¶KZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/InvalidPositionException.phpŸdÌsNŸå -À¶KZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/MissingPropertyException.php¡dÌsN¡¹s¶CZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/RuntimeException.php†dÌsN†‹19<¶NZend_Di-2.0.0dev20110916/php/Zend/Di/Exception/UndefinedReferenceException.php¤dÌsN¤¥|D1¶2Zend_Di-2.0.0dev20110916/php/Zend/Di/Exception.php2dÌsN2YìŽ0¶8Zend_Di-2.0.0dev20110916/php/Zend/Di/InstanceManager.phpê(dÌsNê(Áÿ¨`¶8Zend_Di-2.0.0dev20110916/php/Zend/Di/ServiceLocation.php˜dÌsN˜6L{̶OZend_Di-2.0.0dev20110916/php/Zend/Di/ServiceLocator/DependencyInjectorProxy.php†dÌsN†‹9k¶AZend_Di-2.0.0dev20110916/php/Zend/Di/ServiceLocator/Generator.phpX-dÌsNX-['Íp¶IZend_Di-2.0.0dev20110916/php/Zend/Di/ServiceLocator/GeneratorInstance.php|dÌsN|³šh˜¶7Zend_Di-2.0.0dev20110916/php/Zend/Di/ServiceLocator.phpÀdÌsNÀg-Þå¶)Zend_Di-2.0.0dev20110916/php/Zend/Di/TODOØdÌsNØuÉù¶#Zend_Di-2.0.0dev20110916/php/PEAR2/dÌsNÿ/Zend_Di-2.0.0dev20110916/php/PEAR2/MultiErrors/dÌsNÿ<Zend_Di-2.0.0dev20110916/php/PEAR2/MultiErrors/Exception.phpNdÌsNNö7ˆ ¶ Zend_Di zfcampus.org/packages Package Zend_Di summary. Package detailed description here (found in README) Your Name handle handle@php.net yes 2011-09-16 2.0.0dev20110916 2.0.0dev20110916 devel devel New BSD License Package Zend_Di release notes for version 0.1.0. Package Zend_Di API release notes for version 0.1.0. 5.2.0 2.0.0a1 Zend_Di zfcampus.org/packages Package Zend_Di summary. Package detailed description here (found in README) Your Name handle handle@php.net yes 2011-09-16 2.0.0dev20110916 2.0.0dev20110916 devel devel New BSD License Package Zend_Di release notes for version 0.1.0. Package Zend_Di API release notes for version 0.1.0. 5.2.0 1.4.8 toArray(); } else { $data = iterator_to_array($data, true); } } elseif (!is_array($data)) { throw new Exception\InvalidArgumentException( 'Configuration data must be of type Zend\Config\Config or an array' ); } $this->data = $data; } public function configure(DependencyInjector $di) { if (isset($this->data['definition'])) { $this->configureDefinition($di, $this->data['definition']); } if (isset($this->data['definitions'])) { $this->configureDefinitions($di, $this->data['definitions']); } /* if (isset($this->data['compiler'])) { $this->configureCompiler($di, $this->data['compiler']); } */ if (isset($this->data['instance'])) { $this->configureInstance($di, $this->data['instance']); } } public function configureDefinitions(DependencyInjector $di, $definitionsData) { if ($di->hasDefinition()) { if (!$di->getDefinition() instanceof Definition\AggregateDefinition) { throw new Exception\InvalidArgumentException( 'In order to configure multiple definitions, the primary definition must not be set, ' . 'or must be of type AggregateDefintion' ); } } else { $di->setDefinition($di->createDefinition('Zend\Di\Definition\AggregateDefinition')); } foreach ($definitionsData as $definitionData) { $this->configureDefinition($di, $definitionData); } } public function configureDefinition(DependencyInjector $di, $definitionData) { if ($di->hasDefinition()) { $aggregateDef = $di->getDefinition(); if (!$aggregateDef instanceof Definition\AggregateDefinition) { throw new Exception\InvalidArgumentException( 'In order to configure multiple definitions, the primary definition must not be set, ' . 'or must be of type AggregateDefintion' ); } } /* else { $aggregateDef = $di->createDefinition('Zend\Di\Definition\AggregateDefinition'); $di->setDefinition($aggregateDef); } */ if (isset($definitionData['class'])) { $definition = $di->createDefinition($definitionData['class']); unset($definitionData['class']); if ($definition instanceof Definition\BuilderDefinition) { $definition->createClassesFromArray($definitionData); } else { // @todo other types } } if (isset($aggregateDef)) { $aggregateDef->addDefinition($definition); } else { $di->setDefinition($definition); } } public function configureInstance(DependencyInjector $di, $instanceData) { $im = $di->getInstanceManager(); foreach ($instanceData as $target => $data) { switch (strtolower($target)) { case 'aliases': case 'alias': foreach ($data as $n => $v) { $im->addAlias($n, $v); } break; case 'preferences': case 'preference': foreach ($data as $n => $v) { if (is_array($v)) { foreach ($v as $v2) { $im->addTypePreference($n, $v2); } } else { $im->addTypePreference($n, $v); } } break; default: foreach ($data as $n => $v) { switch ($n) { case 'parameters': case 'parameter': $im->setParameters($target, $v); break; case 'methods': case 'method': $im->setMethods($target, $v); break; } } } } } } definitions[] = $definition; } public function getClasses() { $classes = array(); foreach ($this->definitions as $definition) { $classes = array_merge($classes, $definition->getClasses()); } return $classes; } public function hasClass($class) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class)) { return true; } } return false; } public function getClassSupertypes($class) { $superTypes = array(); foreach ($this->definitions as $definition) { $superTypes = array_merge($superTypes, $definition->getClassSupertypes()); } return $superTypes; } public function getInstantiator($class) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class)) { return $definition->getInstantiator($class); } } return false; } public function hasInjectionMethods($class) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class)) { return $definition->hasInjectionMethods($class); } } return false; } public function hasInjectionMethod($class, $method) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class)) { return $definition->hasInjectionMethod($class, $method); } } return false; } public function getInjectionMethods($class) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class)) { return $definition->getInjectionMethods($class); } } return false; } public function getInjectionMethodParameters($class, $method) { foreach ($this->definitions as $definition) { if ($definition->hasClass($class) && $definition->hasInjectionMethod($class, $method)) { return $definition->getInjectionMethodParameters($class, $method); } } return false; } }dataArray = $dataArray; } public function getClasses() { return array_keys($this->dataArray); } public function hasClass($class) { return array_key_exists($class, $this->dataArray); } public function getClassSupertypes($class) { if (!isset($this->dataArray[$class])) { return array(); } if (!isset($this->dataArray[$class]['superTypes'])) { return array(); } return $this->dataArray[$class]['superTypes']; } public function getInstantiator($class) { if (!isset($this->dataArray[$class])) { return null; } if (!isset($this->dataArray[$class]['instantiator'])) { return '__construct'; } return $this->dataArray[$class]['instantiator']; } public function hasInjectionMethods($class) { if (!isset($this->dataArray[$class])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'])) { return array(); } return (count($this->dataArray[$class]['injectionMethods']) > 0); } public function hasInjectionMethod($class, $method) { if (!isset($this->dataArray[$class])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'][$method])) { return array(); } return array_key_exists($method, $this->dataArray[$class]['injectionMethods']); } public function getInjectionMethods($class) { if (!isset($this->dataArray[$class])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'])) { return array(); } return array_keys($this->dataArray[$class]['injectionMethods']); } public function getInjectionMethodParameters($class, $method) { if (!isset($this->dataArray[$class])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'])) { return array(); } if (!isset($this->dataArray[$class]['injectionMethods'][$method])) { return array(); } return $this->dataArray[$class]['injectionMethods'][$method]; } public function toArray() { return $this->dataArray; } } name = $name; return $this; } public function getName() { return $this->name; } public function addParameter($name, $class = null, $position = self::PARAMETER_POSTION_NEXT) { if ($position == self::PARAMETER_POSTION_NEXT) { $this->parameters[$name] = $class; } else { throw new \Exception('Implementation for parameter placement is incomplete'); } return $this; } public function getParameters() { return $this->parameters; } } name = $name; return $this; } public function getName() { return $this->name; } public function setInstantiator($instantiator) { $this->instantiator = $instantiator; return $this; } public function getInstantiator() { return $this->instantiator; } public function addSuperType($superType) { $this->superTypes[] = $superType; return $this; } public function getSuperTypes() { return $this->superTypes; } public function addInjectionMethod(InjectionMethod $injectionMethod) { $this->injectionMethods[] = $injectionMethod; return $this; } /** * Create and register an injection method * * Optionally takes the method name. * * This method may be used in lieu of addInjectionMethod() in * order to provide a more fluent interface for building classes with * injection methods. * * @param null|string $name * @return InjectionMethod */ public function createInjectionMethod($name = null) { $builder = $this->defaultMethodBuilder; $method = new $builder(); if (null !== $name) { $method->setName($name); } $this->addInjectionMethod($method); return $method; } /** * Override which class will be used by {@link createInjectionMethod()} * * @param string $class * @return PhpClass */ public function setMethodBuilder($class) { $this->defaultMethodBuilder = $class; return $this; } /** * Determine what class will be used by {@link createInjectionMethod()} * * Mainly to provide the ability to temporarily override the class used. * * @return string */ public function getMethodBuilder() { return $this->defaultMethodBuilder; } public function getInjectionMethods() { return $this->injectionMethods; } } $classInfo) { $class = new Builder\PhpClass(); $class->setName($className); foreach ($classInfo as $type => $typeData) { switch (strtolower($type)) { case 'supertypes': foreach ($typeData as $superType) { $class->addSuperType($superType); } break; case 'instantiator': $class->setInstantiator($typeData); break; case 'injectionmethods': case 'injectionmethod': foreach ($typeData as $injectionMethodName => $injectionMethodData) { $injectionMethod = new Builder\InjectionMethod(); $injectionMethod->setName($injectionMethodName); foreach ($injectionMethodData as $parameterName => $parameterType) { $parameterType = ($parameterType) ?: null; // force empty string to null $injectionMethod->addParameter($parameterName, $parameterType); } $class->addInjectionMethod($injectionMethod); } break; } } $this->addClass($class); } } public function addClass(Builder\PhpClass $phpClass) { $this->classes[] = $phpClass; return $this; } /** * Create a class builder object using default class builder class * * This method is a factory that can be used in place of addClass(). * * @param null|string $name Optional name of class to assign * @return Builder\PhpClass */ public function createClass($name = null) { $builderClass = $this->defaultClassBuilder; $class = new $builderClass(); if (null !== $name) { $class->setName($name); } $this->addClass($class); return $class; } /** * Set the class to use with {@link createClass()} * * @param string $class * @return BuilderDefinition */ public function setClassBuilder($class) { $this->defaultClassBuilder = $class; return $this; } /** * Get the class used for {@link createClass()} * * This is primarily to allow developers to temporarily override * the builder strategy. * * @return string */ public function getClassBuilder() { return $this->defaultClassBuilder; } public function getClasses() { $classNames = array(); foreach ($this->classes as $class) { $classNames[] = $class->getName(); } return $classNames; } public function hasClass($class) { foreach ($this->classes as $classObj) { if ($classObj->getName() === $class) { return true; } } return false; } protected function getClass($name) { foreach ($this->classes as $classObj) { if ($classObj->getName() === $name) { return $classObj; } } return false; } public function getClassSupertypes($class) { $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } return $class->getSuperTypes(); } public function getInstantiator($class) { $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } return $class->getInstantiator(); } public function hasInjectionMethods($class) { /* @var $class Zend\Di\Definition\Builder\PhpClass */ $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } return (count($class->getInjectionMethods()) > 0); } public function getInjectionMethods($class) { $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } $methods = $class->getInjectionMethods(); $methodNames = array(); foreach ($methods as $methodObj) { $methodNames[] = $methodObj->getName(); } return $methodNames; } public function hasInjectionMethod($class, $method) { $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } $methods = $class->getInjectionMethods(); foreach ($methods as $methodObj) { if ($methodObj->getName() === $method) { return true; } } return false; } public function getInjectionMethodParameters($class, $method) { $class = $this->getClass($class); if ($class === false) { throw new Exception\RuntimeException('Cannot find class object in this builder definition.'); } $methods = $class->getInjectionMethods(); foreach ($methods as $methodObj) { if ($methodObj->getName() === $method) { $method = $methodObj; } } if (!$method instanceof Builder\InjectionMethod) { throw new Exception\RuntimeException('Cannot find method object for method ' . $method . ' in this builder definition.'); } return $method->getParameters(); } } introspectionRuleset = $introspectionRuleset; } /** * * @return Zend\Di\Definition\IntrospectionRuleset */ public function getIntrospectionRuleset() { if ($this->introspectionRuleset == null) { $this->introspectionRuleset = new IntrospectionRuleset(); } return $this->introspectionRuleset; } public function addCodeScannerDirectory(DirectoryScanner $directoryScanner) { if ($this->directoryScanner == null) { $this->directoryScanner = new DirectoryScanner(); } $this->directoryScanner->addDirectoryScanner($directoryScanner); } public function addCodeScannerFile(FileScanner $fileScanner) { if ($this->directoryScanner == null) { $this->directoryScanner = new DirectoryScanner(); } $this->directoryScanner->addFileScanner($fileScanner); } /* public function addCodeReflection($reflectionFileOrClass, $followTypes = true) { //$this->codeScanners[] = array($reflectionFileOrClass, $followTypes); } */ public function compile() { $data = array(); $gRules = $this->getIntrospectionRuleset()->getGeneralRules(); /* @var $classScanner Zend\Code\Scanner\DerivedClassScanner */ foreach ($this->directoryScanner->getClasses(true, true) as $classScanner) { if ($gRules['excludedClassPatterns']) { foreach ($gRules['excludedClassPatterns'] as $ecPattern) { if (preg_match($ecPattern, $classScanner->getName())) { continue 2; } } } // determine supertypes $superTypes = array(); if (($parentClasses = $classScanner->getParentClasses()) !== null) { $superTypes = array_merge($superTypes, $parentClasses); } if (($interfaces = $classScanner->getInterfaces())) { $superTypes = array_merge($superTypes, $interfaces); } $className = $classScanner->getName(); $data[$className] = array( 'superTypes' => $superTypes, 'instantiator' => $this->compileScannerInstantiator($classScanner), 'injectionMethods' => $this->compileScannerInjectionMethods($classScanner), ); } return new ArrayDefinition($data); } public function compileScannerInstantiator(DerivedClassScanner $scannerClass) { if ($scannerClass->hasMethod('__construct')) { $construct = $scannerClass->getMethod('__construct'); if ($construct->isPublic()) { return '__construct'; } } return null; } public function compileScannerInjectionMethods(DerivedClassScanner $c) { // return value $methods = array(); // name of top level class (only, not derived) $className = $c->getName(); // constructor injection $cRules = $this->getIntrospectionRuleset()->getConstructorRules(); if ($cRules['enabled']) { if ($c->hasMethod('__construct')) { $constructScanner = $c->getMethod('__construct'); if ($constructScanner->isPublic() && $constructScanner->getNumberOfParameters() > 0) { do { // explicity in included classes if ($cRules['includedClasses'] && !in_array($className, $cRules['includedClasses'])) { break; } // explicity NOT in excluded classes if ($cRules['excludedClasses'] && in_array($className, $cRules['excludedClasses'])) { break; } gettype($constructScanner); $methods['__construct'] = $this->compileScannerInjectionMethodParmeters( $constructScanner, IntrospectionRuleset::TYPE_CONSTRUCTOR ); } while (false); } } } // setter injection $sRules = $this->getIntrospectionRuleset()->getSetterRules(); if ($sRules['enabled']) { /* @var $m ReflectionMethod */ foreach ($c->getMethods(true) as $m) { //$declaringClassName = $m->getDeclaringClass()->getName(); if (!$m->isPublic() || $m->getNumberOfParameters() == 0) { continue; } // explicitly in the include classes if ($sRules['includedClasses'] && !in_array($className, $sRules['includedClasses'])) { continue; } // explicity NOT in excluded classes if ($sRules['excludedClasses'] && (in_array($className, $sRules['excludedClasses']) //|| in_array($declaringClassName, $sRules['excludedClasses'])) )) { continue; } // declaring class // if there is a pattern & it does not match if ($sRules['pattern'] && !preg_match('/' . $sRules['pattern'] . '/', $m->getName())) { continue; } // if there are more than methodsMaxParameters, continue if ($sRules['methodMaximumParams'] && ($m->getNumberOfParameters() > $sRules['methodMaximumParams'])) { continue; } $methods[$m->getName()] = $this->compileScannerInjectionMethodParmeters( $m, IntrospectionRuleset::TYPE_SETTER ); } } // interface injection $iRules = $this->getIntrospectionRuleset()->getInterfaceRules(); if ($iRules['enabled']) { foreach ($c->getInterfaces(true) as $i) { // explicitly in the include interfaces if ($iRules['includedInterfaces'] && !in_array($i->getName(), $iRules['includedInterfaces'])) { continue; } // explicity NOT in excluded classes if ($iRules['excludedInterfaces'] && in_array($i->getName(), $iRules['excludedInterfaces'])) { continue; } // if there is a pattern, and it does not match, continue if ($iRules['pattern'] && !preg_match('#' . preg_quote($iRules['pattern'], '#') . '#', $i->getName())) { continue; } foreach ($i->getMethods() as $m) { $methods[$m->getName()] = $this->compileScannerInjectionMethodParmeters( $m, IntrospectionRuleset::TYPE_INTERFACE ); } } } return $methods; } /** * Return the parameters for a method * * 3 item array: * #1 - Class name, string if it exists, else null * #2 - Optional?, boolean * #3 - Instantiable, boolean if class exists, otherwise null * * @return array */ public function compileScannerInjectionMethodParmeters(MethodScanner $methodScanner, $introspectionType) { $params = array(); $parameterScanners = $methodScanner->getParameters(true); // rules according to type $rules = $this->getIntrospectionRuleset()->getRules($introspectionType); /* @var $p Zend\Code\Scanner\ParameterScanner */ foreach ($parameterScanners as $p) { $paramName = $p->getName(); // create array for this parameter $params[$paramName] = array(); // get name, and class if it exists $pcName = $p->getClass(); if ($this->directoryScanner->hasClass($pcName)) { $pc = $this->directoryScanner->getClass($pcName); } if ($pcName) { // @todo Should we throw an exception if its an unknown type? $params[$paramName][] = $pcName; } else { $params[$paramName][] = null; } if ($introspectionType == IntrospectionRuleset::TYPE_SETTER && $rules['paramCanBeOptional']) { $params[$paramName][] = true; } else { $params[$paramName][] = $p->isOptional(); } if (isset($pc)) { $params[$paramName][] = ($pc->isInstantiable()) ? true : false; } else { $params[$paramName][] = null; } } return $params; } } array('/[\w\\\\]*Exception\w*/') ); protected $constructorRules = array( 'enabled' => true, 'includedClasses' => array(), 'excludedClasses' => array(), ); protected $setterRules = array( 'enabled' => true, 'pattern' => '^set[A-Z]{1}\w*', 'includedClasses' => array(), 'excludedClasses' => array('ArrayObject'), 'methodMaximumParams' => 1, 'paramCanBeOptional' => true, ); protected $interfaceRules = array( 'enabled' => true, 'pattern' => '\w*Aware\w*', 'includedInterfaces' => array(), 'excludedInterfaces' => array() ); public function __construct($config = null) { } public function addRule($strategy, $name, $value) { switch ($strategy) { case self::TYPE_GENERAL: $rule = &$this->generalRules; break; case self::TYPE_CONSTRUCTOR: $rule = &$this->constructorRules; break; case self::TYPE_SETTER: $rule = &$this->setterRules; break; case self::TYPE_INTERFACE: $rule = &$this->interfaceRules; break; } if (!isset($rule[$name])) { throw new \InvalidArgumentException('The rule name provided is not a valid rule name.'); } switch (gettype($rule[$name])) { case 'array': array_push($rule[$name], $value); break; case 'boolean': $rule[$name] = (bool) $value; break; case 'string': $rule[$name] = (string) $value; break; } return $this; } public function getRules($ruleType) { if (!$ruleType) { return array( self::TYPE_GENERAL => $this->generalRules, self::TYPE_CONSTRUCTOR => $this->constructorRules, self::TYPE_SETTER => $this->setterRules, self::TYPE_INTERFACE => $this->interfaceRules ); } else { switch ($ruleType) { case self::TYPE_GENERAL: return $this->generalRules; case self::TYPE_CONSTRUCTOR: return $this->constructorRules; case self::TYPE_SETTER: return $this->setterRules; case self::TYPE_INTERFACE: return $this->interfaceRules; } } } public function addGeneralRule($name, $value) { $this->addRule(self::TYPE_GENERAL, $name, $value); } public function getGeneralRules() { return $this->generalRules; } public function addConstructorRule($name, $value) { $this->addRule(self::TYPE_CONSTRUCTOR, $name, $value); } public function getConstructorRules() { return $this->constructorRules; } public function addSetterRule($name, $value) { $this->addRule(self::TYPE_SETTER, $name, $value); } public function getSetterRules() { return $this->setterRules; } public function addInterfaceRule($name, $value) { $this->addRule(self::TYPE_INTERFACE, $name, $value); } public function getInterfaceRules() { return $this->interfaceRules; } } lookupType = $lookupType; } /** * @param IntrospectionRuleset $introspectionRuleset * @return void */ public function setIntrospectionRuleset(IntrospectionRuleset $introspectionRuleset) { $this->introspectionRuleset = $introspectionRuleset; } /** * @return Zend\Di\Definition\IntrospectionRuleset */ public function getIntrospectionRuleset() { if ($this->introspectionRuleset == null) { $this->introspectionRuleset = new IntrospectionRuleset(); } return $this->introspectionRuleset; } /** * Return nothing * * @return array */ public function getClasses() { return array(); } /** * Set the Lookup Type * * @param string $lookupType */ public function setLookupType($lookupType) { $this->lookupType = $lookupType; } /** * Track classes when using EXPLICIT lookups * @param string $class */ public function addClass($class) { $this->classes[] = $class; } /** * Return whether the class exists * * @param string $class * @return bool */ public function hasClass($class) { return class_exists($class, true); } /** * Return the supertypes for this class * * @param string $class * @return array of types */ public function getClassSupertypes($class) { return class_parents($class) + class_implements($class); } /** * Get the instiatiator * * @param string $class * @return string|callable */ public function getInstantiator($class) { $class = new \ReflectionClass($class); if ($class->isInstantiable()) { return '__construct'; } return false; } /** * Return if there are injection methods * * @param string $class * @return bool */ public function hasInjectionMethods($class) { $methods = $this->getInjectionMethods($class); return (count($methods) > 0); } /** * Return injection methods * * @param string $class * @param string $method * @return bool */ public function hasInjectionMethod($class, $method) { $injectionMethods = $this->getInjectionMethods($class); return (in_array($method, $injectionMethods)); } /** * Return an array of the injection methods * * @param string $class * @return array */ public function getInjectionMethods($class) { $introspectionRuleset = $this->getIntrospectionRuleset(); // setup $methods = array(); $c = new \ReflectionClass($class); $className = $c->getName(); if (array_key_exists($className, $this->injectionMethodCache)) { return array_keys($this->injectionMethodCache[$className]); } // constructor injection $cRules = $introspectionRuleset->getConstructorRules(); if ($cRules['enabled']) { $m = ($c->hasMethod('__construct')) ? $c->getMethod('__construct') : null; if ($m && $m->isPublic() && $m->getNumberOfParameters() > 0) { do { // explicity in included classes if ($cRules['includedClasses'] && !in_array($className, $cRules['includedClasses'])) { break; } // explicity NOT in excluded classes if ($cRules['excludedClasses'] && in_array($className, $cRules['excludedClasses'])) { break; } $methods['__construct'] = IntrospectionRuleset::TYPE_CONSTRUCTOR; } while (false); } } // setter injection $sRules = $introspectionRuleset->getSetterRules(); if ($sRules['enabled']) { /* @var $m ReflectionMethod */ foreach ($c->getMethods() as $m) { $declaringClassName = $m->getDeclaringClass()->getName(); if (!$m->isPublic() || $m->getNumberOfParameters() == 0) { continue; } // explicitly in the include classes if ($sRules['includedClasses'] && !in_array($className, $sRules['includedClasses'])) { continue; } // explicitly NOT in excluded classes if ($sRules['excludedClasses'] && (in_array($className, $sRules['excludedClasses']) || in_array($declaringClassName, $sRules['excludedClasses']))) { continue; } // declaring class // if there is a pattern & it does not match if ($sRules['pattern'] && !preg_match('/' . $sRules['pattern'] . '/', $m->getName())) { continue; } // if there are more than methodsMaxParameters, continue if ($sRules['methodMaximumParams'] && ($m->getNumberOfParameters() > $sRules['methodMaximumParams'])) { continue; } $methods[$m->getName()] = IntrospectionRuleset::TYPE_SETTER; } } // interface injection $iRules = $introspectionRuleset->getInterfaceRules(); if ($iRules['enabled']) { foreach ($c->getInterfaces() as $i) { // explicitly in the include interfaces if ($iRules['includedInterfaces'] && !in_array($i->getName(), $iRules['includedInterfaces'])) { continue; } // explicity NOT in excluded classes if ($iRules['excludedInterfaces'] && in_array($i->getName(), $iRules['excludedInterfaces'])) { continue; } // if there is a pattern, and it does not match, continue if ($iRules['pattern'] && !preg_match('#' . preg_quote($iRules['pattern'], '#') . '#', $i->getName())) { continue; } foreach ($i->getMethods() as $m) { $methods[$m->getName()] = IntrospectionRuleset::TYPE_INTERFACE; } } } $this->injectionMethodCache[$className] = $methods; return array_keys($this->injectionMethodCache[$className]); } /** * Return the parameters for a method * * 3 item array: * #1 - Class name, string if it exists, else null * #2 - Optional?, boolean * #3 - Instantiable, boolean if class exists, otherwise null * * @param string $class * @param string $method * @return array */ public function getInjectionMethodParameters($class, $method) { $params = array(); if (!$this->hasClass($class)) { throw new \Exception('Class not found'); } $c = new \ReflectionClass($class); $class = $c->getName(); // normalize provided name $injectionMethods = $this->getInjectionMethods($class); if (!in_array($method, $injectionMethods)) { throw new \Exception('Injectible method was not found.'); } $m = $c->getMethod($method); $introspectionType = $this->injectionMethodCache[$class][$m->getName()]; $rules = $this->getIntrospectionRuleset()->getRules($introspectionType); foreach ($m->getParameters() as $p) { /* @var $p ReflectionParameter */ $pc = $p->getClass(); $paramName = $p->getName(); $params[$paramName] = array(); // set the class name, if it exists $params[$paramName][] = ($pc !== null) ? $pc->getName() : null; // optional? if ($introspectionType == IntrospectionRuleset::TYPE_SETTER && $rules['paramCanBeOptional']) { $params[$paramName][] = true; } else { $params[$paramName][] = $p->isOptional(); } // instantiable? if ($pc !== null) { $params[$paramName][] = ($pc->isInstantiable()) ? true : false; } else { $params[$paramName][] = null; } } return $params; } }configure($config); } } /** * Provide a configuration object to configure this instance * * @param Configuration $config * @return void */ public function configure(Configuration $config) { $config->configure($this); } /** * @param Definition $definition * @return DependencyInjector */ public function setDefinition(Definition $definition) { $this->definition = $definition; return $this; } /** * Definition Factory * * @param string $class * @return Definition */ public function createDefinition($class) { $definition = new $class(); if (!$definition instanceof Definition) { throw new Exception\InvalidArgumentException( 'The class provided to the Definition factory ' . $class . ' does not implement the Definition interface' ); } return $definition; } /** * If this DependencyInjector has a definition currently attached * * @return bool */ public function hasDefinition() { return ($this->definition !== null); } /** * Return a definition, if one doesn't already exist, a Definition\RuntimeDefinition is created * * @return Definition */ public function getDefinition() { if ($this->definition == null) { $this->definition = $this->createDefinition('Zend\Di\Definition\RuntimeDefinition'); } return $this->definition; } /** * @return bool */ public function hasInstanceManager() { return ($this->instanceManager !== null); } /** * Set the instance manager * * @param InstanceManager $instanceManager * @return DependencyInjector */ public function setInstanceManager(InstanceManager $instanceManager) { $this->instanceManager = $instanceManager; return $this; } /** * * @return InstanceManager */ public function getInstanceManager() { if ($this->instanceManager == null) { $this->instanceManager = new InstanceManager(); } return $this->instanceManager; } /** * Lazy-load a class * * Attempts to load the class (or service alias) provided. If it has been * loaded before, the previous instance will be returned (unless the service * definition indicates shared instances should not be used). * * @param string $name Class name or service alias * @param null|array $params Parameters to pass to the constructor * @return object|null */ public function get($name, array $params = array()) { array_push($this->instanceContext, array('GET', $name)); $im = $this->getInstanceManager(); if ($params) { if (($fastHash = $im->hasSharedInstanceWithParameters($name, $params, true))) { array_pop($this->instanceContext); return $im->getSharedInstanceWithParameters(null, array(), $fastHash); } } else { if ($im->hasSharedInstance($name, $params)) { array_pop($this->instanceContext); return $im->getSharedInstance($name, $params); } } $instance = $this->newInstance($name, $params); array_pop($this->instanceContext); return $instance; } /** * Retrieve a new instance of a class * * Forces retrieval of a discrete instance of the given class, using the * constructor parameters provided. * * @param mixed $name Class name or service alias * @param array $params Parameters to pass to the constructor * @param bool $isShared * @return object|null */ public function newInstance($name, array $params = array(), $isShared = true) { // localize dependencies (this also will serve as poka-yoke) $definition = $this->getDefinition(); $instanceManager = $this->getInstanceManager(); if ($instanceManager->hasAlias($name)) { $class = $instanceManager->getClassFromAlias($name); $alias = $name; } else { $class = $name; $alias = null; } array_push($this->instanceContext, array('NEW', $class, $alias)); if (!$definition->hasClass($class)) { $aliasMsg = ($alias) ? '(specified by alias ' . $alias . ') ' : ''; throw new Exception\ClassNotFoundException( 'Class ' . $aliasMsg . $class . ' could not be located in provided definition.' ); } $instantiator = $definition->getInstantiator($class); $injectionMethods = $definition->getInjectionMethods($class); if ($instantiator === '__construct') { $object = $this->createInstanceViaConstructor($class, $params, $alias); if (in_array('__construct', $injectionMethods)) { unset($injectionMethods[array_search('__construct', $injectionMethods)]); } } elseif (is_callable($instantiator)) { $object = $this->createInstanceViaCallback($instantiator, $params, $alias); // @todo make sure we can create via a real object factory throw new \Exception('incomplete implementation'); } else { throw new Exception\RuntimeException('Invalid instantiator'); } if ($injectionMethods) { foreach ($injectionMethods as $injectionMethod) { $this->handleInjectionMethodForObject($object, $injectionMethod, $params, $alias); } $iConfig = ($instanceManager->hasAlias($alias) && $instanceManager->hasConfiguration($alias)) ? $instanceManager->getConfiguration($alias) : $instanceManager->getConfiguration(get_class($object)); if ($iConfig['methods']) { foreach ($iConfig['methods'] as $iConfigMethod => $iConfigMethodParams) { // skip methods processed by handleInjectionMethodForObject if (in_array($iConfigMethod, $injectionMethods) && $iConfigMethod !== '__construct') continue; call_user_func_array(array($object, $iConfigMethod), array_values($iConfigMethodParams)); } } } if ($isShared) { if ($params) { $this->getInstanceManager()->addSharedInstanceWithParameters($object, $name, $params); } else { $this->getInstanceManager()->addSharedInstance($object, $name); } } array_pop($this->instanceContext); return $object; } /** * @todo * Enter description here ... * @param unknown_type $object */ // public function resolveObjectDependencies($object) // { // // } /** * Retrieve a class instance based on class name * * Any parameters provided will be used as constructor arguments. If any * given parameter is a DependencyReference object, it will be fetched * from the container so that the instance may be injected. * * @param string $class * @param array $params * @param string|null $alias * @return object */ protected function createInstanceViaConstructor($class, $params, $alias = null) { $callParameters = array(); if ($this->definition->hasInjectionMethod($class, '__construct')) { $callParameters = $this->resolveMethodParameters($class, '__construct', $params, true, $alias); } // Hack to avoid Reflection in most common use cases switch (count($callParameters)) { case 0: return new $class(); case 1: return new $class($callParameters[0]); case 2: return new $class($callParameters[0], $callParameters[1]); case 3: return new $class($callParameters[0], $callParameters[1], $callParameters[2]); default: $r = new \ReflectionClass($class); return $r->newInstanceArgs($callParameters); } } /** * Get an object instance from the defined callback * * @param callback $callback * @param array $params * @param string $alias * @return object * @throws Exception\InvalidCallbackException */ protected function createInstanceViaCallback($callback, $params, $alias) { if (!is_callable($callback)) { throw new Exception\InvalidCallbackException('An invalid constructor callback was provided'); } if (is_array($callback)) { $class = (is_object($callback[0])) ? get_class($callback[0]) : $callback[0]; $method = $callback[1]; } $callParameters = array(); if ($this->definition->hasInjectionMethod($class, $method)) { $callParameters = $this->resolveMethodParameters($class, $method, $params, true, $alias); } return call_user_func_array($callback, $callParameters); } /** * This parameter will handle any injection methods and resolution of * dependencies for such methods * * @param object $object * @param string $method * @param array $params * @param string $alias */ protected function handleInjectionMethodForObject($object, $method, $params, $alias) { // @todo make sure to resolve the supertypes for both the object & definition $callParameters = $this->resolveMethodParameters(get_class($object), $method, $params, false, $alias); if ($callParameters !== array_fill(0, count($callParameters), null)) { call_user_func_array(array($object, $method), $callParameters); } } /** * Resolve parameters referencing other services * * @param string $class * @param string $method * @param array $callTimeUserParams * @param bool $isInstantiator * @param string $alias * @return array */ protected function resolveMethodParameters($class, $method, array $callTimeUserParams, $isInstantiator, $alias) { /* @var $isSubclassFunc Closure */ static $isSubclassFunc = null; static $isSubclassFuncCache = null; // null as unset, array when set $isSubclassFunc = function($class, $type) use (&$isSubclassFuncCache) { /* @see https://bugs.php.net/bug.php?id=53727 */ if ($isSubclassFuncCache === null) { $isSubclassFuncCache = array(); } if (!array_key_exists($class, $isSubclassFuncCache)) { $isSubclassFuncCache[$class] = class_parents($class, true) + class_implements($class, true); } return (isset($isSubclassFuncCache[$class][$type])); }; // parameters for this method, in proper order, to be returned $resolvedParams = array(); // parameter requirements from the definition $injectionMethodParameters = $this->definition->getInjectionMethodParameters($class, $method); // computed parameters array $computedParams = array( 'value' => array(), 'lookup' => array(), 'optional' => array() ); // retrieve instance configurations for all contexts $iConfig = array(); $aliases = $this->instanceManager->getAliases(); // for the alias in the dependency tree if ($alias && $this->instanceManager->hasConfiguration($alias)) { $iConfig['thisAlias'] = $this->instanceManager->getConfiguration($alias); } // for the current class in the dependency tree if ($this->instanceManager->hasConfiguration($class)) { $iConfig['thisClass'] = $this->instanceManager->getConfiguration($class); } // for the parent class, provided we are deeper than one node list($requestedClass, $requestedAlias) = ($this->instanceContext[0][0] == 'NEW') ? array($this->instanceContext[0][1], $this->instanceContext[0][2]) : array($this->instanceContext[1][1], $this->instanceContext[1][2]); if ($requestedClass != $class && $this->instanceManager->hasConfiguration($requestedClass)) { $iConfig['requestedClass'] = $this->instanceManager->getConfiguration($requestedClass); if ($requestedAlias) { $iConfig['requestedAlias'] = $this->instanceManager->getConfiguration($requestedAlias); } } // This is a 2 pass system for resolving parameters // first pass will find the sources, the second pass will order them and resolve lookups if they exist // MOST methods will only have a single parameters to resolve, so this should be fast foreach ($injectionMethodParameters as $name => $info) { list($type, $isOptional, $isTypeInstantiable) = $info; // PRIORITY 1 - consult user provided parameters if (isset($callTimeUserParams[$name])) { if (is_string($callTimeUserParams[$name])) { if ($this->instanceManager->hasAlias($callTimeUserParams[$name])) { // was an alias provided? $computedParams['lookup'][$name] = array( $callTimeUserParams[$name], $this->instanceManager->getClassFromAlias($callTimeUserParams[$name]) ); } elseif ($this->definition->hasClass($callTimeUserParams[$name])) { // was a known class provided? $computedParams['lookup'][$name] = array( $callTimeUserParams[$name], $callTimeUserParams[$name] ); } else { // must be a value $computedParams['value'][$name] = $callTimeUserParams[$name]; } } else { // int, float, null, object, etc $computedParams['value'][$name] = $callTimeUserParams[$name]; } continue; } // PRIORITY 2 -specific instance configuration (thisAlias) - this alias // PRIORITY 3 -THEN specific instance configuration (thisClass) - this class // PRIORITY 4 -THEN specific instance configuration (requestedAlias) - requested alias // PRIORITY 5 -THEN specific instance configuration (requestedClass) - requested class foreach (array('thisAlias', 'thisClass', 'requestedAlias', 'requestedClass') as $thisIndex) { // check the provided parameters config if (isset($iConfig[$thisIndex]['parameters'][$name])) { if (is_string($iConfig[$thisIndex]['parameters'][$name]) && isset($aliases[$iConfig[$thisIndex]['parameters'][$name]])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['parameters'][$name], $this->instanceManager->getClassFromAlias($iConfig[$thisIndex]['parameters'][$name]) ); } elseif (is_string($iConfig[$thisIndex]['parameters'][$name]) && $this->definition->hasClass($iConfig[$thisIndex]['parameters'][$name])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['parameters'][$name], $iConfig[$thisIndex]['parameters'][$name] ); } else { $computedParams['value'][$name] = $iConfig[$thisIndex]['parameters'][$name]; } continue 2; } // check the provided method config if (isset($iConfig[$thisIndex]['methods'][$method][$name])) { if (is_string(is_string($iConfig[$thisIndex]['methods'][$method][$name])) && isset($aliases[$iConfig[$thisIndex]['methods'][$method][$name]])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['methods'][$method][$name], $this->instanceManager->getClassFromAlias($iConfig[$thisIndex]['methods'][$method][$name]) ); } elseif (is_string(is_string($iConfig[$thisIndex]['methods'][$method][$name])) && $this->definition->hasClass($iConfig[$thisIndex]['methods'][$method][$name])) { $computedParams['lookup'][$name] = array( $iConfig[$thisIndex]['methods'][$method][$name], $iConfig[$thisIndex]['methods'][$method][$name] ); } else { $computedParams['value'][$name] = $iConfig[$thisIndex]['methods'][$method][$name]; } continue 2; } } // PRIORITY 6 - globally preferred implementations // next consult alias level preferred instances if ($alias && $this->instanceManager->hasTypePreferences($alias)) { $pInstances = $this->instanceManager->getTypePreferences($alias); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || $isSubclassFunc($pInstanceClass, $type)) { $computedParams['lookup'][$name] = array($pInstance, $pInstanceClass); continue 2; } } } // next consult class level preferred instances if ($type && $this->instanceManager->hasTypePreferences($type)) { $pInstances = $this->instanceManager->getTypePreferences($type); foreach ($pInstances as $pInstance) { $pInstanceClass = ($this->instanceManager->hasAlias($pInstance)) ? $this->instanceManager->getClassFromAlias($pInstance) : $pInstance; if ($pInstanceClass === $type || $isSubclassFunc($pInstanceClass, $type)) { $computedParams['lookup'][$name] = array($pInstance, $pInstanceClass); continue 2; } } } if ($isOptional) { $computedParams['optional'][$name] = true; } if ($type && $isTypeInstantiable === true && !$isOptional) { $computedParams['lookup'][$name] = array($type, $type); } } $index = 0; foreach ($injectionMethodParameters as $name => $value) { if (isset($computedParams['value'][$name])) { $resolvedParams[$index] = $computedParams['value'][$name]; } elseif (isset($computedParams['lookup'][$name])) { if ($isInstantiator && in_array($computedParams['lookup'][$name][1], $this->currentDependencies)) { throw new Exception\CircularDependencyException( "Circular dependency detected: $class depends on {$value[0]} and viceversa" ); } array_push($this->currentDependencies, $class); $resolvedParams[$index] = $this->get($computedParams['lookup'][$name][0], $callTimeUserParams); array_pop($this->currentDependencies); } elseif (!array_key_exists($name, $computedParams['optional'])) { throw new Exception\MissingPropertyException( 'Missing parameter named ' . $name . ' for ' . $class . '::' . $method ); } else { $resolvedParams[$index] = null; } $index++; } return $resolvedParams; } } array(), 'hashLong' => array()); /** * Array of class aliases * @var array key: alias, value: class */ protected $aliases = array(); /** * The template to use for housing configuration information * @var array */ protected $configurationTemplate = array( /** * alias|class => alias|class * interface|abstract => alias|class|object * name => value */ 'parameters' => array(), /** * method name => array of ordered method params */ 'methods' => array(), ); /** * An array of instance configuration data * @var array */ protected $configurations = array(); /** * An array of globally preferred implementations for interfaces/abstracts * @var array */ protected $typePreferences = array(); /** * Does this instance manager have this shared instance */ public function hasSharedInstance($classOrAlias) { return isset($this->sharedInstances[$classOrAlias]); } /** * getSharedInstance() */ public function getSharedInstance($classOrAlias) { return $this->sharedInstances[$classOrAlias]; } /** * addSharedInstance() */ public function addSharedInstance($instance, $classOrAlias) { if (!is_object($instance)) { throw new Exception\InvalidArgumentException('This method requires an object to be shared'); } $this->sharedInstances[$classOrAlias] = $instance; } public function hasSharedInstanceWithParameters($classOrAlias, array $params, $returnFashHashLookupKey = false) { ksort($params); $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params)); if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) { $hashValue = $this->createHashForValues($classOrAlias, $params); if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) { return ($returnFashHashLookupKey) ? $hashKey . '/' . $hashValue : true; } } return false; } public function addSharedInstanceWithParameters($instance, $classOrAlias, array $params) { ksort($params); $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params)); $hashValue = $this->createHashForValues($classOrAlias, $params); if (!isset($this->sharedInstancesWithParams[$hashKey]) || !is_array($this->sharedInstancesWithParams[$hashKey])) { $this->sharedInstancesWithParams[$hashKey] = array(); } $this->sharedInstancesWithParams['hashShort'][$hashKey] = true; $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue] = $instance; } public function getSharedInstanceWithParameters($classOrAlias, array $params, $fastHashFromHasLookup = null) { if ($fastHashFromHasLookup) { return $this->sharedInstancesWithParams['hashLong'][$fastHashFromHasLookup]; } ksort($params); $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params)); if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) { $hashValue = $this->createHashForValues($classOrAlias, $params); if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) { return $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue]; } } return false; } public function hasAlias($alias) { return (isset($this->aliases[$alias])); } public function getAliases() { return $this->aliases; } /** * getClassFromAlias() */ public function getClassFromAlias($alias) { if (isset($this->aliases[$alias])) { return $this->aliases[$alias]; } return false; } /** * */ public function addAlias($alias, $class, array $parameters = array()) { if (!preg_match('#^[a-zA-Z0-9-_]+$#', $alias)) { throw new Exception\InvalidArgumentException( 'Aliases must be alphanumeric and can contain dashes and underscores only.' ); } $this->aliases[$alias] = $class; if ($parameters) { $this->setParameters($alias, $parameters); } } public function hasConfiguration($aliasOrClass) { $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $aliasOrClass : $aliasOrClass; if (!isset($this->configurations[$key])) { return false; } if ($this->configurations[$key] === $this->configurationTemplate) { return false; } return true; } public function setConfiguration($aliasOrClass, array $configuration, $append = false) { $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $aliasOrClass : $aliasOrClass; if (!isset($this->configurations[$key])) { $this->configurations[$key] = $this->configurationTemplate; } if (isset($configuration['parameters'])) { if (!$append && $this->configurations[$key]['parameters']) { $this->configurations[$key]['parameters'] = array(); } $this->configurations[$key]['parameters'] += $configuration['parameters']; } if (isset($configuration['methods'])) { if (!$append && $this->configurations[$key]['methods']) { $this->configurations[$key]['methods'] = array(); } $this->configurations[$key]['methods'] += $configuration['methods']; } } public function getConfiguration($aliasOrClass) { $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $aliasOrClass : $aliasOrClass; if (isset($this->configurations[$key])) { return $this->configurations[$key]; } else { return $this->configurationTemplate; } } /** * setParameters() is a convienence method for: * setConfiguration($type, array('parameters' => array(...)), true); * * @param string $type Alias or Class * @param array $parameters Multi-dim array of parameters and their values */ public function setParameters($aliasOrClass, array $parameters) { return $this->setConfiguration($aliasOrClass, array('parameters' => $parameters), true); } /** * setMethods() is a convienence method for: * setConfiguration($type, array('methods' => array(...)), true); * * @param string $type Alias or Class * @param array $methods Multi-dim array of methods and their parameters */ public function setMethods($aliasOrClass, array $methods) { return $this->setConfiguration($aliasOrClass, array('methods' => $methods), true); } public function hasTypePreferences($interfaceOrAbstract) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; return (isset($this->typePreferences[$key]) && $this->typePreferences[$key]); } public function setTypePreference($interfaceOrAbstract, array $preferredImplementations) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; foreach ($preferredImplementations as $preferredImplementation) { $this->addTypePreference($key, $preferredImplementation); } return $this; } public function getTypePreferences($interfaceOrAbstract) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; if (isset($this->typePreferences[$key])) { return $this->typePreferences[$key]; } return array(); } public function unsetTypePreferences($interfaceOrAbstract) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; unset($this->typePreferences[$key]); } public function addTypePreference($interfaceOrAbstract, $preferredImplementation) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; if (!isset($this->typePreferences[$key])) { $this->typePreferences[$key] = array(); } $this->typePreferences[$key][] = $preferredImplementation; return $this; } public function removeTypePreference($interfaceOrAbstract, $preferredType) { $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract; if (!isset($this->generalTypePreferences[$key]) || !in_array($preferredType, $this->typePreferences[$key])) { return false; } unset($this->typePreference[$key][array_search($key, $this->typePreferences)]); return $this; } protected function createHashForKeys($classOrAlias, $paramKeys) { return $classOrAlias . ':' . implode('|', $paramKeys); } protected function createHashForValues($classOrAlias, $paramValues) { $hashValue = ''; foreach ($paramValues as $param) { switch (gettype($param)) { case 'object': $hashValue .= spl_object_hash($param) . '|'; break; case 'integer': case 'string': case 'boolean': case 'NULL': case 'double': $hashValue .= $param . '|'; break; case 'array': $hashValue .= 'Array|'; break; case 'resource': $hashValue .= 'resource|'; break; } } return $hashValue; } }di = $di; $this->definition = $di->getDefinition(); $this->instanceManager = $di->getInstanceManager(); } /** * Methods with functionality overrides */ /** * Override, as we want it to use the functionality defined in the proxy * * @param string $name * @param array $params * @return GeneratorInstance */ public function get($name, array $params = array()) { $im = $this->getInstanceManager(); if ($params) { if (($fastHash = $im->hasSharedInstanceWithParameters($name, $params, true))) { return $im->getSharedInstanceWithParameters(null, array(), $fastHash); } } else { if ($im->hasSharedInstance($name, $params)) { return $im->getSharedInstance($name, $params); } } return $this->newInstance($name, $params); } /** * Override createInstanceViaConstructor method from injector * * Returns code generation artifacts. * * @param string $class * @param null|array $params * @param null|string $alias * @return GeneratorInstance */ public function createInstanceViaConstructor($class, $params, $alias = null) { $callParameters = array(); if ($this->di->definition->hasInjectionMethod($class, '__construct')) { $callParameters = $this->resolveMethodParameters( $class, '__construct', $params, true, $alias ); } return new GeneratorInstance($class, '__construct', $callParameters); } /** * Override instance creation via callback * * @param callback $callback * @param null|array $params * @return GeneratorInstance */ public function createInstanceViaCallback($callback, $params, $alias = null) { if (!is_callable($callback)) { throw new Exception\InvalidCallbackException('An invalid constructor callback was provided'); } if (!is_array($callback) || is_object($callback[0])) { throw new Exception\InvalidCallbackException('For purposes of service locator generation, constructor callbacks must refer to static methods only'); } $class = $callback[0]; $method = $callback[1]; $callParameters = array(); if ($this->di->definition->hasInjectionMethod($class, $method)) { $callParameters = $this->resolveMethodParameters($class, $method, $params, true, $alias); } return new GeneratorInstance(null, $callback, $callParameters); } /** * Retrieve metadata for injectible methods * * @param string $class * @param string $method * @param array $params * @param string $alias * @return array */ public function handleInjectionMethodForObject($class, $method, $params, $alias) { $callParameters = $this->resolveMethodParameters($class, $method, $params, false, $alias); return array( 'method' => $method, 'params' => $callParameters, ); } /** * Override new instance creation * * @param string $name * @param array $params * @param bool $isShared * @return GeneratorInstance */ public function newInstance($name, array $params = array(), $isShared = true) { $definition = $this->getDefinition(); $instanceManager = $this->getInstanceManager(); if ($instanceManager->hasAlias($name)) { $class = $instanceManager->getClassFromAlias($name); $alias = $name; } else { $class = $name; $alias = null; } if (!$definition->hasClass($class)) { $aliasMsg = ($alias) ? '(specified by alias ' . $alias . ') ' : ''; throw new Exception\ClassNotFoundException('Class ' . $aliasMsg . $class . ' could not be located in provided definition.'); } $instantiator = $definition->getInstantiator($class); $injectionMethods = $definition->getInjectionMethods($class); if ($instantiator === '__construct') { $object = $this->createInstanceViaConstructor($class, $params, $alias); if (in_array('__construct', $injectionMethods)) { unset($injectionMethods[array_search('__construct', $injectionMethods)]); } } elseif (is_callable($instantiator)) { $object = $this->createInstanceViaCallback($instantiator, $params, $alias); $object->setName($class); } else { throw new Exception\RuntimeException('Invalid instantiator'); } if ($injectionMethods) { $methodMetadata = array(); foreach ($injectionMethods as $injectionMethod) { $methodMetadata[] = $this->handleInjectionMethodForObject($class, $injectionMethod, $params, $alias); } $object->setMethods($methodMetadata); } if ($isShared) { if ($params) { $instanceManager->addSharedInstanceWithParameters($object, $name, $params); } else { $instanceManager->addSharedInstance($object, $name); } } return $object; } } injector = new DependencyInjectorProxy($injector); } /** * Set the class name for the generated service locator container * * @param string $name * @return Generator */ public function setContainerClass($name) { $this->containerClass = $name; return $this; } /** * Set the namespace to use for the generated class file * * @param string $namespace * @return Generator */ public function setNamespace($namespace) { $this->namespace = $namespace; return $this; } /** * Construct, configure, and return a PHP classfile code generation object * * Creates a Zend\CodeGenerator\Php\PhpFile object that has * created the specified class and service locator methods. * * @param null|string $filename * @return CodeGen\PhpFile */ public function getCodeGenerator($filename = null) { $injector = $this->injector; $im = $injector->getInstanceManager(); $indent = ' '; $aliases = $this->reduceAliases($im->getAliases()); $caseStatements = array(); $getters = array(); $definition = $injector->getDefinition(); foreach ($definition->getClasses() as $name) { $getter = $this->normalizeAlias($name); $meta = $injector->get($name); $params = $meta->getParams(); // Build parameter list for instantiation foreach ($params as $key => $param) { if (null === $param || is_scalar($param) || is_array($param)) { $string = var_export($param, 1); if (strstr($string, '::__set_state(')) { throw new Exception\RuntimeException('Arguments in definitions may not contain objects'); } $params[$key] = $string; } elseif ($param instanceof GeneratorInstance) { $params[$key] = sprintf('$this->%s()', $this->normalizeAlias($param->getName())); } else { $message = sprintf('Unable to use object arguments when building containers. Encountered with "%s", parameter of type "%s"', $name, get_class($param)); throw new Exception\RuntimeException($message); } } // Strip null arguments from the end of the params list $reverseParams = array_reverse($params, true); foreach ($reverseParams as $key => $param) { if ('NULL' === $param) { unset($params[$key]); continue; } break; } // Create instantiation code $creation = ''; $constructor = $meta->getConstructor(); if ('__construct' != $constructor) { // Constructor callback $callback = var_export($constructor, 1); if (strstr($callback, '::__set_state(')) { throw new Exception\RuntimeException('Unable to build containers that use callbacks requiring object instances'); } if (count($params)) { $creation = sprintf('$object = call_user_func(%s, %s);', $callback, implode(', ', $params)); } else { $creation = sprintf('$object = call_user_func(%s);', $callback); } } else { // Normal instantiation $className = '\\' . ltrim($name, '\\'); $creation = sprintf('$object = new %s(%s);', $className, implode(', ', $params)); } // Create method call code $methods = ''; foreach ($meta->getMethods() as $key => $methodData) { if (!isset($methodData['name']) && !isset($methodData['method'])) { continue; } $methodName = isset($methodData['name']) ? $methodData['name'] : $methodData['method']; $methodParams = $methodData['params']; // Create method parameter representation foreach ($methodParams as $key => $param) { if (null === $param || is_scalar($param) || is_array($param)) { $string = var_export($param, 1); if (strstr($string, '::__set_state(')) { throw new Exception\RuntimeException('Arguments in definitions may not contain objects'); } $methodParams[$key] = $string; } elseif ($param instanceof GeneratorInstance) { $methodParams[$key] = sprintf('$this->%s()', $this->normalizeAlias($param->getName())); } else { $message = sprintf('Unable to use object arguments when generating method calls. Encountered with class "%s", method "%s", parameter of type "%s"', $name, $methodName, get_class($param)); throw new Exception\RuntimeException($message); } } // Strip null arguments from the end of the params list $reverseParams = array_reverse($methodParams, true); foreach ($reverseParams as $key => $param) { if ('NULL' === $param) { unset($methodParams[$key]); continue; } break; } $methods .= sprintf("\$object->%s(%s);\n", $methodName, implode(', ', $methodParams)); } // Generate caching statement $storage = ''; if ($im->hasSharedInstance($name, $params)) { $storage = sprintf("\$this->services['%s'] = \$object;\n", $name); } // Start creating getter $getterBody = ''; // Create fetch of stored service if ($im->hasSharedInstance($name, $params)) { $getterBody .= sprintf("if (isset(\$this->services['%s'])) {\n", $name); $getterBody .= sprintf("%sreturn \$this->services['%s'];\n}\n\n", $indent, $name); } // Creation and method calls $getterBody .= sprintf("%s\n", $creation); $getterBody .= $methods; // Stored service $getterBody .= $storage; // End getter body $getterBody .= "return \$object;\n"; $getterDef = new CodeGen\PhpMethod(); $getterDef->setName($getter) ->setBody($getterBody); $getters[] = $getterDef; // Get cases for case statements $cases = array($name); if (isset($aliases[$name])) { $cases = array_merge($aliases[$name], $cases); } // Build case statement and store $statement = ''; foreach ($cases as $value) { $statement .= sprintf("%scase '%s':\n", $indent, $value); } $statement .= sprintf("%sreturn \$this->%s();\n", str_repeat($indent, 2), $getter); $caseStatements[] = $statement; } // Build switch statement $switch = sprintf("switch (%s) {\n%s\n", '$name', implode("\n", $caseStatements)); $switch .= sprintf("%sdefault:\n%sreturn parent::get(%s, %s);\n", $indent, str_repeat($indent, 2), '$name', '$params'); $switch .= "}\n\n"; // Build get() method $nameParam = new CodeGen\PhpParameter(); $nameParam->setName('name'); $defaultParams = new CodeGen\PhpParameterDefaultValue(); $defaultParams->setValue(array()); $paramsParam = new CodeGen\PhpParameter(); $paramsParam->setName('params') ->setType('array') ->setDefaultValue($defaultParams); $get = new CodeGen\PhpMethod(); $get->setName('get'); $get->setParameters(array( $nameParam, $paramsParam, )); $get->setBody($switch); // Create getters for aliases $aliasMethods = array(); foreach ($aliases as $class => $classAliases) { foreach ($classAliases as $alias) { $aliasMethods[] = $this->getCodeGenMethodFromAlias($alias, $class); } } // Create class code generation object $container = new CodeGen\PhpClass(); $container->setName($this->containerClass) ->setExtendedClass('ServiceLocator') ->setMethod($get) ->setMethods($getters) ->setMethods($aliasMethods); // Create PHP file code generation object $classFile = new CodeGen\PhpFile(); $classFile->setUse('Zend\Di\ServiceLocator') ->setClass($container); if (null !== $this->namespace) { $classFile->setNamespace($this->namespace); } if (null !== $filename) { $classFile->setFilename($filename); } return $classFile; } /** * Reduces aliases * * Takes alias list and reduces it to a 2-dimensional array of * class names pointing to an array of aliases that resolve to * it. * * @param array $aliasList * @return array */ protected function reduceAliases(array $aliasList) { $reduced = array(); $aliases = array_keys($aliasList); foreach ($aliasList as $alias => $service) { if (in_array($service, $aliases)) { do { $service = $aliasList[$service]; } while (in_array($service, $aliases)); } if (!isset($reduced[$service])) { $reduced[$service] = array(); } $reduced[$service][] = $alias; } return $reduced; } /** * Create a PhpMethod code generation object named after a given alias * * @param string $alias * @param class $class Class to which alias refers * @return CodeGen\PhpMethod */ protected function getCodeGenMethodFromAlias($alias, $class) { $alias = $this->normalizeAlias($alias); $method = new CodeGen\PhpMethod(); $method->setName($alias) ->setBody(sprintf('return $this->get(\'%s\');', $class)); return $method; } /** * Normalize an alias to a getter method name * * @param string $alias * @return string */ protected function normalizeAlias($alias) { $normalized = preg_replace('/[^a-zA-Z0-9]/', ' ', $alias); $normalized = 'get' . str_replace(' ', '', ucwords($normalized)); return $normalized; } } name = $name; $this->constructor = $constructor; $this->params = $params; } public function getName() { return $this->name; } /** * Set class name * * In the case of an instance created via a callback, we need to set the * class name after creating the generator instance. * * @param string $name * @return GeneratorInstance */ public function setName($name) { $this->name = $name; return $this; } public function getConstructor() { return $this->constructor; } public function getParams() { return $this->params; } public function setMethods(array $methods) { $this->methods = $methods; return $this; } public function getMethods() { return $this->methods; } } * protected $map = array('foo' => 'getFoo'); * * * When encountered, the return value of that method will be used. * * Methods mapped in this way may expect a single, array argument, the * $params passed to {@link get()}, if any. * * @var array */ protected $map = array(); /** * Registered services and cached values * * @var array */ protected $services = array(); /** * Register a service with the locator * * @param string $name * @param mixed $service * @return ServiceLocator */ public function set($name, $service) { $this->services[$name] = $service; return $this; } /** * Retrieve a registered service * * Tests first if a value is registered for the service, and, if so, * returns it. * * If the value returned is a non-object callback or closure, the return * value is retrieved, stored, and returned. Parameters passed to the method * are passed to the callback, but only on the first retrieval. * * If the service requested matches a method in the method map, the return * value of that method is returned. Parameters are passed to the matching * method. * * @param string $name * @param array $params * @return mixed */ public function get($name, array $params = array()) { if (!isset($this->services[$name])) { if (!isset($this->map[$name])) { return null; } $method = $this->map[$name]; return $this->$method($params); } $service = $this->services[$name]; if ($service instanceof \Closure || (!is_object($service) && is_callable($service)) ) { $this->services[$name] = $service = call_user_func_array($service, $params); } return $service; } } TODO - Make compiler able to be definition-aware for purposes of derived class information. Basically, allow attaching other Definition objects to a compiler which it will then consult during compile() in order to flesh out dependency details. Use case: a concrete DbTable object in a user library. In this case, currently compiling against that object will provide no supertypes; having the ability to attach a previously created definition would allow the compiler to check against those and provide information as it locates it. - Integrate the EventManager into the Compiler and Code\Scanner in order to allow attaching listeners to interesting events. Use cases include logging and debugging during definition compilation. - Ability to provide both class properties and method-specific parameters via configuration (which solves disambiguation when multiple methods specify the same parameter names) Proposed solution: array( 'properties' => array( 'Zend\Foo\Bar' => array( 'public' => true, 'methods' => array( '__construct' => array( 'params' => array( 'foo' => 'bar', ), 'class' => 'Some\Default\Class' ), 'setConfig' => array( 'params' => array( 'bar' => 'baz', ), ), ), ), ), ) - Ability to pass configuration to a generated ServiceLocator - Skip optional arguments if not passed in configuration or part of definition (current behavior is to raise an exception if *any* arguments are missing) - Scoped Containers: Described here: http://picocontainer.org/scopes.html This is something that should be explored when we start using these containers with ServiceLocators inside an application. While part of this has to do with garbage collection in Java (something we need not worry with in PHP since there is no persistent in-memory objects), the interesting use case would be having a container cloned from another container that has more or less (a subset) of the definitions available to the Container's newInstance() and get() facilities. - Better Strategy Management Currently, the strategies for determining dependencies is hard coded into the various definitions. Ideally, we'd be able to have configurable strategies that the definitions can then utilize to do their job: http://picocontainer.org/injection.html We currently support constructor injection and setter injection (methods prefixed by set[A-Z]) - Annotation Parsing Ideally, at some point, Zend\Code\Scanner will support Annotation parsing. When this is possible, we'd like to be able to use @inject similar to http://picocontainer.org/annotated-method-injection.html - SuperType Resolution (partially done inside resolveMethodParameters with is_subtype_of()) If a class claims it needs a dependency of not an object, but a particular interface, the ability to find an object that suits that dependency, either through a concept called 'Preferred Objects' or via a 'Property'. The following should be supported: The compiler also needs to be aware of other definitions when looking up SuperTypes $definition = new AggregateDefinition(); $definition->addDefinition('Zend\Controller\DiDefinition'); $compiler = new Compiler() $compiler->addDefinition($definition); $compiler->addCodeScanner(__DIR__ . 'My/Blog'); $array = $compiler->compile() $definition->addDefinition(new ArrayDefinition($array)); - Performance & Benchmarking Zend\Code\Scanner- check memory usage, perhaps use gc_collect_cycles() to free memory, we'll have to do this on large scan bases. Benchmark compiler: reflection vs. code scanner