Mendapatkan string kueri SQL mentah dari pernyataan yang disiapkan PDO

130

Apakah ada cara untuk mendapatkan string SQL mentah dieksekusi ketika memanggil PDOStatement :: execute () pada pernyataan yang disiapkan? Untuk keperluan debugging ini akan sangat berguna.

Wilco
sumber
1
Untuk PHP> = 5.1, lihat di php.net/manual/en/pdostatement.debugdumpparams.php
Mawg mengatakan pulihkan Monica
1
Periksa fungsi satu-baris pdo-debug .
Sliq
Cara terbersih yang saya temukan adalah perpustakaan E_PDOStatement . Anda lakukan saja $stmt = $pdo->prepare($query); /* ... */ echo $stmt->fullQuery;. Ia bekerja dengan memperluas kelas PDOStatement , karenanya elegan seperti API PDO memungkinkan.
ComFreek

Jawaban:

110

Saya berasumsi Anda berarti bahwa Anda ingin query SQL final, dengan nilai parameter diinterpolasi ke dalamnya. Saya mengerti bahwa ini akan berguna untuk debugging, tetapi ini bukan cara pernyataan yang disiapkan bekerja. Parameter tidak digabungkan dengan pernyataan yang disiapkan di sisi klien, jadi PDO seharusnya tidak pernah memiliki akses ke string kueri yang dikombinasikan dengan parameternya.

Pernyataan SQL dikirim ke server database ketika Anda melakukan prep (), dan parameter dikirim secara terpisah ketika Anda mengeksekusi (). Log kueri umum MySQL memang menunjukkan SQL final dengan nilai yang diinterpolasi setelah Anda mengeksekusi (). Di bawah ini adalah kutipan dari log kueri umum saya. Saya menjalankan pertanyaan dari CLI mysql, bukan dari PDO, tetapi prinsipnya sama.

081016 16:51:28 2 Query       prepare s1 from 'select * from foo where i = ?'
                2 Prepare     [2] select * from foo where i = ?
081016 16:51:39 2 Query       set @a =1
081016 16:51:47 2 Query       execute s1 using @a
                2 Execute     [2] select * from foo where i = 1

Anda juga bisa mendapatkan apa yang Anda inginkan jika Anda menetapkan atribut PDO PDO :: ATTR_EMULATE_PREPARES. Dalam mode ini, PDO menginterpolasi parameter ke dalam kueri SQL dan mengirimkan seluruh kueri saat Anda mengeksekusi (). Ini bukan permintaan yang disiapkan dengan benar. Anda akan menghindari manfaat dari kueri yang disiapkan dengan menginterpolasi variabel ke dalam string SQL sebelum dieksekusi ().


Komentar dari @afilina:

Tidak, permintaan SQL tekstual tidak digabungkan dengan parameter selama eksekusi. Jadi tidak ada yang bisa ditunjukkan PDO kepada Anda.

Secara internal, jika Anda menggunakan PDO :: ATTR_EMULATE_PREPARES, PDO membuat salinan kueri SQL dan menginterpolasi nilai parameter ke dalamnya sebelum melakukan persiapan dan eksekusi. Tetapi PDO tidak mengekspos permintaan SQL yang dimodifikasi ini.

Objek PDOStatement memiliki properti $ queryString, tetapi ini hanya diatur dalam konstruktor untuk PDOStatement, dan itu tidak diperbarui ketika kueri ditulis ulang dengan parameter.

Ini akan menjadi permintaan fitur yang masuk akal bagi PDO untuk meminta mereka mengekspos kueri yang ditulis ulang. Tetapi bahkan itu tidak akan memberi Anda permintaan "lengkap" kecuali Anda menggunakan PDO :: ATTR_EMULATE_PREPARES.

