PageRenderTime 46ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/kotal/vendor/phptal/PHPTAL/RepeatController.php

https://bitbucket.org/chrispiechowicz/zepto
PHP | 321 lines | 187 code | 32 blank | 102 comment | 17 complexity | 3721a3b91c394ff44921ed26a4bfb101 MD5 | raw file
Possible License(s): LGPL-2.1, MIT, BSD-3-Clause
  1. <?php
  2. /**
  3. * PHPTAL templating engine
  4. *
  5. * PHP Version 5
  6. *
  7. * @category HTML
  8. * @package PHPTAL
  9. * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
  10. * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk>
  11. * @author IvĂĄn Montes <drslump@pollinimini.net>
  12. * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
  13. * @version SVN: $Id$
  14. * @link http://phptal.org/
  15. */
  16. /**
  17. * Stores tal:repeat information during template execution.
  18. *
  19. * An instance of this class is created and stored into PHPTAL context on each
  20. * tal:repeat usage.
  21. *
  22. * repeat/item/index
  23. * repeat/item/number
  24. * ...
  25. * are provided by this instance.
  26. *
  27. * 'repeat' is an stdClass instance created to handle RepeatControllers,
  28. * 'item' is an instance of this class.
  29. *
  30. * @package PHPTAL
  31. * @subpackage Php
  32. * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
  33. */
  34. class PHPTAL_RepeatController implements Iterator
  35. {
  36. public $key;
  37. private $current;
  38. private $valid;
  39. private $validOnNext;
  40. private $uses_groups = false;
  41. protected $iterator;
  42. public $index;
  43. public $end;
  44. /**
  45. * computed lazily
  46. */
  47. private $length = null;
  48. /**
  49. * Construct a new RepeatController.
  50. *
  51. * @param $source array, string, iterator, iterable.
  52. */
  53. public function __construct($source)
  54. {
  55. if ( is_string($source) ) {
  56. $this->iterator = new ArrayIterator( str_split($source) ); // FIXME: invalid for UTF-8 encoding, use preg_match_all('/./u') trick
  57. } elseif ( is_array($source) ) {
  58. $this->iterator = new ArrayIterator($source);
  59. } elseif ($source instanceof IteratorAggregate) {
  60. $this->iterator = $source->getIterator();
  61. } elseif ($source instanceof DOMNodeList) {
  62. $array = array();
  63. foreach ($source as $k=>$v) {
  64. $array[$k] = $v;
  65. }
  66. $this->iterator = new ArrayIterator($array);
  67. } elseif ($source instanceof Iterator) {
  68. $this->iterator = $source;
  69. } elseif ($source instanceof Traversable) {
  70. $this->iterator = new IteratorIterator($source);
  71. } elseif ($source instanceof stdClass) {
  72. $this->iterator = new ArrayIterator( (array) $source );
  73. } else {
  74. $this->iterator = new ArrayIterator( array() );
  75. }
  76. }
  77. /**
  78. * Returns the current element value in the iteration
  79. *
  80. * @return Mixed The current element value
  81. */
  82. public function current()
  83. {
  84. return $this->current;
  85. }
  86. /**
  87. * Returns the current element key in the iteration
  88. *
  89. * @return String/Int The current element key
  90. */
  91. public function key()
  92. {
  93. return $this->key;
  94. }
  95. /**
  96. * Tells if the iteration is over
  97. *
  98. * @return bool True if the iteration is not finished yet
  99. */
  100. public function valid()
  101. {
  102. $valid = $this->valid || $this->validOnNext;
  103. $this->validOnNext = $this->valid;
  104. return $valid;
  105. }
  106. public function length()
  107. {
  108. if ($this->length === null) {
  109. if ($this->iterator instanceof Countable) {
  110. return $this->length = count($this->iterator);
  111. } elseif ( is_object($this->iterator) ) {
  112. // for backwards compatibility with existing PHPTAL templates
  113. if ( method_exists($this->iterator, 'size') ) {
  114. return $this->length = $this->iterator->size();
  115. } elseif ( method_exists($this->iterator, 'length') ) {
  116. return $this->length = $this->iterator->length();
  117. }
  118. }
  119. $this->length = '_PHPTAL_LENGTH_UNKNOWN_';
  120. }
  121. if ($this->length === '_PHPTAL_LENGTH_UNKNOWN_') // return length if end is discovered
  122. {
  123. return $this->end ? $this->index + 1 : null;
  124. }
  125. return $this->length;
  126. }
  127. /**
  128. * Restarts the iteration process going back to the first element
  129. *
  130. */
  131. public function rewind()
  132. {
  133. $this->index = 0;
  134. $this->length = null;
  135. $this->end = false;
  136. $this->iterator->rewind();
  137. // Prefetch the next element
  138. if ($this->iterator->valid()) {
  139. $this->validOnNext = true;
  140. $this->prefetch();
  141. } else {
  142. $this->validOnNext = false;
  143. }
  144. if ($this->uses_groups) {
  145. // Notify the grouping helper of the change
  146. $this->groups->reset();
  147. }
  148. }
  149. /**
  150. * Fetches the next element in the iteration and advances the pointer
  151. *
  152. */
  153. public function next()
  154. {
  155. $this->index++;
  156. // Prefetch the next element
  157. if ($this->validOnNext) $this->prefetch();
  158. if ($this->uses_groups) {
  159. // Notify the grouping helper of the change
  160. $this->groups->reset();
  161. }
  162. }
  163. /**
  164. * Ensures that $this->groups works.
  165. *
  166. * Groups are rarely-used feature, which is why they're lazily loaded.
  167. */
  168. private function initializeGroups()
  169. {
  170. if (!$this->uses_groups) {
  171. $this->groups = new PHPTAL_RepeatControllerGroups();
  172. $this->uses_groups = true;
  173. }
  174. }
  175. /**
  176. * Gets an object property
  177. *
  178. * @return $var Mixed The variable value
  179. */
  180. public function __get($var)
  181. {
  182. switch ($var) {
  183. case 'number':
  184. return $this->index + 1;
  185. case 'start':
  186. return $this->index === 0;
  187. case 'even':
  188. return ($this->index % 2) === 0;
  189. case 'odd':
  190. return ($this->index % 2) === 1;
  191. case 'length':
  192. return $this->length();
  193. case 'letter':
  194. return strtolower( $this->int2letter($this->index+1) );
  195. case 'Letter':
  196. return strtoupper( $this->int2letter($this->index+1) );
  197. case 'roman':
  198. return strtolower( $this->int2roman($this->index+1) );
  199. case 'Roman':
  200. return strtoupper( $this->int2roman($this->index+1) );
  201. case 'groups':
  202. $this->initializeGroups();
  203. return $this->groups;
  204. case 'first':
  205. $this->initializeGroups();
  206. // Compare the current one with the previous in the dictionary
  207. $res = $this->groups->first($this->current);
  208. return is_bool($res) ? $res : $this->groups;
  209. case 'last':
  210. $this->initializeGroups();
  211. // Compare the next one with the dictionary
  212. $res = $this->groups->last( $this->iterator->current() );
  213. return is_bool($res) ? $res : $this->groups;
  214. default:
  215. throw new PHPTAL_VariableNotFoundException("Unable to find part '$var' in repeat variable");
  216. }
  217. }
  218. /**
  219. * Fetches the next element from the source data store and
  220. * updates the end flag if needed.
  221. *
  222. * @access protected
  223. */
  224. protected function prefetch()
  225. {
  226. $this->valid = true;
  227. $this->current = $this->iterator->current();
  228. $this->key = $this->iterator->key();
  229. $this->iterator->next();
  230. if ( !$this->iterator->valid() ) {
  231. $this->valid = false;
  232. $this->end = true;
  233. }
  234. }
  235. /**
  236. * Converts an integer number (1 based) to a sequence of letters
  237. *
  238. * @param int $int The number to convert
  239. *
  240. * @return String The letters equivalent as a, b, c-z ... aa, ab, ac-zz ...
  241. * @access protected
  242. */
  243. protected function int2letter($int)
  244. {
  245. $lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  246. $size = strlen($lookup);
  247. $letters = '';
  248. while ($int > 0) {
  249. $int--;
  250. $letters = $lookup[$int % $size] . $letters;
  251. $int = floor($int / $size);
  252. }
  253. return $letters;
  254. }
  255. /**
  256. * Converts an integer number (1 based) to a roman numeral
  257. *
  258. * @param int $int The number to convert
  259. *
  260. * @return String The roman numeral
  261. * @access protected
  262. */
  263. protected function int2roman($int)
  264. {
  265. $lookup = array(
  266. '1000' => 'M',
  267. '900' => 'CM',
  268. '500' => 'D',
  269. '400' => 'CD',
  270. '100' => 'C',
  271. '90' => 'XC',
  272. '50' => 'L',
  273. '40' => 'XL',
  274. '10' => 'X',
  275. '9' => 'IX',
  276. '5' => 'V',
  277. '4' => 'IV',
  278. '1' => 'I',
  279. );
  280. $roman = '';
  281. foreach ($lookup as $max => $letters) {
  282. while ($int >= $max) {
  283. $roman .= $letters;
  284. $int -= $max;
  285. }
  286. }
  287. return $roman;
  288. }
  289. }