QueryPathExtension.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. /** @file
  3. * This file contains the Query Path extension tools.
  4. *
  5. * Query Path can be extended to support additional features. To do this,
  6. * you need only create a new class that implements {@link QueryPathExtension}
  7. * and add your own methods. This class can then be registered as an extension.
  8. * It will then be available through Query Path.
  9. *
  10. * For information on building your own extension, see {@link QueryPathExtension}.
  11. * If you are trying to load an extension you have downloaded, chances are good that
  12. * all you need to do is {@link require_once} the file that contains the extension.
  13. *
  14. * @author M Butcher <matt@aleph-null.tv>
  15. * @license http://opensource.org/licenses/lgpl-2.1.php LGPL or MIT-like license.
  16. * @see QueryPathExtension
  17. * @see QueryPathExtensionRegistry::extend()
  18. */
  19. /** @addtogroup querypath_extensions Extensions
  20. * The QueryPath extension system and bundled extensions.
  21. *
  22. * Much like jQuery, QueryPath provides a simple extension mechanism that allows
  23. * extensions to auto-register themselves upon being loaded. For a simple example, see
  24. * QPXML. For the internals, see QueryPathExntesion and QueryPath::__construct().
  25. */
  26. /**
  27. * A QueryPathExtension is a tool that extends the capabilities of a QueryPath object.
  28. *
  29. * Extensions to QueryPath should implement the QueryPathExtension interface. The
  30. * only requirement is that the extension provide a constructor that takes a
  31. * QueryPath object as a parameter.
  32. *
  33. * Here is an example QueryPath extension:
  34. * <code><?php
  35. * class StubExtensionOne implements QueryPathExtension {
  36. * private $qp = NULL;
  37. * public function __construct(QueryPath $qp) {
  38. * $this->qp = $qp;
  39. * }
  40. *
  41. * public function stubToe() {
  42. * $this->qp->find(':root')->append('<toe/>')->end();
  43. * return $this->qp;
  44. * }
  45. * }
  46. * QueryPathExtensionRegistry::extend('StubExtensionOne');
  47. * ?></code>
  48. * In this example, the StubExtensionOne class implements QueryPathExtension.
  49. * The constructor stores a local copyof the QueryPath object. This is important
  50. * if you are planning on fully integrating with QueryPath's Fluent Interface.
  51. *
  52. * Finally, the stubToe() function illustrates how the extension makes use of
  53. * QueryPath internally, and remains part of the fluent interface by returning
  54. * the $qp object.
  55. *
  56. * Notice that beneath the class, there is a single call to register the
  57. * extension with QueryPath's registry. Your extension should end with a line
  58. * similar to this.
  59. *
  60. * <b>How is a QueryPath extension called?</b>
  61. *
  62. * QueryPath extensions are called like regular QueryPath functions. For
  63. * example, the extension above can be called like this:
  64. * <code>
  65. * qp('some.xml')->stubToe();
  66. * </code>
  67. * Since it returns the QueryPath ($qp) object, chaining is supported:
  68. * <code>
  69. * print qp('some.xml')->stubToe()->xml();
  70. * </code>
  71. * When you write your own extensions, anything that does not need to return a
  72. * specific value should return the QueryPath object. Between that and the
  73. * extension registry, this will provide the best developer experience.
  74. *
  75. * @ingroup querypath_extensions
  76. */
  77. interface QueryPathExtension {
  78. public function __construct(QueryPath $qp);
  79. }
  80. /**
  81. * A registry for QueryPath extensions.
  82. *
  83. * QueryPath extensions should call the {@link QueryPathExtensionRegistry::extend()}
  84. * function to register their extension classes. The QueryPath library then
  85. * uses this information to determine what QueryPath extensions should be loaded and
  86. * executed.
  87. *
  88. * @ingroup querypath_extensions
  89. */
  90. class QueryPathExtensionRegistry {
  91. /**
  92. * Internal flag indicating whether or not the registry should
  93. * be used for automatic extension loading. If this is false, then
  94. * implementations should not automatically load extensions.
  95. */
  96. public static $useRegistry = TRUE;
  97. /**
  98. * The extension registry. This should consist of an array of class
  99. * names.
  100. */
  101. protected static $extensionRegistry = array();
  102. protected static $extensionMethodRegistry = array();
  103. /**
  104. * Extend QueryPath with the given extension class.
  105. */
  106. public static function extend($classname) {
  107. self::$extensionRegistry[] = $classname;
  108. $class = new ReflectionClass($classname);
  109. $methods = $class->getMethods();
  110. foreach ($methods as $method) {
  111. self::$extensionMethodRegistry[$method->getName()] = $classname;
  112. }
  113. }
  114. /**
  115. * Check to see if a method is known.
  116. * This checks to see if the given method name belongs to one of the
  117. * registered extensions. If it does, then this will return TRUE.
  118. *
  119. * @param string $name
  120. * The name of the method to search for.
  121. * @return boolean
  122. * TRUE if the method exists, false otherwise.
  123. */
  124. public static function hasMethod($name) {
  125. return isset(self::$extensionMethodRegistry[$name]);
  126. }
  127. /**
  128. * Check to see if the given extension class is registered.
  129. * Given a class name for a {@link QueryPathExtension} class, this
  130. * will check to see if that class is registered. If so, it will return
  131. * TRUE.
  132. *
  133. * @param string $name
  134. * The name of the class.
  135. * @return boolean
  136. * TRUE if the class is registered, FALSE otherwise.
  137. */
  138. public static function hasExtension($name) {
  139. return in_array($name, self::$extensionRegistry);
  140. }
  141. /**
  142. * Get the class that a given method belongs to.
  143. * Given a method name, this will check all registered extension classes
  144. * to see if any of them has the named method. If so, this will return
  145. * the classname.
  146. *
  147. * Note that if two extensions are registered that contain the same
  148. * method name, the last one registred will be the only one recognized.
  149. *
  150. * @param string $name
  151. * The name of the method.
  152. * @return string
  153. * The name of the class.
  154. */
  155. public static function getMethodClass($name) {
  156. return self::$extensionMethodRegistry[$name];
  157. }
  158. /**
  159. * Get extensions for the given QueryPath object.
  160. *
  161. * Given a {@link QueryPath} object, this will return
  162. * an associative array of extension names to (new) instances.
  163. * Generally, this is intended to be used internally.
  164. *
  165. * @param QueryPath $qp
  166. * The QueryPath into which the extensions should be registered.
  167. * @return array
  168. * An associative array of classnames to instances.
  169. */
  170. public static function getExtensions(QueryPath $qp) {
  171. $extInstances = array();
  172. foreach (self::$extensionRegistry as $ext) {
  173. $extInstances[$ext] = new $ext($qp);
  174. }
  175. return $extInstances;
  176. }
  177. /**
  178. * Enable or disable automatic extension loading.
  179. *
  180. * If extension autoloading is disabled, then QueryPath will not
  181. * automatically load all registred extensions when a new QueryPath
  182. * object is created using {@link qp()}.
  183. */
  184. public static function autoloadExtensions($boolean = TRUE) {
  185. self::$useRegistry = $boolean;
  186. }
  187. }