Nhận chuỗi truy vấn SQL thô từ các câu lệnh được chuẩn bị PDO


130

Có cách nào để chuỗi SQL thô được thực thi khi gọi PDOStatement :: exec () trên một câu lệnh đã chuẩn bị không? Đối với mục đích gỡ lỗi, điều này sẽ cực kỳ hữu ích.



1
Kiểm tra chức năng một dòng pdo-debug .
Sliq

Cách sạch nhất mà tôi tìm thấy là thư viện E_PDOStatement . Bạn chỉ cần làm $stmt = $pdo->prepare($query); /* ... */ echo $stmt->fullQuery;. Nó hoạt động bằng cách mở rộng lớp PDOStatement , do đó thanh lịch như API PDO cho phép.
ComFalet

Câu trả lời:


110

Tôi giả sử bạn có nghĩa là bạn muốn truy vấn SQL cuối cùng, với các giá trị tham số được nội suy vào nó. Tôi hiểu rằng điều này sẽ hữu ích cho việc gỡ lỗi, nhưng nó không phải là cách mà các câu lệnh được chuẩn bị hoạt động. Các tham số không được kết hợp với một câu lệnh được chuẩn bị ở phía máy khách, do đó PDO không bao giờ có quyền truy cập vào chuỗi truy vấn kết hợp với các tham số của nó.

Câu lệnh SQL được gửi đến máy chủ cơ sở dữ liệu khi bạn chuẩn bị () và các tham số được gửi riêng khi bạn thực hiện (). Nhật ký truy vấn chung của MySQL hiển thị SQL cuối cùng với các giá trị được nội suy sau khi bạn thực thi (). Dưới đây là một đoạn trích từ nhật ký truy vấn chung của tôi. Tôi đã chạy các truy vấn từ mysql CLI, không phải từ PDO, nhưng nguyên tắc là như nhau.

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

Bạn cũng có thể nhận được những gì bạn muốn nếu bạn đặt thuộc tính PDO PDO :: ATTR_EMULATE_PREPARES. Trong chế độ này, PDO nội suy các tham số vào truy vấn SQL và gửi toàn bộ truy vấn khi bạn thực thi (). Đây không phải là một truy vấn chuẩn bị thực sự. Bạn sẽ tránh được các lợi ích của các truy vấn đã chuẩn bị bằng cách nội suy các biến vào chuỗi SQL trước khi thực hiện ().


Nhận xét lại từ @afilina:

Không, truy vấn SQL văn bản không được kết hợp với các tham số trong khi thực hiện. Vì vậy, không có gì để PDO cho bạn thấy.

Trong nội bộ, nếu bạn sử dụng PDO :: ATTR_EMULATE_PREPARES, PDO tạo một bản sao của truy vấn SQL và nội suy các giá trị tham số vào đó trước khi thực hiện chuẩn bị và thực thi. Nhưng PDO không để lộ truy vấn SQL đã sửa đổi này.

Đối tượng PDOStatement có thuộc tính $ queryString, nhưng điều này chỉ được đặt trong hàm tạo cho PDOStatement và nó không được cập nhật khi truy vấn được viết lại bằng các tham số.

PDO sẽ là một yêu cầu tính năng hợp lý để yêu cầu họ đưa ra truy vấn viết lại. Nhưng ngay cả điều đó sẽ không cung cấp cho bạn truy vấn "hoàn thành" trừ khi bạn sử dụng PDO :: ATTR_EMULATE_PREPARES.

Đây là lý do tại sao tôi hiển thị cách giải quyết ở trên về việc sử dụng nhật ký truy vấn chung của máy chủ MySQL, bởi vì trong trường hợp này, ngay cả một truy vấn đã chuẩn bị với trình giữ chỗ tham số cũng được ghi lại trên máy chủ, với các giá trị tham số được điền vào chuỗi truy vấn. Nhưng điều này chỉ được thực hiện trong khi đăng nhập, không phải trong khi thực hiện truy vấn.


10
Và làm thế nào để bạn có được truy vấn lỗ khi PDO :: ATTR_EMULATE_PREPARES được đặt thành TRUE?
Yasen Zhelev

2
@Yasen Zhelev: Nếu PDO đang mô phỏng chuẩn bị, thì nó sẽ nội suy các giá trị tham số vào truy vấn trước khi chuẩn bị truy vấn. Vì vậy, MySQL không bao giờ thấy phiên bản của truy vấn với trình giữ chỗ tham số. MySQL chỉ ghi lại truy vấn đầy đủ.
Bill Karwin

