{"action":"created","issue":{"url":"https://api.github.com/repos/zendframework/zend-form/issues/63","repository_url":"https://api.github.com/repos/zendframework/zend-form","labels_url":"https://api.github.com/repos/zendframework/zend-form/issues/63/labels{/name}","comments_url":"https://api.github.com/repos/zendframework/zend-form/issues/63/comments","events_url":"https://api.github.com/repos/zendframework/zend-form/issues/63/events","html_url":"https://github.com/zendframework/zend-form/issues/63","id":151636151,"number":63,"title":"Element constructor have different behaviours","user":{"login":"Slamdunk","id":152236,"avatar_url":"https://avatars.githubusercontent.com/u/152236?v=3","gravatar_id":"","url":"https://api.github.com/users/Slamdunk","html_url":"https://github.com/Slamdunk","followers_url":"https://api.github.com/users/Slamdunk/followers","following_url":"https://api.github.com/users/Slamdunk/following{/other_user}","gists_url":"https://api.github.com/users/Slamdunk/gists{/gist_id}","starred_url":"https://api.github.com/users/Slamdunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Slamdunk/subscriptions","organizations_url":"https://api.github.com/users/Slamdunk/orgs","repos_url":"https://api.github.com/users/Slamdunk/repos","events_url":"https://api.github.com/users/Slamdunk/events{/privacy}","received_events_url":"https://api.github.com/users/Slamdunk/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-04-28T13:05:03Z","updated_at":"2016-04-29T06:30:02Z","closed_at":null,"body":"Hi, https://github.com/zendframework/zend-form/pull/43 with https://github.com/zendframework/zend-form/pull/45 introduced an unexpected behaviour that I consider a BC break.\r\n\r\nAll starts by defining a custom element as `invokable`:\r\n```php\r\nclass My\\Custom\\Element extends Zend\\Form\\Element\r\n{}\r\n\r\n$formElementManager = $this->getServiceLocator()->get('FormElementManager');\r\n$formElementManager->setInvokableClass('my_element', 'My\\Custom\\Element');\r\n```\r\nAt this point, behavious are different depending on built-in or custom elements, and what I specify as `options` array key:\r\n```php\r\n// Case 1\r\n$element = $formElementManager->get('my_element', array(\r\n    'name' => 'my_name',\r\n    'my_option' => true,\r\n));\r\n// is equivalent to\r\n$element = new My\\Custom\\Element(array(\r\n    'name' => 'my_name',\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // array\r\n```\r\nBecause it's handled by https://github.com/zendframework/zend-servicemanager/blob/release-2.7.6/src/AbstractPluginManager.php#L254\r\n\r\n```php\r\n// Case 2\r\n$element = $formElementManager->get('Zend\\Form\\Element', array(\r\n    'name' => 'my_name',\r\n    'my_option' => true,\r\n));\r\n// is equivalent to\r\n$element = new Zend\\Form\\Element('my_name', array(\r\n    'name' => 'my_name',\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // string\r\n```\r\nBecause it's handled by https://github.com/zendframework/zend-form/blob/release-2.8.1/src/ElementFactory.php#L69\r\n\r\nAnd this is going even harder to understand if we use the `Zend\\Form\\Factory`\r\n```php\r\n// Case 3\r\n$factory = new Zend\\Form\\Factory($formElementManager);\r\n$element = $factory->create(array(\r\n    'type' => 'my_element',\r\n    'name' => 'my_name',\r\n    'options' => array(\r\n        'my_option' => true,\r\n    ),\r\n));\r\n// is equivalent to\r\n$element = new My\\Custom\\Element(array(\r\n    'my_option' => true,\r\n));\r\n$element->setName('my_name');\r\n$element->setOptions(array(\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // string\r\n```\r\nBecause of https://github.com/zendframework/zend-form/blob/release-2.8.1/src/Factory.php#L204-L214\r\n```php\r\n// Case 4\r\n$factory = new Zend\\Form\\Factory($formElementManager);\r\n$element = $factory->create(array(\r\n    'type' => 'Zend\\Form\\Element',\r\n    'name' => 'my_name',\r\n    'options' => array(\r\n        'my_option' => true,\r\n    ),\r\n));\r\n// is equivalent to\r\n$element = new Zend\\Form\\Element('element', array(\r\n    'my_option' => true,\r\n));\r\n$element->setName('my_name');\r\n$element->setOptions(array(\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // string\r\n```\r\nWithout name\r\n```php\r\n// Case 5\r\n$factory = new Zend\\Form\\Factory($formElementManager);\r\n$element = $factory->create(array(\r\n    'type' => 'my_element',\r\n    'options' => array(\r\n        'my_option' => true,\r\n    ),\r\n));\r\n// is equivalent to\r\n$element = new My\\Custom\\Element(array(\r\n    'my_option' => true,\r\n));\r\n$element->setOptions(array(\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // array\r\n\r\n// Case 6\r\n$factory = new Zend\\Form\\Factory($formElementManager);\r\n$element = $factory->create(array(\r\n    'type' => 'Zend\\Form\\Element',\r\n    'options' => array(\r\n        'my_option' => true,\r\n    ),\r\n));\r\n// is equivalent to\r\n$element = new Zend\\Form\\Element('element', array(\r\n    'my_option' => true,\r\n));\r\n$element->setOptions(array(\r\n    'my_option' => true,\r\n));\r\n\r\nvar_dump(gettype($element->getName())); // string\r\n```\r\n\r\nThe transition fro SMv2 to SMv3 is irrelevant, because `Zend\\ServiceManager\\Factory\\InvokableFactory` acts exactly like `Zend\\ServiceManager\\AbstractPluginManager::createFromInvokable`.\r\n\r\nThe sources of misunderstanding are:\r\n\r\n1. The `name` acts differently in different contexts, inside or outside `options`, through Factory or not\r\n2. Built-in plugins use `ElementFactory` to face [1], while custom invokables are instantiated naturally\r\n3. The constructor may behave weird when inherited by `Zend\\Form\\Element` and not overwritten\r\n\r\nThe fact is, if name and options are required, the constructor must be declared in the interface.\r\nIf name and options are optional, `Zend\\Form\\Element` must not have a constructor at all.\r\n\r\nIn my humble opinion, we should decide to be clear about this topic, and:\r\n\r\n1. Remove the constructor from `Zend\\Form\\Element`\r\n2. Lazy-load `Zend\\Form\\Fieldset::$iterator` [PriorityList]\r\n3. Remove the new `Zend\\Form\\ElementFactory` (in favour of `createFromInvokable` or `InvokableFactory` depending on SM version)\r\n4. Document that FormElementManager only instantiate the object, and configuration must be done with the Factory\r\n\r\nSo\r\n\r\n1. Built-in elements will behave like always\r\n2. Custom elements extended by `Zend\\Form\\Element` without constructor will behave like build-in elements\r\n3. Custom elements extended by `Zend\\Form\\Element` with constructor will receive `options` array\r\n3. Custom elements implementing `Zend\\Form\\ElementInterface` with constructor will receive `options` array\r\n\r\nWith no more array-name.\r\n\r\nWhat do you think?\r\n"},"comment":{"url":"https://api.github.com/repos/zendframework/zend-form/issues/comments/215637108","html_url":"https://github.com/zendframework/zend-form/issues/63#issuecomment-215637108","issue_url":"https://api.github.com/repos/zendframework/zend-form/issues/63","id":215637108,"user":{"login":"Slamdunk","id":152236,"avatar_url":"https://avatars.githubusercontent.com/u/152236?v=3","gravatar_id":"","url":"https://api.github.com/users/Slamdunk","html_url":"https://github.com/Slamdunk","followers_url":"https://api.github.com/users/Slamdunk/followers","following_url":"https://api.github.com/users/Slamdunk/following{/other_user}","gists_url":"https://api.github.com/users/Slamdunk/gists{/gist_id}","starred_url":"https://api.github.com/users/Slamdunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Slamdunk/subscriptions","organizations_url":"https://api.github.com/users/Slamdunk/orgs","repos_url":"https://api.github.com/users/Slamdunk/repos","events_url":"https://api.github.com/users/Slamdunk/events{/privacy}","received_events_url":"https://api.github.com/users/Slamdunk/received_events","type":"User","site_admin":false},"created_at":"2016-04-29T06:30:02Z","updated_at":"2016-04-29T06:30:02Z","body":"> Under v2, if you add an invokable mapping for `'foo' => 'Zend\\Form\\Element'`, an instance of the latter will be created, and mapped to the service 'foo'. However, with v3, mapping `'foo' => InvokableFactory::class` _will fail_\r\n\r\nWhy should you change the map? In v3, if you keep the v2 `'foo' => 'Zend\\Form\\Element'` within the invokables, you have the exact behaviour you explained, as far as I can read in the [SM migration guide](http://zendframework.github.io/zend-servicemanager/migration/#invokables).\r\n\r\nI read your https://github.com/zendframework/zend-form/pull/65 and the main issue is still there. I'll try to simplify the topic.\r\n\r\nIf\r\n\r\n1. You have a custom element\r\n2. You do not specify the `name`\r\n3. You do not use the new `ElementFactory`\r\n\r\nThe result is a mess because you have an `array` as the element name. How https://github.com/zendframework/zend-form/pull/65 helps with this scenario?"}}