From 0158f4f73db1c6090c09da8cc3cdcfb97af3883b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 15 Dec 2009 13:53:19 -0800 Subject: [PATCH] PHP 5.3 closure-based implementation of curry(); old implementation used as fallback for older PHP versions. Added unit tests to confirm they both work! --- lib/curry.php | 36 +++++++++++++++++++++++ lib/util.php | 30 +++++++++++-------- tests/CurryTest.php | 72 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 lib/curry.php create mode 100644 tests/CurryTest.php diff --git a/lib/curry.php b/lib/curry.php new file mode 100644 index 0000000000..6136dcdc37 --- /dev/null +++ b/lib/curry.php @@ -0,0 +1,36 @@ +. + */ + +/** + * PHP 5.3 implementation of function currying, using native closures. + * On 5.2 and lower we use the fallback implementation in util.php + * + * @param callback $fn + * @param ... any remaining arguments will be appended to call-time params + * @return callback + */ +function curry($fn) { + $extra_args = func_get_args(); + array_shift($extra_args); + return function() use ($fn, $extra_args) { + $args = func_get_args(); + return call_user_func_array($fn, + array_merge($args, $extra_args)); + }; +} diff --git a/lib/util.php b/lib/util.php index 14d6665037..d4afafb4c0 100644 --- a/lib/util.php +++ b/lib/util.php @@ -523,19 +523,23 @@ function callback_helper($matches, $callback, $notice_id) { return substr($matches[0],0,$left) . $result . substr($matches[0],$right); } -function curry($fn) { - //TODO switch to a PHP 5.3 function closure based approach if PHP 5.3 is used - $args = func_get_args(); - array_shift($args); - $id = uniqid('_partial'); - $GLOBALS[$id] = array($fn, $args); - return create_function('', - '$args = func_get_args(); '. - 'return call_user_func_array('. - '$GLOBALS["'.$id.'"][0],'. - 'array_merge('. - '$args,'. - '$GLOBALS["'.$id.'"][1]));'); +if (version_compare(PHP_VERSION, '5.3.0', 'ge')) { + // lambda implementation in a separate file; PHP 5.2 won't parse it. + require_once INSTALLDIR . "/lib/curry.php"; +} else { + function curry($fn) { + $args = func_get_args(); + array_shift($args); + $id = uniqid('_partial'); + $GLOBALS[$id] = array($fn, $args); + return create_function('', + '$args = func_get_args(); '. + 'return call_user_func_array('. + '$GLOBALS["'.$id.'"][0],'. + 'array_merge('. + '$args,'. + '$GLOBALS["'.$id.'"][1]));'); + } } function common_linkify($url) { diff --git a/tests/CurryTest.php b/tests/CurryTest.php new file mode 100644 index 0000000000..37b66cc749 --- /dev/null +++ b/tests/CurryTest.php @@ -0,0 +1,72 @@ +assertEquals($expected, $result); + } + + static public function provider() + { + $obj = new CurryTestHelperObj('oldval'); + return array(array(array('CurryTest', 'callback'), + array('curried'), + array('called'), + 'called|curried'), + array(array('CurryTest', 'callback'), + array('curried1', 'curried2'), + array('called1', 'called2'), + 'called1|called2|curried1|curried2'), + array(array('CurryTest', 'callbackObj'), + array($obj), + array('newval1'), + 'oldval|newval1'), + // Confirm object identity is retained... + array(array('CurryTest', 'callbackObj'), + array($obj), + array('newval2'), + 'newval1|newval2')); + } + + static function callback() + { + $args = func_get_args(); + return implode("|", $args); + } + + static function callbackObj($val, $obj) + { + $old = $obj->val; + $obj->val = $val; + return "$old|$val"; + } +} + +class CurryTestHelperObj +{ + public $val=''; + + function __construct($val) + { + $this->val = $val; + } +}