315 lines
11 KiB
HTML
315 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html class="no-js" id="top">
|
|
<head>
|
|
<title>ProxyManager - Ghost object</title>
|
|
|
|
<meta name="description" content="A proxyManager write in php" />
|
|
<meta name="keywords" content="ProxyManager, proxy, manager, ocramius, Marco Pivetta, php, ghost object" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
|
|
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600' rel='stylesheet' type='text/css'>
|
|
<link href="css/styles.css" rel="stylesheet" />
|
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/default.min.css">
|
|
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/highlight.min.js"></script>
|
|
<script>hljs.initHighlightingOnLoad();</script>
|
|
<link rel="shortcut icon" href="favicon.ico">
|
|
</head>
|
|
<body>
|
|
|
|
<header class="site-header">
|
|
<div class="container">
|
|
<h1><a href="index.html"><img alt="ProxyManager" src="img/block.png" /></a></h1>
|
|
|
|
<nav class="main-nav" role="navigation">
|
|
<ul>
|
|
<li><a href="https://github.com/Ocramius/ProxyManager" target="_blank">Github</a>
|
|
<div class="bcms-clearfix"></div>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
<main role="main">
|
|
<section class="component-content">
|
|
|
|
<div class="component-demo" id="live-demo">
|
|
<div class="container">
|
|
<div class="main-wrapper" style="text-align: right">
|
|
<iframe src="http://ghbtns.com/github-btn.html?user=ocramius&repo=ProxyManager&type=fork&count=true&size=large"
|
|
allowtransparency="true" frameborder="0" scrolling="0" width="310" height="40"></iframe>
|
|
|
|
<iframe src="http://ghbtns.com/github-btn.html?user=ocramius&repo=ProxyManager&type=watch&count=true&size=large"
|
|
allowtransparency="true" frameborder="0" scrolling="0" width="200" height="40"></iframe>
|
|
|
|
</div>
|
|
<div class="bcms-clearfix bcms-clearfix"></div>
|
|
</div>
|
|
</div>
|
|
<div class="component-info">
|
|
<div class="container">
|
|
<aside class="sidebar">
|
|
<nav class="spy-nav">
|
|
<ul>
|
|
<li><a href="index.html">Intro</a></li>
|
|
<li><a href="virtual-proxy.html">Virtual Proxy</a></li>
|
|
<li><a href="null-object.html">Null Objects</a></li>
|
|
<li><a href="ghost-object.html">Ghost Objects</a></li>
|
|
<li><a href="remote-object.html">Remote Object</a></li>
|
|
<li><a href="contributing.html">Contributing</a></li>
|
|
<li><a href="credits.html">Credits</a></li>
|
|
<li><a href="copyright.html">Copyright</a></li>
|
|
</ul>
|
|
</nav>
|
|
<div class="bcms-clearfix bcms-clearfix"></div>
|
|
<a class="btn btn-action btn-full download-component"
|
|
href="download.html">Download</a>
|
|
<div class="bcms-clearfix"></div>
|
|
</aside>
|
|
|
|
<div class="content">
|
|
<div class="bcms-clearfix"></div>
|
|
<h3 class="section-title">Lazy Loading Ghost Object Proxies</h3>
|
|
|
|
<p>A lazy loading ghost object proxy is a ghost proxy that looks exactly like the real instance of the proxied subject, but which has all properties nulled before initialization.</p>
|
|
<hr />
|
|
|
|
<h3 class="section-title">Lazy loading with the Ghost Object</h3>
|
|
|
|
<p>In pseudo-code, in userland, <a href="http://www.martinfowler.com/eaaCatalog/lazyLoad.html" target="_blank">lazy loading</a> in a ghost object looks like following:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
class MyObjectProxy
|
|
{
|
|
private $initialized = false;
|
|
private $name;
|
|
private $surname;
|
|
|
|
public function doFoo()
|
|
{
|
|
$this->init();
|
|
|
|
// Perform doFoo routine using loaded variables
|
|
}
|
|
|
|
private function init()
|
|
{
|
|
if (! $this->initialized) {
|
|
$data = some_logic_that_loads_data();
|
|
|
|
$this->name = $data['name'];
|
|
$this->surname = $data['surname'];
|
|
|
|
$this->initialized = true;
|
|
}
|
|
}
|
|
}
|
|
</code>
|
|
</pre>
|
|
|
|
<p>Ghost objects work similarly to virtual proxies, but since they don't wrap around a "real" instance of the proxied subject, they are better suited for representing dataset rows.</p>
|
|
|
|
<hr />
|
|
|
|
<h3 class="section-title">When do I use a ghost object?</h3>
|
|
|
|
<p>You usually need a ghost object in cases where following applies</p>
|
|
|
|
<ul>
|
|
<li>you are building a small data-mapper and want to lazily load data across associations in your object graph</li>
|
|
<li>you want to initialize objects representing rows in a large dataset</li>
|
|
<li>you want to compare instances of lazily initialized objects without the risk of comparing a proxy with a real subject</li>
|
|
<li>you are aware of the internal state of the object and are confident in working with its internals via reflection or direct property access</li>
|
|
</ul>
|
|
|
|
<hr />
|
|
|
|
<h3 class="section-title">Usage examples</h3>
|
|
|
|
<p><a href="https://github.com/Ocramius/ProxyManager" target="_blank">ProxyManager</a> provides a factory that creates lazy loading ghost objects. To use it, follow these steps:</p>
|
|
|
|
<p>First of all, define your object's logic without taking care of lazy loading:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
namespace MyApp;
|
|
|
|
class Customer
|
|
{
|
|
private $name;
|
|
private $surname;
|
|
|
|
// just write your business logic or generally logic
|
|
// don't worry about how complex this object will be!
|
|
// don't code lazy-loading oriented optimizations in here!
|
|
public function getName() { return $this->name; }
|
|
public function setName($name) { $this->name = (string) $name; }
|
|
public function getSurname() { return $this->surname; }
|
|
public function setSurname($surname) { $this->surname = (string) $surname; }
|
|
}
|
|
</code>
|
|
</pre>
|
|
|
|
<p>Then use the proxy manager to create a ghost object of it. You will be responsible of setting its state during lazy loading:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
namespace MyApp;
|
|
|
|
use ProxyManager\Factory\LazyLoadingGhostFactory;
|
|
use ProxyManager\Proxy\LazyLoadingInterface;
|
|
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
|
|
$factory = new LazyLoadingGhostFactory();
|
|
$initializer = function (LazyLoadingInterface $proxy, $method, array $parameters, & $initializer) {
|
|
$initializer = null; // disable initialization
|
|
|
|
// load data and modify the object here
|
|
$proxy->setName('Agent');
|
|
$proxy->setSurname('Smith');
|
|
|
|
return true; // confirm that initialization occurred correctly
|
|
};
|
|
|
|
$instance = $factory->createProxy('MyApp\Customer', $initializer);
|
|
</code>
|
|
</pre>
|
|
|
|
<p>You can now simply use your object as before:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
// this will just work as before
|
|
echo $proxy->getName() . ' ' . $proxy->getSurname(); // Agent Smith
|
|
</code>
|
|
</pre>
|
|
<hr />
|
|
|
|
<h3 class="section-title">Lazy Initialization</h3>
|
|
|
|
<p>As you can see, we use a closure to handle lazy initialization of the proxy instance at runtime. The initializer closure signature for ghost objects should be as following:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
/**
|
|
* @var object $proxy the instance the ghost object proxy that is being initialized
|
|
* @var string $method the name of the method that triggered lazy initialization
|
|
* @var array $parameters an ordered list of parameters passed to the method that
|
|
* triggered initialization, indexed by parameter name
|
|
* @var Closure $initializer a reference to the property that is the initializer for the
|
|
* proxy. Set it to null to disable further initialization
|
|
*
|
|
* @return bool true on success
|
|
*/
|
|
$initializer = function ($proxy, $method, $parameters, & $initializer) {};
|
|
</code>
|
|
</pre>
|
|
|
|
<p>The initializer closure should usually be coded like following:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
$initializer = function ($proxy, $method, $parameters, & $initializer) {
|
|
$initializer = null; // disable initializer for this proxy instance
|
|
|
|
// modify the object with loaded data
|
|
$proxy->setFoo(/* ... */);
|
|
$proxy->setBar(/* ... */);
|
|
|
|
return true; // report success
|
|
};
|
|
</code>
|
|
</pre>
|
|
|
|
<p>The <code>ProxyManager\Factory\LazyLoadingGhostFactory</code> produces proxies that implement both the <code>ProxyManager\Proxy\GhostObjectInterface</code> and the <code>ProxyManager\Proxy\LazyLoadingInterface</code>.</p>
|
|
|
|
<p>At any point in time, you can set a new initializer for the proxy:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
$proxy->setProxyInitializer($initializer);
|
|
</code>
|
|
</pre>
|
|
|
|
<p>In your initializer, you <strong>MUST</strong> turn off any further initialization:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
$proxy->setProxyInitializer(null);
|
|
</code>
|
|
</pre>
|
|
|
|
<p>or</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
$initializer = null; // if you use the initializer passed by reference to the closure
|
|
</code>
|
|
</pre>
|
|
<hr />
|
|
|
|
<h3 class="section-title">Triggering Initialization</h3>
|
|
|
|
<p>A lazy loading ghost object is initialized whenever you access any property or method of it. Any of the following interactions would trigger lazy initialization:</p>
|
|
|
|
<pre>
|
|
<code class="php">
|
|
// calling a method
|
|
$proxy->someMethod();
|
|
|
|
// reading a property
|
|
echo $proxy->someProperty;
|
|
|
|
// writing a property
|
|
$proxy->someProperty = 'foo';
|
|
|
|
// checking for existence of a property
|
|
isset($proxy->someProperty);
|
|
|
|
// removing a property
|
|
unset($proxy->someProperty);
|
|
|
|
// cloning the entire proxy
|
|
clone $proxy;
|
|
|
|
// serializing the proxy
|
|
$unserialized = unserialize(serialize($proxy));
|
|
</code>
|
|
</pre>
|
|
|
|
<p>Remember to call <code>$proxy->setProxyInitializer(null);</code> to disable initialization of your proxy, or it will happen more than once.</p>
|
|
|
|
<hr />
|
|
|
|
<h3 class="section-title">Proxying interfaces</h3>
|
|
|
|
<p>You can also generate proxies from an interface FQCN. By proxying an interface, you will only be able to access the methods defined by the interface itself, even if the <code>wrappedObject</code> implements more methods. This will anyway save some memory since the proxy won't contain any properties.</p>
|
|
|
|
<p>Tuning performance for production</p>
|
|
|
|
<p>See <a href="production.html">Tuning ProxyManager for Production.</a></p>
|
|
|
|
</main>
|
|
|
|
<footer class="site-footer" role="contentinfo">
|
|
<div class="container">
|
|
<div class="footer-logos">
|
|
<ul>
|
|
<li><a href="index.html">Intro</a> | </li>
|
|
<li><a href="virtual-proxy.html">Virtual Proxy</a> | </li>
|
|
<li><a href="null-object.html">Null Objects</a> | </li>
|
|
<li><a href="ghost-object.html">Ghost Objects</a> | </li>
|
|
<li><a href="remote-object.html">Remote Object</a> | </li>
|
|
<li><a href="contributing.html">Contributing</a> | </li>
|
|
<li><a href="credits.html">Credits</a> | </li>
|
|
<li><a href="copyright.html">Copyright</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bcms-clearfix"></div>
|
|
</footer>
|
|
<div class="bcms-clearfix"></div>
|
|
</body>
|
|
</html>
|