Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit00e2a9f

Browse files
[VarExporter] AddLazyGhostTrait andLazyProxyTrait documentation
1 parent35ad405 commit00e2a9f

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

‎components/var_exporter.rst‎

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,186 @@ created by using the special ``"\0"`` property name to define their internal val
128128
"\0" => [$inputArray],
129129
]);
130130

131+
Creating Lazy Objects
132+
---------------------
133+
134+
Lazy-objects are objects instantiated empty and populated on-demand. This is
135+
particularly useful when you have for example properties in your classes that
136+
requires some heavy computation to determine their value. In this case, you
137+
may want to trigger the property's value processing only when you actually need
138+
its value. Thanks to this, the heavy computation won't be done if you never use
139+
this property. The VarExporter component is bundled with two traits helping
140+
you implement such mechanism easily in your classes.
141+
142+
.. _var-exporter_ghost-objects:
143+
144+
LazyGhostTrait
145+
~~~~~~~~~~~~~~
146+
147+
Ghost objects are empty objects, which see their properties populated the first
148+
time any method is called. Thanks to:class:`Symfony\\Component\\VarExporter\\LazyGhostTrait`,
149+
the implementation of the lazy mechanism is eased. In the following example, we are
150+
defining the ``$hash`` property as lazy. We also declare that the ``MyLazyObject::computeHash()``
151+
method should be called only when ``$hash``'s value need to be known::
152+
153+
namespace App\Hash;
154+
155+
use Symfony\Component\VarExporter\LazyGhostTrait;
156+
157+
class HashProcessor
158+
{
159+
use LazyGhostTrait;
160+
// Because of how the LazyGhostTrait trait works internally, you
161+
// must add this private property in your class
162+
private int $lazyObjectId;
163+
164+
// This property may require a heavy computation to have its value
165+
public readonly string $hash;
166+
167+
public function __construct()
168+
{
169+
self::createLazyGhost(initializer: [
170+
'hash' => $this->computeHash(...),
171+
], instance: $this);
172+
}
173+
174+
private function computeHash(array $data): string
175+
{
176+
// Compute $this->hash value with the passed data
177+
}
178+
}
179+
180+
:class:`Symfony\\Component\\VarExporter\\LazyGhostTrait` also allows to
181+
convert non-lazy classes to lazy ones::
182+
183+
namespace App\Hash;
184+
185+
use Symfony\Component\VarExporter\LazyGhostTrait;
186+
187+
class HashProcessor
188+
{
189+
public readonly string $hash;
190+
191+
public function __construct(array $data)
192+
{
193+
$this->hash = $this->computeHash($data);
194+
}
195+
196+
private function computeHash(array $data): string
197+
{
198+
// ...
199+
}
200+
201+
public function validateHash(): bool
202+
{
203+
// ...
204+
}
205+
}
206+
207+
class LazyHashProcessor extends HashProcessor
208+
{
209+
use LazyGhostTrait;
210+
}
211+
212+
$processor = LazyHashProcessor::createLazyGhost(initializer: function (HashProcessor $instance): void {
213+
// Do any operation you need here: call setters, getters, methods to validate the hash, etc.
214+
$data = /** Retrieve required data to compute the hash */;
215+
$instance->__construct(...$data);
216+
$instance->validateHash();
217+
});
218+
219+
While you never query ``$processor->hash`` value, heavy methods will never be triggered.
220+
But still, the ``$processor`` object exists and can be used in your code, passed to
221+
methods, functions, etc.
222+
223+
Additionally and by adding two arguments to initializer function, it is possible to initialize
224+
properties one-by-one::
225+
226+
$processor = LazyHashProcessor::createLazyGhost(initializer: function (HashProcessor $instance, string $propertyName, ?string $propertyScope): mixed {
227+
if (HashProcessor::class === $propertyScope && 'hash' === $propertyName) {
228+
// Return $hash value
229+
}
230+
231+
// Then you can add more logic for the other properties
232+
});
233+
234+
Ghost objects unfortunately can't work with abstract classes but also internal PHP classes.
235+
Nevertheless, the VarExporter component covers this need with the help of to
236+
:ref:`Virtual Proxies<var-exporter_virtual-proxies>`.
237+
238+
..versionadded::6.2
239+
240+
The:class:`Symfony\\Component\\VarExporter\\LazyGhostTrait` was introduced in Symfony 6.2.
241+
242+
.. _var-exporter_virtual-proxies:
243+
244+
LazyProxyTrait
245+
~~~~~~~~~~~~~~
246+
247+
The purpose of virtual proxies in the same one as
248+
:ref:`ghost objects<var-exporter_ghost-objects>`, but their internal behavior is
249+
totally different. Where ghost objects requires to extend a base class, virtual
250+
proxies take advantage of the **Liskov Substitution principle**. This principle
251+
describes that if two objects are implementing the same interface, you can swap between
252+
the different implementations without breaking your application. This is what virtual
253+
proxies take advantage of. To use virtual proxies, you may use
254+
:class:`Symfony\\Component\\VarExporter\\ProxyHelper` to generate proxy's class
255+
code::
256+
257+
namespace App\Hash;
258+
259+
use Symfony\Component\VarExporter\ProxyHelper;
260+
261+
interface ProcessorInterface
262+
{
263+
public function getHash(): bool;
264+
}
265+
266+
abstract class AbstractProcessor implements ProcessorInterface
267+
{
268+
protected string $hash;
269+
270+
public function getHash(): bool
271+
{
272+
return $this->hash;
273+
}
274+
}
275+
276+
class HashProcessor extends AbstractProcessor
277+
{
278+
public function __construct(array $data)
279+
{
280+
$this->hash = $this->computeHash($data);
281+
}
282+
283+
private function computeHash(array $data): string
284+
{
285+
// ...
286+
}
287+
}
288+
289+
$proxyCode = ProxyHelper::generateLazyProxy(new \ReflectionClass(AbstractProcessor::class));
290+
// $proxyCode contains the actual proxy and the reference to LazyProxyTrait.
291+
// In production env, this should be dumped into a file to avoid calling eval().
292+
eval('class HashProcessorProxy'.$proxyCode);
293+
294+
$processor = HashProcessorProxy::createLazyProxy(initializer: function (): ProcessorInterface {
295+
$data = /** Retrieve required data to compute the hash */;
296+
$instance = new HashProcessor(...$data);
297+
298+
// Do any operation you need here: call setters, getters, methods to validate the hash, etc.
299+
300+
return $instance;
301+
});
302+
303+
Just like ghost objects, while you never query ``$processor->hash``, its value will not be computed.
304+
The main difference with ghost objects is that this time, we created a proxy of an abstract class.
305+
This also works with internal PHP class.
306+
307+
..versionadded::6.2
308+
309+
The:class:`Symfony\\Component\\VarExporter\\LazyProxyTrait` and
310+
:class:`Symfony\\Component\\VarExporter\\ProxyHelper` were introduced in Symfony 6.2.
311+
131312
.. _`OPcache`:https://www.php.net/opcache
132313
.. _`PSR-2`:https://www.php-fig.org/psr/psr-2/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp