2
0

PluginLoader.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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. * @subpackage PluginLoader
  18. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /** Zend_Loader_PluginLoader_Interface */
  22. require_once 'Zend/Loader/PluginLoader/Interface.php';
  23. /** Zend_Loader */
  24. require_once 'Zend/Loader.php';
  25. /**
  26. * Generic plugin class loader
  27. *
  28. * @category Zend
  29. * @package Zend_Loader
  30. * @subpackage PluginLoader
  31. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  32. * @license http://framework.zend.com/license/new-bsd New BSD License
  33. */
  34. class Zend_Loader_PluginLoader implements Zend_Loader_PluginLoader_Interface
  35. {
  36. /**
  37. * Class map cache file
  38. * @var string
  39. */
  40. protected static $_includeFileCache;
  41. /**
  42. * Instance loaded plugin paths
  43. *
  44. * @var array
  45. */
  46. protected $_loadedPluginPaths = array();
  47. /**
  48. * Instance loaded plugins
  49. *
  50. * @var array
  51. */
  52. protected $_loadedPlugins = array();
  53. /**
  54. * Instance registry property
  55. *
  56. * @var array
  57. */
  58. protected $_prefixToPaths = array();
  59. /**
  60. * Statically loaded plugin path mappings
  61. *
  62. * @var array
  63. */
  64. protected static $_staticLoadedPluginPaths = array();
  65. /**
  66. * Statically loaded plugins
  67. *
  68. * @var array
  69. */
  70. protected static $_staticLoadedPlugins = array();
  71. /**
  72. * Static registry property
  73. *
  74. * @var array
  75. */
  76. protected static $_staticPrefixToPaths = array();
  77. /**
  78. * Whether to use a statically named registry for loading plugins
  79. *
  80. * @var string|null
  81. */
  82. protected $_useStaticRegistry = null;
  83. /**
  84. * Constructor
  85. *
  86. * @param array $prefixToPaths
  87. * @param string $staticRegistryName OPTIONAL
  88. */
  89. public function __construct(Array $prefixToPaths = array(), $staticRegistryName = null)
  90. {
  91. if (is_string($staticRegistryName) && !empty($staticRegistryName)) {
  92. $this->_useStaticRegistry = $staticRegistryName;
  93. if(!isset(self::$_staticPrefixToPaths[$staticRegistryName])) {
  94. self::$_staticPrefixToPaths[$staticRegistryName] = array();
  95. }
  96. if(!isset(self::$_staticLoadedPlugins[$staticRegistryName])) {
  97. self::$_staticLoadedPlugins[$staticRegistryName] = array();
  98. }
  99. }
  100. foreach ($prefixToPaths as $prefix => $path) {
  101. $this->addPrefixPath($prefix, $path);
  102. }
  103. }
  104. /**
  105. * Format prefix for internal use
  106. *
  107. * @param string $prefix
  108. * @return string
  109. */
  110. protected function _formatPrefix($prefix)
  111. {
  112. return rtrim($prefix, '_') . '_';
  113. }
  114. /**
  115. * Add prefixed paths to the registry of paths
  116. *
  117. * @param string $prefix
  118. * @param string $path
  119. * @return Zend_Loader_PluginLoader
  120. */
  121. public function addPrefixPath($prefix, $path)
  122. {
  123. if (!is_string($prefix) || !is_string($path)) {
  124. require_once 'Zend/Loader/PluginLoader/Exception.php';
  125. throw new Zend_Loader_PluginLoader_Exception('Zend_Loader_PluginLoader::addPrefixPath() method only takes strings for prefix and path.');
  126. }
  127. $prefix = $this->_formatPrefix($prefix);
  128. $path = rtrim($path, '/\\') . '/';
  129. if ($this->_useStaticRegistry) {
  130. self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix][] = $path;
  131. } else {
  132. $this->_prefixToPaths[$prefix][] = $path;
  133. }
  134. return $this;
  135. }
  136. /**
  137. * Get path stack
  138. *
  139. * @param string $prefix
  140. * @return false|array False if prefix does not exist, array otherwise
  141. */
  142. public function getPaths($prefix = null)
  143. {
  144. if ((null !== $prefix) && is_string($prefix)) {
  145. $prefix = $this->_formatPrefix($prefix);
  146. if ($this->_useStaticRegistry) {
  147. if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
  148. return self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix];
  149. }
  150. return false;
  151. }
  152. if (isset($this->_prefixToPaths[$prefix])) {
  153. return $this->_prefixToPaths[$prefix];
  154. }
  155. return false;
  156. }
  157. if ($this->_useStaticRegistry) {
  158. return self::$_staticPrefixToPaths[$this->_useStaticRegistry];
  159. }
  160. return $this->_prefixToPaths;
  161. }
  162. /**
  163. * Clear path stack
  164. *
  165. * @param string $prefix
  166. * @return bool False only if $prefix does not exist
  167. */
  168. public function clearPaths($prefix = null)
  169. {
  170. if ((null !== $prefix) && is_string($prefix)) {
  171. $prefix = $this->_formatPrefix($prefix);
  172. if ($this->_useStaticRegistry) {
  173. if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
  174. unset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix]);
  175. return true;
  176. }
  177. return false;
  178. }
  179. if (isset($this->_prefixToPaths[$prefix])) {
  180. unset($this->_prefixToPaths[$prefix]);
  181. return true;
  182. }
  183. return false;
  184. }
  185. if ($this->_useStaticRegistry) {
  186. self::$_staticPrefixToPaths[$this->_useStaticRegistry] = array();
  187. } else {
  188. $this->_prefixToPaths = array();
  189. }
  190. return true;
  191. }
  192. /**
  193. * Remove a prefix (or prefixed-path) from the registry
  194. *
  195. * @param string $prefix
  196. * @param string $path OPTIONAL
  197. * @return Zend_Loader_PluginLoader
  198. */
  199. public function removePrefixPath($prefix, $path = null)
  200. {
  201. $prefix = $this->_formatPrefix($prefix);
  202. if ($this->_useStaticRegistry) {
  203. $registry =& self::$_staticPrefixToPaths[$this->_useStaticRegistry];
  204. } else {
  205. $registry =& $this->_prefixToPaths;
  206. }
  207. if (!isset($registry[$prefix])) {
  208. require_once 'Zend/Loader/PluginLoader/Exception.php';
  209. throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' was not found in the PluginLoader.');
  210. }
  211. if ($path != null) {
  212. $pos = array_search($path, $registry[$prefix]);
  213. if ($pos === null) {
  214. require_once 'Zend/Loader/PluginLoader/Exception.php';
  215. throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' / Path ' . $path . ' was not found in the PluginLoader.');
  216. }
  217. unset($registry[$prefix][$pos]);
  218. } else {
  219. unset($registry[$prefix]);
  220. }
  221. return $this;
  222. }
  223. /**
  224. * Normalize plugin name
  225. *
  226. * @param string $name
  227. * @return string
  228. */
  229. protected function _formatName($name)
  230. {
  231. return ucfirst((string) $name);
  232. }
  233. /**
  234. * Whether or not a Plugin by a specific name is loaded
  235. *
  236. * @param string $name
  237. * @return Zend_Loader_PluginLoader
  238. */
  239. public function isLoaded($name)
  240. {
  241. $name = $this->_formatName($name);
  242. if ($this->_useStaticRegistry) {
  243. return isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]);
  244. }
  245. return isset($this->_loadedPlugins[$name]);
  246. }
  247. /**
  248. * Return full class name for a named plugin
  249. *
  250. * @param string $name
  251. * @return string|false False if class not found, class name otherwise
  252. */
  253. public function getClassName($name)
  254. {
  255. $name = $this->_formatName($name);
  256. if ($this->_useStaticRegistry
  257. && isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name])
  258. ) {
  259. return self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name];
  260. } elseif (isset($this->_loadedPlugins[$name])) {
  261. return $this->_loadedPlugins[$name];
  262. }
  263. return false;
  264. }
  265. /**
  266. * Get path to plugin class
  267. *
  268. * @param mixed $name
  269. * @return string|false False if not found
  270. */
  271. public function getClassPath($name)
  272. {
  273. $name = $this->_formatName($name);
  274. if ($this->_useStaticRegistry
  275. && !empty(self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name])
  276. ) {
  277. return self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name];
  278. } elseif (!empty($this->_loadedPluginPaths[$name])) {
  279. return $this->_loadedPluginPaths[$name];
  280. }
  281. if ($this->isLoaded($name)) {
  282. $class = $this->getClassName($name);
  283. $r = new ReflectionClass($class);
  284. $path = $r->getFileName();
  285. if ($this->_useStaticRegistry) {
  286. self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = $path;
  287. } else {
  288. $this->_loadedPluginPaths[$name] = $path;
  289. }
  290. return $path;
  291. }
  292. return false;
  293. }
  294. /**
  295. * Load a plugin via the name provided
  296. *
  297. * @param string $name
  298. * @param bool $throwExceptions Whether or not to throw exceptions if the
  299. * class is not resolved
  300. * @return string|false Class name of loaded class; false if $throwExceptions
  301. * if false and no class found
  302. * @throws Zend_Loader_Exception if class not found
  303. */
  304. public function load($name, $throwExceptions = true)
  305. {
  306. $name = $this->_formatName($name);
  307. if ($this->isLoaded($name)) {
  308. return $this->getClassName($name);
  309. }
  310. if ($this->_useStaticRegistry) {
  311. $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
  312. } else {
  313. $registry = $this->_prefixToPaths;
  314. }
  315. $registry = array_reverse($registry, true);
  316. $found = false;
  317. $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
  318. $incFile = self::getIncludeFileCache();
  319. foreach ($registry as $prefix => $paths) {
  320. $className = $prefix . $name;
  321. if (class_exists($className, false)) {
  322. $found = true;
  323. break;
  324. }
  325. $paths = array_reverse($paths, true);
  326. foreach ($paths as $path) {
  327. $loadFile = $path . $classFile;
  328. if (Zend_Loader::isReadable($loadFile)) {
  329. include_once $loadFile;
  330. if (class_exists($className, false)) {
  331. if (null !== $incFile) {
  332. self::_appendIncFile($loadFile);
  333. }
  334. $found = true;
  335. break 2;
  336. }
  337. }
  338. }
  339. }
  340. if (!$found) {
  341. if (!$throwExceptions) {
  342. return false;
  343. }
  344. $message = "Plugin by name '$name' was not found in the registry; used paths:";
  345. foreach ($registry as $prefix => $paths) {
  346. $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
  347. }
  348. require_once 'Zend/Loader/PluginLoader/Exception.php';
  349. throw new Zend_Loader_PluginLoader_Exception($message);
  350. }
  351. if ($this->_useStaticRegistry) {
  352. self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name] = $className;
  353. self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = (isset($loadFile) ? $loadFile : '');
  354. } else {
  355. $this->_loadedPlugins[$name] = $className;
  356. $this->_loadedPluginPaths[$name] = (isset($loadFile) ? $loadFile : '');
  357. }
  358. return $className;
  359. }
  360. /**
  361. * Set path to class file cache
  362. *
  363. * Specify a path to a file that will add include_once statements for each
  364. * plugin class loaded. This is an opt-in feature for performance purposes.
  365. *
  366. * @param string $file
  367. * @return void
  368. * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
  369. */
  370. public static function setIncludeFileCache($file)
  371. {
  372. if (null === $file) {
  373. self::$_includeFileCache = null;
  374. return;
  375. }
  376. if (!file_exists($file) && !file_exists(dirname($file))) {
  377. require_once 'Zend/Loader/PluginLoader/Exception.php';
  378. throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
  379. }
  380. if (file_exists($file) && !is_writable($file)) {
  381. require_once 'Zend/Loader/PluginLoader/Exception.php';
  382. throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
  383. }
  384. if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
  385. require_once 'Zend/Loader/PluginLoader/Exception.php';
  386. throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
  387. }
  388. self::$_includeFileCache = $file;
  389. }
  390. /**
  391. * Retrieve class file cache path
  392. *
  393. * @return string|null
  394. */
  395. public static function getIncludeFileCache()
  396. {
  397. return self::$_includeFileCache;
  398. }
  399. /**
  400. * Append an include_once statement to the class file cache
  401. *
  402. * @param string $incFile
  403. * @return void
  404. */
  405. protected static function _appendIncFile($incFile)
  406. {
  407. if (!file_exists(self::$_includeFileCache)) {
  408. $file = '<?php';
  409. } else {
  410. $file = file_get_contents(self::$_includeFileCache);
  411. }
  412. if (!strstr($file, $incFile)) {
  413. $file .= "\ninclude_once '$incFile';";
  414. file_put_contents(self::$_includeFileCache, $file);
  415. }
  416. }
  417. }