tiene - php 7 mysql
¿Por qué no debería usar las funciones mysql_*en PHP? (14)
Facilidad de uso
Las razones analíticas y sintéticas ya fueron mencionadas. Para los recién llegados, hay un incentivo más importante para dejar de usar las funciones mysql_ con fecha.
Las API de bases de datos contemporáneas son más fáciles de usar.
Es sobre todo los parámetros enlazados que pueden simplificar el código. Y con share la transición a la PDO no es demasiado ardua.
Volver a escribir una base de código más grande a la vez, sin embargo, lleva tiempo. Razón de ser de esta alternativa intermedia:
Funciones pdo_ * equivalentes en lugar de mysql_ *
Usando <pdo_mysql.php> puede cambiar las antiguas funciones mysql_ con un mínimo esfuerzo . Añade pdo_
función pdo_
que reemplazan a sus contrapartes mysql_
.
Simplemente
include_once(
<pdo_mysql.php>);
en cada script de invocación que tiene que interactuar con la base de datos.Elimine el prefijo de la función
todas partes y reemplácelo conmysql_
pdo_
.-
mysql_
connect()
convierte enpdo_
connect()
-
mysql_
query()
convierte enpdo_
query()
-
mysql_
num_rows()
convierte enpdo_
num_rows()
-
mysql_
insert_id()
convierte enpdo_
insert_id()
-
mysql_
fetch_array()
convierte enpdo_
fetch_array()
-
mysql_
fetch_assoc()
convierte enpdo_
fetch_assoc()
-
mysql_
real_escape_string()
convierte enpdo_
real_escape_string()
- y así...
-
Su código funcionará de la misma manera y aún así se verá igual:
include_once("pdo_mysql.php"); pdo_connect("localhost", "usrABC", "pw1234567"); pdo_select_db("test"); $result = pdo_query("SELECT title, html FROM pages"); while ($row = pdo_fetch_assoc($result)) { print "$row[title] - $row[html]"; }
Et voilà.
Su código está utilizando DOP.
Ahora es el momento de utilizarlo .
Los parámetros de enlace pueden ser fáciles de usar
Solo necesitas una API menos manejable.
pdo_query()
agrega un soporte muy fácil para los parámetros enlazados. Convertir código antiguo es sencillo:
Mueve tus variables fuera de la cadena SQL.
-
pdo_query()
como parámetros de función delimitados por comas apdo_query()
. - Colocar signos de interrogación
?
como marcadores de posición donde estaban las variables antes. - Deshágase de
''
comillas simples que anteriormente incluían valores / variables de cadena.
La ventaja se vuelve más obvia para el código más largo.
A menudo, las variables de cadena no solo se interpolan en SQL, sino que se concatenan con llamadas de escape entre ellas.
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title=''" . pdo_real_escape_string($title) . "'' OR id=''".
pdo_real_escape_string($title) . "'' AND user <> ''" .
pdo_real_escape_string($root) . "'' ORDER BY date")
Con ?
Los marcadores de posición aplicados no tienen que preocuparse por eso:
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)
Recuerda que pdo_ * todavía permite o .
Simplemente no escape una variable y vincúlela en la misma consulta.
- La característica de marcador de posición es proporcionada por el PDO real detrás de él.
- Así también permitido
:named
listas de marcadores de posición más adelante.
Lo más importante es que puede pasar las variables $ _REQUEST [] de forma segura detrás de cualquier consulta. Cuando los campos <form>
enviados coinciden exactamente con la estructura de la base de datos, es incluso más corto:
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
Tanta simplicidad. Pero volvamos a algunos consejos de reescritura y razones técnicas sobre por qué querrá deshacerse de y escapar. mysql_
Corrige o elimina cualquier función de sanitize()
vieja escuela
Una vez que haya convertido todas llamadas mysql_
a mysql_
pdo_query
con pdo_query
enlazados, elimine todas las llamadas redundantes pdo_real_escape_string
.
En particular, debe corregir cualquier función de sanitize
o clean
o clean_data
o de datos de clean_data
como lo anuncian los tutoriales con fecha de una forma u otra:
function sanitize($str) {
return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}
El error más deslumbrante aquí es la falta de documentación. Más significativamente, el orden de filtrado fue exactamente en el orden incorrecto.
El orden correcto habría sido: desaprobar las
stripslashes
como la llamada más interna, luegotrim
, luegostrip_tags
,htmlentities
para el contexto de salida, y solo por último la_escape_string
como su aplicación debería preceder directamente a la interpersión de SQL.Pero como primer paso simplemente deshacerse de la llamada
_real_escape_string
.Es posible que deba conservar el resto de la función
sanitize()
por ahora si su base de datos y el flujo de aplicaciones esperan cadenas seguras para el contexto HTML. Agregue un comentario que aplique solo HTML que se escapa de aquí en adelante.El manejo de cadena / valor se delega a PDO y sus declaraciones parametrizadas.
Si hubo alguna mención de
stripslashes()
en su función de desinfección, puede indicar una supervisión de nivel superior.Eso solía estar ahí para deshacer el daño (doble escape) de las
magic_quotes
. Lo que, sin embargo, se fija mejor de manera central , no cadena por cadena.Utilice uno de los enfoques de inversión de usuario . Luego quite las
stripslashes()
en la función desanitize
.
Nota histórica sobre magic_quotes. Esa característica está en desuso. Sin embargo, a menudo se presenta incorrectamente como una característica de seguridad fallida. Pero los magic_quotes son tanto una característica de seguridad fallida como las pelotas de tenis que han fallado como fuente de nutrición. Eso simplemente no era su propósito.
La implementación original en PHP2 / FI lo introdujo explícitamente con solo "las comillas se escaparán automáticamente, lo que facilitará el paso de los datos del formulario directamente a las consultas msql ". En particular, fue accidentalmente seguro de usar con mSQL , ya que solo ASCII era compatible.
Luego PHP3 / Zend reintrodujo magic_quotes para MySQL y lo documentó mal. Pero originalmente solo era una característica de conveniencia , no una intención de seguridad.
Cómo difieren las declaraciones preparadas
Cuando codifica variables de cadena en las consultas SQL, no solo se vuelve más intrincado para que usted las siga. También es un esfuerzo extraño para MySQL segregar código y datos nuevamente.
Las inyecciones de SQL son simplemente cuando los datos sangran en el contexto del código . Un servidor de base de datos no puede ver más tarde dónde PHP originalmente pegaba las variables entre las cláusulas de consulta.
Con los parámetros enlazados, se separan el código SQL y los valores de contexto SQL en su código PHP. Pero no se vuelve a barajar entre bambalinas (excepto con DOP :: EMULATE_PREPARES). Su base de datos recibe los comandos SQL no variados y los valores de las variables 1: 1.
Si bien esta respuesta hace hincapié en que debería preocuparse por las ventajas de legibilidad de soltar . Ocasionalmente, también hay una ventaja de rendimiento (repetidos INSERTs con solo valores diferentes) debido a esta separación visible de datos y códigos técnicos. mysql_
Tenga en cuenta que el enlace de parámetros aún no es una solución mágica de ventanilla única contra todas las inyecciones de SQL. Maneja el uso más común para datos / valores. Pero no se pueden incluir en la lista blanca los identificadores de nombres / tablas, la ayuda con la construcción de cláusulas dinámicas o simplemente listas de valores de matriz.
Uso híbrido de PDO
Estas funciones de envoltorio pdo_*
hacen una API de pdo_*
fácil de codificar. (Es más o menos lo que MYSQLI
podría haber sido si no fuera por el cambio de firma de la función idiosincrásica). También exponen la verdadera DOP en la mayoría de los casos.
La reescritura no tiene que detenerse en el uso de los nuevos nombres de función pdo_. Podrías hacer una transición de cada uno pdo_query () a una llamada simple $ pdo-> prepare () -> execute ().
Sin embargo, es mejor comenzar con la simplificación de nuevo. Por ejemplo, la obtención de resultados comunes:
$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {
Se puede reemplazar con solo una iteración foreach:
foreach ($result as $row) {
O mejor aún, una recuperación de matriz directa y completa:
$result->fetchAll();
Obtendrá advertencias más útiles en la mayoría de los casos que PDO o mysql_ generalmente proporcionan después de consultas fallidas.
Otras opciones
Así que, con suerte, esto visualizó algunas razones prácticas y un camino valioso para soltar . mysql_
Simplemente cambiando a pdo no es suficiente. pdo_query()
también es solo una interfaz para él.
A menos que también introduzca el enlace de parámetros o pueda utilizar algo más de la API más agradable, es un interruptor sin sentido. Espero que sea retratado lo suficientemente simple como para no promover el desaliento a los recién llegados. (La educación generalmente funciona mejor que la prohibición).
Si bien califica para la categoría más simple de lo que podría funcionar, también es un código muy experimental. Acabo de escribirlo durante el fin de semana. Hay una gran cantidad de alternativas sin embargo. Solo busca la abstracción de la base de datos de PHP y navega un poco. Siempre ha habido y habrá muchas bibliotecas excelentes para tales tareas.
Si desea simplificar aún más la interacción de su base de datos, vale la pena probar Paris/Idiorm como Paris/Idiorm . Al igual que nadie más usa el DOM suave en JavaScript, no tiene que cuidar a una interfaz de base de datos sin procesar en la actualidad.
¿Cuáles son las razones técnicas por las que no se deben usar las funciones mysql_*
? (por ejemplo, mysql_query()
, mysql_connect()
o mysql_real_escape_string()
)?
¿Por qué debería usar otra cosa incluso si funcionan en mi sitio?
Si no funcionan en mi sitio, ¿por qué recibo errores como
Advertencia: mysql_connect (): no existe tal archivo o directorio
La extensión de MySQL:
- No está en desarrollo activo.
- Está oficialmente en deprecated partir de PHP 5.5 (lanzado en junio de 2013).
- Se ha removed completo a partir de PHP 7.0 (publicado en diciembre de 2015)
- Esto significa que, a partir del 31 de diciembre de 2018 , no existirá en ninguna versión compatible de PHP. Actualmente, solo recibe actualizaciones de seguridad .
- Carece de una interfaz OO
- No soporta:
- Consultas asíncronas no bloqueantes.
- Declaraciones preparadas o consultas parametrizadas.
- Procedimientos almacenados
- Declaraciones multiples
- Actas
- El "nuevo" método de autenticación de contraseña (activado de forma predeterminada en MySQL 5.6; requerido en 5.7)
- Toda la funcionalidad en MySQL 5.1.
Dado que está en desuso, su uso hace que su código sea una prueba menos futura.
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.
PHP ofrece tres API diferentes para conectarse a MySQL. Estas son las extensiones mysql
(eliminadas a partir de PHP 7), mysqli
y PDO
.
Las funciones mysql_*
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: desprecating ext / mysql ).
Y el último equipo de desarrolladores de PHP tomó la decisión de generar errores E_DEPRECATED
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 integrada en ext/mysql
.
ext/mysql
fue deprecated y se ha eliminado a partir de PHP 7 .
¿Ver la caja roja?
Cuando mysql_*
cualquier página del manual de la función mysql_*
, ves un recuadro rojo que explica que ya no se debe utilizar.
Por qué
Alejarse de ext/mysql
no solo se trata de seguridad, sino también de tener acceso a todas las características de la base de datos MySQL.
ext/mysql
fue construido para MySQL 3.23 y solo tuvo muy pocas adiciones desde entonces, aunque mantiene la compatibilidad con esta versión antigua, lo que hace que el código sea un poco más difícil de mantener. Las características que faltan que no son compatibles con ext/mysql
incluyen: ( deprecated ).
- Procedimientos almacenados (no puede manejar múltiples conjuntos de resultados)
- Declaraciones preparadas
- Cifrado (SSL)
- Compresión
- Soporte completo de Charset
Razón para no usar la función mysql_*
:
- No bajo desarrollo activo.
- Eliminado a partir de PHP 7
- Carece de una interfaz OO
- No soporta consultas asíncronas no bloqueantes.
- No soporta declaraciones preparadas o consultas parametrizadas
- No soporta procedimientos almacenados
- No soporta declaraciones múltiples
- No soporta transactions
- No soporta todas las funcionalidades 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 .
Suprimiendo las advertencias de desaprobación.
Mientras el código se convierte a MySQLi
/ PDO
, los errores E_DEPRECATED
se pueden eliminar al configurar error_reporting
en php.ini para excluir E_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: ¿Qué debería usar? Dejan Marjanovic te ayudará a elegir.
Y una mejor manera es PDO
, y ahora estoy escribiendo un sencillo tutorial de PDO
.
Un simple y breve tutorial de DOP.
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 la función mysql_*
o podemos decirlo a la antigua (desaprobado 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 necesita hacer es crear un nuevo objeto PDO
. El constructor acepta parámetros para especificar la fuente de la base de datos. El constructor de la PDO
toma principalmente 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 con DSN
; esto es nuevo en PDO
. Un DSN
es básicamente una cadena de opciones que le indica a PDO
qué controlador usar y los detalles de conexión. Para mayor referencia, verifique el DSO de MySQL .
$db = new PDO(''mysql:host=localhost;dbname=testdb;charset=utf8'', ''username'', ''password'');
Nota: también puedes usar charset=UTF-8
, pero a veces causa un error, por lo que es mejor usar utf8
.
Si hay algún error de conexión, lanzará un objeto PDOException
que se puede capturar para manejar la Exception
aún 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 controladores de PDO
no admiten declaraciones preparadas de forma nativa, PDO
realiza la emulación de la preparación. También te permite habilitar manualmente esta emulación. Para usar las declaraciones preparadas del lado del servidor nativo, debe establecerlo explícitamente como false
.
La otra es desactivar la emulación de preparación que está habilitada en el controlador MySQL
de forma predeterminada, pero la emulación de preparación debe estar desactivada para usar la PDO
forma segura.
Más adelante explicaré por qué la preparación de la emulación debería estar desactivada. Para encontrar la razón por favor revisa este post .
Solo se puede utilizar si está utilizando una versión antigua de MySQL
que no recomiendo.
A continuación se muestra un ejemplo de cómo puedes 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 la DOP?
Sí , también podemos establecer algunos atributos después de la construcción de PDO con el método setAttribute
:
$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 en PDO
que en mysql_*
.
Una práctica común cuando se utiliza mysql_*
es:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
no es una buena manera de manejar el error ya que no podemos manejar la cosa en die
. Simplemente terminará la secuencia de comandos abruptamente y luego mostrará el error en la pantalla que normalmente NO desea mostrar a sus usuarios finales, y permitirá que los piratas informáticos descubran su esquema. Alternativamente, los valores de mysql_*
funciones mysql_*
menudo se pueden usar junto con mysql_error() para manejar los errores.
PDO
ofrece una mejor solución: excepciones. Todo lo que hagamos con PDO
debe estar envuelto en un bloque try
- catch
. Podemos forzar la PDO
en uno de los tres modos de error configurando el atributo de modo de error. A continuación se muestran tres modos de manejo de errores.
-
PDO::ERRMODE_SILENT
. Simplemente está configurando códigos de error y actúa de manera muy similar amysql_*
donde debe verificar cada resultado y luego ver$db->errorInfo();
para obtener los detalles del error. -
PDO::ERRMODE_WARNING
LevanteE_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 planteado por DOP. No debe lanzar unaPDOException
desde su propio código. Consulte Excepciones para obtener más información acerca de las excepciones en PHP. Actúa mucho comoor die(mysql_error());
, cuando no está atrapado. Pero a diferenciaor die()
, laPDOException
puede capturarse y manejarse con gracia si así lo desea.
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 puedes envolverlo 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());
}
No tienes que manejar con try
- catch
ahora. Puede capturarlo en cualquier momento que sea apropiado, pero le recomiendo encarecidamente que use try
- catch
. También puede tener más sentido capturarlo fuera de la función que llama al PDO
:
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, puedes manejar by or die()
o podemos decir como mysql_*
, pero será muy variado. Puede ocultar los mensajes de error peligrosos en producción apagando display_errors off
y simplemente leyendo su registro de errores.
Ahora, después de leer todas las cosas anteriores, probablemente esté pensando: ¿qué diablos es eso cuando solo quiero comenzar a inclinar las SELECT
, INSERT
, UPDATE
o DELETE
simples? No te preocupes, aquí vamos:
Seleccionando datos
Entonces, lo que estás haciendo en 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 en 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 como el siguiente ( query()
), este método devuelve un objeto PDOStatement
. Así que si quieres obtener el resultado, úsalo como arriba.
<?php
foreach($db->query(''SELECT * FROM table'') as $row) {
echo $row[''field1''];
}
En Datos de PDO, se obtiene a través de ->fetch()
, un método de su identificador de declaración. Antes de llamar a fetch, el mejor enfoque sería decirle a PDO cómo le gustaría que se obtuvieran los datos. En la sección de abajo estoy explicando esto.
Modos de búsqueda
Tenga en cuenta el uso de PDO::FETCH_ASSOC
en el código fetch()
y fetchAll()
arriba. Esto le indica a PDO
que devuelva las filas como una matriz asociativa con los nombres de campo como claves. También hay muchos otros modos de búsqueda que explicaré uno por uno.
En primer lugar, explico cómo seleccionar el modo de captura:
$stmt->fetch(PDO::FETCH_ASSOC)
En lo anterior, he estado usando fetch()
. También puedes usar:
-
PDOStatement::fetchAll()
: devuelve una matriz que contiene todas las filas del conjunto de resultados -
PDOStatement::fetchColumn()
: devuelve una sola columna de la siguiente fila de un conjunto de resultados -
PDOStatement::fetchObject()
: busca la siguiente fila y la devuelve como un objeto. -
PDOStatement::setFetchMode()
: establece el modo de recuperación predeterminado para esta declaración
Ahora vengo a buscar el modo:
-
PDO::FETCH_ASSOC
: devuelve una matriz indexada por nombre de columna como se devuelve 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 por 0 según lo devuelto en su conjunto de resultados
¡Hay aún más opciones! Lea sobre todos ellos en la documentación de PDOStatement
Fetch. .
Obtener el recuento de filas :
En lugar de usar mysql_num_rows
para obtener el número de filas devueltas, puede obtener un PDOStatement
y hacer rowCount()
, como:
<?php
$stmt = $db->query(''SELECT * FROM table'');
$row_count = $stmt->rowCount();
echo $row_count.'' rows selected'';
Obtención de la última ID 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 la función mysql_*
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 por:
<?php
$affected_rows = $db->exec("UPDATE table SET field=''value''");
echo $affected_rows;
En la consulta anterior, PDO::exec
ejecuta una instrucción SQL y devuelve el número de filas afectadas.
Insertar y eliminar se tratarán más adelante.
El método anterior solo es útil cuando no está utilizando la variable en la consulta. Pero cuando necesite usar una variable en una consulta, nunca intente como la anterior y allí la declaración preparada o parametrizada .
Declaraciones preparadas
P. ¿Qué es una declaración preparada y por qué los necesito?
A. Una declaración preparada es una instrucció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 de Wikipedia en tres puntos 3 ):
Preparar : La plantilla de sentencias es creada por la aplicación y enviada al sistema de administración de bases de datos (DBMS). Ciertos valores se dejan sin especificar, llamados parámetros, marcadores de posición o variables de enlace (etiquetados
?
Abajo):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 suministra (o enlaza) valores para los parámetros, y el DBMS ejecuta la declaración (posiblemente devolviendo un resultado). La aplicación puede ejecutar la sentencia tantas veces como quiera con diferentes valores. En este ejemplo, podría proporcionar ''Bread'' 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 anterior a una), uno con marcadores de posición sin nombre y otro con marcadores de posición con nombre.
P. Entonces, ¿cómo se llaman marcadores de posición y cómo los uso?
A. Marcadores de posición nombrados. 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 titular del lugar:
$stmt->bindParam('':bla'', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
También puede enlazar utilizando una matriz de ejecución tambié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 característica OOP
para los amigos de OOP
es que los marcadores de posición con nombre tienen la capacidad de insertar objetos directamente en su base de datos, asumiendo que las propiedades coinciden 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 los 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, puedes verlos ?
en lugar de un nombre como en un lugar titular. 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 la 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 de la matriz que estamos pasando al PDOStatement::execute()
.
SELECT
, INSERT
, UPDATE
, DELETE
consultas preparadas
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 las declaraciones preparadas DOP suficientes para evitar la inyección de SQL? por ircmaxell . Además, estoy citando 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 /*"));
Primero, comencemos con el comentario estándar que damos a todos:
share . Ya no se mantienen deprecated . ¿Ves la caja roja ? En su lugar, aprenda sobre declaraciones preparadas y use PDO o MySQLi ; este artículo lo ayudará a decidir cuál. Si elige DOP, aquí hay un buen tutorial .
Revisemos esto, oración por oración, y expliquemos:
Ya no se mantienen, y están oficialmente en desuso.
Esto significa que la comunidad de PHP está disminuyendo gradualmente el soporte para estas funciones muy antiguas. ¡Es probable que no existan en una versión futura (reciente) de PHP! El uso continuado de estas funciones puede romper su código en un futuro (no tan) lejano.
¡NUEVO! - ¡ext / mysql ahora está deprecated
¡Más nuevo! ext / mysql ha sido eliminado en PHP 7 .
En su lugar, debe aprender de declaraciones preparadas
mysql_*
extensiónmysql_*
no admite declaraciones preparadas , lo cual es (entre otras cosas) una contramedida muy efectiva contra la inyección SQL . Se solucionó una vulnerabilidad muy grave en las aplicaciones dependientes de MySQL que permite a los atacantes obtener acceso a su script y realizar cualquier consulta posible en su base de datos.Para obtener más información, consulte ¿Cómo puedo evitar la inyección de SQL en PHP?
¿Ver la caja roja?
Cuando vaya a cualquier página de manual de la función
mysql
, verá un cuadro rojo, explicando que ya no se debe utilizar.Utilice PDO o MySQLi
Existen alternativas mejores, más sólidas y mejor construidas, PDO , que ofrece un enfoque completo de OOP para la interacción de la base de datos, y MySQLi , que es una mejora específica de MySQL.
Las funciones mysql_ * se depreciaron (a partir de php 5.5 ) dado que se desarrollaron mejores funciones y estructuras de código. El hecho de que la función se haya depreciado significa que no se harán más esfuerzos para mejorarla en términos de rendimiento y seguridad, lo que significa que es una prueba menos futura .
Si necesitas más razones:
- Las funciones mysql_ * no admiten sentencias preparadas.
- Las funciones mysql_ * no admiten el enlace de parámetros.
- Las funciones mysql_ * carecen de funcionalidad para la programación orientada a objetos.
- la lista continua ...
Considero que las respuestas anteriores son muy largas, así que para resumir:
La extensión mysqli tiene varios beneficios, las mejoras clave sobre la extensión mysql son:
- Interfaz orientada a objetos
- Soporte para declaraciones preparadas
- Soporte para declaraciones múltiples
- Soporte para transacciones
- Capacidades de depuración mejoradas
- Soporte de servidor incorporado
Fuente: MySQLi Overview
Como se explicó en las respuestas anteriores, las alternativas a mysql son mysqli y PDO (PHP Data Objects).
- La API admite sentencias preparadas del lado del servidor: compatibles con MYSQLi y PDO
- La API admite declaraciones preparadas del lado del cliente: admitidas solo por PDO
- API admite procedimientos almacenados: MySQLi y PDO
- La API es compatible con declaraciones múltiples y toda la funcionalidad de MySQL 4.1+ - Compatible con MySQLi y en su mayoría también con PDO
Tanto MySQLi como PDO se introdujeron en PHP 5.0, mientras que MySQL se introdujo antes de PHP 3.0. Un punto a tener en cuenta es que MySQL está incluido en PHP5.x aunque está obsoleto en versiones posteriores.
Esta respuesta está escrita para mostrar cuán trivial es omitir el código de validación de usuario de PHP mal escrito, cómo (y con qué) funcionan estos ataques y cómo reemplazar las antiguas funciones de MySQL con una declaración preparada de forma segura. (probablemente con muchas repeticiones) están ladrando a nuevos usuarios que hacen preguntas para mejorar su código.
En primer lugar, siéntase libre de crear esta base de datos de prueba de mysql (he llamado preparación mía):
mysql> create table users(
-> id int(2) primary key auto_increment,
-> userid tinytext,
-> pass tinytext);
Query OK, 0 rows affected (0.05 sec)
mysql> insert into users values(null, ''Fluffeh'', ''mypass'');
Query OK, 1 row affected (0.04 sec)
mysql> create user ''prepared''@''localhost'' identified by ''example'';
Query OK, 0 rows affected (0.01 sec)
mysql> grant all privileges on prep.* to ''prepared''@''localhost'' with grant option;
Query OK, 0 rows affected (0.00 sec)
Con eso hecho, podemos pasar a nuestro código PHP.
Supongamos que el siguiente script es el proceso de verificación para un administrador en un sitio web (simplificado pero funciona si lo copia y lo utiliza para realizar pruebas):
<?php
if(!empty($_POST[''user'']))
{
$user=$_POST[''user''];
}
else
{
$user=''bob'';
}
if(!empty($_POST[''pass'']))
{
$pass=$_POST[''pass''];
}
else
{
$pass=''bob'';
}
$database=''prep'';
$link=mysql_connect(''localhost'', ''prepared'', ''example'');
mysql_select_db($database) or die( "Unable to select database");
$sql="select id, userid, pass from users where userid=''$user'' and pass=''$pass''";
//echo $sql."<br><br>";
$result=mysql_query($sql);
$isAdmin=false;
while ($row = mysql_fetch_assoc($result)) {
echo "My id is ".$row[''id'']." and my username is ".$row[''userid'']." and lastly, my password is ".$row[''pass'']."<br>";
$isAdmin=true;
// We have correctly matched the Username and Password
// Lets give this person full access
}
if($isAdmin)
{
echo "The check passed. We have a verified admin!<br>";
}
else
{
echo "You could not be verified. Please try again...<br>";
}
mysql_close($link);
?>
<form name="exploited" method=''post''>
User: <input type=''text'' name=''user''><br>
Pass: <input type=''text'' name=''pass''><br>
<input type=''submit''>
</form>
Parece lo suficientemente legítimo a primera vista.
El usuario tiene que ingresar un nombre de usuario y contraseña, ¿verdad?
Brillante, no entrar en lo siguiente:
user: bob
pass: somePass
y enviarlo.
La salida es la siguiente:
You could not be verified. Please try again...
¡Súper! Trabajando como se esperaba, ahora probemos el nombre de usuario y la contraseña reales:
user: Fluffeh
pass: mypass
¡Increíble!Hi-fives all round, el código verificó correctamente a un administrador. ¡Es perfecto!
Bueno en realidad no. Digamos que el usuario es una personita inteligente. Digamos que la persona soy yo.
Ingrese en lo siguiente:
user: bob
pass: n'' or 1=1 or ''m=m
Y la salida es:
The check passed. We have a verified admin!
Enhorabuena, solo me permitiste ingresar a tu sección de administradores súper protegidos solo cuando ingresé un nombre de usuario falso y una contraseña falsa. En serio, si no me cree, cree la base de datos con el código que proporcioné y ejecute este código PHP, que a simple vista parece verificar bastante bien el nombre de usuario y la contraseña.
Así que, en respuesta, ESO ES POR QUÉ ESTÁ AÚNTEO.
Entonces, echemos un vistazo a lo que salió mal, y por qué acabo de entrar en su cueva de murciélagos solo para administradores. Tomé una conjetura y asumí que no estaba siendo cuidadoso con sus entradas y simplemente las pasó directamente a la base de datos. Construí la entrada de una manera que CAMBIARÍA la consulta que realmente estaba ejecutando. Entonces, ¿qué se suponía que era y qué terminó siendo?
select id, userid, pass from users where userid=''$user'' and pass=''$pass''
Esa es la consulta, pero cuando reemplazamos las variables con las entradas reales que usamos, obtenemos lo siguiente:
select id, userid, pass from users where userid=''bob'' and pass=''n'' or 1=1 or ''m=m''
¿Vea cómo construí mi "contraseña" de modo que primero cierre la comilla alrededor de la contraseña y luego introduzca una comparación completamente nueva? Luego, solo por seguridad, agregué otra "cadena" para que la comilla simple se cierre como se esperaba en el código que originalmente teníamos.
Sin embargo, esto no se trata de que la gente te grite ahora, se trata de mostrarte cómo hacer que tu código sea más seguro.
De acuerdo, entonces, ¿qué salió mal y cómo podemos solucionarlo?
Este es un ataque clásico de inyección SQL. Uno de los más simples para el caso. En la escala de vectores de ataque, este es un niño pequeño que ataca un tanque y gana.
Entonces, ¿cómo protegemos su sección de administrador sagrado y la hacemos agradable y segura? Lo primero que debe hacer es dejar de usar esas mysql_*
funciones realmente antiguas y obsoletas . Lo sé, seguiste un tutorial que encontraste en línea y funciona, pero es antiguo, está desactualizado y en el espacio de unos minutos, lo he superado sin siquiera romper un sudor.
Ahora, tiene las mejores opciones de usar mysqli_ o PDO . Personalmente, soy un gran fanático de PDO, por lo que usaré PDO en el resto de esta respuesta. Hay profesionales y contras, pero personalmente encuentro que los profesionales superan con creces a los contras. Es portátil a través de múltiples motores de base de datos, ya sea que esté usando MySQL u Oracle o simplemente con cualquier cosa. Simplemente cambiando la cadena de conexión, tiene todas las funciones sofisticadas que queremos usar y es agradable y limpio. Me gusta limpiar
Ahora, echemos un vistazo a ese código nuevamente, esta vez escrito con un objeto PDO:
<?php
if(!empty($_POST[''user'']))
{
$user=$_POST[''user''];
}
else
{
$user=''bob'';
}
if(!empty($_POST[''pass'']))
{
$pass=$_POST[''pass''];
}
else
{
$pass=''bob'';
}
$isAdmin=false;
$database=''prep'';
$pdo=new PDO (''mysql:host=localhost;dbname=prep'', ''prepared'', ''example'');
$sql="select id, userid, pass from users where userid=:user and pass=:password";
$myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
if($myPDO->execute(array('':user'' => $user, '':password'' => $pass)))
{
while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
{
echo "My id is ".$row[''id'']." and my username is ".$row[''userid'']." and lastly, my password is ".$row[''pass'']."<br>";
$isAdmin=true;
// We have correctly matched the Username and Password
// Lets give this person full access
}
}
if($isAdmin)
{
echo "The check passed. We have a verified admin!<br>";
}
else
{
echo "You could not be verified. Please try again...<br>";
}
?>
<form name="exploited" method=''post''>
User: <input type=''text'' name=''user''><br>
Pass: <input type=''text'' name=''pass''><br>
<input type=''submit''>
</form>
Las principales diferencias son que no hay más mysql_*
funciones. Todo se hace a través de un objeto PDO, en segundo lugar, está utilizando una declaración preparada. Ahora, ¿qué es una declaración preparada que pides? Es una forma de decirle a la base de datos antes de ejecutar una consulta, qué consulta es la que vamos a ejecutar. En este caso, le decimos a la base de datos: "Hola, voy a ejecutar una instrucción de selección que desea id, ID de usuario y pase de la tabla a los usuarios, donde el ID de usuario es una variable y el paso también es una variable".
Luego, en la instrucción de ejecución, pasamos a la base de datos una matriz con todas las variables que ahora espera.
Los resultados son fantásticos. Vamos a probar esas combinaciones de nombre de usuario y contraseña de antes otra vez:
user: bob
pass: somePass
El usuario no fue verificado. Increíble.
Qué tal si:
user: Fluffeh
pass: mypass
Oh, me emocioné un poco, funcionó: el cheque pasó. Tenemos un administrador verificado!
Ahora, probemos los datos que un ingenioso tipo ingresaría para intentar superar nuestro pequeño sistema de verificación:
user: bob
pass: n'' or 1=1 or ''m=m
Esta vez, obtenemos lo siguiente:
You could not be verified. Please try again...
Esta es la razón por la que le gritan cuando publica preguntas, es porque la gente puede ver que su código puede ser anulado sin siquiera intentarlo. Por favor, use esta pregunta y respuesta para mejorar su código, para hacerlo más seguro y para utilizar las funciones actuales.
Por último, esto no quiere decir que este es un código PERFECTO. Hay muchas más cosas que podría hacer para mejorarla; por ejemplo, use contraseñas con hash, asegúrese de que cuando almacena información sensetiva en la base de datos, no la almacene en texto sin formato, tenga múltiples niveles de verificación, pero realmente simplemente cambia su antiguo código propenso a la inyección a esto, estará muy bien en el camino para escribir un buen código, y el hecho de que haya llegado tan lejos y que todavía esté leyendo me da un sentido de esperanza de que no solo implementará este tipo del código al escribir sus sitios web y aplicaciones, pero puede salir e investigar esas otras cosas que acabo de mencionar, y más. Escriba el mejor código que pueda, no el código más básico que apenas funciona.
Es posible definir casi todas las mysql_*
funciones usando mysqli o PDO. Solo inclúyalos sobre su antigua aplicación PHP, y funcionará en PHP7. Mi solución here .
<?php
define(''MYSQL_LINK'', ''dbl'');
$GLOBALS[MYSQL_LINK] = null;
function mysql_link($link=null) {
return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}
function mysql_connect($host, $user, $pass) {
$GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
return $GLOBALS[MYSQL_LINK];
}
function mysql_pconnect($host, $user, $pass) {
return mysql_connect($host, $user, $pass);
}
function mysql_select_db($db, $link=null) {
$link = mysql_link($link);
return mysqli_select_db($link, $db);
}
function mysql_close($link=null) {
$link = mysql_link($link);
return mysqli_close($link);
}
function mysql_error($link=null) {
$link = mysql_link($link);
return mysqli_error($link);
}
function mysql_errno($link=null) {
$link = mysql_link($link);
return mysqli_errno($link);
}
function mysql_ping($link=null) {
$link = mysql_link($link);
return mysqli_ping($link);
}
function mysql_stat($link=null) {
$link = mysql_link($link);
return mysqli_stat($link);
}
function mysql_affected_rows($link=null) {
$link = mysql_link($link);
return mysqli_affected_rows($link);
}
function mysql_client_encoding($link=null) {
$link = mysql_link($link);
return mysqli_character_set_name($link);
}
function mysql_thread_id($link=null) {
$link = mysql_link($link);
return mysqli_thread_id($link);
}
function mysql_escape_string($string) {
return mysql_real_escape_string($string);
}
function mysql_real_escape_string($string, $link=null) {
$link = mysql_link($link);
return mysqli_real_escape_string($link, $string);
}
function mysql_query($sql, $link=null) {
$link = mysql_link($link);
return mysqli_query($link, $sql);
}
function mysql_unbuffered_query($sql, $link=null) {
$link = mysql_link($link);
return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}
function mysql_set_charset($charset, $link=null){
$link = mysql_link($link);
return mysqli_set_charset($link, $charset);
}
function mysql_get_host_info($link=null) {
$link = mysql_link($link);
return mysqli_get_host_info($link);
}
function mysql_get_proto_info($link=null) {
$link = mysql_link($link);
return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
$link = mysql_link($link);
return mysqli_get_server_info($link);
}
function mysql_info($link=null) {
$link = mysql_link($link);
return mysqli_info($link);
}
function mysql_get_client_info() {
$link = mysql_link();
return mysqli_get_client_info($link);
}
function mysql_create_db($db, $link=null) {
$link = mysql_link($link);
$db = str_replace(''`'', '''', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "CREATE DATABASE `$db`");
}
function mysql_drop_db($db, $link=null) {
$link = mysql_link($link);
$db = str_replace(''`'', '''', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "DROP DATABASE `$db`");
}
function mysql_list_dbs($link=null) {
$link = mysql_link($link);
return mysqli_query($link, "SHOW DATABASES");
}
function mysql_list_fields($db, $table, $link=null) {
$link = mysql_link($link);
$db = str_replace(''`'', '''', mysqli_real_escape_string($link, $db));
$table = str_replace(''`'', '''', mysqli_real_escape_string($link, $table));
return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}
function mysql_list_tables($db, $link=null) {
$link = mysql_link($link);
$db = str_replace(''`'', '''', mysqli_real_escape_string($link, $db));
return mysqli_query($link, "SHOW TABLES FROM `$db`");
}
function mysql_db_query($db, $sql, $link=null) {
$link = mysql_link($link);
mysqli_select_db($link, $db);
return mysqli_query($link, $sql);
}
function mysql_fetch_row($qlink) {
return mysqli_fetch_row($qlink);
}
function mysql_fetch_assoc($qlink) {
return mysqli_fetch_assoc($qlink);
}
function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
return mysqli_fetch_array($qlink, $result);
}
function mysql_fetch_lengths($qlink) {
return mysqli_fetch_lengths($qlink);
}
function mysql_insert_id($qlink) {
return mysqli_insert_id($qlink);
}
function mysql_num_rows($qlink) {
return mysqli_num_rows($qlink);
}
function mysql_num_fields($qlink) {
return mysqli_num_fields($qlink);
}
function mysql_data_seek($qlink, $row) {
return mysqli_data_seek($qlink, $row);
}
function mysql_field_seek($qlink, $offset) {
return mysqli_field_seek($qlink, $offset);
}
function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
return ($params === null)
? mysqli_fetch_object($qlink, $class)
: mysqli_fetch_object($qlink, $class, $params);
}
function mysql_db_name($qlink, $row, $field=''Database'') {
mysqli_data_seek($qlink, $row);
$db = mysqli_fetch_assoc($qlink);
return $db[$field];
}
function mysql_fetch_field($qlink, $offset=null) {
if ($offset !== null)
mysqli_field_seek($qlink, $offset);
return mysqli_fetch_field($qlink);
}
function mysql_result($qlink, $offset, $field=0) {
if ($offset !== null)
mysqli_field_seek($qlink, $offset);
$row = mysqli_fetch_array($qlink);
return (!is_array($row) || !isset($row[$field]))
? false
: $row[$field];
}
function mysql_field_len($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
return is_object($field) ? $field->length : false;
}
function mysql_field_name($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
if (!is_object($field))
return false;
return empty($field->orgname) ? $field->name : $field->orgname;
}
function mysql_field_table($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
if (!is_object($field))
return false;
return empty($field->orgtable) ? $field->table : $field->orgtable;
}
function mysql_field_type($qlink, $offset) {
$field = mysqli_fetch_field_direct($qlink, $offset);
return is_object($field) ? $field->type : false;
}
function mysql_free_result($qlink) {
try {
mysqli_free_result($qlink);
} catch (Exception $e) {
return false;
}
return true;
}
Hablando de razones técnicas , hay solo unas pocas, extremadamente específicas y rara vez utilizadas. Lo más probable es que nunca los uses en tu vida.
Tal vez soy demasiado ignorante, pero nunca tuve la oportunidad de usar cosas como
- Sin bloqueo, consultas asíncronas.
- Procedimientos almacenados que devuelven múltiples conjuntos de resultados.
- Cifrado (SSL)
- Compresión
Si los necesita, estos son, sin duda, razones técnicas para alejarse de la extensión mysql hacia algo más elegante y moderno.
Sin embargo, también hay algunos problemas no técnicos que pueden dificultar un poco su experiencia.
- El uso adicional de estas funciones con las versiones modernas de PHP generará avisos de desaprobación. Simplemente se pueden apagar.
- en un futuro lejano, posiblemente se pueden eliminar de la compilación predeterminada de PHP. Tampoco es un gran problema, ya que mydsql ext se trasladará a PECL y todos los usuarios se complacerán en compilar PHP con él, ya que no quieren perder clientes cuyos sitios trabajaron durante décadas.
- fuerte resistencia de la comunidad . Cuando mencionas estas funciones honestas, te dicen que están bajo un estricto tabú.
- Al ser un usuario medio de PHP, lo más probable es que su idea de usar estas funciones sea errónea y propensa a errores. Solo por todos estos numerosos tutoriales y manuales que te enseñan de manera incorrecta. No las funciones en sí, tengo que enfatizarlas, sino la forma en que se usan.
Este último problema es un problema.
Pero, en mi opinión, la solución propuesta tampoco es mejor.
Me parece demasiado idealista el sueño de que todos los usuarios de PHP aprendan a manejar las consultas de SQL de manera adecuada a la vez. Lo más probable es que simplemente cambien mysql_ * a mysqli_ * mecánicamente, dejando el enfoque igual . Especialmente porque mysqli hace que las declaraciones preparadas sean increíblemente dolorosas y molestas.
Sin mencionar que las declaraciones preparadas de forma nativa no son suficientes para proteger de las inyecciones de SQL, y ni mysqli ni PDO ofrecen una solución.
Entonces, en lugar de luchar contra esta extensión honesta, prefiero luchar contra las prácticas incorrectas y educar a las personas de la manera correcta.
Además, hay algunas razones falsas o no significativas, como
- No es compatible con los procedimientos almacenados (los usamos
mysql_query("CALL my_proc");
durante años) - No admite transacciones (igual que arriba)
- No admite declaraciones múltiples (¿quién las necesita?)
- No bajo desarrollo activo (¿entonces qué? ¿ Te afecta de alguna manera práctica?)
- Carece de una interfaz OO (para crear una es una cuestión de varias horas)
- No admite declaraciones preparadas o consultas parametrizadas
El último es un punto interesante. Aunque mysql ext no admite declaraciones preparadas de forma nativa , no son necesarias para la seguridad. Podemos falsificar fácilmente declaraciones preparadas utilizando marcadores de posición manejados manualmente (al igual que lo hace DOP):
function paraQuery()
{
$args = func_get_args();
$query = array_shift($args);
$query = str_replace("%s","''%s''",$query);
foreach ($args as $key => $val)
{
$args[$key] = mysql_real_escape_string($val);
}
$query = vsprintf($query, $args);
$result = mysql_query($query);
if (!$result)
{
throw new Exception(mysql_error()." [$query]");
}
return $result;
}
$query = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);
voila , todo esta parametrizado y seguro.
Pero bueno, si no te gusta el cuadro rojo en el manual, surge un problema de elección: ¿mysqli o DOP?
Bueno, la respuesta sería la siguiente:
- Si comprende la necesidad de usar una capa de abstracción de base de datos y buscar una API para crear una, mysqli es una muy buena opción, ya que es compatible con muchas características específicas de mysql.
Si, como la gran mayoría de los usuarios de PHP, está usando llamadas de API sin procesar directamente en el código de la aplicación (lo que es esencialmente una práctica incorrecta) - PDO es la única opción , ya que esta extensión pretende no ser solo API sino más bien un semi-DAL. aún incompleto, pero ofrece muchas características importantes, con dos de ellas hace que la DOP se distinga críticamente de mysqli:
- a diferencia de mysqli, PDO puede unir marcadores de posición por valor , lo que hace que las consultas creadas de forma dinámica sean factibles sin varias pantallas de código bastante desordenado.
- a diferencia de mysqli, PDO siempre puede devolver el resultado de la consulta en un simple arreglo habitual, mientras que mysqli puede hacerlo solo en las instalaciones de mysqlnd.
Por lo tanto, si usted es un usuario promedio de PHP y desea ahorrarse muchos dolores de cabeza cuando usa declaraciones preparadas de forma nativa, de nuevo la DOP es la única opción.
Sin embargo, la DOP no es una bala de plata también y tiene sus dificultades.
Por lo tanto, escribí soluciones para todos los escollos y casos complejos en el wiki de etiquetas de DOP.
Sin embargo, todos los que hablan de extensiones siempre se pierden los 2 datos importantes sobre Mysqli y la DOP:
Declaración preparada no es una bala de plata . Hay identificadores dinámicos que no pueden ser enlazados usando declaraciones preparadas. Hay consultas dinámicas con un número desconocido de parámetros que hacen que la búsqueda sea una tarea difícil.
Ni las funciones mysqli_ * ni PDO deberían haber aparecido en el código de la aplicación.
Debe haber una capa de abstracción entre ellos y el código de la aplicación, que hará todo el trabajo sucio de enlace, bucle, manejo de errores, etc., haciendo que el código de la aplicación sea SECO y limpio. Especialmente para los casos complejos como la construcción de consultas dinámicas.
Por lo tanto, simplemente cambiar a PDO o mysqli no es suficiente. Uno tiene que usar un ORM, o un generador de consultas, o cualquier clase de abstracción de base de datos en lugar de llamar funciones de API sin procesar en su código.
Y, por el contrario, si tiene una capa de abstracción entre el código de su aplicación y la API de mysql, en realidad no importa qué motor se use. Puede usar mysql ext hasta que quede en desuso y luego reescribir fácilmente su clase de abstracción a otro motor, teniendo todo el código de la aplicación intacto.
Aquí hay algunos ejemplos basados en mi clase de safemysql para mostrar cómo debería ser tal clase de abstracción:
$city_ids = array(1,2,3);
$cities = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);
Compare esta línea única con la cantidad de código que necesitará con DOP .
Luego compare con la cantidad de código que necesitará con las declaraciones preparadas de Mysqli sin procesar. Tenga en cuenta que el manejo de errores, la creación de perfiles, el registro de consultas ya está integrado y en ejecución.
$insert = array(''name'' => ''John'', ''surname'' => "O''Hara");
$db->query("INSERT INTO users SET ?u", $insert);
Compárelo con las inserciones de PDO habituales, cuando cada nombre de campo se repita de seis a diez veces, en todos estos numerosos marcadores de posición, enlaces y definiciones de consulta.
Otro ejemplo:
$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET[''order'']);
Difícilmente puede encontrar un ejemplo de PDO para manejar este caso práctico.
Y será demasiado prolijo y probablemente inseguro.
Por lo tanto, una vez más, no es solo un controlador en bruto lo que debe preocupar, sino la clase de abstracción, útil no solo para los ejemplos estúpidos del manual para principiantes sino también para resolver cualquier problema de la vida real.
Hay muchas razones, pero quizás la más importante es que esas funciones fomentan prácticas de programación inseguras porque no admiten declaraciones preparadas. Las declaraciones preparadas ayudan a prevenir los ataques de inyección SQL.
Cuando use mysql_*
funciones, debe recordar ejecutar los parámetros proporcionados por el usuario a través de mysql_real_escape_string()
. Si olvida en un solo lugar o si escapa solo parte de la entrada, su base de datos puede estar sujeta a ataques.
Usar declaraciones preparadas en PDO
o mysqli
lo hará para que este tipo de errores de programación sean más difíciles de cometer.
La extensión de MySQL es la más antigua de las tres y fue la forma original que los desarrolladores usaban para comunicarse con MySQL. Esta extensión ahora está en deprecated en favor de las otras mysqli_ PDO debido a las mejoras realizadas en las versiones más recientes de PHP y MySQL.
mysqli_ es la extensión ''mejorada'' para trabajar con bases de datos MySQL. Aprovecha las características que están disponibles en las versiones más nuevas del servidor MySQL, expone una interfaz orientada a funciones y otra orientada a objetos al desarrollador y hace algunas otras cosas ingeniosas.
PDO ofrece una API que consolida la mayor parte de la funcionalidad que se extendió anteriormente en las principales extensiones de acceso a bases de datos, es decir, MySQL, PostgreSQL, SQLite, MSSQL, etc. La interfaz expone objetos de alto nivel para que el programador trabaje con conexiones de base de datos, consultas y los conjuntos de resultados y los controladores de bajo nivel realizan la comunicación y el manejo de recursos con el servidor de base de datos. Se está llevando a cabo una gran cantidad de discusión y trabajo en PDO y se considera el método apropiado para trabajar con bases de datos en un código moderno y profesional.
Las mysql_
funciones:
- están desactualizados - ya no se mantienen
- No te permita moverte fácilmente a otra base de datos
- no soporta declaraciones preparadas, por lo tanto
- alentar a los programadores a utilizar la concatenación para crear consultas, lo que lleva a vulnerabilidades de inyección de SQL
Las funciones que son similares a esta mysql_connect()
, mysql_query()
tipo son las versiones de la versión anterior PHP ie (PHP 4) y ahora no están en uso.
Estos son reemplazados por mysqli_connect()
, de mysqli_query()
manera similar en el último PHP5.
Esta es la razón detrás del error.
Porque (entre otras razones) es mucho más difícil garantizar que los datos de entrada se desinfecten. Si utiliza consultas parametrizadas, como una con PDO o mysqli, puede evitar el riesgo por completo.
Como ejemplo, alguien podría usarlo "enhzflep); drop table users"
como nombre de usuario. Las funciones anteriores permitirán ejecutar varias instrucciones por consulta, por lo que algo así como el desagradable bugger puede eliminar una tabla completa.
Si uno fuera a usar la DOP de mysqli, el nombre de usuario terminaría siendo "enhzflep); drop table users"
.
Ver bobby-tables.com .