Guía rápida Esta guía rápida le ayudará a elegir y obtener familiaridad con la API MySQL de PHP. Esta guía rápida ofrece información general sobre la extensión mysqli. Se proporcionan ejemplos de código para los aspectos más importantes de la API. Los conceptos de la base de datos se explican con el grado necesario para presentar conceptos específicos de MySQL. Se requiere: Familiaridad con el lenguaje de programación PHP, con el lenguaje SQL, y conociemientos básicos del servidor MySQL.
Interfaz dual: procedimental y orientada a objetos La extensión mysqli ofrece una interfaz dual. Soporta el paradigma de programación procedimental y el orientado a objetos. Los usuarios que migren desde la extensión mysql antigua pueden preferir la interfaz procedimental. Esta interfaz es similar a la de la extensión antigua de mysql. En la mayoría de los casos, los nombres de funciones difieren únicamente por el prefijo. Algunas funciones de mysqli toman como primer argumento un gestor de conexión, mientras que las funciones similares de la antigua interfaz de mysql lo toman como el último argumento opcional. Migración sencilla desde la antigua extensión mysql ]]> &example.outputs; La interfaz orientada a objetos Además de la clásica interfaz procedimental, los usuarios pueden optar por usar la interfaz orientada a objetos. La documentación está organizada según la interfaz orientada a objetos. Esta interfaz muestra las funciones agrupadas por su propósito, haciendo más fácil los comienzos. La sección de referencia proporciona ejemplos para ambas variantes de sintaxis. No existen diferencias significativas de rendimiento entre las dos interfaces. Los usuarios puede basar su elección en sus preferencias personales. Interfaz orientada a objetos y procedimental connect_errno) { echo "Fallo al conectar a MySQL: " . $mysqli->connect_error; } $resultado = $mysqli->query("SELECT 'elecciones para complacer a todos.' AS _msg FROM DUAL"); $fila = $resultado->fetch_assoc(); echo $fila['_msg']; ?> ]]> &example.outputs; Se usa la interfaz orientada a objetos en el inicio rápido porque la sección de referencia está organizada de esta manera. Mezclar estilos Es posible cambiar entre los estilos en cualquier momento. No se recomienda mezclar los dos estilos por razones de claridad y estilo de código. Estilo de codificación malo connect_errno) { echo "Fallo al conectar a MySQL: " . $mysqli->connect_error; } $resultado = mysqli_query($mysqli, "SELECT 'Estilo malo pero posible.' AS _msg FROM DUAL"); if (!$resultado) { echo "Fallo al ejecutar la consulta: (" . $mysqli->errno . ") " . $mysqli->error; } if ($fila = $resultado->fetch_assoc()) { echo $fila['_msg']; } ?> ]]> &example.outputs; Véase también mysqli::__construct mysqli::query mysqli_result::fetch_assoc $mysqli::connect_errno $mysqli::connect_error $mysqli::errno $mysqli::error El resumen de funciones de la extensión MySQLi
Conexiones El servidor MySQL soporta el uso de diferentes capas de transporte para conexiones. Las conexiones usan TCP/IP, sockets de dominio Unix o tuberías con nombre de Windows. El nombre del host localhost tiene un significado especial. Está vinculado al uso de sockets de dominio Unix. No es posible abrir una conexión TCP/IP usando como nombre de host localhost, se debe usar 127.0.0.1 en su lugar. Significado especial de localhost connect_errno) { echo "Fallo al conectar a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } echo $mysqli->host_info . "\n"; $mysqli = new mysqli("127.0.0.1", "usuario", "contraseña", "basedatos", 3306); if ($mysqli->connect_errno) { echo "Fallo al conectar a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } echo $mysqli->host_info . "\n"; ?> ]]> &example.outputs; Valores predeterminados de los parámetros de conexión Dependiendo de la función de conexión usada se pueden omitir varios parámetros. Si no se proporciona un parámetro, la extensión intentará usar los valores predeterminados que están establecidos en el fichero de configuración de PHP. Configuración predeterminada Los valores resultantes de los parámetros son pasados a la biblioteca cliente que esté usando esta extensión. Si la biblioteca cliente detecta parámetros vacíos o no establecidos, puede usar los valores internos predeterminados de la biblioteca. Valores predeterminados internos de conexión de la biblioteca Si el valor del host no está establecido o está vacío, la biblioteca cliente usará una conexión de socket Unix sobre localhost. Si el socket no está establecido o está vacío, y es solicitada una conexión de socket Unix, se intentará una conexiónal socket predeterminado de /tmp/mysql.sock. En sistemas Windows, el nombre de host . es interpretado por la biblioteca cliente como un intento de abrir una conexión basada en una tubería con nombre de Windows. En este caso el parámetro del socket se interpreta como el nombre de la tubería. Si no se proporciona o está vacío, se usará como socket (nombre de la tubería) \\.\pipe\MySQL. Si no está establecida una conexión basada en un socket de dominio Unix ni en una tubería con nombre de Windows y el valor del parámetro del puerto no está establecido, la biblioteca usuará como puerto predeterminado el 3306. La biblioteca mysqlnd y la Biblioteca Cliente de MySQL (libmysqlclient) implementan la misma lógica para determinados valores predeterminados. Opciones de conexión Las opciones de conexión están disponibles para, por ejemplo, establecer comando iniciales que son ejecutados sobre la conexión, o para solicitar el uso de ciertos conjuntos de caracteres. Las opciones de conexión deben ser establecidas antes de que se establezcla una conexión de red. Para configurar una opción de conexión, la operación de conexión ha de ser realizada en tres pasos: crear un gestor de conexión con mysqli_init, establecer las opciones solicitadas usando mysqli_options, y establecer la conexión de red con mysqli_real_connect. Caché de conexiones La extensión mysqli soporta conexiones persistentes a bases de datos, las cuales son un tipo especial de conexiones almacenadas en caché. Por defecto, cada conexión a una base de datos abierta por un script es cerrada explícitamente por el usuario durante el tiempo de ejecución o liberada automáticamente al finalizar el script. Una conexión persistente no. En su lugar, se coloca en una caché para su reutilización posterior, si una conexión es abierta al mismo servidor usando el mismo nombre de usuario, contraseña, socket, puerto y base de datos predeterminada. La reutilización ahorra gastos de conexioń. Cada procesos de PHP utiliza su propia caché de conexiones mysqli. Dependiendo de modelo de distribución del servidor web, un proceso PHP puede servir una o múltiples peticiones. Por lo tanto, una conexión almacenada en caché puede ser utilizada posteriormente por uno o más scripts. Conexiones persistentes Si una conexión persistente no usada con una combiación dada de host, nombre de usuario, contraseña, socket, puerto y base de datos predeterminada no se puede encontrar en la caché de conexiones, mysqli abrirá una nueva conexión. El uso de conexiones persistentes se puede habilitar y deshabilitar usando la directiva de PHP mysqli.allow_persistent. El número total de conexiones abiertas por un script puede ser limitado con mysqli.max_links. El número máximo de conexiones persistentes por proceso de PHP puede restringirse con mysqli.max_persistent. Observe que el servidor web puede engendrar muchos procesos de PHP. Una queja común sobre las conexiones persistentes qes que su estado no es reiniciado antes de su uso. Por ejemplo, las transacciones abiertas y no finalizadas no son automéáticamente reanudadas. También, los cambios de autorización que ocurran durante la colocación de la conexión en la caché y su reutilización no están reflejados. Esto puede verse como un efecto secundario no deseado. Al contrario, el nombre persistente puede entenderse como una promesa de que el estado persiste. La extensión mysqli soporta ambas interpretaciones de una conexión persistente: el estado persiste, y el estado se reinicia antes de la reutilización. Lo predeterminado es la reiniciación. Antes de que una conexión sea reutilizada, la extensión llama implicitamente a mysqli_change_user para reiniciar el estado. La conexión persistente aparece al usuario como si estuviera recién abierta. No son visibles ningún artefacto de los usos previos. La función mysqli_change_user es una operación cara. Para un mejor rendimiento, los usuarios pueden recompilar la extensión con la bandera de compilación MYSQLI_NO_CHANGE_USER_ON_PCONNECT establecida. Corresponde al usuario elegir entre comportamiento seguro o mejor rendimiento. Ambas son metas de optimización válidas. Para facilitar el uso, el comportamiento seguro es el predeterminado a expensas de un rendimiento máximo. See also mysqli::__construct mysqli::init mysqli::options mysqli::real_connect mysqli::change_user $mysqli::host_info Opciones de configuración de MySQLi Conexiones persistentes a bases de datos
Ejecutar sentencias Las sentencias se pueden ejecutar con las funciones mysqli_query, mysqli_real_query y mysqli_multi_query. La función mysqli_query es la más común, y combina la sentencia de ejecución con su conjunto de resultados obtenido de un buffer, si lo hubiera, en una llamada. Llamar a mysqli_query es idéntico que llamar a mysqli_real_query seguido de mysqli_store_result. Conectando a MySQL connect_errno) { echo "Falló la conexión con MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } ?> ]]> Conjuntos de resultados almacenados en buffer Después de la ejecución de sentencias, los resultados pueden recuperarse de una sola vez para que sean almacenados en buffer por el cliente o leyendo fila a fila. El almacenamieno en buffer de conjuntos de resultados en el lado del cliente permite al servidor liberar recursos asociados con los resultados de la sentencia tan pronto como sea posible. Generalmente hablando, los clientes son lentos consumiendo conjuntos de resultados. Por lo tanto, se recomienda usar conjuntos de resultados almacenados en buffer. mysqli_query combina la ejecución de sentencias y el almacenamiento en buffer de conjuntos de resultados. Las aplicaciones de PHP pueden navegar libremente a través de resultados almacenados en buffer. La navegación es más rápida debido a que los conjuntos de resultados se mantienen en la memoria del cliente. Tenga en cuenta que a menudo es más fácil procesar datos en el cliente que hacerlo el servidor. Navegación a través de resultados almacenados en buffer connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } $resultado = $mysqli->query("SELECT id FROM test ORDER BY id ASC"); echo "Orden inverso...\n"; for ($num_fila = $resultado->num_rows - 1; $num_fila >= 0; $num_fila--) { $resultado->data_seek($num_fila); $fila = $resultado->fetch_assoc(); echo " id = " . $fila['id'] . "\n"; } echo "Orden del conjunto de resultados...\n"; $resultado->data_seek(0); while ($fila = $resultado->fetch_assoc()) { echo " id = " . $fila['id'] . "\n"; } ?> ]]> &example.outputs; Conjuntos de resultados no almacenados en buffer Si la memoria del cliente es un recurso escaso y no es necesaria la liberación de recursos del servidor tan pronto como sea posible para mantener la carga del servidor baja, se pueden usar conjuntos de resultados no almacenados en buffer. Recorrer resultados no almacenados en buffer no es posible antes de que todas las filas hayan sido leídas. Navegación a través de resultados no almacenados en buffer real_query("SELECT id FROM test ORDER BY id ASC"); $resultado = $mysqli->use_result(); echo "Orden del conjunto de resultados...\n"; while ($fila = $resultado->fetch_assoc()) { echo " id = " . $fila['id'] . "\n"; } ?> ]]> Tipos de datos de los valores del conjunto de resultados Las funciones mysqli_query, mysqli_real_query y mysqli_multi_query se usan para ejecutar sentencias no preparadas. Al nivel del Protocolo Cliente Servidor de MySQL, el comando COM_QUERY y el protocolo de texto se usan para la ejecución de sentencias. Con el protocolo texto, el servidor MySQL convierte todos los datos de los conjuntos de resultados en cadenas antes de enviarlos. Esta conversión se realiza sin considerar el tipo de datos de las columnas del conjunto de resultados SQL. Las bibliotecas cliente de mysql reciben todos los valores de las columnas como cadenas. No se realiza ninguna conversión del lado del cliente pava volver a convertir las columnas a susu tipos nativos. En su lugar, todos los valores son proporcionados como cadenas de PHP. El protocolo de texto devuelve cadenas de manera predeterminado connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } $resultado = $mysqli->query("SELECT id, etiqueta FROM test WHERE id = 1"); $fila = $resultado->fetch_assoc(); printf("id = %s (%s)\n", $fila['id'], gettype($fila['id'])); printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta'])); ?> ]]> &example.outputs; Es posible convertir valores de columnas de tipo integer y float a números de PHP estableciendo la opción de conexión MYSQLI_OPT_INT_AND_FLOAT_NATIVE, si se esta´utilizando la biblioteca mysqlnd. Si se establece, la biblioteca mysqlnd comprobará los tipos de columna de los metadatos del conjunto de resultados y convertirá las columnas númerocas de SQL a números de PHP, si el rango de valores del tipo de datos de PHP lo permite. De esta forma, por ejemplo, las columnas INT de SQL son devueltas como enteros. Tipos de datos nativos con mysqlnd y la opción de conexión options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); $mysqli->real_connect("ejemplo.com", "user", "contraseña", "basedatos"); if ($mysqli->connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } $resultado = $mysqli->query("SELECT id, etiqueta FROM test WHERE id = 1"); $fila = $resultado->fetch_assoc(); printf("id = %s (%s)\n", $fila['id'], gettype($fila['id'])); printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta'])); ?> ]]> &example.outputs; Véase también mysqli::__construct mysqli::init mysqli::options mysqli::real_connect mysqli::query mysqli::multi_query mysqli::use_result mysqli::store_result mysqli_result::free
Sentencias Preparadas Las bases de datos MySQL soportan sentencias preparadas. Una sentencia preparada o una sentencia parametrizada se usa para ejecutar la misma sentencia repetidamente con gran eficiencia. Flujo de trabajo básico La ejecución de sentencias preparadas consiste en dos etapas: la preparación y la ejecución. En la etapa de preparación se envía una plantilla de sentencia al servidor de bases de datos. El servidor realiza una comprobación de sintaxis e inicializa los recursos internos del servidor para su uso posterior. El servidor de MySQL soporta el uso de parámetros de sustitución posicionales anónimos con ?. Primera etapa: prepación connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } /* Sentencia no preparada */ if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } /* Sentencia preparada, etapa 1: preparación */ if (!($sentencia = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } ?> ]]> La preparación es seguida de la ejecución. Durante la ejecución el cliente vincula los valores de los parámetros y los envía al servidor. El servidor crea una sentencia desde la plantilla de la sentencia y los valores vinculados para ejecutarla usando los recursos internos previamente creados. Segunda etapa: vincular y ejecutar bind_param("i", $id)) { echo "Falló la vinculación de parámetros: (" . $sentencia->errno . ") " . $sentencia->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } ?> ]]> Ejecución repetida Una sentencia preparada se puede ejecutar repetidamente. En cada ejecución el valor actual de la variable vinculada se evalúa y se envía al servidor. La sentencia no se analiza de nuevo. La plantilla de la sentencia no es transferida otra vez al servidor. INSERT preparada una vez, ejecutada múltiples veces connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } /* Sentencia no preparada */ if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } /* Sentencia preparada, etapa 1: preparación */ if (!($sentencia = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } /* Sentencia preparada, etapa 2: vinculación y ejecución */ $id = 1; if (!$sentencia->bind_param("i", $id)) { echo "Falló la vinculación de parámetros: (" . $sentencia->errno . ") " . $sentencia->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } /* Sentencia preparada: ejecución repetida, sólo datos transferidos desde el cliente al servidor */ for ($id = 2; $id < 5; $id++) { if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } } /* se recomienda el cierre explícito */ $sentencia->close(); /* Sentencia no preparada */ $resultado = $mysqli->query("SELECT id FROM test"); var_dump($resultado->fetch_all()); ?> ]]> &example.outputs; array(1) { [0]=> string(1) "1" } [1]=> array(1) { [0]=> string(1) "2" } [2]=> array(1) { [0]=> string(1) "3" } [3]=> array(1) { [0]=> string(1) "4" } } ]]> Cada sentencia preparada ocupa recursos del servidor. Las sentencias deberían cerrarse explícitamente inmediatamente después de su uso. Si no se realiza explícitamente, la sentencia será cerrada cuando el gestor de la sentencia sea liberado por PHP. Usar una sentencia preparada no es siempre la manera más eficiente de ejecutar una sentencia. Una sentencia preparada ejecutada una sola vez causa más viajes de ida y vuelta desde el cliente al servidor que una sentencia no preparada. Es por esta razón por lo que SELECT no se ejecuta arriba como una sentencia preparada. También se ha de considerar el uso de la sintaxis SQL multi-INSERT de MySQL para sentencias INSERT. Por ejemplo, multi-INSERT requiere menos viajes de ida y vuelta entre el servidor y el cliente que la sentencia preparada mostrada arriba. Menos viajes de ida y vuelta usando SQL multi-INSERT query("INSERT INTO test(id) VALUES (1), (2), (3), (4)")) { echo "Falló multi-INSERT: (" . $mysqli->errno . ") " . $mysqli->error; } ?> ]]> Tipos de datos de los valores del conjunto de resultados El Protocolo Cliente Servidor de MySQL define un protocolo de transporte de datos diferente para sentencias preparadas y no preparadas. Las sentencias preparadas usan el llamado protocolo binario. El servidor de MySQL envía los datos del conjunto de resultados "tal cual" en formato binario. Los resultados no son serializados en cadenas antes del envío. Las bibliotecas cliente no reciben cadenas solamente. En su lugar, recibirán datos binarios e intentarán convertir los valores a los tipos de datos de PHP apropiados. Por ejemplo, los resultados de una columna de SQL INT serán proporcionados como variables de tipo integer de PHP. Tipos de datos nativos connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } $sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test WHERE id = 1"); $sentencia->execute(); $resultado = $sentencia->get_result(); $fila = $resultado->fetch_assoc(); printf("id = %s (%s)\n", $fila['id'], gettype($fila['id'])); printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta'])); ?> ]]> &example.outputs; Este comportamiento difiere de las sentencias no preparadas. Por defecto, las sentencias no preparadas devolverán todos los resultados como cadenas. Este comportamiento predeterminado se puede cambiar usando una opción de conexión. Si se utiliza la opción de conexión no existirán diferencias. Obtener resultados usando variables vinculadas Los resultados de las sentencias preparadas pueden ser recuperados mediante varibles de salida vinculadas, o por la petición de un objeto mysqli_result. Las variables de salida deben ser vinculadas después de la ejecución de la sentencia. Una variable debe estar vinculada para cada columna del conjunto de resultados de las sentencias. Vinculación de variables de salida connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $mysqli->errno . ") " . $mysqli->error; } $id_salida = NULL; $etiqueta_salida = NULL; if (!$sentencia->bind_result($id_salida, $etiqueta_salida)) { echo "Falló la vinculación de los parámetros de salida: (" . $sentencia->errno . ") " . $sentencia->error; } while ($sentencia->fetch()) { printf("id = %s (%s), etiqueta = %s (%s)\n", $id_salida, gettype($id_salida), $etiqueta_salida, gettype($etiqueta_salida)); } ?> ]]> &example.outputs; Las sentencias preparadas devuelven de manera predeterminada conjuntos de resultados no almacenados en buffer. Los resultados de la sentencia no son obtenidas y transferidas implícitamente desde el servidor al cliente para el almacenamiento en buffer de lado del cliente. El conjunto de resultados toma recursos del servidor hasta que todos los resultados hayan sido obtenidos por el cliente. Por lo que se recomienda consumir resultados cuando sea oportuno. Si un cliente falla al obtener todos los resultados o el cliente cierra la consulta antes de haber obtenido todos los datos, los datos han de ser obtenidos implícitamente por mysqli. También es posible almacenar en buffer los resutados de una sentencia preparada usando mysqli_stmt_store_result. Obtener los resultados usando la interfaz mysqli_result En lugar de usar resultados vinculados, los resultados también se pueden recuperar a través de la interfaz mysqli_result. mysqli_stmt_get_result devuelve un conjunto de resultados almacenado en buffer. Usar mysqli_result para obtener los resultados connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test ORDER BY id ASC"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } if (!($resultado = $sentencia->get_result())) { echo "Falló la obtención del conjunto de resultados: (" . $sentencia->errno . ") " . $sentencia->error; } var_dump($resultado->fetch_all()); ?> ]]> &example.outputs; array(2) { [0]=> int(1) [1]=> string(1) "a" } } ]]> Usar la interfaz mysqli_result ofrece el beneficio adicional de la navegación flexible del conjunto de resultados en el lado del cliente. Conjunto de resultados almacenado en buffer para la lectura flexible connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") || !$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a'), (2, 'b'), (3, 'c')")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } if (!($resultado = $sentencia->get_result())) { echo "Falló la obtención del conjunto de resultados: (" . $sentencia->errno . ") " . $sentencia->error; } for ($num_fila = ($resultado->num_rows - 1); $num_fila >= 0; $num_fila--) { $resultado->data_seek($num_fila); var_dump($resultado->fetch_assoc()); } $resultado->close(); ?> ]]> &example.outputs; int(3) ["etiqueta"]=> string(1) "c" } array(2) { ["id"]=> int(2) ["etiqueta"]=> string(1) "b" } array(2) { ["id"]=> int(1) ["etiqueta"]=> string(1) "a" } ]]> Escape de valores e inyección SQL Las variables vinculadas son enviadas desde la consulta al servidor por separado, por lo que no se puede interferir. El servidor usa estos valores directamente en el momento de la ejecución, después de haber analizado la plantilla de la sentencia. Los parámetros vinculados no necesitan ser escapados porque nunca son sustituidos directamente dentro del string de consulta. Se puede proporcionar una sugerencia al servidor para el tipo de variable vinculada, para crear una conversión apropiada. Véase la función mysqli_stmt_bind_param para más información. A veces, tal separación es considerada como la única característica de seguridad para evitar inyecciones SQL, pero se puede alcanzar el mismo grado de seguridad con sentencias no preparadas, si todos los valores están formateados correctamente. Debería observarse que el formateo correcto no es lo mismo que usar escapes, y que involucra más lógica que simplemente usar escapes. Por lo tanto, las sentencias preparadas son sencillamente una estrategia más conveniente y menos propensa a errores para este elemetno de seguridad de bases de datos. Emulación de sentencias preparadas en el lado del cliente La API no inclye la emulación para sentencias preparadas en el lado del cliente. Comparación entre sentencias preparadas y no preparadas La tabla de abajo compara las sentencias preparadas y no preparadas del lado del servidor. Comparación entre sentencias preparadas y no preparadas Sentencia preparada Sentencia no preparada Viajes de ida y vuelta desde el cliente al servidor, SELECT, ejecución única 2 1 Cadenas de sentencias tranferidas desde el cliente al servidor 1 1 Viajes de ida y vuelta desde el cliente al servidor, SELECT, ejecución repetida (n) 1 + n n Cadenas de sentencias tranferidas desde el cliente al servidor 1 plantilla, n veces parametro vinculado, si existe n veces junto con el parámetro, si existe API de vinculación de parámetros de entrada Sí, escape de entradas automático No, escape de entradas manual API de vinculación de variables de salida No Soporte para el uso de la API mysqli_result Sí, use mysqli_stmt_get_result Conjuntos de resultados almacenados en buffer Sí, use mysqli_stmt_get_result o la vinculación con mysqli_stmt_store_result Sí, lo predeterminado de mysqli_query Conjuntos de resultados no almacenados en buffer Sí, use la API de vinculación de salida Sí, use mysqli_real_query con mysqli_use_result "Sabor" de la transferencia de datos del protocolo Cliente Servidor de MySQL Protocolo binario Protocolo de texto Tipos de datos SQL de los valores del conjunto de resultados Preservados al obtenerlos Convertidos a cadena o preservados al obetenerlos Soporta todas las sentencia SQL Versiones reciente de MySQL soportan muchas pero no todas
Véase también mysqli::__construct mysqli::query mysqli::prepare mysqli_stmt::prepare mysqli_stmt::execute mysqli_stmt::bind_param mysqli_stmt::bind_result
Procedimientos almacenados Las bases de datos de MySQL soportan procedimientos almacenados. Un procedimiento almacenado es una subrutina almacenada en el catálogo de la base de datos. Las aplicaciones pueden llamar y ejecutar el procedimiento almacenado. La sentencia de SQL CALL se usa para ejecutar un procedimiento almacenado. Parámetros Los procedimientos almacenados pueden tener parámetros IN, INOUT y OUT, dependiendo de la versión de MySQL. La interfaz mysqli no tiene una noción especial de los diferentes tipos de parámetros. Parámetro IN Los parámetros de entrada son proporcionados con la sentencia CALL. Asegúrese de que los valores están escapados correctamente. Llamar a un procedimiento almacenado connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") || !$mysqli->query("CREATE PROCEDURE p(IN id_val INT) BEGIN INSERT INTO test(id) VALUES(id_val); END;")) { echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->query("CALL p(1)")) { echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($resultado = $mysqli->query("SELECT id FROM test"))) { echo "Falló SELECT: (" . $mysqli->errno . ") " . $mysqli->error; } var_dump($resultado->fetch_assoc()); ?> ]]> &example.outputs; string(1) "1" } ]]> Parámetro INOUT/OUT A los valores de los parámetros INOUT/OUT se acceden usando variables de sesión. Usar variables de sesión connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") || !$mysqli->query('CREATE PROCEDURE p(OUT msg VARCHAR(50)) BEGIN SELECT "¡Hola!" INTO msg; END;')) { echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->query("SET @msg = ''") || !$mysqli->query("CALL p(@msg)")) { echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($resultado = $mysqli->query("SELECT @msg as _p_out"))) { echo "Falló la obtención: (" . $mysqli->errno . ") " . $mysqli->error; } $fila = $resultado->fetch_assoc(); echo $fila['_p_out']; ?> ]]> &example.outputs; Los desarrolladores de aplicaciones y framework pueden proporcionar una API más conveniente usando una mezcla de variables de sesiones e inspección del catálogo de la base de datos. Sin embargo, observe el posible impacto de rendimiento de una solución personalizada basada en la inspección del catálogo. Manejar conjuntos de resultados Los procedimientos almacenados pueden devolver conjuntos de resultados. Los conjuntos de resultados devueltos desde un procedimiento almacenado no se pueden obtener correctamente usando mysqli_query. La función mysqli_query combina la ejecución de la sentencia y la obtención del primer conjunto de resultados dentro de un conjunto de resultados almacenado en buffer, si existe. Sin embargo, existen unos conjuntos de resultados del procedimiento almacenado ocultos para el usuario que hacen que mysqli_query falle al devolver los conjuntos de resultados esperados por el usuario. Los conjuntos de resultados devueltos desde un procedimiento almacenado son obtenidos usando mysqli_real_query o mysqli_multi_query. Ambas funciones permiten la obtención de cualquier número de conjuntos de resultados devueltos por una sentencia, como CALL. El fallo de la obtención de todos los conjuntos de resultados devueltos por un procedimiento almacenado causa un error. Obtener los resultados de procedimientos almacenados connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") || !$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;')) { echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->multi_query("CALL p()")) { echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error; } do { if ($resultado = $mysqli->store_result()) { printf("---\n"); var_dump($resultado->fetch_all()); $resultado->free(); } else { if ($mysqli->errno) { echo "Store failed: (" . $mysqli->errno . ") " . $mysqli->error; } } } while ($mysqli->more_results() && $mysqli->next_result()); ?> ]]> &example.outputs; array(1) { [0]=> string(1) "1" } [1]=> array(1) { [0]=> string(1) "2" } [2]=> array(1) { [0]=> string(1) "3" } } --- array(3) { [0]=> array(1) { [0]=> string(1) "2" } [1]=> array(1) { [0]=> string(1) "3" } [2]=> array(1) { [0]=> string(1) "4" } } ]]> Uso de sentencias preparadas No es necesario un trato especial al usar la interfaz de sentencias preparadas para obtener los resultados del mismo procedimiento almacenado de arriba. Las interfaces de sentencias preparadas y no preparadas son similares. Obserque que no todas las versioines del servidor de MYSQL pueden soportar la preparación de la sentencia SQL CALL. Procedimientos Almacenados y Sentencias Preparadas connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)") || !$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") || !$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;')) { echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error; } if (!($sentencia = $mysqli->prepare("CALL p()"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } do { if ($resultado = $sentencia->get_result()) { printf("---\n"); var_dump(mysqli_fetch_all($resultado)); mysqli_free_result($resultado); } else { if ($sentencia->errno) { echo "Store failed: (" . $sentencia->errno . ") " . $sentencia->error; } } } while ($sentencia->more_results() && $sentencia->next_result()); ?> ]]> Por supuesto, tamibién está soportado el uso de la API de vinculación para la obtención. Procedimientos Almacenados y Sentencias Preparadas usando la API de vinculación prepare("CALL p()"))) { echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error; } if (!$sentencia->execute()) { echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error; } do { $id_out = NULL; if (!$sentencia->bind_result($id_out)) { echo "Falló la vinculiación: (" . $sentencia->errno . ") " . $sentencia->error; } while ($sentencia->fetch()) { echo "id = $id_out\n"; } } while ($sentencia->more_results() && $sentencia->next_result()); ?> ]]> Véase también mysqli::query mysqli::multi_query mysqli_result::next-result mysqli_result::more-results
Sentencias Múltiples MySQL permite opcionalmente tener múltiples sentencias en una cadena de sentencias. El envío de múltiples sentencias de una sola vez reduce los viajes de ida y vuelta desde el cliente al servidor, pero requiere un manejo especial. Las sentencias múltiples o multiconsultas deben ser ejecutadas con mysqli_multi_query. Las sentencias individuales de la cadena de sentencias están serparadas por un punto y coma. Entonces, todos los conjuntos de resultados devueltos por las sentencias ejecutadas deben ser obtenidos. El servidor MySQL permite tener sentencias que devuelven conjuntos de resultados y sentencias que no devuelve conjuntos de resultados en una sentencia múltiple. Sentencias múltiples connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) { echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error; } $sql = "SELECT COUNT(*) AS _num FROM test; "; $sql.= "INSERT INTO test(id) VALUES (1); "; $sql.= "SELECT COUNT(*) AS _num FROM test; "; if (!$mysqli->multi_query($sql)) { echo "Falló la multiconsulta: (" . $mysqli->errno . ") " . $mysqli->error; } do { if ($resultado = $mysqli->store_result()) { var_dump($resultado->fetch_all(MYSQLI_ASSOC)); $resultado->free(); } } while ($mysqli->more_results() && $mysqli->next_result()); ?> ]]> &example.outputs; array(1) { ["_num"]=> string(1) "0" } } array(1) { [0]=> array(1) { ["_num"]=> string(1) "1" } } ]]> Consideraciones de seguridad Las funciones de la API mysqli_query y mysqli_real_query no establecen una bandera de conexión necesaria para activar las multiconsultas en el servidor. Se usa una llamada extra a la API para las sentencias múltiples para reducir la verosimilitud de los ataques de inyecciones SQL accidentales. Un atacante puede intentar añadir sentencias como ; DROP DATABASE mysql o ; SELECT SLEEP(999). Si el atacante tiene éxito al añadir SQL a la cadena de sentencias pero no se usa mysqli_multi_query, el servidor no ejecutará la segunda sentencia SQL inyectada y maliciosa. Inyección SQL query("SELECT 1; DROP TABLE mysql.user"); if (!$resultado) { echo "Error al ejecutar la consulta: (" . $mysqli->errno . ") " . $mysqli->error; } ?> ]]> &example.outputs; Sentencias preparadas El uso de sentencias múltiples con sentencias preparadas no está soportado. See also mysqli::query mysqli::multi_query mysqli_result::next-result mysqli_result::more-results
Soporte de la API para transacciones El servidor MySQL soporta transacciones dependiendo de del motor de almacenamiento usado. Desde MySQL 5.5, el motor de almacenamiento predeterminado es InnoDB. InnoDB tiene soporte completo para transacciones ACID. Las transacciones se pueden controlar usando SQL o llamadas a la API. Se recomienda usar llamadas a la API para habilitar y deshabilitar el modo de autoconsignación (auto commit) y para consignar y reiniciar transacciones. Establecer el modo de autoconsignación con SQL y a través de la API connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } /* Recomendado: usar la API para cotrolar las configuraciones transaccionales */ $mysqli->autocommit(false); /* No serán monitorizadas y reconocidas por la aplicación y el complemento de balance de carga */ if (!$mysqli->query('SET AUTOCOMMIT = 0')) { echo "Falló la consulta: (" . $mysqli->errno . ") " . $mysqli->error; } ?> ]]> Los paquetes de características opcionales, como los comlementos de replicación y el de balance de carga, pueden fácilmente monitorizar llamadas a la API. El complemento de replicación transacciones conscientes del balance de carga, si las transacciones están controladas con llamadas a la API. Las transacciones conscientes del balance de carga no están disponibles si las sentencias SQL se usan para establecer el modo de autoconsignación, consignación y reinicio de una transacción. Consignación y reinicio autocommit(false); $mysqli->query("INSERT INTO test(id) VALUES (1)"); $mysqli->rollback(); $mysqli->query("INSERT INTO test(id) VALUES (2)"); $mysqli->commit(); ?> ]]> Observe que el servidor MySQL no puede reiniciar todas las sentencias. Algunoas sentencias causan una consignación implícia. Véase también mysqli::autocommit mysqli_result::commit mysqli_result::rollback
Metadatos Un conjunto de resultados de MySQL contiene metadatos. Los metadatos describen las columnas encontradas en el conjunto de resultados. Todos los metadatos enviados por MySQL son accesibles a través de la interfaz de mysqli. La extensión realiza cambios insignificantes o no realiza ninguno a la información que recibe. La diferencias entre versiones del servidor MySQL no están alineadas. A los metadatos se puede acceder a través de la interfaz mysqli_result. Acceder a los metadatos de un conjunto de resultados connect_errno) { echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error; } $resultado = $mysqli->query("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL"); var_dump($resultado->fetch_fields()); ?> ]]> &example.outputs; object(stdClass)#3 (13) { ["name"]=> string(4) "_one" ["orgname"]=> string(0) "" ["table"]=> string(0) "" ["orgtable"]=> string(0) "" ["def"]=> string(0) "" ["db"]=> string(0) "" ["catalog"]=> string(3) "def" ["max_length"]=> int(1) ["length"]=> int(1) ["charsetnr"]=> int(63) ["flags"]=> int(32897) ["type"]=> int(8) ["decimals"]=> int(0) } [1]=> object(stdClass)#4 (13) { ["name"]=> string(4) "_two" ["orgname"]=> string(0) "" ["table"]=> string(0) "" ["orgtable"]=> string(0) "" ["def"]=> string(0) "" ["db"]=> string(0) "" ["catalog"]=> string(3) "def" ["max_length"]=> int(5) ["length"]=> int(5) ["charsetnr"]=> int(8) ["flags"]=> int(1) ["type"]=> int(253) ["decimals"]=> int(31) } } ]]> Sentencias preparadas A los metadatos de un conjunto de resultados creado usando sentencias preparadas se accede de la misma manera. Un gestor de mysqli_result apropiado es devuelto por mysqli_stmt_result_metadata. Metadatos de sentencias preparadas prepare("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL"); $sentencia->execute(); $resultado = $sentencia->result_metadata(); var_dump($resultado->fetch_fields()); ?> ]]> Véase también mysqli::query mysqli_result::fetch_fields