From 7b3f37f6f7c74050bcf238742b77380201e226bb Mon Sep 17 00:00:00 2001 From: Ralph Schindler Date: Tue, 26 Jul 2011 01:48:15 -0500 Subject: [PATCH 1/4] Request and Response refactoring inside Zend\Stdlib, Zend\Http, and Zend\Mvc --- src/Dispatchable.php | 2 +- src/Message.php | 19 +++++++ src/MessageDescription.php | 3 ++ src/Parameters.php | 95 +++++++++++++++++++++++++++-------- src/ParametersDescription.php | 30 +++++++++++ src/Request.php | 19 +------ src/RequestDescription.php | 2 - src/Response.php | 24 +-------- src/ResponseDescription.php | 4 -- 9 files changed, 130 insertions(+), 68 deletions(-) create mode 100644 src/ParametersDescription.php diff --git a/src/Dispatchable.php b/src/Dispatchable.php index f492b9a99..39be7623e 100644 --- a/src/Dispatchable.php +++ b/src/Dispatchable.php @@ -3,5 +3,5 @@ interface Dispatchable { - public function dispatch(Request $request, Response $response = null); + public function dispatch(RequestDescription $request, ResponseDescription $response = null); } diff --git a/src/Message.php b/src/Message.php index 79cd75989..523813b81 100644 --- a/src/Message.php +++ b/src/Message.php @@ -82,4 +82,23 @@ public function getContent() { return $this->content; } + + public function __toString() + { + $request = ''; + foreach ($this->getMetadata() as $key => $value) { + $request .= sprintf( + "%s: %s\r\n", + (string) $key, + (string) $value + ); + } + $request .= "\r\n" . $this->getContent(); + + } + + public function fromString($string) + { + throw new Exception\DomainException('Unimplemented: ' . __METHOD__); + } } diff --git a/src/MessageDescription.php b/src/MessageDescription.php index 7fee04311..89791b2b2 100644 --- a/src/MessageDescription.php +++ b/src/MessageDescription.php @@ -9,4 +9,7 @@ public function getMetadata($key = null); public function setContent($content); public function getContent(); + + public function __toString(); + public function fromString($string); } diff --git a/src/Parameters.php b/src/Parameters.php index 6bb722ce7..4356aae88 100644 --- a/src/Parameters.php +++ b/src/Parameters.php @@ -2,29 +2,84 @@ namespace Zend\Stdlib; -use ArrayAccess, - Countable, - Serializable, - Traversable; - -/* - * Basically, an ArrayObject. You could simply define something like: - * class QueryParams extends ArrayObject implements Parameters {} - * and have 90% of the functionality - */ -interface Parameters extends ArrayAccess, Countable, Serializable, Traversable +use ArrayObject; + +class Parameters extends ArrayObject implements ParametersDescription { - public function __construct(array $values = null); + /** + * Constructor + * + * Enforces that we have an array, and enforces parameter access to array + * elements. + * + * @param array $values + * @return void + */ + public function __construct(array $values = null) + { + if (null === $values) { + $values = array(); + } + parent::__construct($values, ArrayObject::ARRAY_AS_PROPS); + } + + /** + * Populate from native PHP array + * + * @param array $values + * @return void + */ + public function fromArray(array $values) + { + $this->exchangeArray($values); + } - /* Allow deserialization from standard array */ - public function fromArray(array $values); + /** + * Populate from query string + * + * @param string $string + * @return void + */ + public function fromString($string) + { + $array = array(); + parse_str($string, $array); + $this->fromArray($array); + } - /* Allow deserialization from raw body; e.g., for PUT requests */ - public function fromString($string); + /** + * Serialize to native PHP array + * + * @return array + */ + public function toArray() + { + return $this->getArrayCopy(); + } - /* Allow serialization back to standard array */ - public function toArray(); + /** + * Serialize to query string + * + * @return string + */ + public function toString() + { + return http_build_query($this); + } - /* Allow serialization to query format; e.g., for PUT or POST requests */ - public function toString(); + /** + * Retrieve by key + * + * Returns null if the key does not exist. + * + * @param string $name + * @return mixed + */ + public function offsetGet($name) + { + if (isset($this[$name])) { + return parent::offsetGet($name); + } + return null; + } } diff --git a/src/ParametersDescription.php b/src/ParametersDescription.php new file mode 100644 index 000000000..72d2b0c95 --- /dev/null +++ b/src/ParametersDescription.php @@ -0,0 +1,30 @@ +getMetadata() as $key => $value) { - $request .= sprintf( - "%s: %s\r\n", - (string) $key, - (string) $value - ); - } - $request .= "\r\n" . $this->getContent(); - - } - - public function fromString($string) - { - throw new Exception\DomainException('Unimplemented: ' . __METHOD__); - } + // generic request implementation } diff --git a/src/RequestDescription.php b/src/RequestDescription.php index 535ff4488..0bbdc68b3 100644 --- a/src/RequestDescription.php +++ b/src/RequestDescription.php @@ -4,6 +4,4 @@ interface RequestDescription extends MessageDescription { - public function __toString(); - public function fromString($string); } diff --git a/src/Response.php b/src/Response.php index 7e0a85999..002fedb0b 100644 --- a/src/Response.php +++ b/src/Response.php @@ -4,27 +4,5 @@ class Response extends Message implements ResponseDescription { - public function send() - { - echo $this->getContent(); - } - - public function __toString() - { - $request = ''; - foreach ($this->getMetadata() as $key => $value) { - $request .= sprintf( - "%s: %s\r\n", - (string) $key, - (string) $value - ); - } - $request .= "\r\n" . $this->getContent(); - - } - - public function fromString($string) - { - throw new Exception\DomainException('Unimplemented: ' . __METHOD__); - } + // generic response implementation } diff --git a/src/ResponseDescription.php b/src/ResponseDescription.php index ce6a0593c..eefc723a6 100644 --- a/src/ResponseDescription.php +++ b/src/ResponseDescription.php @@ -4,9 +4,5 @@ interface ResponseDescription extends MessageDescription { - public function __toString(); - public function fromString($string); - // send? or emit? - public function send(); } From 126ccb26dae169691707ceade13e8a71eaee8755 Mon Sep 17 00:00:00 2001 From: Ralph Schindler Date: Mon, 8 Aug 2011 09:02:29 -0500 Subject: [PATCH 2/4] Various changes to Zend\Http * Addition of concrete header objects * Altered Headers, RequestHeaders and ResponseHeaders to handle concrete header objects of specific types --- src/Message.php | 15 +++++++++------ src/MessageDescription.php | 4 +--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Message.php b/src/Message.php index 871782581..077c498f8 100644 --- a/src/Message.php +++ b/src/Message.php @@ -6,8 +6,15 @@ class Message implements MessageDescription { + /** + * @var array + */ protected $metadata = array(); - protected $content; + + /** + * @var string + */ + protected $content = ''; /** * Set message metadata @@ -83,7 +90,7 @@ public function getContent() return $this->content; } - public function __toString() + public function toString() { $request = ''; foreach ($this->getMetadata() as $key => $value) { @@ -97,8 +104,4 @@ public function __toString() return $request; } - public function fromString($string) - { - throw new Exception\DomainException('Unimplemented: ' . __METHOD__); - } } diff --git a/src/MessageDescription.php b/src/MessageDescription.php index 89791b2b2..a81ffc98c 100644 --- a/src/MessageDescription.php +++ b/src/MessageDescription.php @@ -9,7 +9,5 @@ public function getMetadata($key = null); public function setContent($content); public function getContent(); - - public function __toString(); - public function fromString($string); + } From 5ac55eee467da1bc94645443372a001c29038625 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 23 Sep 2011 15:34:23 -0500 Subject: [PATCH 3/4] Ensure we return a callback - Check to see if we have a WeakRef; if so, get the object it composes before returning the callback --- src/CallbackHandler.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/CallbackHandler.php b/src/CallbackHandler.php index 483c3db35..be686c626 100644 --- a/src/CallbackHandler.php +++ b/src/CallbackHandler.php @@ -128,7 +128,25 @@ public function getCallback() throw new Exception\InvalidCallbackException('Invalid callback provided; not callable'); } - return $this->callback; + $callback = $this->callback; + if (is_string($callback)) { + return $callback; + } + + if ($callback instanceof WeakRef) { + return $callback->get(); + } + + if (is_object($callback)) { + return $callback; + } + + list($target, $method) = $callback; + if ($target instanceof WeakRef) { + return array($target->get(), $method); + } + + return $callback; } /** From 9bce1ba36abea66eee105acb4f4ea187f0f58466 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 24 Sep 2011 12:47:06 -0500 Subject: [PATCH 4/4] Cleaned up WeakRef support - 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 --- src/CallbackHandler.php | 86 ++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/src/CallbackHandler.php b/src/CallbackHandler.php index be686c626..a42863973 100644 --- a/src/CallbackHandler.php +++ b/src/CallbackHandler.php @@ -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 */ @@ -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); } @@ -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() { @@ -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; } @@ -197,7 +246,7 @@ public function isValid() return $this->validateStringCallback($callback); } - if ($callback instanceof \WeakRef) { + if ($callback instanceof WeakRef) { return $callback->valid(); } @@ -211,7 +260,7 @@ public function isValid() } list($target, $method) = $callback; - if ($target instanceof \WeakRef) { + if ($target instanceof WeakRef) { if (!$target->valid()) { return false; } @@ -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) { @@ -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; } @@ -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; } @@ -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; } }