| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.tool.framework.writing-providers">
- <title>Creating Providers to use with Zend_Tool_Framework</title>
- <para>
- In general, a provider, on its own, is nothing more than the shell for a
- developer to bundle up some capabilities they wish to dispatch with the
- command line (or other) clients. It is an analogue to what a
- "controller" is inside of your <acronym>MVC</acronym> application.
- </para>
- <sect2 id="zend.tool.framework.writing-providers.loading">
- <title>How Zend_Tool finds your Providers</title>
- <para>
- By default <classname>Zend_Tool</classname> uses the IncludePathLoader to find all
- the providers that you can run. It recursivly iterates all
- include path directories and opens all files that end
- with "Manifest.php" or "Provider.php". All classes in these
- files are inspected if they implement either
- <classname>Zend_Tool_Framework_Provider_Interface</classname>
- or <classname>Zend_Tool_Framework_Manifest_ProviderManifestable</classname>.
- Instances of the provider interface make up for the real functionality
- and all their public methods are accessible as provider actions.
- The ProviderManifestable interface however requires the implementation of a method
- <methodname>getProviders()</methodname> which returns an array of
- instantiated provider interface instances.
- </para>
- <para>
- The following naming rules apply on how you can access the providers
- that were found by the IncludePathLoader:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- The last part of your classname split by underscore is used
- for the provider name, e.g. "My_Provider_Hello" leads to your
- provider being accessible by the name "hello".
- </para>
- </listitem>
- <listitem>
- <para>
- If your provider has a method <methodname>getName()</methodname>
- it will be used instead of the previous method to determine
- the name.
- </para>
- </listitem>
- <listitem>
- <para>
- If your provider has "Provider" as prefix, e.g. it is called
- <classname>My_HelloProvider</classname> it will be stripped
- from the name so that the provider will be called "hello".
- </para>
- </listitem>
- </itemizedlist>
- <note>
- <para>
- The IncludePathLoader does not follow symlinks, that means
- you cannot link provider functionality into your include paths,
- they have to be physically present in the include paths.
- </para>
- </note>
- <example id="zend.tool.framework.writing-providers.loading.example">
- <title>Exposing Your Providers with a Manifest</title>
- <para>
- You can expose your providers to <classname>Zend_Tool</classname> by offering a
- manifest with a special filename ending with "Manifest.php".
- A Provider Manifest is an implementation of the
- <interface>Zend_Tool_Framework_Manifest_ProviderManifestable</interface>
- and requires the <methodname>getProviders()</methodname> method to return
- an array of instantiated providers. In anticipation of our first
- own provider <classname>My_Component_HelloProvider</classname>
- we will create the following manifest:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_Manifest
- implements Zend_Tool_Framework_Manifest_ProviderManifestable
- {
- public function getProviders()
- {
- return array(
- new My_Component_HelloProvider()
- );
- }
- }
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.tool.framework.writing-providers.basic">
- <title>Basic Instructions for Creating Providers</title>
- <para>
- As an example, if a developer wants to add the capability of showing
- the version of a datafile that his 3rd party component is working
- from, there is only one class the developer would need to implement.
- Assuming the component is called <classname>My_Component</classname>, he would
- create a class named <classname>My_Component_HelloProvider</classname> in a
- file named <filename>HelloProvider.php</filename> somewhere on the
- <property>include_path</property>. This class would implement
- <classname>Zend_Tool_Framework_Provider_Interface</classname>, and the body of
- this file would only have to look like the following:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- implements Zend_Tool_Framework_Provider_Interface
- {
- public function say()
- {
- echo 'Hello from my provider!';
- }
- }
- ]]></programlisting>
- <para>
- Given that code above, and assuming the developer wishes to access
- this functionality through the console client, the call would look
- like this:
- </para>
- <programlisting language="sh"><![CDATA[
- % zf say hello
- Hello from my provider!
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.tool.framework.writing-providers.response">
- <title>The response object</title>
- <para>
- As discussed in the architecture section <classname>Zend_Tool</classname> allows to hook
- different clients for using your <classname>Zend_Tool</classname> providers. To keep
- compliant with different clients you should use the response object to return messages
- from your providers instead of using <methodname>echo()</methodname> or a similiar
- output mechanism. Rewritting our hello provider with this knowledge it looks like:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- extends Zend_Tool_Framework_Provider_Abstract
- {
- public function say()
- {
- $this->_registry->getResponse
- ->appendContent("Hello from my provider!");
- }
- }
- ]]></programlisting>
- <para>
- As you can see one has to extend the
- <classname>Zend_Tool_Framework_Provider_Abstract</classname> to gain access to the
- Registry which holds the <classname>Zend_Tool_Framework_Client_Response</classname>
- instance.
- </para>
- </sect2>
- <sect2 id="zend.tool.framework.writing-providers.advanced">
- <title>Advanced Development Information</title>
- <sect3 id="zend.tool.framework.writing-providers.advanced.variables">
- <title>Passing Variables to a Provider</title>
- <para>
- The above "Hello World" example is great for simple commands, but
- what about something more advanced? As your scripting and tooling
- needs grow, you might find that you need the ability to accept
- variables. Much like function signatures have parameters, your
- tooling requests can also accept parameters.
- </para>
- <para>
- Just as each tooling request can be isolated to a method within a
- class, the parameters of a tooling request can also be isolated in a
- very well known place. Parameters of the action methods of a
- provider can include the same parameters you want your client to
- utilize when calling that provider and action combination. For
- example, if you wanted to accept a name in the above example, you
- would probably do this in OO code:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- implements Zend_Tool_Framework_Provider_Interface
- {
- public function say($name = 'Ralph')
- {
- echo 'Hello' . $name . ', from my provider!';
- }
- }
- ]]></programlisting>
- <para>
- The above example can then be called via the command line
- <command>zf say hello Joe</command>. "Joe" will be supplied to the provider as
- a parameter of the method call. Also note, as you see that the
- parameter is optional, that means it is also optional on the command
- line, so that <command>zf say hello</command> will still work, and default
- to the name "Ralph".
- </para>
- </sect3>
- <sect3 id="zend.tool.framework.writing-providers.advanced.prompt">
- <title>Prompt the User for Input</title>
- <para>
- There are cases when the workflow of your provider requires
- to prompt the user for input. This can be done by requesting
- the client to ask for more the required input by calling:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- extends Zend_Tool_Framework_Provider_Abstract
- {
- public function say($name = 'Ralph')
- {
- $nameResponse = $this->_registry
- ->getClient()
- ->promptInteractiveInput("Whats your name?");
- $name = $nameResponse->getContent();
- echo 'Hello' . $name . ', from my provider!';
- }
- }
- ]]></programlisting>
- <para>
- This command throws an exception if the current client is not
- able to handle interactive requests. In case of the default Console Client
- however you will be asked to enter the name.
- </para>
- </sect3>
- <sect3 id="zend.tool.framework.writing-providers.advanced.pretendable">
- <title>Pretending to execute a Provider Action</title>
- <para>
- Another interesting feature you might wish to implement is
- <emphasis>pretendability</emphasis>. Pretendabilty is the ability
- for your provider to "pretend" as if it is doing the requested
- action and provider combination and give the user as much
- information about what it <emphasis>would</emphasis> do without
- actually doing it. This might be an important notion when doing
- heavy database or filesystem modifications that the user might not
- otherwise want to do.
- </para>
- <para>
- Pretendability is easy to implement. There are two parts to this
- feature: 1) marking the provider as having the ability to "pretend",
- and 2) checking the request to ensure the current request was indeed
- asked to be "pretended". This feature is demonstrated in the code
- sample below.
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- extends Zend_Tool_Framework_Provider_Abstract
- implements Zend_Tool_Framework_Provider_Pretendable
- {
- public function say($name = 'Ralph')
- {
- if ($this->_registry->getRequest()->isPretend()) {
- echo 'I would say hello to ' . $name . '.';
- } else {
- echo 'Hello' . $name . ', from my provider!';
- }
- }
- }
- ]]></programlisting>
- <para>
- To run the provider in pretend mode just call:
- </para>
- <programlisting language="sh"><![CDATA[
- % zf --pretend say hello Ralph
- I would say hello Ralph.
- ]]></programlisting>
- </sect3>
- <sect3 id="zend.tool.framework.writing-providers.advanced.verbosedebug">
- <title>Verbose and Debug modes</title>
- <para>
- You can also run your provider actions in "verbose" or "debug" modes.
- The semantics in regard to this actions have to be implemented by you
- in the context of your provider. You can access debug or verbose modes
- with:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- implements Zend_Tool_Framework_Provider_Interface
- {
- public function say($name = 'Ralph')
- {
- if($this->_registry->getRequest()->isVerbose()) {
- echo "Hello::say has been called\n";
- }
- if($this->_registry->getRequest()->isDebug()) {
- syslog(LOG_INFO, "Hello::say has been called\n");
- }
- }
- }
- ]]></programlisting>
- </sect3>
- <sect3 id="zend.tool.framework.writing-providers.advanced.configstorage">
- <title>Accessing User Config and Storage</title>
- <para>
- Using the Enviroment variable <property>ZF_CONFIG_FILE</property> or the
- .zf.ini in your home directory you can inject configuration parameters into
- any <classname>Zend_Tool</classname> provider. Access to this configuration is
- available via the registry that is passed to your provider if you extend
- <classname>Zend_Tool_Framework_Provider_Abstract</classname>.
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- extends Zend_Tool_Framework_Provider_Abstract
- {
- public function say()
- {
- $username = $this->_registry->getConfig()->username;
- if(!empty($username)) {
- echo "Hello $username!";
- } else {
- echo "Hello!";
- }
- }
- }
- ]]></programlisting>
- <para>
- The returned configuration is of the type
- <classname>Zend_Tool_Framework_Client_Config</classname> but internally the
- <methodname>__get()</methodname> and <methodname>__set()</methodname> magic methods
- proxy to a <classname>Zend_Config</classname> of the given configuration type.
- </para>
- <para>
- The storage allows to save arbitrary data for later reference. This can be useful
- for batch processing tasks or for re-runs of your tasks. You can access the storage
- in a similar way like the configuration:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Component_HelloProvider
- extends Zend_Tool_Framework_Provider_Abstract
- {
- public function say()
- {
- $aValue = $this->_registry->getStorage()->get("myUsername");
- echo "Hello $aValue!";
- }
- }
- ]]></programlisting>
- <para>
- The <acronym>API</acronym> of the storage is very simple:
- </para>
- <programlisting language="php"><![CDATA[
- class Zend_Tool_Framework_Client_Storage
- {
- public function setAdapter($adapter);
- public function isEnabled();
- public function put($name, $value);
- public function get($name, $defaultValue=null);
- public function has($name);
- public function remove($name);
- public function getStreamUri($name);
- }
- ]]></programlisting>
- <important>
- <para>
- When designing your providers that are config or storage aware remember to
- check if the required user-config or storage keys really exist for a user.
- You won't run into fatal errors when none of these are provided though,
- since empty ones are created upon request.
- </para>
- </important>
- </sect3>
- </sect2>
- </sect1>
|