Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
80.00% |
8 / 10 |
CRAP | |
91.58% |
87 / 95 |
ProxyBuilder | |
0.00% |
0 / 1 |
|
80.00% |
8 / 10 |
33.65 | |
91.58% |
87 / 95 |
buildProxyClassName | |
100.00% |
1 / 1 |
1 | |
100.00% |
6 / 6 |
|||
buildProxyNamespace | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
build | |
0.00% |
0 / 1 |
14.23 | |
80.65% |
25 / 31 |
|||
anonymous function | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
buildLazyLoadItselfMethod | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
buildMethod | |
100.00% |
1 / 1 |
4 | |
100.00% |
16 / 16 |
|||
buildParameter | |
100.00% |
1 / 1 |
6 | |
100.00% |
14 / 14 |
|||
buildMethodBody | |
100.00% |
1 / 1 |
3 | |
100.00% |
11 / 11 |
|||
buildConstructorMethod | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
buildUseStatements | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Component\ProxyBuilder\ProxyBuilder. | |
*/ | |
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) { | |
if (isset($interfaces[$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; | |
} | |
} |