Inilah sebabnya saya menunjukkan solusi di atas menggunakan log kueri umum server MySQL, karena dalam hal ini bahkan kueri yang disiapkan dengan placeholder parameter ditulis ulang di server, dengan nilai parameter yang dikembalikan ke string kueri. Tapi ini hanya dilakukan saat logging, bukan saat eksekusi query.

Bill Karwin
sumber
10
Dan bagaimana Anda mendapatkan kueri lubang saat PDO :: ATTR_EMULATE_PREPARES disetel ke TRUE?
Yasen Zhelev
2
@Yasen Zhelev: Jika PDO mengemulasi prepares, maka ia akan menginterpolasi nilai parameter ke dalam query sebelum ia menyiapkan query. Jadi MySQL tidak pernah melihat versi kueri dengan penampung parameter. MySQL hanya mencatat permintaan penuh.
Bill Karwin
2
@ Bill: 'Parameter tidak digabungkan dengan pernyataan yang disiapkan di sisi klien' - tunggu - tetapi apakah mereka digabungkan di sisi server? Atau bagaimana mysql memasukkan nilai ke dalam DB?
Stann
1
@afilina, tidak, kamu tidak bisa. Lihat penjelasan saya di atas.
Bill Karwin
3
Wow, downvote? Tolong jangan tembak kurirnya. Saya hanya menjelaskan cara kerjanya.
Bill Karwin
107
/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public static function interpolateQuery($query, $params) {
    $keys = array();

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }
    }

    $query = preg_replace($keys, $params, $query, 1, $count);

    #trigger_error('replaced '.$count.' keys');

    return $query;
}
bigwebguy
sumber
6
mengapa tidak menggunakan saja strtr(): lebih cepat, lebih sederhana, hasil yang sama. strtr($query, $params);
Tony Chiboucas
Apa gunanya ini?
Hanya ingin mampir dan mengucapkan terima kasih juga, berada di luar seluruh kelas tambahan untuk ini yang sekarang saya hapus mendukung ini karena kecil dan cemerlang :). Jadi sangat berguna untuk debbug semua permintaan aplikasi lakukan pada setiap halaman dengan login mereka: D
NaughtySquid
Melihat fungsi ini dan itu membuat saya sangat senang, meskipun, sesuatu yang saya tidak mengerti, mengapa Anda memeriksa $keymenjadi stringdan tidak $value? Apakah saya melewatkan sesuatu? Alasan saya bertanya ini karena output ini, parameter kedua tidak dilihat sebagai string:string(115) "INSERT INTO tokens (token_type, token_hash, user_id) VALUES ('resetpassword', hzFs5RLMpKwTeShTjP9AkTA2jtxXls86, 1);"
Kerwin Sneijders
1
Ini adalah awal yang baik, tetapi gagal jika nilai $ param itu sendiri menyertakan tanda tanya ("?").
chickenchilli
32

Saya memodifikasi metode untuk menyertakan penanganan keluaran array untuk pernyataan seperti WHERE IN (?).

UPDATE: Baru saja menambahkan periksa untuk nilai NULL dan duplikasi $ params sehingga nilai-nilai $ param aktual tidak dimodifikasi.

Kerja bagus bigwebguy dan terima kasih!

