/frameworks/solar/1.1.1/source/solar/Solar/Class/Stack.php

https://github.com/ggunlugu/ornekler · PHP · 353 lines · 120 code · 27 blank · 206 comment · 23 complexity · 88e34a93081e37da32f77347494cf0cc MD5 · raw file

  1. <?php
  2. /**
  3. *
  4. * Stack for loading classes from user-defined hierarchies.
  5. *
  6. * As you add classes to the stack, they are searched-for first when you
  7. * call [[Solar_Class_Stack::load()]].
  8. *
  9. * @category Solar
  10. *
  11. * @package Solar
  12. *
  13. * @author Paul M. Jones <pmjones@solarphp.com>
  14. *
  15. * @license http://opensource.org/licenses/bsd-license.php BSD
  16. *
  17. * @version $Id: Stack.php 4533 2010-04-23 16:35:15Z pmjones $
  18. *
  19. */
  20. class Solar_Class_Stack extends Solar_Base
  21. {
  22. /**
  23. *
  24. * The class stack.
  25. *
  26. * @var array
  27. *
  28. */
  29. protected $_stack = array();
  30. /**
  31. *
  32. * Gets a copy of the current stack.
  33. *
  34. * @return array
  35. *
  36. */
  37. public function get()
  38. {
  39. return $this->_stack;
  40. }
  41. /**
  42. *
  43. * Adds one or more classes to the stack.
  44. *
  45. * {{code: php
  46. *
  47. * // add by array
  48. * $stack = Solar::factory('Solar_Class_Stack');
  49. * $stack->add(array('Base1', 'Base2', 'Base3'));
  50. * // $stack->get() reveals that the class search order will be
  51. * // 'Base1_', 'Base2_', 'Base3_'.
  52. *
  53. * // add by string
  54. * $stack = Solar::factory('Solar_Class_Stack');
  55. * $stack->add('Base1, Base2, Base3');
  56. * // $stack->get() reveals that the class search order will be
  57. * // 'Base1_', 'Base2_', 'Base3_'.
  58. *
  59. * // add incrementally -- N.B. THIS IS A SPECIAL CASE
  60. * $stack = Solar::factory('Solar_Class_Stack');
  61. * $stack->add('Base1');
  62. * $stack->add('Base2');
  63. * $stack->add('Base3');
  64. * // $stack->get() reveals that the directory search order will be
  65. * // 'Base3_', 'Base2_', 'Base1_', because the later adds
  66. * // override the newer ones.
  67. * }}
  68. *
  69. * @param array|string $list The classes to add to the stack.
  70. *
  71. * @return void
  72. *
  73. */
  74. public function add($list)
  75. {
  76. if (is_string($list)) {
  77. $list = explode(',', $list);
  78. }
  79. if (is_array($list)) {
  80. $list = array_reverse($list);
  81. }
  82. foreach ((array) $list as $class) {
  83. $class = trim($class);
  84. if (! $class) {
  85. continue;
  86. }
  87. // trim all trailing _, then add just one _,
  88. // and add to the stack.
  89. $class = rtrim($class, '_') . '_';
  90. array_unshift($this->_stack, $class);
  91. }
  92. }
  93. /**
  94. *
  95. * Given a class or object, add itself and its parents to the stack,
  96. * optionally tracking cross-hierarchy shifts around a base name.
  97. *
  98. * @param string|object $spec The class or object to find parents of.
  99. *
  100. * @param string $base The infix base around which to track cross-
  101. * hierarchy shifts.
  102. *
  103. * @return void
  104. *
  105. */
  106. public function addByParents($spec, $base = null)
  107. {
  108. // get the list of parents; always skip Solar_Base
  109. $parents = Solar_Class::parents($spec, true);
  110. array_shift($parents);
  111. // if not tracking cross-hierarchy shifts, add parents as they are
  112. if (! $base) {
  113. $list = array_reverse($parents);
  114. return $this->add($list);
  115. }
  116. // track cross-hierarchy shifts in class names. any time we change
  117. // "*_Base" prefixes, insert "New_Prefix_Base" into the stack.
  118. $old = null;
  119. foreach ($parents as $class) {
  120. $pos = strpos($class, "_$base");
  121. $new = substr($class, 0, $pos);
  122. // check to see if we crossed vendors or hierarchies
  123. if ($new != $old) {
  124. $cross = "{$new}_{$base}";
  125. $this->add($cross);
  126. } else {
  127. $cross = null;
  128. }
  129. // prevent double-adds where the cross-hierarchy class name ends
  130. // up being the same as the current class name
  131. if ($cross != $class) {
  132. // not the same, add the current class name
  133. $this->add($class);
  134. }
  135. // retain old prefix for next loop
  136. $old = $new;
  137. }
  138. }
  139. /**
  140. *
  141. * Given a class or object, add its vendor and its parent vendors to the
  142. * stack; optionally, add a standard suffix base to the vendor name.
  143. *
  144. * @param string|object $spec The class or object to find vendors of.
  145. *
  146. * @param string $base The suffix base to append to each vendor name.
  147. *
  148. * @return void
  149. *
  150. */
  151. public function addByVendors($spec, $base = null)
  152. {
  153. // get the list of parents; retain Solar_Base
  154. $parents = Solar_Class::parents($spec, true);
  155. // if we have a suffix, put a separator on it
  156. if ($base) {
  157. $base = "_$base";
  158. }
  159. // look through vendor names
  160. $old = null;
  161. foreach ($parents as $class) {
  162. $new = Solar_Class::vendor($class);
  163. if ($new != $old) {
  164. // not the same, add the current vendor name and suffix
  165. $this->add("{$new}{$base}");
  166. }
  167. // retain old vendor for next loop
  168. $old = $new;
  169. }
  170. }
  171. /**
  172. *
  173. * Clears the stack and adds one or more classes.
  174. *
  175. * {{code: php
  176. * $stack = Solar::factory('Solar_Class_Stack');
  177. * $stack->add('Base1');
  178. * $stack->add('Base2');
  179. * $stack->add('Base3');
  180. *
  181. * // $stack->get() reveals that the directory search order is
  182. * // 'Base3_', 'Base2_', 'Base1_'.
  183. *
  184. * $stack->set('Another_Base');
  185. *
  186. * // $stack->get() is now array('Another_Base_').
  187. * }}
  188. *
  189. * @param array|string $list The classes to add to the stack
  190. * after clearing it.
  191. *
  192. * @return void
  193. *
  194. */
  195. public function set($list)
  196. {
  197. $this->_stack = array();
  198. return $this->add($list);
  199. }
  200. /**
  201. *
  202. * Given a class or object, set the stack based on itself and its parents,
  203. * optionally tracking cross-hierarchy shifts around a base name.
  204. *
  205. * @param string|object $spec The class or object to find parents of.
  206. *
  207. * @param string $base The infix base around which to track cross-
  208. * hierarchy shifts.
  209. *
  210. * @return void
  211. *
  212. */
  213. public function setByParents($spec, $base = null)
  214. {
  215. $this->_stack = array();
  216. $this->addByParents($spec, $base);
  217. }
  218. /**
  219. *
  220. * Given a class or object, set the stack based on its vendor and its
  221. * parent vendors; optionally, add a standard suffix base to the vendor
  222. * name.
  223. *
  224. * @param string|object $spec The class or object to find vendors of.
  225. *
  226. * @param string $base The suffix base to add to each vendor name.
  227. *
  228. * @return void
  229. *
  230. */
  231. public function setByVendors($spec, $base = null)
  232. {
  233. $this->_stack = array();
  234. $this->addByVendors($spec, $base);
  235. }
  236. /**
  237. *
  238. * Loads a class using the class stack prefixes.
  239. *
  240. * {{code: php
  241. * $stack = Solar::factory('Solar_Class_Stack');
  242. * $stack->add('Base1');
  243. * $stack->add('Base2');
  244. * $stack->add('Base3');
  245. *
  246. * $class = $stack->load('Name');
  247. * // $class is now the first instance of '*_Name' found from the
  248. * // class stack, looking first for 'Base3_Name', then
  249. * // 'Base2_Name', then finally 'Base1_Name'.
  250. * }}
  251. *
  252. * @param string $name The class to load using the class stack.
  253. *
  254. * @param bool $throw Throw an exception if no matching class is found
  255. * in the stack (default true). When false, returns boolean false if no
  256. * matching class is found.
  257. *
  258. * @return string The full name of the loaded class.
  259. *
  260. * @throws Solar_Exception_ClassNotFound
  261. *
  262. */
  263. public function load($name, $throw = true)
  264. {
  265. // some preliminary checks for valid class names
  266. if (! $name || $name != trim($name) || ! ctype_alpha($name[0])) {
  267. if ($throw) {
  268. throw $this->_exception('ERR_CLASS_NOT_VALID', array(
  269. 'name' => $name,
  270. 'stack' => $this->_stack,
  271. ));
  272. } else {
  273. return false;
  274. }
  275. }
  276. // make sure the name is upper-cased, then loop through the stack
  277. // to find it.
  278. $name = ucfirst($name);
  279. foreach ($this->_stack as $prefix) {
  280. // the full class name
  281. $class = "$prefix$name";
  282. // pre-empt searching.
  283. // don't use autoload.
  284. if (class_exists($class, false)) {
  285. return $class;
  286. }
  287. // the related file
  288. $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
  289. // does the file exist?
  290. if (! Solar_File::exists($file)) {
  291. continue;
  292. }
  293. // include it in a limited scope. we don't use Solar_File::load()
  294. // because we want to avoid exceptions.
  295. $this->_run($file);
  296. // did the class exist within the file?
  297. // don't use autoload.
  298. if (class_exists($class, false)) {
  299. // yes, we're done
  300. return $class;
  301. }
  302. }
  303. // failed to find the class in the stack
  304. if ($throw) {
  305. throw $this->_exception('ERR_CLASS_NOT_FOUND', array(
  306. 'class' => $name,
  307. 'stack' => $this->_stack,
  308. ));
  309. } else {
  310. return false;
  311. }
  312. }
  313. /**
  314. *
  315. * Loads the class file in a limited scope.
  316. *
  317. * @param string The file to include.
  318. *
  319. * @return void
  320. *
  321. */
  322. protected function _run()
  323. {
  324. include func_get_arg(0);
  325. }
  326. }