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

Commit

Permalink
Merge branch 'master' of git://github.com/zendframework/zf2
Browse files Browse the repository at this point in the history
  • Loading branch information
Show file tree
Hide file tree
Showing 20 changed files with 831 additions and 56 deletions.
167 changes: 144 additions & 23 deletions src/CallbackHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
*/
namespace Zend\Stdlib;

use Closure,
WeakRef;

/**
* CallbackHandler
*
Expand Down Expand Up @@ -63,15 +66,65 @@ 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
*/
public function __construct($event, $callback, array $options = array())
{
$this->event = $event;
$this->callback = $callback;
$this->options = $options;
$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 pecl/weakref is not installed, simply register it
if (!class_exists('WeakRef', false)) {
$this->callback = $callback;
return;
}

// 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 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 @@ -88,26 +141,33 @@ public function getEvent()
* Retrieve registered callback
*
* @return Callback
* @throws Exception\InvalidCallbackException
* @throws Exception\InvalidCallbackException If callback is invalid
*/
public function getCallback()
{
if ($this->isValidCallback) {
return $this->callback;
if (!$this->isValid()) {
throw new Exception\InvalidCallbackException('Invalid callback provided; not callable');
}

$callback = $this->callback;
if (is_string($callback)) {
return $this->validateStringCallback($callback);
return $callback;
}
if (is_array($callback)) {
return $this->validateArrayCallback($callback);

if ($callback instanceof WeakRef) {
return $callback->get();
}
if (is_callable($callback)) {
$this->isValidCallback = true;

if (is_object($callback)) {
return $callback;
}
throw new Exception\InvalidCallbackException('Invalid callback provided; not callable');

list($target, $method) = $callback;
if ($target instanceof WeakRef) {
return array($target->get(), $method);
}

return $callback;
}

/**
Expand Down Expand Up @@ -146,44 +206,106 @@ 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;
}

$callback = $this->callback;

if (is_string($callback)) {
return $this->validateStringCallback($callback);
}

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

if (is_object($callback) && is_callable($callback)) {
$this->isValidCallback = true;
return true;
}

if (!is_array($callback)) {
return false;
}

list($target, $method) = $callback;
if ($target instanceof WeakRef) {
if (!$target->valid()) {
return false;
}
$target = $target->get();
return is_callable(array($target, $method));
}
return $this->validateArrayCallback($callback);
}

/**
* Validate a string callback
*
* Check first if the string provided is callable. If not see if it is a
* 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 @@ -194,15 +316,15 @@ 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;
}

// We need to determine if we need to instantiate the class first
$r = new \ReflectionClass($context);
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;
}

if ($r->hasMethod('__callStatic')) {
Expand Down Expand Up @@ -238,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;
}
}
7 changes: 7 additions & 0 deletions src/Dispatchable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
namespace Zend\Stdlib;

interface Dispatchable
{
public function dispatch(RequestDescription $request, ResponseDescription $response = null);
}
33 changes: 2 additions & 31 deletions src/Exception.php
Original file line number Diff line number Diff line change
@@ -1,36 +1,7 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Stdlib
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/**
* @namespace
*/
namespace Zend\Stdlib;
namespace Zend\Stdlib;

/**
* Marker interface for exceptions
*
* @category Zend
* @package Zend_Stdlib
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
interface Exception
interface Exception
{
}
9 changes: 9 additions & 0 deletions src/Exception/DomainException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Zend\Stdlib\Exception;

use Zend\Stdlib\Exception;

class DomainException extends \DomainException implements Exception
{
}
11 changes: 11 additions & 0 deletions src/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Zend\Stdlib\Exception;

use Zend\Stdlib\Exception;

class InvalidArgumentException
extends \InvalidArgumentException
implements Exception
{
}
3 changes: 1 addition & 2 deletions src/Exception/InvalidCallbackException.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
*/
namespace Zend\Stdlib\Exception;

use Zend\Stdlib\Exception,
DomainException;
use Zend\Stdlib\Exception;

/**
* Invalid callback exception
Expand Down
26 changes: 26 additions & 0 deletions src/IsAssocArray.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Zend\Stdlib;

/**
* Simple class for testing whether a value is an associative array
*
* Declared abstract, as we have no need for instantiation.
*/
abstract class IsAssocArray
{
/**
* Test whether a value is an associative array
*
* We have an associative array if at least one key is a string.
*
* @param mixed $value
* @return bool
*/
public static function test($value)
{
return (is_array($value)
&& count(array_filter(array_keys($value), 'is_string')) > 0
);
}
}
Loading

0 comments on commit 4f413a5

Please sign in to comment.