proxy oop pattern in php

Post on 29-Nov-2014

4.971 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

An introduction of the Proxy Pattern for PHP Developers. Covers topics such as Lazy loading, Weak References, Smart References, Protection and Remote proxies.

TRANSCRIPT

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

QUESTIONS?

top related