PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.36.0
MailPoet – Newsletters, Email Marketing, and Automation v5.36.0
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / doctrine / common / src / Proxy / ProxyGenerator.php
mailpoet / vendor-prefixed / doctrine / common / src / Proxy Last commit date
Exception 2 years ago AbstractProxyFactory.php 2 years ago Autoloader.php 2 years ago Proxy.php 2 years ago ProxyDefinition.php 2 years ago ProxyGenerator.php 9 months ago index.php 2 years ago
ProxyGenerator.php
734 lines
1 <?php
2 namespace MailPoetVendor\Doctrine\Common\Proxy;
3 if (!defined('ABSPATH')) exit;
4 use BackedEnum;
5 use MailPoetVendor\Doctrine\Common\Proxy\Exception\InvalidArgumentException;
6 use MailPoetVendor\Doctrine\Common\Proxy\Exception\UnexpectedValueException;
7 use MailPoetVendor\Doctrine\Common\Util\ClassUtils;
8 use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadata;
9 use ReflectionIntersectionType;
10 use ReflectionMethod;
11 use ReflectionNamedType;
12 use ReflectionParameter;
13 use ReflectionProperty;
14 use ReflectionType;
15 use ReflectionUnionType;
16 use function array_combine;
17 use function array_diff;
18 use function array_key_exists;
19 use function array_map;
20 use function array_slice;
21 use function array_unique;
22 use function assert;
23 use function bin2hex;
24 use function call_user_func;
25 use function chmod;
26 use function class_exists;
27 use function dirname;
28 use function explode;
29 use function file;
30 use function file_put_contents;
31 use function get_class;
32 use function implode;
33 use function in_array;
34 use function interface_exists;
35 use function is_callable;
36 use function is_dir;
37 use function is_scalar;
38 use function is_string;
39 use function is_writable;
40 use function lcfirst;
41 use function ltrim;
42 use function method_exists;
43 use function mkdir;
44 use function preg_match;
45 use function preg_match_all;
46 use function preg_replace;
47 use function preg_split;
48 use function random_bytes;
49 use function rename;
50 use function rtrim;
51 use function sprintf;
52 use function str_replace;
53 use function strpos;
54 use function strrev;
55 use function strtolower;
56 use function strtr;
57 use function substr;
58 use function trim;
59 use function var_export;
60 use const DIRECTORY_SEPARATOR;
61 use const PHP_VERSION_ID;
62 use const PREG_SPLIT_DELIM_CAPTURE;
63 class ProxyGenerator
64 {
65 public const PATTERN_MATCH_ID_METHOD = <<<'EOT'
66 ((?(DEFINE)
67 (?<type>\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*)
68 (?<intersection_type>(?&type)\s*&\s*(?&type))
69 (?<union_type>(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type))(?:\s*\|\s*(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type)))+)
70 )(?:public\s+)?(?:function\s+%s\s*\(\)\s*)\s*(?::\s*(?:(?&union_type)|(?&intersection_type)|(?:\??(?&type)))\s*)?{\s*return\s*\$this->%s;\s*})i
71 EOT;
72 private $proxyNamespace;
73 private $proxyDirectory;
74 protected $placeholders = ['baseProxyInterface' => Proxy::class, 'additionalProperties' => ''];
75 protected $proxyClassTemplate = '<?php
76 namespace <namespace>;
77 <enumUseStatements>
78 class <proxyShortClassName> extends \\<className> implements \\<baseProxyInterface>
79 {
80 public $__initializer__;
81 public $__cloner__;
82 public $__isInitialized__ = false;
83 public static $lazyPropertiesNames = <lazyPropertiesNames>;
84 public static $lazyPropertiesDefaults = <lazyPropertiesDefaults>;
85 <additionalProperties>
86 <constructorImpl>
87 <magicGet>
88 <magicSet>
89 <magicIsset>
90 <sleepImpl>
91 <wakeupImpl>
92 <cloneImpl>
93 public function __load(): void
94 {
95 $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []);
96 }
97 public function __isInitialized(): bool
98 {
99 return $this->__isInitialized__;
100 }
101 public function __setInitialized($initialized): void
102 {
103 $this->__isInitialized__ = $initialized;
104 }
105 public function __setInitializer(?\\Closure $initializer = null): void
106 {
107 $this->__initializer__ = $initializer;
108 }
109 public function __getInitializer(): ?\\Closure
110 {
111 return $this->__initializer__;
112 }
113 public function __setCloner(?\\Closure $cloner = null): void
114 {
115 $this->__cloner__ = $cloner;
116 }
117 public function __getCloner(): ?\\Closure
118 {
119 return $this->__cloner__;
120 }
121 public function __getLazyProperties(): array
122 {
123 return self::$lazyPropertiesDefaults;
124 }
125 <methods>
126 }
127 ';
128 public function __construct($proxyDirectory, $proxyNamespace)
129 {
130 if (!$proxyDirectory) {
131 throw InvalidArgumentException::proxyDirectoryRequired();
132 }
133 if (!$proxyNamespace) {
134 throw InvalidArgumentException::proxyNamespaceRequired();
135 }
136 $this->proxyDirectory = $proxyDirectory;
137 $this->proxyNamespace = $proxyNamespace;
138 }
139 public function setPlaceholder($name, $placeholder)
140 {
141 if (!is_string($placeholder) && !is_callable($placeholder)) {
142 throw InvalidArgumentException::invalidPlaceholder($name);
143 }
144 $this->placeholders[$name] = $placeholder;
145 }
146 public function setProxyClassTemplate($proxyClassTemplate)
147 {
148 $this->proxyClassTemplate = (string) $proxyClassTemplate;
149 }
150 public function generateProxyClass(ClassMetadata $class, $fileName = \false)
151 {
152 $this->verifyClassCanBeProxied($class);
153 preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches);
154 $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]);
155 $placeholders = [];
156 foreach ($placeholderMatches as $placeholder => $name) {
157 $placeholders[$placeholder] = $this->placeholders[$name] ?? [$this, 'generate' . $name];
158 }
159 foreach ($placeholders as &$placeholder) {
160 if (!is_callable($placeholder)) {
161 continue;
162 }
163 $placeholder = call_user_func($placeholder, $class);
164 }
165 $proxyCode = strtr($this->proxyClassTemplate, $placeholders);
166 if (!$fileName) {
167 $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class);
168 if (!class_exists($proxyClassName)) {
169 eval(substr($proxyCode, 5));
170 }
171 return;
172 }
173 $parentDirectory = dirname($fileName);
174 if (!is_dir($parentDirectory) && @mkdir($parentDirectory, 0775, \true) === \false) {
175 throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
176 }
177 if (!is_writable($parentDirectory)) {
178 throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory);
179 }
180 $tmpFileName = $fileName . '.' . bin2hex(random_bytes(12));
181 file_put_contents($tmpFileName, $proxyCode);
182 @chmod($tmpFileName, 0664);
183 rename($tmpFileName, $fileName);
184 }
185 private function verifyClassCanBeProxied(ClassMetadata $class)
186 {
187 if ($class->getReflectionClass()->isFinal()) {
188 throw InvalidArgumentException::classMustNotBeFinal($class->getName());
189 }
190 if ($class->getReflectionClass()->isAbstract()) {
191 throw InvalidArgumentException::classMustNotBeAbstract($class->getName());
192 }
193 if (PHP_VERSION_ID >= 80200 && $class->getReflectionClass()->isReadOnly()) {
194 throw InvalidArgumentException::classMustNotBeReadOnly($class->getName());
195 }
196 }
197 private function generateProxyShortClassName(ClassMetadata $class)
198 {
199 $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
200 $parts = explode('\\', strrev($proxyClassName), 2);
201 return strrev($parts[0]);
202 }
203 private function generateNamespace(ClassMetadata $class)
204 {
205 $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace);
206 $parts = explode('\\', strrev($proxyClassName), 2);
207 return strrev($parts[1]);
208 }
209 public function generateEnumUseStatements(ClassMetadata $class) : string
210 {
211 if (PHP_VERSION_ID < 80100) {
212 return "\n";
213 }
214 $defaultProperties = $class->getReflectionClass()->getDefaultProperties();
215 $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
216 $enumClasses = [];
217 foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
218 $name = $property->getName();
219 if (!in_array($name, $lazyLoadedPublicProperties, \true)) {
220 continue;
221 }
222 if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) {
223 $enumClassNameParts = explode('\\', get_class($defaultProperties[$name]));
224 $enumClasses[] = $enumClassNameParts[0];
225 }
226 }
227 return implode("\n", array_map(static function ($className) {
228 return 'use ' . $className . ';';
229 }, array_unique($enumClasses))) . "\n";
230 }
231 private function generateClassName(ClassMetadata $class)
232 {
233 return ltrim($class->getName(), '\\');
234 }
235 private function generateLazyPropertiesNames(ClassMetadata $class)
236 {
237 $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
238 $values = [];
239 foreach ($lazyPublicProperties as $name) {
240 $values[$name] = null;
241 }
242 return var_export($values, \true);
243 }
244 private function generateLazyPropertiesDefaults(ClassMetadata $class)
245 {
246 return var_export($this->getLazyLoadedPublicProperties($class), \true);
247 }
248 private function generateConstructorImpl(ClassMetadata $class)
249 {
250 $constructorImpl = <<<'EOT'
251 public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
252 {
253 EOT;
254 $toUnset = array_map(static function (string $name) : string {
255 return '$this->' . $name;
256 }, $this->getLazyLoadedPublicPropertiesNames($class));
257 return $constructorImpl . ($toUnset === [] ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") . <<<'EOT'
258 $this->__initializer__ = $initializer;
259 $this->__cloner__ = $cloner;
260 }
261 EOT;
262 }
263 private function generateMagicGet(ClassMetadata $class)
264 {
265 $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
266 $reflectionClass = $class->getReflectionClass();
267 $hasParentGet = \false;
268 $returnReference = '';
269 $inheritDoc = '';
270 $name = '$name';
271 $parametersString = '$name';
272 $returnTypeHint = null;
273 if ($reflectionClass->hasMethod('__get')) {
274 $hasParentGet = \true;
275 $inheritDoc = '{@inheritDoc}';
276 $methodReflection = $reflectionClass->getMethod('__get');
277 if ($methodReflection->returnsReference()) {
278 $returnReference = '& ';
279 }
280 $methodParameters = $methodReflection->getParameters();
281 $name = '$' . $methodParameters[0]->getName();
282 $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']);
283 $returnTypeHint = $this->getMethodReturnType($methodReflection);
284 }
285 if (empty($lazyPublicProperties) && !$hasParentGet) {
286 return '';
287 }
288 $magicGet = <<<EOT
289 public function {$returnReference}__get({$parametersString}){$returnTypeHint}
290 {
291 EOT;
292 if (!empty($lazyPublicProperties)) {
293 $magicGet .= <<<'EOT'
294 if (\array_key_exists($name, self::$lazyPropertiesNames)) {
295 $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
296 EOT;
297 if ($returnTypeHint === ': void') {
298 $magicGet .= "\n return;";
299 } else {
300 $magicGet .= "\n return \$this->\$name;";
301 }
302 $magicGet .= <<<'EOT'
303 }
304 EOT;
305 }
306 if ($hasParentGet) {
307 $magicGet .= <<<'EOT'
308 $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]);
309 EOT;
310 if ($returnTypeHint === ': void') {
311 $magicGet .= <<<'EOT'
312 parent::__get($name);
313 return;
314 EOT;
315 } elseif ($returnTypeHint === ': never') {
316 $magicGet .= <<<'EOT'
317 parent::__get($name);
318 EOT;
319 } else {
320 $magicGet .= <<<'EOT'
321 return parent::__get($name);
322 EOT;
323 }
324 } else {
325 $magicGet .= sprintf(<<<EOT
326 trigger_error(sprintf('Undefined property: %%s::\$%%s', __CLASS__, %s), E_USER_NOTICE);
327 EOT
328 , $name);
329 }
330 return $magicGet . "\n }";
331 }
332 private function generateMagicSet(ClassMetadata $class)
333 {
334 $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
335 $reflectionClass = $class->getReflectionClass();
336 $hasParentSet = \false;
337 $inheritDoc = '';
338 $parametersString = '$name, $value';
339 $returnTypeHint = null;
340 if ($reflectionClass->hasMethod('__set')) {
341 $hasParentSet = \true;
342 $inheritDoc = '{@inheritDoc}';
343 $methodReflection = $reflectionClass->getMethod('__set');
344 $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name', 'value']);
345 $returnTypeHint = $this->getMethodReturnType($methodReflection);
346 }
347 if (empty($lazyPublicProperties) && !$hasParentSet) {
348 return '';
349 }
350 $magicSet = <<<EOT
351 public function __set({$parametersString}){$returnTypeHint}
352 {
353 EOT;
354 if (!empty($lazyPublicProperties)) {
355 $magicSet .= <<<'EOT'
356 if (\array_key_exists($name, self::$lazyPropertiesNames)) {
357 $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
358 $this->$name = $value;
359 return;
360 }
361 EOT;
362 }
363 if ($hasParentSet) {
364 $magicSet .= <<<'EOT'
365 $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]);
366 EOT;
367 if ($returnTypeHint === ': void') {
368 $magicSet .= <<<'EOT'
369 parent::__set($name, $value);
370 return;
371 EOT;
372 } elseif ($returnTypeHint === ': never') {
373 $magicSet .= <<<'EOT'
374 parent::__set($name, $value);
375 EOT;
376 } else {
377 $magicSet .= <<<'EOT'
378 return parent::__set($name, $value);
379 EOT;
380 }
381 } else {
382 $magicSet .= ' $this->$name = $value;';
383 }
384 return $magicSet . "\n }";
385 }
386 private function generateMagicIsset(ClassMetadata $class)
387 {
388 $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
389 $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset');
390 $parametersString = '$name';
391 $returnTypeHint = null;
392 if ($hasParentIsset) {
393 $methodReflection = $class->getReflectionClass()->getMethod('__isset');
394 $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']);
395 $returnTypeHint = $this->getMethodReturnType($methodReflection);
396 }
397 if (empty($lazyPublicProperties) && !$hasParentIsset) {
398 return '';
399 }
400 $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : '';
401 $magicIsset = <<<EOT
402 public function __isset({$parametersString}){$returnTypeHint}
403 {
404 EOT;
405 if (!empty($lazyPublicProperties)) {
406 $magicIsset .= <<<'EOT'
407 if (\array_key_exists($name, self::$lazyPropertiesNames)) {
408 $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
409 return isset($this->$name);
410 }
411 EOT;
412 }
413 if ($hasParentIsset) {
414 $magicIsset .= <<<'EOT'
415 $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]);
416 return parent::__isset($name);
417 EOT;
418 } else {
419 $magicIsset .= ' return false;';
420 }
421 return $magicIsset . "\n }";
422 }
423 private function generateSleepImpl(ClassMetadata $class)
424 {
425 $reflectionClass = $class->getReflectionClass();
426 $hasParentSleep = $reflectionClass->hasMethod('__sleep');
427 $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : '';
428 $returnTypeHint = $hasParentSleep ? $this->getMethodReturnType($reflectionClass->getMethod('__sleep')) : '';
429 $sleepImpl = <<<EOT
430 public function __sleep(){$returnTypeHint}
431 {
432 EOT;
433 if ($hasParentSleep) {
434 return $sleepImpl . <<<'EOT'
435 $properties = array_merge(['__isInitialized__'], parent::__sleep());
436 if ($this->__isInitialized__) {
437 $properties = array_diff($properties, array_keys(self::$lazyPropertiesNames));
438 }
439 return $properties;
440 }
441 EOT;
442 }
443 $allProperties = ['__isInitialized__'];
444 foreach ($class->getReflectionClass()->getProperties() as $prop) {
445 assert($prop instanceof ReflectionProperty);
446 if ($prop->isStatic()) {
447 continue;
448 }
449 $allProperties[] = $prop->isPrivate() ? "\x00" . $prop->getDeclaringClass()->getName() . "\x00" . $prop->getName() : $prop->getName();
450 }
451 $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
452 $protectedProperties = array_diff($allProperties, $lazyPublicProperties);
453 foreach ($allProperties as &$property) {
454 $property = var_export($property, \true);
455 }
456 foreach ($protectedProperties as &$property) {
457 $property = var_export($property, \true);
458 }
459 $allProperties = implode(', ', $allProperties);
460 $protectedProperties = implode(', ', $protectedProperties);
461 return $sleepImpl . <<<EOT
462 if (\$this->__isInitialized__) {
463 return [{$allProperties}];
464 }
465 return [{$protectedProperties}];
466 }
467 EOT;
468 }
469 private function generateWakeupImpl(ClassMetadata $class)
470 {
471 $reflectionClass = $class->getReflectionClass();
472 $hasParentWakeup = $reflectionClass->hasMethod('__wakeup');
473 $unsetPublicProperties = [];
474 foreach ($this->getLazyLoadedPublicPropertiesNames($class) as $lazyPublicProperty) {
475 $unsetPublicProperties[] = '$this->' . $lazyPublicProperty;
476 }
477 $shortName = $this->generateProxyShortClassName($class);
478 $inheritDoc = $hasParentWakeup ? '{@inheritDoc}' : '';
479 $returnTypeHint = $hasParentWakeup ? $this->getMethodReturnType($reflectionClass->getMethod('__wakeup')) : '';
480 $wakeupImpl = <<<EOT
481 public function __wakeup(){$returnTypeHint}
482 {
483 if ( ! \$this->__isInitialized__) {
484 \$this->__initializer__ = function ({$shortName} \$proxy) {
485 \$proxy->__setInitializer(null);
486 \$proxy->__setCloner(null);
487 \$existingProperties = get_object_vars(\$proxy);
488 foreach (\$proxy::\$lazyPropertiesDefaults as \$property => \$defaultValue) {
489 if ( ! array_key_exists(\$property, \$existingProperties)) {
490 \$proxy->\$property = \$defaultValue;
491 }
492 }
493 };
494 EOT;
495 if (!empty($unsetPublicProperties)) {
496 $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ');';
497 }
498 $wakeupImpl .= "\n }";
499 if ($hasParentWakeup) {
500 $wakeupImpl .= "\n parent::__wakeup();";
501 }
502 $wakeupImpl .= "\n }";
503 return $wakeupImpl;
504 }
505 private function generateCloneImpl(ClassMetadata $class)
506 {
507 $reflectionClass = $class->getReflectionClass();
508 $hasParentClone = $reflectionClass->hasMethod('__clone');
509 $returnTypeHint = $hasParentClone ? $this->getMethodReturnType($reflectionClass->getMethod('__clone')) : '';
510 $inheritDoc = $hasParentClone ? '{@inheritDoc}' : '';
511 $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : '';
512 return <<<EOT
513 public function __clone(){$returnTypeHint}
514 {
515 \$this->__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []);
516 {$callParentClone} }
517 EOT;
518 }
519 private function generateMethods(ClassMetadata $class)
520 {
521 $methods = '';
522 $methodNames = [];
523 $reflectionMethods = $class->getReflectionClass()->getMethods(ReflectionMethod::IS_PUBLIC);
524 $skippedMethods = ['__sleep' => \true, '__clone' => \true, '__wakeup' => \true, '__get' => \true, '__set' => \true, '__isset' => \true];
525 foreach ($reflectionMethods as $method) {
526 $name = $method->getName();
527 if ($method->isConstructor() || isset($skippedMethods[strtolower($name)]) || isset($methodNames[$name]) || $method->isFinal() || $method->isStatic() || !$method->isPublic()) {
528 continue;
529 }
530 $methodNames[$name] = \true;
531 $methods .= "\n /**\n" . " * {@inheritDoc}\n" . " */\n" . ' public function ';
532 if ($method->returnsReference()) {
533 $methods .= '&';
534 }
535 $methods .= $name . '(' . $this->buildParametersString($method->getParameters()) . ')';
536 $methods .= $this->getMethodReturnType($method);
537 $methods .= "\n" . ' {' . "\n";
538 if ($this->isShortIdentifierGetter($method, $class)) {
539 $identifier = lcfirst(substr($name, 3));
540 $fieldType = $class->getTypeOfField($identifier);
541 $cast = in_array($fieldType, ['integer', 'smallint'], \true) ? '(int) ' : '';
542 $methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
543 $methods .= ' ';
544 $methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : '';
545 $methods .= $cast . ' parent::' . $method->getName() . "();\n";
546 $methods .= ' }' . "\n\n";
547 }
548 $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters()));
549 $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters()));
550 $methods .= "\n \$this->__initializer__ " . '&& $this->__initializer__->__invoke($this, ' . var_export($name, \true) . ', [' . $invokeParamsString . ']);' . "\n\n " . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') . 'parent::' . $name . '(' . $callParamsString . ');' . "\n" . ' }' . "\n";
551 }
552 return $methods;
553 }
554 public function getProxyFileName($className, $baseDirectory = null)
555 {
556 $baseDirectory = $baseDirectory ?: $this->proxyDirectory;
557 return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER . str_replace('\\', '', $className) . '.php';
558 }
559 private function isShortIdentifierGetter($method, ClassMetadata $class)
560 {
561 $identifier = lcfirst(substr($method->getName(), 3));
562 $startLine = $method->getStartLine();
563 $endLine = $method->getEndLine();
564 $cheapCheck = $method->getNumberOfParameters() === 0 && substr($method->getName(), 0, 3) === 'get' && in_array($identifier, $class->getIdentifier(), \true) && $class->hasField($identifier) && $endLine - $startLine <= 4;
565 if ($cheapCheck) {
566 $code = file($method->getFileName());
567 $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1)));
568 $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
569 if (preg_match($pattern, $code)) {
570 return \true;
571 }
572 }
573 return \false;
574 }
575 private function getLazyLoadedPublicPropertiesNames(ClassMetadata $class) : array
576 {
577 $properties = [];
578 foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
579 $name = $property->getName();
580 if (!$class->hasField($name) && !$class->hasAssociation($name) || $class->isIdentifier($name)) {
581 continue;
582 }
583 $properties[] = $name;
584 }
585 return $properties;
586 }
587 private function getLazyLoadedPublicProperties(ClassMetadata $class)
588 {
589 $defaultProperties = $class->getReflectionClass()->getDefaultProperties();
590 $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
591 $defaultValues = [];
592 foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
593 $name = $property->getName();
594 if (!in_array($name, $lazyLoadedPublicProperties, \true)) {
595 continue;
596 }
597 if (array_key_exists($name, $defaultProperties)) {
598 $defaultValues[$name] = $defaultProperties[$name];
599 } elseif (method_exists($property, 'getType')) {
600 $propertyType = $property->getType();
601 if ($propertyType !== null && $propertyType->allowsNull()) {
602 $defaultValues[$name] = null;
603 }
604 }
605 }
606 return $defaultValues;
607 }
608 private function buildParametersString(array $parameters, array $renameParameters = [])
609 {
610 $parameterDefinitions = [];
611 $i = -1;
612 foreach ($parameters as $param) {
613 assert($param instanceof ReflectionParameter);
614 $i++;
615 $parameterDefinition = '';
616 $parameterType = $this->getParameterType($param);
617 if ($parameterType !== null) {
618 $parameterDefinition .= $parameterType . ' ';
619 }
620 if ($param->isPassedByReference()) {
621 $parameterDefinition .= '&';
622 }
623 if ($param->isVariadic()) {
624 $parameterDefinition .= '...';
625 }
626 $parameterDefinition .= '$' . ($renameParameters ? $renameParameters[$i] : $param->getName());
627 $parameterDefinition .= $this->getParameterDefaultValue($param);
628 $parameterDefinitions[] = $parameterDefinition;
629 }
630 return implode(', ', $parameterDefinitions);
631 }
632 private function getParameterType(ReflectionParameter $parameter)
633 {
634 if (!$parameter->hasType()) {
635 return null;
636 }
637 $declaringFunction = $parameter->getDeclaringFunction();
638 assert($declaringFunction instanceof ReflectionMethod);
639 return $this->formatType($parameter->getType(), $declaringFunction, $parameter);
640 }
641 private function getParameterDefaultValue(ReflectionParameter $parameter)
642 {
643 if (!$parameter->isDefaultValueAvailable()) {
644 return '';
645 }
646 if (PHP_VERSION_ID < 80100 || is_scalar($parameter->getDefaultValue())) {
647 return ' = ' . var_export($parameter->getDefaultValue(), \true);
648 }
649 $value = rtrim(substr(explode('$' . $parameter->getName() . ' = ', (string) $parameter, 2)[1], 0, -2));
650 if (strpos($value, '\\') !== \false || strpos($value, '::') !== \false) {
651 $value = preg_split("/('(?:[^'\\\\]*+(?:\\\\.)*+)*+')/", $value, -1, PREG_SPLIT_DELIM_CAPTURE);
652 foreach ($value as $i => $part) {
653 if ($i % 2 === 0) {
654 $value[$i] = preg_replace('/(?<![a-zA-Z0-9_\\x7f-\\xff\\\\])[a-zA-Z0-9_\\x7f-\\xff]++(?:\\\\[a-zA-Z0-9_\\x7f-\\xff]++|::)++/', '\\\\\\0', $part);
655 }
656 }
657 $value = implode('', $value);
658 }
659 return ' = ' . $value;
660 }
661 private function getParameterNamesForInvoke(array $parameters)
662 {
663 return array_map(static function (ReflectionParameter $parameter) {
664 return '$' . $parameter->getName();
665 }, $parameters);
666 }
667 private function getParameterNamesForParentCall(array $parameters)
668 {
669 return array_map(static function (ReflectionParameter $parameter) {
670 $name = '';
671 if ($parameter->isVariadic()) {
672 $name .= '...';
673 }
674 $name .= '$' . $parameter->getName();
675 return $name;
676 }, $parameters);
677 }
678 private function getMethodReturnType(ReflectionMethod $method)
679 {
680 if (!$method->hasReturnType()) {
681 return '';
682 }
683 return ': ' . $this->formatType($method->getReturnType(), $method);
684 }
685 private function shouldProxiedMethodReturn(ReflectionMethod $method)
686 {
687 if (!$method->hasReturnType()) {
688 return \true;
689 }
690 return !in_array(strtolower($this->formatType($method->getReturnType(), $method)), ['void', 'never'], \true);
691 }
692 private function formatType(ReflectionType $type, ReflectionMethod $method, ?ReflectionParameter $parameter = null)
693 {
694 if ($type instanceof ReflectionUnionType) {
695 return implode('|', array_map(function (ReflectionType $unionedType) use($method, $parameter) {
696 if ($unionedType instanceof ReflectionIntersectionType) {
697 return '(' . $this->formatType($unionedType, $method, $parameter) . ')';
698 }
699 return $this->formatType($unionedType, $method, $parameter);
700 }, $type->getTypes()));
701 }
702 if ($type instanceof ReflectionIntersectionType) {
703 return implode('&', array_map(function (ReflectionType $intersectedType) use($method, $parameter) {
704 return $this->formatType($intersectedType, $method, $parameter);
705 }, $type->getTypes()));
706 }
707 assert($type instanceof ReflectionNamedType);
708 $name = $type->getName();
709 $nameLower = strtolower($name);
710 if ($nameLower === 'static') {
711 $name = 'static';
712 }
713 if ($nameLower === 'self') {
714 $name = $method->getDeclaringClass()->getName();
715 }
716 if ($nameLower === 'parent') {
717 $name = $method->getDeclaringClass()->getParentClass()->getName();
718 }
719 if (!$type->isBuiltin() && !class_exists($name) && !interface_exists($name) && $name !== 'static') {
720 if ($parameter !== null) {
721 throw UnexpectedValueException::invalidParameterTypeHint($method->getDeclaringClass()->getName(), $method->getName(), $parameter->getName());
722 }
723 throw UnexpectedValueException::invalidReturnTypeHint($method->getDeclaringClass()->getName(), $method->getName());
724 }
725 if (!$type->isBuiltin() && $name !== 'static') {
726 $name = '\\' . $name;
727 }
728 if ($type->allowsNull() && !in_array($name, ['mixed', 'null'], \true)) {
729 $name = '?' . $name;
730 }
731 return $name;
732 }
733 }
734