/lib/Zend/Loader/PluginLoader.php

https://github.com/adrienne/Readability · PHP · 464 lines · 252 code · 53 blank · 159 comment · 57 complexity · 7687b63683d74b1c5d57347d16f5e09c 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. * @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. * @return string Class name of loaded class
  299. * @throws Zend_Loader_Exception if class not found
  300. */
  301. public function load($name)
  302. {
  303. $name = $this->_formatName($name);
  304. if ($this->isLoaded($name)) {
  305. return $this->getClassName($name);
  306. }
  307. if ($this->_useStaticRegistry) {
  308. $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
  309. } else {
  310. $registry = $this->_prefixToPaths;
  311. }
  312. $registry = array_reverse($registry, true);
  313. $found = false;
  314. $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
  315. $incFile = self::getIncludeFileCache();
  316. foreach ($registry as $prefix => $paths) {
  317. $className = $prefix . $name;
  318. if (class_exists($className, false)) {
  319. $found = true;
  320. break;
  321. }
  322. $paths = array_reverse($paths, true);
  323. foreach ($paths as $path) {
  324. $loadFile = $path . $classFile;
  325. if (Zend_Loader::isReadable($loadFile)) {
  326. include_once $loadFile;
  327. if (class_exists($className, false)) {
  328. if (null !== $incFile) {
  329. self::_appendIncFile($loadFile);
  330. }
  331. $found = true;
  332. break 2;
  333. }
  334. }
  335. }
  336. }
  337. if (!$found) {
  338. $message = "Plugin by name '$name' was not found in the registry; used paths:";
  339. foreach ($registry as $prefix => $paths) {
  340. $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
  341. }
  342. require_once 'Zend/Loader/PluginLoader/Exception.php';
  343. throw new Zend_Loader_PluginLoader_Exception($message);
  344. }
  345. if ($this->_useStaticRegistry) {
  346. self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name] = $className;
  347. self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = (isset($loadFile) ? $loadFile : '');
  348. } else {
  349. $this->_loadedPlugins[$name] = $className;
  350. $this->_loadedPluginPaths[$name] = (isset($loadFile) ? $loadFile : '');
  351. }
  352. return $className;
  353. }
  354. /**
  355. * Set path to class file cache
  356. *
  357. * Specify a path to a file that will add include_once statements for each
  358. * plugin class loaded. This is an opt-in feature for performance purposes.
  359. *
  360. * @param string $file
  361. * @return void
  362. * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
  363. */
  364. public static function setIncludeFileCache($file)
  365. {
  366. if (null === $file) {
  367. self::$_includeFileCache = null;
  368. return;
  369. }
  370. if (!file_exists($file) && !file_exists(dirname($file))) {
  371. require_once 'Zend/Loader/PluginLoader/Exception.php';
  372. throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
  373. }
  374. if (file_exists($file) && !is_writable($file)) {
  375. require_once 'Zend/Loader/PluginLoader/Exception.php';
  376. throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
  377. }
  378. if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
  379. require_once 'Zend/Loader/PluginLoader/Exception.php';
  380. throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
  381. }
  382. self::$_includeFileCache = $file;
  383. }
  384. /**
  385. * Retrieve class file cache path
  386. *
  387. * @return string|null
  388. */
  389. public static function getIncludeFileCache()
  390. {
  391. return self::$_includeFileCache;
  392. }
  393. /**
  394. * Append an include_once statement to the class file cache
  395. *
  396. * @param string $incFile
  397. * @return void
  398. */
  399. protected static function _appendIncFile($incFile)
  400. {
  401. if (!file_exists(self::$_includeFileCache)) {
  402. $file = '<?php';
  403. } else {
  404. $file = file_get_contents(self::$_includeFileCache);
  405. }
  406. if (!strstr($file, $incFile)) {
  407. $file .= "\ninclude_once '$incFile';";
  408. file_put_contents(self::$_includeFileCache, $file);
  409. }
  410. }
  411. }