Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Component\ProxyBuilder;
|
Chris@0
|
4
|
Chris@0
|
5 /**
|
Chris@0
|
6 * Generates the string representation of the proxy service.
|
Chris@0
|
7 */
|
Chris@0
|
8 class ProxyBuilder {
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * Generates the used proxy class name from a given class name.
|
Chris@0
|
12 *
|
Chris@0
|
13 * @param string $class_name
|
Chris@0
|
14 * The class name of the actual service.
|
Chris@0
|
15 *
|
Chris@0
|
16 * @return string
|
Chris@0
|
17 * The class name of the proxy.
|
Chris@0
|
18 */
|
Chris@0
|
19 public static function buildProxyClassName($class_name) {
|
Chris@0
|
20 $match = [];
|
Chris@0
|
21 preg_match('/([a-zA-Z0-9_]+\\\\[a-zA-Z0-9_]+)\\\\(.+)/', $class_name, $match);
|
Chris@0
|
22 $root_namespace = $match[1];
|
Chris@0
|
23 $rest_fqcn = $match[2];
|
Chris@0
|
24 $proxy_class_name = $root_namespace . '\\ProxyClass\\' . $rest_fqcn;
|
Chris@0
|
25
|
Chris@0
|
26 return $proxy_class_name;
|
Chris@0
|
27 }
|
Chris@0
|
28
|
Chris@0
|
29 /**
|
Chris@0
|
30 * Generates the used proxy namespace from a given class name.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @param string $class_name
|
Chris@0
|
33 * The class name of the actual service.
|
Chris@0
|
34 *
|
Chris@0
|
35 * @return string
|
Chris@0
|
36 * The namespace name of the proxy.
|
Chris@0
|
37 */
|
Chris@0
|
38 public static function buildProxyNamespace($class_name) {
|
Chris@0
|
39 $proxy_classname = static::buildProxyClassName($class_name);
|
Chris@0
|
40
|
Chris@0
|
41 preg_match('/(.+)\\\\[a-zA-Z0-9]+/', $proxy_classname, $match);
|
Chris@0
|
42 $proxy_namespace = $match[1];
|
Chris@0
|
43 return $proxy_namespace;
|
Chris@0
|
44 }
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * Builds a proxy class string.
|
Chris@0
|
48 *
|
Chris@0
|
49 * @param string $class_name
|
Chris@0
|
50 * The class name of the actual service.
|
Chris@0
|
51 * @param string $proxy_class_name
|
Chris@0
|
52 * (optional) The class name of the proxy service.
|
Chris@0
|
53 *
|
Chris@0
|
54 * @return string
|
Chris@0
|
55 * The full string with namespace class and methods.
|
Chris@0
|
56 */
|
Chris@0
|
57 public function build($class_name, $proxy_class_name = '') {
|
Chris@0
|
58 $reflection = new \ReflectionClass($class_name);
|
Chris@0
|
59
|
Chris@0
|
60 if ($proxy_class_name) {
|
Chris@0
|
61 $proxy_class_reflection = new \ReflectionClass($proxy_class_name);
|
Chris@0
|
62 $proxy_namespace = $proxy_class_reflection->getNamespaceName();
|
Chris@0
|
63 }
|
Chris@0
|
64 else {
|
Chris@0
|
65 $proxy_class_name = $this->buildProxyClassName($class_name);
|
Chris@0
|
66 $proxy_namespace = $this->buildProxyNamespace($class_name);
|
Chris@0
|
67 $proxy_class_shortname = str_replace($proxy_namespace . '\\', '', $proxy_class_name);
|
Chris@0
|
68 }
|
Chris@0
|
69
|
Chris@0
|
70 $output = '';
|
Chris@0
|
71 $class_documentation = <<<'EOS'
|
Chris@0
|
72
|
Chris@0
|
73 namespace {{ namespace }}{
|
Chris@0
|
74
|
Chris@0
|
75 /**
|
Chris@0
|
76 * Provides a proxy class for \{{ class_name }}.
|
Chris@0
|
77 *
|
Chris@0
|
78 * @see \Drupal\Component\ProxyBuilder
|
Chris@0
|
79 */
|
Chris@0
|
80
|
Chris@0
|
81 EOS;
|
Chris@0
|
82 $class_start = ' class {{ proxy_class_shortname }}';
|
Chris@0
|
83
|
Chris@0
|
84 // For cases in which the implemented interface is a child of another
|
Chris@0
|
85 // interface, getInterfaceNames() also returns the parent. This causes a
|
Chris@0
|
86 // PHP error.
|
Chris@0
|
87 // In order to avoid that, check for each interface, whether one of its
|
Chris@0
|
88 // parents is also in the list and exclude it.
|
Chris@0
|
89 if ($interfaces = $reflection->getInterfaces()) {
|
Chris@0
|
90 foreach ($interfaces as $interface_name => $interface) {
|
Chris@0
|
91 // Exclude all parents from the list of implemented interfaces of the
|
Chris@0
|
92 // class.
|
Chris@0
|
93 if ($parent_interfaces = $interface->getInterfaceNames()) {
|
Chris@0
|
94 foreach ($parent_interfaces as $parent_interface) {
|
Chris@0
|
95 unset($interfaces[$parent_interface]);
|
Chris@0
|
96 }
|
Chris@0
|
97 }
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 $interface_names = [];
|
Chris@0
|
101 foreach ($interfaces as $interface) {
|
Chris@0
|
102 $interface_names[] = '\\' . $interface->getName();
|
Chris@0
|
103 }
|
Chris@0
|
104 $class_start .= ' implements ' . implode(', ', $interface_names);
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 $output .= $this->buildUseStatements();
|
Chris@0
|
108
|
Chris@0
|
109 // The actual class;
|
Chris@0
|
110 $properties = <<<'EOS'
|
Chris@0
|
111 /**
|
Chris@0
|
112 * The id of the original proxied service.
|
Chris@0
|
113 *
|
Chris@0
|
114 * @var string
|
Chris@0
|
115 */
|
Chris@0
|
116 protected $drupalProxyOriginalServiceId;
|
Chris@0
|
117
|
Chris@0
|
118 /**
|
Chris@0
|
119 * The real proxied service, after it was lazy loaded.
|
Chris@0
|
120 *
|
Chris@0
|
121 * @var \{{ class_name }}
|
Chris@0
|
122 */
|
Chris@0
|
123 protected $service;
|
Chris@0
|
124
|
Chris@0
|
125 /**
|
Chris@0
|
126 * The service container.
|
Chris@0
|
127 *
|
Chris@0
|
128 * @var \Symfony\Component\DependencyInjection\ContainerInterface
|
Chris@0
|
129 */
|
Chris@0
|
130 protected $container;
|
Chris@0
|
131
|
Chris@0
|
132
|
Chris@0
|
133 EOS;
|
Chris@0
|
134
|
Chris@0
|
135 $output .= $properties;
|
Chris@0
|
136
|
Chris@0
|
137 // Add all the methods.
|
Chris@0
|
138 $methods = [];
|
Chris@0
|
139 $methods[] = $this->buildConstructorMethod();
|
Chris@0
|
140 $methods[] = $this->buildLazyLoadItselfMethod();
|
Chris@0
|
141
|
Chris@0
|
142 // Add all the methods of the proxied service.
|
Chris@0
|
143 $reflection_methods = $reflection->getMethods();
|
Chris@0
|
144
|
Chris@0
|
145 foreach ($reflection_methods as $method) {
|
Chris@0
|
146 if ($method->getName() === '__construct') {
|
Chris@0
|
147 continue;
|
Chris@0
|
148 }
|
Chris@0
|
149
|
Chris@0
|
150 if ($method->isPublic()) {
|
Chris@0
|
151 $methods[] = $this->buildMethod($method) . "\n";
|
Chris@0
|
152 }
|
Chris@0
|
153 }
|
Chris@0
|
154
|
Chris@0
|
155 $output .= implode("\n", $methods);
|
Chris@0
|
156
|
Chris@0
|
157 // Indent the output.
|
Chris@0
|
158 $output = implode("\n", array_map(function ($value) {
|
Chris@0
|
159 if ($value === '') {
|
Chris@0
|
160 return $value;
|
Chris@0
|
161 }
|
Chris@0
|
162 return " $value";
|
Chris@0
|
163 }, explode("\n", $output)));
|
Chris@0
|
164
|
Chris@0
|
165 $final_output = $class_documentation . $class_start . "\n {\n\n" . $output . "\n }\n\n}\n";
|
Chris@0
|
166
|
Chris@0
|
167 $final_output = str_replace('{{ class_name }}', $class_name, $final_output);
|
Chris@0
|
168 $final_output = str_replace('{{ namespace }}', $proxy_namespace ? $proxy_namespace . ' ' : '', $final_output);
|
Chris@0
|
169 $final_output = str_replace('{{ proxy_class_shortname }}', $proxy_class_shortname, $final_output);
|
Chris@0
|
170
|
Chris@0
|
171 return $final_output;
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@0
|
174 /**
|
Chris@0
|
175 * Generates the string for the method which loads the actual service.
|
Chris@0
|
176 *
|
Chris@0
|
177 * @return string
|
Chris@0
|
178 */
|
Chris@0
|
179 protected function buildLazyLoadItselfMethod() {
|
Chris@0
|
180 $output = <<<'EOS'
|
Chris@0
|
181 /**
|
Chris@0
|
182 * Lazy loads the real service from the container.
|
Chris@0
|
183 *
|
Chris@0
|
184 * @return object
|
Chris@0
|
185 * Returns the constructed real service.
|
Chris@0
|
186 */
|
Chris@0
|
187 protected function lazyLoadItself()
|
Chris@0
|
188 {
|
Chris@0
|
189 if (!isset($this->service)) {
|
Chris@0
|
190 $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 return $this->service;
|
Chris@0
|
194 }
|
Chris@0
|
195
|
Chris@0
|
196 EOS;
|
Chris@0
|
197
|
Chris@0
|
198 return $output;
|
Chris@0
|
199 }
|
Chris@0
|
200
|
Chris@0
|
201 /**
|
Chris@0
|
202 * Generates the string representation of a single method: signature, body.
|
Chris@0
|
203 *
|
Chris@0
|
204 * @param \ReflectionMethod $reflection_method
|
Chris@0
|
205 * A reflection method for the method.
|
Chris@0
|
206 *
|
Chris@0
|
207 * @return string
|
Chris@0
|
208 */
|
Chris@0
|
209 protected function buildMethod(\ReflectionMethod $reflection_method) {
|
Chris@0
|
210
|
Chris@0
|
211 $parameters = [];
|
Chris@0
|
212 foreach ($reflection_method->getParameters() as $parameter) {
|
Chris@0
|
213 $parameters[] = $this->buildParameter($parameter);
|
Chris@0
|
214 }
|
Chris@0
|
215
|
Chris@0
|
216 $function_name = $reflection_method->getName();
|
Chris@0
|
217
|
Chris@0
|
218 $reference = '';
|
Chris@0
|
219 if ($reflection_method->returnsReference()) {
|
Chris@0
|
220 $reference = '&';
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 $signature_line = <<<'EOS'
|
Chris@0
|
224 /**
|
Chris@0
|
225 * {@inheritdoc}
|
Chris@0
|
226 */
|
Chris@0
|
227
|
Chris@0
|
228 EOS;
|
Chris@0
|
229
|
Chris@0
|
230 if ($reflection_method->isStatic()) {
|
Chris@0
|
231 $signature_line .= 'public static function ' . $reference . $function_name . '(';
|
Chris@0
|
232 }
|
Chris@0
|
233 else {
|
Chris@0
|
234 $signature_line .= 'public function ' . $reference . $function_name . '(';
|
Chris@0
|
235 }
|
Chris@0
|
236
|
Chris@0
|
237 $signature_line .= implode(', ', $parameters);
|
Chris@0
|
238 $signature_line .= ')';
|
Chris@0
|
239
|
Chris@0
|
240 $output = $signature_line . "\n{\n";
|
Chris@0
|
241
|
Chris@0
|
242 $output .= $this->buildMethodBody($reflection_method);
|
Chris@0
|
243
|
Chris@0
|
244 $output .= "\n" . '}';
|
Chris@0
|
245 return $output;
|
Chris@0
|
246 }
|
Chris@0
|
247
|
Chris@0
|
248 /**
|
Chris@0
|
249 * Builds a string for a single parameter of a method.
|
Chris@0
|
250 *
|
Chris@0
|
251 * @param \ReflectionParameter $parameter
|
Chris@0
|
252 * A reflection object of the parameter.
|
Chris@0
|
253 *
|
Chris@0
|
254 * @return string
|
Chris@0
|
255 */
|
Chris@0
|
256 protected function buildParameter(\ReflectionParameter $parameter) {
|
Chris@0
|
257 $parameter_string = '';
|
Chris@0
|
258
|
Chris@0
|
259 if ($parameter->isArray()) {
|
Chris@0
|
260 $parameter_string .= 'array ';
|
Chris@0
|
261 }
|
Chris@0
|
262 elseif ($parameter->isCallable()) {
|
Chris@0
|
263 $parameter_string .= 'callable ';
|
Chris@0
|
264 }
|
Chris@0
|
265 elseif ($class = $parameter->getClass()) {
|
Chris@0
|
266 $parameter_string .= '\\' . $class->getName() . ' ';
|
Chris@0
|
267 }
|
Chris@0
|
268
|
Chris@0
|
269 if ($parameter->isPassedByReference()) {
|
Chris@0
|
270 $parameter_string .= '&';
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273 $parameter_string .= '$' . $parameter->getName();
|
Chris@0
|
274
|
Chris@0
|
275 if ($parameter->isDefaultValueAvailable()) {
|
Chris@0
|
276 $parameter_string .= ' = ';
|
Chris@0
|
277 $parameter_string .= var_export($parameter->getDefaultValue(), TRUE);
|
Chris@0
|
278 }
|
Chris@0
|
279
|
Chris@0
|
280 return $parameter_string;
|
Chris@0
|
281 }
|
Chris@0
|
282
|
Chris@0
|
283 /**
|
Chris@0
|
284 * Builds the body of a wrapped method.
|
Chris@0
|
285 *
|
Chris@0
|
286 * @param \ReflectionMethod $reflection_method
|
Chris@0
|
287 * A reflection method for the method.
|
Chris@0
|
288 *
|
Chris@0
|
289 * @return string
|
Chris@0
|
290 */
|
Chris@0
|
291 protected function buildMethodBody(\ReflectionMethod $reflection_method) {
|
Chris@0
|
292 $output = '';
|
Chris@0
|
293
|
Chris@0
|
294 $function_name = $reflection_method->getName();
|
Chris@0
|
295
|
Chris@0
|
296 if (!$reflection_method->isStatic()) {
|
Chris@0
|
297 $output .= ' return $this->lazyLoadItself()->' . $function_name . '(';
|
Chris@0
|
298 }
|
Chris@0
|
299 else {
|
Chris@0
|
300 $class_name = $reflection_method->getDeclaringClass()->getName();
|
Chris@0
|
301 $output .= " \\$class_name::$function_name(";
|
Chris@0
|
302 }
|
Chris@0
|
303
|
Chris@0
|
304 // Add parameters;
|
Chris@0
|
305 $parameters = [];
|
Chris@0
|
306 foreach ($reflection_method->getParameters() as $parameter) {
|
Chris@0
|
307 $parameters[] = '$' . $parameter->getName();
|
Chris@0
|
308 }
|
Chris@0
|
309
|
Chris@0
|
310 $output .= implode(', ', $parameters) . ');';
|
Chris@0
|
311
|
Chris@0
|
312 return $output;
|
Chris@0
|
313 }
|
Chris@0
|
314
|
Chris@0
|
315 /**
|
Chris@0
|
316 * Builds the constructor used to inject the actual service ID.
|
Chris@0
|
317 *
|
Chris@0
|
318 * @return string
|
Chris@0
|
319 */
|
Chris@0
|
320 protected function buildConstructorMethod() {
|
Chris@0
|
321 $output = <<<'EOS'
|
Chris@0
|
322 /**
|
Chris@0
|
323 * Constructs a ProxyClass Drupal proxy object.
|
Chris@0
|
324 *
|
Chris@0
|
325 * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
Chris@0
|
326 * The container.
|
Chris@0
|
327 * @param string $drupal_proxy_original_service_id
|
Chris@0
|
328 * The service ID of the original service.
|
Chris@0
|
329 */
|
Chris@0
|
330 public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
|
Chris@0
|
331 {
|
Chris@0
|
332 $this->container = $container;
|
Chris@0
|
333 $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
|
Chris@0
|
334 }
|
Chris@0
|
335
|
Chris@0
|
336 EOS;
|
Chris@0
|
337
|
Chris@0
|
338 return $output;
|
Chris@0
|
339 }
|
Chris@0
|
340
|
Chris@0
|
341 /**
|
Chris@0
|
342 * Build the required use statements of the proxy class.
|
Chris@0
|
343 *
|
Chris@0
|
344 * @return string
|
Chris@0
|
345 */
|
Chris@0
|
346 protected function buildUseStatements() {
|
Chris@0
|
347 $output = '';
|
Chris@0
|
348
|
Chris@0
|
349 return $output;
|
Chris@0
|
350 }
|
Chris@0
|
351
|
Chris@0
|
352 }
|