protected function processExtensions(array $config, Container $container, $alias) { $extensions = !empty($config["extensions"]) ? $config["extensions"] : []; foreach ($extensions as $service => $extension) { if (!$container->offsetExists($service)) { $aliasedService = $this->referenceResolver->aliasThisKey($service, $alias); if (!$container->offsetExists($aliasedService)) { throw new ConfigException(sprintf("Cannot use extension for the service '%s' as it does not exist", $service)); } $service = $aliasedService; } foreach ($extension as $i => $call) { $extension[$i] = $this->processCall($call, $i, $service . " (extension)"); } if (!empty($extension)) { $container->extend($service, function ($object) use($extension, $alias) { return $this->serviceFactory->extendService($object, $extension, $alias); }); } } }
/** * parse service definition and add to the container * * @param array $config * @param Container $container * @param string $alias * @throws Exception\ConfigException */ protected function processServices(array $config, Container $container, $alias = "") { if (!isset($config["services"])) { return; } if (!$this->isAssocArray($config["services"])) { throw new ConfigException("The 'services' configuration must be an associative array"); } // scan for abstract definitions foreach ($config["services"] as $key => $definition) { if (!empty($definition["abstract"])) { $this->abstractDefinitions[self::SERVICE_CHAR . $this->referenceResolver->aliasThisKey($key, $alias)] = $definition; unset($config["services"][$key]); } } // process services foreach ($config["services"] as $key => $definition) { $key = $this->referenceResolver->aliasThisKey($key, $alias); // check for collisions if (isset($container[$key])) { throw new ConfigException(sprintf("Tried to define a service named '%s', but that name already exists in the container", $key)); } if (!$this->isAssocArray($definition)) { throw new ConfigException("A service definition must be an associative array"); } // check if this definition extends an abstract one if (!empty($definition["extends"])) { $extends = $this->referenceResolver->aliasThisKey($definition["extends"], $alias); if (!isset($this->abstractDefinitions[$extends])) { throw new ConfigException(sprintf("The service definition for '%s' extends '%s' but there is no abstract definition of that name", $key, $extends)); } // As calls gets wiped out by the replace_recursive, so we need to store it and merge it separately $calls = !empty($definition["calls"]) ? $definition["calls"] : []; if (!empty($this->abstractDefinitions[$extends]["calls"])) { $calls = array_merge($calls, $this->abstractDefinitions[$extends]["calls"]); } $definition = array_replace_recursive($this->abstractDefinitions[$extends], $definition); $definition["calls"] = $calls; } // get class if (empty($definition["class"])) { throw new ConfigException(sprintf("The service definition for %s does not have a class", $key)); } // get class, resolving parameters if necessary $class = $this->referenceResolver->resolveParameter($definition["class"], $container, $alias); if (!class_exists($class)) { throw new ConfigException(sprintf("The service class '%s' does not exist", $class)); } // factories $factory = []; if (!empty($definition["factoryMethod"])) { $factory["method"] = $definition["factoryMethod"]; } if (!empty($definition["factoryClass"])) { // check for malformed definitions ... if (empty($factory["method"])) { throw new ConfigException(sprintf("A factory class was specified for '%s', but no method was set", $key)); } //... and non-existent classes $factoryClass = $this->referenceResolver->resolveParameter($definition["factoryClass"], $container, $alias); if (!class_exists($factoryClass)) { throw new ConfigException(sprintf("The factory class '%s', for '%s', does not exist", $factoryClass, $key)); } // make sure the method actually exists on the class if (!method_exists($factoryClass, $factory["method"])) { throw new ConfigException(sprintf("Invalid factory definition. The method '%s' does not exist on the class '%s'", $factory["method"], $factoryClass)); } $factory["class"] = $factoryClass; } if (!empty($definition["factoryService"])) { // check for malformed definitions if (empty($factory["method"])) { throw new ConfigException(sprintf("A factory service was specified for '%s', but no method was set", $key)); } if (!empty($factory["class"])) { throw new ConfigException(sprintf("The definition for '%s' cannot have both a factory class and a factory service", $key)); } // remove the service char if it exists if ($definition["factoryService"][0] == self::SERVICE_CHAR) { $definition["factoryService"] = substr($definition["factoryService"], 1); } $factory["service"] = $this->referenceResolver->aliasThisKey($definition["factoryService"], $alias); } // arguments $arguments = !empty($definition["arguments"]) ? $definition["arguments"] : []; // calls / setters $calls = !empty($definition["calls"]) ? $definition["calls"] : []; foreach ($calls as $i => &$call) { if (empty($call["method"])) { throw new ConfigException(sprintf("Call '%s' for the service '%s' does not specify a method name", $i, $key)); } if (!method_exists($class, $call["method"])) { throw new ConfigException(sprintf("Error for service '%s': the method call '%s' does not exist for the class '%s'", $key, $call["method"], $class)); } if (empty($call["arguments"])) { // if no arguments have been defined, set arguments to an empty array $call["arguments"] = []; } else { if (!is_array($call["arguments"])) { throw new ConfigException(sprintf("Error for service '%s': the method call '%s' has invalid arguments", $key, $call["method"])); } } } // tags if (!empty($definition["tags"])) { if (!is_array($definition["tags"])) { throw new ConfigException(sprintf("Error for service '%s': the tags definition was not in the expected array format", $key)); } foreach ($definition["tags"] as $tag) { $tag = self::TAG_CHAR . $tag; if (!isset($container[$tag])) { $container[$tag] = function () { return new TagCollection(); }; } /** @var TagCollection $collection */ $collection = $container[$tag]; $collection->addService($key); } } // add the definition to the container $container[$key] = function () use($class, $factory, $arguments, $calls, $alias) { // parse arguments for injected parameters and services return $this->serviceFactory->createService($class, $factory, $arguments, $calls, $alias); }; } }