PHP ofrece tres API diferentes para conectarse a MySQL. Estas son mysql
(eliminadas a partir de PHP 7) mysqli
, y PDO
extensiones.
Las mysql_*
funciones solían ser muy populares, pero ya no se recomienda su uso. El equipo de documentación está discutiendo la situación de seguridad de la base de datos, y educar a los usuarios para que se alejen de la extensión ext / mysql comúnmente utilizada es parte de esto (ver php.internals: deprecando ext / mysql ).
Y el equipo de desarrolladores de PHP posterior tomó la decisión de generar E_DEPRECATED
errores cuando los usuarios se conectan a MySQL, ya sea a través de mysql_connect()
, mysql_pconnect()
o la funcionalidad de conexión implícita incorporada ext/mysql
.
ext/mysql
fue oficialmente obsoleto a partir de PHP 5.5 y se ha eliminado a partir de PHP 7 .
¿Ves la caja roja?
Cuando va a cualquier mysql_*
página del manual de funciones, ve un cuadro rojo que explica que ya no debe usarse.
Por qué
Alejarse ext/mysql
no solo se trata de seguridad, sino también de tener acceso a todas las funciones de la base de datos MySQL.
ext/mysql
fue construido para MySQL 3.23 y solo obtuvo muy pocas adiciones desde entonces, manteniendo la compatibilidad con esta versión anterior, lo que hace que el código sea un poco más difícil de mantener. Las características faltantes que no son compatibles con ext/mysql
incluyen: ( del manual de PHP ).
Motivo para no usar la mysql_*
función :
- No bajo desarrollo activo
- Eliminado a partir de PHP 7
- Carece de una interfaz OO
- No admite consultas asincrónicas sin bloqueo
- No admite declaraciones preparadas o consultas parametrizadas
- No admite procedimientos almacenados
- No soporta múltiples declaraciones
- No admite transacciones
- No es compatible con toda la funcionalidad en MySQL 5.1
Punto anterior citado de la respuesta de Quentin
La falta de soporte para las declaraciones preparadas es particularmente importante ya que proporcionan un método más claro y menos propenso a errores para escapar y citar datos externos que escapar manualmente con una llamada de función separada.
Ver la comparación de extensiones SQL .
Suprimir advertencias de desaprobación
Mientras el código se convierte a MySQLi
/ PDO
, los E_DEPRECATED
errores se pueden suprimir configurando error_reporting
en php.ini para excluirE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Tenga en cuenta que esto también ocultará otras advertencias de desaprobación , que, sin embargo, pueden ser para otras cosas que no sean MySQL. ( del manual de PHP )
El artículo PDO vs. MySQLi: ¿Cuál debería usar? por Dejan Marjanovic te ayudará a elegir.
Y una mejor manera es PDO
, y ahora estoy escribiendo un PDO
tutorial simple .
Un tutorial PDO simple y breve
P. La primera pregunta en mi mente fue: ¿qué es 'PDO'?
A. " PDO - PHP Data Objects - es una capa de acceso a la base de datos que proporciona un método uniforme de acceso a múltiples bases de datos".
Conectando a MySQL
Con mysql_*
función o podemos decirlo a la antigua usanza (en desuso en PHP 5.5 y superior)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Con PDO
: Todo lo que necesitas hacer es crear un nuevo PDO
objeto. El constructor acepta parámetros para especificar la fuente de base de datos PDO
'constructor s mayormente toma cuatro parámetros que son DSN
(nombre de la fuente de datos) y, opcionalmente username
, password
.
Aquí creo que estás familiarizado con todos excepto DSN
; esto es nuevo en PDO
. B DSN
es básicamente una serie de opciones que indican PDO
qué controlador usar y los detalles de conexión. Para mayor referencia, consulte PDO MySQL DSN .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Nota: también puede usar charset=UTF-8
, pero a veces causa un error, por lo que es mejor usarlo utf8
.
Si hay algún error de conexión, arrojará un PDOException
objeto que puede ser atrapado para manejarlo Exception
más.
Buena lectura : Conexiones y gestión de conexiones ¶
También puede pasar varias opciones de controlador como una matriz al cuarto parámetro. Recomiendo pasar el parámetro que pone PDO
en modo de excepción. Debido a que algunos PDO
controladores no admiten declaraciones preparadas nativas, por lo tanto, PDO
realiza la emulación de la preparación. También le permite habilitar manualmente esta emulación. Para usar las declaraciones preparadas nativas del lado del servidor, debe configurarlas explícitamente false
.
El otro es desactivar la emulación de preparación que está habilitada en el MySQL
controlador de forma predeterminada, pero la emulación de preparación debe estar desactivada para usar de PDO
manera segura.
Más adelante explicaré por qué se debe desactivar la emulación de preparación. Para encontrar la razón, consulte esta publicación .
Solo se puede usar si está utilizando una versión anterior MySQL
que no recomiendo.
A continuación se muestra un ejemplo de cómo puede hacerlo:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
¿Podemos establecer atributos después de la construcción de PDO?
Sí , también podemos establecer algunos atributos después de la construcción PDO con el setAttribute
método:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Manejo de errores
El manejo de errores es mucho más fácil PDO
que mysql_*
.
Una práctica común cuando se usa mysql_*
es:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
no es una buena forma de manejar el error, ya que no podemos manejarlo die
. Simplemente terminará el script abruptamente y luego hará eco del error en la pantalla que generalmente NO desea mostrar a sus usuarios finales, y permitirá que los hackers sangrientos descubran su esquema. Alternativamente, los valores de retorno de las mysql_*
funciones a menudo se pueden usar junto con mysql_error () para manejar errores.
PDO
ofrece una mejor solución: excepciones. Cualquier cosa que hacemos con PDO
debe ser envuelto en una try
- catch
bloque. Podemos forzar PDO
uno de los tres modos de error configurando el atributo del modo de error. Hay tres modos de manejo de errores a continuación.
PDO::ERRMODE_SILENT
. Solo establece códigos de error y actúa casi de la misma manera mysql_*
en que debe verificar cada resultado y luego mirar $db->errorInfo();
para obtener los detalles del error.
PDO::ERRMODE_WARNING
Aumento E_WARNING
. (Advertencias en tiempo de ejecución (errores no fatales). La ejecución del script no se detiene).
PDO::ERRMODE_EXCEPTION
: Lanzar excepciones. Representa un error provocado por PDO. No debe lanzar un PDOException
desde su propio código. Consulte Excepciones para obtener más información sobre excepciones en PHP. Actúa muy parecido a or die(mysql_error());
cuando no es atrapado. Pero a diferencia de esto or die()
, PDOException
se puede atrapar y manejar con gracia si elige hacerlo.
Buena lectura :
Me gusta:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
Y se puede envolver en try
- catch
, como a continuación:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
Usted no tiene que manejar con try
- catch
en este momento. Puede atraparlo en cualquier momento apropiado, pero le recomiendo que use try
- catch
. También puede tener más sentido detectarlo fuera de la función que llama al PDO
material:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
Además, puede manejarlo or die()
o podemos decir que me gusta mysql_*
, pero será muy variado. Puede ocultar los mensajes de error peligrosos en producción girando display_errors off
y simplemente leyendo su registro de errores.
Ahora, después de leer todas las cosas de arriba, es probable que esté pensando: ¿qué diablos es que cuando sólo quiero empezar a inclinarse simples SELECT
, INSERT
, UPDATE
, o DELETE
declaraciones? No te preocupes, aquí vamos:
Seleccionar datos
Entonces, lo que estás haciendo mysql_*
es:
<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());
$num_rows = mysql_num_rows($result);
while($row = mysql_fetch_assoc($result)) {
echo $row['field1'];
}
Ahora adentro PDO
, puedes hacer esto como:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
O
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Nota : Si está utilizando el método que se muestra a continuación ( query()
), este método devuelve un PDOStatement
objeto. Entonces, si desea obtener el resultado, úselo como se indicó anteriormente.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
En PDO Data, se obtiene a través del ->fetch()
método de su manejador de estado de cuenta. Antes de llamar a fetch, el mejor enfoque sería decirle a PDO cómo desea que se recuperen los datos. En la sección a continuación estoy explicando esto.
Modos de recuperación
Tenga en cuenta el uso de PDO::FETCH_ASSOC
en el código fetch()
y fetchAll()
arriba. Esto le indica PDO
que devuelva las filas como una matriz asociativa con los nombres de campo como claves. También hay muchos otros modos de recuperación que explicaré uno por uno.
En primer lugar, explico cómo seleccionar el modo de búsqueda:
$stmt->fetch(PDO::FETCH_ASSOC)
En lo anterior, he estado usando fetch()
. También puedes usar:
Ahora vengo a buscar el modo:
PDO::FETCH_ASSOC
: devuelve una matriz indexada por nombre de columna como se devolvió en su conjunto de resultados
PDO::FETCH_BOTH
(predeterminado): devuelve una matriz indexada tanto por el nombre de la columna como por el número de la columna indexada en 0 como se devolvió en su conjunto de resultados
¡Incluso hay más opciones! Lea sobre todos ellos en la PDOStatement
documentación de Fetch. .
Obteniendo el recuento de filas :
En lugar de usar mysql_num_rows
para obtener el número de filas devueltas, puede obtener ay PDOStatement
hacer rowCount()
, como:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Obteniendo la última identificación insertada
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Insertar y actualizar o eliminar declaraciones
Lo que estamos haciendo en mysql_*
función es:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
Y en pdo, esto mismo se puede hacer:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
En la consulta anterior, PDO::exec
ejecute una instrucción SQL y devuelva el número de filas afectadas.
Insertar y eliminar se cubrirá más adelante.
El método anterior solo es útil cuando no está utilizando variable en la consulta. Pero cuando necesite usar una variable en una consulta, nunca intente como lo anterior y allí para la declaración preparada o la declaración parametrizada .
Declaraciones preparadas
P. ¿Qué es una declaración preparada y por qué los necesito?
R. Una declaración preparada es una declaración SQL precompilada que se puede ejecutar varias veces enviando solo los datos al servidor.
El flujo de trabajo típico de usar una declaración preparada es el siguiente ( citado en Wikipedia tres puntos 3 ):
Preparar : la aplicación crea la plantilla de extracto y la envía al sistema de gestión de bases de datos (DBMS). Ciertos valores se dejan sin especificar, llamados parámetros, marcadores de posición o variables de enlace (etiquetadas a ?
continuación):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
El DBMS analiza, compila y realiza la optimización de consultas en la plantilla de declaración, y almacena el resultado sin ejecutarlo.
- Ejecutar : en un momento posterior, la aplicación proporciona (o enlaza) valores para los parámetros, y el DBMS ejecuta la instrucción (posiblemente devuelve un resultado). La aplicación puede ejecutar la declaración tantas veces como quiera con diferentes valores. En este ejemplo, podría suministrar 'Pan' para el primer parámetro y
1.00
para el segundo parámetro.
Puede usar una declaración preparada incluyendo marcadores de posición en su SQL. Básicamente, hay tres sin marcadores de posición (no intente esto con la variable por encima de uno), uno con marcadores de posición sin nombre y uno con marcadores de posición con nombre.
P. Entonces, ¿qué se denominan marcadores de posición y cómo los uso?
A. Marcadores de posición con nombre. Use nombres descriptivos precedidos por dos puntos, en lugar de signos de interrogación. No nos importa la posición / orden de valor en el marcador de posición de nombre:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
También puedes enlazar usando una matriz de ejecución:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Otra buena característica para los OOP
amigos es que los marcadores de posición con nombre tienen la capacidad de insertar objetos directamente en su base de datos, suponiendo que las propiedades coincidan con los campos con nombre. Por ejemplo:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
P. Entonces, ¿qué son marcadores de posición sin nombre y cómo los uso?
A. Tengamos un ejemplo:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
y
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
En lo anterior, puede ver esos en ?
lugar de un nombre como en un marcador de posición de nombre. Ahora, en el primer ejemplo, asignamos variables a los distintos marcadores de posición ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Luego, asignamos valores a esos marcadores de posición y ejecutamos la declaración. En el segundo ejemplo, el primer elemento de matriz va al primero ?
y el segundo al segundo ?
.
NOTA : En los marcadores de posición sin nombre , debemos cuidar el orden correcto de los elementos en la matriz que estamos pasando al PDOStatement::execute()
método.
SELECT
, INSERT
, UPDATE
, DELETE
Preparado consultas
SELECT
:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
NOTA:
Sin embargo PDO
y / o MySQLi
no son completamente seguros. Verifique la respuesta ¿Son suficientes las declaraciones preparadas por PDO para evitar la inyección de SQL? por ircmaxell . Además, estoy citando alguna parte de su respuesta:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));