Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
lazy-loading-value-holder.md
001 ---
002 title: Lazy Loading Value Holder Proxy
003 ---
004
005 # Lazy Loading Value Holder Proxy
006
007 A lazy loading value holder proxy is a virtual proxy that wraps and lazily initializes a "real" instance of the proxied
008 class.
009
010 ## What is lazy loading?
011
012 In pseudo-code, in userland, [lazy loading](http://www.martinfowler.com/eaaCatalog/lazyLoad.html) looks like following:
013
014 ```php
015 class MyObjectProxy
016 {
017 private $wrapped;
018
019 public function doFoo()
020 {
021 $this->init();
022
023 return $this->wrapped->doFoo();
024 }
025
026 private function init()
027 {
028 if (null === $this->wrapped) {
029 $this->wrapped = new MyObject();
030 }
031 }
032 }
033 ```
034
035 This code is problematic, and adds a lot of complexity that makes your unit tests' code even worse.
036
037 Also, this kind of usage often ends up in coupling your code with a particular
038 [Dependency Injection Container](http://martinfowler.com/articles/injection.html)
039 or a framework that fetches dependencies for you.
040 That way, further complexity is introduced, and some problems related
041 with service location raise, as I've explained
042 [in this article](http://ocramius.github.com/blog/zf2-and-symfony-service-proxies-with-doctrine-proxies/).
043
044 Lazy loading value holders abstract this logic for you, hiding your complex, slow, performance-impacting objects behind
045 tiny wrappers that have their same API, and that get initialized at first usage.
046
047 ## When do I use a lazy value holder?
048
049 You usually need a lazy value holder in cases where following applies
050
051 * your object takes a lot of time and memory to be initialized (with all dependencies)
052 * your object is not always used, and the instantiation overhead can be avoided
053
054 ## Usage examples
055
056 [ProxyManager](https://github.com/Ocramius/ProxyManager) provides a factory that eases instantiation of lazy loading
057 value holders. To use it, follow these steps:
058
059 First of all, define your object's logic without taking care of lazy loading:
060
061 ```php
062 namespace MyApp;
063
064 class HeavyComplexObject
065 {
066 public function __construct()
067 {
068 // just write your business logic
069 // don't worry about how heavy initialization of this will be!
070 }
071
072 public function doFoo() {
073 echo 'OK!';
074 }
075 }
076 ```
077
078 Then use the proxy manager to create a lazy version of the object (as a proxy):
079
080 ```php
081 namespace MyApp;
082
083 use ProxyManager\Factory\LazyLoadingValueHolderFactory;
084 use ProxyManager\Proxy\LazyLoadingInterface;
085
086 require_once __DIR__ . '/vendor/autoload.php';
087
088 $factory = new LazyLoadingValueHolderFactory();
089 $initializer = function (& $wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
090 $initializer = null; // disable initialization
091 $wrappedObject = new HeavyComplexObject(); // fill your object with values here
092
093 return true; // confirm that initialization occurred correctly
094 };
095
096 $proxy = $factory->createProxy('MyApp\HeavyComplexObject', $initializer);
097 ```
098
099 You can now simply use your object as before:
100
101 ```php
102 // this will just work as before
103 $proxy->doFoo(); // OK!
104 ```
105
106 ## Lazy Initialization
107
108 As you can see, we use a closure to handle lazy initialization of the proxy instance at runtime.
109 The initializer closure signature should be as following:
110
111 ```php
112 /**
113 * @var object $wrappedObject the instance (passed by reference) of the wrapped object,
114 * set it to your real object
115 * @var object $proxy the instance proxy that is being initialized
116 * @var string $method the name of the method that triggered lazy initialization
117 * @var array $parameters an ordered list of parameters passed to the method that
118 * triggered initialization, indexed by parameter name
119 * @var Closure $initializer a reference to the property that is the initializer for the
120 * proxy. Set it to null to disable further initialization
121 *
122 * @return bool true on success
123 */
124 $initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {};
125 ```
126
127 The initializer closure should usually be coded like following:
128
129 ```php
130 $initializer = function (& $wrappedObject, $proxy, $method, array $parameters, & $initializer) {
131 $newlyCreatedObject = new Foo(); // instantiation logic
132 $newlyCreatedObject->setBar('baz') // instantiation logic
133 $newlyCreatedObject->setBat('bam') // instantiation logic
134
135 $wrappedObject = $newlyCreatedObject; // set wrapped object in the proxy
136 $initializer = null; // disable initializer
137
138 return true; // report success
139 };
140 ```
141
142 The
143 [`ProxyManager\Factory\LazyLoadingValueHolderFactory`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Factory/LazyLoadingValueHolderFactory.php)
144 produces proxies that implement both the
145 [`ProxyManager\Proxy\ValueHolderInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/ValueHolderInterface.php)
146 and the
147 [`ProxyManager\Proxy\LazyLoadingInterface`](https://github.com/Ocramius/ProxyManager/blob/master/src/ProxyManager/Proxy/LazyLoadingInterface.php).
148
149 At any point in time, you can set a new initializer for the proxy:
150
151 ```php
152 $proxy->setProxyInitializer($initializer);
153 ```
154
155 In your initializer, you currently **MUST** turn off any further initialization:
156
157 ```php
158 $proxy->setProxyInitializer(null);
159 ```
160
161 or
162
163 ```php
164 $initializer = null; // if you use the initializer by reference
165 ```
166
167 ## Triggering Initialization
168
169 A lazy loading proxy is initialized whenever you access any property or method of it.
170 Any of the following interactions would trigger lazy initialization:
171
172 ```php
173 // calling a method
174 $proxy->someMethod();
175
176 // reading a property
177 echo $proxy->someProperty;
178
179 // writing a property
180 $proxy->someProperty = 'foo';
181
182 // checking for existence of a property
183 isset($proxy->someProperty);
184
185 // removing a property
186 unset($proxy->someProperty);
187
188 // cloning the entire proxy
189 clone $proxy;
190
191 // serializing the proxy
192 $unserialized = serialize(unserialize($proxy));
193 ```
194
195 Remember to call `$proxy->setProxyInitializer(null);` to disable initialization of your proxy, or it will happen more
196 than once.
197
198 ## Proxying interfaces
199
200 You can also generate proxies from an interface FQCN. By proxying an interface, you will only be able to access the
201 methods defined by the interface itself, even if the `wrappedObject` implements more methods. This will anyway save
202 some memory since the proxy won't contain useless inherited properties.
203
204 ## Known limitations
205
206 * methods using `func_get_args()`, `func_get_arg()` and `func_num_arg()` will not function properly
207 for parameters that are not part of the proxied object interface: use
208 [variadic arguments](http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list)
209 instead.
210
211 ## Tuning performance for production
212
213 See [Tuning ProxyManager for Production](tuning-for-production.md).
214