Proxy OOP Pattern in PHP
-
Upload
marco-pivetta -
Category
Technology
-
view
4.972 -
download
3
description
Transcript of Proxy OOP Pattern in PHP
PROXY PATTERNIN PHP
Doctrine core teamZf2 contributorWasting time on:
OcraDiCompilerOcraServiceManagerOcraCachedViewResolverDoctrine ZF2 ModulesZeffMuAssetManagerKJSencha
Follow @OcramiusFollow @Ocramius
MARCO PIVETTA
Follow Follow @Ocramius@Ocramius
WHAT IS A PROXY?A proxy is generally an object whoseinterface is a layer between us and a
different object with the sameinterface.
Simplified, in PHP
Even if we're not implementing aninterface, the interface of
CustomerProxy is the same as theone of Customer
WHERE TO USE PROXIES?As of the , a proxy can be used inany place where the "proxied" object
can be used.
LSP
WHEN TO USE A PROXY?Lazy loadingRemote objectsSmart reference/Weak referenceProtectionAOPNull object fallback
LAZY LOADINGWe may want to use lazy loading to
avoid IO operations or heavy loadingoperation until really needed
LAZY LOADING PATTERNS1. Virtual Object2. Value Holder3. Ghost Object
VIRTUAL OBJECTAn object whose properties are all setto null, and where each access to the
properties is tracked.
Access triggers lazy loading on theproperty.
VALUE HOLDERAn object that can hold an instance of
the original proxied object, and loads itonly when needed.
GHOST OBJECTAn object whose properties are the
same of the proxied object, but null.
Accessing any method causes loading ofthe properties.
are thisway.
Doctrine Proxies generated
VALUE HOLDER EXAMPLE(1/3)
class Image { public function __construct($path) { $this->image = imagecreatefromjpeg($path); }
public function getSize() { return array(imagesx($this->image), imagesy($this->image)); }}
VALUE HOLDER EXAMPLE(2/3)
class ImageProxy extends Image { protected $image; public function __construct($path) { $this->path = $path; }
private function init() { if ( ! $this->image) { $this->image = new Image($this->path); } }
public function getSize() { $this->init(); return $this->image->getSize(); }}
VALUE HOLDER EXAMPLE(3/3)
$img1 = new ImageProxy('/path/to/image1.jpg');var_dump(memory_get_usage()); // ~200Kb$img2 = new ImageProxy('/path/to/image2.jpg');var_dump(memory_get_usage()); // ~200Kb$img3 = new ImageProxy('/path/to/image3.jpg');var_dump(memory_get_usage()); // ~200Kb
$size1 = $img1->getSize();var_dump(memory_get_usage()); // ~4Mb$size2 = $img2->getSize();var_dump(memory_get_usage()); // ~8Mb
LAZY LOADING PROS/CONSAdvantages
Low memory impactLow overheadEasy to implementUseful to determine object "dirty"status
Dis-advantagesNot optimal for data that is alwaysloadedLazy loading means lazy failing
REMOTE OBJECT
It basically is a specific form of lazyloading
REMOTE OBJECT EXAMPLE(1/3)
class Tweet { protected $data;
public function __construct(array $data) { $this->data = $data; }
public function getText() { return $this->data['text']; }}
REMOTE OBJECT EXAMPLE(2/3)
class TweetProxy extends Tweet { protected $api; protected $tweet; protected $id;
public function __construct(TwitterApi $api, $id) { $this->api = $api; $this->id = $id; } private function init() { if ( ! $this->tweet) { $this->tweet = new Tweet($this->api->get($this->id)); } } public function getText() { $this->init(); return $this->tweet->getText(); }}
REMOTE OBJECT EXAMPLE(3/3)
$tweet = new Tweet(array('text' => 'Proxies in PHP!'));var_dump($tweet->getText()); // 'Proxies in PHP!'
$api = new TwitterApi(/* yadda */); // zf, buzz, etc
$remoteTweet = new TweetProxy($api, 280643708968386560);var_dump($remoteTweet->getText()); // 'Tweet text!'$remoteTweet = new TweetProxy($api, 280643708968386561);var_dump($remoteTweet->getText()); // 'Another text!'
REMOTE OBJECT PROS/CONSAdvantages
Abstraction of a remote objectYou can re-define the proxiedobject's API locally
Dis-advantagesTo use such a proxy, you almostalways need a configured remoteclientRemote objects fail very easily
SMART REFERENCESmart reference can be used to:
swap the proxied object at runtimereference singletons or use internalstatic registriesOptimize memory usage
SMART REFERENCE WITHWEAKREF
Using to save memory onlong-running processesWeakref
class ImageProxy extends Image { // [...] (see previous example) private function init() { if ( ! $this->imageRef || ! $this->imageRef->valid()) { $this->imageRef = new WeakRef(new Image($this->path)); }
return $this->imageRef->get(); }
public function getSize() { return $this->init()->getSize(); }}
SMART REFERENCE WITH AREGISTRY
class ImageProxy extends Image { // [...] (see previous example) private function init() { if (null === $this->image) { $this->image = ImageRegistry::get($this->path); } }
public function getSize() { $this->init(); return $this->image->getSize(); }}
SMART REFERENCEPROS/CONS
Mainly memory usage, but it dependson how you setup your "smart"
reference
PROTECTION PROXYProtection proxy comes into play whenyou want to transparently limit access
to an API through a set of rules(ACL/limits)
PROTECTION PROXY (1/2)class RemoteApiProxy extends RemoteApi { protected $count = 0; public function __construct(RemoteApi $api, $limit) { $this->api = $api; $this->limit = $limit; }
private function count() { if (++$this->count > $this->limit) { throw new RemoteApiLimit('STAHP!'); } }
public function doStuff() { $this->count(); return $this->api->doStuff(); }}
PROTECTION PROXY (2/2)$api = new RemoteApiProxy(new RemoteApi(/* ... */), 50);
while (1) { $api->doStuff(); // RemoteApiLimit exception!}
PROTECTION PROXYPROS/CONS
AdvantagesTransparent filtering or limiting ofaccess to an object
Dis-advantagesModifies proxied object behavior!More like a decorator!
NULL OBJECT FALLBACKPROXY
A null object is an object thatimplements an interface, but produces
no side effects. It replaces null.
Using null objects allows us to workwith the assumption that an object will
always be available, reducing checksagainst null by a lot.
NULL OBJECT FALLBACKPROXY EXAMPLE
class CustomerProxy extends Customer { public function __construct(Db $db, $id) { $this->customer = $db->find($id);
if ( ! $this->customer) { $this->customer = new NullCustomer(); } }
public function doStuff() { return $this->customer->doStuff(); }}
NULL OBJECT FALLBACKPROXY PROS
Performance (with small # ofinstances)Reduced NPaths, therefore code iseasier to test
AOP AND PROXIESProxies basically enable us to have logic
between us and any object, makingAOP easy even when the language
doesn't allow it.
We can use code generation to createon-the-fly proxies with our custom AOP
logic executed pre- and post- anymethod of the proxied object.
AOP EXAMPLES
Becomes:
/** * @AOP\Cache(ttl=3600) */public function doHeavyStuff() { // [...]}
public function doHeavyStuff() { if($cached = $this->cache->get('doHeavyStuff', func_get_args())) { return $cached; }
$return = $this->originalObject->doHeavyStuff(); $this->cache->set($return, 'doHeavyStuff', func_get_args());
return $return;}
IMPLEMENTATION DETAILS INPHP
IMPLEMENTING THE PUBLICAPI
1. The Proxy class MUST extend theproxied class
2. Each of the proxied methods must berewritten
3. Proxies should be serializable4. Proxies should handle public
properties
PUBLIC PROPERTIESPROXYING
class Customer { public $name; public $surname;}
class CustomerProxy extends Customer { public function __construct(Customer $customer) { unset($this->name, $this->surname); $this->customer = $customer; }
public function __set($name, $value) { $this->customer->$name = $value; } public function __get($name) { return $this->customer->$name; } // __isset, __unset}
A COMPLETE GHOST OBJECTIMPLEMENTATION
https://gist.github.com/4038004
SOME USEFUL LIBRARIEShttp://flow.typo3.orghttps://github.com/schmittjoh/cg-libraryhttps://github.com/doctrine/commonhttps://github.com/lisachenko/go-aop-php
CODE GENERATIONSee Doctrine's Proxy Generator
QUESTIONS?