View source  
  <?php
namespace Drupal\Component\ProxyBuilder;
class ProxyBuilder {
  
  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;
  }
  
  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;
  }
  
  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 }}';
    
    if ($interfaces = $reflection
      ->getInterfaces()) {
      foreach ($interfaces as $interface_name => $interface) {
        
        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();
    
    $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;
    
    $methods = [];
    $methods[] = $this
      ->buildConstructorMethod();
    $methods[] = $this
      ->buildLazyLoadItselfMethod();
    
    $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);
    
    $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;
  }
  
  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;
  }
  
  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;
  }
  
  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;
  }
  
  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}(";
    }
    
    $parameters = [];
    foreach ($reflection_method
      ->getParameters() as $parameter) {
      $parameters[] = '$' . $parameter
        ->getName();
    }
    $output .= implode(', ', $parameters) . ');';
    return $output;
  }
  
  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;
  }
  
  protected function buildUseStatements() {
    $output = '';
    return $output;
  }
}