How to do dynamic and generic preparation with mysqli?

4

I have a mobile application that sends me the following parameters

$tabela    = $_POST[“tabela”];    // ex: “pessoa”
$atributos = $_POST[“atributos”]; // ex: “codigo, nome, cpf”
$valores   = $_POST[“valores”];   // ex: “10, Rafael, 1234567890”
$argumento = $_POST[“argumento”]; // ex: “iss”
$tamanho   = $_POST[“tamanho”];   // ex: “?, ?, ?”

Once I have received the parameters, I will prepare it. This will be generic. My question is how do I get the $valores and saved in new variables to insert in places $codigo , $nome , $cpf ?

Remembering that this function is dynamic, time can receive a person, once a payment with more or fewer attributes, and so on.

I thought about creating variables at runtime but I do not know if this is possible.

$stmt = $conn->prepare("INSERT INTO $tabela ($atributos) VALUES ($tamanho)");
$stmt->bind_param($argumento, $codigo, $nome, $cpf);
    
asked by anonymous 20.01.2017 / 11:21

3 answers

3

What you want to do is bind_param with variable arguments, you can do it by creating an array of references and use the callback call_user_func_array to bind the bind function to the parameter array:

public function my_bind_param( $stmt, $argumento, $array_valores ) 
{
    $params = array_merge($argumento, $array_valores);

    return call_user_func_array( array( $stmt, 'bind_param' ), 
                                 referenciar( $params ));
}

private function referenciar( $array )
{
        $ref = array();

        foreach( $array as $key => $value )
            $ref[$key] = &$array[$key];

        return $ref;
} 
    
20.01.2017 / 12:21
2

Well, it seems kind of strange to answer your own question, but it's that everyone who answered helped me put this script together and come up with a result that I found to be satisfactory.

I do not have much experience with security yet, but from what I've researched it seems to me that it's a good solution. If you find otherwise, please express yourself because it will help me a lot.

follow the code:

<?php

include_once 'Funcoes.php';
include_once 'Config.php';
include_once 'MinhasTabelas.php';

class Util {

private $con;
private $tabela;
private $funcoes;

/**
 * Recebe a tabela que sera manipilada
 * O costrutor cria uma instancia da classe Funcao que contem funcoes uteis.
 * Depois elecria uma nova conexao mysqli e salva no atributo $con.
 * @param type $tabela : "usuario" : Nome da tabela que sera manipulada.
 */
public function __construct($tabela) {
    // Verifica se a tabela existe no banco de dados. se sim, segue o codigo ou para o programa.
    if ((new Tabelas ())->isExist($tabela)) {
        $this->tabela = $tabela;
        $this->funcoes = new Funcao();

        // Cria uma conexão;
        $this->con = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
        // Verifica conexão
        if ($this->con->connect_error) {
            $flag['flag'] = 'CONN_FAILED';
            die (json_encode($flag));
        }
    }else{
        $flag['flag'] = 'INVALID_TABLE';
        die(json_encode($flag));
    }
}

/**
 * Insere um registro no banco de dados.
 * @param type $atributos   = "nome, idade, sexo"
 * @param type $valores     = "rafael, 21, M"
 * @param type $argumentos  = "sis" 
 * @return int = json formated
 */
public function create($atributos, $valores, $argumentos) {
    $qtdValores = $this->funcoes->getInterrogacoes($atributos);

    $valores = explode(",", $valores);

    $sql = "INSERT INTO $this->tabela ($atributos) VALUES ($qtdValores)";
    if ($stmt = $this->con->prepare($sql)) {
        $stmt->bind_param($argumentos, ...$valores);
        $stmt->execute();

        if ($stmt->affected_rows >= 1) {
            $json['flag'] = $stmt->insert_id;
        } else {
            $json['flag'] = 0;
        }
    } else {
        $json['flag'] = 0;
    }

    $stmt->close();
    $this->con->close();

    return $json;
}

}

