Created
December 24, 2013 01:03
-
-
Save mingyun/8107308 to your computer and use it in GitHub Desktop.
源自undersocre.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Underscore.php v1.3.1 | |
* Copyright (c) 2011 Brian Haveri | |
* Underscore.php is licensed under the MIT license | |
* Underscore.php was inspired by and borrowed from Underscore.js | |
* For docs, license, tests, and downloads, see: http://brianhaveri.github.com/Underscore.php | |
*/ | |
// Returns an instance of __ for OO-style calls | |
function __($item=null) { | |
$__ = new __; | |
if(func_num_args() > 0) $__->_wrapped = $item; | |
return $__; | |
} | |
// Underscore.php | |
class __ { | |
// Start the chain | |
private $_chained = false; // Are we in a chain? | |
public function chain($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
$__ = (isset($this) && isset($this->_chained) && $this->_chained) ? $this : __($item); | |
$__->_chained = true; | |
return $__; | |
} | |
// End the chain | |
public function value() { | |
return (isset($this)) ? $this->_wrapped : null; | |
} | |
// Invoke the iterator on each item in the collection | |
public function each($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($collection)) return self::_wrap(null); | |
$collection = (array) self::_collection($collection); | |
if(count($collection) === 0) return self::_wrap(null); | |
foreach($collection as $k=>$v) { | |
call_user_func($iterator, $v, $k, $collection); | |
} | |
return self::_wrap(null); | |
} | |
// Return an array of values by mapping each item through the iterator | |
// map alias: collect | |
public function collect($collection=null, $iterator=null) { return self::map($collection, $iterator); } | |
public function map($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($collection)) return self::_wrap(array()); | |
$collection = (array) self::_collection($collection); | |
if(count($collection) === 0) self::_wrap(array()); | |
$return = array(); | |
foreach($collection as $k=>$v) { | |
$return[] = call_user_func($iterator, $v, $k, $collection); | |
} | |
return self::_wrap($return); | |
} | |
// Reduce a collection to a single value | |
// reduce aliases: foldl, inject | |
public function foldl($collection=null, $iterator=null, $memo=null) { return self::reduce($collection, $iterator, $memo); } | |
public function inject($collection=null, $iterator=null, $memo=null) { return self::reduce($collection, $iterator, $memo); } | |
public function reduce($collection=null, $iterator=null, $memo=null) { | |
list($collection, $iterator, $memo) = self::_wrapArgs(func_get_args(), 3); | |
if(!is_object($collection) && !is_array($collection)) { | |
if(is_null($memo)) throw new Exception('Invalid object'); | |
else return self::_wrap($memo); | |
} | |
return self::_wrap(array_reduce($collection, $iterator, $memo)); | |
} | |
// Right-associative version of reduce | |
// reduceRight alias: foldr | |
public function foldr($collection=null, $iterator=null, $memo=null) { return self::reduceRight($collection, $iterator, $memo); } | |
public function reduceRight($collection=null, $iterator=null, $memo=null) { | |
list($collection, $iterator, $memo) = self::_wrapArgs(func_get_args(), 3); | |
if(!is_object($collection) && !is_array($collection)) { | |
if(is_null($memo)) throw new Exception('Invalid object'); | |
else return self::_wrap($memo); | |
} | |
krsort($collection); | |
$__ = new self; | |
return self::_wrap($__->reduce($collection, $iterator, $memo)); | |
} | |
// Extract an array of values for a given property | |
public function pluck($collection=null, $key=null) { | |
list($collection, $key) = self::_wrapArgs(func_get_args(), 2); | |
$collection = (array) self::_collection($collection); | |
$return = array(); | |
foreach($collection as $item) { | |
foreach($item as $k=>$v) { | |
if($k === $key) $return[] = $v; | |
} | |
} | |
return self::_wrap($return); | |
} | |
// Does the collection contain this value? | |
// includ alias: contains | |
public function contains($collection=null, $val=null) { return self::includ($collection, $val); } | |
public function includ($collection=null, $val=null) { | |
list($collection, $val) = self::_wrapArgs(func_get_args(), 2); | |
$collection = (array) self::_collection($collection); | |
return self::_wrap((array_search($val, $collection, true) !== false)); | |
} | |
// Invoke the named function over each item in the collection, optionally passing arguments to the function | |
public function invoke($collection=null, $function_name=null, $arguments=null) { | |
$args = self::_wrapArgs(func_get_args(), 2); | |
$__ = new self; | |
list($collection, $function_name) = $__->first($args, 2); | |
$arguments = $__->rest(func_get_args(), 2); | |
// If passed an array or string, return an array | |
// If passed an object, return an object | |
$is_obj = is_object($collection); | |
$result = (empty($arguments)) ? array_map($function_name, (array) $collection) : array_map($function_name, (array) $collection, $arguments); | |
if($is_obj) $result = (object) $result; | |
return self::_wrap($result); | |
} | |
// Does any values in the collection meet the iterator's truth test? | |
// any alias: some | |
public function some($collection=null, $iterator=null) { return self::any($collection, $iterator); } | |
public function any($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$__ = new self; | |
if(!is_null($iterator)) $collection = $__->map($collection, $iterator); | |
if(count($collection) === 0) return self::_wrap(false); | |
return self::_wrap(is_int(array_search(true, $collection, false))); | |
} | |
// Do all values in the collection meet the iterator's truth test? | |
// all alias: every | |
public function every($collection=null, $iterator=null) { return self::all($collection, $iterator); } | |
public function all($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$__ = new self; | |
if(!is_null($iterator)) $collection = $__->map($collection, $iterator); | |
$collection = (array) $collection; | |
if(count($collection) === 0) return true; | |
return self::_wrap(is_bool(array_search(false, $collection, false))); | |
} | |
// Return an array of values that pass the truth iterator test | |
// filter alias: select | |
public function select($collection=null, $iterator=null) { return self::filter($collection, $iterator); } | |
public function filter($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$return = array(); | |
foreach($collection as $val) { | |
if(call_user_func($iterator, $val)) $return[] = $val; | |
} | |
return self::_wrap($return); | |
} | |
// Return an array where the items failing the truth test are removed | |
public function reject($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$return = array(); | |
foreach($collection as $val) { | |
if(!call_user_func($iterator, $val)) $return[] = $val; | |
} | |
return self::_wrap($return); | |
} | |
// Return the value of the first item passing the truth iterator test | |
// find alias: detect | |
public function detect($collection=null, $iterator=null) { return self::find($collection, $iterator); } | |
public function find($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
foreach($collection as $val) { | |
if(call_user_func($iterator, $val)) return $val; | |
} | |
return self::_wrap(false); | |
} | |
// How many items are in this collection? | |
public function size($collection=null) { | |
list($collection) = self::_wrapArgs(func_get_args(), 1); | |
$collection = self::_collection($collection); | |
return self::_wrap(count((array) $collection)); | |
} | |
// Get the first element of an array. Passing n returns the first n elements. | |
// first alias: head | |
public function head($collection=null, $n=null) { return self::first($collection, $n); } | |
public function first($collection=null, $n=null) { | |
list($collection, $n) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
if($n === 0) return self::_wrap(array()); | |
if(is_null($n)) return self::_wrap(current(array_splice($collection, 0, 1, true))); | |
return self::_wrap(array_splice($collection, 0, $n, true)); | |
} | |
// Get the rest of the array elements. Passing n returns from that index onward. | |
public function tail($collection=null, $index=null) { return self::rest($collection, $index); } | |
public function rest($collection=null, $index=null) { | |
list($collection, $index) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($index)) $index = 1; | |
$collection = self::_collection($collection); | |
return self::_wrap(array_splice($collection, $index)); | |
} | |
// Return everything but the last array element. Passing n excludes the last n elements. | |
public function initial($collection=null, $n=null) { | |
list($collection, $n) = self::_wrapArgs(func_get_args(), 2); | |
$collection = (array) self::_collection($collection); | |
if(is_null($n)) $n = 1; | |
$first_index = count($collection) - $n; | |
$__ = new self; | |
return self::_wrap($__->first($collection, $first_index)); | |
} | |
// Get the last element from an array. Passing n returns the last n elements. | |
public function last($collection=null, $n=null) { | |
list($collection, $n) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
if($n === 0) $result = array(); | |
elseif($n === 1 || is_null($n)) $result = array_pop($collection); | |
else { | |
$__ = new self; | |
$result = $__->rest($collection, -$n); | |
} | |
return self::_wrap($result); | |
} | |
// Return a copy of the array with falsy values removed | |
public function compact($collection=null) { | |
list($collection) = self::_wrapArgs(func_get_args(), 1); | |
$collection = self::_collection($collection); | |
$__ = new self; | |
return self::_wrap($__->select($collection, function($val) { | |
return (bool) $val; | |
})); | |
} | |
// Flattens a multidimensional array | |
public function flatten($collection=null, $shallow=null) { | |
list($collection, $shallow) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$return = array(); | |
if(count($collection) > 0) { | |
foreach($collection as $item) { | |
if(is_array($item)) { | |
$__ = new self; | |
$return = array_merge($return, ($shallow) ? $item : $__->flatten($item)); | |
} | |
else $return[] = $item; | |
} | |
} | |
return self::_wrap($return); | |
} | |
// Returns a copy of the array with all instances of val removed | |
public function without($collection=null, $val=null) { | |
$args = self::_wrapArgs(func_get_args(), 1); | |
$collection = $args[0]; | |
$collection = self::_collection($collection); | |
$num_args = count($args); | |
if($num_args === 1) return self::_wrap($collection); | |
if(count($collection) === 0) return self::_wrap($collection); | |
$__ = new self; | |
$removes = $__->rest($args); | |
foreach($removes as $remove) { | |
$remove_keys = array_keys($collection, $remove, true); | |
if(count($remove_keys) > 0) { | |
foreach($remove_keys as $key) { | |
unset($collection[$key]); | |
} | |
} | |
} | |
return self::_wrap($collection); | |
} | |
// Return an array of the unique values | |
// uniq alias: unique | |
public function unique($collection=null, $is_sorted=null, $iterator=null) { return self::uniq($collection, $is_sorted, $iterator); } | |
public function uniq($collection=null, $is_sorted=null, $iterator=null) { | |
list($collection, $is_sorted, $iterator) = self::_wrapArgs(func_get_args(), 3); | |
$collection = self::_collection($collection); | |
$return = array(); | |
if(count($collection) === 0) return self::_wrap($return); | |
$calculated = array(); | |
foreach($collection as $item) { | |
$val = (!is_null($iterator)) ? $iterator($item) : $item; | |
if(is_bool(array_search($val, $calculated, true))) { | |
$calculated[] = $val; | |
$return[] = $item; | |
} | |
} | |
return self::_wrap($return); | |
} | |
// Returns an array containing the intersection of all the arrays | |
public function intersection($array=null) { | |
$arrays = self::_wrapArgs(func_get_args(), 1); | |
if(count($arrays) === 1) return self::_wrap($array); | |
$__ = new self; | |
$return = $__->first($arrays); | |
foreach($__->rest($arrays) as $next) { | |
if(!$__->isArray($next)) $next = str_split((string) $next); | |
$return = array_intersect($return, $next); | |
} | |
return self::_wrap(array_values($return)); | |
} | |
// Merge together multiple arrays | |
public function union($array=null) { | |
$arrays = self::_wrapArgs(func_get_args(), 1); | |
if(count($arrays) === 1) return self::_wrap($array); | |
$__ = new self; | |
return self::_wrap($__->flatten(array_values(array_unique(call_user_func_array('array_merge', $arrays))))); | |
} | |
// Get the difference between two arrays | |
public function difference($array_one=null, $array_two=null) { | |
$arrays = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(array_values(call_user_func_array('array_diff', $arrays))); | |
} | |
// Get the index of the first match | |
public function indexOf($collection=null, $item=null) { | |
list($collection, $item) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
$key = array_search($item, $collection, true); | |
return self::_wrap((is_bool($key)) ? -1 : $key); | |
} | |
// Get the index of the last match | |
public function lastIndexOf($collection=null, $item=null) { | |
list($collection, $item) = self::_wrapArgs(func_get_args(), 2); | |
$collection = self::_collection($collection); | |
krsort($collection); | |
$__ = new self; | |
return self::_wrap($__->indexOf($collection, $item)); | |
} | |
// Returns an array of integers from start to stop (exclusive) by step | |
public function range($stop=null) { | |
$args = self::_wrapArgs(func_get_args(), 1); | |
$__ = new self; | |
$args = $__->reject($args, function($val) { | |
return is_null($val); | |
}); | |
$num_args = count($args); | |
switch($num_args) { | |
case 1: | |
list($start, $stop, $step) = array(0, $args[0], 1); | |
break; | |
case 2: | |
list($start, $stop, $step) = array($args[0], $args[1], 1); | |
if($stop < $start) return self::_wrap(array()); | |
break; | |
default: | |
list($start, $stop, $step) = array($args[0], $args[1], $args[2]); | |
if($step > 0 && $step > $stop) return self::_wrap(array($start)); | |
} | |
$results = range($start, $stop, $step); | |
// Switch inclusive to exclusive | |
if($step > 0 && $__->last($results) >= $stop) array_pop($results); | |
elseif($step < 0 && $__->last($results) <= $stop) array_pop($results); | |
return self::_wrap($results); | |
} | |
// Merges arrays | |
public function zip($array=null) { | |
$arrays = self::_wrapArgs(func_get_args()); | |
$num_arrays = count($arrays); | |
if($num_arrays === 1) return self::_wrap($array); | |
$__ = new self; | |
$num_return_arrays = $__->max($__->map($arrays, function($array) { | |
return count($array); | |
})); | |
$return_arrays = $__->range($num_return_arrays); | |
foreach($return_arrays as $k=>$v) { | |
if(!is_array($return_arrays[$k])) $return_arrays[$k] = array(); | |
foreach($arrays as $a=>$array) { | |
$return_arrays[$k][$a] = array_key_exists($k, $array) ? $array[$k] : null; | |
} | |
} | |
return self::_wrap($return_arrays); | |
} | |
// Get the max value in the collection | |
public function max($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($iterator)) return self::_wrap(max($collection)); | |
$results = array(); | |
foreach($collection as $k=>$item) { | |
$results[$k] = $iterator($item); | |
} | |
arsort($results); | |
$__ = new self; | |
$first_key = $__->first(array_keys($results)); | |
return $collection[$first_key]; | |
} | |
// Get the min value in the collection | |
public function min($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($iterator)) return self::_wrap(min($collection)); | |
$results = array(); | |
foreach($collection as $k=>$item) { | |
$results[$k] = $iterator($item); | |
} | |
asort($results); | |
$__ = new self; | |
$first_key = $__->first(array_keys($results)); | |
return self::_wrap($collection[$first_key]); | |
} | |
// Sort the collection by return values from the iterator | |
public function sortBy($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$results = array(); | |
foreach($collection as $k=>$item) { | |
$results[$k] = $iterator($item); | |
} | |
asort($results); | |
foreach($results as $k=>$v) { | |
$results[$k] = $collection[$k]; | |
} | |
return self::_wrap(array_values($results)); | |
} | |
// Group the collection by return values from the iterator | |
public function groupBy($collection=null, $iterator=null) { | |
list($collection, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
$result = array(); | |
$collection = (array) $collection; | |
foreach($collection as $k=>$v) { | |
$key = (is_callable($iterator)) ? $iterator($v, $k) : $v[$iterator]; | |
if(!array_key_exists($key, $result)) $result[$key] = array(); | |
$result[$key][] = $v; | |
} | |
return $result; | |
} | |
// Returns the index at which the value should be inserted into the sorted collection | |
public function sortedIndex($collection=null, $value=null, $iterator=null) { | |
list($collection, $value, $iterator) = self::_wrapArgs(func_get_args(), 3); | |
$collection = (array) self::_collection($collection); | |
$__ = new self; | |
$calculated_value = (!is_null($iterator)) ? $iterator($value) : $value; | |
while(count($collection) > 1) { | |
$midpoint = floor(count($collection) / 2); | |
$midpoint_values = array_slice($collection, $midpoint, 1); | |
$midpoint_value = $midpoint_values[0]; | |
$midpoint_calculated_value = (!is_null($iterator)) ? $iterator($midpoint_value) : $midpoint_value; | |
$collection = ($calculated_value < $midpoint_calculated_value) ? array_slice($collection, 0, $midpoint, true) : array_slice($collection, $midpoint, null, true); | |
} | |
$keys = array_keys($collection); | |
return self::_wrap(current($keys) + 1); | |
} | |
// Shuffle the array | |
public function shuffle($collection=null) { | |
list($collection) = self::_wrapArgs(func_get_args(), 1); | |
$collection = (array) self::_collection($collection); | |
shuffle($collection); | |
return self::_wrap($collection); | |
} | |
// Return the collection as an array | |
public function toArray($collection=null) { | |
return (array) $collection; | |
} | |
// Get the collection's keys | |
public function keys($collection=null) { | |
list($collection) = self::_wrapArgs(func_get_args(), 1); | |
if(!is_object($collection) && !is_array($collection)) throw new Exception('Invalid object'); | |
return self::_wrap(array_keys((array) $collection)); | |
} | |
// Get the collection's values | |
public function values($collection=null) { | |
list($collection) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(array_values((array) $collection)); | |
} | |
// Copy all properties from the source objects into the destination object | |
public function extend($object=null) { | |
$args = self::_wrapArgs(func_get_args(), 1); | |
$num_args = func_num_args(); | |
if($num_args === 1) return $object; | |
$is_object = is_object($object); | |
$array = (array) $object; | |
$__ = new self; | |
$extensions = $__->rest(func_get_args()); | |
foreach($extensions as $extension) { | |
$extension = (array) $extension; | |
$array = array_merge($array, $extension); | |
} | |
return self::_wrap(($is_object) ? (object) $array : $array); | |
} | |
// Returns the object with any missing values filled in using the defaults. | |
public function defaults($object=null) { | |
$args = self::_wrapArgs(func_get_args(), 1); | |
list($object) = $args; | |
$num_args = count($args); | |
if($num_args === 1) return $object; | |
$is_object = is_object($object); | |
$array = (array) $object; | |
$__ = new self; | |
$extensions = $__->rest($args); | |
foreach($extensions as $extension) { | |
$extension = (array) $extension; | |
$array = array_merge($extension, $array); | |
} | |
return self::_wrap(($is_object) ? (object) $array : $array); | |
} | |
// Get the names of functions available to the object | |
// functions alias: methods | |
public function methods($object=null) { return self::functions($object); } | |
public function functions($object=null) { | |
list($object) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(get_class_methods(get_class($object))); | |
} | |
// Returns a shallow copy of the object | |
public function clon(&$object=null) { | |
list($object) = self::_wrapArgs(func_get_args(), 1); | |
$clone = null; | |
if(is_array($object)) $clone = (array) clone (object) $object; | |
elseif(!is_object($object)) $clone = $object; | |
elseif(!$clone) $clone = clone $object; | |
// shallow copy object | |
if(is_object($clone) && count($clone) > 0) { | |
foreach($clone as $k=>$v) { | |
if(is_array($v) || is_object($v)) $clone->$k =& $object->$k; | |
} | |
} | |
// shallow copy array | |
elseif(is_array($clone) && count($clone) > 0) { | |
foreach($clone as $k=>$v) { | |
if(is_array($v) || is_object($v)) $clone[$k] =& $object[$k]; | |
} | |
} | |
return self::_wrap($clone); | |
} | |
// Invokes the interceptor on the object, then returns the object | |
public function tap($object=null, $interceptor=null) { | |
list($object, $interceptor) = self::_wrapArgs(func_get_args(), 2); | |
$interceptor($object); | |
return self::_wrap($object); | |
} | |
// Does the given key exist? | |
public function has($collection=null, $key=null) { | |
list($collection, $key) = self::_wrapArgs(func_get_args(), 2); | |
$collection = (array) self::_collection($collection); | |
return self::_wrap(array_key_exists($key, $collection)); | |
} | |
// Are these items equal? | |
public function isEqual($a=null, $b=null) { | |
list($a, $b) = self::_wrapArgs(func_get_args(), 2); | |
if(isset($this) && isset($this->_chained) && $this->_chained) $a =& $this; | |
if($a === $b) return self::_wrap(true); | |
if(gettype($a) !== gettype($b)) return self::_wrap(false); | |
if(is_callable($a) !== is_callable($b)) return self::_wrap(false); | |
if($a == $b) return self::_wrap(true); | |
// Objects and arrays compared by values | |
if(is_object($a) || is_array($a)) { | |
// Do either implement isEqual()? | |
if(is_object($a) && isset($a->isEqual)) return self::_wrap($a->isEqual($b)); | |
if(is_object($b) && isset($b->isEqual)) return self::_wrap($b->isEqual($a)); | |
if(is_array($a) && array_key_exists('isEqual', $a)) return self::_wrap($a['isEqual']($b)); | |
if(is_array($b) && array_key_exists('isEqual', $b)) return self::_wrap($b['isEqual']($a)); | |
if(count($a) !== count($b)) return self::_wrap(false); | |
$__ = new self; | |
$keys_equal = $__->isEqual($__->keys($a), $__->keys($b)); | |
$values_equal = $__->isEqual($__->values($a), $__->values($b)); | |
return self::_wrap($keys_equal && $values_equal); | |
} | |
return self::_wrap(false); | |
} | |
// Is this item empty? | |
public function isEmpty($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_array($item) || is_object($item)) ? !((bool) count((array) $item)) : (!(bool) $item); | |
} | |
// Is this item an object? | |
public function isObject($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_object($item)); | |
} | |
// Is this item an array? | |
public function isArray($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_array($item)); | |
} | |
// Is this item a string? | |
public function isString($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_string($item)); | |
} | |
// Is this item a number? | |
public function isNumber($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap((is_int($item) || is_float($item)) && !is_nan($item) && !is_infinite($item)); | |
} | |
// Is this item a bool? | |
public function isBoolean($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_bool($item)); | |
} | |
// Is this item a function (by type, not by name)? | |
public function isFunction($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_object($item) && is_callable($item)); | |
} | |
// Is this item an instance of DateTime? | |
public function isDate($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_object($item) && get_class($item) === 'DateTime'); | |
} | |
// Is this item a NaN value? | |
public function isNaN($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(is_nan($item)); | |
} | |
// Returns the same value passed as the argument | |
public function identity() { | |
$args = self::_wrapArgs(func_get_args(), 1); | |
if(is_array($args)) return self::_wrap($args[0]); | |
return self::_wrap(function($x) { | |
return $x; | |
}); | |
} | |
// Generate a globally unique id, optionally prefixed | |
public $_uniqueId = -1; | |
public function uniqueId($prefix=null) { | |
list($prefix) = self::_wrapArgs(func_get_args(), 1); | |
$_instance = self::getInstance(); | |
$_instance->_uniqueId++; | |
return (is_null($prefix)) ? self::_wrap($_instance->_uniqueId) : self::_wrap($prefix . $_instance->_uniqueId); | |
} | |
// Invokes the iterator n times | |
public function times($n=null, $iterator=null) { | |
list($n, $iterator) = self::_wrapArgs(func_get_args(), 2); | |
if(is_null($n)) $n = 0; | |
for($i=0; $i<$n; $i++) $iterator($i); | |
return self::_wrap(null); | |
} | |
// Extend the class with your own functions | |
private $_mixins = array(); | |
public function mixin($functions=null) { | |
list($functions) = self::_wrapArgs(func_get_args(), 1); | |
$mixins =& self::getInstance()->_mixins; | |
foreach($functions as $name=>$function) { | |
$mixins[$name] = $function; | |
} | |
return self::_wrap(null); | |
} | |
// Allows extending methods in static context | |
public static function __callStatic($name, $arguments) { | |
$mixins =& self::getInstance()->_mixins; | |
return call_user_func_array($mixins[$name], $arguments); | |
} | |
// Allows extending methods in non-static context | |
public function __call($name, $arguments) { | |
$mixins =& self::getInstance()->_mixins; | |
$arguments = self::_wrapArgs($arguments); | |
return call_user_func_array($mixins[$name], $arguments); | |
} | |
// Temporary PHP open and close tags used within templates | |
// Allows for normal processing of templates even when | |
// the developer uses PHP open or close tags for interpolation or evaluation | |
const TEMPLATE_OPEN_TAG = '760e7dab2836853c63805033e514668301fa9c47'; | |
const TEMPLATE_CLOSE_TAG= 'd228a8fa36bd7db108b01eddfb03a30899987a2b'; | |
const TEMPLATE_DEFAULT_EVALUATE = '/<%([\s\S]+?)%>/'; | |
const TEMPLATE_DEFAULT_INTERPOLATE= '/<%=([\s\S]+?)%>/'; | |
const TEMPLATE_DEFAULT_ESCAPE = '/<%-([\s\S]+?)%>/'; | |
public $_template_settings = array( | |
'evaluate' => self::TEMPLATE_DEFAULT_EVALUATE, | |
'interpolate' => self::TEMPLATE_DEFAULT_INTERPOLATE, | |
'escape' => self::TEMPLATE_DEFAULT_ESCAPE | |
); | |
// Set template settings | |
public function templateSettings($settings=null) { | |
$_template_settings =& self::getInstance()->_template_settings; | |
if(is_null($settings)) { | |
$_template_settings = array( | |
'evaluate' => self::TEMPLATE_DEFAULT_EVALUATE, | |
'interpolate' => self::TEMPLATE_DEFAULT_INTERPOLATE, | |
'escape' => self::TEMPLATE_DEFAULT_ESCAPE | |
); | |
return true; | |
} | |
foreach($settings as $k=>$v) { | |
if(!array_key_exists($k, $_template_settings)) continue; | |
$_template_settings[$k] = $v; | |
} | |
return true; | |
} | |
// Compile templates into functions that can be evaluated for rendering | |
public function template($code=null, $context=null) { | |
list($code, $context) = self::_wrapArgs(func_get_args(), 2); | |
$class_name = __CLASS__; | |
$return = self::_wrap(function($context=null) use ($code, $class_name) { | |
$ts = $class_name::getInstance()->_template_settings; | |
// Wrap escaped, interpolated, and evaluated blocks inside PHP tags | |
extract((array) $context); | |
preg_match_all($ts['escape'], $code, $vars, PREG_SET_ORDER); | |
if(count($vars) > 0) { | |
foreach($vars as $var) { | |
$echo = $class_name::TEMPLATE_OPEN_TAG . ' echo htmlentities(' . trim($var[1]) . '); ' . $class_name::TEMPLATE_CLOSE_TAG; | |
$code = str_replace($var[0], $echo, $code); | |
} | |
} | |
preg_match_all($ts['interpolate'], $code, $vars, PREG_SET_ORDER); | |
if(count($vars) > 0) { | |
foreach($vars as $var) { | |
$echo = $class_name::TEMPLATE_OPEN_TAG . ' echo ' . trim($var[1]) . '; ' . $class_name::TEMPLATE_CLOSE_TAG; | |
$code = str_replace($var[0], $echo, $code); | |
} | |
} | |
preg_match_all($ts['evaluate'], $code, $vars, PREG_SET_ORDER); | |
if(count($vars) > 0) { | |
foreach($vars as $var) { | |
$echo = $class_name::TEMPLATE_OPEN_TAG . trim($var[1]) . $class_name::TEMPLATE_CLOSE_TAG; | |
$code = str_replace($var[0], $echo, $code); | |
} | |
} | |
$code = str_replace($class_name::TEMPLATE_OPEN_TAG, '<?php ', $code); | |
$code = str_replace($class_name::TEMPLATE_CLOSE_TAG, '?>', $code); | |
// Use the output buffer to grab the return value | |
$code = 'ob_start(); extract($context); ?>' . $code . '<?php return ob_get_clean();'; | |
$func = create_function('$context', $code); | |
return $func((array) $context); | |
}); | |
// Return function or call function depending on context | |
return self::_wrap(((isset($this) && isset($this->_wrapped) && $this->_wrapped) || !is_null($context)) ? $return($context) : $return); | |
} | |
// Escape | |
public function escape($item=null) { | |
list($item) = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(htmlentities($item)); | |
} | |
// Memoizes a function by caching the computed result. | |
public $_memoized = array(); | |
public function memoize($function=null, $hashFunction=null) { | |
list($function, $hashFunction) = self::_wrapArgs(func_get_args(), 2); | |
$_instance = (isset($this) && isset($this->_wrapped)) ? $this : self::getInstance(); | |
return self::_wrap(function() use ($function, &$_instance, $hashFunction) { | |
// Generate a key based on hashFunction | |
$args = func_get_args(); | |
if(is_null($hashFunction)) $hashFunction = function($function, $args) { | |
// Try using var_export to identify the function | |
return md5(join('_', array( | |
var_export($function, true), | |
var_export($args, true) | |
))); | |
}; | |
$key = $hashFunction($function, $args); | |
if(!array_key_exists($key, $_instance->_memoized)) { | |
$_instance->_memoized[$key] = call_user_func_array($function, $args); | |
} | |
return $_instance->_memoized[$key]; | |
}); | |
} | |
// Throttles a function so that it can only be called once every wait milliseconds | |
public $_throttled = array(); | |
public function throttle($function=null, $wait=null) { | |
list($function, $wait) = self::_wrapArgs(func_get_args(), 2); | |
$_instance = (isset($this) && isset($this->_wrapped)) ? $this : self::getInstance(); | |
return self::_wrap(function() use ($function, $wait, &$_instance) { | |
// Try using var_export to identify the function | |
$key = md5(join('', array( | |
var_export($function, true), | |
$wait | |
))); | |
$microtime = microtime(true); | |
$ready_to_call = (!array_key_exists($key, $_instance->_throttled) || $microtime >= $_instance->_throttled[$key]); | |
if($ready_to_call) { | |
$next_callable_time = $microtime + ($wait / 1000); | |
$_instance->_throttled[$key] = $next_callable_time; | |
return call_user_func_array($function, func_get_args()); | |
} | |
}); | |
} | |
// Creates a version of the function that can only be called once | |
public $_onced = array(); | |
public function once($function=null) { | |
list($function) = self::_wrapArgs(func_get_args(), 1); | |
$_instance = (isset($this) && isset($this->_wrapped)) ? $this : self::getInstance(); | |
return self::_wrap(function() use ($function, &$_instance) { | |
// Try using var_export to identify the function | |
$key = md5(var_export($function, true)); | |
if(!array_key_exists($key, $_instance->_onced)) { | |
$_instance->_onced[$key] = call_user_func_array($function, func_get_args()); | |
} | |
return $_instance->_onced[$key]; | |
}); | |
} | |
// Wraps the function inside the wrapper function, passing it as the first argument | |
public function wrap($function=null, $wrapper=null) { | |
list($function, $wrapper) = self::_wrapArgs(func_get_args(), 2); | |
return self::_wrap(function() use ($wrapper, $function) { | |
$args = array_merge(array($function), func_get_args()); | |
return call_user_func_array($wrapper, $args); | |
}); | |
} | |
// Returns the composition of the functions | |
public function compose() { | |
$functions = self::_wrapArgs(func_get_args(), 1); | |
return self::_wrap(function() use ($functions) { | |
$args = func_get_args(); | |
foreach($functions as $function) { | |
$args[0] = call_user_func_array($function, $args); | |
} | |
return $args[0]; | |
}); | |
} | |
// Creates a version of the function that will only run after being called count times | |
public $_aftered = array(); | |
public function after($count=null, $function=null) { | |
list($count, $function) = self::_wrapArgs(func_get_args(), 2); | |
$_instance = (isset($this) && isset($this->_wrapped)) ? $this : self::getInstance(); | |
$key = md5(mt_rand()); | |
$func = function() use ($function, &$_instance, $count, $key) { | |
if(!array_key_exists($key, $_instance->_aftered)) $_instance->_aftered[$key] = 0; | |
$_instance->_aftered[$key] += 1; | |
if($_instance->_aftered[$key] >= $count) return call_user_func_array($function, func_get_args()); | |
}; | |
return self::_wrap(($count) ? $func : $func()); | |
} | |
// Singleton | |
private static $_instance; | |
public function getInstance() { | |
if(!isset(self::$_instance)) { | |
$c = __CLASS__; | |
self::$_instance = new $c; | |
} | |
return self::$_instance; | |
} | |
// All methods should wrap their returns within _wrap | |
// because this function understands both OO-style and functional calls | |
public $_wrapped; // Value passed from one chained method to the next | |
private function _wrap($val) { | |
if(isset($this) && isset($this->_chained) && $this->_chained) { | |
$this->_wrapped = $val; | |
return $this; | |
} | |
return $val; | |
} | |
// All methods should get their arguments from _wrapArgs | |
// because this function understands both OO-style and functional calls | |
private function _wrapArgs($caller_args, $num_args=null) { | |
$num_args = (is_null($num_args)) ? count($caller_args) - 1 : $num_args; | |
$filled_args = array(); | |
if(isset($this) && isset($this->_wrapped)) { | |
$filled_args[] =& $this->_wrapped; | |
} | |
if(count($caller_args) > 0) { | |
foreach($caller_args as $k=>$v) { | |
$filled_args[] = $v; | |
} | |
} | |
return array_pad($filled_args, $num_args, null); | |
} | |
// Get a collection in a way that supports strings | |
private function _collection($collection) { | |
return (!is_array($collection) && !is_object($collection)) ? str_split((string) $collection) : $collection; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment