PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/Underscore.php

https://github.com/michanismus/phalcon-boilerplate
PHP | 931 lines | 601 code | 223 blank | 107 comment | 103 complexity | b0410e290482d7d2d276642c6ca5ff01 MD5 | raw file
  1. <?php
  2. /**
  3. * Underscore.php v1.0
  4. * Copyright (c) 2013 Jonathan Aquino
  5. *
  6. * This is a fork of Brian Haveri's Underscore.php. I have removed the
  7. * object-oriented way of calling the functions, and changed all functions to
  8. * static. This eliminates the E_STRICT warnings under PHP >=5.4.
  9. *
  10. * Underscore.php is licensed under the MIT license
  11. * Underscore.php was inspired by and borrowed from Underscore.js
  12. * For docs, license, tests, and downloads, see: https://github.com/JonathanAquino/Underscore.php
  13. */
  14. // Underscore.php
  15. class __ {
  16. // Invoke the iterator on each item in the collection
  17. public static function each($collection=null, $iterator=null) {
  18. if(is_null($collection)) return null;
  19. $collection = (array) self::_collection($collection);
  20. if(count($collection) === 0) return null;
  21. foreach($collection as $k=>$v) {
  22. call_user_func($iterator, $v, $k, $collection);
  23. }
  24. return null;
  25. }
  26. // Return an array of values by mapping each item through the iterator
  27. // map alias: collect
  28. public static function collect($collection=null, $iterator=null) { return self::map($collection, $iterator); }
  29. public static function map($collection=null, $iterator=null) {
  30. if(is_null($collection)) return array();
  31. $collection = (array) self::_collection($collection);
  32. if(count($collection) === 0) array();
  33. $return = array();
  34. foreach($collection as $k=>$v) {
  35. $return[] = call_user_func($iterator, $v, $k, $collection);
  36. }
  37. return $return;
  38. }
  39. // Reduce a collection to a single value
  40. // reduce aliases: foldl, inject
  41. public static function foldl($collection=null, $iterator=null, $memo=null) { return self::reduce($collection, $iterator, $memo); }
  42. public static function inject($collection=null, $iterator=null, $memo=null) { return self::reduce($collection, $iterator, $memo); }
  43. public static function reduce($collection=null, $iterator=null, $memo=null) {
  44. if(!is_object($collection) && !is_array($collection)) {
  45. if(is_null($memo)) throw new Exception('Invalid object');
  46. else return $memo;
  47. }
  48. return array_reduce($collection, $iterator, $memo);
  49. }
  50. // Right-associative version of reduce
  51. // reduceRight alias: foldr
  52. public static function foldr($collection=null, $iterator=null, $memo=null) { return self::reduceRight($collection, $iterator, $memo); }
  53. public static function reduceRight($collection=null, $iterator=null, $memo=null) {
  54. if(!is_object($collection) && !is_array($collection)) {
  55. if(is_null($memo)) throw new Exception('Invalid object');
  56. else return $memo;
  57. }
  58. krsort($collection);
  59. $__ = new self;
  60. return $__->reduce($collection, $iterator, $memo);
  61. }
  62. // Extract an array of values for a given property
  63. public static function pluck($collection=null, $key=null) {
  64. $collection = (array) self::_collection($collection);
  65. $return = array();
  66. foreach($collection as $item) {
  67. foreach($item as $k=>$v) {
  68. if($k === $key) $return[] = $v;
  69. }
  70. }
  71. return $return;
  72. }
  73. // Does the collection contain this value?
  74. // includ alias: contains
  75. public static function contains($collection=null, $val=null) { return self::includ($collection, $val); }
  76. public static function includ($collection=null, $val=null) {
  77. $collection = (array) self::_collection($collection);
  78. return (array_search($val, $collection, true) !== false);
  79. }
  80. // Invoke the named function over each item in the collection, optionally passing arguments to the function
  81. public static function invoke($collection=null, $function_name=null, $arguments=null) {
  82. $args = func_get_args();
  83. $__ = new self;
  84. list($collection, $function_name) = $__->first($args, 2);
  85. $arguments = $__->rest(func_get_args(), 2);
  86. // If passed an array or string, return an array
  87. // If passed an object, return an object
  88. $is_obj = is_object($collection);
  89. $result = (empty($arguments)) ? array_map($function_name, (array) $collection) : array_map($function_name, (array) $collection, $arguments);
  90. if($is_obj) $result = (object) $result;
  91. return $result;
  92. }
  93. // Does any values in the collection meet the iterator's truth test?
  94. // any alias: some
  95. public static function some($collection=null, $iterator=null) { return self::any($collection, $iterator); }
  96. public static function any($collection=null, $iterator=null) {
  97. $collection = self::_collection($collection);
  98. $__ = new self;
  99. if(!is_null($iterator)) $collection = $__->map($collection, $iterator);
  100. if(count($collection) === 0) return false;
  101. return is_int(array_search(true, $collection, false));
  102. }
  103. // Do all values in the collection meet the iterator's truth test?
  104. // all alias: every
  105. public static function every($collection=null, $iterator=null) { return self::all($collection, $iterator); }
  106. public static function all($collection=null, $iterator=null) {
  107. $collection = self::_collection($collection);
  108. $__ = new self;
  109. if(!is_null($iterator)) $collection = $__->map($collection, $iterator);
  110. $collection = (array) $collection;
  111. if(count($collection) === 0) return true;
  112. return is_bool(array_search(false, $collection, false));
  113. }
  114. // Return an array of values that pass the truth iterator test
  115. // filter alias: select
  116. public static function select($collection=null, $iterator=null) { return self::filter($collection, $iterator); }
  117. public static function filter($collection=null, $iterator=null) {
  118. $collection = self::_collection($collection);
  119. $return = array();
  120. foreach($collection as $val) {
  121. if(call_user_func($iterator, $val)) $return[] = $val;
  122. }
  123. return $return;
  124. }
  125. // Return an array where the items failing the truth test are removed
  126. public static function reject($collection=null, $iterator=null) {
  127. $collection = self::_collection($collection);
  128. $return = array();
  129. foreach($collection as $val) {
  130. if(!call_user_func($iterator, $val)) $return[] = $val;
  131. }
  132. return $return;
  133. }
  134. // Return the value of the first item passing the truth iterator test
  135. // find alias: detect
  136. public static function detect($collection=null, $iterator=null) { return self::find($collection, $iterator); }
  137. public static function find($collection=null, $iterator=null) {
  138. $collection = self::_collection($collection);
  139. foreach($collection as $val) {
  140. if(call_user_func($iterator, $val)) return $val;
  141. }
  142. return false;
  143. }
  144. // How many items are in this collection?
  145. public static function size($collection=null) {
  146. $collection = self::_collection($collection);
  147. return count((array) $collection);
  148. }
  149. // Get the first element of an array. Passing n returns the first n elements.
  150. // first alias: head
  151. public static function head($collection=null, $n=null) { return self::first($collection, $n); }
  152. public static function first($collection=null, $n=null) {
  153. $collection = self::_collection($collection);
  154. if($n === 0) return array();
  155. if(is_null($n)) return current(array_splice($collection, 0, 1, true));
  156. return array_splice($collection, 0, $n, true);
  157. }
  158. // Get the rest of the array elements. Passing n returns from that index onward.
  159. public static function tail($collection=null, $index=null) { return self::rest($collection, $index); }
  160. public static function rest($collection=null, $index=null) {
  161. if(is_null($index)) $index = 1;
  162. $collection = self::_collection($collection);
  163. return array_splice($collection, $index);
  164. }
  165. // Return everything but the last array element. Passing n excludes the last n elements.
  166. public static function initial($collection=null, $n=null) {
  167. $collection = (array) self::_collection($collection);
  168. if(is_null($n)) $n = 1;
  169. $first_index = count($collection) - $n;
  170. $__ = new self;
  171. return $__->first($collection, $first_index);
  172. }
  173. // Get the last element from an array. Passing n returns the last n elements.
  174. public static function last($collection=null, $n=null) {
  175. $collection = self::_collection($collection);
  176. if($n === 0) $result = array();
  177. elseif($n === 1 || is_null($n)) $result = array_pop($collection);
  178. else {
  179. $__ = new self;
  180. $result = $__->rest($collection, -$n);
  181. }
  182. return $result;
  183. }
  184. // Return a copy of the array with falsy values removed
  185. public static function compact($collection=null) {
  186. $collection = self::_collection($collection);
  187. $__ = new self;
  188. return $__->select($collection, function($val) {
  189. return (bool) $val;
  190. });
  191. }
  192. // Flattens a multidimensional array
  193. public static function flatten($collection=null, $shallow=null) {
  194. $collection = self::_collection($collection);
  195. $return = array();
  196. if(count($collection) > 0) {
  197. foreach($collection as $item) {
  198. if(is_array($item)) {
  199. $__ = new self;
  200. $return = array_merge($return, ($shallow) ? $item : $__->flatten($item));
  201. }
  202. else $return[] = $item;
  203. }
  204. }
  205. return $return;
  206. }
  207. // Returns a copy of the array with all instances of val removed
  208. public static function without($collection=null, $val=null) {
  209. $args = func_get_args();
  210. $collection = $args[0];
  211. $collection = self::_collection($collection);
  212. $num_args = count($args);
  213. if($num_args === 1) return $collection;
  214. if(count($collection) === 0) return $collection;
  215. $__ = new self;
  216. $removes = $__->rest($args);
  217. foreach($removes as $remove) {
  218. $remove_keys = array_keys($collection, $remove, true);
  219. if(count($remove_keys) > 0) {
  220. foreach($remove_keys as $key) {
  221. unset($collection[$key]);
  222. }
  223. }
  224. }
  225. return $collection;
  226. }
  227. // Return an array of the unique values
  228. // uniq alias: unique
  229. public static function unique($collection=null, $is_sorted=null, $iterator=null) { return self::uniq($collection, $is_sorted, $iterator); }
  230. public static function uniq($collection=null, $is_sorted=null, $iterator=null) {
  231. $collection = self::_collection($collection);
  232. $return = array();
  233. if(count($collection) === 0) return $return;
  234. $calculated = array();
  235. foreach($collection as $item) {
  236. $val = (!is_null($iterator)) ? $iterator($item) : $item;
  237. if(is_bool(array_search($val, $calculated, true))) {
  238. $calculated[] = $val;
  239. $return[] = $item;
  240. }
  241. }
  242. return $return;
  243. }
  244. // Returns an array containing the intersection of all the arrays
  245. public static function intersection($array=null) {
  246. $arrays = func_get_args();
  247. if(count($arrays) === 1) return $array;
  248. $__ = new self;
  249. $return = $__->first($arrays);
  250. foreach($__->rest($arrays) as $next) {
  251. if(!$__->isArray($next)) $next = str_split((string) $next);
  252. $return = array_intersect($return, $next);
  253. }
  254. return array_values($return);
  255. }
  256. // Merge together multiple arrays
  257. public static function union($array=null) {
  258. $arrays = func_get_args();
  259. $__ = new self;
  260. return $__->flatten(array_values(array_unique(call_user_func_array('array_merge', $arrays))));
  261. }
  262. // Get the difference between two arrays
  263. public static function difference($array_one=null, $array_two=null) {
  264. $arrays = func_get_args();
  265. return array_values(call_user_func_array('array_diff', $arrays));
  266. }
  267. // Get the index of the first match
  268. public static function indexOf($collection=null, $item=null) {
  269. $collection = self::_collection($collection);
  270. $key = array_search($item, $collection, true);
  271. return (is_bool($key)) ? -1 : $key;
  272. }
  273. // Get the index of the last match
  274. public static function lastIndexOf($collection=null, $item=null) {
  275. $collection = self::_collection($collection);
  276. krsort($collection);
  277. $__ = new self;
  278. return $__->indexOf($collection, $item);
  279. }
  280. // Returns an array of integers from start to stop (exclusive) by step
  281. public static function range($stop=null) {
  282. $args = func_get_args();
  283. $__ = new self;
  284. $args = $__->reject($args, function($val) {
  285. return is_null($val);
  286. });
  287. $num_args = count($args);
  288. switch($num_args) {
  289. case 1:
  290. list($start, $stop, $step) = array(0, $args[0], 1);
  291. break;
  292. case 2:
  293. list($start, $stop, $step) = array($args[0], $args[1], 1);
  294. if($stop < $start) return array();
  295. break;
  296. default:
  297. list($start, $stop, $step) = array($args[0], $args[1], $args[2]);
  298. if($step > 0 && $step > $stop) return array($start);
  299. }
  300. $results = range($start, $stop, $step);
  301. // Switch inclusive to exclusive
  302. if($step > 0 && $__->last($results) >= $stop) array_pop($results);
  303. elseif($step < 0 && $__->last($results) <= $stop) array_pop($results);
  304. return $results;
  305. }
  306. // Merges arrays
  307. public static function zip($array=null) {
  308. $arrays = func_get_args();
  309. $num_arrays = count($arrays);
  310. if($num_arrays === 1) return $array;
  311. $__ = new self;
  312. $num_return_arrays = $__->max($__->map($arrays, function($array) {
  313. return count($array);
  314. }));
  315. $return_arrays = $__->range($num_return_arrays);
  316. foreach($return_arrays as $k=>$v) {
  317. if(!is_array($return_arrays[$k])) $return_arrays[$k] = array();
  318. foreach($arrays as $a=>$array) {
  319. $return_arrays[$k][$a] = array_key_exists($k, $array) ? $array[$k] : null;
  320. }
  321. }
  322. return $return_arrays;
  323. }
  324. // Get the max value in the collection
  325. public static function max($collection=null, $iterator=null) {
  326. if(is_null($iterator)) return max($collection);
  327. $results = array();
  328. foreach($collection as $k=>$item) {
  329. $results[$k] = $iterator($item);
  330. }
  331. arsort($results);
  332. $__ = new self;
  333. $first_key = $__->first(array_keys($results));
  334. return $collection[$first_key];
  335. }
  336. // Get the min value in the collection
  337. public static function min($collection=null, $iterator=null) {
  338. if(is_null($iterator)) return min($collection);
  339. $results = array();
  340. foreach($collection as $k=>$item) {
  341. $results[$k] = $iterator($item);
  342. }
  343. asort($results);
  344. $__ = new self;
  345. $first_key = $__->first(array_keys($results));
  346. return $collection[$first_key];
  347. }
  348. // Sort the collection by return values from the iterator
  349. public static function sortBy($collection=null, $iterator=null) {
  350. $results = array();
  351. foreach($collection as $k=>$item) {
  352. $results[$k] = $iterator($item);
  353. }
  354. asort($results);
  355. foreach($results as $k=>$v) {
  356. $results[$k] = $collection[$k];
  357. }
  358. return array_values($results);
  359. }
  360. // Group the collection by return values from the iterator
  361. public static function groupBy($collection=null, $iterator=null) {
  362. $result = array();
  363. $collection = (array) $collection;
  364. foreach($collection as $k=>$v) {
  365. $key = (is_callable($iterator)) ? $iterator($v, $k) : $v[$iterator];
  366. if(!array_key_exists($key, $result)) $result[$key] = array();
  367. $result[$key][] = $v;
  368. }
  369. return $result;
  370. }
  371. // Returns the index at which the value should be inserted into the sorted collection
  372. public static function sortedIndex($collection=null, $value=null, $iterator=null) {
  373. $collection = (array) self::_collection($collection);
  374. $__ = new self;
  375. $calculated_value = (!is_null($iterator)) ? $iterator($value) : $value;
  376. while(count($collection) > 1) {
  377. $midpoint = floor(count($collection) / 2);
  378. $midpoint_values = array_slice($collection, $midpoint, 1);
  379. $midpoint_value = $midpoint_values[0];
  380. $midpoint_calculated_value = (!is_null($iterator)) ? $iterator($midpoint_value) : $midpoint_value;
  381. $collection = ($calculated_value < $midpoint_calculated_value) ? array_slice($collection, 0, $midpoint, true) : array_slice($collection, $midpoint, null, true);
  382. }
  383. $keys = array_keys($collection);
  384. return current($keys) + 1;
  385. }
  386. // Shuffle the array
  387. public static function shuffle($collection=null) {
  388. $collection = (array) self::_collection($collection);
  389. shuffle($collection);
  390. return $collection;
  391. }
  392. // Return the collection as an array
  393. public static function toArray($collection=null) {
  394. return (array) $collection;
  395. }
  396. // Get the collection's keys
  397. public static function keys($collection=null) {
  398. if(!is_object($collection) && !is_array($collection)) throw new Exception('Invalid object');
  399. return array_keys((array) $collection);
  400. }
  401. // Get the collection's values
  402. public static function values($collection=null) {
  403. return array_values((array) $collection);
  404. }
  405. // Copy all properties from the source objects into the destination object
  406. public static function extend($object=null) {
  407. $args = func_get_args();
  408. $num_args = func_num_args();
  409. if($num_args === 1) return $object;
  410. $is_object = is_object($object);
  411. $array = (array) $object;
  412. $__ = new self;
  413. $extensions = $__->rest(func_get_args());
  414. foreach($extensions as $extension) {
  415. $extension = (array) $extension;
  416. $array = array_merge($array, $extension);
  417. }
  418. return ($is_object) ? (object) $array : $array;
  419. }
  420. // Returns the object with any missing values filled in using the defaults.
  421. public static function defaults($object=null) {
  422. $args = func_get_args();
  423. list($object) = $args;
  424. $num_args = count($args);
  425. if($num_args === 1) return $object;
  426. $is_object = is_object($object);
  427. $array = (array) $object;
  428. $__ = new self;
  429. $extensions = $__->rest($args);
  430. foreach($extensions as $extension) {
  431. $extension = (array) $extension;
  432. $array = array_merge($extension, $array);
  433. }
  434. return ($is_object) ? (object) $array : $array;
  435. }
  436. // Get the names of functions available to the object
  437. // functions alias: methods
  438. public static function methods($object=null) { return self::functions($object); }
  439. public static function functions($object=null) {
  440. return get_class_methods(get_class($object));
  441. }
  442. // Returns a shallow copy of the object
  443. public static function clon(&$object=null) {
  444. $clone = null;
  445. if(is_array($object)) $clone = (array) clone (object) $object;
  446. elseif(!is_object($object)) $clone = $object;
  447. elseif(!$clone) $clone = clone $object;
  448. // shallow copy object
  449. if(is_object($clone) && count($clone) > 0) {
  450. foreach($clone as $k=>$v) {
  451. if(is_array($v) || is_object($v)) $clone->$k =& $object->$k;
  452. }
  453. }
  454. // shallow copy array
  455. elseif(is_array($clone) && count($clone) > 0) {
  456. foreach($clone as $k=>$v) {
  457. if(is_array($v) || is_object($v)) $clone[$k] =& $object[$k];
  458. }
  459. }
  460. return $clone;
  461. }
  462. // Invokes the interceptor on the object, then returns the object
  463. public static function tap($object=null, $interceptor=null) {
  464. $interceptor($object);
  465. return $object;
  466. }
  467. // Does the given key exist?
  468. public static function has($collection=null, $key=null) {
  469. $collection = (array) self::_collection($collection);
  470. return array_key_exists($key, $collection);
  471. }
  472. // Are these items equal?
  473. public static function isEqual($a=null, $b=null) {
  474. if($a === $b) return true;
  475. if(gettype($a) !== gettype($b)) return false;
  476. if(is_callable($a) !== is_callable($b)) return false;
  477. if($a == $b) return true;
  478. // Objects and arrays compared by values
  479. if(is_object($a) || is_array($a)) {
  480. // Do either implement isEqual()?
  481. if(is_object($a) && isset($a->isEqual)) return $a->isEqual($b);
  482. if(is_object($b) && isset($b->isEqual)) return $b->isEqual($a);
  483. if(is_array($a) && array_key_exists('isEqual', $a)) return $a['isEqual']($b);
  484. if(is_array($b) && array_key_exists('isEqual', $b)) return $b['isEqual']($a);
  485. if(count($a) !== count($b)) return false;
  486. $__ = new self;
  487. $keys_equal = $__->isEqual($__->keys($a), $__->keys($b));
  488. $values_equal = $__->isEqual($__->values($a), $__->values($b));
  489. return $keys_equal && $values_equal;
  490. }
  491. return false;
  492. }
  493. // Is this item empty?
  494. public static function isEmpty($item=null) {
  495. return is_array($item) || is_object($item) ? !((bool) count((array) $item)) : (!(bool) $item);
  496. }
  497. // Is this item an object?
  498. public static function isObject($item=null) {
  499. return is_object($item);
  500. }
  501. // Is this item an array?
  502. public static function isArray($item=null) {
  503. return is_array($item);
  504. }
  505. // Is this item a string?
  506. public static function isString($item=null) {
  507. return is_string($item);
  508. }
  509. // Is this item a number?
  510. public static function isNumber($item=null) {
  511. return (is_int($item) || is_float($item) && !is_nan($item) && !is_infinite($item));
  512. }
  513. // Is this item a bool?
  514. public static function isBoolean($item=null) {
  515. return is_bool($item);
  516. }
  517. // Is this item a function (by type, not by name)?
  518. public static function isFunction($item=null) {
  519. return is_object($item) && is_callable($item);
  520. }
  521. // Is this item an instance of DateTime?
  522. public static function isDate($item=null) {
  523. return is_object($item) && get_class($item) === 'DateTime';
  524. }
  525. // Is this item a NaN value?
  526. public static function isNaN($item=null) {
  527. return is_nan($item);
  528. }
  529. // Returns the same value passed as the argument
  530. public static function identity() {
  531. $args = func_get_args();
  532. if(count($args) > 0) return $args[0];
  533. return function($x) {
  534. return $x;
  535. };
  536. }
  537. // Generate a globally unique id, optionally prefixed
  538. public static $_uniqueId = -1;
  539. public static function uniqueId($prefix=null) {
  540. self::$_uniqueId++;
  541. return (is_null($prefix)) ? self::$_uniqueId : $prefix . self::$_uniqueId;
  542. }
  543. // Invokes the iterator n times
  544. public static function times($n=null, $iterator=null) {
  545. if(is_null($n)) $n = 0;
  546. for($i=0; $i<$n; $i++) $iterator($i);
  547. return null;
  548. }
  549. // Temporary PHP open and close tags used within templates
  550. // Allows for normal processing of templates even when
  551. // the developer uses PHP open or close tags for interpolation or evaluation
  552. const TEMPLATE_OPEN_TAG = '760e7dab2836853c63805033e514668301fa9c47';
  553. const TEMPLATE_CLOSE_TAG= 'd228a8fa36bd7db108b01eddfb03a30899987a2b';
  554. const TEMPLATE_DEFAULT_EVALUATE = '/<%([\s\S]+?)%>/';
  555. const TEMPLATE_DEFAULT_INTERPOLATE= '/<%=([\s\S]+?)%>/';
  556. const TEMPLATE_DEFAULT_ESCAPE = '/<%-([\s\S]+?)%>/';
  557. public static $_template_settings = array(
  558. 'evaluate' => self::TEMPLATE_DEFAULT_EVALUATE,
  559. 'interpolate' => self::TEMPLATE_DEFAULT_INTERPOLATE,
  560. 'escape' => self::TEMPLATE_DEFAULT_ESCAPE
  561. );
  562. // Set template settings
  563. public static function templateSettings($settings=null) {
  564. $_template_settings =& self::$_template_settings;
  565. if(is_null($settings)) {
  566. $_template_settings = array(
  567. 'evaluate' => self::TEMPLATE_DEFAULT_EVALUATE,
  568. 'interpolate' => self::TEMPLATE_DEFAULT_INTERPOLATE,
  569. 'escape' => self::TEMPLATE_DEFAULT_ESCAPE
  570. );
  571. return true;
  572. }
  573. foreach($settings as $k=>$v) {
  574. if(!array_key_exists($k, $_template_settings)) continue;
  575. $_template_settings[$k] = $v;
  576. }
  577. return true;
  578. }
  579. // Compile templates into functions that can be evaluated for rendering
  580. public static function template($code=null, $context=null) {
  581. $class_name = __CLASS__;
  582. $return = function($context=null) use ($code, $class_name) {
  583. $ts = $class_name::$_template_settings;
  584. // Wrap escaped, interpolated, and evaluated blocks inside PHP tags
  585. extract((array) $context);
  586. preg_match_all($ts['escape'], $code, $vars, PREG_SET_ORDER);
  587. if(count($vars) > 0) {
  588. foreach($vars as $var) {
  589. $echo = $class_name::TEMPLATE_OPEN_TAG . ' echo htmlentities(' . trim($var[1]) . '); ' . $class_name::TEMPLATE_CLOSE_TAG;
  590. $code = str_replace($var[0], $echo, $code);
  591. }
  592. }
  593. preg_match_all($ts['interpolate'], $code, $vars, PREG_SET_ORDER);
  594. if(count($vars) > 0) {
  595. foreach($vars as $var) {
  596. $echo = $class_name::TEMPLATE_OPEN_TAG . ' echo ' . trim($var[1]) . '; ' . $class_name::TEMPLATE_CLOSE_TAG;
  597. $code = str_replace($var[0], $echo, $code);
  598. }
  599. }
  600. preg_match_all($ts['evaluate'], $code, $vars, PREG_SET_ORDER);
  601. if(count($vars) > 0) {
  602. foreach($vars as $var) {
  603. $echo = $class_name::TEMPLATE_OPEN_TAG . trim($var[1]) . $class_name::TEMPLATE_CLOSE_TAG;
  604. $code = str_replace($var[0], $echo, $code);
  605. }
  606. }
  607. $code = str_replace($class_name::TEMPLATE_OPEN_TAG, '<?php ', $code);
  608. $code = str_replace($class_name::TEMPLATE_CLOSE_TAG, '?>', $code);
  609. // Use the output buffer to grab the return value
  610. $code = 'ob_start(); extract($context); ?>' . $code . '<?php return ob_get_clean();';
  611. $func = create_function('$context', $code);
  612. return $func((array) $context);
  613. };
  614. // Return function or call function depending on context
  615. return ((isset($this) && isset($this->_wrapped) && $this->_wrapped) || !is_null($context) ? $return($context) : $return);
  616. }
  617. // Escape
  618. public static function escape($item=null) {
  619. return htmlentities($item);
  620. }
  621. // Memoizes a function by caching the computed result.
  622. public static $_memoized = array();
  623. public static function memoize($function=null, $hashFunction=null) {
  624. $class_name = __CLASS__;
  625. return function() use ($function, $class_name, $hashFunction) {
  626. // Generate a key based on hashFunction
  627. $args = func_get_args();
  628. if(is_null($hashFunction)) $hashFunction = function($function, $args) {
  629. // Try using var_export to identify the function
  630. return md5(join('_', array(
  631. var_export($function, true),
  632. var_export($args, true)
  633. )));
  634. };
  635. $key = $hashFunction($function, $args);
  636. if(!array_key_exists($key, $class_name::$_memoized)) {
  637. $class_name::$_memoized[$key] = call_user_func_array($function, $args);
  638. }
  639. return $class_name::$_memoized[$key];
  640. };
  641. }
  642. // Throttles a function so that it can only be called once every wait milliseconds
  643. public static $_throttled = array();
  644. public static function throttle($function=null, $wait=null) {
  645. $class_name = __CLASS__;
  646. return function() use ($function, $wait, $class_name) {
  647. // Try using var_export to identify the function
  648. $key = md5(join('', array(
  649. var_export($function, true),
  650. $wait
  651. )));
  652. $microtime = microtime(true);
  653. $ready_to_call = (!array_key_exists($key, $class_name::$_throttled) || $microtime >= $class_name::$_throttled[$key]);
  654. if($ready_to_call) {
  655. $next_callable_time = $microtime + ($wait / 1000);
  656. $class_name::$_throttled[$key] = $next_callable_time;
  657. return call_user_func_array($function, func_get_args());
  658. }
  659. };
  660. }
  661. // Creates a version of the function that can only be called once
  662. public static $_onced = array();
  663. public static function once($function=null) {
  664. $class_name = __CLASS__;
  665. return function() use ($function, $class_name) {
  666. // Try using var_export to identify the function
  667. $key = md5(var_export($function, true));
  668. if(!array_key_exists($key, $class_name::$_onced)) {
  669. $class_name::$_onced[$key] = call_user_func_array($function, func_get_args());
  670. }
  671. return $class_name::$_onced[$key];
  672. };
  673. }
  674. // Wraps the function inside the wrapper function, passing it as the first argument
  675. public static function wrap($function=null, $wrapper=null) {
  676. return function() use ($wrapper, $function) {
  677. $args = array_merge(array($function), func_get_args());
  678. return call_user_func_array($wrapper, $args);
  679. };
  680. }
  681. // Returns the composition of the functions
  682. public static function compose() {
  683. $functions = func_get_args();
  684. return function() use ($functions) {
  685. $args = func_get_args();
  686. foreach($functions as $function) {
  687. $args[0] = call_user_func_array($function, $args);
  688. }
  689. return $args[0];
  690. };
  691. }
  692. // Creates a version of the function that will only run after being called count times
  693. public static $_aftered = array();
  694. public static function after($count=null, $function=null) {
  695. $class_name = __CLASS__;
  696. $key = md5(mt_rand());
  697. $func = function() use ($function, $class_name, $count, $key) {
  698. if(!array_key_exists($key, $class_name::$_aftered)) $class_name::$_aftered[$key] = 0;
  699. $class_name::$_aftered[$key] += 1;
  700. if($class_name::$_aftered[$key] >= $count) return call_user_func_array($function, func_get_args());
  701. };
  702. return ($count) ? $func : $func();
  703. }
  704. // Get a collection in a way that supports strings
  705. private static function _collection($collection) {
  706. return (!is_array($collection) && !is_object($collection)) ? str_split((string) $collection) : $collection;
  707. }
  708. }