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

Commit 4f413a5

Browse files
committed
Merge branch 'master' of git://github.com/zendframework/zf2

20 files changed

+831
-56
lines changed

src/CallbackHandler.php

Lines changed: 144 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
*/
2424
namespace Zend\Stdlib;
2525

26+
use Closure,
27+
WeakRef;
28+
2629
/**
2730
* CallbackHandler
2831
*
@@ -63,15 +66,65 @@ class CallbackHandler
6366
* Constructor
6467
*
6568
* @param string $event Event to which slot is subscribed
66-
* @param string|array|object $callback PHP callback (first element may be )
69+
* @param string|array|object $callback PHP callback
6770
* @param array $options Options used by the callback handler (e.g., priority)
6871
* @return void
6972
*/
7073
public function __construct($event, $callback, array $options = array())
7174
{
7275
$this->event = $event;
73-
$this->callback = $callback;
7476
$this->options = $options;
77+
$this->registerCallback($callback);
78+
}
79+
80+
/**
81+
* Registers the callback provided in the constructor
82+
*
83+
* If you have pecl/weakref {@see http://pecl.php.net/weakref} installed,
84+
* this method provides additional behavior.
85+
*
86+
* If a callback is a functor, or an array callback composing an object
87+
* instance, this method will pass the object to a WeakRef instance prior
88+
* to registering the callback. See {@link isValid()} for more information
89+
* on how this affects execution.
90+
*
91+
* @param callback $callback
92+
* @return void
93+
*/
94+
protected function registerCallback($callback)
95+
{
96+
// If pecl/weakref is not installed, simply register it
97+
if (!class_exists('WeakRef', false)) {
98+
$this->callback = $callback;
99+
return;
100+
}
101+
102+
// If we have a non-closure object, pass it to WeakRef, and then
103+
// register it.
104+
if (is_object($callback) && !$callback instanceof Closure) {
105+
$this->callback = new WeakRef($callback);
106+
return;
107+
}
108+
109+
// If we have a string or closure, register as-is
110+
if (!is_array($callback)) {
111+
$this->callback = $callback;
112+
return;
113+
}
114+
115+
list($target, $method) = $callback;
116+
117+
// If we have an array callback, and the first argument is not an
118+
// object, register as-is
119+
if (!is_object($target)) {
120+
$this->callback = $callback;
121+
return;
122+
}
123+
124+
// We have an array callback with an object as the first argument;
125+
// pass it to WeakRef, and then register the new callback
126+
$target = new WeakRef($target);
127+
$this->callback = array($target, $method);
75128
}
76129