2
@ Bill: 'Tham số không được kết hợp với câu lệnh được chuẩn bị ở phía máy khách' - chờ đã - nhưng liệu chúng có kết hợp ở phía máy chủ không? Hoặc làm thế nào để mysql chèn các giá trị vào DB?
Stann

1
@afilina, không, bạn không thể. Xem giải thích của tôi ở trên.
Bill Karwin

3
Wow, một downvote? Xin đừng bắn sứ giả. Tôi chỉ mô tả cách nó hoạt động.
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;
}

6
Tại sao không chỉ sử dụng strtr(): nhanh hơn, đơn giản hơn, kết quả tương tự. strtr($query, $params);
Tony Chiboucas

Việc sử dụng cho việc này là gì?

Chỉ muốn dừng lại và gửi lời cảm ơn của tôi, đã ở bên ngoài toàn bộ một lớp học thêm cho điều này mà bây giờ tôi đã loại bỏ để ủng hộ điều này vì nó nhỏ bé và rực rỡ :). Vì vậy, rất hữu ích cho việc gỡ lỗi tất cả các truy vấn mà ứng dụng đang thực hiện trên mỗi trang bằng cách đăng nhập chúng: D
NaughtySquid

Nhìn thấy chức năng này và nó làm tôi rất hạnh phúc, mặc dù, một điều tôi không hiểu, tại sao bạn kiểm tra xem có $keyphải là một stringvà không $value? Tui bỏ lỡ điều gì vậy? Lý do tôi hỏi điều này là vì đầu ra này, tham số thứ hai không được xem dưới dạng chuỗi:string(115) "INSERT INTO tokens (token_type, token_hash, user_id) VALUES ('resetpassword', hzFs5RLMpKwTeShTjP9AkTA2jtxXls86, 1);"
Kerwin Sneijder

1
Đây là một khởi đầu tốt, nhưng sẽ thất bại nếu giá trị của chính $ param bao gồm một dấu hỏi ("?").
chickenchilli

32

Tôi đã sửa đổi phương thức để bao gồm xử lý đầu ra của mảng cho các câu lệnh như WHERE IN (?).

CẬP NHẬT: Chỉ cần thêm kiểm tra giá trị NULL và trùng lặp $ params để giá trị $ param thực tế không bị sửa đổi.

Greatwebguy làm việc tốt và cảm ơn!