/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public function interpolateQuery($query, $params) {
    $keys = array();
    $values = $params;

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }

        if (is_string($value))
            $values[$key] = "'" . $value . "'";

        if (is_array($value))
            $values[$key] = "'" . implode("','", $value) . "'";

        if (is_null($value))
            $values[$key] = 'NULL';
    }

    $query = preg_replace($keys, $values, $query);

    return $query;
}
Mike
sumber
2
Saya pikir harus Anda lakukan $values = $params;bukan $values = array().
menguji
Sepotong kecil lain yang terlewatkan di sini adalah string. Untuk menangkap mereka, letakkan ini di atas is_arraycek:if (is_string($value)) $values[$key] = "'" . $value . "'";
treeface
Ini hanya nilai ikatan terbatas hanya sekali di preg_replace. tambahkan baris ini setelah $values = $params; $values_limit = []; $words_repeated = array_count_values(str_word_count($sql, 1, ':_')); tambahkan ini di dalam terlebih dahulu jika di foreach $values_limit[$key] = (isset($words_repeated[':'.$key]) ? intval($words_repeated[':'.$key]) : 1);dan ini di yang lain di foreach $values_limit = [];gunakan foreach loop $ values ​​lagi untuk preg_replace denganisset($values_limit[$key])
vee
misalnya nilai loop $. if (is_array($values)) { foreach ($values as $key => $val) { if (isset($values_limit[$key])) { $sql = preg_replace(['/:'.$key.'/'], [$val], $sql, $values_limit[$key], $count); } } unset($key, $val); } else { $sql = preg_replace($keys, $values, $sql, 1, $count); }
vee
12

Agak terlambat mungkin tapi sekarang ada PDOStatement::debugDumpParams

Membuang informasi yang terkandung oleh pernyataan yang disiapkan langsung pada output. Ini akan memberikan query SQL yang digunakan, jumlah parameter yang digunakan (Params), daftar parameter, dengan nama mereka, tipe (paramtype) sebagai integer, nama kunci atau posisi mereka, dan posisi dalam permintaan (jika ini didukung oleh driver PDO, jika tidak, itu akan menjadi -1).

Anda dapat menemukan lebih banyak di dokumen resmi php

Contoh:

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindValue(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();

$sth->debugDumpParams();

?>
Jimmy Kane
sumber
dan untuk keterbacaan yang lebih baik:echo '<pre>'; $sth->debugDumpParams(); echo '</pre>';
SandroMarques
10

Solusi adalah dengan secara sukarela menempatkan kesalahan dalam kueri dan untuk mencetak pesan kesalahan:

//Connection to the database
$co = new PDO('mysql:dbname=myDB;host=localhost','root','');
//We allow to print the errors whenever there is one
$co->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

//We create our prepared statement
$stmt = $co->prepare("ELECT * FROM Person WHERE age=:age"); //I removed the 'S' of 'SELECT'
$stmt->bindValue(':age','18',PDO::PARAM_STR);
try {
    $stmt->execute();
} catch (PDOException $e) {
    echo $e->getMessage();
}

Output standar:

SQLSTATE [42000]: Kesalahan sintaksis atau pelanggaran akses: [...] dekat 'MEMILIH * DARI ORANG DI MANA usia = 18' pada baris 1

Penting untuk dicatat bahwa itu hanya mencetak 80 karakter pertama dari permintaan.

JacopoStanchi
sumber
Saya tidak tahu mengapa ini diturunkan. Sederhana dan berhasil. Ini bekerja dengan cepat. Jauh lebih cepat daripada menyalakan log, mencari baris yang tepat di log, lalu menonaktifkan log, lalu membersihkan file log.
Bojan Hrnkas
@ BojanHrnkas panjang sampel kesalahan sangat terbatas. Untuk permintaan sederhana seperti itu, lebih mudah untuk mengganti placeholder dengan variabel hanya secara manual. Dan metode ini hanya berfungsi jika Anda mengaktifkan emulasi.
Akal Sehat Anda
9

Menambahkan sedikit lebih banyak ke kode oleh Mike - nilai berjalan untuk menambahkan tanda kutip tunggal

/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public function interpolateQuery($query, $params) {
    $keys = array();
    $values = $params;

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }

        if (is_array($value))
            $values[$key] = implode(',', $value);

        if (is_null($value))
            $values[$key] = 'NULL';
    }
    // Walk the array to see if we can add single-quotes to strings
    array_walk($values, create_function('&$v, $k', 'if (!is_numeric($v) && $v!="NULL") $v = "\'".$v."\'";'));

    $query = preg_replace($keys, $values, $query, 1, $count);

    return $query;
}
Chris Go
sumber
1
Sangat berguna, saya melakukan beberapa modifikasi untuk mengesampingkan fungsi bindParam dari kelas PDOStatement dan memvalidasi jika nilainya adalah string atau integer dengan nilai PDO: PARAMS .
Sergio Flores
Di mana kita bisa melihat itu?
Mawg mengatakan mengembalikan Monica
8