class Tabelas {

private $tabs;

public function __construct() {
    // Em $tabs devera conter todas as tabelas que serão manipiuladas pelo sistema.
    // Elas devem ser separadas por ", " (virgula e um espaço);
    $tabs = "adm, usuario";
    $this->tabs = explode(", ", $tabs);
}

/**
 * Verifica se a tabela passada por parametro existe no banco de dados.
 * @param type $tabela = "usuario" : Nome da tabela que será verificada a existencia
 * @return boolean : retorna um true ou false
 */
public function isExist($tabela) {
    foreach ($this->tabs as &$val) {
        if ($val === $tabela) {
            return TRUE;
        }
    }
    return FALSE;
}

}

class Funcao {

/**
 * Esse metodo tem a função de trazer as quantidades de variaveis que serão manipuladas
 * @param type $param = "nome, sexo"    : Recebe os parametros que serão manipilados
 * @return type = "?, ?"                : retorna uma String com as ? para mysqli->prepare.
 */
function getInterrogacoes($param) {
    $qtdValores = explode(",", $param);
    foreach ($qtdValores as &$value) {
        $value = "? ";
    }
    $qtdValores = implode(",", $qtdValores);
    return $qtdValores;
}

}
    
21.01.2017 / 19:33
1

I've now created a function for generic insertion using PDO.

To test, I created a table with two fields:

CREATE TABLE 'teste' 
( 
    'id' INT NOT NULL AUTO_INCREMENT , 
    'nome' VARCHAR(255) NOT NULL , 
    'email' VARCHAR(255) NOT NULL , 
    PRIMARY KEY ('id')
);

HTML form:

<!DOCTYPE html>
<html>
<head>
    <title>Teste Insert PDO Genérico</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>
<body>

<form action="pdo_generico.php" method="post">

    Nome: <input type="text" name="nome"><br>
    E-mail: <input type="mail" name="email"><br>
    <input type="submit" value="Enviar">

</form>

</body>
</html>

pdo_generico.php

<?php
//CONFIG CONEXÃO
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'root';
$password = '';

// CONFIG TABELA E COLUNAS
$tabela = "teste";


                            // Os tipos podem ser:  
                            // bool,  int, str, file, outros(será tratado como str) 
$colunas = array(
                 'nome'  => 'str',
                 'email' => 'str',
                 );  

try 
{
    $pdo_connection = new PDO($dsn, $user, $password);
} catch(PDOException $e) 
{
    echo 'Connection failed: ' . $e->getMessage();
}   

$retorno_do_insert =  generic_PDO_insert($pdo_connection, $tabela, $colunas, $_POST);

if($retorno_do_insert)
{
    echo 'Dados inseridos com sucesso.';
}
else
{
    echo 'Erro.';
}

// =============================================

function generic_PDO_insert($conecta,$tabela,$colunas,$post)
{

    $pdo_tipos = array(
                        'bool'  => PDO::PARAM_BOOL, 
                        'int'   => PDO::PARAM_INT,
                        'str'   => PDO::PARAM_STR, 
                        'file'  => PDO::PARAM_LOB,
                        'outros'=> PDO::PARAM_STR
                      );        

    // Remove os campos que não forem enviados por POST para não criar placeholders para eles
    $colunas_existentes = array();
    $colunas_arr_key = array();
    foreach($colunas as $coluna => $tipo)
    {
        if(isset($post[$coluna]))
        {
            $colunas_existentes[$coluna]=$tipo;
            $colunas_arr_key[]=$coluna;
        }
    }
    $colunas = $colunas_existentes;

    //GERA os PLACEHOLDERS
    $colunas_str='';
    $placeholders_str='';

    $colunas_str = implode(",",$colunas_arr_key);
    $placeholders_str = ':'.implode(",:",$colunas_arr_key); 

    $sql= "INSERT INTO $tabela($colunas_str) VALUES($placeholders_str)";
    // echo $sql;

    try
    {
        $query= $conecta->prepare($sql);

        foreach ($colunas as $coluna => $tipo) 
        {
            $query->bindValue(":$coluna", $post[$coluna], $pdo_tipos[$tipo]);
        }

        $query->execute();

        return true;
    }
    catch(Exception $e)
    {
        return false;
        //echo $e->getMessage();
    }
}
    
20.01.2017 / 15:43