Zend_Loader-PluginLoader.xml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.loader.pluginloader">
  4. <title>Loading Plugins</title>
  5. <para>
  6. A number of Zend Framework components are pluggable, and allow loading
  7. of dynamic functionality by specifying a class prefix and path to class
  8. files that are not necessarily on the <property>include_path</property> or do
  9. not necessarily follow traditional naming conventions.
  10. <classname>Zend_Loader_PluginLoader</classname> provides common functionality for
  11. this process.
  12. </para>
  13. <para>
  14. The basic usage of the <classname>PluginLoader</classname> follows Zend Framework
  15. naming conventions of one class per file, using the underscore as a
  16. directory separator when resolving paths. It allows passing an optional
  17. class prefix to prepend when determining if a particular plugin class is
  18. loaded. Additionally, paths are searched in LIFO order. Due to the LIFO
  19. search and the class prefixes, this allows you to define namespaces for your
  20. plugins, and thus override plugins from paths registered earlier.
  21. </para>
  22. <sect2 id="zend.loader.pluginloader.usage">
  23. <title>Basic Use Case</title>
  24. <para>
  25. First, let's assume the following directory structure and class
  26. files, and that the top level directory and library directory are on
  27. the include_path:
  28. </para>
  29. <programlisting language="txt"><![CDATA[
  30. application/
  31. modules/
  32. foo/
  33. views/
  34. helpers/
  35. FormLabel.php
  36. FormSubmit.php
  37. bar/
  38. views/
  39. helpers/
  40. FormSubmit.php
  41. library/
  42. Zend/
  43. View/
  44. Helper/
  45. FormLabel.php
  46. FormSubmit.php
  47. FormText.php
  48. ]]></programlisting>
  49. <para>
  50. Now, let's create a plugin loader to address the various view
  51. helper repositories available:
  52. </para>
  53. <programlisting language="php"><![CDATA[
  54. $loader = new Zend_Loader_PluginLoader();
  55. $loader->addPrefixPath('Zend_View_Helper', 'Zend/View/Helper/')
  56. ->addPrefixPath('Foo_View_Helper',
  57. 'application/modules/foo/views/helpers')
  58. ->addPrefixPath('Bar_View_Helper',
  59. 'application/modules/bar/views/helpers');
  60. ]]></programlisting>
  61. <para>
  62. We can then load a given view helper using just the portion of the
  63. class name following the prefixes as defined when adding the paths:
  64. </para>
  65. <programlisting language="php"><![CDATA[
  66. // load 'FormText' helper:
  67. $formTextClass = $loader->load('FormText'); // 'Zend_View_Helper_FormText';
  68. // load 'FormLabel' helper:
  69. $formLabelClass = $loader->load('FormLabel'); // 'Foo_View_Helper_FormLabel'
  70. // load 'FormSubmit' helper:
  71. $formSubmitClass = $loader->load('FormSubmit'); // 'Bar_View_Helper_FormSubmit'
  72. ]]></programlisting>
  73. <para>
  74. Once the class is loaded, we can now instantiate it.
  75. </para>
  76. <note>
  77. <para>
  78. In some cases, you may use the same prefix for multiple paths.
  79. <classname>Zend_Loader_PluginLoader</classname> actually registers an
  80. array of paths for each given prefix; the last one registered
  81. will be the first one checked. This is particularly useful if
  82. you are utilizing incubator components.
  83. </para>
  84. </note>
  85. <note>
  86. <title>Paths may be defined at instantiation</title>
  87. <para>
  88. You may optionally provide an array of prefix / path pairs (or
  89. prefix / paths -- plural paths are allowed) as a parameter to
  90. the constructor:
  91. </para>
  92. <programlisting language="php"><![CDATA[
  93. $loader = new Zend_Loader_PluginLoader(array(
  94. 'Zend_View_Helper' => 'Zend/View/Helper/',
  95. 'Foo_View_Helper' => 'application/modules/foo/views/helpers',
  96. 'Bar_View_Helper' => 'application/modules/bar/views/helpers'
  97. ));
  98. ]]></programlisting>
  99. </note>
  100. <para>
  101. <classname>Zend_Loader_PluginLoader</classname> also optionally allows you to
  102. share plugins across plugin-aware objects, without needing to
  103. utilize a singleton instance. It does so via a static registry.
  104. Indicate the registry name at instantiation as the second parameter
  105. to the constructor:
  106. </para>
  107. <programlisting language="php"><![CDATA[
  108. // Store plugins in static registry 'foobar':
  109. $loader = new Zend_Loader_PluginLoader(array(), 'foobar');
  110. ]]></programlisting>
  111. <para>
  112. Other components that instantiate the <classname>PluginLoader</classname>
  113. using the same registry name will then have access to already loaded
  114. paths and plugins.
  115. </para>
  116. </sect2>
  117. <sect2 id="zend.loader.pluginloader.paths">
  118. <title>Manipulating Plugin Paths</title>
  119. <para>
  120. The example in the previous section shows how to add paths to a
  121. plugin loader. What if you want to determine the paths already
  122. loaded, or remove one or more?
  123. </para>
  124. <itemizedlist>
  125. <listitem>
  126. <para>
  127. <methodname>getPaths($prefix = null)</methodname> returns all paths as
  128. prefix / path pairs if no <varname>$prefix</varname> is provided,
  129. or just the paths registered for a given prefix if a
  130. <varname>$prefix</varname> is present.
  131. </para>
  132. </listitem>
  133. <listitem>
  134. <para>
  135. <methodname>clearPaths($prefix = null)</methodname> will clear all
  136. registered paths by default, or only those associated with a
  137. given prefix, if the <varname>$prefix</varname> is provided and
  138. present in the stack.
  139. </para>
  140. </listitem>
  141. <listitem>
  142. <para>
  143. <methodname>removePrefixPath($prefix, $path = null)</methodname> allows
  144. you to selectively remove a specific path associated with a
  145. given prefix. If no <varname>$path</varname> is provided, all
  146. paths for that prefix are removed. If a <varname>$path</varname>
  147. is provided and exists for that prefix, only that path will
  148. be removed.
  149. </para>
  150. </listitem>
  151. </itemizedlist>
  152. </sect2>
  153. <sect2 id="zend.loader.pluginloader.checks">
  154. <title>Testing for Plugins and Retrieving Class Names</title>
  155. <para>
  156. Sometimes you simply want to determine if a plugin class has been
  157. loaded before you perform an action. <methodname>isLoaded()</methodname> takes a
  158. plugin name, and returns the status.
  159. </para>
  160. <para>
  161. Another common use case for the <classname>PluginLoader</classname> is to
  162. determine fully qualified plugin class names of loaded classes;
  163. <methodname>getClassName()</methodname> provides this functionality. Typically,
  164. this would be used in conjunction with <methodname>isLoaded()</methodname>:
  165. </para>
  166. <programlisting language="php"><![CDATA[
  167. if ($loader->isLoaded('Adapter')) {
  168. $class = $loader->getClassName('Adapter');
  169. $adapter = call_user_func(array($class, 'getInstance'));
  170. }
  171. ]]></programlisting>
  172. </sect2>
  173. <sect2 id="zend.loader.pluginloader.performance">
  174. <title>Getting Better Performance for Plugins</title>
  175. <para>
  176. Plugin loading can be an expensive operation. At its heart, it needs
  177. to loop through each prefix, then each path on the prefix, until it
  178. finds a file that matches -- and which defines the class expected.
  179. In cases where the file exists but does not define the class, an
  180. error will be added to the <acronym>PHP</acronym> error stack, which is also an
  181. expensive operation. The question then turns to: how can you keep
  182. the flexibility of plugins and also address performance?
  183. </para>
  184. <para>
  185. <classname>Zend_Loader_PluginLoader</classname> offers an opt-in feature for
  186. just this situation, a class file include cache. When enabled, it
  187. will create a file that contains all successful includes which you
  188. can then call from your bootstrap. Using this strategy, you can
  189. greatly improve the performance of your production servers.
  190. </para>
  191. <example id="zend.loader.pluginloader.performance.example">
  192. <title>Using the PluginLoader class file include cache</title>
  193. <para>
  194. To use the class file include cache, simply drop the
  195. following code into your bootstrap:
  196. </para>
  197. <programlisting language="php"><![CDATA[
  198. $classFileIncCache = APPLICATION_PATH . '/../data/pluginLoaderCache.php';
  199. if (file_exists($classFileIncCache)) {
  200. include_once $classFileIncCache;
  201. }
  202. Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
  203. ]]></programlisting>
  204. <para>
  205. Obviously, the path and filename will vary based on your needs.
  206. This code should come as early as possible, to ensure that
  207. plugin-based components can make use of it.
  208. </para>
  209. <para>
  210. During development, you may wish to disable the cache. One
  211. method for doing so is to use a configuration key for
  212. determining whether or not the plugin loader should cache.
  213. </para>
  214. <programlisting language="php"><![CDATA[
  215. $classFileIncCache = APPLICATION_PATH . '/../data/pluginLoaderCache.php';
  216. if (file_exists($classFileIncCache)) {
  217. include_once $classFileIncCache;
  218. }
  219. if ($config->enablePluginLoaderCache) {
  220. Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
  221. }
  222. ]]></programlisting>
  223. <para>
  224. This technique allows you to keep your modifications to your
  225. configuration file rather than code.
  226. </para>
  227. </example>
  228. </sect2>
  229. </sect1>
  230. <!--
  231. vim:se ts=4 sw=4 et:
  232. -->