Procházet zdrojové kódy

[DOCS] Added tutorials
- Merged tutorials from zf-devteam branch
- Updated WindowsAzure manual build to use xinclude
- Sectioned manual into "parts" to accomodate tutorials

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@19766 44c647ce-9c0f-0410-b52a-842ac1e357ba

matthew před 16 roky
rodič
revize
48054cff4b
50 změnil soubory, kde provedl 6623 přidání a 81 odebrání
  1. binární
      documentation/manual/en/figures/learning.quickstart.create-form.png
  2. binární
      documentation/manual/en/figures/learning.quickstart.create-model.png
  3. binární
      documentation/manual/en/figures/learning.quickstart.intro.mvc.png
  4. 100 6
      documentation/manual/en/manual.xml.in
  5. 6 2
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure.xml
  6. 27 27
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Blob.xml
  7. 14 14
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Queue.xml
  8. 32 32
      documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Table.xml
  9. 20 0
      documentation/manual/en/tutorials/autoloading-conclusion.xml
  10. 101 0
      documentation/manual/en/tutorials/autoloading-design.xml
  11. 32 0
      documentation/manual/en/tutorials/autoloading-intro.xml
  12. 106 0
      documentation/manual/en/tutorials/autoloading-resources.xml
  13. 158 0
      documentation/manual/en/tutorials/autoloading-usage.xml
  14. 384 0
      documentation/manual/en/tutorials/form-decorators-composite.xml
  15. 12 0
      documentation/manual/en/tutorials/form-decorators-conclusion.xml
  16. 283 0
      documentation/manual/en/tutorials/form-decorators-individual.xml
  17. 22 0
      documentation/manual/en/tutorials/form-decorators-intro.xml
  18. 362 0
      documentation/manual/en/tutorials/form-decorators-layering.xml
  19. 244 0
      documentation/manual/en/tutorials/form-decorators-simplest.xml
  20. 20 0
      documentation/manual/en/tutorials/layout-conclusions.xml
  21. 54 0
      documentation/manual/en/tutorials/layout-intro.xml
  22. 242 0
      documentation/manual/en/tutorials/layout-usage.xml
  23. 27 0
      documentation/manual/en/tutorials/lucene-index-opening.xml
  24. 116 0
      documentation/manual/en/tutorials/lucene-index-structure.xml
  25. 94 0
      documentation/manual/en/tutorials/lucene-indexing.xml
  26. 102 0
      documentation/manual/en/tutorials/lucene-intro.xml
  27. 48 0
      documentation/manual/en/tutorials/lucene-pagination.xml
  28. 228 0
      documentation/manual/en/tutorials/lucene-queries.xml
  29. 97 0
      documentation/manual/en/tutorials/lucene-searching.xml
  30. 182 0
      documentation/manual/en/tutorials/multiuser-authentication.xml
  31. 226 0
      documentation/manual/en/tutorials/multiuser-authorization.xml
  32. 71 0
      documentation/manual/en/tutorials/multiuser-intro.xml
  33. 133 0
      documentation/manual/en/tutorials/multiuser-sessions.xml
  34. 106 0
      documentation/manual/en/tutorials/paginator-control.xml
  35. 37 0
      documentation/manual/en/tutorials/paginator-intro.xml
  36. 124 0
      documentation/manual/en/tutorials/paginator-simple.xml
  37. 74 0
      documentation/manual/en/tutorials/paginator-together.xml
  38. 61 0
      documentation/manual/en/tutorials/plugins-conclusion.xml
  39. 54 0
      documentation/manual/en/tutorials/plugins-intro.xml
  40. 151 0
      documentation/manual/en/tutorials/plugins-usage.xml
  41. 15 0
      documentation/manual/en/tutorials/quickstart-conclusion.xml
  42. 171 0
      documentation/manual/en/tutorials/quickstart-create-form.xml
  43. 213 0
      documentation/manual/en/tutorials/quickstart-create-layout.xml
  44. 760 0
      documentation/manual/en/tutorials/quickstart-create-model.xml
  45. 429 0
      documentation/manual/en/tutorials/quickstart-create-project.xml
  46. 114 0
      documentation/manual/en/tutorials/quickstart-intro-mvc.xml
  47. 254 0
      documentation/manual/en/tutorials/view-placeholders-basics.xml
  48. 16 0
      documentation/manual/en/tutorials/view-placeholders-conclusion.xml
  49. 41 0
      documentation/manual/en/tutorials/view-placeholders-intro.xml
  50. 460 0
      documentation/manual/en/tutorials/view-placeholders-standard.xml

binární
documentation/manual/en/figures/learning.quickstart.create-form.png


binární
documentation/manual/en/figures/learning.quickstart.create-model.png


binární
documentation/manual/en/figures/learning.quickstart.intro.mvc.png


+ 100 - 6
documentation/manual/en/manual.xml.in

@@ -29,11 +29,106 @@
         -->
         -->
     </bookinfo>
     </bookinfo>
 
 
-    <chapter id="introduction">
+    <part id="introduction">
         <title>Introduction to Zend Framework</title>
         <title>Introduction to Zend Framework</title>
         <xi:include href="ref/overview.xml" />
         <xi:include href="ref/overview.xml" />
         <xi:include href="ref/installation.xml" />
         <xi:include href="ref/installation.xml" />
-    </chapter>
+    </part>
+
+    <part id="learning">
+        <title>Learning Zend Framework</title>
+
+        <chapter id="learning.quickstart">
+            <title>Zend Framework Quick Start</title>
+            <xi:include href="tutorials/quickstart-intro-mvc.xml" />
+            <xi:include href="tutorials/quickstart-create-project.xml" />
+            <xi:include href="tutorials/quickstart-create-layout.xml" />
+            <xi:include href="tutorials/quickstart-create-model.xml" />
+            <xi:include href="tutorials/quickstart-create-form.xml" />
+            <xi:include href="tutorials/quickstart-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.autoloading">
+            <title>Autoloading in Zend Framework</title>
+            <xi:include href="tutorials/autoloading-intro.xml" />
+            <xi:include href="tutorials/autoloading-design.xml" />
+            <xi:include href="tutorials/autoloading-usage.xml" />
+            <xi:include href="tutorials/autoloading-resources.xml" />
+            <xi:include href="tutorials/autoloading-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.plugins">
+            <title>Plugins in Zend Framework</title>
+            <xi:include href="tutorials/plugins-intro.xml" />
+            <xi:include href="tutorials/plugins-usage.xml" />
+            <xi:include href="tutorials/plugins-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.layout">
+            <title>Getting Started with Zend_Layout</title>
+            <xi:include href="tutorials/layout-intro.xml" />
+            <xi:include href="tutorials/layout-usage.xml" />
+            <xi:include href="tutorials/layout-conclusions.xml" />
+        </chapter>
+
+        <chapter id="learning.view.placeholders">
+            <title>Getting Started with Zend_View Placeholders</title>
+            <xi:include href="tutorials/view-placeholders-intro.xml" />
+            <xi:include href="tutorials/view-placeholders-basics.xml" />
+            <xi:include href="tutorials/view-placeholders-standard.xml" />
+            <xi:include href="tutorials/view-placeholders-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.form.decorators">
+            <title>Understanding and Using Zend Form Decorators</title>
+            <xi:include href="tutorials/form-decorators-intro.xml" />
+            <xi:include href="tutorials/form-decorators-simplest.xml" />
+            <xi:include href="tutorials/form-decorators-layering.xml" />
+            <xi:include href="tutorials/form-decorators-individual.xml" />
+            <xi:include href="tutorials/form-decorators-composite.xml" />
+            <xi:include href="tutorials/form-decorators-conclusion.xml" />
+        </chapter>
+
+        <chapter id="learning.multiuser">
+            <title>Getting Started with Zend_Session, Zend_Auth, and Zend_Acl</title>
+            <xi:include href="tutorials/multiuser-intro.xml" />
+            <xi:include href="tutorials/multiuser-sessions.xml" />
+            <xi:include href="tutorials/multiuser-authentication.xml" />
+            <xi:include href="tutorials/multiuser-authorization.xml" />
+        </chapter>
+
+        <chapter id="learning.lucene">
+            <title>Getting Started with Zend_Search_Lucene</title>
+            <xi:include href="tutorials/lucene-intro.xml" />
+            <xi:include href="tutorials/lucene-index-structure.xml" />
+            <xi:include href="tutorials/lucene-index-opening.xml" />
+            <xi:include href="tutorials/lucene-indexing.xml" />
+            <xi:include href="tutorials/lucene-searching.xml" />
+            <xi:include href="tutorials/lucene-queries.xml" />
+            <xi:include href="tutorials/lucene-pagination.xml" />
+        </chapter>
+        
+        <chapter id="learning.paginator">
+            <title>Getting Started with Zend_Paginator</title>
+            <xi:include href="tutorials/paginator-intro.xml" />
+            <xi:include href="tutorials/paginator-simple.xml" />
+            <xi:include href="tutorials/paginator-control.xml" />
+            <xi:include href="tutorials/paginator-together.xml" />
+        </chapter>
+
+        <!--
+        Include new tutorials like this:
+        <chapter id="learning.foo">
+            <xi:include href="tutorials/foobar.xml" />
+        </chapter>
+
+        Thus, if a tutorial consists of multiple sections, they can still be
+        grouped within the same chapter.
+        -->
+    </part>
+
+    <part id="reference">
+        <title>Zend Framework Reference</title>
 
 
     <chapter id="zend.acl">
     <chapter id="zend.acl">
         <title>Zend_Acl</title>
         <title>Zend_Acl</title>
@@ -489,10 +584,7 @@
         <xi:include href="module_specs/Zend_Service_StrikeIron-AdvancedUses.xml" />
         <xi:include href="module_specs/Zend_Service_StrikeIron-AdvancedUses.xml" />
         <xi:include href="module_specs/Zend_Service_Technorati.xml" />
         <xi:include href="module_specs/Zend_Service_Technorati.xml" />
         <xi:include href="module_specs/Zend_Service_Twitter.xml" parse="xml" />
         <xi:include href="module_specs/Zend_Service_Twitter.xml" parse="xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Blob.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Table.xml" />
-        <xi:include href="module_specs/Zend_Service_WindowsAzure_Queue.xml" />
+        <xi:include href="module_specs/Zend_Service_WindowsAzure.xml" parse="xml" />
         <xi:include href="module_specs/Zend_Service_Yahoo.xml" />
         <xi:include href="module_specs/Zend_Service_Yahoo.xml" />
     </chapter>
     </chapter>
 
 
@@ -607,6 +699,8 @@
         <xi:include href="module_specs/Zend_XmlRpc_Client.xml" />
         <xi:include href="module_specs/Zend_XmlRpc_Client.xml" />
         <xi:include href="module_specs/Zend_XmlRpc_Server.xml" />
         <xi:include href="module_specs/Zend_XmlRpc_Server.xml" />
     </chapter>
     </chapter>
+    </part>
+
     <xi:include href="ref/requirements.xml" />
     <xi:include href="ref/requirements.xml" />
     <appendix id="migration">
     <appendix id="migration">
         <title>Zend Framework Migration Notes</title>
         <title>Zend Framework Migration Notes</title>

+ 6 - 2
documentation/manual/en/module_specs/Zend_Service_WindowsAzure.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure">
+<sect1 id="zend.service.windowsazure" xmlns:xi="http://www.w3.org/2001/XInclude">
     <title>Zend_Service_WindowsAzure</title>
     <title>Zend_Service_WindowsAzure</title>
 
 
     <sect2 id="zend.service.windowsazure.introduction">
     <sect2 id="zend.service.windowsazure.introduction">
@@ -111,5 +111,9 @@
             Windows Azure's features, no matter if it is hosted on the Windows Azure platform or on
             Windows Azure's features, no matter if it is hosted on the Windows Azure platform or on
             an in-premise web server.
             an in-premise web server.
         </para>
         </para>
-  </sect2>
+    </sect2>
+
+    <xi:include href="Zend_Service_WindowsAzure_Blob.xml" />
+    <xi:include href="Zend_Service_WindowsAzure_Table.xml" />
+    <xi:include href="Zend_Service_WindowsAzure_Queue.xml" />
 </sect1>
 </sect1>

+ 27 - 27
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Blob.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.blob">
+<sect2 id="zend.service.windowsazure.storage.blob">
     <title>Zend_Service_WindowsAzure_Storage_Blob</title>
     <title>Zend_Service_WindowsAzure_Storage_Blob</title>
 
 
     <para>
     <para>
@@ -15,7 +15,7 @@
         order to provide a native PHP interface to the storage account.
         order to provide a native PHP interface to the storage account.
     </para>
     </para>
 
 
-    <sect2 id="zend.service.windowsazure.storage.blob.api">
+    <sect3 id="zend.service.windowsazure.storage.blob.api">
         <title>API Examples</title>
         <title>API Examples</title>
 
 
         <para>
         <para>
@@ -25,7 +25,7 @@
             features.
             features.
         </para>
         </para>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.create-container">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.create-container">
             <title>Creating a storage container</title>
             <title>Creating a storage container</title>
 
 
             <para>
             <para>
@@ -43,9 +43,9 @@ $result = $storageClient->createContainer('testcontainer');
 echo 'Container name is: ' . $result->Name;
 echo 'Container name is: ' . $result->Name;
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.delete-container">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.delete-container">
             <title>Deleting a storage container</title>
             <title>Deleting a storage container</title>
 
 
             <para>
             <para>
@@ -61,9 +61,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Blob();
 $storageClient->deleteContainer('testcontainer');
 $storageClient->deleteContainer('testcontainer');
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.storing-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.storing-blob">
             <title>Storing a blob</title>
             <title>Storing a blob</title>
 
 
             <para>
             <para>
@@ -85,9 +85,9 @@ $result = $storageClient->putBlob(
 echo 'Blob name is: ' . $result->Name;
 echo 'Blob name is: ' . $result->Name;
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.copy-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.copy-blob">
             <title>Copying a blob</title>
             <title>Copying a blob</title>
 
 
             <para>
             <para>
@@ -111,9 +111,9 @@ $result = $storageClient->copyBlob(
 echo 'Copied blob name is: ' . $result->Name;
 echo 'Copied blob name is: ' . $result->Name;
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.download-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.download-blob">
             <title>Downloading a blob</title>
             <title>Downloading a blob</title>
 
 
             <para>
             <para>
@@ -134,9 +134,9 @@ $storageClient->getBlob(
 );
 );
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.blob.api.public-blob">
+        <sect4 id="zend.service.windowsazure.storage.blob.api.public-blob">
             <title>Making a blob publicly available</title>
             <title>Making a blob publicly available</title>
 
 
             <para>
             <para>
@@ -161,10 +161,10 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Blob();
 $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Storage_Blob::ACL_PUBLIC);
 $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Storage_Blob::ACL_PUBLIC);
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
   
   
-    <sect2 id="zend.service.windowsazure.storage.blob.root">
+    <sect3 id="zend.service.windowsazure.storage.blob.root">
         <title>Root container</title>
         <title>Root container</title>
 
 
         <para>
         <para>
@@ -179,9 +179,9 @@ $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Stora
             <varname>$root</varname>.  All other operations on the root container should be issued
             <varname>$root</varname>.  All other operations on the root container should be issued
             with the container name set to <varname>$root</varname>.
             with the container name set to <varname>$root</varname>.
         </para>
         </para>
-    </sect2>
+    </sect3>
   
   
-    <sect2 id="zend.service.windowsazure.storage.blob.wrapper">
+    <sect3 id="zend.service.windowsazure.storage.blob.wrapper">
         <title>Blob storage stream wrapper</title>
         <title>Blob storage stream wrapper</title>
 
 
         <para>
         <para>
@@ -189,7 +189,7 @@ $storageClient->setContainerAcl('testcontainer', Zend_Service_WindowsAzure_Stora
             client as a PHP file stream wrapper. The blob storage stream wrapper provides
             client as a PHP file stream wrapper. The blob storage stream wrapper provides
             support for using regular file operations on Windows Azure Blob Storage.
             support for using regular file operations on Windows Azure Blob Storage.
             For example, one can open a file from Windows Azure Blob Storage with
             For example, one can open a file from Windows Azure Blob Storage with
-            the <functionname>fopen()</functionname> function:
+            the <modulename>fopen()</modulename> function:
         </para>
         </para>
 
 
         <example id="zend.service.windowsazure.storage.blob.wrapper.sample">
         <example id="zend.service.windowsazure.storage.blob.wrapper.sample">
@@ -227,9 +227,9 @@ $storageClient->registerStreamWrapper('blob://'); // regiters blob:// on this st
             To unregister the stream wrapper, the <methodname>unregisterStreamWrapper()</methodname>
             To unregister the stream wrapper, the <methodname>unregisterStreamWrapper()</methodname>
             method can be used.
             method can be used.
         </para>
         </para>
-    </sect2>
+    </sect3>
 
 
-    <sect2 id="zend.service.windowsazure.storage.blob.sharedaccesssig">
+    <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig">
         <title>Shared Access Signature</title>
         <title>Shared Access Signature</title>
 
 
         <para>
         <para>
@@ -254,7 +254,7 @@ http://phpstorage.blob.core.windows.net/phpazuretestshared1?st=2009-08-17T09%3A0
             container of the "phpstorage" account.
             container of the "phpstorage" account.
         </para>
         </para>
     
     
-        <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig.generate">
+        <sect4 id="zend.service.windowsazure.storage.blob.sharedaccesssig.generate">
             <title>Generating a Shared Access Signature</title>
             <title>Generating a Shared Access Signature</title>
 
 
             <para>
             <para>
@@ -307,9 +307,9 @@ $sharedAccessUrl = storageClient->generateSharedAccessUrl(
 );
 );
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
         
         
-        <sect3 id="zend.service.windowsazure.storage.blob.sharedaccesssig.consume">
+        <sect4 id="zend.service.windowsazure.storage.blob.sharedaccesssig.consume">
             <title>Working with Shared Access Signatures from others</title>
             <title>Working with Shared Access Signatures from others</title>
 
 
             <para>
             <para>
@@ -354,6 +354,6 @@ $storageClient->putBlob(
                 available for the latter, the Windows Azure SDK for PHP chose those credentials to
                 available for the latter, the Windows Azure SDK for PHP chose those credentials to
                 perform the request on Windows Azure blob storage.
                 perform the request on Windows Azure blob storage.
             </para>
             </para>
-        </sect3>
-    </sect2>
-</sect1>
+        </sect4>
+    </sect3>
+</sect2>

+ 14 - 14
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Queue.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.queue">
+<sect2 id="zend.service.windowsazure.storage.queue">
     <title>Zend_Service_WindowsAzure_Storage_Queue</title>
     <title>Zend_Service_WindowsAzure_Storage_Queue</title>
 
 
     <para>
     <para>
@@ -22,7 +22,7 @@
         provide a native PHP interface to the storage account.
         provide a native PHP interface to the storage account.
     </para>
     </para>
 
 
-    <sect2 id="zend.service.windowsazure.storage.queue.api">
+    <sect3 id="zend.service.windowsazure.storage.queue.api">
         <title>API Examples</title>
         <title>API Examples</title>
 
 
         <para>
         <para>
@@ -32,7 +32,7 @@
             features.
             features.
         </para>
         </para>
 
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.create-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.create-queue">
             <title>Creating a queue</title>
             <title>Creating a queue</title>
 
 
             <para>
             <para>
@@ -49,9 +49,9 @@ $result = $storageClient->createQueue('testqueue');
 echo 'Queue name is: ' . $result->Name;
 echo 'Queue name is: ' . $result->Name;
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.delete-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.delete-queue">
             <title>Deleting a queue</title>
             <title>Deleting a queue</title>
 
 
             <para>
             <para>
@@ -66,9 +66,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Queue();
 $storageClient->deleteQueue('testqueue');
 $storageClient->deleteQueue('testqueue');
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.storing-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.storing-queue">
             <title>Adding a message to a queue</title>
             <title>Adding a message to a queue</title>
 
 
             <para>
             <para>
@@ -86,9 +86,9 @@ $storageClient = new Zend_Service_WindowsAzure_Storage_Queue();
 $storageClient->putMessage('testqueue', 'This is a test message', 3600); 
 $storageClient->putMessage('testqueue', 'This is a test message', 3600); 
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.read-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.read-queue">
             <title>Reading a message from a queue</title>
             <title>Reading a message from a queue</title>
 
 
             <para>
             <para>
@@ -135,9 +135,9 @@ foreach ($messages as $message) {
 }
 }
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.queue.api.peek-queue">
+        <sect4 id="zend.service.windowsazure.storage.queue.api.peek-queue">
             <title>Check if there are messages in a queue</title>
             <title>Check if there are messages in a queue</title>
 
 
             <para>
             <para>
@@ -166,6 +166,6 @@ foreach ($messages as $message) {
                 <methodname>deleteMessage()</methodname> method.  To do this, use
                 <methodname>deleteMessage()</methodname> method.  To do this, use
                 <methodname>getMessages()</methodname> instead.
                 <methodname>getMessages()</methodname> instead.
             </para>
             </para>
-        </sect3>
-    </sect2>
-</sect1>
+        </sect4>
+    </sect3>
+</sect2>

+ 32 - 32
documentation/manual/en/module_specs/Zend_Service_WindowsAzure_Table.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Reviewed: no -->
 <!-- Reviewed: no -->
-<sect1 id="zend.service.windowsazure.storage.table">
+<sect2 id="zend.service.windowsazure.storage.table">
     <title>Zend_Service_WindowsAzure_Storage_Table</title>
     <title>Zend_Service_WindowsAzure_Storage_Table</title>
 
 
     <para>
     <para>
@@ -26,14 +26,14 @@
         Azure production table storage.
         Azure production table storage.
     </para>
     </para>
 
 
-    <sect2 id="zend.service.windowsazure.storage.table.api">
+    <sect3 id="zend.service.windowsazure.storage.table.api">
         <title>Operations on tables</title>
         <title>Operations on tables</title>
 
 
         <para>
         <para>
             This topic lists some samples of operations that can be executed on tables.
             This topic lists some samples of operations that can be executed on tables.
         </para>
         </para>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.api.create">
+        <sect4 id="zend.service.windowsazure.storage.table.api.create">
             <title>Creating a table</title>
             <title>Creating a table</title>
 
 
             <para>
             <para>
@@ -53,9 +53,9 @@ $result = $storageClient->createTable('testtable');
 echo 'New table name is: ' . $result->Name;
 echo 'New table name is: ' . $result->Name;
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.api.list">
+        <sect4 id="zend.service.windowsazure.storage.table.api.list">
             <title>Listing all tables</title>
             <title>Listing all tables</title>
 
 
             <para>
             <para>
@@ -76,10 +76,10 @@ foreach ($result as $table) {
 }
 }
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
 
 
-    <sect2 id="zend.service.windowsazure.storage.table.entities">
+    <sect3 id="zend.service.windowsazure.storage.table.entities">
         <title>Operations on entities</title>
         <title>Operations on entities</title>
 
 
         <para>
         <para>
@@ -147,7 +147,7 @@ class SampleEntity extends Zend_Service_WindowsAzure_Storage_TableEntity
             <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname>.
             <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname>.
         </para>
         </para>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.enforced">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.enforced">
             <title>Enforced schema entities</title>
             <title>Enforced schema entities</title>
 
 
             <para>
             <para>
@@ -323,16 +323,16 @@ public $Age;
                     </para>
                     </para>
                 </listitem>
                 </listitem>
             </itemizedlist>
             </itemizedlist>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.dynamic">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.dynamic">
             <title>No enforced schema entities (a.k.a. DynamicEntity)</title>
             <title>No enforced schema entities (a.k.a. DynamicEntity)</title>
 
 
             <para>
             <para>
                 To use the <classname>Zend_Service_WindowsAzure_Storage_Table</classname> class
                 To use the <classname>Zend_Service_WindowsAzure_Storage_Table</classname> class
                 without defining a schema, you can make use of the
                 without defining a schema, you can make use of the
                 <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname> class.
                 <classname>Zend_Service_WindowsAzure_Storage_DynamicTableEntity</classname> class.
-                This class inherits <classname>Zend_Service_WindowsAzure_Storage_TableEntity</code>
+                This class inherits <classname>Zend_Service_WindowsAzure_Storage_TableEntity</classname>
                 like an enforced schema class does, but contains additional logic to make it dynamic
                 like an enforced schema class does, but contains additional logic to make it dynamic
                 and not bound to a schema.
                 and not bound to a schema.
             </para>
             </para>
@@ -414,12 +414,12 @@ $target->setAzurePropertyType('Age', 'Edm.Int64');
                 <classname>Zend_Service_WindowsAzure_Storage_TableEntity</classname> if no specific
                 <classname>Zend_Service_WindowsAzure_Storage_TableEntity</classname> if no specific
                 class is passed into Table Storage methods.
                 class is passed into Table Storage methods.
             </para>
             </para>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.api">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.api">
             <title>Entities API examples</title>
             <title>Entities API examples</title>
 
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.insert">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.insert">
                 <title>Inserting an entity</title>
                 <title>Inserting an entity</title>
 
 
                 <para>
                 <para>
@@ -446,9 +446,9 @@ echo 'Timestamp: ' . $result->getTimestamp() . "\n";
 echo 'Etag: ' . $result->getEtag() . "\n";
 echo 'Etag: ' . $result->getEtag() . "\n";
 ]]></programlisting>
 ]]></programlisting>
                 </example>
                 </example>
-            </sect4>
+            </sect5>
 
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.retrieve-by-id">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.retrieve-by-id">
                 <title>Retrieving an entity by partition key and row key</title>
                 <title>Retrieving an entity by partition key and row key</title>
 
 
                 <para>
                 <para>
@@ -468,9 +468,9 @@ $entity= $storageClient->retrieveEntityById(
 );
 );
 ]]></programlisting>
 ]]></programlisting>
                 </example>
                 </example>
-            </sect4>
+            </sect5>
 
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.updating">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.updating">
                 <title>Updating an entity</title>
                 <title>Updating an entity</title>
 
 
                 <para>
                 <para>
@@ -518,9 +518,9 @@ $entity->Name = 'New name';
 $result = $storageClient->updateEntity('testtable', $entity, true); 
 $result = $storageClient->updateEntity('testtable', $entity, true); 
 ]]></programlisting>
 ]]></programlisting>
                 </example>
                 </example>
-            </sect4>
+            </sect5>
 
 
-            <sect4 id="zend.service.windowsazure.storage.table.entities.api.delete">
+            <sect5 id="zend.service.windowsazure.storage.table.entities.api.delete">
                 <title>Deleting an entity</title>
                 <title>Deleting an entity</title>
 
 
                 <para>
                 <para>
@@ -541,10 +541,10 @@ $entity = $storageClient->retrieveEntityById(
 $result = $storageClient->deleteEntity('testtable', $entity);
 $result = $storageClient->deleteEntity('testtable', $entity);
 ]]></programlisting>
 ]]></programlisting>
                 </example>
                 </example>
-            </sect4>
-        </sect3>
+            </sect5>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.querying">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.querying">
             <title>Performing queries</title>
             <title>Performing queries</title>
 
 
             <para>
             <para>
@@ -618,9 +618,9 @@ foreach ($entities as $entity) {
 }
 }
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
+        </sect4>
 
 
-        <sect3 id="zend.service.windowsazure.storage.table.entities.batch">
+        <sect4 id="zend.service.windowsazure.storage.table.entities.batch">
             <title>Batch operations</title>
             <title>Batch operations</title>
 
 
             <para>
             <para>
@@ -657,10 +657,10 @@ foreach ($entities as $entity) {
 $batch->commit();
 $batch->commit();
 ]]></programlisting>
 ]]></programlisting>
             </example>
             </example>
-        </sect3>
-    </sect2>
+        </sect4>
+    </sect3>
   
   
-    <sect2 id="zend.service.windowsazure.storage.table.sessionhandler">
+    <sect3 id="zend.service.windowsazure.storage.table.sessionhandler">
         <title>Table storage session handler</title>
         <title>Table storage session handler</title>
 
 
         <para>
         <para>
