The `prepare` , `bind_param`, `bind_result`, `fetch` result, `close` stmt cycle can be tedious at times. Here is an object that does all the mysqli mumbo jumbo for you when all you want is a select leaving you to the bare essential `preparedSelect` on a prepared stmt. The method returns the result set as a 2D associative array with the `select`ed columns as keys. I havent done sufficient error-checking and it also may have some bugs. Help debug and improve on it.
I used the bible.sql db from http://www.biblesql.net/sites/biblesql.net/files/bible.mysql.gz.
Baraka tele!
============================
<?php
class DB
{
public $connection;
#establish db connection
public function __construct($host="localhost", $user="user", $pass="", $db="bible")
{
$this->connection = new mysqli($host, $user, $pass, $db);
if(mysqli_connect_errno())
{
echo("Database connect Error : "
. mysqli_connect_error($mysqli));
}
}
#store mysqli object
public function connect()
{
return $this->connection;
}
#run a prepared query
public function runPreparedQuery($query, $params_r)
{
$stmt = $this->connection->prepare($query);
$this->bindParameters($stmt, $params_r);
if ($stmt->execute()) {
return $stmt;
} else {
echo("Error in $statement: "
. mysqli_error($this->connection));
return 0;
}
}
# To run a select statement with bound parameters and bound results.
# Returns an associative array two dimensional array which u can easily
# manipulate with array functions.
public function preparedSelect($query, $bind_params_r)
{
$select = $this->runPreparedQuery($query, $bind_params_r);
$fields_r = $this->fetchFields($select);
foreach ($fields_r as $field) {
$bind_result_r[] = &${$field};
}
$this->bindResult($select, $bind_result_r);
$result_r = array();
$i = 0;
while ($select->fetch()) {
foreach ($fields_r as $field) {
$result_r[$i][$field] = $$field;
}
$i++;
}
$select->close();
return $result_r;
}
#takes in array of bind parameters and binds them to result of
#executed prepared stmt
private function bindParameters(&$obj, &$bind_params_r)
{
call_user_func_array(array($obj, "bind_param"), $bind_params_r);
}
private function bindResult(&$obj, &$bind_result_r)
{
call_user_func_array(array($obj, "bind_result"), $bind_result_r);
}
#returns a list of the selected field names
private function fetchFields($selectStmt)
{
$metadata = $selectStmt->result_metadata();
$fields_r = array();
while ($field = $metadata->fetch_field()) {
$fields_r[] = $field->name;
}
return $fields_r;
}
}
#end of class
#An example of the DB class in use
$DB = new DB("localhost", "root", "", "bible");
$var = 5;
$query = "SELECT abbr, name from books where id > ?" ;
$bound_params_r = array("i", $var);
$result_r = $DB->preparedSelect($query, $bound_params_r);
#loop thru result array and display result
foreach ($result_r as $result) {
echo $result['abbr'] . " : " . $result['name'] . "<br/>" ;
}
?>
mysqli_stmt::prepare
mysqli_stmt_prepare
(PHP 5)
mysqli_stmt::prepare -- mysqli_stmt_prepare — Prepara uma declaração SQL para execução
Descrição
Estilo orientado a objeto (método)
Estilo procedural:
Prepares the SQL query pointed to by the null-terminated string query.
Os marcadores de parâmetros devem ser ligados as variáveis da aplicação utilizando-se mysqli_stmt_bind_param() e/ou mysqli_stmt_bind_result() antes de executar a declaração ou trazer os resultados.
Parâmetros
- stmt
-
Apenas para estilo de procedimento: Um identificador de statement retornado por mysqli_stmt_init().
- query
-
A query, como uma string. Deve conter somente uma única declaração SQL.
Você pode incluir um ou mais marcadores de parâmetros na declaração SQL colocando a interrogação (?) nas posições apropriadas.
Nota: Você não deve adicionar um ponto-e-virgula ou \g para terminar a declaração
Nota: Os marcadores são permitidos somente em alguns lugares das declarações SQL. Por exemplo, eles são permitidos na lista VALUES() de um INSERT (para especificar valores das colunas em uma linha), ou na comparação de uma coluna na cláusula WHERE para especificar um valor de comparação.
Entretanto, eles não são permitidos em identificadores (como nomes de tabela e coluna, na lista que nomeia os nomes das colunas a serem retornados por uma declaração SELECT), ou para especificar ambos os operandos de um operador binário como o = sinal de igual. A última restrição é necessária pois seria impossÃvel determinar o tipo do parâmetro. No geral, os parâmetros são permitidos somente em declarações de Linguagem de Manipulação de Dados (no inglês DML), e não em Linguagem Definição de Dados (DDL).
Valor Retornado
Retorna TRUE em caso de sucesso ou FALSE em falhas.
Exemplos
Exemplo #1 Estilo orientado a objeto
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* verifica coenxão */
if (mysqli_connect_errno()) {
printf("Conexão falhou: %s\n", mysqli_connect_error());
exit();
}
$cidade = "Curitiba";
/* cria uma declaração preparada */
$stmt = $mysqli->stmt_init();
if ($stmt->prepare("SELECT Estado FROM Cidade WHERE Nome=?")) {
/* atribui os parâmetros aos marcadores */
$stmt->bind_param("s", $cidade);
/* executa a query */
$stmt->execute();
/* atribui as variáveis de resultado */
$stmt->bind_result($estado);
/* busca o valor */
$stmt->fetch();
printf("%s está no estado %s\n", $cidade, $estado);
/* fecha a declaração */
$stmt->close();
}
/* fecha a conexão */
$mysqli->close();
?>
Exemplo #2 Estilo procedural
<?php
$link = mysqli_connect("localhost", "my_user", "my_password", "world");
/* verifica coenxão */
if (mysqli_connect_errno()) {
printf("Conexão falhou: %s\n", mysqli_connect_error());
exit();
}
$cidade = "Curitiba";
/* cria uma declaração preparada */
$stmt = mysqli_stmt_init($link);
if (mysqli_stmt_prepare($stmt, 'SELECT Estado FROM Cidade WHERE Nome=?')) {
/* atribui os parâmetros aos marcadores */
mysqli_stmt_bind_param($stmt, "s", $cidade);
/* executa a query */
mysqli_stmt_execute($stmt);
/* atribui as variáveis de resultado */
mysqli_stmt_bind_result($stmt, $estado);
/* busca o valor */
mysqli_stmt_fetch($stmt);
printf("%s está no estado %s\n", $cidade, $estado);
/* fecha a declaração */
mysqli_stmt_close($stmt);
}
/* fecha a conexão */
mysqli_close($link);
?>
O exemplo acima irá imprimir:
Curitiba está no estado Paraná
Veja Também
mysqli_stmt_init() - Initializes a statement and returns an object for use with mysqli_stmt_prepare, mysqli_stmt_execute() - Executa uma preparada query, mysqli_stmt_fetch() - Obtém resultados de um preparado comando e os coloca nas determinadas variáveis, mysqli_stmt_bind_param() - Passa variáveis para um preparado comando como parâmetros, mysqli_stmt_bind_result() - Passa variáveis para um preparado comando por resultado armazenado mysqli_stmt_close() - Closes a prepared statement.
mysqli_stmt::prepare
22-Apr-2009 07:25
16-Jun-2008 12:22
If you wrap the placeholders with quotation marks you will experience warnings like "Number of variables doesn't match number of parameters in prepared statement" (at least with INSERT Statements).
15-May-2008 09:06
A particularly helpful adaptation of this function and the call_user_func_array function:
// $params is sent as array($val=>'i', $val=>'d', etc...)
function db_stmt_bind_params($stmt, $params)
{
$funcArg[] = $stmt;
foreach($params as $val=>$type)
{
$funcArg['type'] .= $type;
$funcArg[] = $val;
}
return call_user_func_array('mysqli_stmt_bind_param', $funcArgs);
}
Thanks to 'sned' for the code.
15-Jan-2008 01:15
i've got some bad news for you guys if you haven't found out already.
the trick with mysqli_next_result() only prevents having the connection dropped after a stored procedure call.
apparently you can bind parameters for a prepared stored procedure call, but you'll get messed up records from mysqli_stmt_fetch() after mysqli_stmt_bind_result(), at least when the stored procedure itself contains a prepared statement.
a way to avoid data corruption could be specifying the CLIENT_MULTI_STATEMENTS flag in mysqli_real_connect(), if it wasn't disabled entirely (for security reasons, as they say). another option is to use mysqli_multi_query(), but then you can't bind at all.
04-Jun-2007 01:59
In reference to what lachlan76 said before, stored procedures CAN be executed through prepared statements as long as you tell the DB to move to the next result before executing again.
Example (Five calls to a stored procedure):
<?php
for ($i=0;$i<5;$i++) {
$statement = $mysqli->stmt_init();
$statement->prepare("CALL some_procedure( ? )");
// Bind, execute, and bind.
$statement->bind_param("i", 1);
$statement->execute();
$statement->bind_result($results);
while($statement->fetch()) {
// Do what you want with your results.
}
$statement->close();
// Now move the mysqli connection to a new result.
while($mysqli->next_result()) { }
}
?>
If you include the last statement, this code should execute without the nasty "Commands out of sync" error.
29-Nov-2006 02:59
Do not try to use a stored procedure through a prepared statement.
Example:
<?php
$statement = $mysqli->stmt_init();
$statement->prepare("CALL some_procedure()");
?>
If you attempt to do this, it will fail by dropping the connection during the next query. Use mysqli_multi_query instead.
Example:
<?php
$mysqli->multi_query("CALL some_procedure()");
do
{
$result = $mysqli->store_result();
// Do your processing work here
$result->free();
} while($mysqli->next_result());
?>
This means that you cannot bind parameters or results, however.
07-Oct-2005 10:35
If you select LOBs use the following order of execution or you risk mysqli allocating more memory that actually used
1)prepare()
2)execute()
3)store_result()
4)bind_result()
If you skip 3) or exchange 3) and 4) then mysqli will allocate memory for the maximal length of the column which is 255 for tinyblob, 64k for blob(still ok), 16MByte for MEDIUMBLOB - quite a lot and 4G for LONGBLOB (good if you have so much memory). Queries which use this order a bit slower when there is a LOB but this is the price of not having memory exhaustion in seconds.
