Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.
This repository was archived by the owner on Jan 30, 2020. It is now read-only.

Element constructor have different behaviours #63

Closed
@Slamdunk

Description

@Slamdunk

Hi, #43 with #45 introduced an unexpected behaviour that I consider a BC break.

All starts by defining a custom element as invokable:

class My\Custom\Element extends Zend\Form\Element
{}

$formElementManager = $this->getServiceLocator()->get('FormElementManager');
$formElementManager->setInvokableClass('my_element', 'My\Custom\Element');

At this point, behavious are different depending on built-in or custom elements, and what I specify as options array key:

// Case 1
$element = $formElementManager->get('my_element', array(
    'name' => 'my_name',
    'my_option' => true,
));
// is equivalent to
$element = new My\Custom\Element(array(
    'name' => 'my_name',
    'my_option' => true,
));

var_dump(gettype($element->getName())); // array

Because it's handled by https://github.com/zendframework/zend-servicemanager/blob/release-2.7.6/src/AbstractPluginManager.php#L254

// Case 2
$element = $formElementManager->get('Zend\Form\Element', array(
    'name' => 'my_name',
    'my_option' => true,
));
// is equivalent to
$element = new Zend\Form\Element('my_name', array(
    'name' => 'my_name',
    'my_option' => true,
));

var_dump(gettype($element->getName())); // string

Because it's handled by https://github.com/zendframework/zend-form/blob/release-2.8.1/src/ElementFactory.php#L69

And this is going even harder to understand if we use the Zend\Form\Factory

// Case 3
$factory = new Zend\Form\Factory($formElementManager);
$element = $factory->create(array(
    'type' => 'my_element',
    'name' => 'my_name',
    'options' => array(
        'my_option' => true,
    ),
));
// is equivalent to
$element = new My\Custom\Element(array(
    'my_option' => true,
));
$element->setName('my_name');
$element->setOptions(array(
    'my_option' => true,
));

var_dump(gettype($element->getName())); // string

Because of https://github.com/zendframework/zend-form/blob/release-2.8.1/src/Factory.php#L204-L214

// Case 4
$factory = new Zend\Form\Factory($formElementManager);
$element = $factory->create(array(
    'type' => 'Zend\Form\Element',
    'name' => 'my_name',
    'options' => array(
        'my_option' => true,
    ),
));
// is equivalent to
$element = new Zend\Form\Element('element', array(
    'my_option' => true,
));
$element->setName('my_name');
$element->setOptions(array(
    'my_option' => true,
));

var_dump(gettype($element->getName())); // string

Without name

// Case 5
$factory = new Zend\Form\Factory($formElementManager);
$element = $factory->create(array(
    'type' => 'my_element',
    'options' => array(
        'my_option' => true,
    ),
));
// is equivalent to
$element = new My\Custom\Element(array(
    'my_option' => true,
));
$element->setOptions(array(
    'my_option' => true,
));

var_dump(gettype($element->getName())); // array

// Case 6
$factory = new Zend\Form\Factory($formElementManager);
$element = $factory->create(array(
    'type' => 'Zend\Form\Element',
    'options' => array(
        'my_option' => true,
    ),
));
// is equivalent to
$element = new Zend\Form\Element('element', array(
    'my_option' => true,
));
$element->setOptions(array(
    'my_option' => true,
));

var_dump(gettype($element->getName())); // string

The transition fro SMv2 to SMv3 is irrelevant, because Zend\ServiceManager\Factory\InvokableFactory acts exactly like Zend\ServiceManager\AbstractPluginManager::createFromInvokable.

The sources of misunderstanding are:

  1. The name acts differently in different contexts, inside or outside options, through Factory or not
  2. Built-in plugins use ElementFactory to face [1], while custom invokables are instantiated naturally
  3. The constructor may behave weird when inherited by Zend\Form\Element and not overwritten

The fact is, if name and options are required, the constructor must be declared in the interface.
If name and options are optional, Zend\Form\Element must not have a constructor at all.

In my humble opinion, we should decide to be clear about this topic, and:

  1. Remove the constructor from Zend\Form\Element
  2. Lazy-load Zend\Form\Fieldset::$iterator [PriorityList]
  3. Remove the new Zend\Form\ElementFactory (in favour of createFromInvokable or InvokableFactory depending on SM version)
  4. Document that FormElementManager only instantiate the object, and configuration must be done with the Factory

So

  1. Built-in elements will behave like always
  2. Custom elements extended by Zend\Form\Element without constructor will behave like build-in elements
  3. Custom elements extended by Zend\Form\Element with constructor will receive options array
  4. Custom elements implementing Zend\Form\ElementInterface with constructor will receive options array

With no more array-name.

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions