PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Loader/PluginBroker.php

https://github.com/sidealice/zf2
PHP | 375 lines | 194 code | 34 blank | 147 comment | 27 complexity | 9d83d0fae135d0c2de790dd832183d50 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Loader
  17. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. namespace Zend\Loader;
  22. /**
  23. * Plugin broker base implementation
  24. *
  25. * @category Zend
  26. * @package Zend_Loader
  27. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  28. * @license http://framework.zend.com/license/new-bsd New BSD License
  29. */
  30. class PluginBroker implements Broker
  31. {
  32. /**
  33. * @var string Default class loader to utilize with this broker
  34. */
  35. protected $defaultClassLoader = 'Zend\Loader\PluginClassLoader';
  36. /**
  37. * @var ShortNameLocator Plugin class loader used by this instance
  38. */
  39. protected $classLoader;
  40. /**
  41. * @var boolean Whether plugins should be registered on load
  42. */
  43. protected $registerPluginsOnLoad = true;
  44. /**
  45. * @var array Cache of loaded plugin instances
  46. */
  47. protected $plugins = array();
  48. /**
  49. * @var Callback Routine to use when validating plugins
  50. */
  51. protected $validator;
  52. /**
  53. * Constructor
  54. *
  55. * Allow configuration via options; see {@link setOptions()} for details.
  56. *
  57. * @param null|array|Traversable $options
  58. * @return void
  59. */
  60. public function __construct($options = null)
  61. {
  62. if (null !== $options) {
  63. $this->setOptions($options);
  64. }
  65. }
  66. /**
  67. * Configure plugin broker
  68. *
  69. * @param array|Traversable $options
  70. * @return PluginBroker
  71. */
  72. public function setOptions($options)
  73. {
  74. if (!is_array($options) && !$options instanceof \Traversable) {
  75. throw new Exception\InvalidArgumentException(sprintf(
  76. 'Expected an array or Traversable; received "%s"',
  77. (is_object($options) ? get_class($options) : gettype($options))
  78. ));
  79. }
  80. // Cache plugins until after a validator has been registered
  81. $plugins = array();
  82. foreach ($options as $key => $value) {
  83. switch (strtolower($key)) {
  84. case 'class_loader':
  85. if (is_string($value)) {
  86. if (!class_exists($value)) {
  87. throw new Exception\RuntimeException(sprintf(
  88. 'Unknown class "%s" provided as class loader option',
  89. $value
  90. ));
  91. }
  92. $value = new $value;
  93. }
  94. if ($value instanceof ShortNameLocator) {
  95. $this->setClassLoader($value);
  96. break;
  97. }
  98. if (!is_array($value) && !$value instanceof \Traversable) {
  99. throw new Exception\RuntimeException(sprintf(
  100. 'Option passed for class loader (%s) is of an unknown type',
  101. (is_object($value) ? get_class($value) : gettype($value))
  102. ));
  103. }
  104. $class = false;
  105. $options = null;
  106. foreach ($value as $k => $v) {
  107. switch (strtolower($k)) {
  108. case 'class':
  109. $class = $v;
  110. break;
  111. case 'options':
  112. $options = $v;
  113. break;
  114. default:
  115. break;
  116. }
  117. }
  118. if ($class) {
  119. $loader = new $class($options);
  120. $this->setClassLoader($loader);
  121. }
  122. break;
  123. case 'plugins':
  124. if (!is_array($value) && !$value instanceof \Traversable) {
  125. throw new Exception\RuntimeException(sprintf(
  126. 'Plugins option must be an array or Traversable; received "%s"',
  127. (is_object($value) ? get_class($value) : gettype($value))
  128. ));
  129. }
  130. // Aggregate plugins; register only after a validator has
  131. // been registered
  132. $plugins = $value;
  133. break;
  134. case 'register_plugins_on_load':
  135. $this->setRegisterPluginsOnLoad($value);
  136. break;
  137. case 'validator':
  138. $this->setValidator($value);
  139. break;
  140. default:
  141. // ignore unknown options
  142. break;
  143. }
  144. }
  145. // Register any plugins discovered
  146. foreach ($plugins as $name => $plugin) {
  147. $this->register($name, $plugin);
  148. }
  149. return $this;
  150. }
  151. /**
  152. * Load and return a plugin instance
  153. *
  154. * @param string $plugin
  155. * @param array $options Options to pass to the plugin constructor
  156. * @return object
  157. * @throws Exception if plugin not found
  158. */
  159. public function load($plugin, array $options = null)
  160. {
  161. $pluginName = strtolower($plugin);
  162. if (isset($this->plugins[$pluginName])) {
  163. return $this->plugins[$pluginName];
  164. }
  165. if (class_exists($plugin)) {
  166. // Allow loading fully-qualified class names via the broker
  167. $class = $plugin;
  168. } else {
  169. // Unqualified class names are then passed to the class loader
  170. $class = $this->getClassLoader()->load($plugin);
  171. if (empty($class)) {
  172. throw new Exception\RuntimeException('Unable to locate class associated with "' . $pluginName . '"');
  173. }
  174. }
  175. if (empty($options)) {
  176. $instance = new $class();
  177. } elseif ($this->isAssocArray($options)) {
  178. $instance = new $class($options);
  179. } else {
  180. $r = new \ReflectionClass($class);
  181. $instance = $r->newInstanceArgs($options);
  182. }
  183. if ($this->getRegisterPluginsOnLoad()) {
  184. $this->register($pluginName, $instance);
  185. }
  186. return $instance;
  187. }
  188. /**
  189. * Get list of all loaded plugins
  190. *
  191. * @return array
  192. */
  193. public function getPlugins()
  194. {
  195. return $this->plugins;
  196. }
  197. /**
  198. * Whether or not a given plugin has been loaded
  199. *
  200. * @param string $name
  201. * @return bool
  202. */
  203. public function isLoaded($name)
  204. {
  205. return isset($this->plugins[$name]);
  206. }
  207. /**
  208. * Register a plugin object by name
  209. *
  210. * @param string $name
  211. * @param mixed $plugin
  212. * @return PluginBroker
  213. */
  214. public function register($name, $plugin)
  215. {
  216. if (!$this->validatePlugin($plugin)) {
  217. throw new Exception\RuntimeException();
  218. }
  219. $name = strtolower($name);
  220. $this->plugins[$name] = $plugin;
  221. return $this;
  222. }
  223. /**
  224. * Unregister a named plugin
  225. *
  226. * Removes the plugin instance from the registry, if found.
  227. *
  228. * @param string $name
  229. * @return bool
  230. */
  231. public function unregister($name)
  232. {
  233. $name = strtolower($name);
  234. if (isset($this->plugins[$name])) {
  235. unset($this->plugins[$name]);
  236. return true;
  237. }
  238. return false;
  239. }
  240. /**
  241. * Set class loader to use when resolving plugin names to class names
  242. *
  243. * @param ShortNameLocator $loader
  244. * @return PluginBroker
  245. */
  246. public function setClassLoader(ShortNameLocator $loader)
  247. {
  248. $this->classLoader = $loader;
  249. return $this;
  250. }
  251. /**
  252. * Retrieve the class loader
  253. *
  254. * Lazy-loads an instance of PluginClassLocator if no loader is registered.
  255. *
  256. * @return ShortNameLocator
  257. */
  258. public function getClassLoader()
  259. {
  260. if (null === $this->classLoader) {
  261. $loaderClass = $this->defaultClassLoader;
  262. $this->setClassLoader(new $loaderClass());
  263. }
  264. return $this->classLoader;
  265. }
  266. /**
  267. * Set if plugins should be registered on load.
  268. *
  269. * @param boolean $flag
  270. * @return PluginBroker
  271. */
  272. public function setRegisterPluginsOnLoad($flag)
  273. {
  274. $this->registerPluginsOnLoad = (bool) $flag;
  275. return $this;
  276. }
  277. /**
  278. * Retrieve if plugins are registered on load.
  279. *
  280. * @return boolean
  281. */
  282. public function getRegisterPluginsOnLoad()
  283. {
  284. return $this->registerPluginsOnLoad;
  285. }
  286. /**
  287. * Set plugin validator callback
  288. *
  289. * @param callback $callback
  290. * @return PluginBroker
  291. */
  292. public function setValidator($callback)
  293. {
  294. if (!is_callable($callback)) {
  295. throw new Exception\InvalidArgumentException(sprintf('Validator must be a valid PHP callback; %s provided', gettype($callback)));
  296. }
  297. $this->validator = $callback;
  298. return $this;
  299. }
  300. /**
  301. * Retrieve plugin validator callback
  302. *
  303. * @return null|callback
  304. */
  305. public function getValidator()
  306. {
  307. return $this->validator;
  308. }
  309. /**
  310. * Determine whether we have a valid plugin
  311. *
  312. * Override this method to implement custom validation logic. Typically,
  313. * throw a custom exception for invalid plugins.
  314. *
  315. * @param mixed $plugin
  316. * @return bool
  317. */
  318. protected function validatePlugin($plugin)
  319. {
  320. if (null !== ($validator = $this->getValidator())) {
  321. return call_user_func($validator, $plugin);
  322. }
  323. return true;
  324. }
  325. /**
  326. * Is a value an associative array?
  327. *
  328. * @param mixed $value
  329. * @return bool
  330. */
  331. protected function isAssocArray($value)
  332. {
  333. if (!is_array($value)) {
  334. return false;
  335. }
  336. if (array_keys($value) === range(0, count($value) - 1)) {
  337. return false;
  338. }
  339. return true;
  340. }
  341. }