{"action":"created","issue":{"url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/2","labels_url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/2/comments","events_url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/2/events","html_url":"https://github.com/zendframework/zend-eventmanager/pull/2","id":77764959,"number":2,"title":"Refactor","user":{"login":"bakura10","id":1198915,"avatar_url":"https://avatars.githubusercontent.com/u/1198915?v=3","gravatar_id":"","url":"https://api.github.com/users/bakura10","html_url":"https://github.com/bakura10","followers_url":"https://api.github.com/users/bakura10/followers","following_url":"https://api.github.com/users/bakura10/following{/other_user}","gists_url":"https://api.github.com/users/bakura10/gists{/gist_id}","starred_url":"https://api.github.com/users/bakura10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bakura10/subscriptions","organizations_url":"https://api.github.com/users/bakura10/orgs","repos_url":"https://api.github.com/users/bakura10/repos","events_url":"https://api.github.com/users/bakura10/events{/privacy}","received_events_url":"https://api.github.com/users/bakura10/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/zendframework/zend-eventmanager/labels/awaiting%20author%20updates","name":"awaiting author updates","color":"e11d21"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":30,"created_at":"2015-05-18T18:54:14Z","updated_at":"2015-07-03T19:34:22Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/zendframework/zend-eventmanager/pulls/2","html_url":"https://github.com/zendframework/zend-eventmanager/pull/2","diff_url":"https://github.com/zendframework/zend-eventmanager/pull/2.diff","patch_url":"https://github.com/zendframework/zend-eventmanager/pull/2.patch"},"body":"Hi everyone,\r\n\r\nPS: Everyone is free to fork my branch and submit PR to it!\r\n\r\n## Motivation\r\n\r\nEvent 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.\r\n\r\nAlso, the event manager was a complex piece of software with a lot of obscure features, a strange shared manager concept and complex signatures.\r\n\r\n## Breaking changes\r\n\r\n### Massive performance improvements\r\n\r\nI 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):\r\n\r\nZF2 EVM\r\n\r\nPHP 5.6\r\n\r\n    Bench\\EventManagerBenchmark\r\n    Method Name          Iterations    Average Time      Ops/second\r\n    ------------------  ------------  --------------    -------------\r\n    attach20Listeners : [1,000     ] [0.0005693485737] [1,756.39326]\r\n    trigger20Listeners: [1,000     ] [0.0010452888012] [956.67341]\r\n\r\n\r\nPHP 7\r\n\r\n    Bench\\EventManagerBenchmark\r\n    Method Name          Iterations    Average Time      Ops/second\r\n    ------------------  ------------  --------------    -------------\r\n    attach20Listeners : [1,000     ] [0.0000875046253] [11,427.96734]\r\n    trigger20Listeners: [1,000     ] [0.0001518340111] [6,586.13965]\r\n\r\n\r\n\r\nZF3 EVM\r\n\r\nPHP 5.6\r\n\r\n    Bench\\EventManagerBenchmark\r\n    Method Name          Iterations    Average Time      Ops/second\r\n    ------------------  ------------  --------------    -------------\r\n    attach20Listeners : [1,000     ] [0.0001252155304] [7,986.22980]\r\n    trigger20Listeners: [1,000     ] [0.0002773256302] [3,605.86939]\r\n\r\nPHP 7\r\n\r\n    Bench\\EventManagerBenchmark\r\n    Method Name          Iterations    Average Time      Ops/second\r\n    ------------------  ------------  --------------    -------------\r\n    attach20Listeners : [1,000     ] [0.0000128707886] [77,695.31713]\r\n    trigger20Listeners: [1,000     ] [0.0000429670811] [23,273.63123]\r\n    \r\n    \r\n### No more shared event manager\r\n\r\nI'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).\r\n\r\nAs 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.\r\n\r\n### Simplifications\r\n\r\nPreviously, 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).\r\n\r\n### Event no longer has name\r\n\r\nI'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:\r\n\r\n```php\r\n$evm->trigger('event.name', $eventObject);\r\n```\r\n\r\nAs 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).\r\n\r\n`propagationIsStopped` has been renamed to `isPropagationStopped`.    \r\n\r\n### Lazy listeners\r\n\r\nOne 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.\r\n\r\nFrom my app, this can add a non-negligible overhead, and solutions like proxies are annoying to setup.\r\n\r\n\r\n\r\nSome example usage:\r\n\r\n```php\r\n<?php\r\n    // The idea is that in ZF3, there will be one \"application-wide\" event manager registered. Nothing prevents you to\r\n    // create your own local event manager, but to replicate the shared manager behaviour, you should retrieve the\r\n    // application-wide evm\r\n\r\n    // Attaching a simple listener:\r\n\r\n    $eventManager = new EventManager();\r\n    $eventManager->attach('foo', function() {}, $priority);\r\n\r\n    // Triggering a listener\r\n    $eventManager->trigger('foo', new Event());\r\n\r\n    // Triggering a listener with a callable that will stop propagation if return to true\r\n    $eventManager->triggerUntil('foo', new Event(), function() {});\r\n\r\n    // Attaching to wildcard\r\n    $eventManager->attach('*', function() {});\r\n\r\n    // Attaching a listener and detaching it\r\n    $listener = $eventManager->attach('foo', function() {});\r\n    $eventManager->detach('foo', $listener);\r\n\r\n    // Detaching all listeners from a given event\r\n    $eventManager->detach('foo');\r\n\r\n\r\n    // Using listener aggregate to aggregate multiple events, lazily\r\n    class MyListenerAggregate implements ListenerAggregateInterface\r\n    {\r\n        public static function attachAggregate(EventManager $evm)\r\n        {\r\n            // No more confusion of getting the shared manager. Note the method is static, and\r\n            // that we pass a \"spec\" without instantiating the listener, as it will be created only\r\n            // on demand\r\n            $evm->attach('foo', [static::class, 'myMethod']);\r\n        }\r\n\r\n        public function myMethod(Event $event)\r\n        {\r\n\r\n        }\r\n    }\r\n\r\n    // In the module class\r\n    $evm = $serviceLocator->get(EventManager::class);\r\n    MyListenerAggregate::attachAggregate($evm); // Maybe should be called \"bind\" instad of attachAggregate\r\n\r\n    // No more identifiers, no more SEM... Of course, events must now be scoped, so no \"created\" event anymore,\r\n    // but \"user.created\", \"application.dispatch\", \"controller.dispatch\"...\r\n\r\n?>\r\n```"},"comment":{"url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/comments/118411893","html_url":"https://github.com/zendframework/zend-eventmanager/pull/2#issuecomment-118411893","issue_url":"https://api.github.com/repos/zendframework/zend-eventmanager/issues/2","id":118411893,"user":{"login":"bakura10","id":1198915,"avatar_url":"https://avatars.githubusercontent.com/u/1198915?v=3","gravatar_id":"","url":"https://api.github.com/users/bakura10","html_url":"https://github.com/bakura10","followers_url":"https://api.github.com/users/bakura10/followers","following_url":"https://api.github.com/users/bakura10/following{/other_user}","gists_url":"https://api.github.com/users/bakura10/gists{/gist_id}","starred_url":"https://api.github.com/users/bakura10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bakura10/subscriptions","organizations_url":"https://api.github.com/users/bakura10/orgs","repos_url":"https://api.github.com/users/bakura10/repos","events_url":"https://api.github.com/users/bakura10/events{/privacy}","received_events_url":"https://api.github.com/users/bakura10/received_events","type":"User","site_admin":false},"created_at":"2015-07-03T19:34:22Z","updated_at":"2015-07-03T19:34:22Z","body":"@weierophinney , I'm just trying to **understand** the use case. Indeed, from the few talks I have given and the discussion I had, most people found this one confusign because indeed, they were ALL using the SEM only, giving therefore the feeling that if the common use case must be solved using the complex method, then the common use case should instead be solved using the simple method, and the complex method using the complex method. That's why I'm advocating for making the EVM shared by default, so that the expected, logical and common behaviour can be achieved without the SEM (that means making the main EVM shared).\r\n\r\nNow, I could understand that the SEM could have an interest.\r\n\r\n> In the current incarnation, any controller can trigger the dispatch event; however, only instances of the EM that have the ZF\\Rest\\RestController identifier will trigger the listener specified above. That's how identifiers work; they are used by the EM to determine which listeners in the shared manager to invoke. Essentially, the identifier system is a filtering system for determining which listeners are relevant to the trigger context.\r\n\r\nSo why not using a new event like \"rest_mvc.dispatch\"? If events are properly namespaced, your use case can be better solved using namespaced event names. If you attach an event for the dispatch event JUST for EVM that has a specific listeners, then to my understanding, you should simply use a new event, like \"apigility.mvc.dispatch\". If you want to hook to the global mvc.dispatch, you can do so. This solve the use case much more elegantly, because attaching a listener from third party code no longer require the fragile logic of identifiers, but instead rely on simple event names, that can be documented more easily.\r\n\r\nOnce again, my main problem with identifier is the fragility. Currently, identifiers are set automatically using the class name. Moving the event manager that trigger the event from one class to another automatically modify the identifier.\r\n\r\nFor your specific use case, I think that tags would be be a better solution:\r\n\r\n```php\r\n$evm->attach('dispatch', $callable, 'my_tag');\r\n```\r\n\r\nThe tag seems to be a better alternative, because the tag is like an \"identifier\" but is indepdnant of the calling class."}}