<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.controller.migration">
    <title>Migrating from Previous Versions</title>

    <para>
        The API of the MVC components has changed over time. If you started
        using Zend Framework in an early version, follow the guidelines below to
        migrate your scripts to use the new architecture.
    </para>

    <sect2 id="zend.controller.migration.fromonesixtooneseven">
        <title>Migrating from 1.6.x to 1.7.0 or newer</title>

        <sect3 id="zend.controller.migration.fromonesixtooneseven.dispatcher">
            <title>Dispatcher Interface Changes</title>

            <para>
                Users brought to our attention the fact that
                <classname>Zend_Controller_Action_Helper_ViewRenderer</classname> were
                using a method of the dispatcher abstract class that was not in
                the dispatcher interface. We have now added the following method to
                ensure that custom dispatchers will continue to work with the
                shipped implementations:
            </para>

            <itemizedlist>
                <listitem><para>
                    <code>formatModuleName()</code>: should be used to take a raw
                controller name, such as one that would be packaged inside a request
                object, and reformat it to a proper class name that a class extending
                <classname>Zend_Controller_Action</classname> would use
                </para></listitem>
            </itemizedlist>
        </sect3>
    </sect2>

    <sect2 id="zend.controller.migration.fromoneohtoonesix">
        <title>Migrating from 1.5.x to 1.6.0 or Newer</title>

        <sect3 id="zend.controller.migration.fromoneohtoonesix.dispatcher">
            <title>Dispatcher Interface Changes</title>

            <para>
                Users brought to our attention the fact that
                <classname>Zend_Controller_Front</classname> and
                <classname>Zend_Controller_Router_Route_Module</classname> were each
                using methods of the dispatcher that were not in the dispatcher
                interface. We have now added the following three methods to
                ensure that custom dispatchers will continue to work with the
                shipped implementations:
            </para>

            <itemizedlist>
                <listitem><para>
                    <code>getDefaultModule()</code>: should return the name of
                    the default module.
                </para></listitem>

                <listitem><para>
                    <code>getDefaultControllerName()</code>: should return the
                    name of the default controller.
                </para></listitem>

                <listitem><para>
                    <code>getDefaultAction()</code>: should return the
                    name of the default action.
                </para></listitem>
            </itemizedlist>
        </sect3>
    </sect2>

    <sect2 id="zend.controller.migration.fromoneohtoonefive">
        <title>Migrating from 1.0.x to 1.5.0 or Newer</title>

        <para>
            Though most basic functionality remains the same, and all documented
            functionality remains the same, there is one particular
            <emphasis>undocumented</emphasis> "feature" that has changed.
        </para>

        <para>
            When writing URLs, the documented way to write camelCased action
            names is to use a word separator; these are '.' or '-' by default,
            but may be configured in the dispatcher. The dispatcher internally
            lowercases the action name, and uses these word separators to
            re-assemble the action method using camelCasing. However, because PHP
            functions are not case sensitive, you <emphasis>could</emphasis>
            still write URLs using camelCasing, and the dispatcher would resolve
            these to the same location. For example, 'camel-cased' would become
            'camelCasedAction' by the dispatcher, whereas 'camelCased' would
            become 'camelcasedAction'; however, due to the case insensitivity of
            PHP, both will execute the same method.
        </para>

        <para>
            This causes issues with the ViewRenderer when resolving view
            scripts. The canonical, documented way is that all word separators
            are converted to dashes, and the words lowercased. This creates
            a semantic tie between the actions and view scripts, and the
            normalization ensures that the scripts can be found. However, if the
            action 'camelCased' is called and actually resolves, the word
            separator is no longer present, and the ViewRenderer attempts to
            resolve to a different location -- 'camelcased.phtml' instead of
            'camel-cased.phtml'.
        </para>

        <para>
            Some developers relied on this "feature", which was never intended.
            Several changes in the 1.5.0 tree, however, made it so that the
            ViewRenderer no longer resolves these paths; the semantic tie is now
            enforced. First among these, the dispatcher now enforces case
            sensitivity in action names. What this means is that referring to
            your actions on the url using camelCasing will no longer resolve to
            the same method as using word separators (i.e., 'camel-casing').
            This leads to the ViewRenderer now only honoring the word-separated
            actions when resolving view scripts.
        </para>

        <para>
            If you find that you were relying on this "feature", you have several
            options:
        </para>

        <itemizedlist>
            <listitem><para>
                    Best option: rename your view scripts. Pros: forward
                    compatibility. Cons: if you have many view scripts that
                    relied on the former, unintended behavior, you will have a
                    lot of renaming to do.
            </para></listitem>

            <listitem>
                <para>
                    Second best option: The ViewRenderer now delegates view
                    script resolution to <classname>Zend_Filter_Inflector</classname>; you
                    can modify the rules of the inflector to no longer separate
                    the words of an action with a dash:
                </para>

                <programlisting role="php"><![CDATA[
$viewRenderer =
    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$inflector = $viewRenderer->getInflector();
$inflector->setFilterRule(':action', array(
    new Zend_Filter_PregReplace(
        '#[^a-z0-9' . preg_quote(DIRECTORY_SEPARATOR, '#') . ']+#i',
        ''
    ),
    'StringToLower'
));
]]></programlisting>

                <para>
                    The above code will modify the inflector to no longer
                    separate the words with dash; you may also want to remove
                    the 'StringToLower' filter if you <emphasis>do</emphasis>
                    want the actual view script names camelCased as well.
                </para>

                <para>
                    If renaming your view scripts would be too tedious or time
                    consuming, this is your best option until you can find the
                    time to do so.
                </para>
            </listitem>

            <listitem>
                <para>
                    Least desirable option: You can force the dispatcher to
                    dispatch camelCased action names with a new front controller
                    flag, 'useCaseSensitiveActions':
                </para>

                <programlisting role="php"><![CDATA[
$front->setParam('useCaseSensitiveActions', true);
]]></programlisting>

                <para>
                    This will allow you to use camelCasing on the url and still
                    have it resolve to the same action as when you use word
                    separators. However, this will mean that the original issues
                    will cascade on through; you will likely need to use the
                    second option above in addition to this for things to work
                    at all reliably.
                </para>

                <para>
                    Note, also, that usage of this flag will raise a notice that
                    this usage is deprecated.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.migration.fromzeroninethree">
        <title>Migrating from 0.9.3 to 1.0.0RC1 or Newer</title>

        <para>
            The principal changes introduced in 1.0.0RC1 are the introduction of
            and default enabling of the
            <link
                linkend="zend.controller.plugins.standard.errorhandler">ErrorHandler</link>
            plugin and the <link
                linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>
            action helper. Please read the documentation to each thoroughly to
            see how they work and what effect they may have on your
            applications.
        </para>

        <para>
            The <code>ErrorHandler</code> plugin runs during
            <code>postDispatch()</code> checking for exceptions, and forwarding
            to a specified error handler controller. You should include such a
            controller in your application. You may disable it by setting the
            front controller parameter <code>noErrorHandler</code>:
        </para>

        <programlisting role="php"><![CDATA[
$front->setParam('noErrorHandler', true);
]]></programlisting>

        <para>
            The <code>ViewRenderer</code> action helper automates view injection
            into action controllers as well as autorendering of view scripts
            based on the current action. The primary issue you may encounter is
            if you have actions that do not render view scripts and neither
            forward or redirect, as the <code>ViewRenderer</code> will attempt
            to render a view script based on the action name.
        </para>

        <para>
            There are several strategies you can take to update your code. In
            the short term, you can globally disable the
            <code>ViewRenderer</code> in your front controller bootstrap prior
            to dispatching:
        </para>

        <programlisting role="php"><![CDATA[
// Assuming $front is an instance of Zend_Controller_Front
$front->setParam('noViewRenderer', true);
]]></programlisting>

        <para>
            However, this is not a good long term strategy, as it means most
            likely you'll be writing more code.
        </para>

        <para>
            When you're ready to start using the <code>ViewRenderer</code>
            functionality, there are several things to look for in your
            controller code. First, look at your action methods (the methods
            ending in 'Action'), and determine what each is doing. If none of
            the following is happening, you'll need to make changes:
        </para>

        <itemizedlist>
            <listitem><para>Calls to <code>$this-&gt;render()</code></para></listitem>
            <listitem><para>Calls to <code>$this-&gt;_forward()</code></para></listitem>
            <listitem><para>Calls to <code>$this-&gt;_redirect()</code></para></listitem>
            <listitem><para>Calls to the <code>Redirector</code> action helper</para></listitem>
        </itemizedlist>

        <para>
            The easiest change is to disable auto-rendering for that method:
        </para>

        <programlisting role="php"><![CDATA[
$this->_helper->viewRenderer->setNoRender();
]]></programlisting>

        <para>
            If you find that none of your action methods are rendering,
            forwarding, or redirecting, you will likely want to put the above
            line in your <code>preDispatch()</code> or <code>init()</code>
            methods:
        </para>

        <programlisting role="php"><![CDATA[
public function preDispatch()
{
    // disable view script autorendering
    $this->_helper->viewRenderer->setNoRender()
    // .. do other things...
}
]]></programlisting>

        <para>
            If you are calling <code>render()</code>, and you're using <link
                linkend="zend.controller.modular">the Conventional Modular
                directory structure</link>, you'll want to change your code to
            make use of autorendering:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    If you're rendering multiple view scripts in a single
                    action, you don't need to change a thing.
                </para>
            </listitem>
            <listitem>
                <para>
                    If you're simply calling <code>render()</code> with no
                    arguments, you can remove such lines.
                </para>
            </listitem>
            <listitem>
                <para>
                    If you're calling <code>render()</code> with arguments, and
                    not doing any processing afterwards or rendering multiple
                    view scripts, you can change these calls to read
                    <code>$this-&gt;_helper-&gt;viewRenderer()</code>.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            If you're not using the conventional modular directory structure,
            there are a variety of methods for setting the view base path and
            script path specifications so that you can make use of the
            <code>ViewRenderer</code>. Please read the <link
                linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer
                documentation</link> for information on these methods.
        </para>

        <para>
            If you're using a view object from the registry, or customizing your
            view object, or using a different view implementation, you'll want
            to inject the <code>ViewRenderer</code> with this object. This can
            be done easily at any time.
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    Prior to dispatching a front controller instance:
                </para>

                <programlisting role="php"><![CDATA[
// Assuming $view has already been defined
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
]]></programlisting>
            </listitem>

            <listitem>
                <para>
                    Any time during the bootstrap process:
                </para>

                <programlisting role="php"><![CDATA[
$viewRenderer =
    Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView($view);
]]></programlisting>
            </listitem>
        </itemizedlist>

        <para>
            There are many ways to modify the <code>ViewRenderer</code>,
            including setting a different view script to render, specifying
            replacements for all replaceable elements of a view script path
            (including the suffix), choosing a response named segment to
            utilize, and more. If you aren't using the conventional modular
            directory structure, you can even associate different path
            specifications with the <code>ViewRenderer</code>.
        </para>

        <para>
            We encourage you to adapt your code to use the
            <code>ErrorHandler</code> and <code>ViewRenderer</code> as they are
            now core functionality.
        </para>
    </sect2>

    <sect2 id="zend.controller.migration.fromzeroninetwo">
        <title>Migrating from 0.9.2 to 0.9.3 or Newer</title>

        <para>
            0.9.3 introduces <link
                linkend="zend.controller.actionhelpers">action helpers</link>.
            As part of this change, the following methods have been removed as
            they are now encapsulated in the <link
                linkend="zend.controller.actionhelpers.redirector">redirector
                action helper</link>:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <code>setRedirectCode()</code>; use
                    <classname>Zend_Controller_Action_Helper_Redirector::setCode()</classname>.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>setRedirectPrependBase()</code>; use
                    <classname>Zend_Controller_Action_Helper_Redirector::setPrependBase()</classname>.
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>setRedirectExit()</code>; use
                    <classname>Zend_Controller_Action_Helper_Redirector::setExit()</classname>.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Read the <link linkend="zend.controller.actionhelpers">action
                helpers documentation</link> for more information on how to
            retrieve and manipulate helper objects, and the <link
                linkend="zend.controller.actionhelpers.redirector">redirector
                helper documentation</link> for more information on setting
            redirect options (as well as alternate methods for redirecting).
        </para>
    </sect2>

    <sect2 id="zend.controller.migration.fromzerosix">
        <title>Migrating from 0.6.0 to 0.8.0 or Newer</title>

        <para>
            Per previous changes, the most basic usage of the MVC components
            remains the same:
        </para>

        <programlisting role="php"><![CDATA[
Zend_Controller_Front::run('/path/to/controllers');
]]></programlisting>

        <para>
            However, the directory structure underwent an overhaul, several
            components were removed, and several others either renamed or added.
            Changes include:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <classname>Zend_Controller_Router</classname> was removed in favor of
                    the rewrite router.
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_RewriteRouter</classname> was renamed to
                    <classname>Zend_Controller_Router_Rewrite</classname>, and promoted to
                    the standard router shipped with the framework;
                    <classname>Zend_Controller_Front</classname> will use it by default if
                    no other router is supplied.
                </para>
            </listitem>

            <listitem>
                <para>
                    A new route class for use with the rewrite router was
                    introduced,
                    <classname>Zend_Controller_Router_Route_Module</classname>; it covers
                    the default route used by the MVC, and has support for <link
                        linkend="zend.controller.modular">controller
                        modules</link>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_Router_StaticRoute</classname> was renamed
                    to <classname>Zend_Controller_Router_Route_Static</classname>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_Dispatcher</classname> was renamed
                    <classname>Zend_Controller_Dispatcher_Standard</classname>.
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Controller_Action::_forward()</classname>'s arguments
                    have changed. The signature is now:
                </para>

                <programlisting role="php"><![CDATA[
final protected function _forward($action,
                                  $controller = null,
                                  $module = null,
                                  array $params = null);
]]></programlisting>

                <para>
                    <code>$action</code> is always required; if no controller is
                    specified, an action in the current controller is assumed.
                    <code>$module</code> is always ignored unless
                    <code>$controller</code> is specified. Finally, any
                    <code>$params</code> provided will be appended to the
                    request object. If you do not require the controller or
                    module, but still need to pass parameters, simply specify
                    null for those values.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.controller.migration.fromzerotwo">
        <title>Migrating from 0.2.0 or before to 0.6.0</title>

        <para>
            The most basic usage of the MVC components has not changed; you can
            still do each of the following:
        </para>

        <programlisting role="php"><![CDATA[
Zend_Controller_Front::run('/path/to/controllers');
]]></programlisting>

        <programlisting role="php"><![CDATA[
/* -- create a router -- */
$router = new Zend_Controller_RewriteRouter();
$router->addRoute('user',
                  'user/:username',
                  array('controller' => 'user', 'action' => 'info')
);

/* -- set it in a controller -- */
$ctrl = Zend_Controller_Front::getInstance();
$ctrl->setRouter($router);

/* -- set controller directory and dispatch -- */
$ctrl->setControllerDirectory('/path/to/controllers');
$ctrl->dispatch();
]]></programlisting>

        <para>
            We encourage use of the Response object to aggregate content and
            headers. This will allow for more flexible output format switching
            (for instance, JSON or XML instead of XHTML) in your applications.
            By default, <code>dispatch()</code> will render the response, sending both
            headers and rendering any content. You may also have the front
            controller return the response using <code>returnResponse()</code>,
            and then render the response using your own logic. A future version
            of the front controller may enforce use of the response object via
            output buffering.
        </para>

        <para>
            There are many additional features that extend the existing API,
            and these are noted in the documentation.
        </para>

        <para>
            The main changes you will need to be aware of will be found when
            subclassing the various components. Key amongst these are:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <classname>Zend_Controller_Front::dispatch()</classname> by default
                    traps exceptions in the response object, and does not render
                    them, in order to prevent sensitive system information from
                    being rendered. You can override this in several ways:
                </para>

                <itemizedlist>
                    <listitem>
                        <para>
                            Set <code>throwExceptions()</code> in the front
                            controller:
                        </para>
                        <programlisting role="php"><![CDATA[
$front->throwExceptions(true);
]]></programlisting>
                    </listitem>

                    <listitem>
                        <para>
                            Set <code>renderExceptions()</code> in the response
                            object:
                        </para>
                        <programlisting role="php"><![CDATA[
$response->renderExceptions(true);
$front->setResponse($response);
$front->dispatch();

// or:
$front->returnResponse(true);
$response = $front->dispatch();
$response->renderExceptions(true);
echo $response;
]]></programlisting>
                    </listitem>
                </itemizedlist>
            </listitem>

            <listitem><para>
                <classname>Zend_Controller_Dispatcher_Interface::dispatch()</classname>
                now accepts and returns a <xref linkend="zend.controller.request" />
                object instead of a dispatcher token.
            </para></listitem>

            <listitem><para>
                <classname>Zend_Controller_Router_Interface::route()</classname>
                now accepts and returns a <xref linkend="zend.controller.request" />
                object instead of a dispatcher token.
            </para></listitem>

            <listitem>
                <para><classname>Zend_Controller_Action</classname> changes include:</para>

                <itemizedlist>
                    <listitem><para>
                        The constructor now accepts exactly three arguments,
                        <classname>Zend_Controller_Request_Abstract $request</classname>,
                        <classname>Zend_Controller_Response_Abstract $response</classname>,
                        and <code>array $params (optional)</code>.
                        <classname>Zend_Controller_Action::__construct()</classname> uses
                        these to set the request, response, and invokeArgs
                        properties of the object, and if overriding the
                        constructor, you should do so as well. Better yet, use
                        the <code>init()</code> method to do any instance
                        configuration, as this method is called as the final
                        action of the constructor.
                    </para></listitem>

                    <listitem><para>
                        <code>run()</code> is no longer defined as final, but is
                        also no longer used by the front controller; its sole
                        purpose is for using the class as a page controller. It
                        now takes two optional arguments, a
                        <classname>Zend_Controller_Request_Abstract $request</classname>
                        and a <classname>Zend_Controller_Response_Abstract $response</classname>.
                    </para></listitem>

                    <listitem><para>
                        <code>indexAction()</code> no longer needs to be
                        defined, but is encouraged as the default action. This
                        allows using the RewriteRouter and action controllers to
                        specify different default action methods.
                    </para></listitem>

                    <listitem><para>
                        <code>__call()</code> should be overridden to handle any
                        undefined actions automatically.
                    </para></listitem>

                    <listitem><para>
                        <code>_redirect()</code> now takes an optional second
                        argument, the HTTP code to return with the redirect, and
                        an optional third argument, <code>$prependBase</code>,
                        that can indicate that the base URL registered with the
                        request object should be prepended to the url specified.
                    </para></listitem>

                    <listitem>
                        <para>
                            The <code>_action</code> property is no longer set.
                            This property was a <classname>Zend_Controller_Dispatcher_Token</classname>,
                            which no longer exists in the current incarnation.
                            The sole purpose of the token was to provide
                            information about the requested controller, action,
                            and URL parameters. This information is now
                            available in the request object, and can be accessed
                            as follows:
                        </para>

                        <programlisting role="php"><![CDATA[
// Retrieve the requested controller name
// Access used to be via: $this->_action->getControllerName().
// The example below uses getRequest(), though you may also directly
// access the $_request property; using getRequest() is recommended as
// a parent class may override access to the request object.
$controller = $this->getRequest()->getControllerName();

// Retrieve the requested action name
// Access used to be via: $this->_action->getActionName().
$action = $this->getRequest()->getActionName();

// Retrieve the request parameters
// This hasn't changed; the _getParams() and _getParam() methods simply
// proxy to the request object now.
$params = $this->_getParams();
// request 'foo' parameter, using 'default' as default value if not found
$foo = $this->_getParam('foo', 'default');
]]></programlisting>
                    </listitem>

                    <listitem>
                        <para>
                            <code>noRouteAction()</code> has been removed. The
                            appropriate way to handle non-existent action
                            methods should you wish to route them to a default
                            action is using <code>__call()</code>:
                        </para>

                        <programlisting role="php"><![CDATA[
public function __call($method, $args)
{
    // If an unmatched 'Action' method was requested, pass on to the
    // default action method:
    if ('Action' == substr($method, -6)) {
        return $this->defaultAction();
    }

    throw new Zend_Controller_Exception('Invalid method called');
}
]]></programlisting>
                    </listitem>
                </itemizedlist>
            </listitem>

            <listitem><para>
                <classname>Zend_Controller_RewriteRouter::setRewriteBase()</classname> has
                been removed. Use <classname>Zend_Controller_Front::setBaseUrl()</classname>
                instead (or <classname>Zend_Controller_Request_Http::setBaseUrl()</classname>, if using
                that request class).
            </para></listitem>

            <listitem><para>
                <classname>Zend_Controller_Plugin_Interface</classname> was replaced
                by <classname>Zend_Controller_Plugin_Abstract</classname>. All methods now
                accept and return a <xref linkend="zend.controller.request" />
                object instead of a dispatcher token.
            </para></listitem>
        </itemizedlist>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
