2015-08-17 17:00:26 -07:00
< ? 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 ) {
2015-08-27 12:03:05 -07:00
$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 ;
2015-08-17 17:00:26 -07:00
}
/**
* Builds a proxy class string .
*
* @ param string $class_name
* The class name of the actual service .
2015-08-27 12:03:05 -07:00
* @ param string $proxy_class_name
* ( optional ) The class name of the proxy service .
2015-08-17 17:00:26 -07:00
*
* @ return string
* The full string with namespace class and methods .
*/
2015-08-27 12:03:05 -07:00
public function build ( $class_name , $proxy_class_name = '' ) {
2015-08-17 17:00:26 -07:00
$reflection = new \ReflectionClass ( $class_name );
2015-08-27 12:03:05 -07:00
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 );
}
2015-08-17 17:00:26 -07:00
$output = '' ;
$class_documentation = <<< 'EOS'
2015-08-27 12:03:05 -07:00
namespace {{ namespace }}{
/**
* Provides a proxy class for \ {{ class_name }} .
*
* @ see \Drupal\Component\ProxyBuilder
*/
2015-08-17 17:00:26 -07:00
EOS ;
2015-08-27 12:03:05 -07:00
$class_start = ' class {{ proxy_class_shortname }}' ;
2015-08-17 17:00:26 -07:00
// 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'
/**
2015-08-27 12:03:05 -07:00
* The id of the original proxied service .
*
2015-08-17 17:00:26 -07:00
* @ var string
*/
2015-08-27 12:03:05 -07:00
protected $drupalProxyOriginalServiceId ;
2015-08-17 17:00:26 -07:00
/**
2015-08-27 12:03:05 -07:00
* The real proxied service , after it was lazy loaded .
*
2015-08-17 17:00:26 -07:00
* @ 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 ;
}
2015-08-27 12:03:05 -07:00
return " $value " ;
2015-08-17 17:00:26 -07:00
}, explode ( " \n " , $output )));
2015-08-27 12:03:05 -07:00
$final_output = $class_documentation . $class_start . " \n { \n \n " . $output . " \n } \n \n } \n " ;
2015-08-17 17:00:26 -07:00
$final_output = str_replace ( '{{ class_name }}' , $class_name , $final_output );
2015-08-27 12:03:05 -07:00
$final_output = str_replace ( '{{ namespace }}' , $proxy_namespace ? $proxy_namespace . ' ' : '' , $final_output );
$final_output = str_replace ( '{{ proxy_class_shortname }}' , $proxy_class_shortname , $final_output );
2015-08-17 17:00:26 -07:00
return $final_output ;
}
/**
* Generates the string for the method which loads the actual service .
*
* @ return string
*/
protected function buildLazyLoadItselfMethod () {
$output = <<< 'EOS'
2015-08-27 12:03:05 -07:00
/**
* Lazy loads the real service from the container .
*
* @ return object
* Returns the constructed real service .
*/
2015-08-17 17:00:26 -07:00
protected function lazyLoadItself ()
{
if ( ! isset ( $this -> service )) {
2015-08-27 12:03:05 -07:00
$this -> service = $this -> container -> get ( $this -> drupalProxyOriginalServiceId );
2015-08-17 17:00:26 -07:00
}
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 = '&' ;
}
2015-08-27 12:03:05 -07:00
$signature_line = <<< 'EOS'
/**
* { @ inheritdoc }
*/
EOS ;
2015-08-17 17:00:26 -07:00
if ( $reflection_method -> isStatic ()) {
2015-08-27 12:03:05 -07:00
$signature_line .= 'public static function ' . $reference . $function_name . '(' ;
2015-08-17 17:00:26 -07:00
}
else {
2015-08-27 12:03:05 -07:00
$signature_line .= 'public function ' . $reference . $function_name . '(' ;
2015-08-17 17:00:26 -07:00
}
$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'
2015-08-27 12:03:05 -07:00
/**
* 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 )
2015-08-17 17:00:26 -07:00
{
$this -> container = $container ;
2015-08-27 12:03:05 -07:00
$this -> drupalProxyOriginalServiceId = $drupal_proxy_original_service_id ;
2015-08-17 17:00:26 -07:00
}
EOS ;
return $output ;
}
/**
* Build the required use statements of the proxy class .
*
* @ return string
*/
protected function buildUseStatements () {
$output = '' ;
return $output ;
}
}