How can arguments to variadic functions be passed by reference in PHP?

谁都会走 提交于 2019-11-30 22:31:09

问题


Assuming it's possible, how would one pass arguments by reference to a variadic function without generating a warning in PHP? We can no longer use the '&' operator in a function call, otherwise I'd accept that (even though it would be error prone, should a coder forget it).

What inspired this is are old MySQLi wrapper classes that I unearthed (these days, I'd just use PDO). The only difference between the wrappers and the MySQLi classes is the wrappers throw exceptions rather than returning FALSE.

class DBException extends RuntimeException {}
...
class MySQLi_throwing extends mysqli {
    ...
    function prepare($query) {
        $stmt = parent::prepare($query);
        if (!$stmt) {
            throw new DBException($this->error, $this->errno);
        }
        return new MySQLi_stmt_throwing($this, $query, $stmt);
    }
}
// I don't remember why I switched from extension to composition, but
// it shouldn't matter for this question.
class MySQLi_stmt_throwing /* extends MySQLi_stmt */ {
    protected $_link, $_query, $_delegate;

    public function __construct($link, $query, $prepared) {
        //parent::__construct($link, $query);
        $this->_link = $link;
        $this->_query = $query;
        $this->_delegate = $prepared;
    }
    function bind_param($name, &$var) {
        return $this->_delegate->bind_param($name, $var);
    }
    function __call($name, $args) {
        //$rslt = call_user_func_array(array($this, 'parent::' . $name), $args);
        $rslt = call_user_func_array(array($this->_delegate, $name), $args);
        if (False === $rslt) {
            throw new DBException($this->_link->error, $this->errno);
        }
        return $rslt;
    }
}

The difficulty lies in calling methods such as bind_result on the wrapper. Constant-arity functions (e.g. bind_param) can be explicitly defined, allowing for pass-by-reference. bind_result, however, needs all arguments to be pass-by-reference. If you call bind_result on an instance of MySQLi_stmt_throwing as-is, the arguments are passed by value and the binding won't take.

try {
    $id = Null;
    $stmt = $db->prepare('SELECT id FROM tbl WHERE ...');
    $stmt->execute()
    $stmt->bind_result($id);
    // $id is still null at this point
    ...
} catch (DBException $exc) {
   ...
}

Since the above classes are no longer in use, this question is merely a matter of curiosity. Alternate approaches to the wrapper classes are not relevant. Defining a method with a bunch of arguments taking Null default values is not correct (what if you define 20 arguments, but the function is called with 21?). Answers don't even need to be written in terms of MySQL_stmt_throwing; it exists simply to provide a concrete example.


回答1:


There is no way of passing variable length argument lists by reference in PHP. It is a fundamental limitation of the language.

There is, however, a workaround with array(&$var1, &$var2...) syntax:

<?php

/** raise all our arguments to the power of 2 */
function pow2() {
        $args = &func_get_arg(0);

        for ($i = 0; $i< count($args); ++$i) {
            $args[$i] *= 2;
        }
}


$x = 1; $y = 2; $z = 3;
pow2(array(&$x, &$y, &$z)); // this is the important line

echo "$x, $y, $z"; // output "2, 4, 6"

?>

Test could also be declared function test($args) but I wanted to illustrate that this works with the func_get_args() family of functions. It is the array(&$x) that causes the variable to be passed by reference, not the function signature.

From a comment on PHP's documentation on function arguments: http://php.net/manual/en/functions.arguments.php




回答2:


As of PHP 5.6 you can pass arguments by reference to a variadic function. Here's an example from the RFC:

public function prepare($query, &...$params) {
    $stmt = $this->pdo->prepare($query);
    foreach ($params as $i => &$param) {
        $stmt->bindParam($i + 1, $param);
    }
    return $stmt;
}


来源:https://stackoverflow.com/questions/2612086/how-can-arguments-to-variadic-functions-be-passed-by-reference-in-php

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