@@ -730,9 +730,9 @@ if (!isset($_SESSION['firstVisit'])) {
         <warning>
         <warning>
             <para>
             <para>
                 The <classname>Zend_Service_WindowsAzure_SessionHandler</classname> session handler
                 The <classname>Zend_Service_WindowsAzure_SessionHandler</classname> session handler
-                should be registered before a call to <functionname>session_start()</functionname>
+                should be registered before a call to <modulename>session_start()</modulename>
                 is made!
                 is made!
             </para>
             </para>
         </warning>
         </warning>
-    </sect2>
-</sect1>
+    </sect3>
+</sect2>

+ 20 - 0
documentation/manual/en/tutorials/autoloading-conclusion.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Zend Framework encourages the use of autoloading, and even initializes it by default in
+        <classname>Zend_Application</classname>. Hopefully this tutorial provides you with the
+        information you need to use <classname>Zend_Loader_Autoloader</classname> to its best
+        advantage, as well as extend its capabilities by attaching custom autoloaders or resource
+        autoloaders.
+    </para>
+
+    <para>
+        For more information on its usage, read the <link
+            linkend="zend.loader.autoloader">Zend_Loader_Autoloader</link> and <link
+            linkend="zend.loader.autoloader-resources">Zend_Loader_Autoloader_Resource</link> manual
+        pages.
+    </para>
+</sect1>

+ 101 - 0
documentation/manual/en/tutorials/autoloading-design.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.design">
+    <title>Goals and Design</title>
+
+    <sect2 id="learning.autoloading.design.naming">
+        <title>Class Naming Conventions</title>
+
+        <para>
+            To understand autoloading in Zend Framework, first you need to understand the relationship
+            between class names and class files.
+        </para>
+
+        <para>
+            Zend Framework has borrowed an idea from <ulink url="http://pear.php.net/">PEAR</ulink>,
+            whereby class names have a 1:1 relationship with the filesystem. Simply put, the underscore
+            character ("_") is replaced by a directory separator in order to resolve the path to the
+            file, and then the suffix ".php" is added. For example, the class "Foo_Bar_Baz" would
+            correspond to "Foo/Bar/Baz.php" on the filesystem. The assumption is also that the classes
+            may be resolved via PHP's <constant>include_path</constant> setting, which allows both
+            <methodname>include</methodname> and <methodname>require</methodname> to find the filename
+            via a relative path lookup on the <constant>include_path</constant>.
+        </para>
+
+        <para>
+            Additionally, per PEAR as well as the <ulink
+                url="http://php.net/userlandnaming.tips">PHP project</ulink>, we use and recommend
+            using a vendor or project prefix for your code. What this means is that all classes you
+            write will share a common class prefix; for example, all code in Zend Framework has the
+            prefix "Zend_". This naming convention helps prevent naming collisions. Within Zend
+            Framework, we often refer to this as the "namespace" prefix; be careful not to confuse
+            it with PHP's native namespace implementation.
+        </para>
+
+        <para>
+            Zend Framework follows these simple rules internally, and our coding standards encourage
+            that you do so as well for all library code.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.autoloading.design.autoloader">
+        <title>Autoloader Conventions and Design</title>
+
+        <para>
+            Zend Framework's autoloading support, provided primarily via
+            <classname>Zend_Loader_Autoloader</classname>, has the following goals and design elements:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis role="strong">Provide namespace matching.</emphasis> If the class
+                    namespace prefix is not in a list of registered namespaces, return false
+                    immediately. This allows for more optimistic matching, as well as fallback to other
+                    autoloaders.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow the autoloader to act as a fallback
+                        autoloader.</emphasis> In the case where a team may be widely distributed, or
+                    using an undetermined set of namespace prefixes, the autoloader should still be
+                    configurable such that it will attempt to match any namespace prefix. It will be
+                    noted, however, that this practice is not recommended, as it can lead to unnecessary
+                    lookups.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow toggling error suppression.</emphasis> We feel -- and
+                    the greater PHP community does as well -- that error suppression is a bad idea. It's
+                    expensive, and it masks very real application problems. So, by default, it should be
+                    off. However, if a developer <emphasis>insists</emphasis> that it be on, we allow
+                    toggling it on.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow specifying custom callbacks for
+                        autoloading.</emphasis> Some developers don't want to use
+                    <methodname>Zend_Loader::loadClass()</methodname> for autoloading, but still want to
+                    make use of ZF's mechanisms. <classname>Zend_Loader_Autoloader</classname> allows
+                    specyfing an alternate callback for autoloading.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis role="strong">Allow manipulation of the SPL autoload callback
+                        chain.</emphasis> The purpose of this is to allow specifying additional
+                    autoloaders -- for instance, resource loaders for classes that don't have a 1:1
+                    mapping to the filesystem -- to be registered before or after the primary ZF
+                    autoloader.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+</sect1>

+ 32 - 0
documentation/manual/en/tutorials/autoloading-intro.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.intro">
+    <title>Introduction</title>
+
+    <para>
+        Autoloading is a mechanism that eliminates the need to manually require dependencies within
+        your PHP code. Per <ulink url="http://php.net/autoload">the PHP autoload manual</ulink>,
+        once an autoloader has been defined, it "is automatically called in case you are trying to use
+        a class/interface which hasn't been defined yet."
+    </para>
+
+    <para>
+        Using autoloading, you do not need to worry about <emphasis>where</emphasis> a class exists
+        in your project. With well-defined autoloaders, you do not need to worry about where a class
+        file is relative to the current class file; you simply use the class, and the autoloader
+        will perform the file lookup.
+    </para>
+
+    <para>
+        Additionally, autoloading, because it defers loading to the last possible moment and ensures
+        that a match only has to occur once, can be a huge performance boost -- particularly if you
+        take the time to strip out <methodname>require_once()</methodname> calls before you move
+        to deployment.
+    </para>
+
+    <para>
+        Zend Framework encourages the use of autoloading, and provides several tools to provide
+        autoloading of both library code as well as application code. This tutorial covers these
+        tools, as well as how to use them effectively.
+    </para>
+</sect1>

+ 106 - 0
documentation/manual/en/tutorials/autoloading-resources.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.resources">
+    <title>Resource Autoloading</title>
+
+    <para>
+        Often, when developing an application, it's either difficult to package classes in the 1:1
+        classname:filename standard Zend Framework recommends, or it's advantageous for purposes of
+        packaging not to do so. However, this means you class files will not be found by the
+        autoloader.
+    </para>
+
+    <para>
+        If you read through <link linkend="learning.autoloading.design">the design goals</link> for
+        the autoloader, the last point in that section indicated that the solution should cover this
+        situation. Zend Framework does so with
+        <classname>Zend_Loader_Autoloader_Resource</classname>.
+    </para>
+
+    <para>
+        A resource is just a name that corresponds to a component namespace (which
+        is appended to the autoloader's namespace) and a path (which is relative to
+        the autoloader's base path). In action, you'd do something like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader = new Zend_Application_Module_Autoloader(array(
+    'namespace' => 'Blog',
+    'basePath'  => APPLICATION_PATH . '/modules/blog',
+));
+]]></programlisting>
+
+    <para>
+        Once you have the loader in place, you then need to inform it of the various resource types
+        it's aware of. These resource types are simply subtree/prefix pairs.
+    </para>
+
+    <para>
+        As an example, consider the following tree:
+    </para>
+
+    <programlisting language="text"><![CDATA[
+path/to/some/resources/
+|-- forms/
+|   `-- Guestbook.php        // Foo_Form_Guestbook
+|-- models/
+|   |-- DbTable/
+|   |   `-- Guestbook.php    // Foo_Model_DbTable_Guestbook
+|   |-- Guestbook.php        // Foo_Model_Guestbook
+|   `-- GuestbookMapper.php  // Foo_Model_GuestbookMapper
+]]></programlisting>
+
+    <para>
+        Our first step is creating the resource loader:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader = new Zend_Loader_Autoloader_Resource(array(
+    'basePath'  => 'path/to/some/resources/',
+    'namespace' => 'Foo',
+));
+]]></programlisting>
+
+    <para>
+        Next, we need to define some resource types.
+        <methodname>Zend_Loader_Autoloader_Resourse::addResourceType()</methodname> has three
+        arguments: the "type" of resource (an arbitrary string), the path under the base path in
+        which the resource type may be found, and the component prefix to use for the resource type.
+        In the above tree, we have three resource types: form (in the subdirectory "forms", with a
+        component prefix of "Form"), model (in the subdirectory "models", with a component prefix of
+        "Model"), and dbtable (in the subdirectory "models/DbTable, with a component prefix of
+        "Model_DbTable"). We'd define them as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader->addResourceType('form', 'forms', 'Form')
+       ->addResourceType('model', 'models', 'Model')
+       ->addResourceType('dbtable', 'models/DbTable', 'Model_DbTable');
+]]></programlisting>
+
+    <para>
+        Once defined, we can simply use these classes:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$form      = new Foo_Form_Guestbook();
+$guestbook = new Foo_Model_Guestbook();
+]]></programlisting>
+
+    <note>
+        <title>Module Resource Autoloading</title>
+
+        <para>
+            Zend Framework's MVC layer encourages the use of "modules", which are self-contained
+            applications within your site. Modules typically have a number of resource types by
+            default, and Zend Framework even <link linkend="project-structure.filesystem">recommends
+                a standard directory layout for modules</link>. Resource autoloaders are therefore
+            quite useful in this paradigm -- so useful that they are enabled by default when you
+            create a bootstrap class for your module that extends
+            <classname>Zend_Application_Module_Bootstrap</classname>. For more information, read
+            the <link
+                linkend="zend.loader.autoloader-resource.module">Zend_Loader_Autoloader_Module
+                documentation</link>.
+        </para>
+    </note>
+</sect1>

+ 158 - 0
documentation/manual/en/tutorials/autoloading-usage.xml

@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.autoloading.usage">
+    <title>Basic Autoloader Usage</title>
+
+    <para>
+        Now that we have an understanding of what autoloading is and the goals and design of Zend
+        Framework's autoloading solution, let's look at how to use
+        <classname>Zend_Loader_Autoloader</classname>.
+    </para>
+
+    <para>
+        In the simplest case, you would simply require the class, and then instantiate it. Since
+        <classname>Zend_Loader_Autoloader</classname> is a singleton (due to the fact that the SPL
+        autoloader is a single resource), we use <methodname>getInstance()</methodname> to retrieve
+        an instance.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/Autoloader.php';
+Zend_Loader_Autoloader::getInstance();
+]]></programlisting>
+
+    <para>
+        By default, this will allow loading any classes with the class namespace prefixes of "Zend_"
+        or "ZendX_", as long as they are on your <constant>include_path</constant>.
+    </para>
+
+    <para>
+        What happens if you have other namespace prefixes you wish to use? The best, and simplest,
+        way is to call the <methodname>registerNamespace()</methodname> method on the instance. You
+        can pass a single namespace prefix, or an array of them:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/Autoloader.php';
+$loader = Zend_Loader_Autoloader::getInstance();
+$loader->registerNamespace('Foo_');
+$loader->registerNamespace(array('Foo_', 'Bar_'));
+]]></programlisting>
+
+    <para>
+        Alternately, you can tell <classname>Zend_Loader_Autoloader</classname> to act as a
+        "fallback" autoloader. This means that it will try to resolve any class regardless of
+        namespace prefix.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$loader->setFallbackAutoloader(true);
+]]></programlisting>
+
+    <warning>
+        <title>Do not use as a fallback autoloader</title>
+
+        <para>
+            While it's tempting to use <classname>Zend_Loader_Autoloader</classname> as a fallback
+            autoloader, we do not recommend the practice.
+        </para>
+
+        <para>
+            Internally, <classname>Zend_Loader_Autoloader</classname> uses
+            <methodname>Zend_Loader::loadClass()</methodname> to load classes. That method uses
+            <methodname>include()</methodname> to attempt to load the given class file.
+            <methodname>include()</methodname> will return a boolean <constant>false</constant>
+            if not successful -- but also issues a PHP warning. This latter fact can lead to some
+            issues:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    If <constant>display_errors</constant> is enabled, the warning will be included
+                    in output.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    Depending on the <constant>error_reporting</constant> level you have chosen, it
+                    could also clutter your logs.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            You can suppress the error messages (the <classname>Zend_Loader_Autoloader</classname>
+            documentation details this), but note that the suppression is only relevant when
+            <constant>display_errors</constant> is enabled; the error log will always display the
+            messages. For these reasons, we recommend always configuring the namespace prefixes the
+            autoloader should be aware of
+        </para>
+    </warning>
+
+    <note>
+        <title>Namespace Prefixes vs PHP Namespaces</title>
+
+        <para>
+            At the time this is written, PHP 5.3 has been released. With that version, PHP now has
+            official namespace support.
+        </para>
+
+        <para>
+            However, Zend Framework predates PHP 5.3, and thus namespaces. Within Zend Framework,
+            when we refer to "namespaces", we are referring to a practice whereby classes are
+            prefixed with a vender "namespace". As an example, all Zend Framework class names are
+            prefixed with "Zend_" -- that is our vendor "namespace".
+        </para>
+
+        <para>
+            Zend Framework plans to offer native PHP namespace support to the autoloader in future
+            revisions, and its own library will utilize namespaces starting with version 2.0.0.
+        </para>
+    </note>
+
+    <para>
+        If you have a custom autoloader you wish to use with Zend Framework -- perhaps an autoloader
+        from a third-party library you are also using -- you can manage it with
+        <classname>Zend_Loader_Autoloader</classname>'s <methodname>pushAutoloader()</methodname>
+        and <methodname>unshiftAutoloader()</methodname> methods. These methods will append or
+        prepend, respectively, autoloaders to a chain that is called prior to executing ZF's
+        internal autoloading mechanism. This approach offers the following benefits:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Each method takes an optional second argument, a class namespace prefix. This can be
+                used to indicate that the given autoloader should only be used when looking up
+                classes with that given class prefix. If the class being resolved does not have that
+                prefix, the autoloader will be skipped -- which can lead to performance
+                improvements.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                If you need to manipulate <methodname>spl_autoload</methodname>'s registry, any
+                autoloaders that are callbacks pointing to instance methods can pose issues, as
+                <methodname>spl_autoload_functions()</methodname> does not return the exact same
+                callbacks. <classname>Zend_Loader_Autoloader</classname> has no such limitation.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Autoloaders managed this way may be any valid PHP callback.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// Append function 'my_autoloader' to the stack, 
+// to manage classes with the prefix 'My_':
+$loader->pushAutoloader('my_autoloader', 'My_');
+
+// Prepend static method Foo_Loader::autoload() to the stack, 
+// to manage classes with the prefix 'Foo_':
+$loader->unshiftAutoloader(array('Foo_Loader', 'autoload'), 'Foo_');
+]]></programlisting>
+</sect1>

+ 384 - 0
documentation/manual/en/tutorials/form-decorators-composite.xml

@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.composite">
+    <title>Creating and Rendering Composite Elements</title>
+
+    <para>
+        In <link linkend="learning.form.decorators.individual">the last section</link>, we had an
+        example showing a "date of birth element":
+    </para>
+
+
+    <programlisting language="php"><![CDATA[
+<div class="element">
+    <?php echo $form->dateOfBirth->renderLabel() ?>
+    <?php echo $this->formText('dateOfBirth[day]', '', array(
+        'size' => 2, 'maxlength' => 2)) ?>
+    /
+    <?php echo $this->formText('dateOfBirth[month]', '', array(
+        'size' => 2, 'maxlength' => 2)) ?>
+    /
+    <?php echo $this->formText('dateOfBirth[year]', '', array(
+        'size' => 4, 'maxlength' => 4)) ?>
+</div>
+]]></programlisting>
+
+    <para>
+        How might you represent this element as a <classname>Zend_Form_Element</classname>?
+        How might you write a decorator to render it?
+    </para>
+
+    <sect2 id="learning.form.decorators.composite.element">
+        <title>The Element</title>
+
+        <para>
+            The questions about how the element would work include:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    How would you set and retrieve the value?
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    How would you validate the value?
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    Regardless, how would you then allow for discrete form inputs for the three
+                    segments (day, month, year)?
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            The first two questions center around the form element itself: how would
+            <methodname>setValue()</methodname> and <methodname>getValue()</methodname> work?
+            There's actually another question implied by the question about the decorator: how would
+            you retrieve the discrete date segments from the element and/or set them?
+        </para>
+
+        <para>
+            The solution is to override the <methodname>setValue()</methodname> method of your
+            element to provide some custom logic. In this particular case, our element should have
+            three discrete behaviors:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    If an integer timestamp is provided, it should be used to determine and store
+                    the day, month, and year.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    If a textual string is provided, it should be cast to a timestamp, and then that
+                    value used to determine and store the day, month, and year.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    If an array containing keys for date, month, and year is provided, those values
+                    should be stored.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            Internally, the day, month, and year will be stored discretely. When the value of the
+            element is retrieved, it will be done so in a normalized string format. We'll override
+            <methodname>getValue()</methodname> as well to assemble the discrete date segments into
+            a final string.
+        </para>
+
+        <para>
+            Here's what the class would look like:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    protected $_dateFormat = '%year%-%month%-%day%';
+    protected $_day;
+    protected $_month;
+    protected $_year;
+
+    public function setDay($value)
+    {
+        $this->_day = (int) $value;
+        return $this;
+    }
+
+    public function getDay()
+    {
+        return $this->_day;
+    }
+
+    public function setMonth($value)
+    {
+        $this->_month = (int) $value;
+        return $this;
+    }
+
+    public function getMonth()
+    {
+        return $this->_month;
+    }
+
+    public function setYear($value)
+    {
+        $this->_year = (int) $value;
+        return $this;
+    }
+
+    public function getYear()
+    {
+        return $this->_year;
+    }
+
+    public function setValue($value)
+    {
+        if (is_int($value)) {
+            $this->setDay(date('d', $value))
+                 ->setMonth(date('m', $value))
+                 ->setYear(date('Y', $value));
+        } elseif (is_string($value)) {
+            $date = strtotime($value);
+            $this->setDay(date('d', $date))
+                 ->setMonth(date('m', $date))
+                 ->setYear(date('Y', $date));
+        } elseif (is_array($value)
+                  && (isset($value['day']) 
+                      && isset($value['month']) 
+                      && isset($value['year'])
+                  )
+        ) {
+            $this->setDay($value['day'])
+                 ->setMonth($value['month'])
+                 ->setYear($value['year']);
+        } else {
+            throw new Exception('Invalid date value provided');
+        }
+
+        return $this;
+    }
+
+    public function getValue()
+    {
+        return str_replace(
+            array('%year%', '%month%', '%day%'),
+            array($this->getYear(), $this->getMonth(), $this->getDay()),
+            $this->_dateFormat
+        );
+    }
+}
+]]></programlisting>
+
+        <para>
+            This class gives some nice flexibility -- we can set default values from our database, and
+            be certain that the value will be stored and represented correctly.  Additionally, we can
+            allow for the value to be set from an array passed via form input. Finally, we have discrete
+            accessors for each date segment, which we can now use in a decorator to create a composite
+            element.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.composite.decorator">
+        <title>The Decorator</title>
+
+        <para>
+            Revisiting the example from the last section, let's assume that we want users to input
+            each of the year, month, and day separately. PHP fortunately allows us to use array
+            notation when creating elements, so it's still possible to capture these three entities
+            into a single value -- and we've now created a <classname>Zend_Form</classname> element
+            that can handle such an array value.
+        </para>
+
+        <para>
+            The decorator is relatively simple: it will grab the day, month, and year from the
+            element, and pass each to a discrete view helper to render individual form inputs; these
+            will then be aggregated to form the final markup.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract
+{
+    public function render($content)
+    {
+        $element = $this->getElement();
+        if (!$element instanceof My_Form_Element_Date) {
+            // only want to render Date elements
+            return $content;
+        }
+
+        $view = $element->getView();
+        if (!$view instanceof Zend_View_Interface) {
+            // using view helpers, so do nothing if no view present
+            return $content;
+        }
+
+        $day   = $element->getDay();
+        $month = $element->getMonth();
+        $year  = $element->getYear();
+        $name  = $element->getFullyQualifiedName();
+
+        $params = array(
+            'size'      => 2,
+            'maxlength' => 2,
+        );
+        $yearParams = array(
+            'size'      => 4,
+            'maxlength' => 4,
+        );
+
+        $markup = $view->formText($name . '[day]', $day, $params)
+                . ' / ' . $view->formText($name . '[month]', $month, $params)
+                . ' / ' . $view->formText($name . '[year]', $year, $yearParams);
+
+        switch ($this->getPlacement()) {
+            case self::PREPEND:
+                return $markup . $this->getSeparator() . $content;
+            case self::APPEND:
+            default:
+                return $content . $this->getSeparator() . $markup;
+        }
+    }
+}
+]]></programlisting>
+
+    <para>
+        We now have to do a minor tweak to our form element, and tell it that we want to use the
+        above decorator as a default. That takes two steps. First, we need to inform the element of
+        the decorator path. We can do that in the constructor:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    // ...
+
+    public function __construct($spec, $options = null)
+    {
+        $this->addPrefixPath(
+            'My_Form_Decorator', 
+            'My/Form/Decorator', 
+            'decorator'
+        );
+        parent::__construct($spec, $options);
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        Note that this is being done in the constructor and not in <methodname>init()</methodname>.
+        This is for two reasons. First, it allows extending the element later to add logic in
+        <methodname>init</methodname> without needing to worry about calling
+        <methodname>parent::init()</methodname>. Second, it allows passing additional plugin paths
+        via configuration or within an <methodname>init</methodname> method that will then allow
+        overriding the default <classname>Date</classname> decorator with my own replacement.
+    </para>
+
+    <para>
+        Next, we need to override the <methodname>loadDefaultDecorators()</methodname> method to use
+        our new <classname>Date</classname> decorator:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_Element_Date extends Zend_Form_Element_Xhtml
+{
+    // ...
+
+    public function loadDefaultDecorators()
+    {
+        if ($this->loadDefaultDecoratorsIsDisabled()) {
+            return;
+        }
+
+        $decorators = $this->getDecorators();
+        if (empty($decorators)) {
+            $this->addDecorator('Date')
+                 ->addDecorator('Errors')
+                 ->addDecorator('Description', array(
+                     'tag'   => 'p', 
+                     'class' => 'description'
+                 ))
+                 ->addDecorator('HtmlTag', array(
+                     'tag' => 'dd',
+                     'id'  => $this->getName() . '-element'
+                 ))
+                 ->addDecorator('Label', array('tag' => 'dt'));
+        }
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        What does the final output look like? Let's consider the following element:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$d = new My_Form_Element_Date('dateOfBirth');
+$d->setLabel('Date of Birth: ')
+  ->setView(new Zend_View());
+
+// These are equivalent:
+$d->setValue('20 April 2009');
+$d->setValue(array('year' => '2009', 'month' => '04', 'day' => '20'));
+]]></programlisting>
+
+    <para>
+        If you then echo this element, you get the following markup (with some slight whitespace
+        modifications for readability):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<dt id="dateOfBirth-label"><label for="dateOfBirth" class="optional">
+    Date of Birth:
+</label></dt>
+<dd id="dateOfBirth-element">
+    <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day" 
+        value="20" size="2" maxlength="2"> / 
+    <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
+        value="4" size="2" maxlength="2"> / 
+    <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
+        value="2009" size="4" maxlength="4">
+</dd>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.composite.conclusion">
+        <title>Conclusion</title>
+
+        <para>
+            We now have an element that can render multiple related form input fields, and then
+            handle the aggregated fields as a single entity -- the <varname>dateOfBirth</varname>
+            element will be passed as an array to the element, and the element will then, as we
+            noted earlier, create the appropriate date segments and return a value we can use for
+            most backends.
+        </para>
+
+        <para>
+            Additionally, we can use different decorators with the element. If we wanted to use a
+            <ulink url="http://dojotoolkit.org/">Dojo</ulink> <classname>DateTextBox</classname>
+            dijit decorator -- which accepts and returns string values -- we can, with no
+            modifications to the element itself.
+        </para>
+
+        <para>
+            In the end, you get a uniform element API you can use to describe an element
+            representing a composite value.
+        </para>
+    </sect2>
+</sect1>

+ 12 - 0
documentation/manual/en/tutorials/form-decorators-conclusion.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Form decorators are a system that takes some time to learn. At first, they will likely
+        feel cumbersome and overly complex. Hopefully the various topics covered in this chapter
+        will help you to understand both how they work, as well as strategies for using them
+        effectively in your forms.
+    </para>
+</sect1>

+ 283 - 0
documentation/manual/en/tutorials/form-decorators-individual.xml

@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.individual">
+    <title>Rendering Individual Decorators</title>
+
+    <para>
+        In the <link linkend="learning.form.decorators.layering">previous section</link>, we
+        looked at how you can combine decorators to create complex output. We noted that while you
+        have a ton of flexibility with this approach, it also adds some complexity and overhead. In
+        this section, we will examine how to render decorators individually in order to create
+        custom markup for forms and/or individual elements. 
+    </para>
+
+    <para>
+        Once you have registered your decorators, you can later retrieve them by name from the
+        element. Let's review the previous example:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput'
+        array('SimpleLabel', array('placement' => 'append')),
+    ),
+));
+]]></programlisting>
+
+    <para>
+        If we wanted to pull and render just the <classname>SimpleInput</classname> decorator, we
+        can do so using the <methodname>getDecorator()</methodname> method:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$decorator = $element->getDecorator('SimpleInput');
+echo $decorator->render('');
+]]></programlisting>
+
+    <para>
+        This is pretty easy, but it can be made even easier; let's do it in a single line:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+echo $element->getDecorator('SimpleInput')->render('');
+]]></programlisting>
+
+    <para>
+        Not too bad, but still a little complex. To make this easier, a shorthand notation was
+        introduced into <classname>Zend_Form</classname> in 1.7: you can render any registered
+        decorator by calling a method of the format <methodname>renderDecoratorName()</methodname>.
+        This will effectively perform what you see above, but makes the <varname>$content</varname>
+        argument optional and simplifies the usage:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+echo $element->renderSimpleInput();
+]]></programlisting>
+
+    <para>
+        This is a neat trick, but how and why would you use it?
+    </para>
+
+    <para>
+        Many developers and designers have very precise markup needs for their forms. They would
+        rather have full control over the output than rely on a more automated solution which may or
+        may not conform to their design. In other cases, the form layout may require a lot of
+        specialized markup -- grouping arbitrary elements, making some invisible unless a particular
+        link is selected, etc.
+    </para>
+
+    <para>
+        Let's utilize the ability to render individual decorators to create some specialized markup.
+    </para>
+
+    <para>
+        First, let's define a form. Our form will capture a user's demographic details. The markup
+        will be highly customized, and in some cases use view helpers directly instead of form
+        elements in order to achieve its goals.  Here is the basic form definition:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Form_UserDemographics extends Zend_Form
+{
+    public function init()
+    {
+        // Add a path for my own decorators
+        $this->addElementPrefixPaths(array(
+            'decorator' => array('My_Decorator' => 'My/Decorator'),
+        ));
+
+        $this->addElement('text', 'firstName', array(
+            'label' => 'First name: ',
+        ));
+        $this->addElement('text', 'lastName', array(
+            'label' => 'Last name: ',
+        ));
+        $this->addElement('text', 'title', array(
+            'label' => 'Title: ',
+        ));
+        $this->addElement('text', 'dateOfBirth', array(
+            'label' => 'Date of Birth (DD/MM/YYYY): ',
+        ));
+        $this->addElement('text', 'email', array(
+            'label' => 'Your email address: ',
+        ));
+        $this->addElement('password', 'password', array(
+            'label' => 'Password: ',
+        ));
+        $this->addElement('password', 'passwordConfirmation', array(
+            'label' => 'Confirm Password: ',
+        ));
+    }
+}
+]]></programlisting>
+
+    <note>
+        <para>
+            We're not defining any validators or filters at this time, as they are not relevant to
+            the discussion of decoration. In a real-world scenario, you should define them.
+        </para>
+    </note>
+
+    <para>
+        With that out of the way, let's consider how we might want to display this form. One common
+        idiom with first/last names is to display them on a single line; when a title is provided,
+        that is often on the same line as well.  Dates, when not using a JavaScript date chooser,
+        will often be separated into three fields displayed side by side.
+    </para>
+
+    <para>
+        Let's use the ability to render an element's decorators one by one to accomplish this.
+        First, let's note that no explicit decorators were defined for the given elements. As a
+        refresher, the default decorators for (most) elements are:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>ViewHelper</classname>: utilize a view helper to render a form input
+            </para>
+        </listitem>
+        
+        <listitem>
+            <para>
+                <classname>Errors</classname>: utilize the <classname>FormErrors</classname> view
+                helper to render validation errors
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Description</classname>: utilize the <classname>FormNote</classname> view
+                helper to render the element description (if any)
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>HtmlTag</classname>: wrap the above three items in a
+                <code>&lt;dd&gt;</code> tag
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Label</classname>: render the element label using the
+                <classname>FormLabel</classname> view helper (and wrap it in a
+                <code>&lt;dt&gt;</code> tag)
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Also, as a refresher, you can access any element of a form as if it were a class property;
+        simply reference the element by the name you assigned it.
+    </para>
+
+    <para>
+        Our view script might then look like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<?php 
+$form = $this->form; 
+// Remove <dt> from label generation
+foreach ($form->getElements() as $element) {
+    $element->getDecorator('label')->setOption('tag', null);
+}
+?>
+<form method="<?php echo $form->getMethod() ?>" action="<?php echo
+    $form->getAction()?>">
+    <div class="element">
+        <?php echo $form->title->renderLabel() 
+              . $form->title->renderViewHelper() ?>
+        <?php echo $form->firstName->renderLabel() 
+              . $form->firstName->renderViewHelper() ?>
+        <?php echo $form->lastName->renderLabel() 
+              . $form->lastName->renderViewHelper() ?>
+    </div>
+    <div class="element">
+        <?php echo $form->dateOfBirth->renderLabel() ?>
+        <?php echo $this->formText('dateOfBirth['day']', '', array(
+            'size' => 2, 'maxlength' => 2)) ?>
+        /
+        <?php echo $this->formText('dateOfBirth['month']', '', array(
+            'size' => 2, 'maxlength' => 2)) ?>
+        /
+        <?php echo $this->formText('dateOfBirth['year']', '', array(
+            'size' => 4, 'maxlength' => 4)) ?>
+    </div>
+    <div class="element">
+        <?php echo $form->password->renderLabel() 
+              . $form->password->renderViewHelper() ?>
+    </div>
+    <div class="element">
+        <?php echo $form->passwordConfirmation->renderLabel() 
+              . $form->passwordConfirmation->renderViewHelper() ?>
+    </div>
+    <?php echo $this->formSubmit('submit', 'Save') ?>
+</form>
+]]></programlisting>
+
+    <para>
+        If you use the above view script, you'll get approximately the following HTML (approximate,
+        as the HTML below is formatted):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<form method="post" action="">
+    <div class="element">
+        <label for="title" tag="" class="optional">Title:</label>
+        <input type="text" name="title" id="title" value=""/>
+
+        <label for="firstName" tag="" class="optional">First name:</label>
+        <input type="text" name="firstName" id="firstName" value=""/>
+        
+        <label for="lastName" tag="" class="optional">Last name:</label>
+        <input type="text" name="lastName" id="lastName" value=""/>
+    </div>
+
+    <div class="element">
+        <label for="dateOfBirth" tag="" class="optional">Date of Birth
+            (DD/MM/YYYY):</label>
+        <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day" 
+            value="" size="2" maxlength="2"/>
+        /
+        <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
+            value="" size="2" maxlength="2"/>
+        /
+        <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
+            value="" size="4" maxlength="4"/>
+    </div>
+
+    <div class="element">
+        <label for="password" tag="" class="optional">Password:</label>
+        <input type="password" name="password" id="password" value=""/>
+    </div>
+
+    <div class="element">
+        <label for="passwordConfirmation" tag="" class="" id="submit" 
+            value="Save"/>
+</form>
+]]></programlisting>
+
+    <para>
+        It may not be truly pretty, but with some CSS, it could be made to look exactly how you
+        might want to see it. The main point, however, is that this form was generated using almost
+        entirely custom markup, while still leveraging decorators for the most common markup (and to
+        ensure things like escaping with htmlentities and value injection occur).
+    </para>
+
+    <para>
+        By this point in the tutorial, you should be getting fairly comfortable with the markup
+        possibilities using <classname>Zend_Form</classname>'s decorators. In the next section, we'll
+        revisit the date element from above, and demonstrate how to create a custom element
+        and decorator for composite elements.
+    </para>
+</sect1>

+ 22 - 0
documentation/manual/en/tutorials/form-decorators-intro.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.intro">
+    <title>Introduction</title>
+
+    <para>
+        <link linkend="zend.form">Zend_Form</link> utilizes the <emphasis>decorator</emphasis>
+        pattern in order to render elements and forms. Unlike the classic <ulink
+            url="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</ulink>, in
+        which you pass an object to a wrapping class, decorators in <classname>Zend_Form</classname>
+        implement a <ulink url="http://en.wikipedia.org/wiki/Strategy_pattern">strategy
+            pattern</ulink>, and utilize the metadata contained in an element or form in order to
+        create a representation of it
+    </para>
+
+    <para>
+        Don't let the terminology scare you away, however; at heart, decorators in
+        <classname>Zend_Form</classname> are not terribly difficult, and the mini-tutorials that
+        follow should help you along the way. They will guide you through the basics of decoration,
+        all the way to creating decorators for composite elements.
+    </para>
+</sect1>

+ 362 - 0
documentation/manual/en/tutorials/form-decorators-layering.xml

@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.layering">
+    <title>Layering Decorators</title>
+
+    <para>
+        If you were following closely in <link linkend="learning.form.decorators.simplest">the
+            previous section</link>, you may have noticed that a decorator's
+        <methodname>render()</methodname> method takes a single argument,
+        <varname>$content</varname>. This is expected to be a string.
+        <methodname>render()</methodname> will then take this string and decide to either replace
+        it, append to it, or prepend it. This allows you to have a chain of decorators -- which
+        allows you to create decorators that render only a subset of the element's metadata, and
+        then layer these decorators to build the full markup for the element. 
+    </para>
+
+    <para>
+        Let's look at how this works in practice.
+    </para>
+
+    <para>
+        For most form element types, the following decorators are used:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>ViewHelper</classname> (render the form input using one of the standard
+                form view helpers).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Errors</classname> (render validation errors via an unordered list).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Description</classname> (render any description attached to the element;
+                often used for tooltips).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>HtmlTag</classname> (wrap all of the above in a <code>&lt;dd&gt;</code>
+                tag.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Label</classname> (render the label preceding the above, wrapped in a
+                <code>&lt;dt&gt;</code> tag.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        You'll notice that each of these decorators does just one thing, and operates on one
+        specific piece of metadata stored in the form element: the <classname>Errors</classname>
+        decorator pulls validation errors and renders them; the <classname>Label</classname>
+        decorator pulls just the label and renders it. This allows the individual decorators to be
+        very succinct, repeatable, and, more importantly, testable.
+    </para>
+
+    <para>
+        It's also where that <varname>$content</varname> argument comes into play: each decorator's
+        <methodname>render()</methodname> method is designed to accept content, and then either
+        replace it (usually by wrapping it), prepend to it, or append to it.
+    </para>
+
+    <para>
+        So, it's best to think of the process of decoration as one of building an onion from the
+        inside out.
+    </para>
+
+    <para>
+        To simplify the process, we'll take a look at the example from <link
+            linkend="learning.form.decorators.simplest">the previous section</link>. Recall:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>'
+                       . '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $label   = htmlentities($element->getLabel());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $label, $id, $name, $value);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Let's now remove the label functionality, and build a separate decorator for that.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $name, $value);
+        return $markup;
+    }
+}
+
+class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $id      = htmlentities($element->getId());
+        $label   = htmlentities($element->getLabel());
+
+        $markup = sprint($this->_format, $id, $label);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now, this may look all well and good, but here's the problem: as written currently, the last
+        decorator to run wins, and overwrites everything.  You'll end up with just the input, or
+        just the label, depending on which you register last.
+    </para>
+
+    <para>
+        To overcome this, simply concatenate the passed in <varname>$content</varname> with the
+        markup somehow:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+return $content . $markup;
+]]></programlisting>
+
+    <para>
+        The problem with the above approach comes when you want to programmatically choose whether
+        the original content should precede or append the new markup. Fortunately, there's a
+        standard mechanism for this already; <classname>Zend_Form_Decorator_Abstract</classname> has
+        a concept of placement and defines some constants for matching it. Additionally, it allows
+        specifying a separator to place between the two. Let's make use of those:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $id, $name, $value);
+
+        $placement = $this->getPlacement();
+        $separator = $this->getSeparator();
+        switch ($placement) {
+            case self::PREPEND:
+                return $markup . $separator . $content;
+            case self::APPEND:
+            default:
+                return $content . $separator . $markup;
+        }
+    }
+}
+
+class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $id      = htmlentities($element->getId());
+        $label   = htmlentities($element->getLabel());
+
+        $markup = sprint($this->_format, $id, $label);
+
+        $placement = $this->getPlacement();
+        $separator = $this->getSeparator();
+        switch ($placement) {
+            case self::APPEND:
+                return $markup . $separator . $content;
+            case self::PREPEND:
+            default:
+                return $content . $separator . $markup;
+        }
+    }
+}
+]]></programlisting>
+
+    <para>
+        Notice in the above that I'm switching the default case for each; the assumption will be
+        that labels prepend content, and input appends.
+    </para>
+
+    <para>
+        Now, let's create a form element that uses these:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput',
+        'SimpleLabel',
+    ),
+));
+]]></programlisting>
+
+    <para>
+        How will this work? When we call <methodname>render()</methodname>, the element will iterate
+        through the various attached decorators, calling <methodname>render()</methodname> on each.
+        It will pass an empty string to the very first, and then whatever content is created will be
+        passed to the next, and so on:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Initial content is an empty string: ''.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                '' is passed to the <classname>SimpleInput</classname> decorator, which then
+                generates a form input that it appends to the empty string: <code>&lt;input
+                    id="bar-foo" name="bar[foo]" type="text" value="test"/&gt;</code>.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                The input is then passed as content to the <classname>SimpleLabel</classname>
+                decorator, which generates a label and prepends it to the original content; the
+                default separator is a <constant>PHP_EOL</constant> character, giving us this:
+                <code>&lt;label for="bar-foo"&gt;\n&lt;input id="bar-foo" name="bar[foo]"
+                    type="text" value="test"/&gt;</code>.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        But wait a second! What if you wanted the label to come after the input for some reason?
+        Remember that "placement" flag? You can pass it as an option to the decorator. The easiest
+        way to do this is to pass an array of options with the decorator during element creation:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array(
+        'SimpleInput'
+        array('SimpleLabel', array('placement' => 'append')),
+    ),
+));
+]]></programlisting>
+
+    <para>
+        Notice that when passing options, you must wrap the decorator within an array; this hints to
+        the constructor that options are available. The decorator name is the first element of the
+        array, and options are passed in an array to the second element of the array.
+    </para>
+
+    <para>
+        The above results in the markup <code>&lt;input id="bar-foo" name="bar[foo]" type="text"
+            value="test"/&gt;\n&lt;label for="bar-foo"&gt;</code>.
+    </para>
+
+    <para>
+        Using this technique, you can have decorators that target specific metadata of the element
+        or form and create only the markup relevant to that metadata; by using mulitiple decorators,
+        you can then build up the complete element markup. Our onion is the result.
+    </para>
+
+    <para>
+        There are pros and cons to this approach. First, the cons:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                More complex to implement. You have to pay careful attention to the decorators you
+                use and what placement you utilize in order to build up the markup in the correct
+                sequence.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                More resource intensive. More decorators means more objects; multiply this by the
+                number of elements you have in a form, and you may end up with some serious resource
+                usage. Caching can help here.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        The advantages are compelling, though:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Reusable decorators. You can create truly re-usable decorators with this technique,
+                as you don't have to worry about the complete markup, but only markup for one or a
+                few pieces of element/form metadata.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Ultimate flexibility. You can theoretically generate any markup combination you want
+                from a small number of decorators.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        While the above examples are the intended usage of decorators within
+        <classname>Zend_Form</classname>, it's often hard to wrap your head around how the
+        decorators interact with one another to build the final markup. For this reason, some
+        flexibility was added in the 1.7 series to make rendering individual decorators possible --
+        which gives some Rails-like simplicity to rendering forms. We'll look at that in the next
+        section.
+    </para>
+</sect1>

+ 244 - 0
documentation/manual/en/tutorials/form-decorators-simplest.xml

@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.form.decorators.simplest">
+    <title>Decorator Basics</title>
+
+    <sect2 id="learning.form.decorators.simplest.decorator-overview">
+        <title>Overview of the Decorator Pattern</title>
+
+        <para>
+            To begin, we'll cover some background on the <ulink
+                url="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator design
+                pattern</ulink>.  One common technique is to define a common interface that both
+            your originating object and decorator will implement; your decorator than accepts the
+            originating object as a dependency, and will either proxy to it or override its methods.
+            Let's put that into code to make it more easily understood:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+interface Window
+{
+    public function isOpen();
+    public function open();
+    public function close();
+}
+
+class StandardWindow implements Window
+{
+    protected $_open = false;
+
+    public function isOpen()
+    {
+        return $this->_open;
+    }
+
+    public function open()
+    {
+        if (!$this->_open) {
+            $this->_open = true;
+        }
+    }
+
+    public function close()
+    {
+        if ($this->_open) {
+            $this->_open = false;
+        }
+    }
+}
+
+class LockedWindow implements Window
+{
+    protected $_window;
+
+    public function __construct(Window $window)
+    {
+        $this->_window = $window;
+        $this->_window->close();
+    }
+
+    public function isOpen()
+    {
+        return false;
+    }
+
+    public function open()
+    {
+        throw new Exception('Cannot open locked windows');
+    }
+
+    public function close()
+    {
+        $this->_window->close();
+    }
+}
+]]></programlisting>
+
+        <para>
+            You then create an object of type <classname>StandardWindow</classname>, pass it to the
+            constructor of <classname>LockedWindow</classname>, and your window instance now has
+            different behavior. The beauty is that you don't have to implement any sort of "locking"
+            functionality on your standard window class -- the decorator takes care of that for you.
+            In the meantime, you can pass your locked window around as if it were just another
+            window.
+        </para>
+
+        <para>
+            One particular place where the decorator pattern is useful is for creating textual
+            representations of objects. As an example, you might have a "Person" object that, by
+            itself, has no textual representation. By using the Decorator pattern, you can create an
+            object that will act as if it were a Person, but also provide the ability to render that
+            Person textually.
+        </para>
+
+        <para>
+            In this particular example, we're going to use <ulink
+                url="http://en.wikipedia.org/wiki/Duck_typing">duck typing</ulink> instead of an
+            explicit interface. This allows our implementation to be a bit more flexible, while
+            still allowing the decorator object to act exactly as if it were a Person object.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Person
+{
+    public function setFirstName($name) {}
+    public function getFirstName() {}
+    public function setLastName($name) {}
+    public function getLastName() {}
+    public function setTitle($title) {}
+    public function getTitle() {}
+}
+
+class TextPerson
+{
+    protected $_person;
+
+    public function __construct(Person $person)
+    {
+        $this->_person = $person;
+    }
+
+    public function __call($method, $args)
+    {
+        if (!method_exists($this->_person, $method)) {
+            throw new Exception('Invalid method called on HtmlPerson: ' 
+                .  $method);
+        }
+        return call_user_func_array(array($this->_person, $method), $args);
+    }
+
+    public function __toString()
+    {
+        return $this->_person->getTitle() . ' '
+            . $this->_person->getFirstName() . ' '
+            . $this->_person->getLastName();
+    }
+}
+]]></programlisting>
+
+        <para>
+            In this example, you pass your <classname>Person</classname> instance to the
+            <classname>TextPerson</classname> constructor. By using method overloading, you are able
+            to continue to call all the methods of <classname>Person</classname> -- to set the first
+            name, last name, or title -- but you also now gain a string representation via the
+            <methodname>__toString()</methodname> method.
+        </para>
+
+        <para>
+            This latter example is getting close to how <classname>Zend_Form</classname> decorators
+            work. The key difference is that instead of a decorator wrapping the element, the
+            element has one or more decorators attached to it that it then injects itself into in
+            order to render.  The decorator then can access the element's methods and properties in
+            order to create a representation of the element -- or a subset of it.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.form.decorators.simplest.first-decorator">
+        <title>Creating Your First Decorator</title>
+
+        <para>
+            <classname>Zend_Form</classname> decorators all implement a common interface,
+            <interfacename>Zend_Form_Decorator_Interface</interfacename>. That interface provides
+            the ability to set decorator-specific options, register and retrieve the element, and
+            render. A base decorator, <classname>Zend_Form_Decorator_Abstract</classname>, provides
+            most of the functionality you will ever need, with the exception of the rendering logic.
+        </para>
+
+        <para>
+            Let's consider a situation where we simply want to render an element as a standard form
+            text input with a label. We won't worry about error handling or whether or not the
+            element should be wrapped within other tags for now -- just the basics. Such a decorator
+            might look like this:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
+{
+    protected $_format = '<label for="%s">%s</label>'
+                       . '<input id="%s" name="%s" type="text" value="%s"/>';
+
+    public function render($content)
+    {
+        $element = $this->getElement();
+        $name    = htmlentities($element->getFullyQualifiedName());
+        $label   = htmlentities($element->getLabel());
+        $id      = htmlentities($element->getId());
+        $value   = htmlentities($element->getValue());
+
+        $markup  = sprintf($this->_format, $name, $label, $id, $name, $value);
+        return $markup;
+    }
+}
+]]></programlisting>
+
+        <para>
+            Let's create an element that uses this decorator:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$decorator = new My_Decorator_SimpleInput();
+$element   = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'decorators' => array($decorator),
+));
+]]></programlisting>
+
+        <para>
+            Rendering this element results in the following markup:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<label for="bar[foo]">Foo</label>
+<input id="bar-foo" name="bar[foo]" type="text" value="test"/>
+]]></programlisting>
+
+        <para>
+            You could also put this class in your library somewhere, inform your element of that
+            path, and refer to the decorator as simply "SimpleInput" as well:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element = new Zend_Form_Element('foo', array(
+    'label'      => 'Foo',
+    'belongsTo'  => 'bar',
+    'value'      => 'test',
+    'prefixPath' => array('decorator' => array(
+        'My_Decorator' => 'path/to/decorators/',
+    )),
+    'decorators' => array('SimpleInput'),
+));
+]]></programlisting>
+
+        <para>
+            This gives you the benefit of re-use in other projects, and also opens the door for
+            providing alternate implementations of that decorator later.
+        </para>
+
+        <para>
+            In the next section, we'll look at how to combine decorators in order to create
+            composite output.
+        </para>
+    </sect2>
+</sect1>

+ 20 - 0
documentation/manual/en/tutorials/layout-conclusions.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.conclusions">
+    <title>Zend_Layout: Conclusions</title>
+
+    <para>
+        <classname>Zend_Layout</classname> is a very simple wrapper around
+        <classname>Zend_View</classname> which provides you immediately with the benefits of a Two
+        Step View, giving you the flexibility to create a site-wide design into which you may inject
+        your application content.
+    </para>
+
+    <para>
+        If you're looking closely at the examples, however, you may come away with the idea that the
+        functionality is relatively limited: how do you alter the page title, inject an optional
+        script tag, or even create an optional sidebar? These questions are addressed with the
+        concept of a "Composite View" -- and are the subject of the next chapter in the tutorial,
+        which covers view "placeholders."
+    </para>
+</sect1>

+ 54 - 0
documentation/manual/en/tutorials/layout-intro.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.intro">
+    <title>Introduction</title>
+
+    <para>
+        When building a website using the Zend Framework <acronym>MVC</acronym> layers, your view
+        scripts will typically be just snippets of <acronym>HTML</acronym> pertinent to the
+        requested action. For instance, if you had the action "/user/list", you might create a view
+        script that iterates through the users and presents an unordered list:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<h2>Users</h2>
+<ul>
+    <?php if (!count($this->users)): ?>
+    <li>No users found</li>
+    <?php else: ?>
+    <?php foreach ($this->users as $user): ?>
+    <li>
+        <?php echo $this->escape($user->fullname) ?> 
+        (<?php echo $this->escape($user->email) ?>)
+    </li>
+    <?php endforeach ?>
+    <?php endif ?>
+</ul>
+]]></programlisting>
+
+    <para>
+        Since this is just a snippet of <acronym>HTML</acronym>, it's not a valid page; it's missing
+        a <acronym>Doctype</acronym> declaration, and the opening <acronym>HTML</acronym> and
+        <acronym>body</acronym> tags. So, the question is, where will these be created?
+    </para>
+
+    <para>
+        In early versions of Zend Framework, developers often created "header" and "footer" view
+        scripts that had these artifacts, and then in each view script they would render them. While
+        this methodology worked, it also made it difficult to refactor later, or to build composite
+        content by calling multiple actions.
+    </para>
+
+    <para>
+        The <ulink url="http://martinfowler.com/eaaCatalog/twoStepView.html">Two Step View</ulink>
+        design pattern answers many of the issues presented. In this pattern, the "application" view
+        is created first, and then injected into the "page" view, which is then presented to the
+        client. The page view can be thought of as your site-wide template or layout, and would have
+        common elements used across various pages.
+    </para>
+
+    <para>
+        Within Zend Framework, <classname>Zend_Layout</classname> implements the Two Step View
+        pattern.
+    </para>
+</sect1>

+ 242 - 0
documentation/manual/en/tutorials/layout-usage.xml

@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.layout.usage">
+    <title>Using Zend_Layout</title>
+
+    <para>
+        Basic usage of <classname>Zend_Layout</classname> is fairly trivial. Assuming you're using
+        <classname>Zend_Application</classname> already, you can simply provide some configuration
+        options and create a layout view script.
+    </para>
+
+    <sect2 id="learning.layout.usage.configuration">
+        <title>Layout Configuration</title>
+
+        <para>
+            The recommended location of layouts is in a "layouts/scripts/" subdirectory of your
+            application:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+application
+|-- Bootstrap.php
+|-- configs
+|   `-- application.ini
+|-- controllers
+|-- layouts
+|   `-- scripts
+|       |-- layout.phtml
+]]></programlisting>
+
+        <para>
+            To initialize <classname>Zend_Layout</classname>, add the following to your
+            configuration file ("application/configs/application.ini"):
+        </para>
+
+        <programlisting language="dosini"><![CDATA[
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+resources.layout.layout = "layout"
+]]></programlisting>
+
+        <para>
+            The first line indicates where to look for layout scripts; the second line gives the
+            name of the layout to use, minus the view script extension (which is assumed to be
+            ".phtml" by default).
+        </para>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.layout-script">
+        <title>Create a Layout Script</title>
+
+        <para>
+            Now that you have your configuration in place, you need to create your layout script.
+            First, make sure that you've created the "application/layouts/scripts" directory; then,
+            open an editor, and create the markup for your layout. Layout scripts are simply view
+            scripts, with some slight differences.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<html>
+<head>
+    <title>My Site</title>
+</head>
+<body>
+    <?php echo $this->layout()->content ?>
+</body>
+</html>
+]]></programlisting>
+
+        <para>
+            In the example above, you'll note the call to a <methodname>layout()</methodname> view
+            helper. When you register the <classname>Zend_Layout</classname> resource, you also gain
+            access to both an action and view helper that allow you access to the
+            <classname>Zend_Layout</classname> instance; you can then call operations on the layout
+            object. In this case, we're retrieving a named variable, <varname>content</varname>, and
+            echoing it. By default, the <varname>content</varname> variable is populated for you
+            from the application view script rendered. Otherwise, anything you'd normally do in a
+            view script is perfectly valid -- call any helpers or view methods you desire.
+        </para>
+
+        <para>
+            At this point, you have a working layout script, and your application is informed of its
+            location and knows to render it.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.access">
+        <title>Accessing the Layout Object</title>
+
+        <para>
+            On occasion, you may need direct access to the layout object. There are three ways you
+            can do this:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis>Within view scripts:</emphasis> use the
+                    <methodname>layout()</methodname> view helper, which returns the
+                    <classname>Zend_Layout</classname> instance registered with the front controller
+                    plugin.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+<?php $layout = $this->layout(); ?>
+]]></programlisting>
+
+                <para>
+                    Since it returns the layout instance, you can also simply call methods on it,
+                    rather than assigning it to a variable.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Within action controllers:</emphasis> use the
+                    <methodname>layout()</methodname> action helper, which acts just like the view
+                    helper.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Calling helper as a method of the helper broker:
+$layout = $this->_helper->layout();
+
+// Or, more verbosely:
+$helper = $this->_helper->getHelper('Layout');
+$layout = $helper->getLayoutInstance();
+]]></programlisting>
+
+                <para>
+                    As with the view helper, since the action helper returns the layout instance,
+                    you can also simply call methods on it, rather than assigning it to a variable.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Elsewhere: </emphasis> use the static method
+                    <methodname>getMvcInstance()</methodname>. This will return the layout instance
+                    registered by the bootstrap resource.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout = Zend_Layout::getMvcInstance();
+]]></programlisting>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Via the bootstrap: </emphasis> retrieve the layout resource, which
+                    will be the <classname>Zend_Layout</classname> instance.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout = $bootstrap->getResource('Layout');
+]]></programlisting>
+
+                <para>
+                    Anywhere you have access to the bootstrap object, this method is preferred over
+                    using the static <methodname>getMvcInstance()</methodname> method.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+
+    <sect2 id="learning.layout.usage.other-operations">
+        <title>Other Operations</title>
+
+        <para>
+            In most cases, the above configuration and layout script (with modifications) will get
+            you what you need. However, some other functionality exists you will likely use sooner
+            or later. In all of the following examples, you may use one of the <link
+                linkend="learning.layout.usage.access">methods listed above</link> for retrieving
+            the layout object.
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <emphasis>Setting layout variables</emphasis>.
+                    <classname>Zend_Layout</classname> keeps its own registry of layout-specific
+                    view variables that you can access; the <varname>content</varname> key noted in
+                    the initial layout script sample is one such example. You can assign and
+                    retrieve these using normal property access, or via the
+                    <methodname>assign()</methodname> method.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Setting content:
+$layout->somekey = "foo"
+
+// Echoing that same content:
+echo $layout->somekey; // 'foo'
+
+// Using the assign() method:
+$layout->assign('someotherkey', 'bar');
+
+// Access to assign()'d variables remains the same:
+echo $layout->someotherkey; // 'bar'
+]]></programlisting>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <methodname>disableLayout()</methodname>. Occasionally, you may want to disable
+                    layouts; for example, when answering an Ajax request, or providing a RESTful
+                    representation of a resource. In these cases, you can call the
+                    <methodname>disableLayout()</methodname> method on your layout object.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+$layout->disableLayout();
+]]></programlisting>
+
+                <para>
+                    The opposite of this method is, of course,
+                    <methodname>enableLayout()</methodname>, which can be called at any time to
+                    re-enable layouts for the requested action.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <emphasis>Selecting an alternate layout</emphasis>: If you have multiple
+                    layouts for your site or application, you can select the layout to use at any
+                    time by simply calling the <methodname>setLayout()</methodname> method. Call it
+                    by specifying the name of the layout script without the file suffix.
+                </para>
+
+                <programlisting language="php"><![CDATA[
+// Use the layout script "alternate.phtml":
+$layout->setLayout('alternate');
+]]></programlisting>
+
+                <para>
+                    The layout script should reside in the <varname>layoutPath</varname> directory
+                    specified in your configuration. <classname>Zend_Layout</classname> will then
+                    use this new layout when rendering.
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+</sect1>

+ 27 - 0
documentation/manual/en/tutorials/lucene-index-opening.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.index-opening">
+    <title>Index Opening and Creation</title>
+
+    <para>
+        All index operations (e.g., creating a new index, adding a document to the index, deleting a
+        document, searching through the index) need an index object. One can be obtained using one
+        of the following two methods.
+    </para>
+
+    <example id="learning.lucene.index-opening.creation">
+        <title>Lucene Index Creation</title>
+        
+        <programlisting language="php"><![CDATA[
+$index = Zend_Search_Lucene::create($indexPath);
+]]></programlisting>
+    </example>
+    
+    <example id="learning.lucene.index-opening.creation">
+        <title>Lucene Index Opening</title>
+        
+        <programlisting language="php"><![CDATA[
+$index = Zend_Search_Lucene::open($indexPath);
+]]></programlisting>
+    </example>
+</sect1>

+ 116 - 0
documentation/manual/en/tutorials/lucene-index-structure.xml

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.index-structure">
+    <title>Lucene Index Structure</title>
+
+    <para>
+        In order to fully utilize <classname>Zend_Search_Lucene</classname>'s capabilities with
+        maximum performance, you need to understand it's internal index structure.
+    </para>
+    
+    <para>
+        An <emphasis>index</emphasis> is stored as a set of files within a single directory.
+    </para>
+
+    <para>
+        An <emphasis>index</emphasis> consists of any number of independent
+        <emphasis>segments</emphasis> which store information about a subset of indexed documents.
+        Each <emphasis>segment</emphasis> has its own <emphasis>terms dictionary</emphasis>, terms
+        dictionary index, and document storage (stored field values) <footnote><para>Starting with
+                Lucene 2.3, document storage files can be shared between segments; however,
+                <classname>Zend_Search_Lucene</classname> doesn't use this
+                capability</para></footnote>. All segment data is stored in
+        <filename>_xxxxx.cfs</filename> files, where <varname>xxxxx</varname> is a segment name.
+    </para>
+
+    <para>
+        Once an index segment file is created, it can't be updated.  New documents are added to new
+        segments.  Deleted documents are only marked as deleted in an optional
+        <filename>&lt;segmentname&gt;.del</filename> file. 
+    </para>
+
+    <para>
+        Document updating is performed as separate delete and add operations, even though it's done
+        using an <methodname>update()</methodname> API call<footnote><para>This call is provided
+                only by Java Lucene now, but it's planned to extend the
+                <classname>Zend_Search_Lucene</classname> API with similar
+                functionality</para></footnote>.  This simplifies adding new documents, and allows
+        updating concurrently with search operations.
+    </para>
+
+    <para>
+        On the other hand, using several segments (one document per segment as a borderline case) 
+        increases search time:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                retrieving a term from a dictionary is performed for each segment;
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                the terms dictionary index is pre-loaded for each segment (this process takes the
+                most search time for simple queries, and it also requires additional memory).
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        If the terms dictionary reaches a saturation point, then search through one segment is 
+        <emphasis>N</emphasis> times faster than search through <emphasis>N</emphasis> segments 
+        in most cases.
+    </para>
+
+    <para>
+        <emphasis>Index optimization</emphasis> merges two or more segments into a single new one. A
+        new segment is added to the index segments list, and old segments are excluded.
+    </para>
+
+    <para>
+        Segment list updates are performed as an atomic operation. This gives the ability of
+        concurrently adding new documents, performing index optimization, and searching through the
+        index.
+    </para>
+
+    <para>
+        Index auto-optimization is performed after each new segment generation. It merges sets of
+        the smallest segments into larger segments, and larger segments into even larger segments,
+        if we have enough segments to merge.
+    </para>
+
+    <para>
+        Index auto-optimization is controlled by three options:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <varname>MaxBufferedDocs</varname> (the minimal number of documents required before
+                the buffered in-memory documents are written into a new segment);
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <varname>MaxMergeDocs</varname> (the largest number of documents ever merged by
+                an optimization operation); and
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <varname>MergeFactor</varname> (which determines how often segment indices are
+                merged by auto-optimization operations).
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        If we add one document per script execution, then <emphasis>MaxBufferedDocs</emphasis> is
+        actually not used (only one new segment with only one document is created at the end of
+        script execution, at which time the auto-optimization process starts).
+    </para>
+</sect1>

+ 94 - 0
documentation/manual/en/tutorials/lucene-indexing.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.indexing">
+    <title>Indexing</title>
+
+    <para>
+        Indexing is performed by adding a new document to an existing or new index:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$index->addDocument($doc);
+]]></programlisting>
+
+    <para>
+        There are two ways to create document object. The first is to do it manually.
+    </para>
+
+    <example id="learning.lucene.indexing.doc-creation">
+        <title>Manual Document Construction</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = new Zend_Search_Lucene_Document();
+$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
+$doc->addField(Zend_Search_Lucene_Field::Text('title', $docTitle));
+$doc->addField(Zend_Search_Lucene_Field::unStored('contents', $docBody));
+$doc->addField(Zend_Search_Lucene_Field::binary('avatar', $avatarData));
+]]></programlisting>
+    </example>
+    
+    <para>
+        The second method is to load it from <acronym>HTML</acronym> or Microsoft Office 2007 files:
+    </para>
+
+    <example id="learning.lucene.indexing.doc-loading">
+        <title>Document loading</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
+$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($path);
+$doc = Zend_Search_Lucene_Document_Pptx::loadPptFile($path);
+$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsxFile($path);
+]]></programlisting>
+    </example>
+
+    <para>
+        If a document is loaded from one of the supported formats, it still can be extended manually
+        with new user defined fields.
+    </para>
+
+    <sect2 id="learning.lucene.indexing.policy">
+        <title>Indexing Policy</title>
+
+        <para>
+            You should define indexing policy within your application architectural design.
+        </para>
+
+        <para>
+            You may need an on-demand indexing configuration (something like OLTP system). In such
+            systems, you usually add one document per user request. As such, the
+            <emphasis>MaxBufferedDocs</emphasis> option will not affect the system. On the other
+            hand, <emphasis>MaxMergeDocs</emphasis> is really helpful as it allows you to limit
+            maximum script execution time. <emphasis>MergeFactor</emphasis> should be set to a value
+            that keeps balance between the average indexing time (it's also affected by average
+            auto-optimization time) and search performance (index optimization level is dependent on
+            the number of segments).
+        </para>
+
+        <para>
+            If you will be primarily performing batch index updates, your configuration should use a
+            <emphasis>MaxBufferedDocs</emphasis> option set to the maximum value supported by the
+            available amount of memory.  <emphasis>MaxMergeDocs</emphasis> and
+            <emphasis>MergeFactor</emphasis> have to be set to values reducing auto-optimization
+            involvement as much as possible <footnote><para>An additional limit is the maximum file
+                    handlers supported by the operation system for concurrent open
+                    operations</para></footnote>. Full index optimization should be applied after
+            indexing.
+        </para>
+
+        <example id="learning.lucene.indexing.optimization">
+            <title>Index optimization</title>
+
+            <programlisting language="php"><![CDATA[
+$index->optimize();
+]]></programlisting>
+        </example>
+
+        <para>
+            In some configurations, it's more effective to serialize index updates by organizing
+            update requests into a queue and processing several update requests in a single script
+            execution. This reduces index opening overhead, and allows utilizing index document
+            buffering.
+        </para>
+    </sect2>
+</sect1>

+ 102 - 0
documentation/manual/en/tutorials/lucene-intro.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.intro">
+    <title>Zend_Search_Lucene Introduction</title>
+
+    <para>
+        The <classname>Zend_Search_Lucene</classname> component is intended to provide a
+        ready-for-use full-text search solution.  It doesn't require any PHP
+        extensions<footnote><para>Though some <acronym>UTF-8</acronym> processing functionality
+                requires the <classname>mbstring</classname> extension to be turned
+                on</para></footnote> or additional software to be installed, and can be used
+        immediately after Zend Framework installation.
+    </para>
+
+    <para>
+        <classname>Zend_Search_Lucene</classname> is a pure PHP port of the popular open source
+        full-text search engine known as Apache Lucene.  See <ulink
+            url="http://lucene.apache.org">http://lucene.apache.org/</ulink> for the details.
+    </para>
+
+    <para>
+        Information must be indexed to be available for searching.
+        <classname>Zend_Search_Lucene</classname> and Java Lucene use a document concept known as an
+        "atomic indexing item."
+    </para>
+
+    <para>
+        Each document is a set of fields: &lt;name, value&gt; pairs where name and value are
+        <acronym>UTF-8</acronym> strings<footnote><para>Binary strings are also allowed to be used
+                as field values</para></footnote>. Any subset of the document fields may be marked
+        as "indexed" to include field data in the text indexing process.
+    </para>
+
+    <para>
+        Field values may or may not be tokenized while indexing. If a field is not tokenized, then
+        the field value is stored as one term; otherwise, the current analyzer is used for
+        tokenization.
+    </para>
+
+    <para>
+        Several analyzers are provided within the <classname>Zend_Search_Lucene</classname> package.
+        The default analyzer works with <acronym>ASCII</acronym> text (since the
+        <acronym>UTF-8</acronym> analyzer needs the <classname>mbstring</classname> extension to be
+        turned on). It is case insensitive, and it skips numbers. Use other analyzers or create your
+        own analyzer if you need to change this behavior.
+    </para>
+
+    <note>
+        <title>Using analyzers during indexing and searching</title>
+
+        <para>
+            Important note! Search queries are also tokenized using the "current analyzer", so the
+            same analyzer must be set as the default during both the indexing and searching process.
+            This will guarantee that source and searched text will be transformed into terms in the
+            same way.
+        </para>
+    </note>
+
+    <para>
+        Field values are optionally stored within an index. This allows the original field data to
+        be retrieved from the index while searching. This is the only way to associate search
+        results with the original data (internal document IDs may be changed after index
+        optimization or auto-optimization).
+    </para>
+
+    <para>
+        The thing that should be remembered is that a Lucene index is not a database. It doesn't
+        provide index backup mechanisms except backup of the file system directory. It doesn't
+        provide transactional mechanisms though concurrent index update as well as concurrent update
+        and read are supported. It doesn't compare with databases in data retrieving speed.
+    </para>
+
+    <para>
+        So it's good idea:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <emphasis>Not</emphasis> to use Lucene index as a storage since it may dramatically
+                decrease search hit retrieving performance. Store only unique document identifiers
+                (doc paths, URLs, database unique IDs) and associated data within an index. E.g.
+                title, annotation, category, language info, avatar. (Note: a field may be included
+                in indexing, but not stored, or stored, but not indexed).
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                To write functionality that can rebuild an index completely if it's corrupted for
+                any reason.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Individual documents in the index may have completely different sets of fields. The same
+        fields in different documents don't need to have the same attributes. E.g. a field may be
+        indexed for one document and skipped from indexing for another. The same applies for
+        storing, tokenizing, or treating field value as a binary string.
+    </para>
+</sect1>

+ 48 - 0
documentation/manual/en/tutorials/lucene-pagination.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.pagination">
+    <title>Search result pagination</title>
+
+    <para>
+        As <link linkend="learning.lucene.searching.identifiers">mentioned above</link>, search
+        result hit objects use lazy loading for stored document fields. When any stored field is
+        accessed, the complete document is loaded.
+    </para>
+        
+    <para>
+        Do not retrieve all documents if you actually need to work only with some portion of them.
+        Go through the search results and store document IDs (and optionally the score) somewhere to
+        retrive documents from the index during the next script execution.
+    </para>
+        
+    <example id="learning.lucene.pagination.example">
+        <title>Search result pagination example</title>
+
+        <programlisting language="php"><![CDATA[
+$cacheId = md5($query);
+
+if (!$resultSet = $cache->load($cacheId)) {
+    $hits = $index->find($query);
+    $resultSet = array();
+    foreach ($hits as $hit) {
+        $resultSetEntry          = array();
+        $resultSetEntry['id']    = $hit->id;
+        $resultSetEntry['score'] = $hit->score;
+
+        $resultSet[] = $resultSetEntry;
+    }
+
+    $cache->save($resultSet, $cacheId);
+}
+
+$publishedResultSet = array();
+for ($resultId = $startId; $resultId < $endId; $resultId++) {
+    $publishedResultSet[$resultId] = array(
+        'id'    => $resultSet[$resultId]['id'], 
+        'score' => $resultSet[$resultId]['score'],
+        'doc'   => $index->getDocument($resultSet[$resultId]['id']),
+    );
+}
+]]></programlisting>
+    </example>
+</sect1>

+ 228 - 0
documentation/manual/en/tutorials/lucene-queries.xml

@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.queries">
+    <title>Supported queries</title>
+
+    <para>
+        <classname>Zend_Search_Lucene</classname> and Java Lucene support a powerful query language.
+        It allows searching for individual terms, phrases, ranges of terms; using wildcards and
+        fuzzy search; combining queries using boolean operators; and so on.
+    </para>
+        
+    <para>
+        A detailed query language description can be found in the <link
+            linkend="zend.search.lucene.query-language">
+            Zend_Search_Lucene component documentation</link>.
+    </para>
+        
+    <para>
+        What follows are examples of some common query types and strategies.
+    </para>
+
+    <example id="learning.lucene.queries.keyword">
+        <title>Querying for a single word</title>
+
+        <programlisting language="text"><![CDATA[
+hello
+]]></programlisting>
+
+        <para>
+            Searches for the word "hello" through all document fields.
+        </para>
+    </example>
+
+    <note>
+        <title>Default search field</title>
+
+        <para>
+            Important note! Java Lucene searches only through the "contents" field by default, but
+            <classname>Zend_Search_Lucene</classname> searches through <emphasis>all</emphasis>
+            fields. This behavior can be modified using the
+            <methodname>Zend_Search_Lucene::setDefaultSearchField($fieldName)</methodname> method.
+        </para>
+    </note>
+
+    <example id="learning.lucene.queries.multiple-words">
+        <title>Querying for multiple words</title>
+
+        <programlisting language="text"><![CDATA[
+hello dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words. Both words are optional; at least one of them must be present in
+            the result.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.required-words">
+        <title>Requiring words in a query</title>
+
+        <programlisting language="text"><![CDATA[
++hello dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words; "hello" is required, "dolly" is optional.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.prohibited-words">
+        <title>Prohibiting words in queried documents</title>
+
+        <programlisting language="text"><![CDATA[
++hello -dolly
+]]></programlisting>
+
+        <para>
+            Searches for two words; "hello" is required, 'dolly' is prohibited. In other words, if
+            the document matches "hello", but contains the word "dolly", it will not be returned in
+            the set of matches.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.phrases">
+        <title>Querying for phrases</title>
+
+        <programlisting language="text"><![CDATA[
+"hello dolly"
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "hello dolly"; a document only matches if that exact string is
+            present.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields">
+        <title>Querying against specific fields</title>
+
+        <programlisting language="text"><![CDATA[
+title:"The Right Way" AND text:go
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "The Right Way" within the <varname>title</varname> field and
+            the word "go" within the <varname>text</varname> field.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields-and-document">
+        <title>Querying against specific fields as well as the entire document</title>
+
+        <programlisting language="text"><![CDATA[
+title:"The Right Way" AND  go
+]]></programlisting>
+
+        <para>
+            Searches for the phrase "The Right Way" within the <varname>title</varname> field and
+            the word "go" word appearing in any field of the document.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fields-and-document-alt">
+        <title>Querying against specific fields as well as the entire document (alternate)</title>
+        
+        <programlisting language="text"><![CDATA[
+title:Do it right
+]]></programlisting>
+
+        <para>
+            Searches for the word "Do" within the <varname>title</varname> field and the words "it"
+            and "right" words through all fields; any single one matching will result in a document
+            match.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.wildcard-question">
+        <title>Querying with the wildcard "?"</title>
+
+        <programlisting language="text"><![CDATA[
+te?t
+]]></programlisting>
+
+        <para>
+            Search for words matching the pattern "te?t", where "?" is any single character.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.wildcard-asterisk">
+        <title>Querying with the wildcard "*"</title>
+
+        <programlisting language="text"><![CDATA[
+test*
+]]></programlisting>
+
+        <para>
+            Search for words matching the pattern "test*", where "*" is any sequence of zero or more
+            characters.
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.range-inclusive">
+        <title>Querying for an inclusive range of terms</title>
+
+        <programlisting language="text"><![CDATA[
+mod_date:[20020101 TO 20030101]
+]]></programlisting>
+
+        <para>
+            Search for the range of terms (inclusive).
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.range-exclusive">
+        <title>Querying for an exclusive range of terms</title>
+
+        <programlisting language="text"><![CDATA[
+title:{Aida to Carmen}
+]]></programlisting>
+
+        <para>
+            Search for the range of terms (exclusive).
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.fuzzy">
+        <title>Fuzzy searches</title>
+
+        <programlisting language="text"><![CDATA[
+roam~
+]]></programlisting>
+
+        <para>
+            Fuzzy search for the word "roam".
+        </para>
+    </example>
+
+    <example id="learning.lucene.queries.boolean">
+        <title>Boolean searches</title>
+
+        <programlisting language="text"><![CDATA[
+(framework OR library) AND php
+]]></programlisting>
+
+        <para>
+            Boolean query.
+        </para>
+    </example>
+
+    <para>
+        All supported queries can be constructed through <classname>Zend_Search_Lucene</classname>'s
+        <link linkend="zend.search.lucene.query-api"> query
+            construction API</link>. Moreover, query parsing and query constructing may be
+        combined:
+    </para>
+
+    <example id="learning.lucene.queries.combining">
+        <title>Combining parsed and constructed queries</title>
+
+        <programlisting language="php"><![CDATA[
+$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
+
+$query = new Zend_Search_Lucene_Search_Query_Boolean();
+$query->addSubquery($userQuery, true  /* required */);
+$query->addSubquery($constructedQuery, true  /* required */);
+]]></programlisting>
+    </example>
+</sect1>

+ 97 - 0
documentation/manual/en/tutorials/lucene-searching.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.lucene.searching">
+    <title>Searching</title>
+
+    <para>
+        Searching is performed by using the <methodname>find()</methodname> method:
+    </para>
+
+    <example id="learning.lucene.searching.search-example">
+        <title>Searching through the index</title>
+
+        <programlisting language="php"><![CDATA[
+$hits = $index->find($query);
+
+foreach ($hits as $hit) {
+    printf(“%d %f %s\n”, $hit->id, $hit->score, $hit->title);
+}
+]]></programlisting>
+    </example>
+
+    <para>
+        This example demonstrates the usage of two special search hit properties -
+        <varname>id</varname> and <varname>score</varname>.
+    </para>
+    
+    <para>
+        <varname>id</varname> is an internal document identifier used within a Lucene index. It may
+        be used for a variety of operations, including deleting a document from the index:
+    </para>
+
+    <example id="learning.lucene.searching.delete-example">
+        <title>Deleting an Indexed Document</title>
+
+        <programlisting language="php"><![CDATA[
+$index->delete($id);
+]]></programlisting>
+    </example>
+
+    <para>
+        Or retrieving the document from the index:
+    </para>
+
+    <example id="learning.lucene.searching.retrieve-example">
+        <title>Retrieving an Indexed Document</title>
+
+        <programlisting language="php"><![CDATA[
+$doc = $index->getDocument($id);
+]]></programlisting>
+    </example>
+
+    <note id="learning.lucene.searching.identifiers">
+        <title>Internal Document Identifiers</title>
+
+        <para>
+            Important note! Internal document identifiers may be changed by index optimization or
+            the auto-optimization process, but it's never changed within a single script's execution
+            unless the <methodname>addDocument()</methodname> (which may involve an
+            auto-optimization procedure) or <methodname>optimize()</methodname> methods are called.
+        </para>
+    </note>
+    
+    <para>
+        The <varname>score</varname> field is a hit score. Search results are ordered by score by
+        default (best results returned first).
+    </para>
+    
+    <para>
+        It's also possible to order result sets by specific field values. See the <link
+            linkend="zend.search.lucene.searching.sorting">
+            <classname>Zend_Search_Lucene</classname> documentation</link> for more details about
+        this possibility.
+    </para>
+        
+    <para>
+        The example also demonstrates an ability to access stored fields (e.g.,
+        <code>$hit-&gt;title</code>).  At the first access to any hit property other than
+        <varname>id</varname> or <varname>score</varname>, document stored fields are loaded, and
+        the corresponding field value is returned.
+    </para>
+        
+    <para>
+        This causes an ambiguity for documents having their own <varname>id</varname> or
+        <varname>score</varname> fields; as a result, it's not recommended to use these field names
+        within stored documents. Nevertheless, they still can be accessed via the
+        <methodname>getDocument()</methodname> method:
+    </para>
+
+    <example id="learning.lucene.searching.id-score-fields">
+        <title>Accessing the original document's "id" and "score" fields</title>
+
+        <programlisting language="php"><![CDATA[
+$id    = $hit->getDocument()->id;
+$score = $hit->getDocument()->score;
+]]></programlisting>
+    </example>
+</sect1>

+ 182 - 0
documentation/manual/en/tutorials/multiuser-authentication.xml

@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.authentication">
+    <title>Authenticating Users in ZF</title>
+
+    <sect2 id="learning.multiuser.authentication.intro">
+        <title>Introduction to Authentication</title>
+        
+        <para>
+            Once a web application has been able to distinguish one user from another by establishing a 
+            session, web applications typically want to validate the identity of a user.  The process 
+            of validating a consumer as being authentic is "authentication."  Authentication is made up 
+            of two distinctive parts: an identity and a set of credentials.  It takes some variation of 
+            both presented to the application for processing so that it may authenticate a user.    
+        </para>
+        
+        <para>
+            While the most common pattern of authentication revolves around usernames and passwords,
+            it should be stated that this is not always the case.  Identities are not limited to 
+            usernames.  In fact, any public identifier can be used: an assigned number, social security 
+            number, or residence address.  Likewise, credentials are not limited to passwords.  
+            Credentials can come in the form of protected private information: fingerprint, eye retinal 
+            scan, passphrase, or any other obscure personal information.  
+        </para>
+        
+    </sect2>
+
+    <sect2 id="learning.multiuser.authentication.basic-usage">
+        <title>Basic Usage of Zend_Auth</title>
+        
+        <para>
+            In the following example, we will be using Zend_Auth to complete what is probably the most
+            prolific form of authentication: username and password from a database table.  This
+            example assumes that you have already setup your application using Zend_Application, and
+            that inside that application you have configured a database connection.
+        </para>
+        
+        <para>
+            The job of the Zend_Auth class is twofold.  First, it should be able to accept an
+            authentication adapter to use to authenticate a user.  Secondly, after a successful
+            authentication of a user, it should persist throughout each and every request that might
+            need to know if the current user has indeed been authenticated.  To persist this data,
+            Zend_Auth consumes Zend_Session_Namespace, but you will generally never need to interact
+            with this session object.
+        </para>
+        
+        <para>
+            Lets assume we have the following database table setup:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+CREATE TABLE users (
+    id INTEGER  NOT NULL PRIMARY KEY, 
+    username VARCHAR(50) UNIQUE NOT NULL, 
+    password VARCHAR(32) NULL, 
+    password_salt VARCHAR(32) NULL,
+    real_name VARCHAR(150) NULL
+)
+]]></programlisting>
+
+        <para>
+            The above demonstrates a user table that includes a username, password, and also a 
+            password salt column.  This salt column is used as part of a technique called salting that 
+            would improve the security of your database of information against brute force attacks 
+            targeting the algorithm of your password hashing. 
+            <a href="http://en.wikipedia.org/wiki/Salting_%28cryptography%29">More information</a>
+            on salting.
+        </para>
+
+        <para>
+            For this implementation, we must first make a simple form that we can utilized as the 
+            "login form".  We will use Zend_Form to accomplish this.
+        </para>
+
+
+<programlisting language="php"><![CDATA[
+<?php
+// located at application/forms/Auth/Login.php
+
+class Default_Form_Auth_Login extends Zend_Form
+{
+    public function init()
+    {
+        $this->setMethod('post');
+
+        $this->addElement(
+            'text', 'username', array(
+                'label' => 'Username:',
+                'required' => true,
+                'filters'    => array('StringTrim'),
+            ));
+
+        $this->addElement('password', 'password', array(
+            'label' => 'Password:',
+            'required' => true,
+            ));
+
+        $this->addElement('submit', 'submit', array(
+            'ignore'   => true,
+            'label'    => 'Login',
+            ));
+
+    }
+}
+]]></programlisting>
+
+        <para>
+            With the above form, we can now go about creating our login action for
+            our authentication controller.  This controller will be called "AuthController", and
+            will be located at application/controllers/AuthController.php.  It will have a single
+            method called "loginAction" which will serve as the self-posting action.  In other words,
+            regardless of the url was POSTed to or GETed to, this method will handle the logic.
+        </para>
+        
+        <para>
+            The following code will demonstrate how to construct the proper adapter, integrate it
+            with the form:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class AuthController extends Zend_Controller_Action
+{
+
+    public function loginAction()
+    {
+        $db = $this->_getParam('db');
+        
+        $loginForm = new Default_Form_Auth_Login($_POST);
+        
+        if ($loginForm->isValid()) {
+        
+            $adapter = new Zend_Auth_Adapter_DbTable(
+                $db,
+                'users',
+                'username',
+                'password',
+                'MD5(CONCAT(?, password_salt))'
+                );
+            
+            $adapter->setIdentity($loginForm->getValue('username'));
+            $adapter->setCredential($loginForm->getValue('password'));
+            
+            $result = $auth->authenticate($adapter);
+            
+            if ($result->isValid()) {
+                $this->_helper->FlashMessenger('Successful Login');
+                $this->redirect('/');
+                return;
+            }
+            
+        }
+
+        $this->view->loginForm = $loginForm;
+
+    }
+
+}
+]]></programlisting>
+
+        <para>
+            The corresponding view script is quite simple for this action.  It will set the current
+            url since this form is self processing, and it will display the form.  This view script is
+            located at application/views/scripts/auth/login.phtml:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php 
+
+$this->form->setAction($this->url());
+echo $this->form;
+]]></programlisting>
+        
+        <para>
+            There you have it.  With these basics you can expand the general concepts to include 
+            more complex authentication scenarios.  For more information on other Zend_Auth adapters,
+            have a look in <link linkend="zend.auth">the reference guide</link>.
+        </para>
+        
+    </sect2>
+
+</sect1>
+

+ 226 - 0
documentation/manual/en/tutorials/multiuser-authorization.xml

@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.authentication">
+    <title>Building an Authorization System in ZF</title>
+
+    <sect2 id="learning.multiuser.authorization.intro">
+        <title>Introduction to Authorization</title>
+
+        <para>
+            After a user has been identified as being authentic, an application can go about its 
+            business of providing some useful and desirable resources to a consumer.  In many cases, 
+            applications might contain different resource types, with some resources having stricter 
+            rules regarding access.  This process of determining who has access to which resources is 
+            the process of "authorization".  Authorization in its simplest form is the composition of 
+            these elements:
+        </para>
+        
+        <itemizedlist>
+            <listitem>
+                <para>
+                    the identity whom wishes to be granted access
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    the resource the identity is asking permission to consume
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    and optionally, what the identity is privileged to do with the resource
+                </para>
+            </listitem>
+        </itemizedlist>
+        
+        <para>
+            In ZF, the Zend_Acl component handles the task of building a tree of roles, resources and
+            privileges to manage and query authorization requests against.
+        </para>
+        
+    </sect2>
+    
+    <sect2 id="learning.multiuser.authorization.basic-usage">
+        <title>Basic Usage of Zend_Acl</title>
+
+<!-- explain the interaction with a User object, how -->
+        
+        <para>
+            When using Zend_Acl, any models can serve as roles or resources by simply implementing
+            the proper interface.  To be used in a role capacity, the class must implement the 
+            Zend_Acl_Role_Interface, which requires only getRoleId().  To be used in a resource
+            capacity, a class must implement the Zend_Acl_Resource_Interface which similarly requires
+            the class implement the getResourceId() method.
+        </para>
+
+        <para>
+            Demonstrated below is a simple user model.  This model can take part in our ACL system
+            simply by implementing the Zend_Acl_Role_Interface.  The method getRoleId() will return
+            the id "guest" when an ID is not known, or it will return the role ID that was assigned
+            to this actual user object.  This value can effectively come from anywhere, a static definition
+            or perhaps dynamically from the users database role itself.
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+class Default_Model_User implements Zend_Acl_Role_Interface
+{
+    protected $_aclRoleId = null;
+    
+    public function getRoleId()
+    {
+        if ($this->_aclRoleId == null) {
+            return 'guest';
+        }
+        
+        return $this->_aclRoleId;
+    }
+}
+]]></programlisting>
+
+        <para>
+            While the concept of a user as a role is pretty straight forward, your application might
+            choose to have any other models in your system as a potential "resource" to be consumed in
+            this ACL system.  For simplicity, we'll use the example of a blog post.  Since the type of
+            the resource is tied to the type of the object, this class will only return 'blogPost' as
+            the resource ID in this system.  Naturally, this value can be dynamic if your system requires
+            it to be so.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Default_Model_BlogPost implements Zend_Acl_Resource_Interface
+{
+    public function getResourceId()
+    {
+        return 'blogPost';
+    }
+}
+]]></programlisting>
+
+        <para>
+            Now that we have at least a role and a resource, we can go about defining the rules of the
+            ACL system.  These rules will be consulted when the system receives a query about what is 
+            possible given a certain role, resources, and optionally a privilege.
+        </para>
+        
+        <para>
+            Lets assume the following rules:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+$acl = new Zend_Acl();
+
+// setup the various roles in our system
+$acl->addRole('guest');
+$acl->addRole('owner', 'guest');  // owner inherits all of the rules of guest
+
+// add the resources
+$acl->addResource('blogPost');
+
+// add privileges to roles and resource combinations
+$acl->allow('guest', 'blogPost', 'view');
+$acl->allow('owner', 'blogPost', 'post');
+$acl->allow('owner', 'blogPost', 'publish');
+]]></programlisting>
+
+        <para>
+            The above rules are quite simple: a guest role and an owner role exist; as does a blogPost
+            type resource.  Guests are allowed to view blog posts, and owners are allowed to post and publish
+            blog posts.  To query this system one might do any of the following:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+$guestUser = new Default_Model_User(); // assume the user model is of type guest resource 
+$ownerUser = new Default_Model_Owner('OwnersUsername');
+
+$post = new Default_Model_BlogPost();
+            
+$acl->isAllowed($guestUser, $post, 'view'); // true
+$acl->isAllowed($ownerUser, $post, 'view'); // true
+$acl->isAllowed($guestUser, $post, 'post'); // false
+$acl->isAllowed($ownerUser, $post, 'post'); // true
+]]></programlisting>
+        
+        <para>
+            As you can see, the above rules exercise whether owners and guests can view posts, which
+            they can, or post new posts, which owners can and guests cannot.  But as you might expect
+            this type of system might not be as dynamic as we wish it to be.  What if we want to ensure
+            a specific owner actual owns a very specific blog post before allowing him to publish it?
+            In other words, we want to ensure that only post owners have the ability to publish their
+            own posts.  
+        </para>
+        
+        <para>
+            This is where assertions come in.  Assertions are methods that will be called out to
+            when the static rule checking is simply not enough.  When registering an assertion object
+            this object will be consulted to determine, typically dynamically, if some roles has access
+            to some resource, with some optional privlidge that can only be answered by the logic within
+            the assertion.  For this example, we'll use the following assertion:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class OwnerCanPublishBlogPostAssertion implements Zend_Acl_Assert_Interface
+{
+    /**
+     * This assertion should receive the actual User and BlogPost objects.
+     *
+     * @param Zend_Acl $acl
+     * @param Zend_Acl_Role_Interface $user
+     * @param Zend_Acl_Resource_Interface $blogPost
+     * @param $privilege
+     * @return bool
+     */
+    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $user = null, Zend_Acl_Resource_Interface $blogPost = null, $privilege = null)
+    {
+    	if (!$user instanceof Default_Model_User) {
+            throw new Exception(__CLASS__ . '::' . __METHOD__ . ' expects the role to be an instance of User');
+        }
+
+        if (!$blogPost instanceof Default_Model_BlogPost) {
+            throw new Exception(__CLASS__ . '::' . __METHOD__ . ' expects the resource to be an instance of BlogPost');
+        }
+
+        // if role is publisher, he can always modify a post
+        if ($user->getRoleId() == 'publisher') {
+        	return true;
+        }
+
+        // check to ensure that everyone else is only modifying their own post
+        if ($user->id != null && $blogPost->ownerUserId == $user->id) {
+        	return true;
+        } else {
+        	return false;
+        }
+    }
+}
+
+]]></programlisting>
+        
+        <para>
+            To hook this into our ACL system, we would do the following:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+// replace this:
+//   $acl->allow('owner', 'blogPost', 'publish');
+// with this:
+$acl->allow('owner', 'blogPost', 'publish', new OwnerCanPublishBlogPostAssertion());
+
+// lets also add the role of a "publisher" who has access to everything
+$acl->allow('publisher', 'blogPost', 'publish');
+
+]]></programlisting>
+
+        <para>
+            Now, anytime the ACL is consulted about whether or not an owner can publish a specific
+            blog post, this assertion will be run.  This assertion will ensure that unless the role
+            type is 'publisher' the owner role must be logically tied to the blog post in question.
+            In this example, we check to see that the ownerUserId property of the blog post matches
+            the id of the owner passed in.
+        </para>
+
+        
+    </sect2>
+    
+</sect1>

+ 71 - 0
documentation/manual/en/tutorials/multiuser-intro.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.intro">
+    <title>Building Multi-User Applications With ZF</title>
+    
+    <sect2 id="learning.multiuser.intro.zf">
+        <title>Zend Framework</title>
+
+        <para>
+            When the original "web" was created, it was designed to be a publishing platform for
+            predominantly static content.  As demand for content on the web grew, as did the number of
+            consumers on the internet for web content, the demand for using the web as an application
+            platform also grew.  Since the web is inherently good at delivering a simultaneous
+            experience to many consumers from a single location, it makes it an ideal environment for
+            building dynamically driven, multi-user, and more commonly today, social systems.
+        </para>
+        
+        <para>
+            HTTP is the protocol of the web: a stateless, typically short lived, request/response
+            protocol.  This protocol was designed this way because the original intent of the web was
+            to serve or publish static content.  It is this very design that has made the web as
+            immensely successful as it is.  It is also exactly this design that brings new concerns to
+            developers who wish to use the web as an application platform.
+        </para>
+
+        <para>
+            These concerns and responsibilities can effectively be summed up by three questions:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    How do you distinguish one application consumer from another?
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    How do you identify a consumer as authentic?
+                </para>
+            </listitem>
+            
+            <listitem>
+                <para>
+                    How do you control what a consumer has access to?
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <note>
+            <title>Consumer Vs. User</title>
+            
+            <para>
+                Notice we use the term "consumer" instead of person.  Increasingly, web applications
+                are becoming service driven.  This means that not only are real people ("users") with
+                real web browsers consuming and using your application, but also other web applications
+                through machine service technologies such as ReST, SOAP, and XML-RPC.  In this respect,
+                people, as well as other consuming applications, should all be treated in same with
+                regard to the concerns outlined above.
+            </para>
+            
+        </note>
+        
+        <para>
+            In the following chapters, we'll take a look at these common problems relating to 
+            authentication and authorization in detail.  We will discover how 3 main components: 
+            Zend_Session, Zend_Auth, and Zend_Acl; provide an out-of-the-box solution as well as the 
+            extension points each have that will cater to a more customized solution.
+        </para>
+    </sect2>
+</sect1>

+ 133 - 0
documentation/manual/en/tutorials/multiuser-sessions.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.multiuser.sessions">
+    <title>Managing User Sessions In ZF</title>
+
+    <sect2 id="learning.multiuser.sessions.intro">
+        <title>Introduction to Sessions</title>
+        
+        <para>
+            The success of the web is deeply rooted in the protocol that drives the web: HTTP.  HTTP
+            over TCP is by its very nature stateless, which means that inherently the web is also 
+            stateless.  While this very aspect is one of the dominating factors for why the web has
+            become such a popular medium, it also causes an interesting problem for developers that
+            want to use the web as an application platform.
+        </para>
+        
+        <para>
+            The act of interacting with a web application is typically defined by the sum 
+            of all requests sent to a web server.  Since there can be many consumers being served 
+            simultaneously, the application must decide which requests belong to which consumer.  These 
+            requests are typically known as a "session".
+        </para>
+        
+        <para>
+            In PHP, the session problem is solved by the session extension which utilizes some state 
+            tracking, typically cookies, and some form of local storage which is exposed via the 
+            $_SESSION superglobal.  In Zend Framework, the component Zend_Session adds value to the php 
+            session extension making it easier to use and depend on inside object-oriented 
+            applications.
+        </para>
+
+    </sect2>
+    
+    <sect2 id="learning.multiuser.sessions.basic-usage">
+        <title>Basic Usage of Zend_Session</title>
+        
+        <para>
+            The Zend_Session component is both a session manager as well as an API for
+            storing data into a session object for long-term persistence.  The Zend_Session API is
+            for managing the options and behavior of a session, like options, starting and stopping
+            a session, whereas Zend_Session_Namespace is the actual object used to store data.
+        </para>
+        
+        <para>
+            While its generally good practice to start a session inside a bootstrap process, this
+            is generally not necessary as all sessions will be automatically started upon the first
+            creation of a Zend_Session_Namespace object.
+        </para>
+        
+        <para>
+            Zend_Application is capable of configuring Zend_Session for you as part of the
+            Zend_Application_Resource system.  To use this, assuming your project uses
+            Zend_Application to bootstrap, you would add the following code to your 
+            application.ini file:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+resources.session.save_path = APPLICATION_PATH "/../data/session"
+resources.session.use_only_cookies = true
+resources.session.remember_me_seconds = 864000
+]]></programlisting>
+        
+        <para>
+            As you can see, the options passed in are the same options that you'd expect to find
+            in the ext/session extension in PHP.  Those options setup the path to the session
+            files where data will be stored within the project.  Since ini files can additionally
+            use constants, the above will use the APPLICATION_PATH constant and relatively point
+            to a data session directory.
+        </para>
+        
+        <para>
+            Most Zend Framework components that use sessions need nothing more to use Zend_Session.
+            At this point, you an either use a component that consumes Zend_Session, or start 
+            storing your own data inside a session with Zend_Session_Namespace.
+        </para>
+        
+        <para>
+            Zend_Session_Namespace is a simple class that proxies data via an easy to use API
+            into the Zend_Session managed $_SESSION superglobal.  The reason it is called
+            Zend_Session_Namespace is that it effectively namespaces the data inside $_SESSION, thus
+            allowing multiple components and objects to safely store and retrieve data.  In the
+            following code, we'll explore how to build a simple session incrementing counter, starting
+            at 1000 and resetting itself after 1999.
+        </para>
+            
+        <programlisting language="php"><![CDATA[
+$mysession = Zend_Session_Namespace('mysession');
+
+if (!isset($mysession->counter)) {
+    $mysession->counter = 1000;
+} else {
+    $mysession->counter++;
+}
+
+if ($mysession->counter > 1999) {
+    unset($mysession->counter);
+}
+]]></programlisting>
+            
+        <para>
+            As you can see above, the session namespace object uses the magic __get, __set,
+            __isset, and __unset to allow you to seemlessly and fluently interact with the session.
+            The information stored in the above example is stored at $_SESSION['mysession']['counter'].
+        </para>
+
+    </sect2>
+    <sect2 id="learning.multiuser.sessions.basic-usage">
+        <title>Advanced Usage of Zend_Session</title>
+
+        <para>
+            Addionally, if you wanted to use the DbTable
+            save handler for Zend_Session, you'd add the following code to your application.ini:
+        </para>
+        
+        <programlisting language="php"><![CDATA[
+resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
+resources.session.saveHandler.options.name = "session"
+resources.session.saveHandler.options.primary.session_id = "session_id"
+resources.session.saveHandler.options.primary.save_path = "save_path"
+resources.session.saveHandler.options.primary.name = "name"
+resources.session.saveHandler.options.primaryAssignment.sessionId = "sessionId"
+resources.session.saveHandler.options.primaryAssignment.sessionSavePath = "sessionSavePath"
+resources.session.saveHandler.options.primaryAssignment.sessionName = "sessionName"
+resources.session.saveHandler.options.modifiedColumn = "modified"
+resources.session.saveHandler.options.dataColumn = "session_data"
+resources.session.saveHandler.options.lifetimeColumn = "lifetime"
+]]></programlisting>
+        
+        
+    </sect2>
+        
+
+</sect1>

+ 106 - 0
documentation/manual/en/tutorials/paginator-control.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.control">
+    <title>Pagination Control and ScrollingStyles</title>
+    
+    <para>
+        Rendering the items for a page on the screen has been a good start. In the code 
+        snippets in previous section we have also seen the <methodname>setCurrentPageNumber()</methodname>
+        method to set the active page number. The next step is to navigate through your pages.
+        To do this, Paginator provides you with two important tools: the ability to render the Paginator
+        with help of a View Partial, and support for so-called ScrollingStyles.
+    </para>
+    
+    <para>
+        The View Partial is a small view script that renders the Pagination controls, such as buttons to go 
+        to the next or previous page. Which pagination controls are rendered depends on the contents of
+        the view partial. Working with the view partial requires that you have set up Zend_View.
+        To get started with the pagination control, create a new view script somewhere in
+        your view scripts path. You can name it anything you want, but we'll call it "controls.phtml" in this text.
+        The reference manual contains various examples of what might go in the view script. Here is one example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<?php if ($this->pageCount): ?>
+<!-- First page link -->
+<?php if (isset($this->previous)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->first)); ?>">
+    First
+  </a> |
+<?php else: ?>
+  <span class="disabled">First</span> |
+<?php endif; ?>
+
+<!-- Previous page link -->
+<?php if (isset($this->previous)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
+    &lt; Previous
+  </a> |
+<?php else: ?>
+  <span class="disabled">&lt; Previous</span> |
+<?php endif; ?>
+
+<!-- Next page link -->
+<?php if (isset($this->next)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->next)); ?>">
+    Next &gt;
+  </a> |
+<?php else: ?>
+  <span class="disabled">Next &gt;</span> |
+<?php endif; ?>
+
+<!-- Last page link -->
+<?php if (isset($this->next)): ?>
+  <a href="<?php echo $this->url(array('page' => $this->last)); ?>">
+    Last
+  </a>
+<?php else: ?>
+  <span class="disabled">Last</span>
+<?php endif; ?>
+
+</div>
+<?php endif; ?>
+]]></programlisting>
+    
+    <para>
+        The next step is to tell Zend_Paginator which view partial can be used to render
+        the navigation controls. Put the following line in your application's bootstrap file.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+Zend_View_Helper_PaginationControl::setDefaultViewPartial('controls.phtml');
+]]></programlisting>
+    
+    <para>
+        The last step is probably the easiest. Make sure you have assigned your Paginator object
+        to the a script (NOT the 'controls.phtml' script!). The only thing left to do is echo the Paginator in the view script.
+        This will automatically render the Paginator using the PaginationControl view helper.
+        In this next example, the Paginator object has been assigned to the 'paginator' view variable.
+        Don't worry if you don't fully get how it all works yet. The next section will feature
+        a complete example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<?php echo $this->paginator; ?>
+]]></programlisting>
+    
+    <para>
+        <classname>Zend_Paginator</classname>, together with the 'controls.phtml' view script you wrote,
+        makes sure your Paginator navigation is rendered properly. In order to decide
+        which page numbers need to be displayed on screen, Paginator uses so-called ScrollingStyles.
+        The default style is called "Sliding", which is similar to the way Yahoo's search result
+        navigation works. To mimick Google's ScrollingStyle, use the Elastic style.
+        You can set a default ScrollingStyle with the static <methodname>setDefaultScrollingStyle()</methodname> method,
+        or you can specify a ScrollingStyle dynamically when rendering the Paginator in your view script. This
+        requires manual invocation of the view helper in your view script.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// $this->paginator is a Paginator object
+<?php echo $this->paginationControl($this->paginator, 'Elastic', 'controls.phtml'); ?>
+]]></programlisting>
+    
+    <para>
+        For a list of all available ScrollingStyles, see the reference manual.
+    </para>
+</sect1>

+ 37 - 0
documentation/manual/en/tutorials/paginator-intro.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.intro">
+    <title>Introduction</title>
+    
+    <para>
+        Let's say you're creating a blogging application that will be home to your vast
+        collection of blog posts. There is a good chance that you do not want all of
+        your blog posts to appear on one single page when someone visits your blog.
+        An obvious solution would be to only display a small number of blog posts
+        on the screen at a time, and allow the user to browse through the different pages,
+        much like your favorite search engine shows you the result of your search query.
+        <classname>Zend_Paginator</classname> is designed to help you achieve the goal of dividing collections
+        of data in smaller, more manageable sets more easily, with more consistency,
+        and with less duplicate code.
+    </para>
+    
+    <para>
+        <classname>Zend_Paginator</classname> uses Adapters to support various data sources and ScrollingStyles
+        to support various methods of showing the user which pages are available.
+        In later sections of this text we will have a closer look at what these things
+        are and how they can help you to make the most out of <classname>Zend_Paginator</classname>.
+    </para>
+    
+    <para>
+        Before going in-depth, we will have a look at some simple examples first.
+        After these simple examples, we will see how <classname>Zend_Paginator</classname> supports the most
+        common use-case; paginating database results.
+    </para>
+    
+    <para>
+        This introduction has given you a quick overview of <classname>Zend_Paginator</classname>. To get
+        started and to have a look at some code snippets, let's have a look at some
+        simple examples.
+    </para>
+    
+</sect1>

+ 124 - 0
documentation/manual/en/tutorials/paginator-simple.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.simple">
+    <title>Simple Examples</title>
+    
+    <para>
+        In this first example we won't do anything spectacular, but hopefully it will
+        give you a good idea of what Zend_Paginator is designed to do.
+        Let's say we have an array called $data with the numbers 1 to 100 in it, which
+        we want to divide over a number of pages. We can use the static <methodname>factory()</methodname>
+        method in the <classname>Zend_Paginator</classname> class to get a <classname>Zend_Paginator</classname>
+        object with our array in it.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+]]></programlisting>
+    
+    <para>
+        We're already almost done! The $paginator variable now contains a reference to the
+        Paginator object. By default it is setup to display 10 items per page.
+        To display the items for the currently active page, all you need to do is iterate
+        over the Paginator object with a foreach loop. The currently active page defaults
+        to the first page if it's not explicitly specified. We will see how you can select
+        a specific page later on. The snippet below will display an unordered list containing the
+        numbers 1 to 10, which are the numbers on the first page.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+
+?><ul><?php
+
+// Render each item for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        Now let's try and render the items on the second page. You can use the
+        <methodname>setCurrentPageNumber()</methodname> method to select which page you want to view.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create an array with numbers 1 to 100
+$data = range(1, 100);
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($data);
+
+// Select the second page
+$paginator->setCurrentPageNumber(2);
+
+?><ul><?php
+
+// Render each item for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        As expected, this little snippet will render an unordered list with the numbers
+        11 to 20 in it.
+    </para>
+    
+    <para>
+        These simple examples demonstrate a small portion of what can be achieved with
+        <classname>Zend_Paginator</classname>. However, a real application rarely reads its data from a plain
+        array, so the next section is dedicated to showing you how you can use Paginator
+        to paginate the results of a database query. Before reading on, make sure you're familiar with
+        the way <classname>Zend_Db_Select</classname> works!
+    </para>
+    
+    <para>
+        In the database examples we will look at a table with blog posts called 'posts'.
+        The 'posts' table has four columns: id, title, body, date_created.
+        Let's dive right in and have a look at a simple example.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+// Create a select query. $db is a Zend_Db_Adapter object, which we assume
+// already exists in your script.
+$select = $db->select()->from('posts')->order('date_created DESC');
+
+// Get a Paginator object using Zend_Paginator's built-in factory.
+$paginator = Zend_Paginator::factory($select);
+
+// Select the second page
+$paginator->setCurrentPageNumber(2);
+
+?><ul><?php
+
+// Render each the title of each post for the current page in a list-item
+foreach ($paginator as $item) {
+    echo '<li>' . $item->title . '</li>';
+}
+
+?></ul>
+]]></programlisting>
+    
+    <para>
+        As you can see, this example is not that different from the previous one.
+        The only difference is that you pass a <classname>Zend_Db_Select</classname> object to the 
+        Paginator's <methodname>factory()</methodname> method, rather than an array.
+        For more details on how the database adapter makes sure that your query
+        is being executed efficiently, see the Zend_Paginator chapter in the reference manual
+        on the DbSelect and DbTableSelect adapters.
+    </para>
+    
+</sect1>

+ 74 - 0
documentation/manual/en/tutorials/paginator-together.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.paginator.together">
+    <title>Putting it all Together</title>
+    
+    <para>
+        You have seen how to create a Paginator object, how to render the items on the current page, and
+        how to render a navigation element to browse through your pages. In this section you will
+        see how Paginator fits in with the rest of your MVC application.
+    </para>
+    
+    <para>
+        In the following examples we will ignore the best practice implementation of using a Service Layer to keep
+        the example simple and easier to understand. Once you get familiar with using Service Layers, it should be easy to see
+        how Paginator can fit in with the best practice approach.
+    </para>
+    
+    <para>
+        Lets start with the controller. The sample application is simple, and we'll just put everything
+        in the IndexController and the IndexAction. Again, this is for demonstration purposes only. A real application
+        should not use controllers in this manner.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+class IndexController extends Zend_Controller_Action
+{
+    public function indexAction()
+    {
+        // Setup pagination control view script. See the pagation control tutorial page
+        // for more information about this view script.
+        Zend_View_Helper_PaginationControl::setDefaultViewPartial('controls.phtml');
+        
+        // Fetch an already instantiated database connection from the registry
+        $db = Zend_Registry::get('db');
+        
+        // Create a select object which fetches blog posts, sorted decending by date of creation
+        $select = $db->select()->from('posts')->sort('date_created DESC');
+        
+        // Create a Paginator for the blog posts query
+        $paginator = Zend_Paginator::factory($select);
+        
+        // Read the current page number from the request. Default to 1 if no explicit page number is provided.
+        $paginator->setCurrentPageNumber($this->_getParam('page', 1));
+        
+        // Assign the Paginator object to the view
+        $this->view->paginator = $paginator;
+    }
+}
+]]></programlisting>
+    
+    <para>
+        The following view script is the index.phtml view script for the IndexController's indexAction.
+        The view script can be kept simple. We're assuming the use of the default ScrollingStyle.
+    </para>
+    
+    <programlisting language="php"><![CDATA[
+<ul>
+<?php
+// Render each the title of each post for the current page in a list-item
+foreach ($this->paginator as $item) {
+    echo '<li>' . $item->title . '</li>';
+}
+?>
+</ul>
+<?php echo $this->paginator; ?>
+]]></programlisting>
+    
+    <para>
+        Now navigate to your project's index and see Paginator in action. What we have discussed in this
+        tutorial is just the tip of the iceberg. The reference manual and API documentation can tell
+        you more about what you can do with Zend_Paginator.
+    </para>
+    
+</sect1>

+ 61 - 0
documentation/manual/en/tutorials/plugins-conclusion.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.conclusion">
+    <title>Conclusion</title>
+
+    <para>
+        Understanding the concept of prefix paths and overriding existing plugins will help you with
+        your understanding of many components within the framework. Plugins are used in a variety of
+        places: 
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <classname>Zend_Application</classname>: resources.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Controller_Action</classname>: action helpers.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Feed_Reader</classname>: plugins.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_Form</classname>: elements, filters, validators, and decorators.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <classname>Zend_View</classname>: view helpers.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        And several more places, besides. Learn the concepts early so you can leverage this
+        important extension point in Zend Framework.
+    </para>
+
+    <note>
+        <title>Caveat</title>
+
+        <para>
+            We'll note here that <classname>Zend_Controller_Front</classname> has a plugin system -
+            but it does not adhere to any of the guidelines offerred in this tutorial. The plugins
+            registered with the front controller must be instantiated directly and registered
+            individually with it. The reason for this is that this system predates any other plugin
+            system in the framework, and changes to it must be carefully weighed to ensure existing
+            plugins written by developers continue to work with it.
+        </para>
+    </note>
+</sect1>

+ 54 - 0
documentation/manual/en/tutorials/plugins-intro.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.intro">
+    <title>Introduction</title>
+
+    <para>
+        Zend Framework makes heavy use of plugin architectures. Plugins allow for easy extensibility
+        and customization of the framework while keeping your code separate from the Zend Framework
+        code.
+    </para>
+
+    <para>
+        Typically, plugins in Zend Framework work as follows:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                Plugins are classes. The actual class definition will vary based on the component --
+                you may need to extend an abstract class or implement an interface, but the fact
+                remains that the plugin is itself a class.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Related plugins will share a common class prefix. For instance, if you have created
+                a number of view helpers, they might all share the class prefix "Foo_View_Helper_". 
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Everything after the common prefix will be considered the <emphasis>plugin
+                    name</emphasis> or <emphasis>short name</emphasis> (versus the "long name",
+                which is the full classname). For example, if the plugin prefix is
+                "Foo_View_Helper_", and the class name is "Foo_View_Helper_Bar", the plugin name
+                will be simply "Bar".
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Plugin names are typically case sensitive. The one caveat is that the initial letter
+                can often be either lower or uppercase; in our previous example, both "bar" and
+                "Bar" would refer to the same plugin.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Now let's turn to using plugins.
+    </para>
+</sect1>

+ 151 - 0
documentation/manual/en/tutorials/plugins-usage.xml

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.plugins.usage">
+    <title>Using Plugins</title>
+
+    <para>
+        Components that make use of plugins typically use
+        <classname>Zend_Loader_PluginLoader</classname> to do their work. This class has you
+        register plugins by specifying one or more "prefix paths". The component will then call the
+        PluginLoader's <methodname>load()</methodname> method, passing the plugin's short name to
+        it. The PluginLoader will then query each prefix path to see if a class matching that short
+        name exists. Prefix paths are searched in LIFO (last in, first out) order, so it will match
+        those prefix paths registered last first -- allowing you to override existing plugins.
+    </para>
+
+    <para>
+        Some examples will make all of this more clear.
+    </para>
+
+    <example id="learning.plugins.usage.basic">
+        <title>Basic Plugin Example: Adding a single prefix path</title>
+
+        <para>
+            In this example, we will assume some validators have been written and placed in the
+            directory <filename>foo/plugins/validators/</filename>, and that all these classes share
+            the class prefix "Foo_Validate_"; these two bits of information form our "prefix path".
+            Furthermore, let's assume we have two validators, one named "Even" (ensuring a number to
+            be validated is even), and another named "Dozens" (ensuring the number is a multiple of
+            12). The tree might look like this:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+foo/
+|-- plugins/
+|   |-- validators/
+|   |   |-- Even.php
+|   |   |-- Dozens.php
+]]></programlisting>
+
+        <para>
+            Now, we'll inform a <classname>Zend_Form_Element</classname> instance of this prefix
+            path. <classname>Zend_Form_Element</classname>'s
+            <methodname>addPrefixPath()</methodname> method expects a third argument that indicates
+            the type of plugin for which the path is being registered; in this case, it's a
+            "validate" plugin.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element->addPrefixPath('Foo_Validate', 'foo/plugins/validators/', 'validate');
+]]></programlisting>
+
+        <para>
+            Now we can simply tell the element the short name of the validators we want to use. In
+            the following example, we're using a mix of standard validators ("NotEmpty", "Int") and
+            custom validators ("Even", "Dozens"):
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$element->addValidator('NotEmpty')
+        ->addValidator('Int')
+        ->addValidator('Even')
+        ->addValidator('Dozens');
+]]></programlisting>
+
+        <para>
+            When the element needs to validate, it will then request the plugin class from the
+            PluginLoader. The first two validators will resolve to
+            <classname>Zend_Validate_NotEmpty</classname> and
+            <classname>Zend_Validate_Int</classname>, respectively; the next two will resolve to
+            <classname>Foo_Validate_Even</classname> and <classname>Foo_Validate_Dozens</classname>,
+            respectively.
+        </para>
+    </example>
+
+    <note>
+        <title>What happens if a plugin is not found?</title>
+
+        <para>
+            What happens if a plugin is requested, but the PluginLoader is unable to find a class
+            matching it? For instance, in the above example, if we registered the plugin "Bar" with
+            the element, what would happen?
+        </para>
+
+        <para>
+            The plugin loader will look through each prefix path, checking to see if a file matching
+            the plugin name is found on that path. If the file is not found, it then moves on to the
+            next prefix path.
+        </para>
+
+        <para>
+            Once the stack of prefix paths has been exhausted, if no matching file has been found,
+            it will throw a <exceptionname>Zend_Loader_PluginLoader_Exception</exceptionname>.
+        </para>
+    </note>
+
+    <example id="learning.plugins.usage.override">
+        <title>Intermediate Plugin Usage: Overriding existing plugins</title>
+
+        <para>
+            One strength of the PluginLoader is that its use of a LIFO stack allows you to override
+            existing plugins by creating your own versions locally with a different prefix path, and
+            registering that prefix path later in the stack.
+        </para>
+
+        <para>
+            For example, let's consider <classname>Zend_View_Helper_FormButton</classname> (view
+            helpers are one form of plugin). This view helper accepts three arguments, an element
+            name (also used as the element's DOM identifier), a value (used as the button label),
+            and an optional array of attributes.  The helper then generates HTML markup for a form
+            input element.
+        </para>
+
+        <para>
+            Let's say you want the helper to instead generate a true HTML
+            <constant>button</constant> element; don't want the helper to generate a DOM identifier,
+            but instead use the value for a CSS class selector; and that you have no interest in
+            handling arbitrary attributes. You could accomplish this in a couple of ways. In both
+            cases, you'd create your own view helper class that implements the behavior you want;
+            the difference is in how you would name and invoke them.
+        </para>
+
+        <para>
+            Our first example will be to name the element with a unique name:
+            <classname>Foo_View_Helper_CssButton</classname>, which implies the plugin name
+            "CssButton". While this certainly is a viable approach, it poses several issues: if
+            you've already used the Button view helper in your code, you now have to refactor;
+            alternately, if another developer starts writing code for your application, they may
+            inadvertently use the Button view helper instead of your new view helper.
+        </para>
+
+        <para>
+            So, the better example is to use the plugin name "Button", giving us the class name
+            <classname>Foo_View_Helper_Button</classname>. We then register the prefix path with the
+            view:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// Zend_View::addHelperPath() utilizes the PluginLoader; however, it inverts 
+// the arguments, as it provides a default value of "Zend_View_Helper" for the
+// plugin prefix.
+//
+// The below assumes your class is in the directory 'foo/view/helpers/'.
+$view->addHelperPath('foo/view/helpers', 'Foo_View_Helper');
+]]></programlisting>
+
+        <para>
+            Once done, anywhere you now use the "Button" helper will delegate to your custom
+            <classname>Foo_View_Helper_Button</classname> class!
+        </para>
+    </example>
+</sect1>

+ 15 - 0
documentation/manual/en/tutorials/quickstart-conclusion.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.conclusion">
+    <title>Congratulations!</title>
+
+    <para>
+        You have now built a very simple application using some of the most commonly used Zend
+        Framework components. Zend Framework makes many components available to you which address
+        most common requirements in web applications, including web services, search, PDF reading
+        and writing, authentication, authorization, and much more. The <link
+            linkend="reference">Reference Guide</link> is a great place to find out more about the
+        components you've used in this QuickStart as well as other components. We hope you find Zend
+        Framework useful and - more importantly - fun!
+    </para>
+</sect1>

+ 171 - 0
documentation/manual/en/tutorials/quickstart-create-form.xml

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-form">
+    <title>Create A Form</title>
+
+    <para>
+        For our guestbook to be useful, we need a form for submitting new entries.
+    </para>
+
+    <para>
+        Our first order of business is to create the actual form class. First, create the directory
+        <filename>application/forms/</filename>. This directory will contain form classes for the
+        application. Next, we'll create a form class in
+        <filename>application/forms/Guestbook.php</filename>:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/forms/Guestbook.php
+
+class Default_Form_Guestbook extends Zend_Form
+{
+    public function init()
+    {
+        // Set the method for the display form to POST
+        $this->setMethod('post');
+
+        // Add an email element
+        $this->addElement('text', 'email', array(
+            'label'      => 'Your email address:',
+            'required'   => true,
+            'filters'    => array('StringTrim'),
+            'validators' => array(
+                'EmailAddress',
+            )
+        ));
+
+        // Add the comment element
+        $this->addElement('textarea', 'comment', array(
+            'label'      => 'Please Comment:',
+            'required'   => true,
+            'validators' => array(
+                array('validator' => 'StringLength', 'options' => array(0, 20))
+                )
+        ));
+
+        // Add a captcha
+        $this->addElement('captcha', 'captcha', array(
+            'label'      => 'Please enter the 5 letters displayed below:',
+            'required'   => true,
+            'captcha'    => array(
+                'captcha' => 'Figlet', 
+                'wordLen' => 5, 
+                'timeout' => 300
+            )
+        ));
+
+        // Add the submit button
+        $this->addElement('submit', 'submit', array(
+            'ignore'   => true,
+            'label'    => 'Sign Guestbook',
+        ));
+
+        // And finally add some CSRF protection
+        $this->addElement('hash', 'csrf', array(
+            'ignore' => true,
+        ));
+    }
+}
+]]></programlisting>
+
+    <para>
+        The above form defines five elements: an email address field, a comment field, a CAPTCHA for
+        preventing spam submissions, a submit button, and a CSRF protection token.
+    </para>
+
+    <para>
+        Next, we will add a <methodname>signAction()</methodname> to our
+        <classname>GuestbookController</classname> which will process the form upon submission. To
+        create the action and related view script, execute the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+# Unix-like systems:
+% zf.sh create action sign guestbook
+
+# DOS/Windows:
+C:> zf.bat create action sign guestbook
+]]></programlisting>
+
+    <para>
+        This will create a <methodname>signAction()</methodname> method in our controller, as well
+        as the appropriate view script.
+    </para>
+
+    <para>
+        Let's add some logic into our guestbook controller's sign action. We need to first check if
+        we're getting a POST or a GET request; in the latter case, we'll simply display the form.
+        However, if we get a POST request, we'll want to validate the posted data against our form,
+        and, if valid, create a new entry and save it. The logic might look like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/controllers/GuestbookController.php
+
+class GuestbookController extends Zend_Controller_Action
+{
+    // snipping indexAction()...
+
+    public function signAction()
+    {
+        $request = $this->getRequest();
+        $form    = new Default_Form_Guestbook();
+
+        if ($this->getRequest()->isPost()) {
+            if ($form->isValid($request->getPost())) {
+                $model = new Default_Model_Guestbook($form->getValues());
+                $model->save();
+                return $this->_helper->redirector('index');
+            }
+        }
+        
+        $this->view->form = $form;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Of course, we also need to edit the view script; edit <filename>application/views/scripts/guestbook/sign.phtml</filename> to read:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/guestbook/sign.phtml -->
+
+Please use the form below to sign our guestbook!
+
+<?php 
+$this->form->setAction($this->url());
+echo $this->form;
+]]></programlisting>
+
+    <note>
+        <title>Better Looking Forms</title>
+
+        <para>
+            No one will be waxing poetic about the beauty of this form anytime soon. No matter -
+            form appearance is fully customizable! See the <link
+                linkend="zend.form.decorators">decorators section in the reference guide</link>
+            for details.
+        </para>
+
+        <para>
+            Additionally, you may be interested in <ulink
+                url="http://weierophinney.net/matthew/plugin/tag/decorators">this series of posts on
+                decorators</ulink>.
+        </para>
+    </note>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now browse to "http://localhost/guestbook/sign". You should see the following in your
+            browser:
+        </para>
+
+        <para>
+            <inlinegraphic width="421" scale="100" align="center" valign="middle"
+                fileref="figures/learning.quickstart.create-form.png" format="PNG" />
+        </para>
+    </note>
+</sect1>

+ 213 - 0
documentation/manual/en/tutorials/quickstart-create-layout.xml

@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-layout">
+    <title>Create A Layout</title>
+
+    <para>
+        You may have noticed that the view scripts in the previous sections were HTML fragments- not
+        complete pages. This is by design; we want our actions to return content only related to the
+        action itself, not the application as a whole.
+    </para>
+
+    <para>
+        Now we must compose that generated content into a full HTML page. We'd also like to have a
+        consistent look and feel for the application. We will use a global site layout to accomplish
+        both of these tasks.
+    </para>
+
+    <para>
+        There are two design patterns that Zend Framework uses to implement layouts: <ulink
+            url="http://martinfowler.com/eaaCatalog/twoStepView.html">Two Step View</ulink> and
+        <ulink
+            url="http://java.sun.com/blueprints/corej2eepatterns/Patterns/CompositeView.html">Composite
+            View</ulink>. <emphasis>Two Step View</emphasis> is usually associated with the <ulink
+            url="http://www.martinfowler.com/eaaCatalog/transformView.html">Transform View</ulink>
+        pattern; the basic idea is that your application view creates a representation that is then
+        injected into the master view for final transformation. The <emphasis>Composite
+            View</emphasis> pattern deals with a view made of one or more atomic, application views.
+    </para>
+
+    <para>
+        In Zend Framework, <link linkend="zend.layout">Zend_Layout</link> combines the ideas behind
+        these patterns. Instead of each action view script needing to include site-wide artifacts,
+        they can simply focus on their own responsibilities.
+    </para>
+
+    <para>
+        Occasionally, however, you may need application-specific information in your site-wide view
+        script. Fortunately, Zend Framework provides a variety of view
+        <emphasis>placeholders</emphasis> to allow you to provide such information from your action
+        view scripts.
+    </para>
+
+    <para>
+        To get started using Zend_Layout, first we need to inform our bootstrap to use the
+        <classname>Layout</classname> resource. This can be done by adding the following line to
+        your <filename>application/configs/application.ini</filename> file, within the
+        <constant>production</constant> section:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add to [production] section:
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+]]></programlisting>
+
+    <para>
+        The final INI file should look as follows:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+; PHP settings we want to initialize
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+includePaths.library = APPLICATION_PATH "/../library"
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+]]></programlisting>
+
+    <para>
+        This directive tells your application to look for layout view scripts in
+        <filename>application/layouts/scripts</filename>. Create those directories now.
+    </para>
+
+    <para>
+        We also want to ensure we have an XHTML DocType declaration for our application. To enable
+        this, we need to add a resource to our bootstrap.
+    </para>
+
+    <para>
+        The simplest way to add a bootstrap resource is to simply create a protected method
+        beginning with the phrase <methodname>_init</methodname>. In this case, we want to
+        initialize the doctype, so we'll create an <methodname>_initDoctype()</methodname> method
+        within our bootstrap class:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDoctype()
+    {
+    }
+}
+]]></programlisting>
+
+    <para>
+        Within that method, we need to hint to the view to use the appropriate doctype. But where
+        will the view object come from? The easy solution is to initialize the
+        <classname>View</classname> resource; once we have, we can pull the view object from the
+        bootstrap and use it.
+    </para>
+
+    <para>
+        To initialize the view resource, add the following line to your
+        <filename>application/configs/application.ini</filename> file, in the section marked
+        <constant>production</constant>:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add to [production] section:
+resources.view[] =
+]]></programlisting>
+
+    <para>
+        This tells us to initialize the view with no options (the '[]' indicates that the "view" key
+        is an array, and we pass nothing to it).
+    </para>
+
+    <para>
+        Now that we have a view, let's flesh out our <methodname>_initDoctype()</methodname> method.
+        In it, we will first ensure the <classname>View</classname> resource has run, fetch the view
+        object, and then configure it:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDoctype()
+    {
+        $this->bootstrap('view');
+        $view = $this->getResource('view');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now that we've initialized <classname>Zend_Layout</classname> and set the Doctype, let's
+        create our site-wide layout:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/layouts/scripts/layout.phtml
+
+echo $this->doctype() ?>
+<html xmlns="http://www.w3.org/1999/xhtml"> 
+<head>  
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+  <title>Zend Framework Quickstart Application</title>
+  <?php echo $this->headLink()->appendStylesheet('/css/global.css') ?>
+</head> 
+<body>
+<div id="header" style="background-color: #EEEEEE; height: 30px;">
+    <div id="header-logo" style="float: left">
+        <b>ZF Quickstart Application</b>
+    </div>
+    <div id="header-navigation" style="float: right">
+        <a href="<?php echo $this->url(
+            array('controller'=>'guestbook'), 
+            'default', 
+            true) ?>">Guestbook</a>
+    </div>
+</div>
+
+<?php echo $this->layout()->content ?>
+
+</body>
+</html>
+]]></programlisting>
+
+    <para>
+        We grab our application content using the <methodname>layout()</methodname> view helper, and
+        accessing the "content" key. You may render to other response segments if you wish to, but
+        in most cases, this is all that's necessary.
+    </para>
+
+    <para>
+        Note also the use of the <methodname>headLink()</methodname> placeholder. This is an easy
+        way to generate the HTML for &lt;link&gt; elements, as well as to keep track of them
+        throughout your application. If you need to add additional CSS sheets to support a single
+        action, you can do so, and be assured it will be present in the final rendered page.
+    </para>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now go to "http://localhost" and check out the source. You should see your XHTML header,
+            head, title, and body sections.
+        </para>
+    </note>
+</sect1>

+ 760 - 0
documentation/manual/en/tutorials/quickstart-create-model.xml

@@ -0,0 +1,760 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-model">
+    <title>Create a Model and Database Table</title>
+
+    <para>
+        Before we get started, let's consider something: where will these classes live, and how will
+        we find them? The default project we created instantiates an autoloader. We can attach other
+        autoloaders to it so that it knows where to find different classes. Typically, we want our
+        various MVC classes grouped under the same tree -- in this case,
+        <filename>application/</filename> -- and most often using a common prefix.
+    </para>
+
+    <para>
+        <classname>Zend_Controller_Front</classname> has a notion of "modules", which are individual
+        mini-applications.  Modules mimic the directory structure that the <command>zf</command>
+        tool sets up under <filename>application/</filename>, and all classes inside them are
+        assumed to begin with a common prefix, the module name.  <filename>application/</filename>
+        is itself a module -- the "default" module. As such, let's setup autoloading for resources
+        within this directory, giving them a prefix of "Default". We can do this by creating another
+        bootstrap resource.
+    </para>
+
+    <para>
+        <classname>Zend_Application_Module_Autoloader</classname> provides the functionality needed
+        to map the various resources under a module to the appropriate directories, and provides a
+        standard naming mechanism as well. In our bootstrap resource, we'll instantiate this, and be
+        done. The method looks like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+// Add this method to the Bootstrap class:
+
+    protected function _initAutoload()
+    {
+        $autoloader = new Zend_Application_Module_Autoloader(array(
+            'namespace' => 'Default_',
+            'basePath'  => dirname(__FILE__),
+        ));
+        return $autoloader;
+    }
+]]></programlisting>
+
+    <para>
+        The final bootstrap class will look as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initAutoload()
+    {
+        $autoloader = new Zend_Application_Module_Autoloader(array(
+            'namespace' => 'Default',
+            'basePath'  => dirname(__FILE__),
+        ));
+        return $autoloader;
+    }
+
+    protected function _initDoctype()
+    {
+        $this->bootstrap('view');
+        $view = $this->getResource('view');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now, let's consider what makes up a guestbook. Typically, they are simply a list of entries
+        with a <emphasis>comment</emphasis>, <emphasis>timestamp</emphasis>, and, often,
+        <emphasis>email address</emphasis>. Assuming we store them in a database, we may also want a
+        <emphasis>unique identifier</emphasis> for each entry. We'll likely want to be able to save
+        an entry, fetch individual entries, and retrieve all entries. As such, a simple guestbook
+        model API might look something like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/Guestbook.php
+
+class Default_Model_Guestbook
+{
+    protected $_comment;
+    protected $_created;
+    protected $_email;
+    protected $_id;
+
+    public function __set($name, $value);
+    public function __get($name);
+
+    public function setComment($text);
+    public function getComment();
+
+    public function setEmail($email);
+    public function getEmail();
+
+    public function setCreated($ts);
+    public function getCreated();
+
+    public function setId($id);
+    public function getId();
+
+    public function save();
+    public function find($id);
+    public function fetchAll();
+}
+]]></programlisting>
+
+    <para>
+        <methodname>__get()</methodname> and <methodname>__set()</methodname> will provide a
+        convenience mechanism for us to access the individual entry properties, and proxy to the
+        other getters and setters. They also will help ensure that only properties we whitelist will
+        be available in the object.
+    </para>
+
+    <para>
+        <methodname>find()</methodname> and <methodname>fetchAll()</methodname> provide the ability
+        to fetch a single entry or all entries.
+    </para>
+
+    <para>
+        Now from here, we can start thinking about setting up our database.
+    </para>
+
+    <para>
+        First we need to initialize our <classname>Db</classname> resource. As with the
+        <classname>Layout</classname> and <classname>View</classname> resource, we can provide
+        configuration for the <classname>Db</classname> resource. In your
+        <filename>application/configs/application.ini</filename> file, add the following lines in
+        the appropriate sections.
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+; Add these lines to the appropriate sections:
+[production]
+resources.db.adapter       = "PDO_SQLITE"
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
+
+[testing : production]
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
+
+[development : production]
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
+]]></programlisting>
+    
+    <para>
+        Your final configuration file should look like the following:
+    </para>
+
+    <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
+resources.view[] = 
+resources.db.adapter = "PDO_SQLITE"
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
+]]></programlisting>
+
+    <para>
+        Note that the database(s) will be stored in <filename>data/db/</filename>. Create those
+        directories, and make them world-writeable. On unix-like systems, you can do that as
+        follows:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+% mkdir -p data/db; chmod -R a+rwX data
+]]></programlisting>
+
+    <para>
+        On Windows, you will need to create the directories in Explorer and set the permissions to
+        allow anyone to write to the directory.
+    </para>
+
+    <para>
+        At this point we have a connection to a database; in our case, its a connection to a Sqlite
+        database located inside our <filename>application/data/</filename> directory. So, let's
+        design a simple table that will hold our guestbook entries.
+    </para>
+
+    <programlisting language="sql"><![CDATA[
+-- scripts/schema.sqlite.sql
+--
+-- You will need load your database schema with this SQL.
+
+CREATE TABLE guestbook (
+    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+    email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
+    comment TEXT NULL,
+    created DATETIME NOT NULL
+);
+
+CREATE INDEX "id" ON "guestbook" ("id");
+]]></programlisting>
+
+    <para>
+        And, so that we can have some working data out of the box, lets create a few rows of
+        information to make our application interesting.
+    </para>
+
+    <programlisting language="sql"><![CDATA[
+-- scripts/data.sqlite.sql
+--
+-- You can begin populating the database with the following SQL statements.
+
+INSERT INTO guestbook (email, comment, created) VALUES 
+    ('ralph.schindler@zend.com', 
+    'Hello! Hope you enjoy this sample zf application!', 
+    DATETIME('NOW'));
+INSERT INTO guestbook (email, comment, created) VALUES 
+    ('foo@bar.com', 
+    'Baz baz baz, baz baz Baz baz baz - baz baz baz.', 
+    DATETIME('NOW'));
+]]></programlisting>
+
+    <para>
+        Now that we have both the schema and some data defined. Lets get a script together that we
+        can now execute to build this database. Naturally, this is not needed in production, but
+        this script will help developers build out the database requirements locally so they can
+        have the fully working application. Create the script as
+        <filename>scripts/load.sqlite.php</filename> with the following contents:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// scripts/load.sqlite.php
+
+/**
+ * Script for creating and loading database
+ */
+
+// Initialize the application path and autoloading
+defined('APPLICATION_PATH')
+    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
+set_include_path(implode(PATH_SEPARATOR, array(
+    APPLICATION_PATH . '/../library',
+    get_include_path(),
+)));
+require_once 'Zend/Loader/Autoloader.php';
+Zend_Loader_Autoloader::getInstance();
+
+// Define some CLI options
+$getopt = new Zend_Console_Getopt(array(
+    'withdata|w' => 'Load database with sample data',
+    'env|e-s'    => 'Application environment for which to create database (defaults to development)',
+    'help|h'     => 'Help -- usage message',
+));
+try {
+    $getopt->parse();
+} catch (Zend_Console_Getopt_Exception $e) {
+    // Bad options passed: report usage
+    echo $e->getUsageMessage();
+    return false;
+}
+
+// If help requested, report usage message
+if ($getopt->getOption('h')) {
+    echo $getopt->getUsageMessage();
+    return true;
+}
+
+// Initialize values based on presence or absence of CLI options
+$withData = $getopt->getOption('w');
+$env      = $getopt->getOption('e');
+defined('APPLICATION_ENV')
+    || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
+
+// Initialize Zend_Application
+$application = new Zend_Application(
+    APPLICATION_ENV,
+    APPLICATION_PATH . '/configs/application.ini'
+);
+
+// Initialize and retrieve DB resource
+$bootstrap = $application->getBootstrap();
+$bootstrap->bootstrap('db');
+$dbAdapter = $bootstrap->getResource('db');
+
+// let the user know whats going on (we are actually creating a 
+// database here)
+if ('testing' != APPLICATION_ENV) {
+    echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
+    for ($x = 5; $x > 0; $x--) {
+        echo $x . "\r"; sleep(1);
+    }
+}
+
+// Check to see if we have a database file already
+$options = $bootstrap->getOption('resources');
+$dbFile  = $options['db']['params']['dbname'];
+if (file_exists($dbFile)) {
+    unlink($dbFile);
+}
+
+// this block executes the actual statements that were loaded from 
+// the schema file.
+try {
+    $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
+    // use the connection directly to load sql in batches
+    $dbAdapter->getConnection()->exec($schemaSql);
+    chmod($dbFile, 0666);
+
+    if ('testing' != APPLICATION_ENV) {
+        echo PHP_EOL;
+        echo 'Database Created';
+        echo PHP_EOL;
+    }
+    
+    if ($withData) {
+        $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
+        // use the connection directly to load sql in batches
+        $dbAdapter->getConnection()->exec($dataSql);
+        if ('testing' != APPLICATION_ENV) {
+            echo 'Data Loaded.';
+            echo PHP_EOL;
+        }
+    }
+    
+} catch (Exception $e) {
+    echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
+    echo $e->getMessage() . PHP_EOL;
+    return false;
+}
+
+// generally speaking, this script will be run from the command line
+return true;
+]]></programlisting>
+
+    <para>
+        Now, let's execute this script. From a terminal or the DOS command line, do the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+% php scripts/load.sqlite.php --withdata
+]]></programlisting>
+
+    <para>
+        You should see output like the following:
+    </para>
+
+    <programlisting language="text"><![CDATA[
+path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
+Writing Database Guestbook in (control-c to cancel): 
+1
+Database Created
+Data Loaded.
+]]></programlisting>
+
+    <para>
+        Now we have a fully working database and table for our guestbook application. Our next few
+        steps are to build out our application code. This includes building a data source (in our
+        case, we will use <classname>Zend_Db_Table</classname>), and a data mapper to connect that
+        data source to our domain model. Finally we'll also create the controller that will interact
+        with this model to both display existing entries and process new entries.
+    </para>
+
+    <para>
+        We'll use a <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
+            Gateway</ulink> to connect to our data source; <classname>Zend_Db_Table</classname>
+        provides this functionality. To get started, lets create a
+        <classname>Zend_Db_Table</classname>-based table class. First, create the directory
+        <filename>application/models/DbTable/</filename>. Then create and edit a file
+        <filename>Guestbook.php</filename> within it, and add the following contents:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/DbTable/Guestbook.php
+
+/**
+ * This is the DbTable class for the guestbook table.
+ */
+class Default_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
+{
+    /** Table name */
+    protected $_name    = 'guestbook';
+}
+]]></programlisting>
+
+    <para>
+        Note the class prefix: <classname>Default_Model_DbTable</classname>. The class prefix
+        "Default" from our autoloader is the first segment, and then we have the component,
+        "Model_DbTable"; the latter is mapped to the <filename>models/DbTable/</filename> directory
+        of the module.
+    </para>
+
+    <para>
+        All that is truly necessary when extending <classname>Zend_Db_Table</classname> is to
+        provide a table name and optionally the primary key (if it is not "id").
+    </para>
+
+    <para>
+        Now let's create a <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data
+            Mapper</ulink>. A <emphasis>Data Mapper</emphasis> maps a domain object to the database.
+        In our case, it will map our model, <classname>Default_Model_Guestbook</classname>, to our
+        data source, <classname>Default_Model_DbTable_Guestbook</classname>. A typical API for a
+        data mapper is as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/GuestbookMapper.php
+
+class Default_Model_GuestbookMapper
+{
+    public function save($model);
+    public function find($id, $model);
+    public function fetchAll();
+}
+]]></programlisting>
+
+    <para>
+        In addition to these methods, we'll add methods for setting and retrieving the Table Data
+        Gateway. The final class, located in
+        <filename>application/models/GuestbookMapper.php</filename>, looks like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/GuestbookMapper.php
+
+class Default_Model_GuestbookMapper
+{
+    protected $_dbTable;
+
+    public function setDbTable($dbTable)
+    {
+        if (is_string($dbTable)) {
+            $dbTable = new $dbTable();
+        }
+        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
+            throw new Exception('Invalid table data gateway provided');
+        }
+        $this->_dbTable = $dbTable;
+        return $this;
+    }
+
+    public function getDbTable()
+    {
+        if (null === $this->_dbTable) {
+            $this->setDbTable('Default_Model_DbTable_Guestbook');
+        }
+        return $this->_dbTable;
+    }
+
+    public function save(Default_Model_Guestbook $guestbook)
+    {
+        $data = array(
+            'email'   => $guestbook->getEmail(),
+            'comment' => $guestbook->getComment(),
+            'created' => date('Y-m-d H:i:s'),
+        );
+
+        if (null === ($id = $guestbook->getId())) {
+            unset($data['id']);
+            $this->getDbTable()->insert($data);
+        } else {
+            $this->getDbTable()->update($data, array('id = ?' => $id));
+        }
+    }
+
+    public function find($id, Default_Model_Guestbook $guestbook)
+    {
+        $result = $this->getDbTable()->find($id);
+        if (0 == count($result)) {
+            return;
+        }
+        $row = $result->current();
+        $guestbook->setId($row->id)
+                  ->setEmail($row->email)
+                  ->setComment($row->comment)
+                  ->setCreated($row->created);
+    }
+
+    public function fetchAll()
+    {
+        $resultSet = $this->getDbTable()->fetchAll();
+        $entries   = array();
+        foreach ($resultSet as $row) {
+            $entry = new Default_Model_Guestbook();
+            $entry->setId($row->id)
+                  ->setEmail($row->email)
+                  ->setComment($row->comment)
+                  ->setCreated($row->created)
+                  ->setMapper($this);
+            $entries[] = $entry;
+        }
+        return $entries;
+    }
+}
+]]></programlisting>
+
+    <para>
+        Now it's time to update our model class slightly, to accomodate the data mapper. Just like
+        the data mapper contains a reference to the data source, the model contains a reference to
+        the data mapper. Additionally, we'll make it easy to populate the model by passing an array
+        of data either to the constructor or a <methodname>setOptions()</methodname> method. The
+        final model class, located in <filename>application/models/Guestbook.php</filename>, looks
+        like this:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/models/Guestbook.php
+
+class Default_Model_Guestbook
+{
+    protected $_comment;
+    protected $_created;
+    protected $_email;
+    protected $_id;
+    protected $_mapper;
+
+    public function __construct(array $options = null)
+    {
+        if (is_array($options)) {
+            $this->setOptions($options);
+        }
+    }
+
+    public function __set($name, $value)
+    {
+        $method = 'set' . $name;
+        if (('mapper' == $name) || !method_exists($this, $method)) {
+            throw new Exception('Invalid guestbook property');
+        }
+        $this->$method($value);
+    }
+
+    public function __get($name)
+    {
+        $method = 'get' . $name;
+        if (('mapper' == $name) || !method_exists($this, $method)) {
+            throw new Exception('Invalid guestbook property');
+        }
+        return $this->$method();
+    }
+
+    public function setOptions(array $options)
+    {
+        $methods = get_class_methods($this);
+        foreach ($options as $key => $value) {
+            $method = 'set' . ucfirst($key);
+            if (in_array($method, $methods)) {
+                $this->$method($value);
+            }
+        }
+        return $this;
+    }
+
+    public function setComment($text)
+    {
+        $this->_comment = (string) $text;
+        return $this;
+    }
+
+    public function getComment()
+    {
+        return $this->_comment;
+    }
+
+    public function setEmail($email)
+    {
+        $this->_email = (string) $email;
+        return $this;
+    }
+
+    public function getEmail()
+    {
+        return $this->_email;
+    }
+
+    public function setCreated($ts)
+    {
+        $this->_created = $ts;
+        return $this;
+    }
+
+    public function getCreated()
+    {
+        return $this->_created;
+    }
+
+    public function setId($id)
+    {
+        $this->_id = (int) $id;
+        return $this;
+    }
+
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    public function setMapper($mapper)
+    {
+        $this->_mapper = $mapper;
+        return $this;
+    }
+
+    public function getMapper()
+    {
+        if (null === $this->_mapper) {
+            $this->setMapper(new Default_Model_GuestbookMapper());
+        }
+        return $this->_mapper;
+    }
+
+    public function save()
+    {
+        $this->getMapper()->save($this);
+    }
+
+    public function find($id)
+    {
+        $this->getMapper()->find($id, $this);
+        return $this;
+    }
+
+    public function fetchAll()
+    {
+        return $this->getMapper()->fetchAll();
+    }
+}
+]]></programlisting>
+
+    <para>
+        Lastly, to connect these elements all together, lets create a guestbook controller that will
+        both list the entries that are currently inside the database.
+    </para>
+
+    <para>
+        To create a new controller, open a terminal or DOS console, navigate to your project
+        directory, and enter the following:
+    </para>
+
+    <programlisting language="shell"><![CDATA[
+# Unix-like systems:
+% zf.sh create controller guestbook
+
+# DOS/Windows:
+C:> zf.bat create controller guestbook
+]]></programlisting>
+
+    <para>
+        This will create a new controller, <classname>GuestbookController</classname>, in
+        <filename>application/controllers/GuestbookController.php</filename>, with a single action
+        method, <methodname>indexAction()</methodname>.  It will also create a view script directory
+        for the controller, <filename>application/views/scripts/guestbook/</filename>, with a view
+        script for the index action.
+    </para>
+
+    <para>
+        We'll use the "index" action as a landing page to view all guestbook entries.
+    </para>
+
+    <para>
+        Now, let's flesh out the basic application logic. On a hit to
+        <methodname>indexAction()</methodname>, we'll display all guestbook entries. This would look
+        like the following:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+// application/controllers/GuestbookController.php
+
+class GuestbookController extends Zend_Controller_Action 
+{
+    public function indexAction()
+    {
+        $guestbook = new Default_Model_Guestbook();
+        $this->view->entries = $guestbook->fetchAll();
+    }
+}
+]]></programlisting>
+
+    <para>
+        And, of course, we need a view script to go along with that. Edit
+        <filename>application/views/scripts/guestbook/index.phtml</filename> to read as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/guestbook/index.phtml -->
+
+<p><a href="<?php echo $this->url(
+    array(
+        'controller' => 'guestbook',
+        'action'     => 'sign'
+    ), 
+    'default', 
+    true) ?>">Sign Our Guestbook</a></p>
+
+Guestbook Entries: <br />
+<dl>
+    <?php foreach ($this->entries as $entry): ?>
+    <dt><?php echo $this->escape($entry->email) ?></dt>
+    <dd><?php echo $this->escape($entry->comment) ?></dd>
+    <?php endforeach ?>
+</dl>
+]]></programlisting>
+
+    <note>
+        <title>Checkpoint</title>
+
+        <para>
+            Now browse to "http://localhost/guestbook". You should see the following in your
+            browser:
+        </para>
+
+        <para>
+            <inlinegraphic width="525" scale="100" align="center" valign="middle"
+                fileref="figures/learning.quickstart.create-model.png" format="PNG" />
+        </para>
+    </note>
+
+    <note>
+        <title>Using the data loader script</title>
+
+        <para>
+            The data loader script introduced in this section
+            (<filename>scripts/load.sqlite.php</filename>) can be used to create the database for
+            each environment you have defined, as well as to load it with sample data. Internally,
+            it utilizes <classname>Zend_Console_Getopt</classname>, which allows it to provide a
+            number of command line switches. If you pass the "-h" or "--help" switch, it will give
+            you the available options:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+Usage: load.sqlite.php [ options ]
+--withdata|-w         Load database with sample data
+--env|-e [  ]         Application environment for which to create database 
+                      (defaults to development)
+--help|-h             Help -- usage message)]]
+]]></programlisting>
+
+        <para>
+            The "-e" switch allows you to specify the value to use for the constant
+            <constant>APPLICATION_ENV</constant> -- which in turn allows you to create a SQLite
+            database for each environment you define.  Be sure to run the script for the environment
+            you choose for your application when deploying.
+        </para>
+    </note>
+</sect1>

+ 429 - 0
documentation/manual/en/tutorials/quickstart-create-project.xml

@@ -0,0 +1,429 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.create-project">
+    <title>Create Your Project</title>
+
+    <para>
+        In order to create your project, you must first download and extract Zend Framework.
+    </para>
+
+    <sect2 id="learning.quickstart.create-project.install-zf">
+        <title>Install Zend Framework</title>
+
+        <para>
+            The easiest way to get Zend Framework along with a complete PHP stack is by installing
+            <ulink url="http://www.zend.com/en/products/server-ce/downloads">Zend Server</ulink>.
+            Zend Server has native installers for Mac OSX, Windows, Fedora Core, and Ubuntu, as well
+            as a universal installation package compatible with most Linux distributions.
+        </para>
+
+        <para> 
+            After you have installed Zend Server, the Framework files may be found
+            under <filename>/Applications/ZendServer/share/ZendFramework</filename> on Mac
+            OSX, <filename>C:\Program Files\Zend\ZendServer\share\ZendFramework</filename> on
+            Windows, and <filename>/usr/local/zend/share/ZendFramework</filename> on Linux.
+            The <constant>include_path</constant> will already be configured to include
+            Zend Framework.
+        </para>
+
+        <para>
+            Alternately, you can <ulink url="http://framework.zend.com/download/latest">Download the
+            latest version of Zend Framework</ulink> and extract the contents; make a note of where
+            you have done so.
+        </para>
+
+        <para>
+            Optionally, you can add the path to the <filename>library/</filename> subdirectory of
+            the archive to your <filename>php.ini</filename>'s <constant>include_path</constant>
+            setting.
+        </para>
+
+        <para>
+            That's it! Zend Framework is now installed and ready to use.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.create-project">
+        <title>Create Your Project</title>
+
+        <note>
+            <title>zf Command Line Tool</title>
+
+            <para>
+                In your Zend Framework installation is a <filename>bin/</filename> subdirectory,
+                containing the scripts <filename>zf.sh</filename> and <filename>zf.bat</filename>
+                for Unix-based and Windows-based users, respectively. Make a note of the absolute
+                path to this script.
+            </para>
+
+            <para>
+                Wherever you see references to <filename>zf.sh</filename> or
+                <filename>zf.bat</filename>, please substitute the absolute path to the script. On
+                Unix-like systems, you may want to use your shell's alias functionality:
+                <command>alias zf.sh=path/to/ZendFramework/bin/zf.sh</command>.
+            </para>
+
+            <para>
+                If you have problems setting up the <command>zf</command> command-line tool, please
+                refer to <link linkend="zend.tool.framework.clitool.setup-general">the
+                    manual</link>.
+            </para>
+        </note>
+
+        <para>
+            Open a terminal (in Windows, <command>Start -> Run</command>, and then use
+            <command>cmd</command>). Navigate to a directory where you would like to start a
+            project. Then, use the path to the appropriate script, and execute one of the following:
+        </para>
+
+        <programlisting language="shell"><![CDATA[
+# Unix:
+% zf.sh create project quickstart
+
+# DOS/Windows:
+C:> zf.bat create project quickstart
+]]></programlisting>
+
+        <para>
+            Running this command will create your basic site structure, including your initial
+            controllers and views. The tree looks like the following:
+        </para>
+
+        <programlisting language="text"><![CDATA[
+quickstart
+|-- application
+|   |-- Bootstrap.php
+|   |-- configs
+|   |   `-- application.ini
+|   |-- controllers
+|   |   |-- ErrorController.php
+|   |   `-- IndexController.php
+|   |-- models
+|   `-- views
+|       |-- helpers
+|       `-- scripts
+|           |-- error
+|           |   `-- error.phtml
+|           `-- index
+|               `-- index.phtml
+|-- library
+|-- public
+|   `-- index.php
+`-- tests
+    |-- application
+    |   `-- bootstrap.php
+    |-- library
+    |   `-- bootstrap.php
+    `-- phpunit.xml
+]]></programlisting>
+
+        <para>
+            At this point, if you haven't added Zend Framework to your
+            <constant>include_path</constant>, we recommend either copying or symlinking it into
+            your <filename>library/</filename> directory. In either case, you'll want to either
+            recursively copy or symlink the <filename>library/Zend/</filename> directory of your
+            Zend Framework installation into the <filename>library/</filename> directory of your
+            project. On unix-like systems, that would look like one of the following:
+        </para>
+
+        <programlisting language="shell"><![CDATA[
+# Symlink:
+% cd library; ln -s path/to/ZendFramework/library/Zend .
+
+# Copy:
+% cd library; cp -r path/to/ZendFramework/library/Zend .
+]]></programlisting>
+
+        <para>
+            On Windows systems, it may be easiest to do this from the Explorer.
+        </para>
+
+        <para>
+            Now that the project is created, the main artifacts to begin understanding are the
+            bootstrap, configuration, action controllers, and views.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.bootstrap">
+        <title>The Bootstrap</title>
+
+        <para>
+            Your <classname>Bootstrap</classname> class defines what resources and components to
+            initialize. By default, Zend Framework's <link linkend="zend.controller.front">Front
+                Controller</link> is initialized, and it uses the
+            <filename>application/controllers/</filename> as the default directory in which to look
+            for action controllers (more on that later). The class looks like the following:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/Bootstrap.php
+
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+}
+]]></programlisting>
+
+        <para>
+            As you can see, not much is necessary to begin with.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.configuration">
+        <title>Configuration</title>
+
+        <para>
+            While Zend Framework is itself configurationless, you often need to configure your
+            application. The default configuration is placed in
+            <filename>application/configs/application.ini</filename>, and contains some basic
+            directives for setting your PHP environment (for instance, turning error reporting on
+            and off), indicating the path to your bootstrap class (as well as its class name), and
+            the path to your action controllers. It looks as follows:
+        </para>
+
+        <programlisting language="ini"><![CDATA[
+; application/configs/application.ini
+
+[production]
+phpSettings.display_startup_errors = 0
+phpSettings.display_errors = 0
+includePaths.library = APPLICATION_PATH "/../library"
+bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
+bootstrap.class = "Bootstrap"
+resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
+
+[staging : production]
+
+[testing : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+
+[development : production]
+phpSettings.display_startup_errors = 1
+phpSettings.display_errors = 1
+]]></programlisting>
+
+        <para>
+            Several things about this file should be noted. First, when using INI-style
+            configuration, you can reference constants directly and expand them;
+            <constant>APPLICATION_PATH</constant> is actually a constant. Additionally note that
+            there are several sections defined: production, staging, testing, and development. The
+            latter three inherit settings from the "production" environment. This is a useful way to
+            organize configuration to ensure that appropriate settings are available in each stage
+            of application development.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.action-controllers">
+        <title>Action Controllers</title>
+
+        <para>
+            Your application's <emphasis>action controllers</emphasis> contain your application
+            workflow, and do the work of mapping your requests to the appropriate models and views.
+        </para>
+
+        <para>
+            An action controller should have one or more methods ending in "Action"; these methods
+            may then be requested via the web. By default, Zend Framework URLs follow the schema
+            <constant>/controller/action</constant>, where "controller" maps to the action
+            controller name (minus the "Controller" suffix) and "action" maps to an action method
+            (minus the "Action" suffix).
+        </para>
+
+        <para>
+            Typically, you always need an <classname>IndexController</classname>, which is a
+            fallback controller and which also serves the home page of the site, and an
+            <classname>ErrorController</classname>, which is used to indicate things such as HTTP
+            404 errors (controller or action not found) and HTTP 500 errors (application errors).
+        </para>
+
+        <para>
+            The default <classname>IndexController</classname> is as follows:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/controllers/IndexController.php
+
+class IndexController extends Zend_Controller_Action
+{
+
+    public function init()
+    {
+        /* Initialize action controller here */
+    }
+
+    public function indexAction()
+    {
+        // action body
+    }
+}
+]]></programlisting>
+
+        <para>
+            And the default <classname>ErrorController</classname> is as follows:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// application/controllers/ErrorController.php
+
+class ErrorController extends Zend_Controller_Action
+{
+
+    public function errorAction()
+    {
+        $errors = $this->_getParam('error_handler');
+        
+        switch ($errors->type) { 
+            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
+            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
+        
+                // 404 error -- controller or action not found
+                $this->getResponse()->setHttpResponseCode(404);
+                $this->view->message = 'Page not found';
+                break;
+            default:
+                // application error 
+                $this->getResponse()->setHttpResponseCode(500);
+                $this->view->message = 'Application error';
+                break;
+        }
+        
+        $this->view->exception = $errors->exception;
+        $this->view->request   = $errors->request;
+    }
+}
+]]></programlisting>
+
+        <para>
+            You'll note that (1) the <classname>IndexController</classname> contains no real code,
+            and (2) the <classname>ErrorController</classname> makes reference to a "view" property.
+            That leads nicely into our next subject.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.views">
+        <title>Views</title>
+
+        <para>
+            Views in Zend Framework are written in plain old PHP. View scripts are placed in
+            <filename>application/views/scripts/</filename>, where they are further categorized
+            using the controller names. In our case, we have an
+            <classname>IndexController</classname> and an <classname>ErrorController</classname>,
+            and thus we have corresponding <filename>index/</filename> and
+            <filename>error/</filename> subdirectories within our view scripts directory. Within
+            these subdirectories, you will then find and create view scripts that correspond to each
+            controller action exposed; in the default case, we thus have the view scripts
+            <filename>index/index.phtml</filename> and <filename>error/error.phtml</filename>.
+        </para>
+
+        <para>
+            View scripts may contain any markup you want, and use the <code>&lt;?php</code> opening
+            tag and <code>?&gt;</code> closing tag to insert PHP directives.
+        </para>
+
+        <para>
+            The following is what we install by default for the
+            <filename>index/index.phtml</filename> view script:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/index/index.phtml -->
+<style>
+    
+    a:link,
+    a:visited
+    {
+        color: #0398CA;
+    }
+
+    span#zf-name
+    {
+        color: #91BE3F;
+    }
+
+    div#welcome
+    {
+        color: #FFFFFF;
+        background-image: url(http://framework.zend.com/images/bkg_header.jpg);
+        width:  600px;
+        height: 400px;
+        border: 2px solid #444444;
+        overflow: hidden;
+        text-align: center;
+    }
+    
+    div#more-information
+    {
+        background-image: url(http://framework.zend.com/images/bkg_body-bottom.gif);
+        height: 100%;
+    }
+
+</style>
+<div id="welcome">
+    <h1>Welcome to the <span id="zf-name">Zend Framework!</span><h1 />
+    <h3>This is your project's main page<h3 />
+    <div id="more-information">
+        <p>
+            <img src="http://framework.zend.com/images/PoweredBy_ZF_4LightBG.png" />
+        </p>
+
+        <p>
+            Helpful Links: <br />
+            <a href="http://framework.zend.com/">Zend Framework Website</a> |
+            <a href="http://framework.zend.com/manual/en/">Zend Framework 
+                Manual</a>
+        </p>
+    </div>
+</div>
+]]></programlisting>
+
+        <para>
+            The <filename>error/error.phtml</filename> view script is slightly more interesting as
+            it uses some PHP conditionals:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<!-- application/views/scripts/error/error.phtml -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"; 
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd> 
+<html xmlns="http://www.w3.org/1999/xhtml"> 
+<head>  
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+  <title>Zend Framework Default Application</title> 
+</head> 
+<body> 
+  <h1>An error occurred</h1> 
+  <h2><?php echo $this->message ?></h2> 
+
+  <?php if ('development' == $this->env): ?> 
+  
+  <h3>Exception information:</h3> 
+  <p> 
+      <b>Message:</b> <?php echo $this->exception->getMessage() ?> 
+  </p> 
+
+  <h3>Stack trace:</h3> 
+  <pre><?php echo $this->exception->getTraceAsString() ?> 
+  </pre> 
+
+  <h3>Request Parameters:</h3> 
+  <pre><?php echo var_export($this->request->getParams(), 1) ?> 
+  </pre> 
+  <?php endif ?>
+  
+</body> 
+</html>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.quickstart.create-project.checkpoint">
+        <title>Checkpoint</title>
+
+        <para>
+            At this point, you should be able to fire up your initial Zend Framework application.
+            Create a virtual host in your web server, and point its document root to your
+            application's <filename>public/</filename> subdirectory. Make sure your host's name is
+            in your DNS or hosts file, and then point your browser to it. You should be able to see
+            a welcome page at this point.
+        </para>
+    </sect2>
+</sect1>

+ 114 - 0
documentation/manual/en/tutorials/quickstart-intro-mvc.xml

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.quickstart.intro">
+    <title>ZF &amp; MVC Introduction</title>
+
+    <sect2 id="learning.quickstart.intro.zf">
+        <title>Zend Framework</title>
+
+        <para>
+            Zend Framework is an open source, object oriented web application framework for PHP 5.
+            ZF is often called a 'component library', because it has many loosely coupled components
+            that you can use more or less independently. But Zend Framework also provides an
+            advanced Model-View-Controller (MVC) implementation that can be used to establish a
+            basic structure for your ZF applications. A full list of Zend Framework components along
+            with short descriptions may be found in the <ulink
+                url="http://framework.zend.com/about/components">components overview</ulink>. This
+            QuickStart will introduce you to some of ZF's most commonly used components, including
+            <classname>Zend_Controller</classname>, <classname>Zend_Layout</classname>,
+            <classname>Zend_Config</classname>, <classname>Zend_Db</classname>,
+            <classname>Zend_Db_Table</classname>, <classname>Zend_Registry</classname>, along
+            with a few view helpers.
+        </para>
+
+        <para>
+            Using these components, we will build a simple database-driven guest book application
+            within minutes. The complete source code for this application is available in the
+            following archives:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    <ulink
+                        url="http://framework.zend.com/demos/ZendFrameworkQuickstart.zip">zip</ulink>
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    <ulink
+                        url="http://framework.zend.com/demos/ZendFrameworkQuickstart.tar.gz">tar.gz</ulink>
+                </para>
+            </listitem>
+        </itemizedlist>
+    </sect2>
+
+    <sect2 id="learning.quickstart.intro.mvc">
+        <title>Model-View-Controller</title>
+
+        <para>
+            So what exactly is this MVC pattern everyone keeps talking about, and why should you
+            care? MVC is much more than just a three-letter acronym (TLA) that you can whip out
+            anytime you want to sound smart; it has become something of a standard in the design of
+            modern web applications. And for good reason. Most web application code falls under one
+            of the following three categories: presentation, business logic, and data access. The
+            MVC pattern models this separation of concerns well. The end result is that your
+            presentation code can be consolidated in one part of your application with your business
+            logic in another and your data access code in yet another. Many developers have found
+            this well-defined separation indispensable for keeping their code organized, especially
+            when more than one developer is working on the same application.
+        </para>
+
+        <note>
+            <title>More Information</title>
+
+            <para>
+                Let's break down the pattern and take a look at the individual pieces:
+            </para>
+
+            <para>
+                <inlinegraphic width="321" scale="100" align="center" valign="middle"
+                    fileref="figures/learning.quickstart.intro.mvc.png" format="PNG" />
+            </para>
+
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        <emphasis role="strong">Model</emphasis> - This is the part of your
+                        application that defines its basic functionality behind a set of
+                        abstractions. Data access routines and some business logic can be defined in
+                        the model.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        <emphasis role="strong">View</emphasis> - Views define exactly what is
+                        presented to the user. Usually controllers pass data to each view to render
+                        in some format. Views will often collect data from the user, as well. This
+                        is where you're likely to find HTML markup in your MVC applications.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        <emphasis role="strong">Controller</emphasis> - Controllers bind the whole
+                        pattern together. They manipulate models, decide which view to display based
+                        on the user's request and other factors, pass along the data that each view
+                        will need, or hand off control to another controller entirely. Most MVC
+                        experts recommend <ulink
+                            url="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">keeping
+                        controllers as skinny as possible</ulink>.
+                    </para>
+                </listitem>
+            </itemizedlist>
+
+            <para>
+                Of course there is <ulink url="http://ootips.org/mvc-pattern.html">more to be
+                    said</ulink> about this critical pattern, but this should give you enough
+                background to understand the guestbook application we'll be building.
+            </para>
+        </note>
+    </sect2>
+</sect1>

+ 254 - 0
documentation/manual/en/tutorials/view-placeholders-basics.xml

@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.basics">
+    <title>Basic Placeholder Usage</title>
+
+    <para>
+        Zend Framework defines a generic <methodname>placeholder()</methodname> view helper that you
+        may use for as many custom placeholders you need. It also provides a variety of specific
+        placeholder implementations for often-needed functionality, such as specifying the
+        <acronym>DocType</acronym> declaration, document title, and more.
+    </para>
+
+    <para>
+        All placeholders operate in roughly the same way. They are containers, and thus allow you to
+        operate on them as collections. With them you can:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                <emphasis>Append</emphasis> or <emphasis>prepend</emphasis> items to the collection.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Replace</emphasis> the entire collection with a single value.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>prepend output</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>append output</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                Specify a string with which to <emphasis>separate items</emphasis> of the collection
+                when rendering.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Capture content</emphasis> into the collection.
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                <emphasis>Render</emphasis> the aggregated content.
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        Typically, you will call the helper with no arguments, which will return a container on
+        which you may operate. You will then either echo this container to render it, or call
+        methods on it to configure or populate it. If the container is empty, rendering it will
+        simply return an empty string; otherwise, the content will be aggregated according to the
+        rules by which you configure it.
+    </para>
+
+    <para>
+        As an example, let's create a sidebar that consists of a number of "blocks" of content.
+        You'll likely know up-front the structure of each block; let's assume for this example that
+        it might look like this:
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<div class="sidebar">
+    <div class="block">
+        <p>
+            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur aliquet
+            odio ac consectetur. Nulla quis eleifend tortor. Pellentesque varius, odio quis bibendum
+            consequat, diam lectus porttitor quam, et aliquet mauris orci eu augue.
+        </p>
+    </div>
+    <div class="block">
+        <ul>
+            <li><a href="/some/target">Link</a></li>
+            <li><a href="/some/target">Link</a></li>
+        </ul>
+    </div>
+</div>        
+]]></programlisting>
+
+    <para>
+        The content will vary based on the controller and action, but the structure will be the
+        same. Let's first setup the sidebar in a resource method of our bootstrap:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initSidebar()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+
+        $view->placeholder('sidebar')
+             // "prefix" -> markup to emit once, before all items in collection
+             ->setPrefix("<div class=\"sidebar\">\n    <div class=\"block\">\n")
+             // "separator" -> markup to emit between items in a collection
+             ->setSeparator("</div>\n    <div class=\"block\">\n")
+             // "postfix" -> markup to emit once, after all items in a collection
+             ->setPostfix("</div>\n</div>");
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        The above defines a placeholder, "sidebar", that has no items. It configures the basic
+        markup structure of that placeholder, however, per our requirements.
+    </para>
+
+    <para>
+        Now, let's assume for the "user" controller that for all actions we'll want a block at the
+        top containing some information. We could accomplish this in two ways: (a) we could add the
+        content to the placeholder directly in the controller's
+        <methodname>preDispatch()</methodname> method, or (b) we could render a view script from
+        within the <methodname>preDispatch()</methodname> method. We'll use (b), as it follows a
+        more proper separation of concerns (leaving view-related logic and functionality within a
+        view script).
+    </para>
+
+    <para>
+        We'll name the view script "user/_sidebar.phtml", and populate it as follows:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<?php $this->placeholder('sidebar')->captureStart() ?>
+<h4>User Administration</h4>
+<ul>
+    <li><a href="<?php $this->url(array('action' => 'list')) ?>">
+        List</a></li>
+    <li><a href="<?php $this->url(array('action' => 'create')) ?>">
+        Create</a></a></li>
+</ul>
+<?php $this->placeholder('sidebar')->captureEnd() ?>
+]]></programlisting>
+
+    <para>
+        The above example makes use of the content capturing feature of placeholders. By default,
+        content is appended as a new item in the container, allowing us to aggregate content. This
+        example makes use of view helpers and static HTML in order to generate markup, and the
+        content is then captured and appended into the placeholder itself.
+    </para>
+
+    <para>
+        To invoke the above view script, we would write the following in our
+        <methodname>preDispatch()</methodname> method:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+class UserController extends Zend_Controller_Action
+{
+    // ...
+
+    public function preDispatch()
+    {
+        // ...
+
+        $this->view->render('user/_sidebar.phtml');
+
+        // ...
+    }
+
+    // ...
+}
+]]></programlisting>
+
+    <para>
+        Note that we're not capturing the rendered value; there's no need, as the entierty of that
+        view is being captured into a placeholder.
+    </para>
+
+    <para>
+        Now, let's assume our "view" action in that same controller needs to present some
+        information. Within the "user/view.phtml" view script, we might have the following snippet
+        of content:
+    </para>
+
+    <programlisting language="php"><![CDATA[
+$this->placeholder('sidebar')
+     ->append('<p>User: ' . $this->escape($this->username) .  '</p>');
+]]></programlisting>
+
+    <para>
+        This example makes use of the <methodname>append()</methodname> method, and passes it some
+        simple markup to aggregate.
+    </para>
+
+    <para>
+        Finally, let's modify our layout view script, and have it render the placeholder.
+    </para>
+
+    <programlisting language="php"><![CDATA[
+<html>
+<head>
+    <title>My Site</title>
+</head>
+<body>
+    <div class="content">
+        <?php echo $this->layout()->content ?>
+    </div>
+    <?php echo $this->placeholder('sidebar') ?>
+</body>
+</html>
+]]></programlisting>
+
+    <para>
+        For controllers and actions that do not populate the "sidebar" placeholder, no content will
+        be rendered; for those that do, however, echoing the placeholder will render the content
+        according to the rules we created in our bootstrap, and the content we aggregated throughout
+        the application. In the case of the "/user/view" action, and assuming a username of
+        "matthew", we would get content for the sidebar as follows (formatted for readability):
+    </para>
+
+    <programlisting language="html"><![CDATA[
+<div class="sidebar">
+    <div class="block">
+        <h4>User Administration</h4>
+        <ul>
+            <li><a href="/user/list">List</a></li>
+            <li><a href="/user/create">Create</a></a></li>
+        </ul>
+    </div>
+    <div class="block">
+        <p>User: matthew</p>
+    </div>
+</div>        
+]]></programlisting>
+
+    <para>
+        There are a large number of things you can do by combining placeholders and layout scripts;
+        experiment with them, and read the <link
+            linkend="zend.view.helpers.initial.placeholder">relevant manual sections</link> for more
+        information.
+    </para>
+</sect1>

+ 16 - 0
documentation/manual/en/tutorials/view-placeholders-conclusion.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.conclusion">
+    <title>View Placeholders: Conclusion</title>
+
+    <para>
+        View placeholders are a simple and powerful method for creating rich layouts for your
+        application. You can use a variety of standard placeholders, such as those discussed
+        (<methodname>doctype()</methodname>, <methodname>headTitle()</methodname>,
+        <methodname>headLink()</methodname>, and <methodname>headScript()</methodname>), or use
+        the generic <methodname>placeholder()</methodname> helper to aggregate content and render it
+        in custom ways. Experiment with their exposed functionality, and visit the appropriate
+        sections in the reference guide to find out about the additional features they offer -- and
+        how you may leverage those features to create rich content for your readers.
+    </para>
+</sect1>

+ 41 - 0
documentation/manual/en/tutorials/view-placeholders-intro.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.intro">
+    <title>Introduction</title>
+
+    <para>
+        In <link linkend="learning.layout">the previous chapter</link>, we looked at primarily the
+        Two Step View pattern, which allows you to embed individual application views within a
+        sitewide layout. At the end of that chapter, however, we discussed some limitations:
+    </para>
+
+    <itemizedlist>
+        <listitem>
+            <para>
+                How do you alter the page title?
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                How would you inject conditional scripts or stylesheets into the sitewide layout?
+            </para>
+        </listitem>
+
+        <listitem>
+            <para>
+                How would you create and render an optional sidebar? What if there was some content
+                that was unconditional, and other content that was conditional for the sidebar?
+            </para>
+        </listitem>
+    </itemizedlist>
+
+    <para>
+        These questions are addressed in the <ulink
+            url="http://java.sun.com/blueprints/corej2eepatterns/Patterns/CompositeView.html">Composite
+        View</ulink> design pattern. One approach to that pattern is to provide "hints" or content
+        to the sitewide layout. In Zend Framework, this is achieved through specialized view helpers
+        called "placeholders." Placeholders allow you to aggregate content, and then render that
+        aggregate content elsewhere.
+    </para>
+</sect1>

+ 460 - 0
documentation/manual/en/tutorials/view-placeholders-standard.xml

@@ -0,0 +1,460 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Reviewed: no -->
+<sect1 id="learning.view.placeholders.standard">
+    <title>Standard Placeholders</title>
+
+    <para>
+        In the <link linkend="learning.view.placeholders.basics">previous section</link>, we learned
+        about the <methodname>placeholder()</methodname> view helper, and how it can be used to
+        aggregate custom content. In this section, we'll look at some of the concrete placeholders
+        shipped with Zend Framework, and how you can use them to your advantage when creating
+        complex composite layouts.
+    </para>
+
+    <para>
+        Most of the shipped placeholders are for generating content for the
+        <code>&lt;head&gt;</code> section of your layout content -- an area you typically cannot
+        manipulate directly via your application view scripts, but one you may want to influence. As
+        examples: you may want your title to contain certain content on every page, but specific
+        content based on the controller and/or action; you may want to specify
+        <acronym>CSS</acronym> files to load based on what section of the application you're in; you
+        may need specific JavaScript scripts loaded at different times; or you may want to set the
+        <acronym>DocType</acronym> declaration.
+    </para>
+
+    <para>
+        Zend Framework ships with placeholder implementations for each of these situations, and
+        several more.
+    </para>
+
+    <sect2 id="learning.view.placeholders.standard.doctype">
+        <title>Setting the DocType</title>
+
+        <para>
+            <acronym>DocType</acronym> declarations are troublesome to memorize, and often essential
+            to include in your document to ensure the browser properly renders your content. The
+            <methodname>doctype()</methodname> view helper allows you to use simple string mnemonics
+            to specify the desired <acronym>DocType</acronym>; additionally, other helpers will
+            query the <methodname>doctype()</methodname> helper to ensure the output generated
+            conforms with the requested <acronym>DocType</acronym>.
+        </para>
+
+        <para>
+            As an example, if you want to use the <acronym>XHTML1</acronym> Strict
+            <acronym>DTD</acronym>, you can simply specify:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$this->doctype('XHTML1_STRICT');
+]]></programlisting>
+
+        <para>
+            Among the other available mnemonics, you'll find these common types:
+        </para>
+
+        <variablelist>
+            <varlistentry>
+                <term>XHTML1_STRICT</term>
+
+                <listitem>
+                    <para>
+                        <acronym>XHTML</acronym> 1.0 Strict
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>XHTML1_TRANSITIONAL</term>
+
+                <listitem>
+                    <para>
+                        <acronym>XHTML</acronym> 1.0 Transitional
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML4_STRICT</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 4.01 Strict
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML4_Loose</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 4.01 Loose
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>HTML5</term>
+
+                <listitem>
+                    <para>
+                        <acronym>HTML</acronym> 5
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+
+        <para>
+            You can assign the type and render the declaration in a single call:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+echo $this->doctype('XHTML1_STRICT');
+]]></programlisting>
+
+        <para>
+            However, the better approach is to assign the type in your bootstrap, and then render it
+            in your layout. Try adding the following to your bootstrap class:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    protected function _initDocType()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+    }
+}
+]]></programlisting>
+
+        <para>
+            Then, in your layout script, simply <functionname>echo</functionname> the helper at the
+            top of the file:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will ensure that your DocType-aware view helpers render the appropriate markup, 
+            ensure that the type is set well before the layout is rendered, and provide a single
+            location to change the DocType.
+        </para>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-title">
+        <title>Specifying the Page Title</title>
+
+        <para>
+            Often, a site will include the site or business name as  part of the page title, and
+            then add additional information based on the location within the site. As an example,
+            the zend.com website includes the string "Zend.com" on all pages, and the prepends
+            information based on the page: "Zend Server - Zend.com". Within Zend Framework, the
+            <methodname>headTitle()</methodname> view helper can help simplify this task.
+        </para>
+
+        <para>
+            At its simplest, the <methodname>headTitle()</methodname> helper allows you to aggregate
+            content for the <code>&lt;title&gt;</code> tag; when you echo it, it then assembles it
+            based on the order in which segments are added. You can control the order using
+            <methodname>prepend()</methodname> and <methodname>append()</methodname>, and provide a
+            separator to use between segments using the <methodname>setSeparator()</methodname>
+            method.
+        </para>
+
+        <para>
+            Typically, you should specify any segments common to all pages in your bootstrap,
+            similar to how we define the doctype. In this case, we'll define a
+            <methodname>_initPlaceholders()</methodname> method for operating on all the various
+            placeholders, and specify an initial title as well as a separator.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Within a view script, we might want to add another segment:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headTitle()->append('Some Page'); // place after other segments ?>
+<?php $this->headTitle()->prepend('Some Page'); // place before ?>
+]]></programlisting>
+
+        <para>
+            In our layout, we will simply echo the <methodname>headTitle()</methodname> helper:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<!-- If append() was used: -->
+<title>My Site :: Some Page</title>
+
+<!-- If prepend() was used: -->
+<title>Some Page :: My Site</title>
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-link">
+        <title>Specifying Stylesheets with HeadLink</title>
+
+        <para>
+            Good CSS developers will often create a general stylesheet for sitewide styles, and
+            individual stylesheets for specific sections or pages of the website, and load these
+            latter conditionally so as to decrease the amount of data needing to be transferred on
+            each request. The <methodname>headLink()</methodname> placeholder makes such conditional
+            aggregation of stylesheets trivial within your application.
+        </para>
+
+        <para>
+            To accomplish this, <methodname>headLink()</methodname> defines a number of "virtual"
+            methods (via overloading) to make the process trivial. The ones we will be concerned
+            with are <methodname>appendStylesheet()</methodname> and
+            <methodname>prependStylesheet()</methodname>. Each takes up to four arguments,
+            <varname>$href</varname> (the relative path to the stylesheet),
+            <varname>$media</varname> (the MIME type, which defaults to "text/css"),
+            <varname>$conditionalStylesheet</varname> (which can be used to specify a "condition"
+            under which the stylesheet will be evaluated), and <varname>$extras</varname> (an
+            associative array of key/value pairs, commonly used to specify a key for "media"). In
+            most cases, you will only need to specify the first argument, the relative path to the
+            stylesheet.
+        </para>
+
+        <para>
+            In our example, we'll assume that all pages need to load the stylesheet located in
+            "/styles/site.css" (relative to the document root); we'll specify this in our
+            <methodname>_initPlaceholders()</methodname> bootstrap method.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+
+        // Set the initial stylesheet:
+        $view->headLink()->prependStylesheet('/styles/site.css');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Later, in a controller or action-specific view script, we can add more stylesheets:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headLink()->appendStylesheet('/styles/user-list.css') ?>
+]]></programlisting>
+
+        <para>
+            Within our layout view script, once again, we simply echo the placeholder:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <?php echo $this->headLink() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<link rel="stylesheet" type="text/css" href="/styles/site.css" />
+<link rel="stylesheet" type="text/css" href="/styles/user-list.css" />
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="learning.view.placeholders.standard.head-script">
+        <title>Aggregating Scripts Using HeadScript</title>
+
+        <para>
+            Another common tactic to prevent long page load times is to only load JavaScript when
+            necessary. That said, you may need several layers of scripts: perhaps one for
+            progressively enhancing menus on the site, and another for page-specific content. In
+            these situations, the <methodname>headScript()</methodname> helper presents a solution.
+        </para>
+
+        <para>
+            Similar to the <methodname>headLink()</methodname> helper,
+            <methodname>headScript()</methodname> provides the ability to append or prepend scripts
+            to the collection, and then echo the entire set. It provides the flexibility to specify
+            either script files themselves to load, or explicit JavaScript. You also have the option
+            of capturing JavaScript via
+            <methodname>captureStart()</methodname>/<methodname>captureEnd()</methodname>, which
+            allows you to simply inline the JavaScript instead of requiring an additional call to
+            your server.
+        </para>
+
+        <para>
+            Also like <methodname>headLink()</methodname>, <methodname>headScript</methodname>
+            provides "virtual" methods via overloading as a convenience when specifying items to
+            aggregate; common methods include <methodname>prependFile()</methodname>,
+            <methodname>appendFile()</methodname>, <methodname>prependScript()</methodname>, and
+            <methodname>appendScript()</methodname>. The first two allow you to specify files that
+            will be referenced in a <code>&lt;script&gt;</code> tag's <varname>src</varname>
+            attribute; the latter two will take the content provided and render it as literal
+            JavaScript within a <code>&lt;script&gt;</code> tag.
+        </para>
+
+        <para>
+            In this example, we'll specify that a script, "/js/site.js" needs to be loaded on every
+            page; we'll update our <methodname>_initPlaceholders()</methodname> bootstrap method to
+            do this.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
+{
+    // ...
+
+    protected function _initPlaceholders()
+    {
+        $this->bootstrap('View');
+        $view = $this->getResource('View');
+        $view->doctype('XHTML1_STRICT');
+
+        // Set the initial title and separator:
+        $view->headTitle('My Site')
+             ->setSeparator(' :: ');
+
+        // Set the initial stylesheet:
+        $view->headLink()->prependStylesheet('/styles/site.css');
+
+        // Set the initial JS to load:
+        $view->headScript()->prependFile('/js/site.js');
+    }
+
+    // ...
+}
+]]></programlisting>
+
+        <para>
+            Within a view script, we might then add an extra script file to source, or capture some
+            JavaScript to include in our document.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php $this->headScript()->appendFile('/js/user-list.js') ?>
+<?php $this->headScript()->captureStart() ?>
+site = {
+    baseUrl: "<?php echo $this->baseUrl() ?>"
+};
+<?php $this->headScript()->captureEnd() ?>
+]]></programlisting>
+
+        <para>
+            Within our layout script, we then simply echo the placeholder, just as we have all the
+            others:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+<?php echo $this->doctype() ?>
+<html>
+    <?php echo $this->headTitle() ?> 
+    <?php echo $this->headLink() ?> 
+    <?php echo $this->headScript() ?> 
+    <!-- ... -->
+]]></programlisting>
+
+        <para>
+            This will generate the following output:
+        </para>
+
+        <programlisting language="html"><![CDATA[
+<script type="text/javascript" src="/js/site.js"></script>
+<script type="text/javascript" src="/js/user-list.js"></script>
+<script type="text/javascript">
+site = {
+    baseUrl: "<?php echo $this->baseUrl() ?>"
+};
+</script>
+]]></programlisting>
+
+        <note>
+            <title>InlineScript Variant</title>
+
+            <para>
+                Many browsers will often block display of a page until all scripts and stylesheets
+                referenced in the <code>&lt;head&gt;</code> section have loaded. If you have a
+                number of such directives, this can impact how soon somebody can start actually
+                viewing the page.
+            </para>
+
+            <para>
+                One way around this is to emit your <code>&lt;script&gt;</code> tags just prior to
+                closing the <code>&lt;body&gt;</code> of your document. (This is a practice
+                specifically recommend by the <ulink
+                    url="http://developer.yahoo.com/yslow/">Y! Slow project</ulink>.)
+            </para>
+
+            <para>
+                Zend Framework supports this in two different ways:
+            </para>
+
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        You can render your <methodname>headScript()</methodname> tag whereever you
+                        like in your layout script; just because the title references "head" does
+                        not mean it needs to be rendered in that location.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        Alternately, you may use the <methodname>inlineScript()</methodname> helper,
+                        which is simply a variant on <methodname>headScript()</methodname>, and
+                        retains the same behavior, but uses a separate registry.
+                    </para>
+                </listitem>
+            </itemizedlist>
+        </note>
+    </sect2>
+</sect1>