Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[]]></ac:plain-text-body></ac:macro>
<h1>RFC: better configuration for components</h1>
<ac:macro ac:name="note"><ac:parameter ac:name="title">RFC Status: For discussion</ac:parameter><ac:rich-text-body>
<p>This RFC is currently under discussion and is not complete.<br />
Please comment on this RFC on the mailing list.</p></ac:rich-text-body></ac:macro>
<ac:macro ac:name="info"><ac:rich-text-body>
<p>This RFC is an alternate approach (counter-RFC) to <a href="http://framework.zend.com/wiki/display/ZFDEV2/RFC+-+Object+instantiation+and+configuration">Ralph's RFC</a>.</p></ac:rich-text-body></ac:macro>

<h2>TOC</h2>
<ac:macro ac:name="toc"><ac:parameter ac:name="minlevel">2</ac:parameter></ac:macro>

<h2>Introduction</h2>
<p>Currently, most ZF1/ZF2 components are instantiated using one of the following styles:</p>

<h4>A constructor that consumes an array of config options </h4>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$f = new Zend\Filter\Compress(array( ... ) );
]]></ac:plain-text-body></ac:macro>
<h4>A constructor that consumes 2 or more arrays for configuration:</h4>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$f = new Zend\Filter\InputFilter(
$filters,
$validators,
$data,
$options
;
]]></ac:plain-text-body></ac:macro>
<h4>A cocktail of scalars, arrays and objects</h4>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$p = new Zend\Mail\Protocol\Smtp(
'127.0.0.1',
null,
array( 'auth' => 'login')
;

Zend/Form/DisplayGroup::__construct(
$name,
PrefixPathMapper $loader,
$options = null
);
]]></ac:plain-text-body></ac:macro>
<h4>Any number of setters and getters for config options</h4>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$application->setOptions(array( ... ) );
$application->hasOption('foo');
$db->setAdapter($adapter);
$db->setParams(array( ... ));
$alnumFilter->setAllowWhiteSpace(true);
]]></ac:plain-text-body></ac:macro>
<h4>"Deaf" constructor (no parameters accepted) + setters</h4>
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$amfServer = new Zend\Amf\Server();
$amfServer->setAuthService($auth);
$amfServer->setProduction(1);
$amfServer->setBroker($broker);
]]></ac:plain-text-body></ac:macro>

<p>The purpose of this thread is to try and normalize, simplify and unify the way we components in Zend Framework. This is becoming more important each as more and more components are ported to ZF2. We have also agreed on some form of public invitation for 3rd party developers to contribute additional components. A simplified, unified configuration style will help the framework cope with that.</p>

<h2>Problems with current approach (ZF1)</h2>
<ol>
<li><strong>What config params can I use?</strong></li>
</ol>

<p>An array() is convenient for passing large number of named parameters but because it is weakly-typed (i.e. array can hold any keys and values), the developer does not know what options are accepted, required or optional. What should happen with unrecognized values? Currently, they are usually ignored which introduces confusion for the developer. There are also inconsistencies in how configuration keys are named, and how even their associated getters and setters (should then exist) are named. This increases the confusion by making it much harder to quickly intuit the correct configuration key name.</p>

<ol>
<li><strong>Code duplication and multiple class responsibilities</strong></li>
</ol>

<p>For certain objects, the work is done by a network of helper objects within the same class hierarchy which may themselves be independently usable. Should any of these require bits of the configuration, the approaches can lead to duplication of getters, setters, validation logic and inconsistent methods of transporting configuration across the object boundaries. Where this imposes on one class the responsibility of managing configuration of other classes, it adds another responsibility which is in breach of the Single Responsibility Principle. Using a core Config object centralises all the configuration values, methods and operating logic in a single transportable unit.</p>

<ol>
<li><strong>Array performance</strong></li>
</ol>

<p>Array parsing and validation requires looping over its contents and processing all values. This is performed any number of different ways across components creating another point of inconsistent behaviour.</p>

<ol>
<li><strong>Validation results inconsistencies</strong></li>
</ol>

<p>Sometimes components throw an exception directly form constructor, sometimes during operation (when lazy-loading), sometimes they will just silently fail (return false?). Also - getters do not always cover all configuration parameters. The result is that users have no particular guarantee that configuration is validated. In many cases, a bad configuration value is first communicating to the user when some basic PHP operation fails. This is poor practice. Objects should locate problems BEFORE calling PHP functions and class methods. It ensures rapid debugging of faulty configuration values with a helpful Exception and reduces the change of a faulty configuration value ending up in a Fatal Error scenario unexpectedly.</p>

<ol>
<li><strong>Terminology inconsistencies</strong></li>
</ol>

<p>There are several different terms used in various components to describe configuration:</p>
<ul>
<li><em>options</em></li>
<li><em>parameters</em></li>
<li><em>properties</em></li>
<li><em>preferences</em></li>
</ul>

<ol>
<li><strong>Configuration key naming</strong></li>
</ol>

<p>At present, while discussed previously, configuration keys are not always named consistently. A core Config object could implement a simple convention based approach in that it could map configuration keys to getter/setter method names in one consistent fashion. This allows configuration keys to be driven by getter/setter naming and actively discourage random key naming which makes intuitive key selection difficult.</p>

<h2>Goals:</h2>
<p>Main goals of this proposal are:</p>
<ol>
<li>To decouple configuration from component functionality (adheres to separation of concerns, single responsibility principle, law of demeter)</li>
<li>To unify the way we configure all components (common standard).</li>
<li>To decrease learning curve.</li>
<li>To make configuration more robust and explicit.</li>
<li>To make configuration more IDE friendly.</li>
</ol>

<h2>Non-goals</h2>
<p>This proposal is <strong>NOT</strong> meant to :</p>
<ol>
<li>increase performance (in some scenarios it will, but that's not the goal)</li>
<li>decrease code verbosity</li>
<li>force better coding standards per se</li>
<li>deprecate all <code>__constructors</code> and their parameters in all classes</li>
</ol>

<ac:macro ac:name="info"><ac:rich-text-body>
<p>This is a proposal for configuring components' main classes - those directly exposed to the end-user. It is not intended to be applied to all possible classes, though sub units such as adapters/plugins/helpers <strong>should</strong> accept a configuration object, where necessary, rather than altering the transport mechanism needlessly (e.g. calling a list of setters or transferring config to an array).</p></ac:rich-text-body></ac:macro>

<h2>Using an object as a replacement for array</h2>

<p>Let's consider using simple objects as a configuration containers. Here's a very simple example:</p>

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

]]></ac:plain-text-body></ac:macro>

<h4>Pros</h4>
<ul>
<li>all before</li>
<li>we can use array and any Iterable object for configuration (including Zend\Config)</li>
<li>we can use any of the 3 config styles, fully BC.</li>
</ul>

<h2>DI, lazy-loading and instantiating depending classes</h2>
<p>Because we are internally using setters, we can instantiate depending objects using any method imaginable.<br />
Let's modify our configuration to digest a <code>Zend\Filter</code>. Let's also try to use DI to set up some defaults.</p>

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

]]></ac:plain-text-body></ac:macro>

<h4>Pros</h4>
<ul>
<li>DI is a great candidate to handle sub-instances, loading, both run-time and global configuration (defaults)</li>
<li>DI could be modified to "understand" config objects and read their parameters</li>
<li>it is easy to implement things like optional/required parameters</li>
<li>lazy-loading</li>
</ul>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.