简体   繁体   中英

Trying to understand odd variant of a PHP array_map() call

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?

-- roschler

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM