Skip to end of metadata
Go to start of metadata

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

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

Zend Framework: Zend_Application_Resource Component Proposal

Proposed Component Name Zend_Application_Resource
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Application_Resource
Proposers Bill Karwin
Revision 1.1 - 13 August 2007: Initial proposal. (wiki revision: 6)

Table of Contents

1. Overview

This is a proposal for configuration and instantiation of application-wide resources such as database connection, authentication, cache, log, and others.

The intended use cases of this solution are (1) to enable developers or tools declare some types of objects for their application in a configuration file or an array, and (2) to create concrete instances of resource classes declared in a configuration file or array, as part of an application's bootstrap logic.

2. References

  • tbd

3. Component Requirements, Constraints, and Acceptance Criteria

  • The component will manage a collection of application resources.
  • The component will allow a new resource to be added to the collection.
  • The component will provide manager classes for a number of common resource types in Zend Framework.
  • The component will provide an abstract class for resource type managers.
  • The component will support class name prefixes so custom resource classes can be implemented in class heirarchies other than Zend_Application_Resource.
  • The component will provide methods to create instances of one or all managed resource classes.
  • The component will save instances of managed classes in the Zend_Registry, unless requested not to do so.
  • The resource classes will support Zend_Config and interpret a Zend_Config object to use when instantiating a managed class.
  • The resource classes will define conventions for recognized config properties.
  • The resource classes will support a default configuration for their managed class.
  • The resource classes will return the final config object used to instantiate its managed class.

4. Dependencies on Other Framework Components

  • Zend_Config
  • Zend_Exception
  • Zend_Loader
  • Zend_Registry

5. Theory of Operation

We use the term resource in this proposal to mean an instance of a Zend Framework component that is likely to be used in an application-wide manner. It is recommended to create resources in the bootstrap script of an MVC application, to make these objects available globally. We can achieve this by storing the objects in the Zend_Registry. It may be convenient to create the resource objects based on values declared in an application's configuration file.

The Zend_Application_Resource component functions as a collection of resource specifications, and also as a factory for resources based on those specifications.

Specifying resources

You can add resource specifications to an object of the Zend_Application_Resource class, with the set($id, $type, $config) method. The first argument is an identifier for the resource.

The second argument is a string that names the type of resource. A class of the name Zend_Application_Resource_$type should exist, where $type is the string containing the type argument, transformed to initial-capital format. The class must be a subclass of Zend_Application_Resource_Abstract.

The third argument is an optional Zend_Config object. This is passed to the constructor of the resource-type class, and that class is responsible for interpreting the config object. The config is used during instantiation or configuration of the corresponding object.

If no class can be found corresponding to the type argument, the set() method throws an exception. There is no restriction on the key format, except that it must be a string legal to use as a key in a PHP associative array. But see below for the convention of key format used in configuration file processing.

Using configuration files

The Zend_Application_Resource class also has a method setConfig($config), which iterates recursively through its Zend_Config argument and finds entries that correspond to resource plugin classes. A key must match the pattern of two strings separated by a dash (e.g. "foo-1").

The first part of this pattern corresponds to a class name for a resource manager class. The class must exist and be loadable with the current application's include_path. For example, if the key is "foo-1", then the resource manager class is Zend_Application_Resource_Foo. The setConfig() method calls $this->set('foo-1', 'foo', $subConfig), where $subConfig is the subtree of the config heirarchy rooted at the current config entry.

The second part of the key is arbitrary and serves as a way to identify resources uniquely. This part of the key is not required to be numeric, though the examples in this proposal show it as numeric. It can be virtually any string of characters except for whitespace, '=' or '.'.

The dash character is used as a separator because this is permitted in configuration files but it is not a normal part of a PHP class identifier.

config.ini:

Code:

You can also pass the $config argument to the constructor. This processes the config object in an identical way to using the setConfig() method.

Instantiating resources

After your resource object has all the resource specifications you need, you can use the create() method or createAll() method to request that the resource specifications be used to instantiate resource objects. The create() method accepts a string argument that names the key of one resource.

This calls the create() method in the corresponding resource manager class, which is responsible for interpreting the Zend_Config object and using it to create an instance of the proper concrete object. Thus the config properties recognized must be defined and documented by convention for each managed class.

The createAll() method iterates over all known resources keys and creates them all.

By default, the create() and createAll() methods store the created objects in the Zend_Registry, using the keys by which the resources are identified.

