<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>Phly_PubSub/library/Phly/PubSub/Exception.php</filename>
    </added>
    <added>
      <filename>Phly_PubSub/library/Phly/PubSub/InvalidCallbackException.php</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -44,6 +44,9 @@ class Phly_PubSub_Handle
         } else {
             $this-&gt;_callback = array($context, $handler);
         }
+        if (!is_callable($this-&gt;_callback)) {
+            throw new Phly_PubSub_InvalidCallbackException();
+        }
     }
 
     /**
@@ -70,10 +73,10 @@ class Phly_PubSub_Handle
      * Invoke handler
      * 
      * @param  array $args Arguments to pass to callback
-     * @return void
+     * @return mixed
      */
     public function call(array $args)
     {
-        call_user_func_array($this-&gt;getCallback(), $args);
+        return call_user_func_array($this-&gt;getCallback(), $args);
     }
 }</diff>
      <filename>Phly_PubSub/library/Phly/PubSub/Handle.php</filename>
    </modified>
    <modified>
      <diff>@@ -29,19 +29,81 @@ class Phly_PubSub_Provider
      * Publish to all handlers for a given topic
      * 
      * @param  string $topic 
-     * @param  mixed $args All arguments besides the topic are passed as arguments to the handler
+     * @param  mixed $argv All arguments besides the topic are passed as arguments to the handler
      * @return void
      */
