PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/full/underscore.phpt

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