Code analysis
Its implementation is correct, because by passing the values of a query as an array to the function Pdostatement::execute, it will prepare the received parameters treating all as PDO::PARAM_STR
, that is, all received parameters will be treated as a string and will not risk suffering an Sql Injection, unless the values are concatenated earlier in your query.
In the example below, I use an array as a third parameter, but the reason will be explained later.
// Forma vulnerável
$nome = $_POST['nome'];
$valor = $_POST['valor'];
$query = 'INSERT INTO REGISTRY (name, value) VALUES (' . $nome . ', ' . $valor . ')';
$resultado0 = executaConsulta($conecta, $query);
// 1º Forma correta
$query = 'INSERT INTO REGISTRY (name, value) VALUES (:name, :value)';
$valores = array(
':name' => $_POST['nome']
':value' => $_POST['value']
);
$resultado1 = executaConsulta($conecta, $query, $valores);
// 2º Forma correta
$query = 'INSERT INTO REGISTRY (name, value) VALUES (?, ?)';
$valores = array(
$_POST['nome']
$_POST['value']
);
$resultado2 = executaConsulta($conecta, $query, $valores);
// 3º Forma correta
// A função filter_input é utilizada para acessar variáveis globais de forma mais segura.
// Existem outras funções deste tipo. http://php.net/manual/en/book.filter.php
$query = 'INSERT INTO REGISTRY (name, value) VALUES (:name, :value)';
$valores = array(
':name' => filter_input( INPUT_POST, 'nome', FILTER_SANITIZE_STRING );
':value' => filter_input( INPUT_POST, 'value', FILTER_SANITIZE_STRING );
);
$resultado3 = executaConsulta($conecta, $query, $valores);
About prepared query or Prepared statements
Some databases allow to use a resource of reuse of queries. It would be something like preparing a text where only a few words change, and each time you use this text, you need to inform only what the new words are.
However, not all databases support this feature (today is no longer the case with Mysql), and as the PDO aims to be a practical interface for using multiple database drivers, they had to implement a way that this feature could be used when needed. To solve this problem, the resource is simulated by the function.
In the documentation there are these examples:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// Inserindo uma linha
$name = 'one';
$value = 1;
$stmt->execute();
// Inserindo outra linha com diferentes valores
$name = 'two';
$value = 2;
$stmt->execute();
Forcing PDO to use native prepared queries
To force PDO to always use the native prepared queries feature, add right after connecting:
$conexao->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
The PDO has treatment for if an error occurs, the emulated version is used, but there are some disadvantages in using the native feature in some versions
- Do not take advantage of cache resulting in loss of performance;
- Cannot be performed by some types of queries like "SHOW TABLES";
- Do not communicate well with the size of columns in other queries that use "SHOW" returning distorted results.
- Calling several times procedurespode cause connection loss.
Some additional information on https://stackoverflow.com/questions/10113562/pdo-mysql-use-pdoattr-emulate-prepares-or-not
Binding of values and variables
In PDO there are two methods that allow you to associate parameters to your queries, they are the Pdostatement::bindParam and Pdostatement::bindValue.
The bindParam
associates the name of a variable to the query parameter, that is, if the value of a variable is changed and the method execute
is called again, the query will be executed with the new value.
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name) VALUES (:name)");
$stmt->bindParam(':name', $name);
// Inserindo uma linha
$name = 'one';
$stmt->execute();
// Inserindo outra linha com diferentes valores
$name = 'two';
$stmt->execute();
Already the bindValue
assigns a specific value to a parameter of your query, ie to change the value of the parameter you will have to call the method bindValue
again.
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name) VALUES (:name)");
// Inserindo uma linha
$stmt->bindValue(':name', 'one');
$stmt->execute();
// Inserindo outra linha com diferentes valores
$stmt->bindValue(':name', 'two');
$stmt->execute();
In both methods it is possible to specify which type should be used to treat the query parameter value. O bindParam
besides the type it is also possible to indicate other options such as the maximum value size.
Suggestions for improvements
Improvement in code style
I recommend giving a read on the attempt that the PHP community has made to standardize some things, the PSR-2 is a guide on how to better format your code. Improving this template is a good point to start observing what your code can improve and will help you find both security holes and some bugs.
Organization of parameters
When using the parameters "magic" using the function func_get_args()
you will have problems documenting, because the signature of your function does not make it explicit that it is possible to pass more arguments, this will leave other developers confused with your code.
In the presented code, there is no need for the function to receive infinite parameters, because the only thing that the function code performs is to obtain an array of everything that has been passed, removing what is already known.
To make the signature clearer, you can set a third parameter with a default value, in the case of an empty array, because the function execute
PDO expects an array.
// Versão reescrita
function executaConsulta($conexao, $sql, $valores = array()){
$preparado = $conexao->prepare($sql);
$preparado->execute($valores);
$resultado = $preparado->fetchAll();
return $resultado;
}
In the function I also changed the function Dostatement::fetch() by function Dostatement::fetchAll(), so its function will always return all the result obtained in an array. Working with array is much easier than with PDO objects.
Use of the function
When using the function, you will make it clearer to use keys instead of the wildcard character (?) as well as avoid having to pass the same value twice, if used in two places of your query. See the two possibilities of use:
// 1º forma
$sql = "SELECT * FROM se_professores WHERE id = ?";
$resultado1 = executaConsulta($conecta, $sql, array(1));
foreach ($resultado1 as $linha) {
echo $linha['nome'];
}
// 2º forma
$sql = "SELECT * FROM se_professores WHERE id = :id";
$valores = array(':id' => 1);
$resultado2 = executaConsulta($conecta, $sql, $valores);
foreach ($resultado1 as $linha) {
echo $linha['nome'];
}
Everything is up to date with your code, functions are useful for reusing code, vc n will create 5 connections in 5 files? a function solves ;)
– rray
Thanks for the answer. And no, I won’t create 5 connections in 5 files, I’m replacing mysql... So I’m putting this in the connection file that goes through all parts of the site, just mixed it up there to get better
– LocalHost
The only thing missing is to make Binding to make queries safer etc. You can refer to: https://github.com/Leonardo-Souza/MinPDO
– Leonardo
Blz. I’ll read about it
– LocalHost
I think it’s a good question of code review and can serve as a basis for a canonical response and possible target of future duplicates. What do you think of the edition I made, @rray?
– brasofilo
got better, clearer.
– LocalHost
Migrating from which lib to PDO? And which DB to use?
– Bacco
Of the mysql absoleto
– LocalHost
It would probably be better mysqli then, if what it seeks is security and efficiency (mainly because mysqli does not simulate Binding by default, as the PDO does, which is "fake" if it does not modify the default behavior). Anyway, whether with one or the other, the important thing is the correct escape from things before using in queries. Just so you understand the difference, a real Binding sends the query separate from the values, which eliminates any possibility of injection. A simulation depends on exhaust from the customer’s side. Also, only native bind is efficient for sequential operations.
– Bacco
@Bacco, could you elaborate a complete answer for me? because I’ve heard about this about the PDO, but it wasn’t entirely clear. And I’m deepening more recently. These questions that you pointed out in your comment would be of great interest I think not only for me
– LocalHost
@Localhost this already has on the site in some corner, about the differences of PDO and mysqli_ - I confess that the answers are not explaining so well this part, but it is something that already has on the site.
– Bacco
Okay, I’ll look then :)
– LocalHost
Follow the link. There is an answer that I even gave 50 bonus points, because the author remembered to explain this. http://answall.com/questions/8302/70
– Bacco