<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.event-manager.event-manager">
    <title>The EventManager</title>
 
    <sect2 id="zend.event-manager.event-manager.intro">
        <title>Overview</title>

        <para>
            <classname>Zend_EventManager</classname> is a component designed for the following use
            cases:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    Implementing simple subject/observer patterns.
                </para>
            </listitem>

            <listitem>
                <para>
                    Implementing Aspect-Oriented designs.
                </para>
            </listitem>

            <listitem>
                <para>
                    Implementing event-driven architectures.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            The basic architecture allows you to attach and detach listeners to named events, both on
            a per-instance basis as well as statically; trigger events; and interrupt execution of
            listeners.
        </para>
    </sect2>
 
    <sect2 id="zend.event-manager.event-manager.quick-start">
        <title>Quick Start</title>

        <para>
            Typically, you will compose a <classname>Zend_EventManager_EventManager</classname> instance in a class.
        </para>

        <programlisting language="php"><![CDATA[
class Foo
{
    protected $events;

    public function events(Zend_EventManager_EventCollection $events = null)
    {
        if (null !== $events) {
            $this->events = $events;
        } elseif (null === $this->events) {
            $this->events = new Zend_EventManager_EventManager(__CLASS__);
        }
        return $this->events;
    }
}
]]></programlisting>

        <para>
            The above allows users to access the <classname>EventManager</classname> instance, or
            reset it with a new instance; if one does not exist, it will be lazily instantiated
            on-demand.
        </para>

        <para>
            An <classname>EventManager</classname> is really only interesting if it triggers some
            events. Basic triggering takes three arguments: the event name, which is usually the
            current function/method name; the "context", which is usually the current object
            instance; and the arguments, which are usually the arguments provided to the current
            function/method.
        </para>

        <programlisting language="php"><![CDATA[
class Foo
{
    // ... assume events definition from above

    public function bar($baz, $bat = null)
    {
        $params = compact('baz', 'bat');
        $this->events()->trigger(__FUNCTION__, $this, $params);
    }
}
]]></programlisting>

        <para>
            In turn, triggering events is only interesting if something is listening for the event.
            Listeners attach to the <classname>EventManager</classname>, specifying a named event
            and the callback to notify. The callback receives a <classname>Zend_EventManager_Event</classname>
            object, which has accessors for retrieving the event name, context, and parameters.
            Let's add a listener, and trigger the event.
        </para>

        <programlisting language="php"><![CDATA[
$log = Zend_Log::factory($someConfig);
$foo = new Foo();
$foo->events()->attach('bar', function ($e) use ($log) {
    $event  = $e->getName();
    $target = get_class($e->getTarget());
    $params = json_encode($e->getParams());

    $log->info(sprintf(
        '%s called on %s, using params %s',
        $event,
        $target,
        $params
    ));
});

// Results in log message:
$foo->bar('baz', 'bat');
// reading: bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"
]]></programlisting>

        <para>
            Note that the second argument to <methodname>attach()</methodname> is any valid callback;
            an anonymous function is shown in the example in order to keep the example
            self-contained. However, you could also utilize a valid function name, a functor, a
            string referencing a static method, or an array callback with a named static method or
            instance method. Again, any PHP callback is valid.
        </para>

        <para>
            Sometimes you may want to specify listeners without yet having an object instance of the
            class composing an <classname>EventManager</classname>. The
            <classname>Zend_EventManager_StaticEventManager</classname> allows you to do this. The call to
            <methodname>attach</methodname> is identical to the <classname>EventManager</classname>,
            but expects an additional parameter at the beginning: a named instance. Remember the
            example of composing an <classname>EventManager</classname>, how we passed it
            <constant>__CLASS__</constant>? That value, or any strings you provide in an array to
            the constructor, may be used to identify an instance when using the
            <classname>StaticEventManager</classname>. As an example, we could change the above
            example to attach statically:
        </para>

        <programlisting language="php"><![CDATA[
$log = Zend_Log::factory($someConfig);
$events = Zend_EventManager_StaticEventManager::getInstance();
$events->attach('Foo', 'bar', function ($e) use ($log) {
    $event  = $e->getName();
    $target = get_class($e->getTarget());
    $params = json_encode($e->getParams());

    $log->info(sprintf(
        '%s called on %s, using params %s',
        $event,
        $target,
        $params
    ));
});

// Later, instantiate Foo:
$foo = new Foo();

// And we can still trigger the above event:
$foo->bar('baz', 'bat');
// results in log message: 
// bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"
]]></programlisting>

        <para>
            The <classname>EventManager</classname> also provides the ability to detach listeners,
            short-circuit execution of an event either from within a listener or by testing return
            values of listeners, test and loop through the results returned by listeners, prioritize
            listeners, and more. Many of these features are detailed in the examples.
        </para>
        
        <sect3 id="zend.event-manager.event-manager.quick-start.wildcard">
            <title>Wildcard Listeners</title>

            <para>
                Sometimes you'll want to attach the same listener to many events or to all events of
                a given instance -- or potentially, with the static manager, many contexts, and many
                events. The <classname>EventManager</classname> component allows for this.
            </para>

            <example id="zend.event-manager.event-manager.quick-start.wildcard.many">
                <title>Attaching to many events at once</title>

                <programlisting language="php"><![CDATA[
$events = new Zend_EventManager_EventManager();
$events->attach(array('these', 'are', 'event', 'names'), $callback);
]]></programlisting>

                <para>
                    Note that if you specify a priority, that priority will be used for all events
                    specified.
                </para>
            </example>

            <example id="zend.event-manager.event-manager.quick-start.wildcard.wildcard">
                <title>Attaching using the wildcard</title>

                <programlisting language="php"><![CDATA[
$events = new Zend_EventManager_EventManager();
$events->attach('*', $callback);
]]></programlisting>

                <para>
                    Note that if you specify a priority, that priority will be used for this
                    listener for any event triggered.
                </para>

                <para>
                    What the above specifies is that <emphasis>any</emphasis> event triggered will
                    result in notification of this particular listener.
                </para>
            </example>

            <example id="zend.event-manager.event-manager.quick-start.wildcard.static-many">
                <title>Attaching to many events at once via the StaticEventManager</title>

                <programlisting language="php"><![CDATA[
$events = Zend_EventManager_StaticEventManager::getInstance();
// Attach to many events on the context "foo"
$events->attach('foo', array('these', 'are', 'event', 'names'), $callback);

// Attach to many events on the contexts "foo" and "bar"
$events->attach(array('foo', 'bar'), array('these', 'are', 'event', 'names'), $callback);
]]></programlisting>

                <para>
                    Note that if you specify a priority, that priority will be used for all events
                    specified.
                </para>
            </example>

            <example id="zend.event-manager.event-manager.quick-start.wildcard.static-wildcard">
                <title>Attaching to many events at once via the StaticEventManager</title>

                <programlisting language="php"><![CDATA[
$events = Zend_EventManager_StaticEventManager::getInstance();
// Attach to all events on the context "foo"
$events->attach('foo', '*', $callback);

// Attach to all events on the contexts "foo" and "bar"
$events->attach(array('foo', 'bar'), '*', $callback);
]]></programlisting>

                <para>
                    Note that if you specify a priority, that priority will be used for all events
                    specified.
                </para>

                <para>
                    The above is specifying that for the contexts "foo" and "bar", the specified
                    listener should be notified for any event they trigger.
                </para>
            </example>
        </sect3>
    </sect2>
 
    <sect2 id="zend.event-manager.event-manager.options">
        <title>Configuration Options</title>
 
        <variablelist>
            <title>Zend_EventManager_EventManager Options</title>
 
            <varlistentry>
                <term>identifier</term>
 
                <listitem>
                    <para>
                        A string or array of strings to which the given
                        <classname>EventManager</classname> instance can answer when accessed via
                        the <classname>StaticEventManager</classname>.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry>
                <term>event_class</term>
 
                <listitem>
                    <para>
                        The name of an alternate <classname>Zend_EventManager_Event</classname> class to use for
                        representing events passed to listeners.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry>
                <term>static_connections</term>
 
                <listitem>
                    <para>
                        An instance of a <interfacename>Zend_EventManager_StaticEventCollection</interfacename>
                        instance to use when triggering events. By default, this will use
                        the global <classname>Zend_EventManager_StaticEventManager</classname> instance, but that can
                        be overridden by passing a value to this method. A <constant>null</constant>
                        value will prevent the instance from triggering any further statically
                        attached listeners.
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
    </sect2>
 
    <sect2 id="zend.event-manager.event-manager.methods">
        <title>Available Methods</title>
 
        <variablelist>
            <varlistentry id="zend.event-manager.event-manager.methods.constructor">
                <term>__construct</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>__construct</methodname>
                        <methodparam>
                            <funcparams>null|string|int $identifier</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Constructs a new <classname>EventManager</classname> instance, using the
                        given identifier, if provided, for purposes of static attachment.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.set-event-class">
                <term>setEventClass</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>setEventClass</methodname>
                        <methodparam>
                            <funcparams>string $class</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Provide the name of an alternate <classname>Zend_EventManager_Event</classname> class to use
                        when creating events to pass to triggered listeners.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.set-static-connections">
                <term>setStaticConnections</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>setStaticConnections</methodname>
                        <methodparam>
                            <funcparams>Zend_EventManager_StaticEventCollection $connections = null</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        An instance of a <interfacename>Zend_EventManager_StaticEventCollection</interfacename>
                        instance to use when triggering events. By default, this will use
                        the global <classname>Zend_EventManager_StaticEventManager</classname> instance, but that can
                        be overridden by passing a value to this method. A <constant>null</constant>
                        value will prevent the instance from triggering any further statically
                        attached listeners.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.get-static-connections">
                <term>getStaticConnections</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>getStaticConnections</methodname>
                        <void/>
                    </methodsynopsis>
                    <para>
                        Returns the currently attached
                        <interfacename>Zend_EventManager_StaticEventCollection</interfacename> instance, lazily
                        retrieving the global <classname>Zend_EventManager_StaticEventManager</classname> instance if
                        none is attached and usage of static listeners hasn't been disabled by
                        passing a <constant>null</constant> value to <link
                            linkend="zend.event-manager.event-manager.methods.set-static-connections">setStaticConnections()</link>.
                        Returns either a boolean <constant>false</constant> if static listeners are
                        disabled, or a <interfacename>StaticEventCollection</interfacename> instance
                        otherwise.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.trigger">
                <term>trigger</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>trigger</methodname>

                        <methodparam>
                            <funcparams>string $event, mixed $target, mixed $argv, callback $callback</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Triggers all listeners to a named event. The recommendation is to use the
                        current function/method name for <varname>$event</varname>, appending it
                        with values such as ".pre", ".post", etc. as needed.
                        <varname>$context</varname> should be the current object instance, or the
                        name of the function if not triggering within an object.
                        <varname>$params</varname> should typically be an associative array or
                        <classname>ArrayAccess</classname> instance; we recommend using the
                        parameters passed to the function/method (<function>compact()</function> is
                        often useful here). This method can also take a callback and behave in the 
                        same way as <methodname>triggerUntil()</methodname>.
                    </para>

                    <para>
                        The method returns an instance of <classname>Zend_EventManager_ResponseCollection</classname>,
                        which may be used to introspect return values of the various listeners, test
                        for short-circuiting, and more.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.trigger-until">
                <term>triggerUntil</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>triggerUntil</methodname>

                        <methodparam>
                            <funcparams>string $event, mixed $context, mixed $argv, callback
                                $callback</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Triggers all listeners to a named event, just like <link
                            linkend="zend.event-manager.event-manager.methods.trigger">trigger()</link>,
                        with the addition that it passes the return value from each listener to
                        <varname>$callback</varname>; if <varname>$callback</varname> returns a
                        boolean <constant>true</constant> value, execution of the listeners is
                        interrupted. You can test for this using <code>$result-&gt;stopped()</code>.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.attach">
                <term>attach</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>attach</methodname>

                        <methodparam>
                            <funcparams>string $event, callback $callback, int $priority</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Attaches <varname>$callback</varname> to the
                        <classname>Zend_EventManager_EventManager</classname> instance, listening for the event
                        <varname>$event</varname>. If a <varname>$priority</varname> is provided,
                        the listener will be inserted into the internal listener stack using that
                        priority; higher values execute earliest. (Default priority is "1", and
                        negative priorities are allowed.)
                    </para>

                    <para>
                        The method returns an instance of
                        <classname>Zend_Stdlib_CallbackHandler</classname>; this value can later be
                        passed to <methodname>detach()</methodname> if desired.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.attach-aggregate">
                <term>attachAggregate</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>attachAggregate</methodname>
                        <methodparam>
                            <funcparams>string|Zend_EventManager_ListenerAggregate $aggregate</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        If a string is passed for <varname>$aggregate</varname>, instantiates that
                        class. The <varname>$aggregate</varname> is then passed the
                        <classname>EventManager</classname> instance to its
                        <methodname>attach()</methodname> method so that it may register listeners.
                    </para>

                    <para>
                        The <classname>ListenerAggregate</classname> instance is returned.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.detach">
                <term>detach</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>detach</methodname>
                        <methodparam>
                            <funcparams>Zend_Stdlib_CallbackHandler $listener</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Scans all listeners, and detaches any that match <varname>$listener</varname>
                        so that they will no longer be triggered.
                    </para>

                    <para>
                        Returns a boolean <constant>true</constant> if any listeners have been
                        identified and unsubscribed, and a boolean <constant>false</constant>
                        otherwise.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.detach-aggregate">
                <term>detachAggregate</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>detachAggregate</methodname>
                        <methodparam>
                            <funcparams>Zend_EventManager_ListenerAggregate $aggregate</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Loops through all listeners of all events to identify listeners that are
                        represented by the aggregate; for all matches, the listeners will be removed.

                    </para>

                    <para>
                        Returns a boolean <constant>true</constant> if any listeners have been
                        identified and unsubscribed, and a boolean <constant>false</constant>
                        otherwise.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.get-events">
                <term>detachAggregate</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>getEvents</methodname>
                        <void/>
                    </methodsynopsis>
                    <para>
                        Returns an array of all event names that have listeners attached.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.get-listeners">
                <term>getListeners</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>getListeners</methodname>

                        <methodparam>
                            <funcparams>string $event</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Returns a <classname>Zend_Stdlib_PriorityQueue</classname> instance of all
                        listeners attached to <varname>$event</varname>.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.clear-listeners">
                <term>clearListeners</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>clearListeners</methodname>

                        <methodparam>
                            <funcparams>string $event</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Removes all listeners attached to <varname>$event</varname>.
                    </para>
                </listitem>
            </varlistentry>

            <varlistentry id="zend.event-manager.event-manager.methods.prepare-args">
                <term>prepareArgs</term>
                <listitem>
                    <methodsynopsis>
                        <methodname>prepareArgs</methodname>

                        <methodparam>
                            <funcparams>array $args</funcparams>
                        </methodparam>
                    </methodsynopsis>
                    <para>
                        Creates an <classname>ArrayObject</classname> from the provided
                        <varname>$args</varname>. This can be useful if you want yours listeners
                        to be able to modify arguments such that later listeners or the triggering
                        method can see the changes.
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
    </sect2>
 
    <sect2 id="zend.event-manager.event-manager.examples">
        <title>Examples</title>
 
        <example id="zend.event-manager.event-manager.examples.modifying-args">
            <title>Modifying Arguments</title>
 
            <para>
                Occasionally it can be useful to allow listeners to modify the arguments they
                receive so that later listeners or the calling method will receive those changed
                values.
            </para>

            <para>
                As an example, you might want to pre-filter a date that you know will arrive as a
                string and convert it to a <classname>DateTime</classname> argument.
            </para>

            <para>
                To do this, you can pass your arguments to <methodname>prepareArgs()</methodname>,
                and pass this new object when triggering an event. You will then pull that value
                back into your method.
            </para>
 
            <programlisting language="php"><![CDATA[
class ValueObject
{
    // assume a composed event manager

    function inject(array $values)
    {
        $argv = compact('values');
        $argv = $this->events()->prepareArgs($argv);
        $this->events()->trigger(__FUNCTION__, $this, $argv);
        $date = isset($argv['values']['date']) ? $argv['values']['date'] : new DateTime('now');

        // ...
    }
}

$v = new ValueObject();

$v->events()->attach('inject', function($e) {
    $values = $e->getParam('values');
    if (!$values) {
        return;
    }
    if (!isset($values['date'])) {
        $values['date'] = new DateTime('now');
        return;
    }
    $values['date'] = new Datetime($values['date']);
});

$v->inject(array(
    'date' => '2011-08-10 15:30:29',
));
]]></programlisting>
        </example>

        <example id="zend.event-manager.event-manager.examples.short-circuiting">
            <title>Short Circuiting</title>
 
            <para>
                One common use case for events is to trigger listeners until either one indicates no
                further processing should be done, or until a return value meets specific criteria.
                As examples, if an event creates a Response object, it may want execution to stop.
            </para>

            <programlisting language="php"><![CDATA[
$listener = function($e) {
    // do some work
    
    // Stop propagation and return a response
    $e->stopPropagation(true);
    return $response;
};
]]></programlisting>

            <para>
                Alternately, we could do the check from the method triggering the event.
            </para>
 
            <programlisting language="php"><![CDATA[
class Foo implements Dispatchable
{
    // assume composed event manager

    public function dispatch(Request $request, Response $response = null)
    {
        $argv = compact('request', 'response');
        $results = $this->events()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
            return ($v instanceof Response);
        });
    }
}
]]></programlisting>

            <para>
                Typically, you may want to return a value that stopped execution, or use it some
                way. Both <methodname>trigger()</methodname> and
                <methodname>triggerUntil()</methodname> return a
                <classname>Zend_EventManager_ResponseCollection</classname> instance; call its
                <methodname>stopped()</methodname> method to test if execution was stopped, and
                <methodname>last()</methodname> method to retrieve the return value from the last
                executed listener:
            </para>
 
            <programlisting language="php"><![CDATA[
class Foo implements Dispatchable
{
    // assume composed event manager

    public function dispatch(Request $request, Response $response = null)
    {
        $argv = compact('request', 'response');
        $results = $this->events()->triggerUntil(__FUNCTION__, $this, $argv, function($v) {
            return ($v instanceof Response);
        });

        // Test if execution was halted, and return last result:
        if ($results->stopped()) {
            return $results->last();
        }

        // continue...
    }
}
]]></programlisting>
        </example>

        <example id="zend.event-manager.event-manager.examples.priority">
            <title>Assigning Priority to Listeners</title>
 
            <para>
                One use case for the <classname>EventManager</classname> is for implementing caching
                systems. As such, you often want to check the cache early, and save to it late. 
            </para>

            <para>
                The third argument to <methodname>attach()</methodname> is a priority value. The
                higher this number, the earlier that listener will execute; the lower it is, the
                later it executes. The value defaults to 1, and values will trigger in the order
                registered within a given priority.
            </para>

            <para>
                So, to implement a caching system, our method will need to trigger an event at
                method start as well as at method end. At method start, we want an event that will
                trigger early; at method end, an event should trigger late. 
            </para>

            <para>
                Here is the class in which we want caching:
            </para>

            <programlisting language="php"><![CDATA[
class SomeValueObject
{
    // assume it composes an event manager

    public function get($id)
    {
        $params = compact('id');
        $results = $this->events()->trigger('get.pre', $this, $params);

        // If an event stopped propagation, return the value
        if ($results->stopped()) {
            return $results->last();
        }

        // do some work...

        $params['__RESULT__'] = $someComputedContent;
        $this->events()->trigger('get.post', $this, $params);
    }
}
]]></programlisting>

            <para>
                Now, let's create a <interfacename>ListenerAggregate</interfacename> that can handle
                caching for us:
            </para>

            <programlisting language="php"><![CDATA[
class CacheListener implements Zend_EventManager_ListenerAggregate
{
    protected $cache;

    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    public function attach(Zend_EventManager_EventCollection $events)
    {
        $events->attach('get.pre', array($this, 'load'), 100);
        $events->attach('get.post', array($this, 'save'), -100);
    }

    public function load($e)
    {
        $id = get_class($e->getTarget()) . '-' . json_encode($e->getParams());
        if (false !== ($content = $this->cache->load($id))) {
            $e->stopPropagation(true);
            return $content;
        }
    }

    public function save($e)
    {
        $params  = $e->getParams();
        $content = $params['__RESULT__'];
        unset($params['__RESULT__']);

        $id = get_class($e->getTarget()) . '-' . json_encode($params);
        $this->cache->save($content, $id);
    }
}
]]></programlisting>

            <para>
                We can then attach the aggregate to an instance.
            </para>

            <programlisting language="php"><![CDATA[
$value         = new SomeValueObject();
$cacheListener = new CacheListener($cache);
$value->events()->attachAggregate($cacheListener);
]]></programlisting>

            <para>
                Now, as we call <methodname>get()</methodname>, if we have a cached entry, it will
                be returned immediately; if not, a computed entry will be cached when we complete
                the method.
            </para>
        </example>
    </sect2>
</sect1>
