Implement event systems, signal slots, intercepting filters, and observers.

Overview

zend-eventmanager

Repository abandoned 2019-12-31

This repository has moved to laminas/laminas-eventmanager.

Build Status Coverage Status

zend-eventmanager is designed for the following use cases:

  • Implementing simple subject/observer patterns.
  • Implementing Aspect-Oriented designs.
  • Implementing event-driven architectures.

The basic architecture allows you to attach and detach listeners to named events, both on a per-instance basis as well as via shared collections; trigger events; and interrupt execution of listeners.

For migration from version 2 to version 3, please read the migration documentation.

Benchmarks

We provide scripts for benchmarking zend-eventmanager using the Athletic framework; these can be found in the benchmarks/ directory.

To execute the benchmarks you can run the following command:

$ vendor/bin/athletic -p benchmarks
Comments
  • EventManager refactor for version 3

    EventManager refactor for version 3

    This patch refactors zend-eventmanager with the following goals:

    • Maintainability.
      • It removes methods where they have no clear purpose or are redundant.
      • It simplifies signatures where arguments have multiple possible types or meanings; if necessary, it creates new methods when multiple signatures are deemed useful.
    • Performance.
      • Queues are inlined into the implementations whenever possible to promote performance.
      • Arrays are preferred over objects for queues due to the speed of array manipulation in PHP.
      • Features such as lazy listeners were added to help users further optimize their event manager usage.
      • A benchmarking suite using Athletic was written to help gauge the differences between versions and approaches.
    • Usability.
      • Whenever possible, signatures were refactored to have a single set of valid arguments, and a single execution path, providing reliability and easing the ability to learn usage.
      • Documentation was written alongside the implementation to aid in identifying difficult and redundant usage areas.

    Alongside these goals, we also tried to minimize migration for end users. To this end, migration document was written, and a 2.6.0 release is planned in order to deprecate methods as well as add implementation methods to the EventManager that will aid in helping users prepare their code for version 3.

    Listeners are executed exactly as they were in version 2, using the same priorities and precedence between local explicit and wildcard listeners, and shared and shared wildcard listeners. Tests were added to describe and document these rules.

    Change synopsis

    Removed functionality

    • GlobalEventManager and StaticEventManager are removed (with prejudice!).
    • ProvidesEvents, which was previously deprecated, is removed.
    • EventManagerInterface::setSharedManager() is removed. Shared managers are now expected to be injected during instantiation.
    • EventManagerInterface::getEvents() and getListeners() are removed; they had now purpose within the implementation.
    • EventManagerInterface::setEventClass() was renamed to setEventPrototype(), which now expects an EventInterface instance. That instance will be cloned whenever a new event is created.
    • EventManagerInterface::attachAggregate() and detachAggregate() are removed. Users should use the attach() and detach() methods of the aggregates themselves.
    • SharedEventAggregateAwareInterface and SharedListenerAggregateInterface are removed. This was an undocumented and largely unused feature.
    • SharedEventManagerAwareInterface is removed. A new interface, SharedEventsCapableInterface defines the getSharedManager() method from the interface, and EventManagerInterface extends that new interface.
    • SharedEventManagerInterface::getEvents() is removed, as it had no purpose in the implementation.

    Changed signatures

    • EventManager::__construct() now accepts an optional SharedEventManagerInterface instance as the first argument, and an optional array of identifiers as the second. As identifiers have no meaning without a shared manager present, they are secondary to providing the shared manager.
    • EventManagerInterface::trigger() changes its signature to trigger($eventName, $target = null, $argv = []); each argument has exactly one possible meaning; the $eventName can only be a string event name. The fourth $callback argument is removed.
    • EventManagerInterface::triggerUntil() changes its signature to triggerUntil(callable $callback, $eventName, $target = null, $argv = null). Each argument has exactly one meaning.
    • EventManagerInterface adds two new methods for triggering provided EventInterface arguments: triggerEvent(EventInterface $event) and triggerEventUntil(callable $callback, EventInterface $event).
    • EventManagerInterface::attach() and detach() change their signatures to attach($eventName, callable $listener, $priority = 1) and detach(callable $listener, $eventName = null), respectively. Note that $eventName can now only be a string event name, not an array or Traversable.
    • EventManagerInterface::setIdentifiers() and addIdentifiers() change their signatures to each only accept an array of identifiers.
    • SharedEventManagerInterface::getListeners() changes signature to getListeners(array $identifiers, $eventName) and now guarantees return of an array. Note that the second argument is now required.
    • SharedEventManagerInterface::attach() changes signature to attach($identifier, $eventName, callable $listener, $priority = 1). The $identifier and $eventName must be strings.
    • SharedEventManagerInterface::detach() changes signature to detach(callable $listener, $identifier = null, $eventName = null); $identifier and $eventName must be strings if passed.
    • ListenerAggregateInterface::attach() adds an optional $priority = 1 argument. This was used already in v2, but not dictated by the interface.
    • FilterInterface::attach() and detach() have changed signature to attach(callable $callback) and detach(callable $ilter), respectively.
    • FilterIterator::insert() has been modified to raise an exception if the value provided is not a callable.
    • ResponseCollection::setStopped() no longer implements a fluent interface.

    New functionality

    • LazyListener allows wrapping:
      • fetching a listener service from a container-interop container, and
      • invoking a designated listener method with the provided event.
    • LazyEventListener extends LazyListener, and provides metadata for discovering the intended event name and priority at which to attach the lazy listener; these are consumed by:
    • LazyListenerAggregate, which, provided a list of LazyEventListeners and/or definitions to use to create them, acts as an aggregate for attaching a number of such listeners at once.

    Benchmarks

    Benchmarks are listed in a gist. In particular:

    The highlight is the MultipleEventMultipleLocalAndSharedListener benchmark, which demonstrates a real-world usage scenario in zend-mvc with many local and shared listeners, and many events being triggered. This benchmark jumped from 435 operations per second in version 2 to 1265 operations per second in this implementation — a change of ~300%! Considering this represents 10–50X the number of events triggered in a reasonably sized zend-mvc application, performance will be in the sub-millisecond range.

    enhancement BC Break 
    opened by weierophinney 65
  • Performance increasement by integer priority

    Performance increasement by integer priority

    After my PR #16 I experimented with integer priorities.

    It's no longer storing priorities as string with .0 suffix but store as integer. This makes it harder to merge listeners of the same priority by name, wildcard or shared but in the end I sow a very good performance improvement, only MultipleEventMultipleSharedListener decreases a little (see below).

    All tests passed for me but it should be tested carefully anyway ;)

    PHP 5.6

    PHP 5.6.14-0+deb8u1 (cli) (built: Oct 4 2015 16:13:10) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

    Branch: develop

    ZendBench\EventManager\MultipleEventIndividualSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000237437248] [42,116.39107]
    
    
    ZendBench\EventManager\MultipleEventLocalListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000196894646] [50,788.58272]
    
    
    ZendBench\EventManager\MultipleEventMultipleLocalAndSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0002710880280] [3,688.83867]
    
    
    ZendBench\EventManager\MultipleEventMultipleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000575615883] [17,372.69644]
    
    
    ZendBench\EventManager\SingleEventMultipleListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000446063042] [22,418.35585]
    
    
    ZendBench\EventManager\SingleEventMultipleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000478469849] [20,899.95854]
    
    
    ZendBench\EventManager\SingleEventSingleListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000078002930] [128,200.31299]
    
    
    ZendBench\EventManager\SingleEventSingleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000106687546] [93,731.65281]
    

    Branch: integer_priority

    ZendBench\EventManager\MultipleEventIndividualSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000230637074] [43,358.16375]
    
    
    ZendBench\EventManager\MultipleEventLocalListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000166062355] [60,218.34387]
    
    
    ZendBench\EventManager\MultipleEventMultipleLocalAndSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0002452962399] [4,076.70334]
    
    
    ZendBench\EventManager\MultipleEventMultipleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000640497684] [15,612.85894]
    
    
    ZendBench\EventManager\SingleEventMultipleListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000399168015] [25,052.10747]
    
    
    ZendBench\EventManager\SingleEventMultipleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000434321880] [23,024.39838]
    
    
    ZendBench\EventManager\SingleEventSingleListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000066055775] [151,387.21856]
    
    
    ZendBench\EventManager\SingleEventSingleSharedListener
        Method Name   Iterations    Average Time      Ops/second
        -------  ------------  --------------    -------------
        trigger: [5,000     ] [0.0000096778870] [103,328.34056]
    

    PHP 7.0

    PHP 7.0.0 (cli) (built: Dec 5 2015 23:50:11) ( NTS ) Copyright (c) 1997-2015 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2015 Zend Technologies

    Fatal error: Uncaught TypeError: Argument 1 passed to Athletic\Common\CmdLineErrorHandler::handleException() must be an instance of Exception, instance of Error given in /home/mabe/workspace/zend-eventmanager/vendor/athletic/athletic/src/Athletic/Common/CmdLineErrorHandler.php:35
    Stack trace:
    #0 [internal function]: Athletic\Common\CmdLineErrorHandler->handleException(Object(Error))
    #1 {main}
      thrown in /home/mabe/workspace/zend-eventmanager/vendor/athletic/athletic/src/Athletic/Common/CmdLineErrorHandler.php on line 35
    

    Something with athletic doesn't work with PHP-7 but I have no time to figure that out.

    enhancement 
    opened by marc-mabe 42
  • Refactor

    Refactor

    Hi everyone,

    PS: Everyone is free to fork my branch and submit PR to it!

    Motivation

    Event manager has always been, with service manager, the slowest parts of ZF2, but the most widely used one internally. It was therefore highly important to make them faster.

    Also, the event manager was a complex piece of software with a lot of obscure features, a strange shared manager concept and complex signatures.

    Breaking changes

    Massive performance improvements

    I copy-paste some of the benchmarks I've made (the PHP7 build was an old one, so expect at least 10-15% improvments on PHP7):

    ZF2 EVM

    PHP 5.6

    Bench\EventManagerBenchmark
    Method Name          Iterations    Average Time      Ops/second
    ------------------  ------------  --------------    -------------
    attach20Listeners : [1,000     ] [0.0005693485737] [1,756.39326]
    trigger20Listeners: [1,000     ] [0.0010452888012] [956.67341]
    

    PHP 7

    Bench\EventManagerBenchmark
    Method Name          Iterations    Average Time      Ops/second
    ------------------  ------------  --------------    -------------
    attach20Listeners : [1,000     ] [0.0000875046253] [11,427.96734]
    trigger20Listeners: [1,000     ] [0.0001518340111] [6,586.13965]
    

    ZF3 EVM

    PHP 5.6

    Bench\EventManagerBenchmark
    Method Name          Iterations    Average Time      Ops/second
    ------------------  ------------  --------------    -------------
    attach20Listeners : [1,000     ] [0.0001252155304] [7,986.22980]
    trigger20Listeners: [1,000     ] [0.0002773256302] [3,605.86939]
    

    PHP 7

    Bench\EventManagerBenchmark
    Method Name          Iterations    Average Time      Ops/second
    ------------------  ------------  --------------    -------------
    attach20Listeners : [1,000     ] [0.0000128707886] [77,695.31713]
    trigger20Listeners: [1,000     ] [0.0000429670811] [23,273.63123]
    

    No more shared event manager

    I've explain myself why I think the shared event manager was a bad idea (although @ocramius tried to show me the contrary, I think the event manager should stay simple as introduced in this PR).

    As I used EVM in my app, I realized I ONLY used the Shared event manager. But the SEM is SLOW AS HELL and complex (because of identifiers, expensive merging...). I think that in most use cases, people should have a global EVM used throughout their applications, and have more precise event name.

    Simplifications

    Previously, trigger and triggerUntil were very complex. Depending on the order and types of parameters, you could use it in a LOT of different ways. Now everything has been enforced in interfaces, so there is one possible use case, which make things clearer and more efficient (less checks).

    Event no longer has name

    I've simplified the EventInterface so that it no longer has a getName method. I'm not against reverting it, but to me, we should trigger event like this:

    $evm->trigger('event.name', $eventObject);
    

    As an event object could be used for various event names, I think it makes more sense to be used like this (and avoid to re-add logic to check if the first parameter is a string or object... that kind of things).

    propagationIsStopped has been renamed to isPropagationStopped.

    Lazy listeners

    One main issue with ZF2 was the inability to have lazy listeners. This means taht when you attach listeners, they are created, with all their dependencies, on every requests, even if you don't need them.

    From my app, this can add a non-negligible overhead, and solutions like proxies are annoying to setup.

    Some example usage:

    <?php
        // The idea is that in ZF3, there will be one "application-wide" event manager registered. Nothing prevents you to
        // create your own local event manager, but to replicate the shared manager behaviour, you should retrieve the
        // application-wide evm
    
        // Attaching a simple listener:
    
        $eventManager = new EventManager();
        $eventManager->attach('foo', function() {}, $priority);
    
        // Triggering a listener
        $eventManager->trigger('foo', new Event());
    
        // Triggering a listener with a callable that will stop propagation if return to true
        $eventManager->triggerUntil('foo', new Event(), function() {});
    
        // Attaching to wildcard
        $eventManager->attach('*', function() {});
    
        // Attaching a listener and detaching it
        $listener = $eventManager->attach('foo', function() {});
        $eventManager->detach('foo', $listener);
    
        // Detaching all listeners from a given event
        $eventManager->detach('foo');
    
    
        // Using listener aggregate to aggregate multiple events, lazily
        class MyListenerAggregate implements ListenerAggregateInterface
        {
            public static function attachAggregate(EventManager $evm)
            {
                // No more confusion of getting the shared manager. Note the method is static, and
                // that we pass a "spec" without instantiating the listener, as it will be created only
                // on demand
                $evm->attach('foo', [static::class, 'myMethod']);
            }
    
            public function myMethod(Event $event)
            {
    
            }
        }
    
        // In the module class
        $evm = $serviceLocator->get(EventManager::class);
        MyListenerAggregate::attachAggregate($evm); // Maybe should be called "bind" instad of attachAggregate
    
        // No more identifiers, no more SEM... Of course, events must now be scoped, so no "created" event anymore,
        // but "user.created", "application.dispatch", "controller.dispatch"...
    
    ?>
    
    opened by bakura10 33
  • Even lazier instantiation of event listeners?

    Even lazier instantiation of event listeners?

    Hey folks,

    while deeply diving into event manager and its performance, I wonder if even lazier instantiation of event listeners at the last possible moment, i.e. \Zend\EventManager\EventManager::triggerListeners() could further improve performance? I realize LazyListeners already exist, but they are usually instantiated during instantiation of the respective event manager. In case of many events for the same event manager this obviously leads to many unnecessary operations.

    Any opions?

    opened by Pittiplatsch 17
  • 1 byte less per attached event

    1 byte less per attached event

    Store priority as array key as "<prio>." instead of "<prio>.0" as:

    • it also doesn't get converted to int
    • it is a compatible float format
    opened by marc-mabe 7
  • Trigger doesn't reset stop propagation

    Trigger doesn't reset stop propagation

    I have found issue in apigility https://github.com/zfcampus/zf-apigility/commit/076a21d3b81ea0ee744f73d2d40a383305a6dbb0#diff-c9248b3167fc44af085b81db2e292837R121.

    My MvcEvent has stop propagation property set to true during dispatch. After this name is changed to event finish. triggerEvent method trigger my event, but after first listener stop propagation stops dispatching my event, because it's already affected by event dispatch. My proposition it's to reset always stop propagation property before trigger like here https://github.com/zendframework/zend-eventmanager/blob/release-2.6.3/src/EventManager.php#L211 . Now trigger() and triggerEvent() (or triggerEventUntil) trigger events with stop propagation = true in different way

    opened by snapshotpl 5
  • Migration Guide: ListenerAggregateInterface is not compatible

    Migration Guide: ListenerAggregateInterface is not compatible

    Running my tests, I find out that ListenerAggregateInterface is not compatibile migrating from v2 to v3 due to a Fatal Error for Incompatible Interface. Seems that every listener implementing that interface should add the second parameter $priority before to migrate.

    Is it right?

    question 
    opened by thomasvargiu 5
  • Deprecations to create in 2.6.0

    Deprecations to create in 2.6.0

    Per #4, we need to mark a number of features as deprecated for an upcoming 2.6.0 release. This should be done against master.

    Deprecations can be done via annotations; all deprecations MUST also be noted in the CHANGELOG.md.

    • [x] Mark GlobalEventManager as deprecated
    • [x] Mark StaticEventManager as deprecated
    • [x] Mark EventManager(Interface)?::setSharedManager as deprecated
    • [x] Mark EventManager(Interface)?::getEvents as deprecated
    • [x] Mark EventManager(Interface)?::getListeners as deprecated
    • [x] Mark EventManager(Interface)?::setEventClass as deprecated
    • [x] Mark EventManager(Interface)?::attachAggregate as deprecated
    • [x] Mark EventManager(Interface)?::detachAggregate as deprecated
    • [x] Mark SharedListenerAggregateInterface as deprecated
    • [x] Mark SharedEventAggregateAwareInterface as deprecated
    • [x] Mark SharedEventManagerAwareInterface as deprecated
    • [x] Mark SharedEventManagerInterface::getEvents as deprecated
    • [x] Add EventManager::triggerEvent() (but not at interface level)
    • [x] Add EventManager::triggerEventUntil() (but not at interface level)
    help wanted EasyFix BC Break 
    opened by weierophinney 5
  • Syntax error in EventManager docs

    Syntax error in EventManager docs

    On page: https://docs.zendframework.com/zend-eventmanager/tutorial/ , there is a syntax error: $params = )'foo' => 'bar', 'baz' => 'bat']; which should be: $params = ['foo' => 'bar', 'baz' => 'bat'];

    documentation 
    opened by divix1988 4
  • Explore the option of use event class FQCN (::class) as event name

    Explore the option of use event class FQCN (::class) as event name

    Use the FQCN of an event name make easy to provide unique event names and avoid name conflicts between libraries.

    Pro:

    • FQCN + PHP 5.5 class makes almost imposible mistakes in event name (typos while typing the event name string) and PHP error message is enough for to determinate error nature and location.
    • Does not require getName neither getTarget on EventInterface
    • SharedEventManager could be removed.

    Con:

    • Need to compare large strings while filtering the listeners. Anyone knows if PHP it's enough smart for compare two strings by the same memory reference?
    • Require to have 1 class per event.
    • Makes more difficult wildcard events (could require substring matches)
    question 
    opened by Maks3w 4
  • Optimization of the SplPriorityQueue

    Optimization of the SplPriorityQueue

    I'm sorry, I do not do PR. But I'll show you the code.

    Event Manager uses the queue.

    1. For the experiment, I did a SplPriorityQueue with php. If you add an element to the queue, this leads to complex calculations. But not every Queue will be used by the event manager!!!!!!!
    • Therefore, I propose to add items only to the array, but SplPriorityQueue create only when the getIterator() method.
    1. Delete operations are very complex in terms of computing. Two or more operations - even worse :)
    • The solution is the same. I propose to add items only to the array, but SplPriorityQueue create only when the getIterator() method.
    <?php
    
    namespace Zend\Stdlib;
    
    use Countable;
    use IteratorAggregate;
    use Serializable;
    
    /**
     * Optimized for add and remove operations.
     */
    class PriorityQueue implements Countable, IteratorAggregate, Serializable
    {
        const EXTR_DATA     = 0x00000001;
        const EXTR_PRIORITY = 0x00000002;
        const EXTR_BOTH     = 0x00000003;
    
        /**
         * Inner queue class to use for iteration
         * @var string
         */
        protected $queueClass = 'Zend\Stdlib\SplPriorityQueue';
    
        /**
         * Actual items aggregated in the priority queue. Each item is an array
         * with keys "data" and "priority".
         * @var array
         */
        protected $items      = [];
    
        /**
         * Inner queue object
         * @var SplPriorityQueue
         */
        protected $queue;
    
        /**
         * Insert an item into the queue
         *
         * Priority defaults to 1 (low priority) if none provided.
         *
         * @param  mixed $data
         * @param  int $priority
         * @return PriorityQueue
         */
        public function insert($data, $priority = 1)
        {
            $priority = (int) $priority;
            $this->items[] = [
                'data'     => $data,
                'priority' => $priority,
            ];
            return $this;
        }
    
        /**
         * Remove an item from the queue
         *
         * This is different than {@link extract()}; its purpose is to dequeue an
         * item.
         *
         * This operation is potentially expensive, as it requires
         * re-initialization and re-population of the inner queue.
         *
         * Note: this removes the first item matching the provided item found. If
         * the same item has been added multiple times, it will not remove other
         * instances.
         *
         * @param  mixed $datum
         * @return bool False if the item was not found, true otherwise.
         */
        public function remove($datum)
        {
            $found = false;
            foreach ($this->items as $key => $item) {
                if ($item['data'] === $datum) {
                    unset($this->items[$key]);
                    $this->queue = null;
                    $found = true;
                    break;
                }
            }
            return $found;
        }
    
        /**
         * Is the queue empty?
         *
         * @return bool
         */
        public function isEmpty()
        {
            return (0 === $this->count());
        }
    
        /**
         * How many items are in the queue?
         *
         * @return int
         */
        public function count()
        {
            return count($this->items);
        }
    
        /**
         * Peek at the top node in the queue, based on priority.
         *
         * @return mixed
         */
        public function top()
        {
            return $this->getIterator()->top();
        }
    
        /**
         * Extract a node from the inner queue and sift up
         *
         * @return mixed
         */
        public function extract()
        {
            return $this->getQueue()->extract();
        }
    
        /**
         * Retrieve the inner iterator
         *
         * SplPriorityQueue acts as a heap, which typically implies that as items
         * are iterated, they are also removed. This does not work for situations
         * where the queue may be iterated multiple times. As such, this class
         * aggregates the values, and also injects an SplPriorityQueue. This method
         * retrieves the inner queue object, and clones it for purposes of
         * iteration.
         *
         * @return SplPriorityQueue
         */
        public function getIterator()
        {
            $queue = $this->getQueue();
            return clone $queue;
        }
    
        /**
         * Serialize the data structure
         *
         * @return string
         */
        public function serialize()
        {
            return serialize($this->items);
        }
    
        /**
         * Unserialize a string into a PriorityQueue object
         *
         * Serialization format is compatible with {@link Zend\Stdlib\SplPriorityQueue}
         *
         * @param  string $data
         * @return void
         */
        public function unserialize($data)
        {
            foreach (unserialize($data) as $item) {
                $this->insert($item['data'], $item['priority']);
            }
        }
    
        /**
         * Serialize to an array
         *
         * By default, returns only the item data, and in the order registered (not
         * sorted). You may provide one of the EXTR_* flags as an argument, allowing
         * the ability to return priorities or both data and priority.
         *
         * @param  int $flag
         * @return array
         */
        public function toArray($flag = self::EXTR_DATA)
        {
            switch ($flag) {
                case self::EXTR_BOTH:
                    return $this->items;
                case self::EXTR_PRIORITY:
                    return array_map(function ($item) {
                        return $item['priority'];
                    }, $this->items);
                case self::EXTR_DATA:
                default:
                    return array_map(function ($item) {
                        return $item['data'];
                    }, $this->items);
            }
        }
    
        /**
         * Specify the internal queue class
         *
         * Please see {@link getIterator()} for details on the necessity of an
         * internal queue class. The class provided should extend SplPriorityQueue.
         *
         * @param  string $class
         * @return PriorityQueue
         */
        public function setInternalQueueClass($class)
        {
            $this->queueClass = (string) $class;
            return $this;
        }
    
        /**
         * Does the queue contain the given datum?
         *
         * @param  mixed $datum
         * @return bool
         */
        public function contains($datum)
        {
            foreach ($this->items as $item) {
                if ($item['data'] === $datum) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * Does the queue have an item with the given priority?
         *
         * @param  int $priority
         * @return bool
         */
        public function hasPriority($priority)
        {
            foreach ($this->items as $item) {
                if ($item['priority'] === $priority) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * Get the inner priority queue instance
         *
         * @throws Exception\DomainException
         * @return SplPriorityQueue
         */
        protected function getQueue()
        {
            if (null === $this->queue) {
                $this->queue = new $this->queueClass();
                if (!$this->queue instanceof \SplPriorityQueue) {
                    throw new Exception\DomainException(sprintf(
                        'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"',
                        get_class($this->queue)
                    ));
                }
                foreach ($this->items as $item) {
                    $this->queue->insert($item['data'], $item['priority']);
                }
            }
            return $this->queue;
        }
    
        /**
         * Add support for deep cloning
         *
         * @return void
         */
        public function __clone()
        {
            if (null !== $this->queue) {
                $this->queue = clone $this->queue;
            }
        }
    }
    
    
    opened by dimon-smith 4
  • Upgrade help from 2x to 3x

    Upgrade help from 2x to 3x

    Hi @weierophinney, I try to update zendframework/zend-eventmanager - ^2.6.3 to ^3.0.0 main Problem in \Zend\EventManager\SharedEventManager::attach was the return removed. We using this return for caching and detach our events. Is there any migration guide I try to do it by self but I am not sure if this be working as before.

    Old:

        /**
         * {@inheritdoc}
         */
     public function attach(EventManagerInterface $events, $priority = 1)
        {
            $sharedEvents = $events->getSharedManager();
            $this->listeners[] = $sharedEvents->attach(
                Application::class,
                MvcEvent::EVENT_BOOTSTRAP,
                [$this, 'onBootstrap'],
                $priority
            );
        }
    
        /**
         * {@inheritdoc}
         */
        public function detach(EventManagerInterface $events)
        {
            foreach ($this->listeners as $index => $listener) {
                if ($events->detach($listener)) {
                    unset($this->listeners[$index]);
                }
            }
        }
    

    New:

     public function attach(EventManagerInterface $events, $priority = 1)
        {
            $sharedEvents = $events->getSharedManager();
            $sharedEvents->attach(
                Application::class,
                MvcEvent::EVENT_BOOTSTRAP,
                [$this, 'onBootstrap'],
                $priority
            );
    
            $this->listeners = $sharedEvents->getListeners([Application::class],
                MvcEvent::EVENT_BOOTSTRAP);
        }
    
        /**
         * {@inheritdoc}
         */
        public function detach(EventManagerInterface $events)
        {
            foreach ($this->listeners as $index => $listener) {
                    $events->detach($listener);
                    unset($this->listeners[$index]);
            }
        }
    
    opened by larsroettig 1
  • Provide duck-typed PSR-14 implementation

    Provide duck-typed PSR-14 implementation

    This patch provides a forwards-compatibility release that adapts zend-eventmanager to work as a PSR-14 EventDispatcher. It does so by doing the following:

    • Creating package-specific ListenerProviderInterface and EventDispatcherInterface versions that work with PHP 5.6.
    • Moving all listener aggregation into classes within a ListenerProvider subnamespace; this includes both the listener attachment previously in the EventManager instance as well as the SharedEventManager instance.
    • Renaming "listener aggregates" to "listener subscribers" in the new ListenerProvider subnamespace.
    • Adapting EventManager to consume listener providers. By default, it will create a default priority-based listener provider, and have its various listener attachment methods proxy to that provider; it then aggregates that provider with the SharedEventManager (which is itself a provider now) in order to retrieve listeners. This is done in such a way that behavior is completely backwards compatible with current usage.
      • A new named constructor, createUsingListenerProvider(), allows providing a specific listener provider for use with the EventManager. If the provider is attachment capable, the various listener attachment methods will proxy to it; otherwise, they will raise an exception.

    The patch also deprecates a number of features, including:

    • Most interfaces, including the EventInterface.
    • The entire "shared event manager" concept (this can be accomplished via event object hierarchies instead)

    TODO

    • [x] Write changelog entries
    • [ ] Create documentation of new features
    • [ ] Document how to start migrating to the new features in order to prepare for version 4
    enhancement WIP 
    opened by weierophinney 2
  • ListenerAggregatePluginManager?

    ListenerAggregatePluginManager?

    hi Team,

    assuming that an application has factories for its listeners, it would be nice to configure them in a central location and to not pollute the service container. any interest?

    thanks : )

    opened by abacaphiliac 3
  • Null/No-op ListenerAggregate?

    Null/No-op ListenerAggregate?

    hi Team,

    do you see any value in a NullListenerAggregate?

    i'm using feature toggles in a production app and would like for my listener factory to be able to produce a valid ListenerAggregateInterface but maybe one that doesn't do anything if the behavior in the aspect is disabled via toggle.

    maybe there's another way i can solve this problem that i haven't considered.

    thank you : )

    opened by abacaphiliac 1
  • Replace container-interop with PSR-11 container interface

    Replace container-interop with PSR-11 container interface

    This PR replaces use of the container-interop package with the PSR-11 container interface. This is obviously a BC break and would need to be released in EventManager 4.0 along with a migration guide.

    This PR is being submitted in conjunction with a similar PR on the zend-framework/zend-servicemanager repository. Before merging and releasing the zend-servicemanager PR, this one should be merged and released with a new major version, and the zend-servicemanager should have its Composer dependencies updated to include version 4.0 of zend-eventmanager.

    enhancement BC Break 
    opened by michaelmoussa 4
  • Should there be a SharedLazyListenerAggregate?

    Should there be a SharedLazyListenerAggregate?

    Is there a reason there isn't a Lazy Aggregate for attaching listeners to the Shared Event Manager?

    Something like:

    class SharedLazyListenerAggregate
    {
        ...
    
        public function attachShared(SharedEventManagerInterface $manager, $priority = 1)
        {
            foreach ($this->lazyListeners as $lazyListener) {
                $this->listeners[] = $manager->attach(
                    '*',
                    $lazyListener->getEvent(),
                    $lazyListener,
                    $lazyListener->getPriority($priority)
                );
            }
        }
    

    Ping @bakura10

    opened by davidwindell 3
Releases(release-3.2.1)
  • release-3.2.1(Apr 25, 2018)

  • release-2.6.4(Dec 12, 2017)

    Added

    • Nothing.

    Changed

    • Nothing.

    Deprecated

    • Nothing.

    Removed

    • Nothing.

    Fixed

    • #68 fixes an issue whereby triggerListeners() was not resetting the event instance's "stop propagation" flag before triggering listeners; this could result in all listeners for a given event being skipped in cases where the event manager instance triggers multiple events.
    Source code(tar.gz)
    Source code(zip)
  • release-3.2.0(Jul 11, 2017)

  • release-3.1.0(Dec 19, 2016)

    Added

    • #26 publishes the documentation to https://zendframework.github.io/zend-eventmanager/

    Changes

    • #17 makes a number of internal changes to how listeners are stored in order to improve performance, by as much as 10% in the scenario used in the MVC layer.

      Additionally, it optimizes when the target and event arguments are injected into an event, eliminating that step entirely when either is unavailable.

    Deprecated

    • Nothing.

    Removed

    • Nothing.

    Fixed

    • Nothing.
    Source code(tar.gz)
    Source code(zip)
  • release-3.0.1(Feb 18, 2016)

  • release-2.6.3(Feb 18, 2016)

    Added

    • Nothing.

    Deprecated

    • Nothing.

    Removed

    • Nothing.

    Fixed

    • #23 updates the requirements to allow usage with PHP 7, bumps zend-stdlib to ^2.7, and requires PHP 7 builds to pass continuous integration.
    Source code(tar.gz)
    Source code(zip)
  • release-3.0.0(Jan 12, 2016)

    Documenation:

    • http://zend-eventmanager.rtfd.org

    Migration docs:

    • http://zend-eventmanager.readthedocs.org/en/latest/migration/intro/

    Added

    • Migration documentation was added.
    • Automated benchmarks were added.
    • EventManager::__construct() now accepts an optional SharedEventManagerInterface instance as the first argument, and an optional array of identifiers as the second. As identifiers have no meaning without a shared manager present, they are secondary to providing the shared manager.
    • EventManagerInterface::trigger() changes its signature to trigger($eventName, $target = null, $argv = []); each argument has exactly one possible meaning; the $eventName can only be a string event name. The fourth $callback argument is removed.
    • EventManagerInterface::triggerUntil() changes its signature to triggerUntil(callable $callback, $eventName, $target = null, $argv = null). Each argument has exactly one meaning.
    • EventManagerInterface adds two new methods for triggering provided EventInterface arguments: triggerEvent(EventInterface $event) and triggerEventUntil(callable $callback, EventInterface $event).
    • EventManagerInterface::attach() and detach() change their signatures to attach($eventName, callable $listener, $priority = 1) and detach(callable $listener, $eventName = null), respectively. Note that $eventName can now only be a string event name, not an array or Traversable.
    • EventManagerInterface::setIdentifiers() and addIdentifiers() change their signatures to each only accept an array of identifiers.
    • SharedEventManagerInterface::getListeners() changes signature to getListeners(array $identifiers, $eventName) and now guarantees return of an array. Note that the second argument is now required.
    • SharedEventManagerInterface::attach() changes signature to attach($identifier, $eventName, callable $listener, $priority = 1). The $identifier and $eventName must be strings.
    • SharedEventManagerInterface::detach() changes signature to detach(callable $listener, $identifier = null, $eventName = null); $identifier and $eventName must be strings if passed.
    • ListenerAggregateInterface::attach() adds an optional $priority = 1 argument. This was used already in v2, but not dictated by the interface.
    • FilterInterface::attach() and detach() have changed signature to attach(callable $callback) and detach(callable $ilter), respectively.
    • LazyListener allows wrapping:
      • fetching a listener service from a container-interop container, and
      • invoking a designated listener method with the provided event.
    • LazyEventListener extends LazyListener, and provides metadata for discovering the intended event name and priority at which to attach the lazy listener; these are consumed by:
    • LazyListenerAggregate, which, provided a list of LazyEventListeners and/or definitions to use to create them, acts as an aggregate for attaching a number of such listeners at once.
    • #20 updates the trait Zend\EventManager\Test\EventListenerIntrospectionTrait so that the implementation will work with the v3 changes; the tests written for v2 continue to pass, allowing this trait to be used to provide compatibility testing between v2 and v3.

    Deprecated

    • Nothing.

    Removed

    • GlobalEventManager and StaticEventManager are removed (with prejudice!).
    • ProvidesEvents, which was previously deprecated, is removed.
    • EventManagerInterface::setSharedManager() is removed. Shared managers are now expected to be injected during instantiation.
    • EventManagerInterface::getEvents() and getListeners() are removed; they had now purpose within the implementation.
    • EventManagerInterface::setEventClass() was renamed to setEventPrototype(), which now expects an EventInterface instance. That instance will be cloned whenever a new event is created.
    • EventManagerInterface::attachAggregate() and detachAggregate() are removed. Users should use the attach() and detach() methods of the aggregates themselves.
    • SharedEventAggregateAwareInterface and SharedListenerAggregateInterface are removed. This was an undocumented and largely unused feature.
    • SharedEventManagerAwareInterface is removed. A new interface, SharedEventsCapableInterface defines the getSharedManager() method from the interface, and EventManagerInterface extends that new interface.
    • SharedEventManagerInterface::getEvents() is removed, as it had no purpose in the implementation.
    • ResponseCollection::setStopped() no longer implements a fluent interface.

    Fixed

    • FilterIterator::insert() has been modified to raise an exception if the value provided is not a callable.
    Source code(tar.gz)
    Source code(zip)
  • release-2.6.2(Jan 12, 2016)

    Added

    • #19 adds a new trait, Zend\EventManager\Test\EventListenerIntrospectionTrait, intended for composition in unit tests. It provides a number of methods that can be used to retrieve listeners with or without associated priority, and the assertion assertListenerAtPriority(callable $listener, $priority, $event, EventManager $events, $message = ''), which can be used for testing that a listener was registered at the specified priority with the specified event.

      The features in this patch are intended to facilitate testing against both version 2 and version 3 of zend-eventmanager, as it provides a consistent API for retrieving lists of events and listeners between the two versions.

    Deprecated

    • Nothing.

    Removed

    • Nothing.

    Fixed

    • Nothing.
    Source code(tar.gz)
    Source code(zip)
  • release-2.6.1(Dec 8, 2015)

    zend-eventmanager 2.6.1

    This patch release fixes a dependency problem that prevented users to require the component, as the dev-dependency athletic/athletic was required as dev-master (#12).

    Source code(tar.gz)
    Source code(zip)
  • release-2.6.0(Sep 29, 2015)

    This release is geared towards forwards-compatibility with version 3.0. It primarily marks a number of interfaces, classes, and methods as deprecated, but also introduces several features to aid in preparing your code for version 3.

    We recommend reading the migration guide to understand the impact of these changes and what changes you can make to your code base in order to minimize the impact of migration.

    Added

    • Added Zend\EventManager\SharedEventsCapableInterface. This interface will largely replace Zend\EventManager\SharedEventManagerAwareInterface in version 3, and the latter was updated to extend it.
    • Added EventManager::triggerEvent(EventInterface $event) as a forwards-compatibility feature.
    • Add EventManager::triggerEventUntil(callable $callback, EventIterface $event) as a forwards-compatibility feature.
    • Adds Athletic benchmarks to aid in gauging performanc impact of changes; these are a development change only.

    Deprecated

    • Marked GlobalEventManager as deprecated; this class will be removed in version 3.
    • Marked StaticEventManager as deprecated; this class will be removed in version 3.
    • Marked SharedListenerAggregateInterface as deprecated; this interface will be removed in version 3.
    • Marked SharedEventAggregateAwareInterface as deprecated; this interface will be removed in version 3.
    • Marked SharedEventManagerAwareInterface as deprecated; this interface will be removed in version 3.
    • Marked EventManager::setSharedManager() as deprecated; this method will be removed in version 3.
    • Marked EventManager::unsetSharedManager() as deprecated; this method will be removed in version 3.
    • Marked EventManagerInterface:: and EventManager::getEvents() as deprecated; this method will be removed in version 3.
    • Marked EventManagerInterface:: and EventManager::getListeners() as deprecated; this method will be removed in version 3.
    • Marked EventManagerInterface:: and Eventmanager::setEventClass() as deprecated; this method is renamed to setEventPrototype(EventInterface $event) in version 3.
    • Marked EventManagerInterface:: and EventManager::attachAggregate() as deprecated; this method will be removed in version 3.
    • Marked EventManagerInterface:: and EventManager::detachAggregate() as deprecated; this method will be removed in version 3.
    • Marked SharedEventManagerInterface:: and SharedEventManager::getEvents() as deprecated; this method will be removed in version 3.

    Removed

    • Nothing.

    Fixed

    • Nothing.
    Source code(tar.gz)
    Source code(zip)
Owner
Zend Framework
Zend Framework
Php-file-iterator - FilterIterator implementation that filters files based on a list of suffixes, prefixes, and other exclusion criteria.

php-file-iterator Installation You can add this library as a local, per-project dependency to your project using Composer: composer require phpunit/ph

Sebastian Bergmann 7.1k Jan 3, 2023
Reset UI Bookmarks allows admin users to reset their own UI bookmarks such as state of filters, column positions and applied sorting ( e.g Sales > Orders ).

Reset Ui Bookmarks Reset UI Bookmarks becomes an invaluable tool while working daily in the admin panel, especially on Magento® instances with a large

Magenizr 23 Oct 19, 2022
A study of the design, implementation, and management of enterprise information systems.

Enterprise-Architecture A study of the design, implementation, and management of enterprise information systems. Organization: University of North Ala

Look Alive 1 Jan 7, 2022
Xplico is a Network Forensic Analisys Tool NFAT, for Unix and Unix-like operating systems

Xplico is a Network Forensic Analisys Tool NFAT, for Unix and Unix-like operating systems. It uses libpcap, a packet capture and filtering library.

Xplico 158 Dec 31, 2022
The tool converts different error reporting standards for deep compatibility with popular CI systems (TeamCity, IntelliJ IDEA, GitHub Actions, etc).

JBZoo / CI-Report-Converter Why? Installing Using as GitHub Action Example GitHub Action workflow Available Directions Help description in terminal Co

JBZoo Toolbox 17 Jun 16, 2022
Queue Management Systems for LPG vendor agencies of Sri Lanka, for the LPG shortages in 2022

gas-queue-mgt Queue Management Systems for LPG vendor agencies of Sri Lanka, for the LPG shortages in 2022 Installation Requirements PHP 7.4 or later

Madhusanka Goonathilake 14 Oct 18, 2022
YL MVC Structure (PHP MVC) is a pattern made in PHP used to implement user interfaces, data, and controlling logic.

YL MVC Structure (PHP MVC) is a pattern made in PHP used to implement user interfaces, data, and controlling logic. It is built based on the combination of ideas from the Yii framework and Laravel framework (yl).

Tan Nguyen 3 Jan 3, 2023
The only way to implement the pipe operator in PHP.

Pipe Operator in PHP Introduction This package is based on the pipe operator RFC by Sara Golemon and Marcelo Camargo (2016), who explains the problem

BoostPHP 21 Nov 12, 2022
a simple pastebin implement in php

alcohol/pastebin-php I mostly use this demo application to keep myself up to date with the various changes introduced by new (major) Symfony releases.

Rob 26 Nov 5, 2022
Here are few exercises to practice how to implement API Security with NGINX App-Protect WAF.

api-security-lab This repo contains files for customers and partners to practice an API Security with NGINX App-Protect WAF. To demonstrate the capabi

null 4 Mar 30, 2022
Simple class that implement a CAPTCHA for your PHP App.

simple-captcha Simple class that implement a CAPTCHA for your PHP App. Installation Use the package manager composer to install. composer require will

WILLIAM B. SAMPAIO 3 Nov 9, 2022
A light-weight RPC implement of google protobuf RPC framework.

sofa-pbrpc A light-weight RPC implementation of Google's protobuf RPC framework. Wiki: https://github.com/baidu/sofa-pbrpc/wiki Features High performa

Baidu 2.1k Dec 10, 2022
Provides simple interfaces to implement a webhook-based tweeting system

webhook-tweeter This package aims to provide simple interfaces to implement a webhook-based tweeting system. This can, for example, be used to tweet a

Ricardo Boss 2 May 7, 2022
This Pocketmine-MP plugin lets you implement the ultimate birthday wishing system on your server.

BirthdaysPE This Pocketmine-MP plugin will let you wish player(s) a happy birthday and notify others to wish them too. Commands /birthday <set/reset>

MCA7 3 Jul 25, 2022
Implement a "Where's Wally" (or Waldo/Charlie) Captcha in a simple Symfony Project

Where's Wally ? Nowadays, we are often confronted with "Captcha". These tests to know if we are robots or not. They are all very boring and not very f

rherault 3 Oct 7, 2022
A Simplistic Plugin to Implement Server Claims to your Minecraft: Bedrock Server.

Claims This plugin allows administrators to create, edit, list, and teleport to land claims on a PocketMine server. These claims have a variety of cus

Santana 5 Jun 10, 2023
Command and event buses interface and logic.

CoreBus - Command and Event buses interfaces Discrete command bus and domain event dispatcher interfaces for message based architectured projects. Dis

Makina Corpus 0 Feb 7, 2022
True coroutines for PHP>=8.1 without worrying about event loops and callbacks.

Moebius Pure coroutines for PHP 8.1. To promises and callbacks needed. Just pure parallel PHP code inside coroutines. Moebius Band: A loop with only o

Frode Børli 204 Dec 21, 2022
True coroutines for PHP>=8.1 without worrying about event loops and callbacks.

Moebius Pure coroutines for PHP 8.1. No promises and callbacks needed. Just pure parallel PHP code inside coroutines. Moebius Band: A loop with only o

Moebius for PHP 141 Jun 16, 2022