Chris@0: getArray()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the service container definition as a PHP array. Chris@0: * Chris@0: * @return array Chris@0: * A PHP array representation of the service container. Chris@0: */ Chris@0: public function getArray() { Chris@0: $definition = []; Chris@0: $this->aliases = $this->getAliases(); Chris@0: $definition['aliases'] = $this->getAliases(); Chris@0: $definition['parameters'] = $this->getParameters(); Chris@0: $definition['services'] = $this->getServiceDefinitions(); Chris@14: $definition['frozen'] = $this->container->isCompiled(); Chris@0: $definition['machine_format'] = $this->supportsMachineFormat(); Chris@0: return $definition; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the aliases as a PHP array. Chris@0: * Chris@0: * @return array Chris@0: * The aliases. Chris@0: */ Chris@0: protected function getAliases() { Chris@0: $alias_definitions = []; Chris@0: Chris@0: $aliases = $this->container->getAliases(); Chris@0: foreach ($aliases as $alias => $id) { Chris@0: $id = (string) $id; Chris@0: while (isset($aliases[$id])) { Chris@0: $id = (string) $aliases[$id]; Chris@0: } Chris@0: $alias_definitions[$alias] = $id; Chris@0: } Chris@0: Chris@0: return $alias_definitions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets parameters of the container as a PHP array. Chris@0: * Chris@0: * @return array Chris@0: * The escaped and prepared parameters of the container. Chris@0: */ Chris@0: protected function getParameters() { Chris@0: if (!$this->container->getParameterBag()->all()) { Chris@0: return []; Chris@0: } Chris@0: Chris@0: $parameters = $this->container->getParameterBag()->all(); Chris@14: $is_compiled = $this->container->isCompiled(); Chris@14: return $this->prepareParameters($parameters, $is_compiled); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets services of the container as a PHP array. Chris@0: * Chris@0: * @return array Chris@0: * The service definitions. Chris@0: */ Chris@0: protected function getServiceDefinitions() { Chris@0: if (!$this->container->getDefinitions()) { Chris@0: return []; Chris@0: } Chris@0: Chris@0: $services = []; Chris@0: foreach ($this->container->getDefinitions() as $id => $definition) { Chris@0: // Only store public service definitions, references to shared private Chris@0: // services are handled in ::getReferenceCall(). Chris@0: if ($definition->isPublic()) { Chris@0: $service_definition = $this->getServiceDefinition($definition); Chris@0: $services[$id] = $this->serialize ? serialize($service_definition) : $service_definition; Chris@0: } Chris@0: } Chris@0: Chris@0: return $services; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Prepares parameters for the PHP array dumping. Chris@0: * Chris@0: * @param array $parameters Chris@0: * An array of parameters. Chris@0: * @param bool $escape Chris@0: * Whether keys with '%' should be escaped or not. Chris@0: * Chris@0: * @return array Chris@0: * An array of prepared parameters. Chris@0: */ Chris@0: protected function prepareParameters(array $parameters, $escape = TRUE) { Chris@0: $filtered = []; Chris@0: foreach ($parameters as $key => $value) { Chris@0: if (is_array($value)) { Chris@0: $value = $this->prepareParameters($value, $escape); Chris@0: } Chris@0: elseif ($value instanceof Reference) { Chris@0: $value = $this->dumpValue($value); Chris@0: } Chris@0: Chris@0: $filtered[$key] = $value; Chris@0: } Chris@0: Chris@0: return $escape ? $this->escape($filtered) : $filtered; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Escapes parameters. Chris@0: * Chris@0: * @param array $parameters Chris@0: * The parameters to escape for '%' characters. Chris@0: * Chris@0: * @return array Chris@0: * The escaped parameters. Chris@0: */ Chris@0: protected function escape(array $parameters) { Chris@0: $args = []; Chris@0: Chris@0: foreach ($parameters as $key => $value) { Chris@0: if (is_array($value)) { Chris@0: $args[$key] = $this->escape($value); Chris@0: } Chris@0: elseif (is_string($value)) { Chris@0: $args[$key] = str_replace('%', '%%', $value); Chris@0: } Chris@0: else { Chris@0: $args[$key] = $value; Chris@0: } Chris@0: } Chris@0: Chris@0: return $args; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a service definition as PHP array. Chris@0: * Chris@0: * @param \Symfony\Component\DependencyInjection\Definition $definition Chris@0: * The definition to process. Chris@0: * Chris@0: * @return array Chris@0: * The service definition as PHP array. Chris@0: * Chris@0: * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException Chris@0: * Thrown when the definition is marked as decorated, or with an explicit Chris@0: * scope different from SCOPE_CONTAINER and SCOPE_PROTOTYPE. Chris@0: */ Chris@0: protected function getServiceDefinition(Definition $definition) { Chris@0: $service = []; Chris@0: if ($definition->getClass()) { Chris@0: $service['class'] = $definition->getClass(); Chris@0: } Chris@0: Chris@0: if (!$definition->isPublic()) { Chris@0: $service['public'] = FALSE; Chris@0: } Chris@0: Chris@0: if ($definition->getFile()) { Chris@0: $service['file'] = $definition->getFile(); Chris@0: } Chris@0: Chris@0: if ($definition->isSynthetic()) { Chris@0: $service['synthetic'] = TRUE; Chris@0: } Chris@0: Chris@0: if ($definition->isLazy()) { Chris@0: $service['lazy'] = TRUE; Chris@0: } Chris@0: Chris@0: if ($definition->getArguments()) { Chris@0: $arguments = $definition->getArguments(); Chris@0: $service['arguments'] = $this->dumpCollection($arguments); Chris@0: $service['arguments_count'] = count($arguments); Chris@0: } Chris@0: else { Chris@0: $service['arguments_count'] = 0; Chris@0: } Chris@0: Chris@0: if ($definition->getProperties()) { Chris@0: $service['properties'] = $this->dumpCollection($definition->getProperties()); Chris@0: } Chris@0: Chris@0: if ($definition->getMethodCalls()) { Chris@0: $service['calls'] = $this->dumpMethodCalls($definition->getMethodCalls()); Chris@0: } Chris@0: Chris@0: // By default services are shared, so just provide the flag, when needed. Chris@0: if ($definition->isShared() === FALSE) { Chris@0: $service['shared'] = $definition->isShared(); Chris@0: } Chris@0: Chris@0: if (($decorated = $definition->getDecoratedService()) !== NULL) { Chris@0: throw new InvalidArgumentException("The 'decorated' definition is not supported by the Drupal 8 run-time container. The Container Builder should have resolved that during the DecoratorServicePass compiler pass."); Chris@0: } Chris@0: Chris@0: if ($callable = $definition->getFactory()) { Chris@0: $service['factory'] = $this->dumpCallable($callable); Chris@0: } Chris@0: Chris@0: if ($callable = $definition->getConfigurator()) { Chris@0: $service['configurator'] = $this->dumpCallable($callable); Chris@0: } Chris@0: Chris@0: return $service; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Dumps method calls to a PHP array. Chris@0: * Chris@0: * @param array $calls Chris@0: * An array of method calls. Chris@0: * Chris@0: * @return array Chris@0: * The PHP array representation of the method calls. Chris@0: */ Chris@0: protected function dumpMethodCalls(array $calls) { Chris@0: $code = []; Chris@0: Chris@0: foreach ($calls as $key => $call) { Chris@0: $method = $call[0]; Chris@0: $arguments = []; Chris@0: if (!empty($call[1])) { Chris@0: $arguments = $this->dumpCollection($call[1]); Chris@0: } Chris@0: Chris@0: $code[$key] = [$method, $arguments]; Chris@0: } Chris@0: Chris@0: return $code; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Dumps a collection to a PHP array. Chris@0: * Chris@0: * @param mixed $collection Chris@0: * A collection to process. Chris@0: * @param bool &$resolve Chris@0: * Used for passing the information to the caller whether the given Chris@0: * collection needed to be resolved or not. This is used for optimizing Chris@0: * deep arrays that don't need to be traversed. Chris@0: * Chris@0: * @return \stdClass|array Chris@0: * The collection in a suitable format. Chris@0: */ Chris@0: protected function dumpCollection($collection, &$resolve = FALSE) { Chris@0: $code = []; Chris@0: Chris@0: foreach ($collection as $key => $value) { Chris@0: if (is_array($value)) { Chris@0: $resolve_collection = FALSE; Chris@0: $code[$key] = $this->dumpCollection($value, $resolve_collection); Chris@0: Chris@0: if ($resolve_collection) { Chris@0: $resolve = TRUE; Chris@0: } Chris@0: } Chris@0: else { Chris@14: $code[$key] = $this->dumpValue($value); Chris@14: if (is_object($code[$key])) { Chris@0: $resolve = TRUE; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: if (!$resolve) { Chris@0: return $collection; Chris@0: } Chris@0: Chris@0: return (object) [ Chris@0: 'type' => 'collection', Chris@0: 'value' => $code, Chris@0: 'resolve' => $resolve, Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Dumps callable to a PHP array. Chris@0: * Chris@0: * @param array|callable $callable Chris@0: * The callable to process. Chris@0: * Chris@0: * @return callable Chris@0: * The processed callable. Chris@0: */ Chris@0: protected function dumpCallable($callable) { Chris@0: if (is_array($callable)) { Chris@0: $callable[0] = $this->dumpValue($callable[0]); Chris@0: $callable = [$callable[0], $callable[1]]; Chris@0: } Chris@0: Chris@0: return $callable; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a private service definition in a suitable format. Chris@0: * Chris@0: * @param string $id Chris@0: * The ID of the service to get a private definition for. Chris@0: * @param \Symfony\Component\DependencyInjection\Definition $definition Chris@0: * The definition to process. Chris@0: * @param bool $shared Chris@0: * (optional) Whether the service will be shared with others. Chris@0: * By default this parameter is FALSE. Chris@0: * Chris@0: * @return \stdClass Chris@0: * A very lightweight private service value object. Chris@0: */ Chris@0: protected function getPrivateServiceCall($id, Definition $definition, $shared = FALSE) { Chris@0: $service_definition = $this->getServiceDefinition($definition); Chris@0: if (!$id) { Chris@0: $hash = Crypt::hashBase64(serialize($service_definition)); Chris@0: $id = 'private__' . $hash; Chris@0: } Chris@0: return (object) [ Chris@0: 'type' => 'private_service', Chris@0: 'id' => $id, Chris@0: 'value' => $service_definition, Chris@0: 'shared' => $shared, Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Dumps the value to PHP array format. Chris@0: * Chris@0: * @param mixed $value Chris@0: * The value to dump. Chris@0: * Chris@0: * @return mixed Chris@0: * The dumped value in a suitable format. Chris@0: * Chris@0: * @throws RuntimeException Chris@0: * When trying to dump object or resource. Chris@0: */ Chris@0: protected function dumpValue($value) { Chris@0: if (is_array($value)) { Chris@0: $code = []; Chris@0: foreach ($value as $k => $v) { Chris@0: $code[$k] = $this->dumpValue($v); Chris@0: } Chris@0: Chris@0: return $code; Chris@0: } Chris@0: elseif ($value instanceof Reference) { Chris@0: return $this->getReferenceCall((string) $value, $value); Chris@0: } Chris@0: elseif ($value instanceof Definition) { Chris@0: return $this->getPrivateServiceCall(NULL, $value); Chris@0: } Chris@0: elseif ($value instanceof Parameter) { Chris@0: return $this->getParameterCall((string) $value); Chris@0: } Chris@14: elseif (is_string($value) && preg_match('/^\%(.*)\%$/', $value, $matches)) { Chris@14: return $this->getParameterCall($matches[1]); Chris@14: } Chris@0: elseif ($value instanceof Expression) { Chris@0: throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); Chris@0: } Chris@0: elseif (is_object($value)) { Chris@0: // Drupal specific: Instantiated objects have a _serviceId parameter. Chris@0: if (isset($value->_serviceId)) { Chris@0: return $this->getReferenceCall($value->_serviceId); Chris@0: } Chris@0: throw new RuntimeException('Unable to dump a service container if a parameter is an object without _serviceId.'); Chris@0: } Chris@0: elseif (is_resource($value)) { Chris@0: throw new RuntimeException('Unable to dump a service container if a parameter is a resource.'); Chris@0: } Chris@0: Chris@0: return $value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a service reference for a reference in a suitable PHP array format. Chris@0: * Chris@0: * The main difference is that this function treats references to private Chris@0: * services differently and returns a private service reference instead of Chris@0: * a normal reference. Chris@0: * Chris@0: * @param string $id Chris@0: * The ID of the service to get a reference for. Chris@0: * @param \Symfony\Component\DependencyInjection\Reference|null $reference Chris@0: * (optional) The reference object to process; needed to get the invalid Chris@0: * behavior value. Chris@0: * Chris@0: * @return string|\stdClass Chris@0: * A suitable representation of the service reference. Chris@0: */ Chris@0: protected function getReferenceCall($id, Reference $reference = NULL) { Chris@0: $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; Chris@0: Chris@0: if ($reference !== NULL) { Chris@0: $invalid_behavior = $reference->getInvalidBehavior(); Chris@0: } Chris@0: Chris@0: // Private shared service. Chris@0: if (isset($this->aliases[$id])) { Chris@0: $id = $this->aliases[$id]; Chris@0: } Chris@0: $definition = $this->container->getDefinition($id); Chris@0: if (!$definition->isPublic()) { Chris@0: // The ContainerBuilder does not share a private service, but this means a Chris@0: // new service is instantiated every time. Use a private shared service to Chris@0: // circumvent the problem. Chris@0: return $this->getPrivateServiceCall($id, $definition, TRUE); Chris@0: } Chris@0: Chris@0: return $this->getServiceCall($id, $invalid_behavior); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a service reference for an ID in a suitable PHP array format. Chris@0: * Chris@0: * @param string $id Chris@0: * The ID of the service to get a reference for. Chris@0: * @param int $invalid_behavior Chris@0: * (optional) The invalid behavior of the service. Chris@0: * Chris@0: * @return string|\stdClass Chris@0: * A suitable representation of the service reference. Chris@0: */ Chris@0: protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { Chris@0: return (object) [ Chris@0: 'type' => 'service', Chris@0: 'id' => $id, Chris@0: 'invalidBehavior' => $invalid_behavior, Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a parameter reference in a suitable PHP array format. Chris@0: * Chris@0: * @param string $name Chris@0: * The name of the parameter to get a reference for. Chris@0: * Chris@0: * @return string|\stdClass Chris@0: * A suitable representation of the parameter reference. Chris@0: */ Chris@0: protected function getParameterCall($name) { Chris@0: return (object) [ Chris@0: 'type' => 'parameter', Chris@0: 'name' => $name, Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Whether this supports the machine-optimized format or not. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if this supports machine-optimized format, FALSE otherwise. Chris@0: */ Chris@0: protected function supportsMachineFormat() { Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: }