PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Loader/PluginLoader.php

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