Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Event Component Proposal

Proposed Component Name Zend_Event
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Event
Proposers Alvar Vilu
Zend Liaison TBD
Revision 1.0 - 2 December 2009: Initial Draft. (wiki revision: 15)

Table of Contents

1. Overview

Zend_Event is AS3'like powerful event system.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Any event dispatchable component must extend Zend_Event_Dispatcher or implement Zend_Event_Dispatcher_Interface.

4. Dependencies on Other Framework Components

  • Zend_Exception

5. Theory of Operation

This component can handle event listeners per obj or via stack trace.

6. Milestones / Tasks

Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.

  • Milestone 1: design notes will be published here
  • Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
  • Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
  • Milestone 4: Unit tests exist, work, and are checked into SVN.
  • Milestone 5: Initial documentation exists.

If a milestone is already done, begin the description with "[DONE]", like this:

  • Milestone #: [DONE] Unit tests ...

7. Class Index

  • Zend_Magic_Exception
  • Zend_Magic (factory class)
  • Zend_Magic_MindProbe
  • Zend_Magic_MindProbe_Intent
  • Zend_Magic_Action
  • Zend_Magic_CodeGen

8. Use Cases

UC-01
UC-02

9. Class Skeletons

/**

  • @see Zend_Exception
    */
    require_once 'AVlib/Exception.php';

/**

  • @author Alvar Vilu <alvar.vilu@msn.com>
    *
    */
    class Zend_Event_Exception extends Zend_Exception
    {}

/**

  • @see Zend_Event_Dispatcher_Interface
    */
    require_once 'AVlib/Event/Dispatcher/Interface.php';

/**

  • @see Zend_Event_Phase
    */
    require_once 'AVlib/Event/Phase.php';

/**

  • @see Zend_Event
    */
    require_once 'AVlib/Event.php';