PDOStatement memiliki properti publik $ queryString. Itu harus menjadi apa yang Anda inginkan.

Saya baru saja memperhatikan bahwa PDOStatement memiliki metode debugDumpParams () yang tidak didokumentasikan yang mungkin ingin Anda lihat.

Robot Kaca
sumber
1
DebugDumpParams tidak didokumentasikan php.net/manual/en/pdostatement.debugdumpparams.php
mloskot
Nggak. $ queryString tidak menampilkan nilai param yang disertakan.
Andreas
5

Anda bisa memperluas kelas PDOStatement untuk menangkap variabel terikat dan menyimpannya untuk digunakan nanti. Kemudian 2 metode dapat ditambahkan, satu untuk sanitasi variabel (debugBindedVariables) dan lainnya untuk mencetak kueri dengan variabel-variabel tersebut (debugQuery):

class DebugPDOStatement extends \PDOStatement{
  private $bound_variables=array();
  protected $pdo;

  protected function __construct($pdo) {
    $this->pdo = $pdo;
  }

  public function bindValue($parameter, $value, $data_type=\PDO::PARAM_STR){
    $this->bound_variables[$parameter] = (object) array('type'=>$data_type, 'value'=>$value);
    return parent::bindValue($parameter, $value, $data_type);
  }

  public function bindParam($parameter, &$variable, $data_type=\PDO::PARAM_STR, $length=NULL , $driver_options=NULL){
    $this->bound_variables[$parameter] = (object) array('type'=>$data_type, 'value'=>&$variable);
    return parent::bindParam($parameter, $variable, $data_type, $length, $driver_options);
  }

  public function debugBindedVariables(){
    $vars=array();

    foreach($this->bound_variables as $key=>$val){
      $vars[$key] = $val->value;

      if($vars[$key]===NULL)
        continue;

      switch($val->type){
        case \PDO::PARAM_STR: $type = 'string'; break;
        case \PDO::PARAM_BOOL: $type = 'boolean'; break;
        case \PDO::PARAM_INT: $type = 'integer'; break;
        case \PDO::PARAM_NULL: $type = 'null'; break;
        default: $type = FALSE;
      }

      if($type !== FALSE)
        settype($vars[$key], $type);
    }

    if(is_numeric(key($vars)))
      ksort($vars);

    return $vars;
  }

  public function debugQuery(){
    $queryString = $this->queryString;

    $vars=$this->debugBindedVariables();
    $params_are_numeric=is_numeric(key($vars));

    foreach($vars as $key=>&$var){
      switch(gettype($var)){
        case 'string': $var = "'{$var}'"; break;
        case 'integer': $var = "{$var}"; break;
        case 'boolean': $var = $var ? 'TRUE' : 'FALSE'; break;
        case 'NULL': $var = 'NULL';
        default:
      }
    }

    if($params_are_numeric){
      $queryString = preg_replace_callback( '/\?/', function($match) use( &$vars) { return array_shift($vars); }, $queryString);
    }else{
      $queryString = strtr($queryString, $vars);
    }

    echo $queryString.PHP_EOL;
  }
}


class DebugPDO extends \PDO{
  public function __construct($dsn, $username="", $password="", $driver_options=array()) {
    $driver_options[\PDO::ATTR_STATEMENT_CLASS] = array('DebugPDOStatement', array($this));
    $driver_options[\PDO::ATTR_PERSISTENT] = FALSE;
    parent::__construct($dsn,$username,$password, $driver_options);
  }
}

