PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Underscore.php/underscore.php

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