/**

  • @author Alvar Vilu <alvar.vilu@msn.com>
    *
    */
    class Zend_Event_Dispatcher implements Zend_Event_Dispatcher_Interface
    {

/**

  • An array that holds all listeners that have been created.
  • [eventType][objID][useCapture] = array(listener, listener, ...)
  • listener = array (0 => priority, 1 => callback func
  • @var array
    */
    private static $_listeners = array ();

/**

  • A object that's used to dispatch events,
  • can be $this on obj that interface Zend_Event_Dispatcher_Interface.
  • @var Zend_Event_Dispatcher
    */
    private $dispatchObj = null;

public function __construct ( $dispatchObj = null )

Unknown macro: { $this->dispatchObj = $dispatchObj; }

final private function _getDispatchObj ( )

Unknown macro: { if ( null === $this->dispatchObj ) $this->dispatchObj = $this; return $this->dispatchObj; }

/**

  • Add new event listener to spesific type.
  • If use capture is enabled then event will flow from (main) to target
  • The higher the priority number - event will sorted to first.
  • If two or more same priority level events added then first one will be dispatched.
  • @param string $type
  • @param string|array $listener
  • @param bool $useCapture
  • @param int $priority
  • @param bool $useWeakReference UNIMPLEMENTED!
  • @return Zend_Event_Dispatcher Event dispatchable object
    */
    final public function addEventListener ( $type, $listener, $useCapture = false, $priority = 0, $useWeakReference = false ) {

$objHash = spl_object_hash ( $this->_getDispatchObj ( ) );
$useCapture = ( bool ) $useCapture;
$priority = ( int ) $priority;
$useWeakReference = ( bool ) $useWeakReference;

//Prepare self::$_listeners array indexes.
if ( ! isset ( self::$_listeners[ $type ][ $objHash ][ $useCapture ] ) ) {

if ( ! isset ( self::$_listeners[ $type ][ $objHash ] ) ) {

if ( ! isset ( self::$_listeners[ $type ] ) )

Unknown macro: { self}

self::$_listeners[ $type ][ $objHash ] = array ();
}

self::$_listeners[ $type ][ $objHash ][ $useCapture ] = array ();
}

//If ve have that same listner, don't add it twice.
foreach ( self::$_listeners[ $type ][ $objHash ][ $useCapture ] as $l ) {

if ( $l[ 1 ] === $listener )

Unknown macro: { return $this; }

}

//Finally add the listener.
self::$_listeners[ $type ][ $objHash ][ $useCapture ][ ] = array (
0 => $priority,
1 => $listener
);

//Now rearrange listeners by priority
rsort ( self::$_listeners[ $type ][ $objHash ][ $useCapture ] );

return $this;
}

/**

  • @param string $type
  • @param string|array $listener
  • @param bool $useCapture
  • @return Zend_Event_Dispatcher
    */
    final public function removeEventListener ( $type, $listener, $useCapture = false ) {

$objHash = spl_object_hash ( $this->_getDispatchObj ( ) );
$useCapture = ( bool ) $useCapture;

if ( ! isset ( self::$_listeners[ $type ][ $objHash ][ $useCapture ] ) )
return $this;

foreach ( self::$_listeners[ $type ][ $objHash ][ $useCapture ] as $k => $l ) {
if ( $l[ 1 ] === $listener )

Unknown macro: { unset ( self}

}

return $this;
}

/**

  • @param string|array|null $type If null, all listeners will be removed.
  • @return Zend_Event_Dispatcher
    */
    final public function removeEventListeners ( $type = null ) {

$objHash = spl_object_hash ( $this->_getDispatchObj ( ) );

if ( null === $type )

Unknown macro: { $types = array_keys ( self}

elseif ( is_array ( $type ) )

Unknown macro: { $types = $type; }

else

Unknown macro: { $types = array ( ( string ) $type ); }

foreach ( $types as $type ) {

if ( isset ( self::$_listeners[ $type ][ $objHash ] ) )

Unknown macro: { unset ( self}

}

return $this;
}

/**

  • @param Zend_Event $event Instance of Zend_Event to dispatch.
  • @throws Zend_Event_Dispatcher_Exception If listener function returned with a exception.
  • @return bool True if event dispatched successfully.
    */
    final public function dispatchEvent ( Zend_Event $event ) {

$last = null;
$dispatchTree = array ();

//Put 'at target' phase to dispatchTree.
$objHash = spl_object_hash ( $this->_getDispatchObj ( ) );
if ( isset ( self::$_listeners[ $event->type ][ $objHash ][ false ] ) )

Unknown macro: { $dispatchTree[ ] = array ( $this->_getDispatchObj ( ), Zend_Event_Phase}

//Prepare dispatch trees for capture and if needed, bubble phase.
foreach ( debug_backtrace ( ) as $branch ) {

if ( ! isset ( $branch[ 'object' ] ) or ! $branch[ 'object' ] instanceof Zend_Event_Dispatcher_Interface )
break;

if ( $branch[ 'object' ] === $this or $branch[ 'object' ] === $this->_getDispatchObj ( ) )
continue;

if ( $last !== $branch[ 'object' ] ) {

$last = $branch[ 'object' ];
$objHash = spl_object_hash ( $last );

if ( isset ( self::$_listeners[ $event->type ][ $objHash ][ true ] ) )

Unknown macro: { array_unshift ( $dispatchTree, array ( $last, Zend_Event_Phase}

if ( $event->bubbles and isset ( self::$_listeners[ $event->type ][ $objHash ][ false ] ) )

Unknown macro: { $dispatchTree[ ] = array ( $last, Zend_Event_Phase}

}

}

//Start dispatching.
$stopPropagation = false;

try {

foreach ( $dispatchTree as $dispatcher ) {
//$dispatcher = array (0 => $obj, 1 => phase, 2 => array(listener, ...))

foreach ( $dispatcher[ 2 ] as $listener ) {

$evt = $event->cloneEvent ( $dispatcher[ 1 ], $this->_getDispatchObj ( ), $dispatcher[ 0 ] );

if ( false === call_user_func ( $listener[ 1 ], $evt ) )

Unknown macro: { require_once 'AVlib/Event/Dispatcher/Exception.php'; throw new Zend_Event_Dispatcher_Exception ( 'Call to event listener failed' ); }

if ( true === $evt->stopPropagation )

Unknown macro: { $stopPropagation = true; }

if ( true === $evt->stopImmediatePropagation )

Unknown macro: { return false; }

}

if ( true === $stopPropagation )

Unknown macro: { return false; }

}

} catch ( Exception $e )

Unknown macro: { require_once 'AVlib/Event/Dispatcher/Exception.php'; throw new Zend_Event_Dispatcher_Exception ( 'Event dispatching failed', 0, $e ); }

return true;
}

/**

  • @param string $type
  • @return bool
    */
    final public function hasEventListener ( $type )
    Unknown macro: { return isset ( self}

/**

  • @param string $type Event type
  • @return bool
    */
    final public function willEventTrigger ( $type ) {

//No events at all for this type
if ( ! isset ( self::$_listeners[ $type ] ) )
return false;

foreach ( debug_backtrace ( ) as $branch )

Unknown macro: { if ( ! isset ( $branch[ 'object' ] ) or ! ( $branch[ 'object' ] instanceof Zend_Event_Dispatcher_Interface ) ) return false; if ( isset ( self}

return false;
}

}

/**

  • @see Zend_Event_Exception
    */
    require_once 'AVlib/Event/Exception.php';

/**

  • @author Alvar Vilu <alvar.vilu@msn.com>
    *
    */
    class Zend_Event_Dispatcher_Exception extends Zend_Event_Exception
    {}

interface Zend_Event_Dispatcher_Interface

Unknown macro: { public function addEventListener ( $type, $listener, $useCapture = false, $priority = 0, $useWeakReference = false ); public function removeEventListener ( $type, $listener, $useCapture = false ); public function removeEventListeners ( $type = null ); public function dispatchEvent ( Zend_Event $event ); public function hasEventListener ( $type ); public function willEventTrigger ( $type ); }

/**

  • @author Alvar Vilu <alvar.vilu@msn.com>
    *
    */
    class Zend_Event_Phase
    Unknown macro: { /** * main -> target * * @var int */ const CAPTURING = 1; /** * at target * * @var int */ const AT_TARGET = 2; /** * target -> main * * @var int */ const BUBBLING = 3; }

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.