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.
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.
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.
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
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.
Related
So I'm really confused about anonymous functions in PHP, I want to know if anonymous functions are mainly used as a matter of taste or coding style.
I'm asking this because you can achieve the same result without a callback function and less code.
So here is some test code:
$output = 10;
$people = (new People(new John));
//VERSION 1 ANONYMOUS FUNCTION
$people->run(function ($value) use(&$output){
$output = $output + $value;
});
var_dump($output); //RESULT => 20
//VERSION 2 WITHOUT ANONYMOUS FUNCTION
var_dump($people->run() + $output); //RESULT => 30
You can run and see the full code here:
https://www.tehplayground.com/IhWJJU0jbNnzuird
<?php
interface HumanInterface
{
public function hello();
}
class People
{
protected $person;
protected $value;
public function __construct(HumanInterface $person)
{
$this->person = $person;
return $this;
}
public function run(callable $callback = null, $name = null)
{
$this->value = 10;
if(is_callable($callback)) {
return call_user_func($callback, $this->value);
}
return $this->value;
}
}
class John implements HumanInterface
{
public function hello()
{
return 'hi i am john';
}
}
$output = 10;
$people = (new People(new John));
$people->run(function ($value) use(&$output){
$output = $output + $value;
});
var_dump($output);
var_dump($people->run() + $output);
So my question is: why use an anonymous function? Is it a matter of
personal choice?
Anonymous functions or „Closures“ are very useful if it is used as a one-time callback. If you use PHP's usort-method for example. The second parameter can be a Closure. So instead of writing a named function which is used once and then never again you use a Closure.
IMHO this is the only way to use Closures: as a callback.
Anonymous functions are useful to pass around for later execution or to other code accepting functions. Using them can dramatically reduce the code needed to do things.
Imagine you have a UserCollection object that uses a generic Collection underneath. You want to reduce the UserCollection to just the Users from a certain country. So you could add a method findByCountry() on the UserCollection:
public function findByCountry($country) : UserCollection {
$subset = new UserCollection;
foreach ($this->users as $user) {
if ($user->country === $country) {
$subset->add($user);
}
}
return $subset;
}
This is all fine, but additional finder methods will all do the same: iterate and collect into the subset with only the criteria being different. So a lot of boilerplate code.
You can separate the boilerplate from the criteria easily by adding a method find(callable $callback) on the underlying Collection, like this:
public function find(callable $criteria) : Collection {
$subset = new Collection;
foreach ($this->users as $user) {
if ($criteria($user)) {
$subset->add($user);
}
}
return $subset;
}
This is a generic finder. Now your code in the UserCollection will only contain the actual criteria:
public function findByCountry($country): UserCollection {
return $this->subset(function(User $user) {
return $user->country === $country;
});
}
private function subset($criteria): UserCollection {
return new UserCollection($this->allUsers->find($criteria));
}
By separating the criteria from the boilerplate, it's much easier to grasp that you are trying to find users by country. There is no iteration code distracting from the actual criteria. So it becomes easier to understand and less effort to write. Also, you cannot accidentally mess up the iteration because it's defined elsewhere.
When using anonymous functions like this, they are very similar to using the Strategy Pattern or a FilterIterator. The notable difference being that you are creating them on the fly.
Note that you have to differentiate between Lambdas and Closures. I deliberately ignored the difference for this answer.
I am trying to study the code for Slim framework. In the constructor for Slim class, $c is passed to the closure (for instance, when storing/setting the request/response object in the container):
public function __construct(array $userSettings = array())
{
// Setup IoC container
$this->container = new \Slim\Helper\Set();
$this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings);
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
// Default response
$this->container->singleton('response', function ($c) {
return new \Slim\Http\Response();
});
But $c is not defined/declared anywhere prior to this statement, so how does it work? I started to trace everything from the beginning and I can't find $c anywhere prior to it being used in this manner.
$c is a parameter of the closure function. Imagine you had a function by itself:
function myFunction($c) {
echo $c;
}
In the case of a closure, you can store an anonymous function in a variable:
$someFunction = function ($c) {
echo $c;
}
$someFunction("hello world");
So instead of directly storing the closure into the variable, the code above is passing the anonymous function as a parameter to $this->container->singleton(). So $c is not created until the closure is called. The singleton method stores this in a variable called $value, so if that function ran:
$value(array('environment'=>'test'));
$c would now contain array('environment'=>'test')
Slim also uses the __get() __set() magic methods quite a bit, so from the example code you set, within the Slim class, one could call:
$request = $this->container->request(array('environment'=>'test'));
The container is of class Slim\Helper\Set. Since this doesn't have a request method, this would call the container's __get() method. It would look up the stored method configured above for 'request' and pass the array in as $c
I think khartnett gave a perfect answer. To make it clearer for you, an example.
When you define a function, you are only describing how it works. You are not setting any specific values. For example, when I write:
function sum($a, $b) {
return $a + $b;
}
I am not saying what the values of $a and $b are here. I am just describing what I am doing with these variables to calculate a result. It is not until I call this function that I'm working with actual values:
sum(3, 4); // returns 7
In your question, the $c variable is like the $a and $b variable.
Like khartnett showed in his answer, it works like this:
// Definition time
$someFunction = function ($c) {
echo $c;
}
// Calling time
$someFunction("hello world");
It is not until calling time that $c gets its value (in this example, the value is "hello world").
The $c is a reference to the container itself - so that any dependencies will be automatically resolved when invoked.
So using for example the request object:
// Default request
$this->container->singleton('request', function ($c) {
return new \Slim\Http\Request($c['environment']);
});
Looking at the Request constructor you will see that it expects an instance of Environment, which we just told the container should be available already using the key 'environment'.
The answer will lay in $app->run():
$this->container->get("service_name") then in get() method offsetGet($id)
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this); // THIS LINE CALLS THE CONTAINER ITSELF
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
Pimple Container functioning it's a bit more complicated due to the fact:
Allowing any PHP callable leads to difficult to debug problems
as function names (strings) are callable (creating a function with
the same name as an existing parameter would break your container)
So unique identifiers are introduced.
Here is a simple implementation of ServiceContainer get() method inside the ServiceContainer class:
public function get($serviceName)
{
if (!array_key_exists($serviceName, $this->container)) {
throw new \http\Exception\InvalidArgumentException("Service not found!");
}
$service = $this->container[$serviceName];
if (is_callable($service)) {
$this->container[$serviceName] = $service = $service($this);
// $this (aka ServiceContainer) will be passed as parameter to Closures
}
return $service;
}
Hope it clarifies the question, have a nice day!
It is possible to determine how many arguments a function accepts by using reflection.
I want to be able to define a function compose that performs function composition. That is to say, compose($f, $g) should produce a new function that returns $f($g($x)).
I have a sample implementation here:
function compose()
{
$fns = func_get_args();
$prev = array_shift($fns);
foreach ($fns as $fn) {
$prev = function ($x) use ($fn, $prev) {
$args = func_get_args();
return $prev(call_user_func_array($fn, $args));
};
}
return $prev;
}
When composing $f and $g, $g may have an arity higher than 1. Which means it can take more than one argument. Thus, the function returned by compose($f, $g) may also take more than one argument -- it takes exactly the same arguments as $g.
The problem with this implementation is that there is no way to control the exposed arity of what compose returns. In this case it is always 1, because of the function ($x) .... When trying to determine the arity using reflection, it will always return 1 instead of that of $g.
Is there a way to change the amount of arguments of an anonymous function seen by PHP's reflection system, without using eval and other code generation techniques?
The moment you introduce func_get_args() into a function should invalidate any hope of being able to determine its true arity. At that point, the arity is really only defined by the function's logic, and cannot be determined by reflection or static code analysis.
I've written a compose-like implementation before, but it just assumed that the functions you are composing both have an arity of 1.
Here is an ugly solution as it does not work with all number of parameters (or you end up with tons of case stuff), but it does not rely on eval:
function compose($f, $g)
{
switch(getReflectiveArity($g)) {
case 1:
return function($x) use ($f, $g) {
return $f($g($x));
};
break;
case 2:
return function($x, $y) use ($f, $g) {
return $f($g($x));
};
break;
case 3:
return function($x, $y, $z) use ($f, $g) {
return $f($g($x, $y, $z));
};
break;
/* ... */
default:
throw new RuntimeException();
}
}
My proposed solution to this would be to introduce a custom convention for specifying the arity using a custom property, defined on an object that wraps the closure.
Like this:
class CustomArityFunction
{
public $f;
public $arity;
function __construct(callable $f, $arity)
{
$this->f = $f;
$this->arity = $arity;
}
function __invoke()
{
return call_user_func_array($this->f, func_get_args());
}
}
// define function
$f = function () { ... };
return new CustomArityFunction($f, $n);
// determine arity
$arity = ($f instanceof CustomArityFunction) ? $f->arity : getReflectiveArity($f);
The major downside of this solution is that the consuming code needs to be aware of the convention.
It is however the cleanest way of doing this that I could come up with.
Note: The wrapper is needed because PHP does not allow assigning properties to closures. Thanks to #nikic for pointing that out.
Based upon How can I dynamically check the number of arguments expected of an anonymous function in PHP?
is the following piece of code which works on php >= 5.3
http://3v4l.org/6KPFN
Update: Just reread the question ... this is probably BS
I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)
I'd like to be able to write a PHP class that behaves like an array and uses normal array syntax for getting & setting.
For example (where Foo is a PHP class of my making):
$foo = new Foo();
$foo['fooKey'] = 'foo value';
echo $foo['fooKey'];
I know that PHP has the _get and _set magic methods but those don't let you use array notation to access items. Python handles it by overloading __getitem__ and __setitem__.
Is there a way to do this in PHP? If it makes a difference, I'm running PHP 5.2.
If you extend ArrayObject or implement ArrayAccess then you can do what you want.
ArrayObject
ArrayAccess
Nope, casting just results in a normal PHP array -- losing whatever functionality your ArrayObject-derived class had. Check this out:
class CaseInsensitiveArray extends ArrayObject {
public function __construct($input = array(), $flags = 0, $iterator_class = 'ArrayIterator') {
if (isset($input) && is_array($input)) {
$tmpargs = func_get_args();
$tmpargs[0] = array_change_key_case($tmpargs[0], CASE_LOWER);
return call_user_func_array(array('parent', __FUNCTION__), $tmp args);
}
return call_user_func_array(array('parent', __FUNCTION__), func_get_args());
}
public function offsetExists($index) {
if (is_string($index)) return parent::offsetExists(strtolower($index));
return parent::offsetExists($index);
}
public function offsetGet($index) {
if (is_string($index)) return parent::offsetGet(strtolower($index));
return parent::offsetGet($index);
}
public function offsetSet($index, $value) {
if (is_string($index)) return parent::offsetSet(strtolower($index, $value));
return parent::offsetSet($index, $value);
}
public function offsetUnset($index) {
if (is_string($index)) return parent::offsetUnset(strtolower($index));
return parent::offsetUnset($index);
}
}
$blah = new CaseInsensitiveArray(array(
'A'=>'hello',
'bcD'=>'goodbye',
'efg'=>'Aloha',
));
echo "is array: ".is_array($blah)."\n";
print_r($blah);
print_r(array_keys($blah));
echo $blah['a']."\n";
echo $blah['BCD']."\n";
echo $blah['eFg']."\n";
echo $blah['A']."\n";
As expected, the array_keys() call fails. In addition, is_array($blah) returns false. But if you change the constructor line to:
$blah = (array)new CaseInsensitiveArray(array(
then you just get a normal PHP array (is_array($blah) returns true, and array_keys($blah) works), but all of the functionality of the ArrayObject-derived subclass is lost (in this case, case-insensitive keys no longer work). Try running the above code both ways, and you'll see what I mean.
PHP should either provide a native array in which the keys are case-insensitive, or make ArrayObject be castable to array without losing whatever functionality the subclass implements, or just make all array functions accept ArrayObject instances.