Ligue dinamicamente parâmetros mysqli_stmt e, em seguida, lê o resultado (PHP)

Estou tentando ligar dinamicamente parâmetros mysql_stmt e obter o resultado em uma matriz associativa. Eu encontrei esta postagem aqui no stackoverflow onde Amber postou uma resposta com o seguinte código:

Postagem original: como fazer uma class de extensão mysqli apropriada com declarações preparadas?

“Supondo que você realmente esteja querendo escrever sua própria versão (ao contrário de utilizar uma das bibliotecas existentes, outras respostas sugeriram – e essas também são boas opções) …

Aqui estão algumas das funções que você pode achar útil analisar. O primeiro permite que você vincule os resultados de uma consulta a uma matriz associativa e a segunda permite que você passe duas matrizes, uma matriz de chaves ordenada e outra uma matriz associativa de dados para essas chaves e que esses dados se liguem a uma declaração preparada: ”

function stmt_bind_assoc (&$stmt, &$out) { $data = mysqli_stmt_result_metadata($stmt); $fields = array(); $out = array(); $fields[0] = $stmt; $count = 1; while($field = mysqli_fetch_field($data)) { $fields[$count] = &$out[$field->name]; $count++; } call_user_func_array(mysqli_stmt_bind_result, $fields); } function stmt_bind_params($stmt, $fields, $data) { // Dynamically build up the arguments for bind_param $paramstr = ''; $params = array(); foreach($fields as $key) { if(is_float($data[$key])) $paramstr .= 'd'; elseif(is_int($data[$key])) $paramstr .= 'i'; else $paramstr .= 's'; $params[] = $data[$key]; } array_unshift($params, $stmt, $paramstr); // and then call bind_param with the proper arguments call_user_func_array('mysqli_stmt_bind_param', $params); } 

Eu tentei estudar o código para entender o que faz e eu fiz a segunda function funcionar corretamente, mas eu não sei o que devo fazer para poder utilizar a primeira function. Como faço para usá-lo para recuperar uma matriz semelhante a mysqli_result :: fetch_assoc ()?

Eu quero ser capaz de utilizar o resultado da maneira como você costumava fazer com:

 while ($row = mysql_fetch_array($result)){ echo $row['foo']." ".$row['bar']; } 

Por favor, ajude-me a fazer algum progresso com isso 🙂

Ok, aqui está uma maneira de fazê-lo:

Editado, para corrigir erros ao buscar várias linhas

 $sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?"; $params = array('Australia','Victoria'); /* In my real app the below code is wrapped up in a class But this is just for example's sake. You could easily throw it in a function or class */ // This will loop through params, and generate types. eg 'ss' $types = ''; foreach($params as $param) { if(is_int($param)) { $types .= 'i'; //integer } elseif (is_float($param)) { $types .= 'd'; //double } elseif (is_string($param)) { $types .= 's'; //string } else { $types .= 'b'; //blob and unknown } } array_unshift($params, $types); // Start stmt $query = $this->connection->stmt_init(); // $this->connection is the mysqli connection instance if($query->prepare($sql)) { // Bind Params call_user_func_array(array($query,'bind_param'),$params); $query->execute(); // Get metadata for field names $meta = $query->result_metadata(); // initialise some empty arrays $fields = $results = array(); // This is the tricky bit dynamically creating an array of variables to use // to bind the results while ($field = $meta->fetch_field()) { $var = $field->name; $$var = null; $fields[$var] = &$$var; } $fieldCount = count($fieldNames); // Bind Results call_user_func_array(array($query,'bind_result'),$fields); $i=0; while ($query->fetch()){ for($l=0;$l<$fieldCount;$l++) $results[$i][$fieldNames[$l]] = $fields[$fieldNames[$l]]; $i++; } $query->close(); // And now we have a beautiful // array of results, just like //fetch_assoc echo "
"; print_r($results); echo "

"; }

A Resposta do Emmanuel funciona bem, se apenas uma linha for selecionada! Se a consulta selecionar várias linhas, a tabela de resultados de $ para cada linha é um resultado, mas o resultado sempre é preenchido com a última input. Com uma pequena mudança na busca () – enquanto funciona bem.

$ sqlStmt é uma string, preenchida com mysql-query

$ params é uma matriz, preenchida com as variables ​​que devem ser passadas

$ resultados é uma matriz vazia, que contém o resultado

  if (!is_string($sqlStmt) || empty($sqlStmt)) { return false; } // initialise some empty arrays $fields = array(); $results = array(); if ($stmt = $this->prepare($sqlStmt)) { // bind params if they are set if (!empty($params)) { $types = ''; foreach($params as $param) { // set param type if (is_string($param)) { $types .= 's'; // strings } else if (is_int($param)) { $types .= 'i'; // integer } else if (is_float($param)) { $types .= 'd'; // double } else { $types .= 'b'; // default: blob and unknown types } } $bind_names[] = $types; for ($i=0; $iexecute(); // Get metadata for field names $meta = $stmt->result_metadata(); // This is the tricky bit dynamically creating an array of variables to use // to bind the results while ($field = $meta->fetch_field()) { $var = $field->name; $$var = null; $fields[$var] = &$$var; } // Bind Results call_user_func_array(array($stmt,'bind_result'),$fields); // Fetch Results $i = 0; while ($stmt->fetch()) { $results[$i] = array(); foreach($fields as $k => $v) $results[$i][$k] = $v; $i++; } // close statement $stmt->close(); } 

Apenas para comparar excelentes respostas de @Emmanuel e @matzino com o código que você pode obter se escolher PDO sobre mysqli:

 $sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?"; $params = array('Australia','Victoria'); $stm = $query->prepare($sql); $stm->execute($params); $results = $stm->fetchAll(); // or fetch() or fetchColumn() depends on expected type 

whoops, isso é tudo?

Depois de usar a resposta acima, percebi que havia alguma limpeza necessária para mim, particularmente a parte ‘fieldNames []’. O código abaixo é em estilo processual. Espero que venha de ser útil a alguém.

Cortei o código de uma class que fiz, que pode consultar dados dinamicamente. Há algumas coisas que removi para facilitar a leitura. Na class, eu permiti que o usuário defina tabelas de definições e foreign keys para que a input de dados no front-end seja restrita, bem como opções de filtragem e sorting para os referidos dados relacionados. Estes são todos os parâmetros que eu removi, bem como o construtor de consultas automáticas.

 $sql = "SELECT `first_name`,`last_name` FROM `users` WHERE `country` =? AND `state`=?"; $params = array('Australia','Victoria'); ////////////// GENERATE PARAMETER TYPES IF ANY ////////////// // This will loop through parameters, and generate types. ex: 'ss' $types = ''; $params_size = sizeof($params); if($params_size > 0) { foreach($params as $param) { if(is_int($param)) { $types .= 'i'; //integer }else if(is_float($param)) { $types .= 'd'; //double }else if(is_string($param)) { $types .= 's'; //string }else { $types .= 'b'; //blob and unknown } } array_unshift($params, $types); } //////////////////////////////////////////////////////////// // This is the tricky part to dynamically create an array of // variables to use to bind the results //below from http://php.net/manual/en/mysqli-result.fetch-field.php /* name The name of the column orgname Original column name if an alias was specified table The name of the table this field belongs to (if not calculated) orgtable Original table name if an alias was specified def Reserved for default value, currently always "" db Database (since PHP 5.3.6) catalog The catalog name, always "def" (since PHP 5.3.6) max_length The maximum width of the field for the result set. length The width of the field, as specified in the table definition. charsetnr The character set number for the field. flags An integer representing the bit-flags for the field. type The data type used for this field decimals The number of decimals used (for integer fields) */ /// FIELD TYPE REFERENCE /// /* numerics ------------- BIT: 16 TINYINT: 1 BOOL: 1 SMALLINT: 2 MEDIUMINT: 9 INTEGER: 3 BIGINT: 8 SERIAL: 8 FLOAT: 4 DOUBLE: 5 DECIMAL: 246 NUMERIC: 246 FIXED: 246 dates ------------ DATE: 10 DATETIME: 12 TIMESTAMP: 7 TIME: 11 YEAR: 13 strings & binary ------------ CHAR: 254 VARCHAR: 253 ENUM: 254 SET: 254 BINARY: 254 VARBINARY: 253 TINYBLOB: 252 BLOB: 252 MEDIUMBLOB: 252 TINYTEXT: 252 TEXT: 252 MEDIUMTEXT: 252 LONGTEXT: 252 */ if($stmt = mysqli_prepare($db_link, $query)) { // BIND PARAMETERS IF ANY // if($params_size > 0) { call_user_func_array(array($stmt, 'bind_param'), makeValuesReferenced($params)); } mysqli_stmt_execute($stmt); $meta = mysqli_stmt_result_metadata($stmt); $field_names = array(); $field_length = array(); $field_type = array(); $output_data = array(); /// THIS GET THE NAMES OF THE FIELDS AND ASSIGNS NEW VARIABLES USING THE FIELD NAME. THESE VARIABLES ARE THEN SET TO NULL /// $count = 0; while($field = mysqli_fetch_field($meta)) { $field_names[$count] = $field->name;// field names $var = $field->name; $$var = null; $field_names_variables[$var] = &$$var;// fields variables using the field name $field_length[$var] = $field->length;// field length as defined in table $field_type[$var] = $field->type;// field data type as defined in table (numeric return) $count++; } setFieldLengthInfo($field_length); setFieldTypesInfo($field_type); $field_names_variables_size = sizeof($field_names_variables); call_user_func_array(array($stmt, 'bind_result'), $field_names_variables); $count = 0; while(mysqli_stmt_fetch($stmt)) { for($l = 0; $l < $field_names_variables_size; $l++) { $output_data[$count][$field_names[$l]] = $field_names_variables[$field_names[$l]];/// THIS SETS ALL OF THE FINAL DATA USING THE DYNAMICALLY CREATED VARIABLES ABOVE } $count++; } mysqli_stmt_close($stmt); echo "
"; print_r($output_data); echo "

"; } function makeValuesReferenced($arr) { $refs = array(); foreach($arr as $key => $value) $refs[$key] = &$arr[$key]; return $refs; }