77130
/**
@@ -88,26 +141,33 @@ public function getEvent()
88141
* Retrieve registered callback
89142
*
90143
* @return Callback
91-
* @throws Exception\InvalidCallbackException
144+
* @throws Exception\InvalidCallbackException If callback is invalid
92145
*/
93146
public function getCallback()
94147
{
95-
if ($this->isValidCallback) {
96-
return $this->callback;
148+
if (!$this->isValid()) {
149+
throw new Exception\InvalidCallbackException('Invalid callback provided; not callable');
97150
}
98151

99152
$callback = $this->callback;
100153
if (is_string($callback)) {
101-
return $this->validateStringCallback($callback);
154+
return $callback;
102155
}
103-
if (is_array($callback)) {
104-
return $this->validateArrayCallback($callback);
156+
157+
if ($callback instanceof WeakRef) {
158+
return $callback->get();
105159
}
106-
if (is_callable($callback)) {
107-
$this->isValidCallback = true;
160+
161+
if (is_object($callback)) {
108162
return $callback;
109163
}
110-
throw new Exception\InvalidCallbackException('Invalid callback provided; not callable');
164+
165+
list($target, $method) = $callback;
166+
if ($target instanceof WeakRef) {
167+
return array($target->get(), $method);
168+
}
169+
170+
return $callback;
111171
}
112172

113173
/**
@@ -146,44 +206,106 @@ public function getOption($name)
146206
return null;
147207
}
148208

209+
/**
210+
* Is the composed callback valid?
211+
*
212+
* Typically, this method simply checks to see if we have a valid callback.
213+
* In a few situations, it does more.
214+
*
215+
* * If we have a string callback, we pass execution to
216+
* {@link validateStringCallback()}.
217+
* * If we have an object callback, we test to see if that object is a
218+
* WeakRef {@see http://pecl.php.net/weakref}. If so, we return the value
219+
* of its valid() method. Otherwise, we return the result of is_callable().
220+
* * If we have a callback array with a string in the first position, we
221+
* pass execution to {@link validateArrayCallback()}.
222+
* * If we have a callback array with an object in the first position, we
223+
* test to see if that object is a WeakRef (@see http://pecl.php.net/weakref).
224+
* If so, we return the value of its valid() method. Otherwise, we return
225+
* the result of is_callable() on the callback.
226+
*
227+
* WeakRef is used to allow listeners to go out of scope. This functionality
228+
* is turn-key if you have pecl/weakref installed; otherwise, you will have
229+
* to manually remove listeners before destroying an object referenced in a
230+
* listener.
231+
*
232+
* @return bool
233+
*/
234+
public function isValid()
235+
{
236+
// If we've already tested this, we can move on. Note: if a callback
237+
// composes a WeakRef, this will never get set, and thus result in
238+
// validation on each call.
239+
if ($this->isValidCallback) {
240+
return $this->callback;
241+
}
242+
243+
$callback = $this->callback;
244+
245+
if (is_string($callback)) {
246+
return $this->validateStringCallback($callback);
247+
}
248+
249+
if ($callback instanceof WeakRef) {
250+
return $callback->valid();
251+
}
252+
253+
if (is_object($callback) && is_callable($callback)) {
254+
$this->isValidCallback = true;
255+
return true;
256+
}
257+
258+
if (!is_array($callback)) {
259+
return false;
260+
}
261+
262+
list($target, $method) = $callback;
263+
if ($target instanceof WeakRef) {
264+
if (!$target->valid()) {
265+
return false;
266+
}
267+
$target = $target->get();
268+
return is_callable(array($target, $method));
269+
}
270+
return $this->validateArrayCallback($callback);
271+
}
272+
149273
/**
150274
* Validate a string callback
151275
*
152276
* Check first if the string provided is callable. If not see if it is a
153277
* valid class name; if so, determine if the object is invokable.
154278
*
155279
* @param string $callback
156-
* @return Callback
157-
* @throws Exception\InvalidCallbackException
280+
* @return bool
158281
*/
159282
protected function validateStringCallback($callback)
160283
{
161284
if (is_callable($callback)) {
162285
$this->isValidCallback = true;
163-
return $callback;
286+
return true;
164287
}
165288

166289
if (!class_exists($callback)) {
167-
throw new Exception\InvalidCallbackException('Provided callback is not a function or a class');
290+
return false;
168291
}
169292

170293
// check __invoke before instantiating
171294
if (!method_exists($callback, '__invoke')) {
172-
throw new Exception\InvalidCallbackException('Class provided as a callback does not implement __invoke');
295+
return false;
173296
}
174297
$object = new $callback();
175298

176299
$this->callback = $object;
177300
$this->isValidCallback = true;
178-
return $object;
301+
return true;
179302
}
180303

181304
/**
182305
* Validate an array callback
183306
*
184307
* @param array $callback
185-
* @return callback
186-
* @throws Exception\InvalidCallbackException
308+
* @return bool
187309
*/
188310
protected function validateArrayCallback(array $callback)
189311
{
@@ -194,15 +316,15 @@ protected function validateArrayCallback(array $callback)
194316
// Dealing with a class/method callback, and class provided is a string classname
195317

196318
if (!class_exists($context)) {
197-
throw new Exception\InvalidCallbackException('Class provided in callback does not exist');
319+
return false;
198320
}
199321

200322
// We need to determine if we need to instantiate the class first
201323
$r = new \ReflectionClass($context);
202324
if (!$r->hasMethod($method)) {
203325
// Explicit method does not exist
204326
if (!$r->hasMethod('__callStatic') && !$r->hasMethod('__call')) {
205-
throw new Exception\InvalidCallbackException('Class provided in callback does not define the method requested');
327+
return false;
206328
}
207329

208330
if ($r->hasMethod('__callStatic')) {
@@ -238,7 +360,6 @@ protected function validateArrayCallback(array $callback)
238360
return $callback;
239361
}
240362

241-
242-
throw new Exception\InvalidCallbackException('Method provided in callback does not exist in object');
363+
return false;
243364
}
244365
}

src/Dispatchable.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
namespace Zend\Stdlib;
3+
4+
interface Dispatchable
5+
{
6+
public function dispatch(RequestDescription $request, ResponseDescription $response = null);
7+
}

src/Exception.php

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,7 @@
11
<?php
2-
/**
3-
* Zend Framework
4-
*
5-
* LICENSE
6-
*
7-
* This source file is subject to the new BSD license that is bundled
8-
* with this package in the file LICENSE.txt.
9-
* It is also available through the world-wide-web at this URL:
10-
* http://framework.zend.com/license/new-bsd
11-
* If you did not receive a copy of the license and are unable to
12-
* obtain it through the world-wide-web, please send an email
13-
* to license@zend.com so we can send you a copy immediately.
14-
*
15-
* @category Zend
16-
* @package Zend_Stdlib
17-
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
18-
* @license http://framework.zend.com/license/new-bsd New BSD License
19-
*/
202

21-
/**
22-
* @namespace
23-
*/
24-
namespace Zend\Stdlib;
3+
namespace Zend\Stdlib;
254

26-
/**
27-
* Marker interface for exceptions
28-
*
29-
* @category Zend
30-
* @package Zend_Stdlib
31-
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
32-
* @license http://framework.zend.com/license/new-bsd New BSD License
33-
*/
34-
interface Exception
5+
interface Exception
356
{
367
}

src/Exception/DomainException.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Zend\Stdlib\Exception;
4+
5+
use Zend\Stdlib\Exception;
6+
7+
class DomainException extends \DomainException implements Exception
8+
{
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Zend\Stdlib\Exception;
4+
5+
use Zend\Stdlib\Exception;
6+
7+
class InvalidArgumentException
8+
extends \InvalidArgumentException
9+
implements Exception
10+
{
11+
}

src/Exception/InvalidCallbackException.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
*/
2424
namespace Zend\Stdlib\Exception;
2525

26-
use Zend\Stdlib\Exception,
27-
DomainException;
26+
use Zend\Stdlib\Exception;
2827

2928
/**
3029
* Invalid callback exception

src/IsAssocArray.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Zend\Stdlib;
4+
5+
/**
6+
* Simple class for testing whether a value is an associative array
7+
*
8+
* Declared abstract, as we have no need for instantiation.
9+
*/
10+
abstract class IsAssocArray
11+
{
12+
/**
13+
* Test whether a value is an associative array
14+
*
15+
* We have an associative array if at least one key is a string.
16+
*
17+
* @param mixed $value
18+
* @return bool
19+
*/
20+
public static function test($value)
21+
{
22+
return (is_array($value)
23+
&& count(array_filter(array_keys($value), 'is_string')) > 0
24+
);
25+
}
26+
}

0 commit comments

Comments
 (0)