| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.loader.pluginloader">
- <title>Loading Plugins</title>
- <para>
- A number of Zend Framework components are pluggable, and allow loading
- of dynamic functionality by specifying a class prefix and path to class
- files that are not necessarily on the <property>include_path</property> or do
- not necessarily follow traditional naming conventions.
- <classname>Zend_Loader_PluginLoader</classname> provides common functionality for
- this process.
- </para>
- <para>
- The basic usage of the <classname>PluginLoader</classname> follows Zend Framework
- naming conventions of one class per file, using the underscore as a
- directory separator when resolving paths. It allows passing an optional
- class prefix to prepend when determining if a particular plugin class is
- loaded. Additionally, paths are searched in LIFO order. Due to the LIFO
- search and the class prefixes, this allows you to define namespaces for your
- plugins, and thus override plugins from paths registered earlier.
- </para>
- <sect2 id="zend.loader.pluginloader.usage">
- <title>Basic Use Case</title>
- <para>
- First, let's assume the following directory structure and class
- files, and that the top level directory and library directory are on
- the include_path:
- </para>
- <programlisting language="txt"><![CDATA[
- application/
- modules/
- foo/
- views/
- helpers/
- FormLabel.php
- FormSubmit.php
- bar/
- views/
- helpers/
- FormSubmit.php
- library/
- Zend/
- View/
- Helper/
- FormLabel.php
- FormSubmit.php
- FormText.php
- ]]></programlisting>
- <para>
- Now, let's create a plugin loader to address the various view
- helper repositories available:
- </para>
- <programlisting language="php"><![CDATA[
- $loader = new Zend_Loader_PluginLoader();
- $loader->addPrefixPath('Zend_View_Helper', 'Zend/View/Helper/')
- ->addPrefixPath('Foo_View_Helper',
- 'application/modules/foo/views/helpers')
- ->addPrefixPath('Bar_View_Helper',
- 'application/modules/bar/views/helpers');
- ]]></programlisting>
- <para>
- We can then load a given view helper using just the portion of the
- class name following the prefixes as defined when adding the paths:
- </para>
- <programlisting language="php"><![CDATA[
- // load 'FormText' helper:
- $formTextClass = $loader->load('FormText'); // 'Zend_View_Helper_FormText';
- // load 'FormLabel' helper:
- $formLabelClass = $loader->load('FormLabel'); // 'Foo_View_Helper_FormLabel'
- // load 'FormSubmit' helper:
- $formSubmitClass = $loader->load('FormSubmit'); // 'Bar_View_Helper_FormSubmit'
- ]]></programlisting>
- <para>
- Once the class is loaded, we can now instantiate it.
- </para>
- <note>
- <para>
- In some cases, you may use the same prefix for multiple paths.
- <classname>Zend_Loader_PluginLoader</classname> actually registers an
- array of paths for each given prefix; the last one registered
- will be the first one checked. This is particularly useful if
- you are utilizing incubator components.
- </para>
- </note>
- <note>
- <title>Paths may be defined at instantiation</title>
- <para>
- You may optionally provide an array of prefix / path pairs (or
- prefix / paths -- plural paths are allowed) as a parameter to
- the constructor:
- </para>
- <programlisting language="php"><![CDATA[
- $loader = new Zend_Loader_PluginLoader(array(
- 'Zend_View_Helper' => 'Zend/View/Helper/',
- 'Foo_View_Helper' => 'application/modules/foo/views/helpers',
- 'Bar_View_Helper' => 'application/modules/bar/views/helpers'
- ));
- ]]></programlisting>
- </note>
- <para>
- <classname>Zend_Loader_PluginLoader</classname> also optionally allows you to
- share plugins across plugin-aware objects, without needing to
- utilize a singleton instance. It does so via a static registry.
- Indicate the registry name at instantiation as the second parameter
- to the constructor:
- </para>
- <programlisting language="php"><![CDATA[
- // Store plugins in static registry 'foobar':
- $loader = new Zend_Loader_PluginLoader(array(), 'foobar');
- ]]></programlisting>
- <para>
- Other components that instantiate the <classname>PluginLoader</classname>
- using the same registry name will then have access to already loaded
- paths and plugins.
- </para>
- </sect2>
- <sect2 id="zend.loader.pluginloader.paths">
- <title>Manipulating Plugin Paths</title>
- <para>
- The example in the previous section shows how to add paths to a
- plugin loader. What if you want to determine the paths already
- loaded, or remove one or more?
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <methodname>getPaths($prefix = null)</methodname> returns all paths as
- prefix / path pairs if no <varname>$prefix</varname> is provided,
- or just the paths registered for a given prefix if a
- <varname>$prefix</varname> is present.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>clearPaths($prefix = null)</methodname> will clear all
- registered paths by default, or only those associated with a
- given prefix, if the <varname>$prefix</varname> is provided and
- present in the stack.
- </para>
- </listitem>
- <listitem>
- <para>
- <methodname>removePrefixPath($prefix, $path = null)</methodname> allows
- you to selectively remove a specific path associated with a
- given prefix. If no <varname>$path</varname> is provided, all
- paths for that prefix are removed. If a <varname>$path</varname>
- is provided and exists for that prefix, only that path will
- be removed.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.loader.pluginloader.checks">
- <title>Testing for Plugins and Retrieving Class Names</title>
- <para>
- Sometimes you simply want to determine if a plugin class has been
- loaded before you perform an action. <methodname>isLoaded()</methodname> takes a
- plugin name, and returns the status.
- </para>
- <para>
- Another common use case for the <classname>PluginLoader</classname> is to
- determine fully qualified plugin class names of loaded classes;
- <methodname>getClassName()</methodname> provides this functionality. Typically,
- this would be used in conjunction with <methodname>isLoaded()</methodname>:
- </para>
- <programlisting language="php"><![CDATA[
- if ($loader->isLoaded('Adapter')) {
- $class = $loader->getClassName('Adapter');
- $adapter = call_user_func(array($class, 'getInstance'));
- }
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.loader.pluginloader.performance">
- <title>Getting Better Performance for Plugins</title>
- <para>
- Plugin loading can be an expensive operation. At its heart, it needs
- to loop through each prefix, then each path on the prefix, until it
- finds a file that matches -- and which defines the class expected.
- In cases where the file exists but does not define the class, an
- error will be added to the <acronym>PHP</acronym> error stack, which is also an
- expensive operation. The question then turns to: how can you keep
- the flexibility of plugins and also address performance?
- </para>
- <para>
- <classname>Zend_Loader_PluginLoader</classname> offers an opt-in feature for
- just this situation, a class file include cache. When enabled, it
- will create a file that contains all successful includes which you
- can then call from your bootstrap. Using this strategy, you can
- greatly improve the performance of your production servers.
- </para>
- <example id="zend.loader.pluginloader.performance.example">
- <title>Using the PluginLoader class file include cache</title>
- <para>
- To use the class file include cache, simply drop the
- following code into your bootstrap:
- </para>
- <programlisting language="php"><![CDATA[
- $classFileIncCache = APPLICATION_PATH . '/../data/pluginLoaderCache.php';
- if (file_exists($classFileIncCache)) {
- include_once $classFileIncCache;
- }
- Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
- ]]></programlisting>
- <para>
- Obviously, the path and filename will vary based on your needs.
- This code should come as early as possible, to ensure that
- plugin-based components can make use of it.
- </para>
- <para>
- During development, you may wish to disable the cache. One
- method for doing so is to use a configuration key for
- determining whether or not the plugin loader should cache.
- </para>
- <programlisting language="php"><![CDATA[
- $classFileIncCache = APPLICATION_PATH . '/../data/pluginLoaderCache.php';
- if (file_exists($classFileIncCache)) {
- include_once $classFileIncCache;
- }
- if ($config->enablePluginLoaderCache) {
- Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
- }
- ]]></programlisting>
- <para>
- This technique allows you to keep your modifications to your
- configuration file rather than code.
- </para>
- </example>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|