Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Cleaned up WeakRef support
Browse files Browse the repository at this point in the history
- Added API docs for CallbackHandler::isValid() and
  CallbackHandler::registerCallback()
- Cleaned up commented code in CallbackHandler
- Cleaned up API docs for CallbackHandler's validateStringCallback() and
  validateArrayCallback() methods
- Added comment in EventManager::triggerListeners() to note
  deregistration of invalid listeners
- Fixed the logic of CallbackHandler::registerCallback() to immediately
  register if WeakRef is not available
  • Loading branch information
weierophinney committed Sep 24, 2011
1 parent 5ac55ee commit 9bce1ba
Showing 1 changed file with 64 additions and 22 deletions.
86 changes: 64 additions & 22 deletions src/CallbackHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class CallbackHandler
* Constructor
*
* @param string $event Event to which slot is subscribed
* @param string|array|object $callback PHP callback (first element may be )
* @param string|array|object $callback PHP callback
* @param array $options Options used by the callback handler (e.g., priority)
* @return void
*/
Expand All @@ -77,31 +77,52 @@ public function __construct($event, $callback, array $options = array())
$this->registerCallback($callback);
}

/**
* Registers the callback provided in the constructor
*
* If you have pecl/weakref {@see http://pecl.php.net/weakref} installed,
* this method provides additional behavior.
*
* If a callback is a functor, or an array callback composing an object
* instance, this method will pass the object to a WeakRef instance prior
* to registering the callback. See {@link isValid()} for more information
* on how this affects execution.
*
* @param callback $callback
* @return void
*/
protected function registerCallback($callback)
{
if (is_object($callback) && !$callback instanceof Closure) {
if (class_exists('WeakRef', false)) {
$this->callback = new WeakRef($callback);
}
// If pecl/weakref is not installed, simply register it
if (!class_exists('WeakRef', false)) {
$this->callback = $callback;
return;
}

if (!is_array($callback)) {
$this->callback = $callback;
// If we have a non-closure object, pass it to WeakRef, and then
// register it.
if (is_object($callback) && !$callback instanceof Closure) {
$this->callback = new WeakRef($callback);
return;
}

if (!class_exists('WeakRef', false)) {
// If we have a string or closure, register as-is
if (!is_array($callback)) {
$this->callback = $callback;
return;
}

list($target, $method) = $callback;

// If we have an array callback, and the first argument is not an
// object, register as-is
if (!is_object($target)) {
$this->callback = $callback;
return;
}

// We have an array callback with an object as the first argument;
// pass it to WeakRef, and then register the new callback
$target = new WeakRef($target);
$this->callback = array($target, $method);
}
Expand All @@ -120,7 +141,7 @@ public function getEvent()
* Retrieve registered callback
*
* @return Callback
* @throws Exception\InvalidCallbackException
* @throws Exception\InvalidCallbackException If callback is invalid
*/
public function getCallback()
{
Expand Down Expand Up @@ -185,8 +206,36 @@ public function getOption($name)
return null;
}

/**
* Is the composed callback valid?
*
* Typically, this method simply checks to see if we have a valid callback.
* In a few situations, it does more.
*
* * If we have a string callback, we pass execution to
* {@link validateStringCallback()}.
* * If we have an object callback, we test to see if that object is a
* WeakRef {@see http://pecl.php.net/weakref}. If so, we return the value
* of its valid() method. Otherwise, we return the result of is_callable().
* * If we have a callback array with a string in the first position, we
* pass execution to {@link validateArrayCallback()}.
* * If we have a callback array with an object in the first position, we
* test to see if that object is a WeakRef (@see http://pecl.php.net/weakref).
* If so, we return the value of its valid() method. Otherwise, we return
* the result of is_callable() on the callback.
*
* WeakRef is used to allow listeners to go out of scope. This functionality
* is turn-key if you have pecl/weakref installed; otherwise, you will have
* to manually remove listeners before destroying an object referenced in a
* listener.
*
* @return bool
*/
public function isValid()
{
// If we've already tested this, we can move on. Note: if a callback
// composes a WeakRef, this will never get set, and thus result in
// validation on each call.
if ($this->isValidCallback) {
return $this->callback;
}
Expand All @@ -197,7 +246,7 @@ public function isValid()
return $this->validateStringCallback($callback);
}

if ($callback instanceof \WeakRef) {
if ($callback instanceof WeakRef) {
return $callback->valid();
}

Expand All @@ -211,7 +260,7 @@ public function isValid()
}

list($target, $method) = $callback;
if ($target instanceof \WeakRef) {
if ($target instanceof WeakRef) {
if (!$target->valid()) {
return false;
}
Expand All @@ -228,39 +277,35 @@ public function isValid()
* valid class name; if so, determine if the object is invokable.
*
* @param string $callback
* @return Callback
* @throws Exception\InvalidCallbackException
* @return bool
*/
protected function validateStringCallback($callback)
{
if (is_callable($callback)) {
$this->isValidCallback = true;
return $callback;
return true;
}

if (!class_exists($callback)) {
// throw new Exception\InvalidCallbackException('Provided callback is not a function or a class');
return false;
}

// check __invoke before instantiating
if (!method_exists($callback, '__invoke')) {
// throw new Exception\InvalidCallbackException('Class provided as a callback does not implement __invoke');
return false;
}
$object = new $callback();

$this->callback = $object;
$this->isValidCallback = true;
return $object;
return true;
}

/**
* Validate an array callback
*
* @param array $callback
* @return callback
* @throws Exception\InvalidCallbackException
* @return bool
*/
protected function validateArrayCallback(array $callback)
{
Expand All @@ -271,7 +316,6 @@ protected function validateArrayCallback(array $callback)
// Dealing with a class/method callback, and class provided is a string classname

if (!class_exists($context)) {
// throw new Exception\InvalidCallbackException('Class provided in callback does not exist');
return false;
}

Expand All @@ -280,7 +324,6 @@ protected function validateArrayCallback(array $callback)
if (!$r->hasMethod($method)) {
// Explicit method does not exist
if (!$r->hasMethod('__callStatic') && !$r->hasMethod('__call')) {
// throw new Exception\InvalidCallbackException('Class provided in callback does not define the method requested');
return false;
}

Expand Down Expand Up @@ -317,7 +360,6 @@ protected function validateArrayCallback(array $callback)
return $callback;
}

// throw new Exception\InvalidCallbackException('Method provided in callback does not exist in object');
return false;
}
}

0 comments on commit 9bce1ba

Please sign in to comment.