2010-04-10 15 views
12

Suponiendo que sea posible, ¿cómo se pasarían argumentos por referencia a una función variadica sin generar una advertencia en PHP? Ya no podemos usar el operador '&' en una llamada a función, de lo contrario, aceptaría eso (aunque sería propenso a errores, un codificador debería olvidarlo).¿Cómo se pueden pasar argumentos a funciones variadic por referencia en PHP?

Lo que inspiró esto son las viejas clases de envoltorios de MySQL que encontré (en estos días, solo usaría PDO). La única diferencia entre los wrappers y las clases de MySQLi es que los wrappers lanzan excepciones en lugar de devolver 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; 
    } 
} 

La dificultad radica en llamar a métodos como bind_result en el contenedor. Las funciones de constante constante (por ejemplo, bind_param) se pueden definir explícitamente, lo que permite pasar de referencia. bind_result, sin embargo, necesita que todos los argumentos pasen por referencia. Si llama al bind_result en una instancia de MySQLi_stmt_throwing tal como está, los argumentos se pasan por valor y el enlace no se realizará.

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) { 
    ... 
} 

Dado que las clases anteriores ya no se utilizan, esta pregunta es simplemente una cuestión de curiosidad. Los enfoques alternativos a las clases contenedoras no son relevantes. Definir un método con un grupo de argumentos que toma Null valores por defecto no es correcto (¿qué ocurre si define 20 argumentos, pero la función se llama con 21?). Las respuestas ni siquiera necesitan escribirse en términos de MySQL_stmt_throwing; existe simplemente para proporcionar un ejemplo concreto.

+0

chillidos ... acaba de encontrar la cuestión que éste dups: http://stackoverflow.com/questions/1925253/php-variable-length-argument-list-by-reference , aunque prefiero la respuesta de Meager a continuación a la respuesta aceptada para la otra pregunta. – outis

Respuesta

6

No hay forma de pasar listas de argumentos de longitud variable por referencia en PHP. Es una limitación fundamental del lenguaje.

Hay, sin embargo, una solución con array(&$var1, &$var2...) sintaxis:

<?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 podría también ser declarado function test($args) pero quería ilustrar que esto funciona con el func_get_args() familia de funciones. Es el array(&$x) que hace que la variable pase por referencia, no la firma de la función.

Desde un comentario en la documentación de PHP en los argumentos de funciones: http://php.net/manual/en/functions.arguments.php

+0

Llámame loco, pero ¿quisiste decir que tu método 'test' sería' pow2' o viceversa? –

+0

@anthony El primero, gracias. – meagar

+0

@meager, No hay problema, es tarde por aquí, era curioso si comencé a tener alucinaciones. –

8

A partir de PHP 5.6 se puede pass arguments by reference a una función variadic. He aquí un ejemplo de the RFC:

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