Dan kemudian Anda bisa menggunakan kelas bawaan ini untuk tujuan debugging.

$dbh = new DebugPDO('mysql:host=localhost;dbname=test;','user','pass');

$var='user_test';
$sql=$dbh->prepare("SELECT user FROM users WHERE user = :test");
$sql->bindValue(':test', $var, PDO::PARAM_STR);
$sql->execute();

$sql->debugQuery();
print_r($sql->debugBindedVariables());

Yang menghasilkan

PILIH pengguna DARI pengguna DI MANA pengguna = 'user_test'

Array ([: test] => user_test)

Otamay
sumber
4

Saya menghabiskan banyak waktu meneliti situasi ini untuk kebutuhan saya sendiri. Ini dan beberapa utas SO lainnya sangat membantu saya, jadi saya ingin membagikan apa yang saya hasilkan.

Meskipun memiliki akses ke string kueri yang diinterpolasi adalah manfaat yang signifikan saat pemecahan masalah, kami ingin dapat mempertahankan log hanya dari kueri tertentu (oleh karena itu, menggunakan log basis data untuk tujuan ini tidak ideal). Kami juga ingin dapat menggunakan log untuk menciptakan kembali kondisi tabel pada waktu tertentu, oleh karena itu, kami perlu memastikan string yang diinterpolasi lolos dengan benar. Akhirnya, kami ingin memperluas fungsionalitas ini ke seluruh basis kode kami harus menulis ulang sesedikit mungkin (tenggat waktu, pemasaran, dan semacamnya; Anda tahu bagaimana ini).

Solusi saya adalah untuk memperluas fungsionalitas objek PDOStatement default untuk cache nilai parameter (atau referensi), dan ketika pernyataan dieksekusi, gunakan fungsionalitas objek PDO untuk keluar dari parameter dengan benar ketika mereka disuntikkan kembali ke permintaan tali. Kami kemudian dapat mengikat untuk mengeksekusi metode objek pernyataan dan mencatat permintaan aktual yang dieksekusi pada waktu itu ( atau setidaknya setepat reproduksi mungkin) .

Seperti yang saya katakan, kami tidak ingin memodifikasi seluruh basis kode untuk menambahkan fungsionalitas ini, jadi kami menimpa default bindParam()dan bindValue()metode objek PDOStatement, melakukan caching kami terhadap data yang terikat, kemudian memanggil parent::bindParam()atau induk :: bindValue(). Ini memungkinkan basis kode kami yang ada untuk terus berfungsi seperti biasa.

Akhirnya, ketika execute()metode dipanggil, kami melakukan interpolasi kami dan memberikan string yang dihasilkan sebagai properti baru E_PDOStatement->fullQuery. Ini bisa berupa output untuk melihat kueri atau, misalnya, ditulis ke file log.

Ekstensi, bersama dengan petunjuk pemasangan dan konfigurasi, tersedia di github:

https://github.com/noahheck/E_PDOStatement

PENOLAKAN :
Jelas, seperti yang saya sebutkan, saya menulis ekstensi ini. Karena ini dikembangkan dengan bantuan dari banyak utas di sini, saya ingin memposting solusi saya di sini kalau-kalau ada orang lain menemukan utas ini, sama seperti yang saya lakukan.

myesain
sumber
Terima kasih telah berbagi. Tidak upvote karena jawaban terlalu lama dengan kode terlalu sedikit
T30
1

Properti $ queryString yang disebutkan mungkin hanya akan mengembalikan permintaan yang diteruskan, tanpa parameter diganti dengan nilainya. Di .Net, saya memiliki bagian tangkapan dari query saya melakukan pencarian sederhana menggantikan pada parameter dengan nilai-nilai mereka yang disediakan sehingga log kesalahan dapat menunjukkan nilai aktual yang digunakan untuk permintaan. Anda harus dapat menghitung parameter dalam PHP, dan mengganti parameter dengan nilai yang ditetapkan.

