/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $class_name = ltrim($input->getArgument('class_name'), '\\'); $namespace_root = $input->getArgument('namespace_root_path'); $match = []; preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match); if ($match) { $root_namespace = $match[1]; $rest_fqcn = $match[2]; $proxy_filename = $namespace_root . '/ProxyClass/' . str_replace('\\', '/', $rest_fqcn) . '.php'; $proxy_class_name = $root_namespace . '\\ProxyClass\\' . $rest_fqcn; $proxy_class_string = $this->proxyBuilder->build($class_name); $file_string = <<<EOF <?php // @codingStandardsIgnoreFile /** * This file was generated via php core/scripts/generate-proxy-class.php '{$class_name}' "{$namespace_root}". */ {{ proxy_class_string }} EOF; $file_string = str_replace(['{{ proxy_class_name }}', '{{ proxy_class_string }}'], [$proxy_class_name, $proxy_class_string], $file_string); mkdir(dirname($proxy_filename), 0775, TRUE); file_put_contents($proxy_filename, $file_string); $output->writeln(sprintf('Proxy of class %s written to %s', $class_name, $proxy_filename)); } }
/** * {@inheritdoc} */ public function getProxyCode(Definition $definition) { // Maybe the same class is used in different services, which are both marked // as lazy (just think about 2 database connections). // In those cases we should not generate proxy code the second time. if (!isset($this->buildClasses[$definition->getClass()])) { $this->buildClasses[$definition->getClass()] = TRUE; return $this->builder->build($definition->getClass()); } else { return ''; } }
/** * @covers ::getProxyCode */ public function testGetProxyCodeWithSameClassMultipleTimes() { $definition = new Definition('Drupal\\Tests\\Component\\ProxyBuilder\\TestService'); $definition->setLazy(TRUE); $class = 'class Drupal_Tests_Component_ProxyBuilder_TestService_Proxy {}'; $this->proxyBuilder->expects($this->once())->method('build')->with('Drupal\\Tests\\Component\\ProxyBuilder\\TestService')->willReturn($class); $result = $this->proxyDumper->getProxyCode($definition); $this->assertEquals($class, $result); $result = $this->proxyDumper->getProxyCode($definition); $this->assertEquals('', $result); }
/** * Constructs the expected class output. * * @param string $expected_methods_body * The expected body of decorated methods. * * @return string * The code of the entire proxy. */ protected function buildExpectedClass($class, $expected_methods_body, $interface_string = '') { $proxy_class = $this->proxyBuilder->buildProxyClassName($class); $expected_string = <<<'EOS' /** * Provides a proxy class for \{{ class }}. * * @see \Drupal\Component\ProxyBuilder */ class {{ proxy_class }}{{ interface_string }} { /** * @var string */ protected $serviceId; /** * @var \{{ class }} */ protected $service; /** * The service container. * * @var \Symfony\Component\DependencyInjection\ContainerInterface */ protected $container; public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $serviceId) { $this->container = $container; $this->serviceId = $serviceId; } protected function lazyLoadItself() { if (!isset($this->service)) { $method_name = 'get' . Container::camelize($this->serviceId) . 'Service'; $this->service = $this->container->$method_name(false); } return $this->service; } {{ expected_methods_body }} } EOS; $expected_string = str_replace('{{ proxy_class }}', $proxy_class, $expected_string); $expected_string = str_replace('{{ class }}', $class, $expected_string); $expected_string = str_replace('{{ expected_methods_body }}', $expected_methods_body, $expected_string); $expected_string = str_replace('{{ interface_string }}', $interface_string, $expected_string); return $expected_string; }
/** * {@inheritdoc} */ public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $service_id => $definition) { if ($definition->isLazy()) { $proxy_class = ProxyBuilder::buildProxyClassName($definition->getClass()); if (class_exists($proxy_class)) { // Copy the existing definition to a new entry. $definition->setLazy(FALSE); // Ensure that the service is accessible. $definition->setPublic(TRUE); $new_service_id = 'drupal.proxy_original_service.' . $service_id; $container->setDefinition($new_service_id, $definition); $container->register($service_id, $proxy_class)->setArguments([new Reference('service_container'), $new_service_id]); } else { $class_name = $definition->getClass(); // Find the root namespace. $match = []; preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match); $root_namespace = $match[1]; // Find the root namespace path. $root_namespace_dir = '[namespace_root_path]'; $namespaces = $container->getParameter('container.namespaces'); // Hardcode Drupal Core, because it is not registered. $namespaces['Drupal\\Core'] = 'core/lib/Drupal/Core'; if (isset($namespaces[$root_namespace])) { $root_namespace_dir = $namespaces[$root_namespace]; } $message = <<<EOF Missing proxy class '{$proxy_class}' for lazy service '{$service_id}'. Use the following command to generate the proxy class: php core/scripts/generate-proxy-class.php '{$class_name}' "{$root_namespace_dir}" EOF; trigger_error($message, E_USER_WARNING); } } } }
/** * Constructs the expected class output. * * @param string $expected_methods_body * The expected body of decorated methods. * * @return string * The code of the entire proxy. */ protected function buildExpectedClass($class, $expected_methods_body, $interface_string = '') { $namespace = ProxyBuilder::buildProxyNamespace($class); $reflection = new \ReflectionClass($class); $proxy_class = $reflection->getShortName(); $expected_string = <<<'EOS' namespace {{ namespace }} { /** * Provides a proxy class for \{{ class }}. * * @see \Drupal\Component\ProxyBuilder */ class {{ proxy_class }}{{ interface_string }} { /** * The id of the original proxied service. * * @var string */ protected $drupalProxyOriginalServiceId; /** * The real proxied service, after it was lazy loaded. * * @var \{{ class }} */ protected $service; /** * The service container. * * @var \Symfony\Component\DependencyInjection\ContainerInterface */ protected $container; /** * 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; } /** * 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; } {{ expected_methods_body }} } } EOS; $expected_methods_body = implode("\n", array_map(function ($value) { if ($value === '') { return $value; } return " {$value}"; }, explode("\n", $expected_methods_body))); $expected_string = str_replace('{{ proxy_class }}', $proxy_class, $expected_string); $expected_string = str_replace('{{ namespace }}', $namespace, $expected_string); $expected_string = str_replace('{{ class }}', $class, $expected_string); $expected_string = str_replace('{{ expected_methods_body }}', $expected_methods_body, $expected_string); $expected_string = str_replace('{{ interface_string }}', $interface_string, $expected_string); return $expected_string; }
/** * {@inheritdoc{ */ protected function buildUseStatements() { $output = parent::buildUseStatements(); $output .= 'use \\Drupal\\Core\\DependencyInjection\\DependencySerializationTrait;' . "\n\n"; return $output; }