Each method accepts an optional boolean argument. If this argument is false, the objects are created without saving them in the registry.

You can also fetch the created resource object with the get($key) method.

Configuration file life cycle

You can load a config file, add a new resource, and return the config object containing the previous data combined with the new resource entry. Use the getConfig() method to retrieve the Zend_Config object.

The Zend_Config object returned should be of the same class type as the original config object used to initialize the resource data. If no initial config object was given, then the default class type is Zend_Config_Ini.

A Zend_Config object should have some method to render its data as a config file in the appropriate format. Each subclass of Zend_Config should implement this method. That is, Zend_Config_Ini::dump() returns a string that is the content of a config.ini file, and Zend_Config_Xml::dump() returns a string that is the content of a config.xml file. In the case of the base Zend_Config class, the dump() method might be a synonym for toArray().

Proposed resource wrappers for Zend Framework components

Some components in Zend Framework are natural choices for creating Zend_Application_Resource wrapper classes to instantiate them based on config data. The scope of this proposal includes defining config property conventions and implementing resource classes for the following Zend Framework components:

  • Zend_Acl
  • Zend_Cache
  • Zend_Controller_Front
  • Zend_Db
  • Zend_Filter_Input
  • Zend_Http_Client
  • Zend_Log
  • Zend_Translate

Other Zend Framework component may be given resource wrapper classes in the future.

Creating custom resource adapters

A new resource type "bar" can be created in a class Zend_Application_Resource_Bar, which extends the abstract class Zend_Application_Resource_Abstract.

You can also define the class in another class prefix, and then declare the namespace to Zend_Application_Resource with the addNamespace($namespace) method.

Supporting module-oriented MVC applications

Zend Framework's MVC architecture includes support for modules which are reusable sets of application functionality, complete with controllers, models, and views. Since these modules are reusable, they should be easy to drop into a modular MVC application without conflicting with other modules.

The solution here is to create a new Zend_Registry object for each module, and store this registry as an object in the singleton instance of Zend_Registry. Then use the sub-registry object with Zend_Application_Resource to store objects with confidence that the key names won't conflict with key names used by another module.

Zend_Application_Resource has a method setRegistry(Zend_Registry $registry) to support this. You can specify the registry object in which managed resources will be saved. If you don't specify a registry object, it defaults to use the singleton instance returned by Zend_Registry::getInstance().

It is recommended that an application module create a new registry object and save it in the singleton registry using the name of the module as the key. Then use the new registry object with the Zend_Application_Resource::setRegistry() method, so that subsequent objects are saved in this new registry.

In a module's application code, you need to use one extra layer of indirection to retrieve objects saved in that module's respective registry.

6. Milestones / Tasks

Milestone 1: [DONE] Publish proposal.
Milestone 2: Revise proposal, approve for Incubator development.
Milestone 3: Commit working prototype to Incubator.
Milestone 4: Commit working unit tests.
Milestone 5: Write end-user documentation.
Milestone 6: Release prototype in incubator.
Milestone 7: Revise implementation, tests, and documentation based on feedback.
Milestone 8: Merge changes from Incubator to Core.

7. Class Index

  • Zend_Application_Resource
  • Zend_Application_Resource_Abstract
  • Zend_Application_Resource_Acl
  • Zend_Application_Resource_Cache
  • Zend_Application_Resource_Controller_Front
  • Zend_Application_Resource_Db
  • Zend_Application_Resource_Filter_Input
  • Zend_Application_Resource_Http_Client
  • Zend_Application_Resource_Log
  • Zend_Application_Resource_Translate

8. Use Cases

The use cases intended to be addressed by Zend_Application_Resource are to be called from an MVC application bootstrap script, or from code prototyping tools.

UC01: Create an empty collection of resources
UC02: Add a resource to a collection using a config array
UC03: Add a resource to a collection using a config file

Config.ini:

Code:

UC04: Create a collection of resources from an array
UC05: Create a collection of resources from a config file
UC06: Create a single resource object and save it in the registry
UC07: Create a single resource object and retrieve it from the registry
UC06: Create a single resource object, don't save it in the registry, retrieve the object
UC06: Create all resource objects in the collection
UC08: Retrieve the combined config object
UC09: Define and use a custom resource type
UC10: Using Zend_Application_Resource in a bootstrap script
UC11: Managing resources for multiple modules in a bootstrap script