-    public function publish($topic, $args = null)
+    public function publish($topic, $argv = null)
     {
         if (empty($this-&gt;_topics[$topic])) {
             return;
         }
-        $args = func_get_args();
-        array_shift($args);
+
+        $return = null;
+        $argv   = func_get_args();
+        array_shift($argv);
+        foreach ($this-&gt;_topics[$topic] as $handle) {
+            $return = $handle-&gt;call($argv);
+        }
+        return $return;
+    }
+
+    /**
+     * Notify subscribers until return value of one causes a callback to 
+     * evaluate to true
+     *
+     * Publishes subscribers until the provided callback evaluates the return 
+     * value of one as true, or until all subscribers have been executed.
+     * 
+     * @param  Callable $callback 
+     * @param  string $topic 
+     * @param  mixed $argv All arguments besides the topic are passed as arguments to the handler
+     * @return mixed
+     * @throws Phly_PubSub_InvalidCallbackException if invalid callback provided
+     */
+    public function publishUntil($callback, $topic, $argv = null)
+    {
+        if (!is_callable($callback)) {
+            throw new Phly_PubSub_InvalidCallbackException('Invalid filter callback provided');
+        }
+
+        if (empty($this-&gt;_topics[$topic])) {
+            return;
+        }
+
+        $return = null;
+        $argv   = func_get_args();
+        $argv   = array_slice($argv, 2);
+        foreach ($this-&gt;_topics[$topic] as $handle) {
+            $return = $handle-&gt;call($argv);
+            if (call_user_func($callback, $return)) {
+                break;
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Filter a value
+     *
+     * Notifies subscribers to the topic and passes the single value provided
+     * as an argument. Each subsequent subscriber is passed the return value
+     * of the previous subscriber, and the value of the last subscriber is 
+     * returned.
+     * 
+     * @param  string $topic 
+     * @param  mixed $value 
+     * @return mixed
+     */
+    public function filter($topic, $value)
+    {
+        if (empty($this-&gt;_topics[$topic])) {
+            return;
+        }
+
         foreach ($this-&gt;_topics[$topic] as $handle) {
-            $handle-&gt;call($args);
+            $value = $handle-&gt;call(array($value));
         }
+        return $value;
     }
 
     /**</diff>
      <filename>Phly_PubSub/library/Phly/PubSub/Provider.php</filename>
    </modified>
    <modified>
      <diff>@@ -49,20 +49,20 @@ class Phly_PubSub_HandleTest extends PHPUnit_Framework_TestCase
 
     public function testGetTopicShouldReturnTopic()
     {
-        $handle = new Phly_PubSub_Handle('foo', 'bar');
+        $handle = new Phly_PubSub_Handle('foo', 'rand');
         $this-&gt;assertEquals('foo', $handle-&gt;getTopic());
     }
 
     public function testCallbackShouldBeStringIfNoHandlerPassedToConstructor()
     {
-        $handle = new Phly_PubSub_Handle('foo', 'bar');
-        $this-&gt;assertSame('bar', $handle-&gt;getCallback());
+        $handle = new Phly_PubSub_Handle('foo', 'rand');
+        $this-&gt;assertSame('rand', $handle-&gt;getCallback());
     }
 
     public function testCallbackShouldBeArrayIfHandlerPassedToConstructor()
     {
-        $handle = new Phly_PubSub_Handle('foo', 'bar', 'baz');
-        $this-&gt;assertSame(array('bar', 'baz'), $handle-&gt;getCallback());
+        $handle = new Phly_PubSub_Handle('foo', 'Phly_PubSub_HandleTest_ObjectCallback', 'test');
+        $this-&gt;assertSame(array('Phly_PubSub_HandleTest_ObjectCallback', 'test'), $handle-&gt;getCallback());
     }
 
     public function testCallShouldInvokeCallbackWithSuppliedArguments()
@@ -73,12 +73,34 @@ class Phly_PubSub_HandleTest extends PHPUnit_Framework_TestCase
         $this-&gt;assertSame($args, $this-&gt;args);
     }
 
+    /**
+     * @expectedException Phly_PubSub_InvalidCallbackException
+     */
+    public function testPassingInvalidCallbackShouldRaiseInvalidCallbackException()
+    {
+        $handle = new Phly_PubSub_Handle('foo', 'boguscallback');
+    }
+
+    public function testCallShouldReturnTheReturnValueOfTheCallback()
+    {
+        $handle = new Phly_PubSub_Handle('foo', 'Phly_PubSub_HandleTest_ObjectCallback', 'test');
+        $this-&gt;assertEquals('bar', $handle-&gt;call(array()));
+    }
+
     public function handleCall()
     {
         $this-&gt;args = func_get_args();
     }
 }
 
+class Phly_PubSub_HandleTest_ObjectCallback
+{
+    public static function test()
+    {
+        return 'bar';
+    }
+}
+
 // Call Phly_PubSub_HandleTest::main() if this source file is executed directly.
 if (PHPUnit_MAIN_METHOD == &quot;Phly_PubSub_HandleTest::main&quot;) {
     Phly_PubSub_HandleTest::main();</diff>
      <filename>Phly_PubSub/tests/Phly/PubSub/HandleTest.php</filename>
    </modified>
    <modified>
      <diff>@@ -114,10 +114,43 @@ class Phly_PubSub_ProviderTest extends PHPUnit_Framework_TestCase
         $this-&gt;assertEquals('test message', $this-&gt;message);
     }
 
+    public function testPublishShouldReturnTheReturnValueOfTheLastInvokedSubscriber()
+    {
+        $this-&gt;provider-&gt;subscribe('string.transform', 'trim');
+        $this-&gt;provider-&gt;subscribe('string.transform', 'str_rot13');
+        $value = $this-&gt;provider-&gt;publish('string.transform', ' foo ');
+        $this-&gt;assertEquals(str_rot13(' foo '), $value);
+    }
+
+    public function testPublishUntilShouldReturnAsSoonAsCallbackReturnsTrue()
+    {
+        $this-&gt;provider-&gt;subscribe('foo.bar', 'strpos');
+        $this-&gt;provider-&gt;subscribe('foo.bar', 'strstr');
+        $value = $this-&gt;provider-&gt;publishUntil(
+            array($this, 'evaluateStringCallback'), 
+            'foo.bar',
+            'foo', 'f'
+        );
+        $this-&gt;assertSame(0, $value);
+    }
+
+    public function testFilterShouldPassReturnValueOfEachSubscriberToNextSubscriber()
+    {
+        $this-&gt;provider-&gt;subscribe('string.transform', 'trim');
+        $this-&gt;provider-&gt;subscribe('string.transform', 'str_rot13');
+        $value = $this-&gt;provider-&gt;filter('string.transform', ' foo ');
+        $this-&gt;assertEquals(str_rot13('foo'), $value);
+    }
+
     public function handleTestTopic($message)
     {
         $this-&gt;message = $message;
     }
+
+    public function evaluateStringCallback($value)
+    {
+        return (!$value);
+    }
 }
 
 // Call Phly_PubSub_ProviderTest::main() if this source file is executed directly.</diff>
      <filename>Phly_PubSub/tests/Phly/PubSub/ProviderTest.php</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f93f1d88dc29eeafbd8baa6987be85f0f43057e3</id>
    </parent>
  </parents>
  <author>
    <name>Matthew Weier O'Phinney</name>
    <email>matthew@zend.com</email>
  </author>
  <url>http://github.com/weierophinney/phly/commit/165faf07eede9173448c95b7310862a19177766f</url>
  <id>165faf07eede9173448c95b7310862a19177766f</id>
  <committed-date>2009-09-30T07:24:13-07:00</committed-date>
  <authored-date>2009-09-30T07:24:13-07:00</authored-date>
  <message>New features:
- publish() now returns return value of last subscriber notified
- publishUntil($callback, $topic, ...) notifies subscribers until the callback
  provided returns true. The return value of the last notified subscriber is
  returned. (Idea borrowed from Symfony 2 event dispatcher.)
- filter($topic, $value) passes the value to subscribers. Each subscriber's
  return value is then passed to the next subscriber, and the final return value
  returned. (Idea borrowed from Symfony 2 event dispatcher.)</message>
  <tree>c3af0eaab07e5d77bd29a947df772eab2573b847</tree>
  <committer>
    <name>Matthew Weier O'Phinney</name>
    <email>matthew@zend.com</email>
  </committer>
</commit>
