This question already has answers here:
How to use class methods as callbacks
(5 answers)
Closed 10 months ago.
I'm trying to understand some code I found the open source oauth-php library. The relevant code snippet is:
protected function sql_printf ( $args )
{
$sql = array_shift($args);
if (count($args) == 1 && is_array($args[0]))
{
$args = $args[0];
}
$args = array_map(array($this, 'sql_escape_string'), $args);
return vsprintf($sql, $args);
}
Where $args is an array of arguments that contain variables intended for use in a formatted printing operation. I looked at the docs for array_map:
http://php.net/manual/en/function.array-map.php
and the user comments and I did not see any use case where the first parameter in a call to array_map() was an array itself. In all the use cases I saw, the first parameter was either NULL or a (callback) function. It seems pretty obvious to me that the code takes the $args array and then builds a new array with the arguments sanitized by $this->sql_escape_string().
But the statement "array($this, 'sql_escape_string')" is throwing me since I would have expected simply '$this->sql_escape_string', or is that not a valid syntax? If so, how does wrapping $this and 'sql_escape_string' in an array create a valid callback function for array_map() to use?
It is actually passing the sql_escape_string method from the class itself as a callback. It is a way of clarifying ambiguous method calls. For example:
array_map('sql_escape_string', $args);
of course applies sql_escape_string() to each value in $args, whereas:
array_map(array($someClass, 'sql_escape_string'), $args);
applies the sql_escape_string() method from $someClass to each value in $args.
The first parameter is a callback. It can be either a string or an array.
since I would have expected simply '$this->sql_escape_string'
You would if it were just one scalar value. But you have an array and you need to apply that escape function to each item of the $args array. So you need to implement foreach and apply that function or use one-liner with array_map.
But the statement "array($this, 'sql_escape_string')" is throwing me since I would have expected simply '$this->sql_escape_string', or is that not a valid syntax?
It's valid, but doesn't refer to what you think it refers to. Consider free functions, constants, class names and variables: each exists in different environments (or "namespaces" if you prefer, but that's easily confused with PHP namespaces). The different environment for variables is made explicit by the use of "$" as a sigil: the variable $foo versus the function foo(), constant foo and class Foo. This is also why constants and variables are case-sensitive, but functions and class names aren't: the different environments allow for different name resolution rules.
Similarly, object methods and properties exist in different environments. As a consequence, $this->sql_escape_string refers to a property, not a method. To confuse matters, that property could contain a callable, though such a callable couldn't be invoked directly:
class Foo {
function frob() {return 23.0 / 42;}
}
$foo = new Foo;
$foo->frob = function () {return 0 / 0;};
$foo->frob(); # calls method, not closure function
$frob = $foo->frob;
$frob(); # oops: division by zero
As with constants and functions, properties and methods are distinguished by the absence or presence of an argument list.
If so, how does wrapping $this and 'sql_escape_string' in an array create a valid callback function for array_map() to use?
PHP's syntax for callable references goes beyond strings.
Free functions (functions not associated with a class or object; contrast with "bound functions") can unambiguously be referred to by their names. Static methods are bound to a class, but can be referred to with a string if it includes the class name (the syntax is "Class::method"). A string cannot contain enough information for an object method, however, since the method must be bound to a particular object, and PHP doesn't have a way to refer to an object using a string. The solution PHP's developers settled on was to use array syntax (as shown in the question sample code). They also included support for array syntax for static methods (array('Class', 'method')).
Besides callable references, callables can be closures. These offer an alternative way of passing object methods, but are more verbose and complex.
$self = $this; # workaround: $this not accessible in closures before 5.4
$args = array_map(
function ($value) use($self) {
return $self->sql_escape_string($value);
}, $args);
Closures aren't so useful when a callable reference will do, but are more powerful overall.
Related
The PHP documentation says:
[...] if a variable name has parentheses appended to it, PHP will look for a function with the same name as whatever the variable evaluates to, and will attempt to execute it.
This is about named functions. Parentheses also works with Closures, and this is widely used (backward compatibility with the old lambda functions).
Currently, this works too (with PHP 5.4):
$method = [$oObject, "methodName"];
$result = $method($param1, $param2);
But this is not documented, and my hands "tingle" when I write it.
Is there any guarantee that this will keep to work in the future (not need to use call_user_func() etc.)?
Is this safe:
public function apply(callable $any_callable_variant)
{
return $any_callable_variant($this);
}
There are precedents, then not-documented-but-useful features was removed from PHP.
Update: one more thing
Some comments (already deleted) mentioned the callable pseudotype.
Documentation says about is_callable:
Verify that the contents of a variable can be called as a function.
This can check that a simple variable contains the name of a valid
function, or that an array contains a properly encoded object and
function name.
According to this, a Closure is not callable (not true, of course). Make a test:
var_dump(is_callable(function () { // prints: bool(true)
return false;
}, true, $callable_name));
var_dump($callable_name); // prints: string(17) "Closure::__invoke"
It seems, no one even thought about the documentation when Closure was introduced.
Take the following code from CodeIgniter's show_error function:
$_error =& load_class('Exceptions', 'core');
The documentation for the load_class function says it acts as a singleton. The function basically takes the given parameters and searches for a class in the appropriate path. It then includes the file if it exists. The function is declared as:
function &load_class(...)
Why does it have the & and what is its purpose? Is $_error declared as such as a result of defining the function like that?
I don't see any point of declaring and using load_class like that. From the source code of load_class(), we can see that it caches loaded objects in an array with the class name as the key. If it is not in the cache, it loads an object given a name, and then stores that object reference into the array. In both cases, it returns the element of the array (by reference).
Returning by reference allows the caller to have a reference to the element of the array. The only things that this allows us to do are:
See later changes to that array element (i.e. the value associated with that key) from the outside reference we have. But this is not applicable, since the load_class function never changes the value associated with a key after it sets it.
Have external code be able to change the element in the array, without the load_class function knowing about it. But this would be a highly dubious practice, to mess with the cache from the outside, and I highly doubt this is something the authors wanted.
So there is no legitimate reason to return by reference. My guess is that it is a leftover from PHP 4, when objects were values, and so assigning or returning an "object value" would copy it. In PHP 5, objects are not values; you can only manipulate them through object references, and assigning or returning an object reference by value never copies the object it points to.
The php documentation seems to explain why you have to uses =& even though the function is marked to return a refrence function &load_class
Returning References
Returning by reference is useful when you want to use a function to
find to which variable a reference should be bound. Do not use
return-by-reference to increase performance. The engine will
automatically optimize this on its own. Only return references when
you have a valid technical reason to do so. To return references, use
this syntax:
<?php class foo {
public $value = 42;
public function &getValue() {
return $this->value;
}
}
$obj = new foo;
$myValue = &$obj->getValue(); // $myValue is a reference to $obj->value, which is 42.
$obj->value = 2;
echo $myValue;
// prints the new value of $obj->value, i.e. 2. ?>
In this example,
the property of the object returned by the getValue function would be
set, not the copy, as it would be without using reference syntax.
Note: 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 for $myValue.
If you are asking what references in general are the documentation explains.
This question already has answers here:
PHP call_user_func vs. just calling function
(8 answers)
Closed 6 months ago.
I dont understand the function, call_user_func() in the sense that i get how it works but I'm not sure why its required or in what context to use it in php. As far as I'm concerned why not just call the function instead of calling a function with a function? Thnx!
call_user_func gives PHP the ability to treat methods and functions as quasi first-class citizens. In functional languages like javascript there is no need for these special tools per-se because a function is an object that happens to be callable.
PHP is getting closer to having this sort of notion though, especially with the closure support that came along with PHP5.3. Take a look at the comment I put under #deceze's answer. There are some other tools (namely variable functions, reflection and now closures) that offer the same basic functionality.
The most notable thing about call_user_func though is how it allows you to treat global functions, static classes and objects with a uniform interface. It's probably the closest thing they have to a single interface to invoke functions no matter how they're implemented. Internally the PHP core group of developers is working to homogenize some sort of a 'callable' or 'invokable' interface for the language, I'm sure we'll see a clean offering in PHP5.4 or the next major release.
I've had a couple situations where this was very necessary. For example, when I was creating a project that allowed a user to construct parts of a programming language, the system would wrap the "tag" definition in a function before running it for security reasons. However, in doing this, I can't simply call those functions, because I don't know when I need to call them, or even what the names would be. Enter call_user_func()...
So, I'm sure you're confused right now. Let me explain. What my system did, was take some XML, and convert it into PHP/HTML/JS with PHP. This allowed for rapid creation of GUIs (which was the goal). Take this XML for example:
<window id="login-win" title="Access Restricted" width="310" height="186" layout="accordion" layoutConfig="animate:true">
<panel id="login-panel" title="User Login">
<form id="login-form" title="Credentials">
<textbox id="login-uname" label="Username"/>
<password id="login-pass" label="Password"/>
<submit text="Login"/>
</form>
</panel>
<panel id="login-register" title="Register">
Nothing, you can't register!
</panel>
</window>
Each XML tag would be read, then fed into it's corresponding PHP function. That function would determine what to do for that 1 tag. In order to pull this off, I had files each named after the tag it handled. So, for example, the tag's file was "submit.php". This file's contents would get wrapped in a generated function, something like:
function tag_submit($variables, $parent, $children){
// deal with the data, then echo what is needed
}
This function's name would be stored in an array, with it's associated tag name, with the rest of the generated functions. This makes it so the function is generated once, and only created when needed, saving memory, since I would do a if(func_exists()) call to determine if I needed it or not.
However, since this is all dynamic, and the user may want to add in a new tag, for say, a < date > tag, I needed to use call_user_func() to get things to work. I can't hard-code a function call if I don't know what the name is.
Hope that all made sense. Basically, yes, it is a rarely used function, but it is still very very useful.
Named functions, anonymous functions, static methods, instance methods and objects with an "__invoke" method are collectively known as 'callables'. If I have a callable it should be possible to call it by placing parentheses afterwards "()" (I'll assume it takes no arguments). This works when our callable is stored in a variable, for example:
$f(); // Call $f
However, what if I store a callable in an object property?
$obj = new stdClass;
$obj->my_func = function() {};
$obj->my_func(); // Error: stdClass does not have a "my_func" method
The problem is that PHP's parser is getting confused because the code is ambiguous, it could mean calling a callable property or calling a method. PHP chooses to always treat this kind of code as a method call, so we cannot call callable properties this way. This is why we need 'call_user_func':
call_user_func($obj->my_func); // Calls $obj->my_func
There are other times when PHP doesn't understand the normal parentheses syntax; for example, PHP currently (5.5) doesn't know how to call the return value of another callable:
get_a_callable()(); // Parse error, unexpected "("
call_user_func(get_a_callable()); // Calls the return value of get_a_callable
It's also not currently possible to call a function definion directly, which is useful to work around PHP's lack of "let" statements, for example:
function($x) { echo $x . $x; }('hello'); // Parse error, unexpected "("
call_user_func(function($x) { echo $x . $x; }, 'hello'); // Calls the function
There are also times where it's useful to have function calls /reified/ as the 'call_user_func' function. For example, when using higher-order functions like array_map:
$funcs = [function() { return 'hello'; },
function() { return 'world'; }];
array_map('call_user_func', $funcs); // ['hello', 'world']
It's also useful when we don't know how many parameters will be needed, ie. we can't substitute our own "function($f, $x, $y, ...) { return $f($x, $y, ...); }".
One particularly nice definition is partial application, which only takes a few lines thanks to call_user_func and call_user_func_array:
function partial() {
$args1 = func_get_args(); // $f, $a, $b, $c, ...
return function() use ($args1) {
// Returns $f($a, $b, $c, ..., $d, $e, $f, ...)
$args2 = func_get_args(); // $d, $e, $f, ...
return call_user_func_array('call_user_func',
array_merge($args1, $args2));
};
}
Is it possible to use PHP array_walk with an anonymous function (before PHP 5.3)? or some workaround..
I'm using array_walk inside a public class method and I don't want to define a PHP function.. I want to do something like:
array_walk($array, function($value) {...});
If that's not possible is it possible to use a class method instead of a function?
Use create_function.
array_walk($array, create_function('&$v, $k', '$v...'));
Yes, you can pass an array as second parameter, containing the object and the method name as string:
class Foo {
public function a($a) {
array_walk($a, array($this, 'b'));
}
private function b($v, $k) {
print $v;
}
}
$f = new Foo();
$f->a(array('foo', 'bar'));
prints
foobar
The signature of the array_walk() (can be found in the documentation), defines the second argument as callback, for which the following description can be found:
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo(), empty(), eval(), exit(), isset(), list(), print() or unset().
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Static class methods can also be passed without instantiating an object of that class by passing the class name instead of an object at index 0.
Apart from common user-defined function, create_function() can also be used to create an anonymous callback function. As of PHP 5.3.0 it is possible to also pass a closure to a callback parameter.
It's possible using create_function().
Stolen from the manual:
<?php
$av = array("the ", "a ", "that ", "this ");
array_walk($av, create_function('&$v,$k', '$v = $v . "mango";'));
print_r($av);
?>
To answer the second portion (the first has been answered well enough I think, although I never really liked create_function):
If that's not possible is it possible to use a class method instead of a function?
Yes, pass array($instance,'methodname') or array('classname','methodname') for object methods and static methods respectively.
Would it be possible to write a class that is virtually indistinguishable from an actual PHP array by implementing all the necessary SPL interfaces? Are they missing anything that would be critical?
I'd like to build a more advanced Array object, but I want to make sure I wouldn't break an existing app that uses arrays everywhere if I substituted them with a custom Array class.
The only problems i can think of are the gettype() and the is_array() functions.
Check your code for
gettype($FakeArray) == 'array'
is_array($FakeArray)
Because although you can use the object just like an array, it will still be identified as an object.
In addition to the points made above, you would not be able to make user-space array type hints work with instances of your class. For example:
<?php
function f(array $a) { /*...*/ }
$ao = new ArrayObject();
f($ao); //error
?>
Output:
Catchable fatal error: Argument 1 passed to f() must be an array, object given
Other differences include the '+' operator for arrays (merging) and the failure of the entire array_* functions, including the commonly used array_merge and array_shift.