Skip to content

Instantly share code, notes, and snippets.

@thinkingmedia
Last active January 13, 2017 13:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkingmedia/84e2ae541785e633bdde2822d5edbc89 to your computer and use it in GitHub Desktop.
Save thinkingmedia/84e2ae541785e633bdde2822d5edbc89 to your computer and use it in GitHub Desktop.
Disposable pattern implemented in PHP
<?php
/**
* Wraps the callable in a try/finally before calling dispose()
*
* @param GemsDisposable $obj
* @param callable $worker
* @return mixed
*/
function using(GemsDisposable $obj, callable $worker)
{
try {
return $worker($obj);
} finally {
if($obj !== null) {
$obj->dispose();
unset($obj);
}
}
}
/**
* Calls dispose if the object is GemsDisposable
*
* @param mixed $ref
* @param string|null $property
* @throws Exception
*/
function dispose($ref, $property = null)
{
if ($property === null) {
if ($ref instanceof GemsDisposable) {
$ref->dispose();
}
return;
}
if (!isset($ref->$property)) {
throw new GemsException(sprintf("dispose(%s,'%s') called, but property does not exist on target object.", get_class($ref), $property));
}
if ($ref->$property instanceof GemsDisposable) {
$ref->$property->dispose();
}
unset($ref->$property);
}
/**
* Inspired by IDisposable on .NET
*/
interface GemsDisposable
{
/**
* Call dispose() on child properties.
* Call unset() on object properties to reduce memory leaks.
*/
public function dispose();
}
/**
* Class GemsDisposeTrait
*
* @mixin GemsDisposable
*/
trait GemsDisposeTrait
{
/**
* @var bool Set to false to disable disposing of array properties.
*/
private $dispose_arrays = true;
/**
* Disposes of all public, protected and private properties.
*/
public function dispose()
{
$reflector = new \ReflectionClass($this);
foreach ($reflector->getProperties() as $property) {
$name = $property->getName();
$property->setAccessible(true);
$value = $property->getValue($this);
if ($value instanceof GemsDisposable) {
if ($property->isStatic()) {
throw new GemsException("Found a static property that implements GemsDisposable. This usage is not supported.");
}
if (!$property->isPublic()) {
throw new GemsException("Can not auto-dispose of non-public GemsDisposable property: {$reflector->name}::{$name} is {$property->class}");
}
}
if (is_array($value) && $this->dispose_arrays === true) {
array_walk_recursive($value, function($item) {
dispose($item);
});
}
if (is_object($value) && $property->isPublic()) {
dispose($this, $name);
}
}
// remove models added by GemsModelsTrait
if(method_exists($this, 'disposeModels'))
{
$this->disposeModels();
}
unset($reflector);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment