{"action":"created","issue":{"url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/2","labels_url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/2/comments","events_url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/2/events","html_url":"https://github.com/zendframework/zend-servicemanager/pull/2","id":77591591,"number":2,"title":"SM 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":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":51,"created_at":"2015-05-18T10:01:54Z","updated_at":"2015-06-29T18:06:48Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/zendframework/zend-servicemanager/pulls/2","html_url":"https://github.com/zendframework/zend-servicemanager/pull/2","diff_url":"https://github.com/zendframework/zend-servicemanager/pull/2.diff","patch_url":"https://github.com/zendframework/zend-servicemanager/pull/2.patch"},"body":"Hi everyone,\r\n\r\nThis is my first PR for ZF3. It is a direct backport of my PR on ZF2 repo.\r\n\r\nComponent is not yet fully tested but coverage should be already quite good.\r\n\r\nLet me quickly recap the major changes of this refactor.\r\n\r\n## Motivation\r\n\r\nThe initial motivation was to dramatically increase the performance of this core component, while making it easier to use.\r\n\r\nPerformance wise, this refactor is dramatically faster, consume less memory (benchmark can be found on ZF2 repo).\r\n\r\n## Breaking changes\r\n\r\n### Removal of invokables\r\n\r\nThe concept of invokables no longer exist. Now, everything is a factory. This simplifies the creation code by having a single main way to create object.\r\n\r\nA built-in factory to create invokables has been added.\r\n\r\nZF2 code:\r\n\r\n```php\r\nreturn [\r\n\t'invokables' => [\r\n\t\tMyClass::class => MyClass::class\r\n\t]\r\n];\r\n```\r\n\r\nZF3 code:\r\n\r\n```php\r\nreturn [\r\n\t'factories' => [\r\n\t\tMyClass::class => InvokableFactory::class\r\n\t]\r\n]\r\n```\r\n\r\n### Factories are invokables\r\n\r\nTo increase the performance and reduce branching, most interfaces (FactoryInterface, DelegatorFactory, AbstractFactory and Initializer) must be callable by implementing __invoke.\r\n\r\nThis offer a unified way to create objects and dramatically increase performance be removing a lot of instanceof checks.\r\n\r\n### Removal of aliases\r\n\r\nNow that this component is 5.5+, we can take advantage of ::class, and we should encourage this usage.\r\n\r\nThe usage of aliases has been removed from the main service locator where ::class must be encouraged.\r\n\r\nFor plugin managers, a specialized plugin manager that supports alias has been introduced (`AbstractAliasedPluginManager`) while a more efficient, alias-free `AbstractPluginManager` is still here.\r\n\r\n### Ability to map multiple class to same factory\r\n\r\nThis was previously possible in ZF2 but in a hacky way. Now you can map multiple class names to the same factory, and it will be supported by default in the interface signature.\r\n\r\n### Plugin manager change\r\n\r\nPreviously, a plugin manager had a method `getServiceLocator` to retrieve the main service locator.\r\n\r\nThis was confusing for new-comers, and could lead to hard to debug error because the constructor didn't neforce the need to pass a service locator as a dependency.\r\n\r\nNow, what you receive in the factory is ALWAYS the main service manager, not the plugin manager one.\r\n\r\nIf you create an object through a plugin manager in ZF2 and want to retrieve a dependency in the main service locator you had to do:\r\n\r\n```php\r\nclass MyFactory implements FactoryInterface\r\n{\r\n\tpublic function createService(ServiceLocatorInterface $sl)\r\n\t{\r\n\t\t$mainSl = $sl->getServiceLocator();\r\n\t\t\r\n\t\t// ...\r\n\t}\r\n}\r\n```\r\n\r\nIn ZF3:\r\n\r\n```php\r\nclass MyFactory implements FactoryInterface\r\n{\r\n  public function __invoke(ServiceLocatorInterface $sl, $className, array $options = [])\r\n  {\r\n  \t// $sl is already the main service locator.\r\n  \t// If you want a dependency coming from the same plugin manager, you'll need to fetch it first\r\n  \t\r\n  \t$pluginManager = $sl->get(MyPluginManager::class)\r\n  }\r\n}\r\n```\r\n\r\n### Unified way of having options\r\n\r\nIn ZF2 it was very hard to have options on factories. This is made easy now, because each interface have a last parameter called options.\r\n\r\nUsage:\r\n\r\n```php\r\n$object = $sl->get(MyObject::class, ['key' => 'value']);\r\n\r\n// Options are passed as part of the options\r\n```\r\n\r\nThanks"},"comment":{"url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/comments/116779298","html_url":"https://github.com/zendframework/zend-servicemanager/pull/2#issuecomment-116779298","issue_url":"https://api.github.com/repos/zendframework/zend-servicemanager/issues/2","id":116779298,"user":{"login":"weierophinney","id":25943,"avatar_url":"https://avatars.githubusercontent.com/u/25943?v=3","gravatar_id":"","url":"https://api.github.com/users/weierophinney","html_url":"https://github.com/weierophinney","followers_url":"https://api.github.com/users/weierophinney/followers","following_url":"https://api.github.com/users/weierophinney/following{/other_user}","gists_url":"https://api.github.com/users/weierophinney/gists{/gist_id}","starred_url":"https://api.github.com/users/weierophinney/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weierophinney/subscriptions","organizations_url":"https://api.github.com/users/weierophinney/orgs","repos_url":"https://api.github.com/users/weierophinney/repos","events_url":"https://api.github.com/users/weierophinney/events{/privacy}","received_events_url":"https://api.github.com/users/weierophinney/received_events","type":"User","site_admin":false},"created_at":"2015-06-29T18:06:48Z","updated_at":"2015-06-29T18:06:48Z","body":"> To increase the performance and reduce branching, most interfaces (FactoryInterface, DelegatorFactory, AbstractFactory and Initializer) must be callable by implementing `__invoke`.\r\n\r\nDo you mean literally `__invoke()`, or would the code be using the `callable` typehint?\r\n\r\nAlso, what will the defined behavior be if you call something that has different numbers and/or types of arguments? As an example, `DelegatorFactory` accepts:\r\n\r\n```php\r\nfunction (ServiceLocatorInterface $services, $name, $requestedName, callable $callback)\r\n```\r\n\r\nwhile `AbstractFactoryInterface` accepts the same, minus the `$callback` argument, while `FactoryInterface` accepts just a `ServiceLocatorInterface`, and `InitializerInterface` accepts:\r\n\r\n```php\r\nfunction ($instance, ServiceLocatorInterface $services)\r\n```\r\n\r\nCurrently in PHP, calling any of these without one of the required arguments, or calling one with an argument of the wrong type will raise an `E_FATAL` (though in PHP 7, we'll be able to catch on `Error`). What's the plan for reporting errors due to mal-registered factories?\r\n\r\nAdditionally, the `AbstractFactoryInterface` defines two methods, one for creating an instance, but the other for determining if the factory is _capable_ of creating an instance for the named service. How would naming be affected in this case?\r\n\r\nLast but not least: what's the migration plan for people who have implemented these interfaces previously? A script can take care of scanning classes implementing the interfaces and then rename the methods, but I'd like to see that script as part of the proposal. As noted in some other comment threads, one of the things I want to avoid is the migration debacle that was ZF1 -> ZF2. Incremental improvements like this proposal are fantastic, but will be all the better if they help define and automate that migration for users."}}