Mercurial > hg > cmmr2012-drupal-site
diff core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/lib/Drupal/Component/ProxyBuilder/ProxyBuilder.php Thu Jul 05 14:24:15 2018 +0000 @@ -0,0 +1,352 @@ +<?php + +namespace Drupal\Component\ProxyBuilder; + +/** + * Generates the string representation of the proxy service. + */ +class ProxyBuilder { + + /** + * Generates the used proxy class name from a given class name. + * + * @param string $class_name + * The class name of the actual service. + * + * @return string + * The class name of the proxy. + */ + public static function buildProxyClassName($class_name) { + $match = []; + preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match); + $root_namespace = $match[1]; + $rest_fqcn = $match[2]; + $proxy_class_name = $root_namespace . '\\ProxyClass\\' . $rest_fqcn; + + return $proxy_class_name; + } + + /** + * Generates the used proxy namespace from a given class name. + * + * @param string $class_name + * The class name of the actual service. + * + * @return string + * The namespace name of the proxy. + */ + public static function buildProxyNamespace($class_name) { + $proxy_classname = static::buildProxyClassName($class_name); + + preg_match('/(.+)\\\\[a-zA-Z0-9]+/', $proxy_classname, $match); + $proxy_namespace = $match[1]; + return $proxy_namespace; + } + + /** + * Builds a proxy class string. + * + * @param string $class_name + * The class name of the actual service. + * @param string $proxy_class_name + * (optional) The class name of the proxy service. + * + * @return string + * The full string with namespace class and methods. + */ + public function build($class_name, $proxy_class_name = '') { + $reflection = new \ReflectionClass($class_name); + + if ($proxy_class_name) { + $proxy_class_reflection = new \ReflectionClass($proxy_class_name); + $proxy_namespace = $proxy_class_reflection->getNamespaceName(); + } + else { + $proxy_class_name = $this->buildProxyClassName($class_name); + $proxy_namespace = $this->buildProxyNamespace($class_name); + $proxy_class_shortname = str_replace($proxy_namespace . '\\', '', $proxy_class_name); + } + + $output = ''; + $class_documentation = <<<'EOS' + +namespace {{ namespace }}{ + + /** + * Provides a proxy class for \{{ class_name }}. + * + * @see \Drupal\Component\ProxyBuilder + */ + +EOS; + $class_start = ' class {{ proxy_class_shortname }}'; + + // For cases in which the implemented interface is a child of another + // interface, getInterfaceNames() also returns the parent. This causes a + // PHP error. + // In order to avoid that, check for each interface, whether one of its + // parents is also in the list and exclude it. + if ($interfaces = $reflection->getInterfaces()) { + foreach ($interfaces as $interface_name => $interface) { + // Exclude all parents from the list of implemented interfaces of the + // class. + if ($parent_interfaces = $interface->getInterfaceNames()) { + foreach ($parent_interfaces as $parent_interface) { + unset($interfaces[$parent_interface]); + } + } + } + + $interface_names = []; + foreach ($interfaces as $interface) { + $interface_names[] = '\\' . $interface->getName(); + } + $class_start .= ' implements ' . implode(', ', $interface_names); + } + + $output .= $this->buildUseStatements(); + + // The actual class; + $properties = <<<'EOS' +/** + * The id of the original proxied service. + * + * @var string + */ +protected $drupalProxyOriginalServiceId; + +/** + * The real proxied service, after it was lazy loaded. + * + * @var \{{ class_name }} + */ +protected $service; + +/** + * The service container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ +protected $container; + + +EOS; + + $output .= $properties; + + // Add all the methods. + $methods = []; + $methods[] = $this->buildConstructorMethod(); + $methods[] = $this->buildLazyLoadItselfMethod(); + + // Add all the methods of the proxied service. + $reflection_methods = $reflection->getMethods(); + + foreach ($reflection_methods as $method) { + if ($method->getName() === '__construct') { + continue; + } + + if ($method->isPublic()) { + $methods[] = $this->buildMethod($method) . "\n"; + } + } + + $output .= implode("\n", $methods); + + // Indent the output. + $output = implode("\n", array_map(function ($value) { + if ($value === '') { + return $value; + } + return " $value"; + }, explode("\n", $output))); + + $final_output = $class_documentation . $class_start . "\n {\n\n" . $output . "\n }\n\n}\n"; + + $final_output = str_replace('{{ class_name }}', $class_name, $final_output); + $final_output = str_replace('{{ namespace }}', $proxy_namespace ? $proxy_namespace . ' ' : '', $final_output); + $final_output = str_replace('{{ proxy_class_shortname }}', $proxy_class_shortname, $final_output); + + return $final_output; + } + + /** + * Generates the string for the method which loads the actual service. + * + * @return string + */ + protected function buildLazyLoadItselfMethod() { + $output = <<<'EOS' +/** + * Lazy loads the real service from the container. + * + * @return object + * Returns the constructed real service. + */ +protected function lazyLoadItself() +{ + if (!isset($this->service)) { + $this->service = $this->container->get($this->drupalProxyOriginalServiceId); + } + + return $this->service; +} + +EOS; + + return $output; + } + + /** + * Generates the string representation of a single method: signature, body. + * + * @param \ReflectionMethod $reflection_method + * A reflection method for the method. + * + * @return string + */ + protected function buildMethod(\ReflectionMethod $reflection_method) { + + $parameters = []; + foreach ($reflection_method->getParameters() as $parameter) { + $parameters[] = $this->buildParameter($parameter); + } + + $function_name = $reflection_method->getName(); + + $reference = ''; + if ($reflection_method->returnsReference()) { + $reference = '&'; + } + + $signature_line = <<<'EOS' +/** + * {@inheritdoc} + */ + +EOS; + + if ($reflection_method->isStatic()) { + $signature_line .= 'public static function ' . $reference . $function_name . '('; + } + else { + $signature_line .= 'public function ' . $reference . $function_name . '('; + } + + $signature_line .= implode(', ', $parameters); + $signature_line .= ')'; + + $output = $signature_line . "\n{\n"; + + $output .= $this->buildMethodBody($reflection_method); + + $output .= "\n" . '}'; + return $output; + } + + /** + * Builds a string for a single parameter of a method. + * + * @param \ReflectionParameter $parameter + * A reflection object of the parameter. + * + * @return string + */ + protected function buildParameter(\ReflectionParameter $parameter) { + $parameter_string = ''; + + if ($parameter->isArray()) { + $parameter_string .= 'array '; + } + elseif ($parameter->isCallable()) { + $parameter_string .= 'callable '; + } + elseif ($class = $parameter->getClass()) { + $parameter_string .= '\\' . $class->getName() . ' '; + } + + if ($parameter->isPassedByReference()) { + $parameter_string .= '&'; + } + + $parameter_string .= '$' . $parameter->getName(); + + if ($parameter->isDefaultValueAvailable()) { + $parameter_string .= ' = '; + $parameter_string .= var_export($parameter->getDefaultValue(), TRUE); + } + + return $parameter_string; + } + + /** + * Builds the body of a wrapped method. + * + * @param \ReflectionMethod $reflection_method + * A reflection method for the method. + * + * @return string + */ + protected function buildMethodBody(\ReflectionMethod $reflection_method) { + $output = ''; + + $function_name = $reflection_method->getName(); + + if (!$reflection_method->isStatic()) { + $output .= ' return $this->lazyLoadItself()->' . $function_name . '('; + } + else { + $class_name = $reflection_method->getDeclaringClass()->getName(); + $output .= " \\$class_name::$function_name("; + } + + // Add parameters; + $parameters = []; + foreach ($reflection_method->getParameters() as $parameter) { + $parameters[] = '$' . $parameter->getName(); + } + + $output .= implode(', ', $parameters) . ');'; + + return $output; + } + + /** + * Builds the constructor used to inject the actual service ID. + * + * @return string + */ + protected function buildConstructorMethod() { + $output = <<<'EOS' +/** + * Constructs a ProxyClass Drupal proxy object. + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The container. + * @param string $drupal_proxy_original_service_id + * The service ID of the original service. + */ +public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id) +{ + $this->container = $container; + $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id; +} + +EOS; + + return $output; + } + + /** + * Build the required use statements of the proxy class. + * + * @return string + */ + protected function buildUseStatements() { + $output = ''; + + return $output; + } + +}