diff options
author | Brion Vibber <brion@pobox.com> | 2009-12-15 13:53:19 -0800 |
---|---|---|
committer | Brion Vibber <brion@pobox.com> | 2009-12-16 09:27:48 -0500 |
commit | 0158f4f73db1c6090c09da8cc3cdcfb97af3883b (patch) | |
tree | d9595dd5285fe0341c93338721cb5216579a86f9 | |
parent | 00fb5feff8f2552f63f1ddc7b1bef25ebd408507 (diff) |
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!
-rw-r--r-- | lib/curry.php | 36 | ||||
-rw-r--r-- | lib/util.php | 30 | ||||
-rw-r--r-- | tests/CurryTest.php | 72 |
3 files changed, 125 insertions, 13 deletions
diff --git a/lib/curry.php b/lib/curry.php new file mode 100644 index 000000000..6136dcdc3 --- /dev/null +++ b/lib/curry.php @@ -0,0 +1,36 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * 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 14d666503..d4afafb4c 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 000000000..37b66cc74 --- /dev/null +++ b/tests/CurryTest.php @@ -0,0 +1,72 @@ +<?php + +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(); +} + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('STATUSNET', true); +define('LACONICA', true); + +require_once INSTALLDIR . '/lib/common.php'; + +class CurryTest extends PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provider + * + */ + public function testProduction($callback, $curry_params, $call_params, $expected) + { + $params = array_merge(array($callback), $curry_params); + $curried = call_user_func_array('curry', $params); + $result = call_user_func_array($curried, $call_params); + $this->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; + } +} |