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_Controller_Directory Component Proposal

Proposed Component Name Zend_Controller_Directory
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Controller_Directory
Proposers Federico Cargnelutti
Revision 1.0 - 21 August 2008: Initial Proposal. (wiki revision: 44)

Table of Contents

1. Overview

The Zend_Controller_Directory component allows users to define directory names and paths.

Goals

This proposal attempts to solve the following problems:

  1. Zend_Controller_Front violates the single responsibility principle, each responsibility should be a separate class.
  2. Zend_Controller_Front is not polymorphic and therefore makes the system less tolerant to change in requirements.
  3. Zend_Controller_Front provides data to the Zend_Controller_Dispatcher_Standard object creating control coupling.
  4. Extending Zend_Controller_Dispatcher_Abstract violates the DRY principle.
  5. Zend_Controller_Dispatcher_Standard allows components not only to retrieve directory names and paths, but also to dispatch controllers. This includes Action Controllers and Action Helpers.
Example

The Front Controller needs one or more paths to directories containing action controllers in order to dispatch the request:

To add additional directory names and paths, the user has to create a class that extends Zend_Controller_Dispatcher_Abstract and implements Zend_Controller_Dispatcher_Interface:

Extending the Zend_Controller_Dispatcher_Abstract class leads to the following problems:

1. Code Duplication

In order to add additional methods, the user has to duplicate the behaviour and internal structure of the Zend_Controller_Dispatcher_Standard class. This approach violates the DRY principle:

2. API Inconsistency

Because the Front Controller is a singleton and cannot be subclassed, the user is unable to add additional accessor methods to the Front Controller, therefore there's no consistency in the way the data is provided and retrieved from the Dispatcher:

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will provide abstracts that facilitate the extension of the base functionality.

4. Dependencies on Other Framework Components

  • Zend_Exception

5. Theory of Operation

Zend Framework organizes code in a project structure and puts the project files into different directory structures:

  1. MVC directory structure
  2. Modular directory structure

The goal of this component is to move this responsibility away from the Front Controller, avoid code duplication, reduce control coupling and improve the consistency of the API.

6. Milestones / Tasks

  • Write proposal
  • Gather feedback
  • Review by the Zend team
  • Develop full implementation and unit tests
  • Documentation

7. Class Index

  • Zend_Controller_Directory_Abstract
  • Zend_Controller_Directory_Exception
  • Zend_Controller_Directory_Standard

8. Use Cases

UC-01 Bootstrapper example
UC-02 Bootstrapper example

9. Class Skeletons

  • Zend_Controller_Directory_Abstract
  • Zend_Controller_Directory_Standard
  • Zend_Controller_Front
  • Zend_Controller_Dispatcher_Abstract
  • Zend_Controller_Dispatcher_Standard
  • Zend_Controller_Action_Helper_ViewRenderer

]]></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 26, 2008

    <p>Can you provide some more information about <em>what problem</em> this proposal is attempting to solve? I've read your blog post, but it's still unclear to me what the exact problem is you perceive, and how this solution then addresses it.</p>

    1. Aug 26, 2008

      fc

      <p>Yes sure. This refactoring proposal attempts to solve the following issues:</p>

      <ol>
      <li>Zend_Controller_Front violates the single responsibility principle, each responsibility should be a separate class.</li>
      <li>Zend_Controller_Front is not polymorphic and therefore makes the system less tolerant to change in requirements.</li>
      <li>Zend_Controller_Front provides data to other objects.</li>
      <li>Extending Zend_Controller_Dispatcher_Abstract violates the DRY principle.</li>
      <li>Zend_Controller_Dispatcher_Abstract allows components not only to retrieve directory names and paths, but also to dispatch controllers. This includes Action Controllers and Action Helpers.</li>
      <li>Zend_Controller_Front acts as a global container and provides access to other objects.</li>
      </ol>

      <p>This proposal focuses on issues 1 to 5. Issue 6 can be solved by introducing the Zend_Container component (or Zend_Registry).</p>

      <p>Basically, the refactoring I'm proposing solves the problem of having to create a new Dispatcher class every time the user extends the functionality of the system. Also, it prevents components from using the Front Controller, the Dispatcher or both to manage directory names and paths. This way, each class has a single responsibility: The Directory component knows where the classes are located and the Dispatcher which class to dispatch. </p>

  2. Aug 26, 2008

    fc

    <p>Hi Matthew, I've just provided more information about the problem this refactoring attempts to solve. Let me know what you think, thanks.</p>

    1. Sep 01, 2008

      <p>I need better explanations of the problems you're solving. FOr instance, "Extending Zend_Controller_Dispatcher_Abstract violates the DRY principle" – how? Or, "Zend_Controller_Front is not polymorphic and therefore makes the system less tolerant to change in requirements" – what changes in requirements would not be supported by Zend_Controller_Front? Or with the first point, which responsibility are you addressing? (I know which one, and it becomes clear in your proposal, but you may want to note if there are others, and explicitly note which particular one you are addressing). Regarding number 6, how is this a problem? (again, spell it out)</p>

      <p>As noted on the lists and other proposals, I'm actually in agreement with you on most points. However, this proposal absolutely cannot be implemented prior to 2.0.0, as it presents a very big BC break with the current architecture, even though it improves it. Let's answer the above questions very explicitly so that we can get good community discussion around them.</p>

      1. Sep 01, 2008

        fc

        <p>I agree, this cannot be implemented prior to 2.0. Should I change the parent document of this proposal back to "New"? </p>

        <p>In the meantime, I'll keep adding more examples. </p>

  3. Sep 25, 2008

    <p>If I'm understanding your first example correctly, you're trying to show that if an application wanted to have multiple controller directories (within the same module), the only way to do this is to subclass the dispatcher, which results in a lot of code duplication?</p>

    <p>If so, it might be simpler to instead change the standard dispatcher so that it supports multiple controller directories for a module. Calling addControllerDirectory more than once could add the path to an internal array rather than replacing it. E.g.</p>

    <ac:macro ac:name="code"><ac:default-parameter>PHP</ac:default-parameter><ac:plain-text-body><![CDATA[
    $front->addControllerDirectory('./application/controllers', 'default');
    $front->addControllerDirectory('./application/foo', 'default');
    ]]></ac:plain-text-body></ac:macro>

    <p>I'm also a little confused as to why a 'controller directory' class would hold variables specifying the names of model and view directories, surely these are separate things?</p>

    <p>Unless I'm going crazy, the blog post you wrote that spawned this proposal originally called this class 'Zend_Module_Front', so it was like a mini front controller for modules. My understanding of it was that it would make it easier for modules to use completely different names for controller/view/model directories and move this level of configuration away from the main front controller. Now that the class has been renamed I don't think it makes sense to use it to control paths to anything except controllers.</p>

    <p>Also, whilst I can appreciate the advantages of refactoring, I think we need to be very careful about any change which potentially requires users to add more code to their bootstrap (i.e. having to instantiate a dispatcher as well as the front controller). The bootstrap already seems to be a cause of confusion among new ZF users, so IMHO the MVC components should try and facilitate as lean a bootstrap as possible.</p>

    1. Sep 29, 2008

      fc

      <p>Hi Tim, </p>

      <p>It's a complex issue. It's not about controller or view directories, it's about defining responsibilities. I spent more than 3 months analysing individuals components of the Zend_Controller package, and I came to the conclusion that the Dispatcher is doing to much. It not only manages directory names and paths, but also dispatches controllers. The first responsibility can be decoupled from the process. There are several advantages of doing this, for example, improve performance, consistency, etc.</p>

    2. Sep 29, 2008

      fc

      <p>In case you are interested in knowing how this proposal can help improve the performance of the framework, here's an example:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      if (!xcache_isset('front_controller')) {
      /* Set the singleton instance of the front controller */
      require_once 'Zend/Controller/Front.php';
      $frontController = Zend_Controller_Front::getInstance();
      $frontController->throwExceptions(true);
      $frontController->addModuleDirectory('../application/modules');
      $frontController->setParam('noViewRenderer', true);

      /* Instantiates a Zend_Controller_Router_Rewrite object */
      $router = $frontController->getRouter();
      include_once('../application/config/routes.php');
      $router->addRoutes($routes);
      xcache_set('front_controller', serialize($frontController));
      } else {
      require_once 'Zend/Controller/Front.php';
      require_once 'Zend/Controller/Dispatcher/Standard.php';
      require_once 'Zend/Controller/Router/Rewrite.php';
      require_once 'Zend/Controller/Request/Http.php';

      $frontController = unserialize(xcache_get('front_controller'));
      $frontController->setBaseUrl(null);
      $frontController->setRequest(new Zend_Controller_Request_Http());
      }
      ]]></ac:plain-text-body></ac:macro>

      <p>When you dispatch the request the front controller throws an exception. If you look at Zend_Controller_Action, line 160, you'll see that it calls the front controller and retrieves a list of directories:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      $this->getFrontController()->getControllerDirectory();
      ]]></ac:plain-text-body></ac:macro>

      <p>And the front controller calls the Dispatcher:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      return $this->getDispatcher()->getControllerDirectory($name);
      ]]></ac:plain-text-body></ac:macro>

      <p>And that's why, unfortunately, it doesn't work.</p>

      1. Nov 09, 2008

        <p>Really interesting examples, I've been following your proposal for a while. Did you move forward?</p>

  4. Mar 30, 2009

    <p>I really hope this makes zf version 2.</p>

    <p>I like keeping classes single purpose, and I like having the front controller contain the directory object. Comment #8 demonstrates the current "circular reference" between the front controller and the dispatcher. Currently, the front controller calls the dispatcher for directory info, and the dispatcher has a variable for the front controller instance. Since the dispatcher has access to the front controller, it can get the directory info from it.</p>

  5. Feb 07, 2011

    <p>Archiving this proposal, feel free to recover it when you want to work on it again. For more details see <a href="http://framework.zend.com/wiki/display/ZFDEV/Archiving+of+abandoned+proposals+(Feb+5+2011)">this email</a>.</p>