/**
 * 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;
}

2
Tôi nghĩ bạn phải làm $values = $params;thay vì $values = array().
thử nghiệm

Một mảnh nhỏ khác bị bỏ lỡ ở đây là chuỗi. Để nắm bắt những thứ đó, hãy đặt cái này lên trên tấm is_arrayséc:if (is_string($value)) $values[$key] = "'" . $value . "'";
cây

Đây chỉ là giá trị ràng buộc giới hạn chỉ một lần trong preg numplace. thêm dòng này sau khi $values = $params; $values_limit = []; $words_repeated = array_count_values(str_word_count($sql, 1, ':_')); thêm dòng này vào bên trong trước nếu trong foreach $values_limit[$key] = (isset($words_repeated[':'.$key]) ? intval($words_repeated[':'.$key]) : 1);và dòng này trước tiên trong foreach $values_limit = [];sử dụng foreach loop $ value một lần nữa vào preg numplace vớiisset($values_limit[$key])
vee

ví dụ vòng lặp giá trị $. 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

Có lẽ hơi muộn nhưng bây giờ có PDOStatement::debugDumpParams

Bỏ các thông tin có trong một tuyên bố chuẩn bị trực tiếp trên đầu ra. Nó sẽ cung cấp truy vấn SQL đang sử dụng, số lượng tham số được sử dụng (Params), danh sách các tham số, với tên, loại (paramtype) dưới dạng số nguyên, tên khóa hoặc vị trí của chúng và vị trí trong truy vấn (nếu điều này được hỗ trợ bởi trình điều khiển PDO, nếu không, nó sẽ là -1).

Bạn có thể tìm thêm về các tài liệu php chính thức

Thí dụ:

<?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();

?>


và để dễ đọc hơn:echo '<pre>'; $sth->debugDumpParams(); echo '</pre>';
SandroMarques

10

Một giải pháp là tự nguyện đặt một lỗi trong truy vấn và in thông báo lỗi:

//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();
}

Đầu ra tiêu chuẩn:

SQLSTATE [42000]: Lỗi cú pháp hoặc vi phạm quyền truy cập: [...] gần 'BẦU * TỪ NGƯỜI Ở ĐÂU = 18' ở dòng 1

Điều quan trọng cần lưu ý là nó chỉ in 80 ký tự đầu tiên của truy vấn.


Tôi không biết tại sao điều này đã bị hạ thấp. Nó là đơn giản và nó hoạt động. Nó hoạt động nhanh chóng. Nhanh hơn nhiều so với bật nhật ký, tìm kiếm đúng dòng trong nhật ký, sau đó vô hiệu hóa nhật ký, sau đó dọn sạch các tệp nhật ký.
Bojan Hrnkas

@BojanHrnkas độ dài của mẫu lỗi rất hạn chế. Đối với một truy vấn đơn giản như vậy, việc thay thế một trình giữ chỗ bằng một biến chỉ bằng tay sẽ dễ dàng hơn. Và phương pháp này chỉ hoạt động nếu bạn kích hoạt mô phỏng.
Ý thức chung của bạn

9

Mike đã thêm một chút vào mã - đi bộ các giá trị để thêm dấu ngoặc đơn

/**
 * 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;
}

1
Rất hữu ích, tôi đã thực hiện một số sửa đổi để ghi đè hàm bindParam của lớp PDOStatement và xác thực nếu giá trị là một chuỗi hoặc số nguyên với các giá trị PDO: PARAM .
Sergio Flores

chúng ta có thể thấy điều đó ở đâu?
Mawg nói rằng phục hồi Monica

8

PDOStatement có thuộc tính công khai $ queryString. Nó nên là những gì bạn muốn.

Tôi chỉ lưu ý rằng PDOStatement có một phương thức không có giấy tờ debugDumpParams () mà bạn cũng có thể muốn xem xét.


1
Các debugDumpParams là không có tài liệu php.net/manual/en/pdostatement.debugdumpparams.php
mloskot

Không. $ queryString không hiển thị các giá trị param được bao gồm.
Andreas

5

Bạn có thể mở rộng lớp PDOStatement để nắm bắt các biến bị ràng buộc và lưu trữ chúng để sử dụng sau. Sau đó, có thể thêm 2 phương thức, một phương thức khử trùng biến (debugBindedVariables) và phương thức khác để in truy vấn với các biến đó (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);
  }
}

Và sau đó bạn có thể sử dụng lớp kế thừa này để gỡ lỗi các purpouse.

$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());

Kết quả là

CHỌN người dùng TỪ người dùng WHERE user = 'user_test'

Mảng ([: test] => user_test)


4

Tôi đã dành rất nhiều thời gian để nghiên cứu tình huống này cho nhu cầu của riêng tôi. Điều này và một số chủ đề SO khác đã giúp tôi rất nhiều, vì vậy tôi muốn chia sẻ những gì tôi nghĩ ra.

Mặc dù có quyền truy cập vào chuỗi truy vấn được nội suy là một lợi ích đáng kể trong khi khắc phục sự cố, chúng tôi muốn có thể duy trì nhật ký của một số truy vấn nhất định (do đó, sử dụng nhật ký cơ sở dữ liệu cho mục đích này là không lý tưởng). Chúng tôi cũng muốn có thể sử dụng các bản ghi để tạo lại tình trạng của các bảng tại bất kỳ thời điểm nào, do đó, chúng tôi cần phải đảm bảo các chuỗi nội suy được thoát đúng. Cuối cùng, chúng tôi muốn mở rộng chức năng này cho toàn bộ cơ sở mã của chúng tôi phải viết lại càng ít càng tốt (thời hạn, tiếp thị, v.v. bạn biết nó như thế nào).

Giải pháp của tôi là mở rộng chức năng của đối tượng PDOStatement mặc định để lưu trữ các giá trị (hoặc tham chiếu) được tham số hóa và khi câu lệnh được thực thi, sử dụng chức năng của đối tượng PDO để thoát đúng các tham số khi chúng được đưa trở lại vào truy vấn chuỗi. Sau đó chúng ta có thể liên kết để thực thi phương thức của đối tượng câu lệnh và ghi nhật ký truy vấn thực tế được thực hiện tại thời điểm đó ( hoặc ít nhất là trung thành với một bản sao chép càng tốt) .

Như tôi đã nói, chúng tôi không muốn sửa đổi toàn bộ cơ sở mã để thêm chức năng này, vì vậy chúng tôi ghi đè mặc định bindParam()bindValue()phương thức của đối tượng PDOStatement, thực hiện lưu trữ dữ liệu bị ràng buộc, sau đó gọi parent::bindParam()hoặc cha mẹ ::bindValue() . Điều này cho phép cơ sở mã hiện tại của chúng tôi tiếp tục hoạt động như bình thường.

Cuối cùng, khi execute()phương thức được gọi, chúng tôi thực hiện phép nội suy và cung cấp chuỗi kết quả dưới dạng một thuộc tính mới E_PDOStatement->fullQuery. Đây có thể là đầu ra để xem truy vấn hoặc, ví dụ, được ghi vào một tệp nhật ký.

Phần mở rộng, cùng với hướng dẫn cài đặt và cấu hình, có sẵn trên github:

https://github.com/noahheck/E_PDOStatement

TUYÊN BỐ TỪ CHỐI :
Rõ ràng, như tôi đã đề cập, tôi đã viết phần mở rộng này. Bởi vì nó được phát triển với sự giúp đỡ từ nhiều chủ đề ở đây, tôi muốn đăng giải pháp của mình lên đây trong trường hợp có ai khác bắt gặp những chủ đề này, giống như tôi đã làm.


Cám ơn vì đã chia sẻ. Không upvote vì câu trả lời quá dài với quá ít mã
T30

1

Thuộc tính $ queryString được đề cập có thể sẽ chỉ trả về truy vấn được truyền vào mà không có các tham số được thay thế bằng các giá trị của chúng. Trong .Net, tôi có phần bắt của trình thực thi truy vấn của mình thực hiện tìm kiếm đơn giản thay thế các tham số bằng các giá trị được cung cấp để nhật ký lỗi có thể hiển thị các giá trị thực tế đang được sử dụng cho truy vấn. Bạn sẽ có thể liệt kê các tham số trong PHP và thay thế các tham số bằng giá trị được gán của chúng.


1

Bạn có thể dùng sprintf(str_replace('?', '"%s"', $sql), ...$params);

Đây là một ví dụ:

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";
}

Lưu ý điều này chỉ hoạt động với PHP> = 5.6


0

Tôi biết câu hỏi này hơi cũ, nhưng, tôi đã sử dụng mã này từ rất lâu rồi (tôi đã sử dụng phản hồi từ @ chris-go) và bây giờ, các mã này đã lỗi thời với PHP 7.2

Tôi sẽ đăng phiên bản cập nhật của các mã này (Tín dụng cho mã chính là từ @bigwebguy , @mike@ chris-go , tất cả chúng đều trả lời câu hỏi này):

/**
 * 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;
}

Lưu ý thay đổi trên mã là trên hàm Array_walk (), thay thế hàm tạo_ hàm bằng một hàm ẩn danh. Điều này làm cho các đoạn mã tốt này hoạt động và tương thích với PHP 7.2 (và hy vọng các phiên bản trong tương lai cũng vậy).


-1

Hơi liên quan ... nếu bạn chỉ cố gắng vệ sinh một biến cụ thể, bạn có thể sử dụng PDO :: quote . Ví dụ: để tìm kiếm nhiều điều kiện THÍCH một phần nếu bạn bị mắc kẹt với một khung giới hạn như CakePHP:

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

-1

Câu trả lời của Mike đang hoạt động tốt cho đến khi bạn sử dụng giá trị ràng buộc "tái sử dụng".
Ví dụ:

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)

Câu trả lời của Mike chỉ có thể thay thế đầu tiên: tìm kiếm chứ không phải thứ hai.
Vì vậy, tôi viết lại câu trả lời của anh ấy để làm việc với nhiều tham số có thể được sử dụng lại đúng cách.

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;
}

-1

preg numplace không hoạt động với tôi và khi ràng buộc_ trên 9, ràng buộc_1 và ràng buộc_10 đã được thay thế bằng str numplace (bỏ lại 0 phía sau), vì vậy tôi đã thay thế ngược lại:

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;

}

Hy vọng ai đó thấy nó hữu ích.


-1

Tôi cần phải đăng nhập chuỗi truy vấn đầy đủ sau khi liên kết param vì vậy đây là một phần trong mã của tôi. Hy vọng, nó hữu ích cho mọi người mũ có cùng một vấn đề.

/**
 * 
 * @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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.