1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.tool.extending">
4 <title>Extending Zend_Tool</title>
6 <sect2 id="zend.tool.extending.overview">
7 <title>Overview of Zend_Tool</title>
10 <classname>Zend_Tool_Framework</classname> is a framework for exposing common
11 functionalities such as the creation of project scaffolds, code
12 generation, search index generation, and much more. Functionality may be
13 written and exposed via <acronym>PHP</acronym> classes dropped into the
14 <acronym>PHP</acronym> <property>include_path</property>, providing incredible
15 flexibility of implementation. The functionality may then be consumed by writing
16 implementation and/or protocol-specific clients -- such as console
17 clients, <acronym>XML-RPC</acronym>, <acronym>SOAP</acronym>, and much more.
21 <classname>Zend_Tool_Project</classname> builds on and extends the capabilities of
22 <classname>Zend_Tool_Framework</classname> to that of managing a "project". In general,
23 a "project" is a planned endeavor or an initiative. In the computer world, projects
24 generally are a collection of resources. These resources can be files, directories,
25 databases, schemas, images, styles, and more.
29 <sect2 id="zend.tool.extending.zend-tool-framework">
30 <title>Zend_Tool_Framework Extensions</title>
32 <sect3 id="zend.tool.extending.zend-tool-framework.architecture">
33 <title>Overall Architecture</title>
36 <classname>Zend_Tool_Framework</classname> provides the following:
42 <emphasis>Common interfaces and abstracts</emphasis> that allow
43 developers to create functionality and capabilities that are
44 dispatchable by tooling clients.
50 <emphasis>Base client functionality</emphasis> and a concrete
51 console implementation that connect external tools and
52 interfaces to the <classname>Zend_Tool_Framework</classname>. The Console
53 client may be used in <acronym>CLI</acronym> environments such as unix
54 shells and the Windows console.
60 <emphasis>"Provider" and "Manifest" interfaces</emphasis> that
61 can be utilized by the tooling system. "Providers" represent the
62 functional aspect of the framework, and define the actions that
63 tooling clients may call. "Manifests" act as metadata registries
64 that provide additional context for the various defined
71 <emphasis>An introspective loading system</emphasis> that will
72 scan the environment for providers and determine what is
73 required to dispatch them.
79 <emphasis>A standard set of system providers</emphasis> that
80 allow the system to report what the full capabilities of the
81 system are as well as provide useful feedback. This also
82 includes a comprehensive "Help System".
88 Definitions that you should be aware of through this manual with respect
89 to <classname>Zend_Tool_Framework</classname> include:
95 <classname>Zend_Tool_Framework</classname> - The framework which exposes
102 <emphasis>Tooling Client</emphasis> - A developer tool that connects
103 to and consumes <classname>Zend_Tool_Framework</classname>.
109 <emphasis>Client</emphasis> - The subsystem of
110 <classname>Zend_Tool_Framework</classname> that exposes an interface such
111 that tooling clients can connect, query and execute commands.
117 <emphasis>Console Client / Command Line Interface /
118 <filename>zf.php</filename></emphasis> - The tooling client for the command
124 <emphasis>Provider</emphasis> - A subsystem and a collection of
125 built-in functionality that the framework exports.
131 <emphasis>Manifest</emphasis> - A subsystem for defining,
132 organizing, and disseminating provider requirement data.
138 <classname>Zend_Tool_Project</classname> Provider - A set of providers
139 specifically for creating and maintaining Zend Framework-based
146 <sect3 id="zend.tool.extending.zend-tool-framework.cli-client">
147 <title>Understanding the CLI Client</title>
150 The <acronym>CLI</acronym>, or command line tool (internally known as the console
151 tool), is currently the primary interface for dispatching
152 <classname>Zend_Tool</classname> requests. With the <acronym>CLI</acronym> tool,
153 developers can issue tooling requests inside the "command line windows", also
154 commonly known as a "terminal" window. This environment is predominant in the *nix
155 environment, but also has a common implementation in windows with the
156 <filename>cmd.exe</filename>, console2 and also with the Cygwin project.
159 <sect4 id="zend.tool.extending.zend-tool-framework.cli-client.setup-general">
160 <title>Setting up the CLI tool</title>
163 To issue tooling requests via the command line client, you first
164 need to set up the client so that your system can handle the "zf"
165 command. The command line client, for all intents and purposes, is
166 the <filename>.sh</filename> or <filename>.bat</filename> file that is provided
167 with your Zend Framework distribution. In trunk, it can be found here:
169 url="http://framework.zend.com/svn/framework/standard/trunk/bin/">http://framework.zend.com/svn/framework/standard/trunk/bin/</ulink>.
173 As you can see, there are 3 files in the <filename>/bin/</filename>
174 directory: a <filename>zf.php</filename>, <filename>zf.sh</filename>, and
175 <filename>zf.bat</filename>. The <filename>zf.sh</filename> and the
176 <filename>zf.bat</filename> are the operating system specific client
177 wrappers: <filename>zf.sh</filename> for the *nix environment, and
178 <filename>zf.bat</filename> for the Win32 environment. These client wrappers are
179 responsible for finding the proper <filename>php.exe</filename>, finding the
180 <filename>zf.php</filename>, and passing on the client request. The
181 <filename>zf.php</filename> is the responsible for handling understanding
182 your environment, constructing the proper include_path, and passing
183 what is provided on the command line to the proper library component
188 Ultimately, you want to ensure two things to make everything work
189 regardless of the operating system you are on:
195 <filename>zf.sh/zf.bat</filename> is reachable from your system
196 path. This is the ability to call <command>zf</command> from
197 anywhere on your command line, regardless of what your
198 current working directory is.
204 <filename>ZendFramework/library</filename> is in your
205 <property>include_path</property>.
212 Note: while the above are the most ideal
213 requirements, you can simply download Zend Framework and expect it
214 to work as <filename>./path/to/zf.php</filename> some command.
219 <sect4 id="zend.tool.extending.zend-tool-framework.cli-client.setup-starnix">
220 <title>Setting up the CLI tool on Unix-like Systems</title>
223 The most common setup in the *nix environment, is to copy the
224 <filename>zf.sh</filename> and <filename>zf.php</filename> into the same
225 directory as your <acronym>PHP</acronym> binary. This can generally be found in
226 one of the following places:
229 <programlisting language="text"><![CDATA[
232 /usr/local/ZendServer/bin/
233 /Applications/ZendServer/bin/
237 To find out the location of your <acronym>PHP</acronym> binary, you can execute
238 'which php' on the command line. This will return the location of the
239 <acronym>PHP</acronym> binary you will be using to run <acronym>PHP</acronym>
240 scripts in this environment.
244 The next order of business is to ensure that Zend Framework
245 library is set up correctly inside of the system <acronym>PHP</acronym>
246 <property>include_path</property>. To find out where your
247 <property>include_path</property> is located, you can execute
248 <command>php -i</command> and look for the <property>include_path</property>
249 variable, or more succinctly, execute
250 <command>php -i | grep include_path</command>. Once you have found where
251 your <property>include_path</property> is located (this will generally be
252 something like <filename>/usr/lib/php</filename>,
253 <filename>/usr/share/php</filename>, <filename>/usr/local/lib/php</filename>, or
254 similar), ensure that the contents of the <filename>/library/</filename>
255 directory are put inside your <property>include_path</property> specified
260 Once you have done those two things, you should be able to issue a
261 command and get back the proper response like this:
265 <inlinegraphic scale="100" align="center" valign="middle"
266 fileref="figures/zend.tool.framework.cliversionunix.png" format="PNG" />
270 If you do not see this type of output, go back and check your setup
271 to ensure you have all of the necessary pieces in the proper place.
275 There are a couple of alternative setups you might want to employ
276 depending on your servers configuration, your level of access, or
281 <emphasis>Alternative Setup</emphasis> involves keeping the Zend
282 Framework download together as is, and creating a link from a
283 <constant>PATH</constant> location to the <filename>zf.sh</filename>. What this
284 means is you can place the contents of the ZendFramework download into a
285 location such as <filename>/usr/local/share/ZendFramework</filename>, or more
286 locally like <filename>/home/username/lib/ZendFramework</filename>, and creating
287 a symbolic link to the <filename>zf.sh</filename>.
291 Assuming you want to put the link inside <filename>/usr/local/bin</filename>
292 (this could also work for placing the link inside
293 <filename>/home/username/bin/</filename> for example) you would issue a
294 command similar to this:
297 <programlisting language="sh"><![CDATA[
298 ln -s /usr/local/share/ZendFramework/bin/zf.sh /usr/local/bin/zf
301 ln -s /home/username/lib/ZendFramework/bin/zf.sh /home/username/bin/zf
305 This will create a link which you should be able to access globally
310 <sect4 id="zend.tool.extending.zend-tool-framework.cli-client.setup-windows">
311 <title>Setting up the CLI tool on Windows</title>
314 The most common setup in the Windows Win32 environment, is to copy
315 the <filename>zf.bat</filename> and <filename>zf.php</filename> into the same
316 directory as your <acronym>PHP</acronym> binary. This can generally be found in
317 one of the following places:
320 <programlisting language="text"><![CDATA[
322 C:\Program Files\ZendServer\bin\
327 You should be able to run <filename>php.exe</filename> on the command line.
328 If you are not able to, first check the documentation that came with
329 your <acronym>PHP</acronym> distribution, or ensure that the path to
330 <filename>php.exe</filename> is in your
331 Windows <constant>PATH</constant> environment variable.
335 The next order of business is to ensure that Zend Framework
336 library is set up correctly inside of the system <acronym>PHP</acronym>
337 <property>include_path</property>. To find out where your
338 <property>include_path</property> is located, you can type
339 <command>php -i</command> and look for the <property>include_path</property>
340 variable, or more succinctly execute
341 <command>php -i | grep include_path</command> if you have Cygwin setup with
342 grep available. Once you have found where your
343 <property>include_path</property> is located (this will generally be
344 something like <filename>C:\PHP\pear</filename>,
345 <filename>C:\PHP\share</filename>,
346 <filename>C:\Program%20Files\ZendServer\share</filename> or similar), ensure
347 that the contents of the library/ directory are put inside your
348 <property>include_path</property> specified directory.
352 Once you have done those two things, you should be able to issue a
353 command and get back the proper response like this:
357 <inlinegraphic scale="100" align="center" valign="middle"
358 fileref="figures/zend.tool.framework.cliversionwin32.png" format="PNG" />
362 If you do not see this type of output, go back and check your setup
363 to ensure you have all of the necessary pieces in the proper place.
367 There are a couple of alternative setups you might want to employ
368 depending on your server's configuration, your level of access, or
373 <emphasis>Alternative Setup</emphasis> involves keeping the Zend
374 Framework download together as is, and altering both your system
375 <constant>PATH</constant> as well as the <filename>php.ini</filename> file.
376 In your user's environment, make sure to add
377 <filename>C:\Path\To\ZendFramework\bin</filename>, so that your
378 <filename>zf.bat</filename> file is executable. Also, alter the
379 <filename>php.ini</filename> file to ensure that
380 <filename>C:\Path\To\ZendFramework\library</filename> is in your
381 <property>include_path</property>.
385 <sect4 id="zend.tool.extending.zend-tool-framework.cli-client.setup-othernotes">
386 <title>Other Setup Considerations</title>
389 If for some reason you do not want Zend Framework library inside
390 your <property>include_path</property>, there is another option. There are
391 two special environment variables that <filename>zf.php</filename> will
392 utilize to determine the location of your Zend Framework
397 The first is <constant>ZEND_TOOL_INCLUDE_PATH_PREPEND</constant>, which will
398 prepend the value of this environment variable to the system
399 (<filename>php.ini</filename>) <property>include_path</property> before loading
404 Alternatively, you might want to use
405 <constant>ZEND_TOOL_INCLUDE_PATH</constant> to completely
406 <emphasis>replace</emphasis> the system <property>include_path</property>
407 for one that makes sense specifically for the <command>zf</command>
413 <sect3 id="zend.tool.extending.zend-tool-framework.providers-and-manifests">
414 <title>Creating Providers</title>
417 In general, a provider, on its own, is nothing more than the shell for a
418 developer to bundle up some capabilities they wish to dispatch with the
419 command line (or other) clients. It is an analogue to what a
420 "controller" is inside of your <acronym>MVC</acronym> application.
423 <sect4 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.loading">
424 <title>How Zend_Tool finds your Providers</title>
427 By default <classname>Zend_Tool</classname> uses the BasicLoader to find all
428 the providers that you can run. It recursivly iterates all
429 include path directories and opens all files that end
430 with "Manifest.php" or "Provider.php". All classes in these
431 files are inspected if they implement either
432 <classname>Zend_Tool_Framework_Provider_Interface</classname>
433 or <classname>Zend_Tool_Framework_Manifest_ProviderManifestable</classname>.
434 Instances of the provider interface make up for the real functionality
435 and all their public methods are accessible as provider actions.
436 The ProviderManifestable interface however requires the implementation of a
437 method <methodname>getProviders()</methodname> which returns an array of
438 instantiated provider interface instances.
442 The following naming rules apply on how you can access the providers
443 that were found by the IncludePathLoader:
449 The last part of your classname split by underscore is used
450 for the provider name, e.g. "My_Provider_Hello" leads to your
451 provider being accessible by the name "hello".
457 If your provider has a method <methodname>getName()</methodname>
458 it will be used instead of the previous method to determine
465 If your provider has "Provider" as prefix, e.g. it is called
466 <classname>My_HelloProvider</classname> it will be stripped
467 from the name so that the provider will be called "hello".
473 <para>The IncludePathLoader does not follow symlinks, that means
474 you cannot link provider functionality into your include paths,
475 they have to be physically present in the include paths.</para>
479 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.loading.example">
480 <title>Exposing Your Providers with a Manifest</title>
483 You can expose your providers to <classname>Zend_Tool</classname> by
484 offering a manifest with a special filename ending with "Manifest.php".
485 A Provider Manifest is an implementation of the
486 <interface>Zend_Tool_Framework_Manifest_ProviderManifestable</interface>
487 and requires the <methodname>getProviders()</methodname> method to return
488 an array of instantiated providers. In anticipation of our first
489 own provider <classname>My_Component_HelloProvider</classname>
490 we will create the following manifest:
493 <programlisting language="php"><![CDATA[
494 class My_Component_Manifest
495 implements Zend_Tool_Framework_Manifest_ProviderManifestable
497 public function getProviders()
500 new My_Component_HelloProvider()
508 <sect4 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.basic">
509 <title>Basic Instructions for Creating Providers</title>
512 As an example, if a developer wants to add the capability of showing
513 the version of a datafile that his 3rd party component is working
514 from, there is only one class the developer would need to implement.
515 Assuming the component is called <classname>My_Component</classname>, he would
516 create a class named <classname>My_Component_HelloProvider</classname> in a
517 file named <filename>HelloProvider.php</filename> somewhere on the
518 <property>include_path</property>. This class would implement
519 <classname>Zend_Tool_Framework_Provider_Interface</classname>, and the body of
520 this file would only have to look like the following:
523 <programlisting language="php"><![CDATA[
524 class My_Component_HelloProvider
525 implements Zend_Tool_Framework_Provider_Interface
527 public function say()
529 echo 'Hello from my provider!';
535 Given that code above, and assuming the developer wishes to access
536 this functionality through the console client, the call would look
540 <programlisting language="sh"><![CDATA[
542 Hello from my provider!
546 <sect4 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.response">
547 <title>The response object</title>
550 As discussed in the architecture section <classname>Zend_Tool</classname> allows
551 to hook different clients for using your <classname>Zend_Tool</classname>
552 providers. To keep compliant with different clients you should use the response
553 object to return messages from your providers instead of using
554 <methodname>echo()</methodname> or a similiar output mechanism. Rewritting our
555 hello provider with this knowledge it looks like:
558 <programlisting language="php"><![CDATA[
559 class My_Component_HelloProvider
560 extends Zend_Tool_Framework_Provider_Abstract
562 public function say()
564 $this->_registry->getResponse
565 ->appendContent("Hello from my provider!");
571 As you can see one has to extend the
572 <classname>Zend_Tool_Framework_Provider_Abstract</classname> to gain access to
573 the Registry which holds the
574 <classname>Zend_Tool_Framework_Client_Response</classname> instance.
578 <sect4 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced">
579 <title>Advanced Development Information</title>
582 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced.variables">
583 <title>Passing Variables to a Provider</title>
586 The above "Hello World" example is great for simple commands, but
587 what about something more advanced? As your scripting and tooling
588 needs grow, you might find that you need the ability to accept
589 variables. Much like function signatures have parameters, your
590 tooling requests can also accept parameters.
594 Just as each tooling request can be isolated to a method within a
595 class, the parameters of a tooling request can also be isolated in a
596 very well known place. Parameters of the action methods of a
597 provider can include the same parameters you want your client to
598 utilize when calling that provider and action combination. For
599 example, if you wanted to accept a name in the above example, you
600 would probably do this in OO code:
603 <programlisting language="php"><![CDATA[
604 class My_Component_HelloProvider
605 implements Zend_Tool_Framework_Provider_Interface
607 public function say($name = 'Ralph')
609 echo 'Hello' . $name . ', from my provider!';
615 The above example can then be called via the command line
616 <command>zf say hello Joe</command>. "Joe" will be supplied to the provider
617 as a parameter of the method call. Also note, as you see that the
618 parameter is optional, that means it is also optional on the command
619 line, so that <command>zf say hello</command> will still work, and default
625 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced.prompt">
626 <title>Prompt the User for Input</title>
629 There are cases when the workflow of your provider requires
630 to prompt the user for input. This can be done by requesting
631 the client to ask for more the required input by calling:
634 <programlisting language="php"><![CDATA[
635 class My_Component_HelloProvider
636 extends Zend_Tool_Framework_Provider_Abstract
638 public function say($name = 'Ralph')
640 $nameResponse = $this->_registry
642 ->promptInteractiveInput("Whats your name?");
643 $name = $name->getContent();
645 echo 'Hello' . $name . ', from my provider!';
651 This command throws an exception if the current client is not
652 able to handle interactive requests. In case of the default Console Client
653 however you will be asked to enter the name.
658 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced.pretendable">
659 <title>Pretending to execute a Provider Action</title>
662 Another interesting feature you might wish to implement is
663 <emphasis>pretendability</emphasis>. Pretendabilty is the ability
664 for your provider to "pretend" as if it is doing the requested
665 action and provider combination and give the user as much
666 information about what it <emphasis>would</emphasis> do without
667 actually doing it. This might be an important notion when doing
668 heavy database or filesystem modifications that the user might not
669 otherwise want to do.
673 Pretendability is easy to implement. There are two parts to this
674 feature: 1) marking the provider as having the ability to "pretend",
675 and 2) checking the request to ensure the current request was indeed
676 asked to be "pretended". This feature is demonstrated in the code
680 <programlisting language="php"><![CDATA[
681 class My_Component_HelloProvider
682 extends Zend_Tool_Framework_Provider_Abstract
683 implements Zend_Tool_Framework_Provider_Pretendable
685 public function say($name = 'Ralph')
687 if ($this->_registry->getRequest()->isPretend()) {
688 echo 'I would say hello to ' . $name . '.';
690 echo 'Hello' . $name . ', from my provider!';
697 To run the provider in pretend mode just call:
700 <programlisting language="sh"><![CDATA[
701 % zf --pretend say hello Ralph
702 I would say hello Ralph.
707 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced.verbosedebug">
708 <title>Verbose and Debug modes</title>
711 You can also run your provider actions in "verbose" or "debug" modes.
712 The semantics in regard to this actions have to be implemented by you
713 in the context of your provider. You can access debug or verbose modes
717 <programlisting language="php"><![CDATA[
718 class My_Component_HelloProvider
719 implements Zend_Tool_Framework_Provider_Interface
721 public function say($name = 'Ralph')
723 if($this->_registry->getRequest()->isVerbose()) {
724 echo "Hello::say has been called\n";
726 if($this->_registry->getRequest()->isDebug()) {
727 syslog(LOG_INFO, "Hello::say has been called\n");
735 id="zend.tool.extending.zend-tool-framework.providers-and-manifests.advanced.configstorage">
736 <title>Accessing User Config and Storage</title>
739 Using the Enviroment variable <property>ZF_CONFIG_FILE</property> or the
740 .zf.ini in your home directory you can inject configuration parameters into
741 any <classname>Zend_Tool</classname> provider. Access to this configuration
742 is available via the registry that is passed to your provider if you extend
743 <classname>Zend_Tool_Framework_Provider_Abstract</classname>.
746 <programlisting language="php"><![CDATA[
747 class My_Component_HelloProvider
748 extends Zend_Tool_Framework_Provider_Abstract
750 public function say()
752 $username = $this->_registry->getConfig()->username;
753 if(!empty($username)) {
754 echo "Hello $username!";
763 The returned configuration is of the type
764 <classname>Zend_Tool_Framework_Client_Config</classname> but internally the
765 <methodname>__get()</methodname> and <methodname>__set()</methodname> magic
766 methods proxy to a <classname>Zend_Config</classname> of the given
771 The storage allows to save arbitrary data for later reference. This can be
772 useful for batch processing tasks or for re-runs of your tasks. You can
773 access the storage in a similar way like the configuration:
776 <programlisting language="php"><![CDATA[
777 class My_Component_HelloProvider
778 extends Zend_Tool_Framework_Provider_Abstract
780 public function say()
782 $aValue = $this->_registry->getStorage()->get("myUsername");
783 echo "Hello $aValue!";
789 The <acronym>API</acronym> of the storage is very simple:
792 <programlisting language="php"><![CDATA[
793 class Zend_Tool_Framework_Client_Storage
795 public function setAdapter($adapter);
796 public function isEnabled();
797 public function put($name, $value);
798 public function get($name, $defaultValue=null);
799 public function has($name);
800 public function remove($name);
801 public function getStreamUri($name);
807 When designing your providers that are config or storage aware remember
808 to check if the required user-config or storage keys really exist for a
809 user. You won't run into fatal errors when none of these are provided
810 though, since empty ones are created upon request.
818 <sect2 id="zend.tool.extending.zend-tool-project">
819 <title>Zend_Tool_Project Extensions</title>
822 <classname>Zend_Tool_Project</classname> exposes a rich set of functionality and
823 capabilities that make the task of creating new providers, specficially those targetting
824 project easier and more manageable.
827 <sect3 id="zend.tool.extending.zend-tool-project.architecture">
828 <title>Overall Architecture</title>
831 This same concept applies to Zend Framework projects. In Zend Framework projects,
832 you have controllers, actions, views, models, databases and so on and so forth. In
833 terms of <classname>Zend_Tool</classname>, we need a way to track these types of
834 resources - thus <classname>Zend_Tool_Project</classname>.
838 <classname>Zend_Tool_Project</classname> is capable of tracking project resources
839 throughout the development of a project. So, for example, if in one command you
840 created a controller, and in the next command you wish to create an action within
841 that controller, <classname>Zend_Tool_Project</classname> is gonna have to
842 <emphasis>know</emphasis> about the controller file you created so that you can (in
843 the next action), be able to append that action to it. This is what keeps our
844 projects up to date and <emphasis>stateful</emphasis>.
848 Another important point to understand about projects is that typically, resources
849 are organized in a hierarchical fashion. With that in mind,
850 <classname>Zend_Tool_Project</classname> is capable of serializing the current
851 project into a internal representation that allows it to keep track of not only
852 <emphasis>what</emphasis> resources are part of a project at any given time, but
853 also <emphasis>where</emphasis> they are in relation to one another.
858 <sect3 id="zend.tool.extending.zend-tool-project.providers">
859 <title>Creating Providers</title>
862 Project specific providers are created in the same fashion as plain framework
863 providers, with one exception: project providers must extend the
864 <code>Zend_Tool_Project_Provider_Abstract</code>. This class comes with some
865 significant functionality that helps developers load existing project, obtian the
866 profile object, and be able to search the profile, then later store any changes to
867 the current project profile.
870 <programlisting language="php"><![CDATA[
871 class My_Component_HelloProvider
872 extends Zend_Tool_Project_Provider_Abstract
874 public function say()
876 $profile = $this->_loadExistingProfile();
878 /* ... do project stuff here */
880 $this->_storeProfile();
887 <sect3 id="zend.tool.extending.zend-tool-project.resources-and-contexts">
888 <title>Creating Resources & Contexts</title>