plugins-usage.xml 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="learning.plugins.usage">
  4. <title>Using Plugins</title>
  5. <para>
  6. Components that make use of plugins typically use
  7. <classname>Zend_Loader_PluginLoader</classname> to do their work. This class has you
  8. register plugins by specifying one or more "prefix paths". The component will then call the
  9. PluginLoader's <methodname>load()</methodname> method, passing the plugin's short name to
  10. it. The PluginLoader will then query each prefix path to see if a class matching that short
  11. name exists. Prefix paths are searched in LIFO (last in, first out) order, so it will match
  12. those prefix paths registered last first -- allowing you to override existing plugins.
  13. </para>
  14. <para>
  15. Some examples will make all of this more clear.
  16. </para>
  17. <example id="learning.plugins.usage.basic">
  18. <title>Basic Plugin Example: Adding a single prefix path</title>
  19. <para>
  20. In this example, we will assume some validators have been written and placed in the
  21. directory <filename>foo/plugins/validators/</filename>, and that all these classes share
  22. the class prefix "Foo_Validate_"; these two bits of information form our "prefix path".
  23. Furthermore, let's assume we have two validators, one named "Even" (ensuring a number to
  24. be validated is even), and another named "Dozens" (ensuring the number is a multiple of
  25. 12). The tree might look like this:
  26. </para>
  27. <programlisting language="text"><![CDATA[
  28. foo/
  29. |-- plugins/
  30. | |-- validators/
  31. | | |-- Even.php
  32. | | |-- Dozens.php
  33. ]]></programlisting>
  34. <para>
  35. Now, we'll inform a <classname>Zend_Form_Element</classname> instance of this prefix
  36. path. <classname>Zend_Form_Element</classname>'s
  37. <methodname>addPrefixPath()</methodname> method expects a third argument that indicates
  38. the type of plugin for which the path is being registered; in this case, it's a
  39. "validate" plugin.
  40. </para>
  41. <programlisting language="php"><![CDATA[
  42. $element->addPrefixPath('Foo_Validate', 'foo/plugins/validators/', 'validate');
  43. ]]></programlisting>
  44. <para>
  45. Now we can simply tell the element the short name of the validators we want to use. In
  46. the following example, we're using a mix of standard validators ("NotEmpty", "Int") and
  47. custom validators ("Even", "Dozens"):
  48. </para>
  49. <programlisting language="php"><![CDATA[
  50. $element->addValidator('NotEmpty')
  51. ->addValidator('Int')
  52. ->addValidator('Even')
  53. ->addValidator('Dozens');
  54. ]]></programlisting>
  55. <para>
  56. When the element needs to validate, it will then request the plugin class from the
  57. PluginLoader. The first two validators will resolve to
  58. <classname>Zend_Validate_NotEmpty</classname> and
  59. <classname>Zend_Validate_Int</classname>, respectively; the next two will resolve to
  60. <classname>Foo_Validate_Even</classname> and <classname>Foo_Validate_Dozens</classname>,
  61. respectively.
  62. </para>
  63. </example>
  64. <note>
  65. <title>What happens if a plugin is not found?</title>
  66. <para>
  67. What happens if a plugin is requested, but the PluginLoader is unable to find a class
  68. matching it? For instance, in the above example, if we registered the plugin "Bar" with
  69. the element, what would happen?
  70. </para>
  71. <para>
  72. The plugin loader will look through each prefix path, checking to see if a file matching
  73. the plugin name is found on that path. If the file is not found, it then moves on to the
  74. next prefix path.
  75. </para>
  76. <para>
  77. Once the stack of prefix paths has been exhausted, if no matching file has been found,
  78. it will throw a <exceptionname>Zend_Loader_PluginLoader_Exception</exceptionname>.
  79. </para>
  80. </note>
  81. <example id="learning.plugins.usage.override">
  82. <title>Intermediate Plugin Usage: Overriding existing plugins</title>
  83. <para>
  84. One strength of the PluginLoader is that its use of a LIFO stack allows you to override
  85. existing plugins by creating your own versions locally with a different prefix path, and
  86. registering that prefix path later in the stack.
  87. </para>
  88. <para>
  89. For example, let's consider <classname>Zend_View_Helper_FormButton</classname> (view
  90. helpers are one form of plugin). This view helper accepts three arguments, an element
  91. name (also used as the element's DOM identifier), a value (used as the button label),
  92. and an optional array of attributes. The helper then generates <acronym>HTML</acronym>
  93. markup for a form input element.
  94. </para>
  95. <para>
  96. Let's say you want the helper to instead generate a true <acronym>HTML</acronym>
  97. <constant>button</constant> element; don't want the helper to generate a DOM identifier,
  98. but instead use the value for a CSS class selector; and that you have no interest in
  99. handling arbitrary attributes. You could accomplish this in a couple of ways. In both
  100. cases, you'd create your own view helper class that implements the behavior you want;
  101. the difference is in how you would name and invoke them.
  102. </para>
  103. <para>
  104. Our first example will be to name the element with a unique name:
  105. <classname>Foo_View_Helper_CssButton</classname>, which implies the plugin name
  106. "CssButton". While this certainly is a viable approach, it poses several issues: if
  107. you've already used the Button view helper in your code, you now have to refactor;
  108. alternately, if another developer starts writing code for your application, they may
  109. inadvertently use the Button view helper instead of your new view helper.
  110. </para>
  111. <para>
  112. So, the better example is to use the plugin name "Button", giving us the class name
  113. <classname>Foo_View_Helper_Button</classname>. We then register the prefix path with the
  114. view:
  115. </para>
  116. <programlisting language="php"><![CDATA[
  117. // Zend_View::addHelperPath() utilizes the PluginLoader; however, it inverts
  118. // the arguments, as it provides a default value of "Zend_View_Helper" for the
  119. // plugin prefix.
  120. //
  121. // The below assumes your class is in the directory 'foo/view/helpers/'.
  122. $view->addHelperPath('foo/view/helpers', 'Foo_View_Helper');
  123. ]]></programlisting>
  124. <para>
  125. Once done, anywhere you now use the "Button" helper will delegate to your custom
  126. <classname>Foo_View_Helper_Button</classname> class!
  127. </para>
  128. </example>
  129. </sect1>