PHP modify code to avoid anonymous functions

南笙酒味 提交于 2019-12-17 20:28:41

问题


I've found some solutions to a sorting problem I've been having, but the code uses anonymous functions in PHP. Im using version 5.2.17 and I believe anonymous functions are not supported.

How would I change the following blocks of code so I can use them in PHP 5.2.17

$keys = array_flip($order);

usort($items, function($a, $b) use($keys)
{
    return $keys[$a['id']] - $keys[$b['id']];
});

from PHP sort multidimensional array by other array

And

$sorted = array_map(function($v) use ($data) {
    return $data[$v - 1];
}, $order);

from PHP - Sort multi-dimensional array by another array

UPDATE: One of the problems is I'm not sure how the variables $a, $b and $v are used. So I'm not sure how to create normal functions, thus bypassing the anon functions.


回答1:


Both of the anonymous functions make use of the use clause to pass variables into the local scope.

You can achieve the same with object methods in which the objects have those variables as properties.

Inside the object method you then can access these.

$sorted = array_map(function($v) use ($data) {
    return $data[$v - 1];
}, $order);

An exemplary mapping object then could look like:

class MapObj
{
    private $data;
    public function __construct($data) {
        $this->data = $data;
    }

    public function callback($v) {
        return $this->data[$v - 1];
    }
}

As you can see it has the same functionality but just written in PHP 5.2 syntax.

And it's usage:

$map = new MapObj($data);
$callback = array($map, 'callback');
$sorted = array_map($callback, $order);

And that's how it works. Callbacks for object methods are always written in form of an array with two members, the first one is the object instance, and the second one is the name of the object method.

Naturally you can extend this an put the mapping function into the mapping object, so it's more straight forward:

class MapObj
{
    ...

    public function map(array $order) {
        $callback = array($this, 'callback');
        return array_map($callback, $order);
    }
}

New usage:

$map = new MapObj($data);
$sorted = $map->map($order);

As you can see this might make the usage a bit more straight forward. I must admit, my method naming is not really brilliant here, so I leave some room for your improvements.

Another benefit is, you can make the visibility of the callback method private then.


The situation with passing the data to work with in the callback as a parameter to the mapping function. That is because you wrote you already have a class that you want to make use of, but you can not touch the constructor. So the given example is a bit short.

Here is another example without using the constructor, I removed it:

class MyObj
{
    private $data;

    private function callback($v) {
        return $this->data[$v - 1];
    }

    public function map($order, $data) {
        $callback = array($this, 'callback');
        $this->data = $data;
        return array_map($callback, $order);
    }
}

As you can see, the constructor is not needed any longer to pass the $data, but instead it's just passed into the map() method as an additional parameter. Usage:

$myObj = new MyObj(....); // somewhere.

// later on:

$myObj->map($order, $data);

// could be also:

$this->map($order, $data);

As you can see, how you set the private member variable is up to you. Do what fits the job.




回答2:


What you have here is a closure over $data -- it's not 100% possible to rewrite it without an anonymous function. Here's the closest possible approximation:

function _array_sort_callback($a, $b) {
    global $_array_sort_callback__keys;
    return $_array_sort_callback__keys[$a['id']] - $_array_sort_callback__keys[$b['id']];
}

... {
    $keys = array_flip($order);
    global $_array_sort_callback__keys;
    $_array_sort_callback__keys = $keys;
    usort($items, "_array_sort_callback");
}

Note that I've prefixed the name of the global to try to avoid a collision. Both the function name and the global name need to be unique within your application.

Also, keep in mind that PHP 5.2.17 is obsolete and unsupported. You should migrate off of it as soon as possible.




回答3:


If you want to mimic a closure, where you snapshot variables at a specific time, you can use a simple base class to serve as a container for the values, and then just define sub classes to implement the comparison logic.

Untested

// base class exists purely to capture the value of some variables at instantiation time
// kinda like a closure
class VarCapture {
    protected $vars;
    function __construct($vars) {
        $this->vars = $vars;
    }
}
class ItemComparer extends VarCapture {
    function compare($a, $b) {
       $keys = $this->vars['keys'];
       return $keys[$a['id']] - $keys[$b['id']];
    }
}

class PluckMapper extends VarCapture {
    function pluck($v) {
        $data = $this->vars['data'];
        return $data[$v - 1];
    }
}
$keys = array_flip($order);
$ic = new ItemComparer(compact('keys'));
$callable = array($ic, 'compare');
usort($items, $callable);

$pm = new PluckMapper(compact('data'));
$callable = array($mp, 'pluck');
$sorted = array_map($callable, $order);

Note that I made use of php's callback psuedo type http://php.net/manual/en/language.types.callable.php




回答4:


You can also re-write it into pre-5.3 anonymous functions, a la create_function(). Although create_function() functions don't normally act as closures, you can use some tricks (without using global variables) to make them work as closures in some limited circumstances. You encode the closed-over variables directly into the source of the function. The limitations are that data only goes one-way -- in; closed-over variables can only be "simple" data types, like numbers, strings, and arrays; and functions created with create_function are never deallocated, leaking memory; plus it is not very efficient. But I think it is sufficient for your example (assuming you only use arrays and strings and such).

$keys = array_flip($order);

usort($items, create_function('$a,$b', '$keys = '.var_export($keys,true).';
    return $keys[$a["id"]] - $keys[$b["id"]];
'));

and

$sorted = array_map(create_function('$v', '$data = '.var_export($data,true).';
    return $data[$v - 1];
'), $order);

The more general pre-5.3 solution, though, is to use an object and method as the closure, as in hakra's answer.



来源:https://stackoverflow.com/questions/13589707/php-modify-code-to-avoid-anonymous-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!