Kibbee
sumber
1

Kamu bisa memakai sprintf(str_replace('?', '"%s"', $sql), ...$params);

Berikut ini sebuah contoh:

function mysqli_prepared_query($link, $sql, $types='', $params=array()) {
    echo sprintf(str_replace('?', '"%s"', $sql), ...$params);
    //prepare, bind, execute
}

$link = new mysqli($server, $dbusername, $dbpassword, $database);
$sql = "SELECT firstname, lastname FROM users WHERE userage >= ? AND favecolor = ?";
$types = "is"; //integer and string
$params = array(20, "Brown");

if(!$qry = mysqli_prepared_query($link, $sql, $types, $params)){
    echo "Failed";
} else {
    echo "Success";
}

Perhatikan ini hanya berfungsi untuk PHP> = 5.6

kurdtpage
sumber
0

Saya tahu pertanyaan ini agak lama, tapi, saya menggunakan kode ini sejak beberapa waktu yang lalu (saya telah menggunakan respons dari @ chris-go), dan sekarang, kode ini sudah usang dengan PHP 7.2

Saya akan memposting versi terbaru dari kode ini (Kredit untuk kode utama berasal dari @bigwebguy , @mike dan @ chris-go , semuanya menjawab pertanyaan ini):

/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public function interpolateQuery($query, $params) {
    $keys = array();
    $values = $params;

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }

        if (is_array($value))
            $values[$key] = implode(',', $value);

        if (is_null($value))
            $values[$key] = 'NULL';
    }
    // Walk the array to see if we can add single-quotes to strings
    array_walk($values, function(&$v, $k) { if (!is_numeric($v) && $v != "NULL") $v = "\'" . $v . "\'"; });

    $query = preg_replace($keys, $values, $query, 1, $count);

    return $query;
}

Perhatikan bahwa perubahan pada kode berada pada fungsi array_walk (), menggantikan create_function dengan fungsi anonim. Ini membuat kode yang baik ini berfungsi dan kompatibel dengan PHP 7.2 (dan juga berharap versi yang akan datang).

Sakura Kinomoto
sumber
-1

Agak terkait ... jika Anda hanya mencoba membersihkan variabel tertentu, Anda dapat menggunakan PDO :: quote . Misalnya, untuk mencari beberapa kondisi LIKE sebagian jika Anda terjebak dengan kerangka kerja terbatas seperti CakePHP:

$pdo = $this->getDataSource()->getConnection();
$results = $this->find('all', array(
    'conditions' => array(
        'Model.name LIKE ' . $pdo->quote("%{$keyword1}%"),
        'Model.name LIKE ' . $pdo->quote("%{$keyword2}%"),
    ),
);
Synexis
sumber
-1

Jawaban Mike bekerja dengan baik sampai Anda menggunakan nilai ikat "gunakan kembali".
Sebagai contoh:

SELECT * FROM `an_modules` AS `m` LEFT JOIN `an_module_sites` AS `ms` ON m.module_id = ms.module_id WHERE 1 AND `module_enable` = :module_enable AND `site_id` = :site_id AND (`module_system_name` LIKE :search OR `module_version` LIKE :search)

Jawaban Mike hanya dapat menggantikan yang pertama: cari tetapi bukan yang kedua.
Jadi, saya menulis ulang jawabannya untuk bekerja dengan beberapa parameter yang dapat digunakan kembali dengan benar.

public function interpolateQuery($query, $params) {
    $keys = array();
    $values = $params;
    $values_limit = [];

    $words_repeated = array_count_values(str_word_count($query, 1, ':_'));

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
            $values_limit[$key] = (isset($words_repeated[':'.$key]) ? intval($words_repeated[':'.$key]) : 1);
        } else {
            $keys[] = '/[?]/';
            $values_limit = [];
        }

        if (is_string($value))
            $values[$key] = "'" . $value . "'";

        if (is_array($value))
            $values[$key] = "'" . implode("','", $value) . "'";

        if (is_null($value))
            $values[$key] = 'NULL';
    }

    if (is_array($values)) {
        foreach ($values as $key => $val) {
            if (isset($values_limit[$key])) {
                $query = preg_replace(['/:'.$key.'/'], [$val], $query, $values_limit[$key], $count);
            } else {
                $query = preg_replace(['/:'.$key.'/'], [$val], $query, 1, $count);
            }
        }
        unset($key, $val);
    } else {
        $query = preg_replace($keys, $values, $query, 1, $count);
    }
    unset($keys, $values, $values_limit, $words_repeated);

    return $query;
}
vee
sumber
-1