In your application code, you can access an entry 'db-1' in a module-specific registry in the following way:

9. Class Skeletons

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

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

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

    <p>I'm afraid this concept is too complex - most people would do just fine with config+registry and instantiating the needed objects by themselves. This would make a lot of sense for a really big applications - but I'm afraid they'd need something custom then so they'd do it manually anyway. </p>

    <p>Maybe I'm just missing the point - then it needs more clear explanation of added value vs. just config+registry.</p>

    1. Aug 31, 2007

      <p>Well, it kind of is just config+registry. But there is some code required to take us from 'config' to 'registry'. <ac:emoticon ac:name="smile" /></p>

      <p>There is some variety in how ZF components are instantiated. Some have constructors, some have static factory methods, some require configuration by a series of setter methods after the constructor (this is not simply due to inconsistent practices – sometimes there are good architectural reasons for this variation).</p>

      <p>So Zend_Application_Resource provides classes whose job is to map config data to the steps required to instantiate and configure respective ZF components. Sometimes this is very simple (Zend_Db) and sometimes it is more complex (Zend_Controller_Front).</p>

      <p>The top class, Zend_Application_Resource, is a kind of config filter-iterator, which searches for config data that match a convention for declaring resources, and then delegates the config data to classes that each know how to make a specific type of ZF component.</p>

      1. Sep 07, 2007

        <p>I think unless it somehow magically works on full-auto mode - i.e. you give it the name of config file and you have database, controller, etc. magically created without absolutely any code - I don't see it being significantly easier than writing the actual code. It's quite hard to be easier than 2 lines of code we already have <ac:emoticon ac:name="smile" /> <br />
        But even then we still have to teach users to write correct configs - so no matter what some effort will be spent on figuring out the right magic to apply for all resources. So the question here is if the magic with config file and resources is better than the magic of just creating the thing. I think maybe the other proposal featuring Zend_Config as ctor parameter is a better way to go.</p>

  2. Aug 31, 2007

    <p>Maybe I'm being a bit thick here but why could we not simply use associative keys that correspond to the resource types? It seems more intuitive to do so.</p>

    <p>E.g. db -> Zend_Db, cache -> Zend_Cache, mail -> Zend_Mail, etc...</p>

    <p>I realise there may be a case for multiple instances but surely then you could use the proposed naming scheme of 'db-2', 'db-3' in the config to create those.</p>

    <p>It was the first thing that tripped me up with this.</p>

    <p>Secondly, could the 'createAll' not be an option provided in the constructor? If we're going down the completely config-driven path then it makes sense to have this happen automatically if we're going to the trouble of creating these resources.</p>

    <p>Finally could calling the 'createAll' option be added to the parent 'Zend_Application' constructor? If I'm using Zend_Application to create my MVC app then I'm more than likely wanting to use the auto-configure approach, otherwise I'd write my own bootstrap from scratch. Perhaps the use cases above and on the Zend_Application could show a little more interaction because currently they seem like unrelated parts.</p>

    <p>I guess I'm wanting to see how far this class can be simplified so as to justify its existence - it's there for an out-of-the-box kind of convenience for developers as well as establishing a kind of standard for configs and resources. I'm sure I'll be using this a lot when it matures.</p>

    1. Aug 31, 2007

      <p>Okay, you've convinced me. If this proposal seems too complex to developers like you and Stas, then it <strong>clearly</strong> does not fit the "extremely simple" philosophy. I should re-think this proposal, and be more crisp about what use cases it's trying to solve.</p>

      <p>Perhaps it is a clumsy solution because it's trying to solve a clumsy problem: bringing consistency to a wide variety of component initialization.</p>

  3. Oct 19, 2007

    <p>A couple of questions:</p>

    <p>Doesn't it make more sense for $res->create() to return the actual created object, or throw an exception if something's wrong, rather than to return a flag?</p>

    <p>Could you illustrate what's happening a little bit better in the "Configuration file life cycle" section? I don't really understand what's happening there, or why the Zend_Config objects should have a dump() method.</p>

    <p>What is the value of $fooConfig in UC08?</p>

    <p>Overall: I actually like the idea here-something that will automatically execute a class to set up a global resource-but I do feel like there's a simpler way.</p>

  4. Nov 11, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Proposal Rejected</ac:parameter><ac:rich-text-body>
    <p>This proposal did not meet the goals of Zend Framework and further development on it has been rejected.</p></ac:rich-text-body></ac:macro>