preg_replace tidak berfungsi untuk saya dan ketika binding_ lebih dari 9, binding_1 dan binding_10 diganti dengan str_replace (meninggalkan 0 di belakang), jadi saya membuat penggantian ke belakang:

public function interpolateQuery($query, $params) {
$keys = array();
    $length = count($params)-1;
    for ($i = $length; $i >=0; $i--) {
            $query  = str_replace(':binding_'.(string)$i, '\''.$params[$i]['val'].'\'', $query);
           }
        // $query  = str_replace('SQL_CALC_FOUND_ROWS', '', $query, $count);
        return $query;

}

Semoga seseorang menemukan itu berguna.

Markos F
sumber
-1

Saya perlu login string kueri penuh setelah mengikat param jadi ini adalah bagian dalam kode saya. Semoga bermanfaat bagi setiap orang yang memiliki masalah yang sama.

/**
 * 
 * @param string $str
 * @return string
 */
public function quote($str) {
    if (!is_array($str)) {
        return $this->pdo->quote($str);
    } else {
        $str = implode(',', array_map(function($v) {
                    return $this->quote($v);
                }, $str));

        if (empty($str)) {
            return 'NULL';
        }

        return $str;
    }
}

/**
 * 
 * @param string $query
 * @param array $params
 * @return string
 * @throws Exception
 */
public function interpolateQuery($query, $params) {
    $ps = preg_split("/'/is", $query);
    $pieces = [];
    $prev = null;
    foreach ($ps as $p) {
        $lastChar = substr($p, strlen($p) - 1);

        if ($lastChar != "\\") {
            if ($prev === null) {
                $pieces[] = $p;
            } else {
                $pieces[] = $prev . "'" . $p;
                $prev = null;
            }
        } else {
            $prev .= ($prev === null ? '' : "'") . $p;
        }
    }

    $arr = [];
    $indexQuestionMark = -1;
    $matches = [];

    for ($i = 0; $i < count($pieces); $i++) {
        if ($i % 2 !== 0) {
            $arr[] = "'" . $pieces[$i] . "'";
        } else {
            $st = '';
            $s = $pieces[$i];
            while (!empty($s)) {
                if (preg_match("/(\?|:[A-Z0-9_\-]+)/is", $s, $matches, PREG_OFFSET_CAPTURE)) {
                    $index = $matches[0][1];
                    $st .= substr($s, 0, $index);
                    $key = $matches[0][0];
                    $s = substr($s, $index + strlen($key));

                    if ($key == '?') {
                        $indexQuestionMark++;
                        if (array_key_exists($indexQuestionMark, $params)) {
                            $st .= $this->quote($params[$indexQuestionMark]);
                        } else {
                            throw new Exception('Wrong params in query at ' . $index);
                        }
                    } else {
                        if (array_key_exists($key, $params)) {
                            $st .= $this->quote($params[$key]);
                        } else {
                            throw new Exception('Wrong params in query with key ' . $key);
                        }
                    }
                } else {
                    $st .= $s;
                    $s = null;
                }
            }
            $arr[] = $st;
        }
    }

    return implode('', $arr);
}
ducminh1903
sumber