Replace certain characters in a string from array values - php

I've got an array :
$arr = [5, "David"];
I've got a query :
$query = "SELECT * FROM users WHERE user_id = ? AND user_name = ?";
I want to create a logger query that takes these 2 and returns the query, manually.
logger($query, $arr); // returns : SELECT * FROM users WHERE user_id = 5 AND user_name = 'David'
I'm using eloquent's capsule manager.
I tried doing something like this but it doesn't replace anything :
function logger($query, $arr) {
foreach($arr as $a) {
is_numeric($a) ? str_replace("?",$a,$query) : str_replace("?","'".$a."'",$query);
}
return $query;
}
Why isn't this code working?

str_replace does not modify its argument, it returns the modified value instead. So you need to assign its output to a variable. However the problem is that str_replace will replace all occurrences of the pattern at the same time. Instead, you can use preg_replace, which has a limit parameter which can be used to tell it to replace only one occurrence of the pattern for each call. I've used a pattern for preg_replace that will ensure that only a ? on its own in the query will be replaced, in case there is a question mark elsewhere e.g. in a column alias. Change your function to this:
function logger($query, $arr) {
foreach($arr as $a) {
$query = is_numeric($a) ?
preg_replace("/(?<=\s)\?(?=\s|$)/", $a, $query, 1) :
preg_replace("/(?<=\s)\?(?=\s|$)/", "'$a'", $query, 1);
}
return $query;
}
Output:
SELECT * FROM users WHERE user_id = 5 AND user_name = 'David'
Demo on 3v4l.org

I found out how to do it, by using str_replace_first and assiging the value into a variable.
foreach($params as $a) {
$query = is_numeric($a) ? str_replace_first("?",$a,$query,1) : str_replace_first("?","'".$a."'",$query);
}
return $query;
str_replace_first :
function str_replace_first($search_for, $replace_with, $in) {
$pos = strpos($in, $search_for);
if($pos === false) {
return $in;
} else {
return substr($in, 0, $pos) . $replace_with . substr($in, $pos + strlen($search_for), strlen($in));
}
}

Related

Search elements inside string

Let me explain what I want to do.
I need to connect to a device using SSH and execute some shell commands. I get the result of these commands (stream, using ssh2_fetch_stream) and save into a variable. This Works fine.
But what I need to know is how can I search by elements in a string?
Here a sample string:
$stringToSearch = "id=10 name=interfacename1 stringx=102040 stringy=50606040,id=20 name=interfacename2 stringx=872345 stringy=23875454,id=30 name=interfacename3 stringx=654389 stringy=34567865";
I need to obtain all the 'name=' results, like (SELECT name FROM stringToSearch;) and this would return:
____name____
interfacename1
interfacename2
interfacename3
I created a simple function to read this string.
<?php
function readString($stringToSearch, $start, $end) {
$result = '-1';
// I need to do a loop here, but how can I do it to read and return all elements between $start and $end?
if(strlen(strstr($stringToSearch, $start)) > 0)
{
$startsAt = strpos($stringToSearch, $start) + strlen($start);
$endsAt = strpos($stringToSearch, $end, $startsAt);
$result = substr($stringToSearch, $startsAt, $endsAt - $startsAt);
}
return $result;
}
$stringToSearch = 'id=10 name=interfacename1 stringx=102040 stringy=50606040,id=20 name=interfacename2 stringx=872345 stringy=23875454,id=30 name=interfacename3 stringx=654389 stringy=34567865';
$element = readString($stringToSearch, 'name=', ' '); // Will return only the 1st element
?>
But I can only retrieve the 1st element. How can I get all the elements in this string?
try with this solution :)
<?php
$stringToSearch = "id=10 name=interfacename1 stringx=102040 stringy=50606040,id=20 name=interfacename2 stringx=872345 stringy=23875454,id=30 name=interfacename3 stringx=654389 stringy=34567865";
var_dump(getName($stringToSearch));
function getName($stringToSearch) {
$pattern = '/name=([A-Z0-9]*)/i';
if(preg_match_all($pattern, $stringToSearch, $result) == 0){
return NULL;
}
return $result[1];
}
Enhancement (to get all values of desired parameter):
/**
* #param String $varName : the name of variable to get their values. ex: stringx, stringy
* #param String $stringToSearch : the string to search in
* #return Array if variable was found in $stringToSearch, NULL else
*/
function getName($varName, $stringToSearch) {
$pattern = '/' . $varName . '=([A-Z0-9]*)/i';
if(preg_match_all($pattern, $stringToSearch, $result) == 0){
return NULL;
}
return $result[1];
}
Using Halayem Anis's answer I was able to find all the elements in the string.
But I found other problem. If I have a string like that: name="foo bar" (with space at the middle).
To solve this problem I adapted the $pattern.
The result is:
//$varname = string to be searched like name=
//$stringToSearch = the original string
function getName($varName, $stringToSearch) {
$pattern = '/(?<=' . $varName . ')"(.*?)"/i'; // Changed this to get all between name=""
if(preg_match_all($pattern, $stringToSearch, $result) == 0){
return NULL;
}
return $result[1];
}

Display query created with stmt [duplicate]

Is there a way to get the raw SQL string executed when calling PDOStatement::execute() on a prepared statement? For debugging purposes this would be extremely useful.
I assume you mean that you want the final SQL query, with parameter values interpolated into it. I understand that this would be useful for debugging, but it is not the way prepared statements work. Parameters are not combined with a prepared statement on the client-side, so PDO should never have access to the query string combined with its parameters.
The SQL statement is sent to the database server when you do prepare(), and the parameters are sent separately when you do execute(). MySQL's general query log does show the final SQL with values interpolated after you execute(). Below is an excerpt from my general query log. I ran the queries from the mysql CLI, not from PDO, but the principle is the same.
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
You can also get what you want if you set the PDO attribute PDO::ATTR_EMULATE_PREPARES. In this mode, PDO interpolate parameters into the SQL query and sends the whole query when you execute(). This is not a true prepared query. You will circumvent the benefits of prepared queries by interpolating variables into the SQL string before execute().
Re comment from #afilina:
No, the textual SQL query is not combined with the parameters during execution. So there's nothing for PDO to show you.
Internally, if you use PDO::ATTR_EMULATE_PREPARES, PDO makes a copy of the SQL query and interpolates parameter values into it before doing the prepare and execute. But PDO does not expose this modified SQL query.
The PDOStatement object has a property $queryString, but this is set only in the constructor for the PDOStatement, and it's not updated when the query is rewritten with parameters.
It would be a reasonable feature request for PDO to ask them to expose the rewritten query. But even that wouldn't give you the "complete" query unless you use PDO::ATTR_EMULATE_PREPARES.
This is why I show the workaround above of using the MySQL server's general query log, because in this case even a prepared query with parameter placeholders is rewritten on the server, with parameter values backfilled into the query string. But this is only done during logging, not during query execution.
/**
* 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;
}
I modified the method to include handling output of arrays for statements like WHERE IN (?).
UPDATE: Just added check for NULL value and duplicated $params so actual $param values are not modified.
Great work bigwebguy and thanks!
/**
* 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;
}
A solution is to voluntarily put an error in the query and to print the error's message:
//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();
}
Standard output:
SQLSTATE[42000]: Syntax error or access violation: [...] near 'ELECT * FROM Person WHERE age=18' at line 1
It is important to note that it only prints the first 80 characters of the query.
A bit late probably but now there is PDOStatement::debugDumpParams
Dumps the informations contained by a prepared statement directly on
the output. It will provide the SQL query in use, the number of
parameters used (Params), the list of parameters, with their name,
type (paramtype) as an integer, their key name or position, and the
position in the query (if this is supported by the PDO driver,
otherwise, it will be -1).
You can find more on the official php docs
Example:
<?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();
?>
Added a little bit more to the code by Mike - walk the values to add single quotes
/**
* 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;
}
PDOStatement has a public property $queryString. It should be what you want.
I've just notice that PDOStatement has an undocumented method debugDumpParams() which you may also want to look at.
You can extend PDOStatement class to capture the bounded variables and store them for later use. Then 2 methods may be added, one for variable sanitizing ( debugBindedVariables ) and another to print the query with those variables ( 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);
}
}
And then you can use this inherited class for debugging purpouses.
$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());
Resulting in
SELECT user FROM users WHERE user = 'user_test'
Array (
[:test] => user_test
)
I spent a good deal of time researching this situation for my own needs. This and several other SO threads helped me a great deal, so I wanted to share what I came up with.
While having access to the interpolated query string is a significant benefit while troubleshooting, we wanted to be able to maintain a log of only certain queries (therefore, using the database logs for this purpose was not ideal). We also wanted to be able to use the logs to recreate the condition of the tables at any given time, therefore, we needed to make certain the interpolated strings were escaped properly. Finally, we wanted to extend this functionality to our entire code base having to re-write as little of it as possible (deadlines, marketing, and such; you know how it is).
My solution was to extend the functionality of the default PDOStatement object to cache the parameterized values (or references), and when the statement is executed, use the functionality of the PDO object to properly escape the parameters when they are injected back in to the query string. We could then tie in to execute method of the statement object and log the actual query that was executed at that time (or at least as faithful of a reproduction as possible).
As I said, we didn't want to modify the entire code base to add this functionality, so we overwrite the default bindParam() and bindValue() methods of the PDOStatement object, do our caching of the bound data, then call parent::bindParam() or parent::bindValue(). This allowed our existing code base to continue to function as normal.
Finally, when the execute() method is called, we perform our interpolation and provide the resultant string as a new property E_PDOStatement->fullQuery. This can be output to view the query or, for example, written to a log file.
The extension, along with installation and configuration instructions, are available on github:
https://github.com/noahheck/E_PDOStatement
DISCLAIMER:
Obviously, as I mentioned, I wrote this extension. Because it was developed with help from many threads here, I wanted to post my solution here in case anyone else comes across these threads, just as I did.
None of the existing answers seemed complete or safe, so I came up with this function, which has the following improvements:
works with both unnamed (?) and named (:foo) parameters.
using PDO::quote() to properly escape values which are not NULL, int, float or bool.
properly handles string values containing "?" and ":foo" without mistaking them for placeholders.
function interpolateSQL(PDO $pdo, string $query, array $params) : string {
$s = chr(2); // Escape sequence for start of placeholder
$e = chr(3); // Escape sequence for end of placeholder
$keys = [];
$values = [];
// Make sure we use escape sequences that are not present in any value
// to escape the placeholders.
foreach ($params as $key => $value) {
while( mb_stripos($value, $s) !== false ) $s .= $s;
while( mb_stripos($value, $e) !== false ) $e .= $e;
}
foreach ($params as $key => $value) {
// Build a regular expression for each parameter
$keys[] = is_string($key) ? "/$s:$key$e/" : "/$s\?$e/";
// Treat each value depending on what type it is.
// While PDO::quote() has a second parameter for type hinting,
// it doesn't seem reliable (at least for the SQLite driver).
if( is_null($value) ){
$values[$key] = 'NULL';
}
elseif( is_int($value) || is_float($value) ){
$values[$key] = $value;
}
elseif( is_bool($value) ){
$values[$key] = $value ? 'true' : 'false';
}
else{
$value = str_replace('\\', '\\\\', $value);
$values[$key] = $pdo->quote($value);
}
}
// Surround placehodlers with escape sequence, so we don't accidentally match
// "?" or ":foo" inside any of the values.
$query = preg_replace(['/\?/', '/(:[a-zA-Z0-9_]+)/'], ["$s?$e", "$s$1$e"], $query);
// Replace placeholders with actual values
$query = preg_replace($keys, $values, $query, 1, $count);
// Verify that we replaced exactly as many placeholders as there are keys and values
if( $count !== count($keys) || $count !== count($values) ){
throw new \Exception('Number of replacements not same as number of keys and/or values');
}
return $query;
}
I'm sure it can be improved further.
In my case, I eventually ended up just logging the actual "unprepared query" (i.e. SQL containing placeholders) along with JSON-encoded parameters. However, this code might come in use for some use cases where you really need to interpolate the final SQL query.
You can use sprintf(str_replace('?', '"%s"', $sql), ...$params);
Here is an example:
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";
}
Note this only works for PHP >= 5.6
The $queryString property mentioned will probably only return the query passed in, without the parameters replaced with their values. In .Net, I have the catch part of my query executer do a simple search replace on the parameters with their values which was supplied so that the error log can show actual values that were being used for the query. You should be able to enumerate the parameters in PHP, and replace the parameters with their assigned value.
I know this question is a bit old, but, I'm using this code since lot time ago (I've used response from #chris-go), and now, these code are obsolete with PHP 7.2
I'll post an updated version of these code (Credit for the main code are from #bigwebguy, #mike and #chris-go, all of them answers of this question):
/**
* 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;
}
Note the change on the code are on array_walk() function, replacing create_function by an anonymous function. This make these good piece of code functional and compatible with PHP 7.2 (and hope future versions too).
preg_replace didn't work for me and when binding_ was over 9, binding_1 and binding_10 was replaced with str_replace (leaving the 0 behind), so I made the replacements backwards:
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;
}
Hope someone finds it useful.
I need to log full query string after bind param so this is a piece in my code. Hope, it is useful for everyone hat has the same issue.
/**
*
* #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);
}
Mike's answer is working good until you are using the "re-use" bind value.
For example:
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)
The Mike's answer can only replace first :search but not the second.
So, I rewrite his answer to work with multiple parameters that can re-used properly.
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;
}

String to array without explode

I am using an api to retrieve data from another server the data returned is something like this:
accountid=10110 type=prem servertime=1263752255 validuntil=1266163393
username= curfiles=11 curspace=188374868 bodkb=5000000 premkbleft=24875313
This is a whole string I need two values out of whole string, I am currently using preg_match to get it, but just to learn more and improve my coding is there any other way or function in which all values are automatically convert to array?
Thank You.
Sooo, my faster-than-preg_split, strpos-based function looks like this:
function unpack_server_data($serverData)
{
$output = array();
$keyStart = 0;
$keepParsing = true;
do
{
$keyEnd = strpos($serverData, '=', $keyStart);
$valueStart = $keyEnd + 1;
$valueEnd = strpos($serverData, ' ', $valueStart);
if($valueEnd === false)
{
$valueEnd = strlen($serverData);
$keepParsing = false;
}
$key = substr($serverData, $keyStart, $keyEnd - $keyStart);
$value = substr($serverData, $valueStart, $valueEnd - $valueStart);
$output[$key] = $value;
$keyStart = $valueEnd + 1;
}
while($keepParsing);
return $output;
}
It looks for an equals character, then looks for a space character, and uses these two to decide where a key name begins, and when a value name begins.
Using explode is the fastest for this, no matter what.
However, to answer you question, you can do this many ways. But just because you can, doesn't mean you should. But if you really wanna make it weird, try this.
UPdated using strpos
$arr = array();
$begin = 0;
$str = trim($str); # remove leading and trailing whitespace
while ($end = strpos($str, ' ', $begin)) {
$split = strpos($str, '=', $begin);
if ($split > $end) break;
$arr[substr($str, $begin, $split-$begin)] = substr($str, $split+1, $end-$split-1);
$begin = $end+1;
}
try out parse_str maybe you need to do str_replace(' ', '&', $string); before

How to pass an array into a function, and return the results with an array

So I'm trying to learn how to pass arrays through a function, so that I can get around PHP's inability to return multiple values. Haven't been able to get anything to work so far, but here is my best try. Can anybody point out where I'm going wrong?
function foo($array)
{
$array[3]=$array[0]+$array[1]+$array[2];
return $array;
}
$waffles[0]=1;
$waffles[1]=2;
$waffles[2]=3;
foo($waffles);
echo $waffles[3];
For clarification: I want to be able to pass multiple variables into a function, do something, then return multiple variables back out while keeping them seperate. This was just an example I was trying to get working as a work around for not being able to return multiple variables from an array
You seem to be looking for pass-by-reference, to do that make your function look this way (note the ampersand):
function foo(&$array)
{
$array[3]=$array[0]+$array[1]+$array[2];
}
Alternately, you can assign the return value of the function to a variable:
function foo($array)
{
$array[3]=$array[0]+$array[1]+$array[2];
return $array;
}
$waffles = foo($waffles)
You're passing the array into the function by copy. Only objects are passed by reference in PHP, and an array is not an object. Here's what you do (note the &)
function foo(&$arr) { # note the &
$arr[3] = $arr[0]+$arr[1]+$arr[2];
}
$waffles = array(1,2,3);
foo($waffles);
echo $waffles[3]; # prints 6
That aside, I'm not sure why you would do that particular operation like that. Why not just return the sum instead of assigning it to a new array element?
function foo(Array $array)
{
return $array;
}
Try
$waffles = foo($waffles);
Or pass the array by reference, like suggested in the other answers.
In addition, you can add new elements to an array without writing the index, e.g.
$waffles = array(1,2,3); // filling on initialization
or
$waffles = array();
$waffles[] = 1;
$waffles[] = 2;
$waffles[] = 3;
On a sidenote, if you want to sum all values in an array, use array_sum()
I always return multiple values by using a combination of list() and array()s:
function DecideStuffToReturn() {
$IsValid = true;
$AnswerToLife = 42;
// Build the return array.
return array($IsValid, $AnswerToLife);
}
// Part out the return array in to multiple variables.
list($IsValid, $AnswerToLife) = DecideStuffToReturn();
You can name them whatever you like. I chose to keep the function variables and the return variables the same for consistency but you can call them whatever you like.
See list() for more information.
i know a Class is a bit the overkill
class Foo
{
private $sum = NULL;
public function __construct($array)
{
$this->sum[] = $array;
return $this;
}
public function getSum()
{
$sum = $this->sum;
for($i=0;$i<count($sum);$i++)
{
// get the last array index
$res[$i] = $sum[$i] + $sum[count($sum)-$i];
}
return $res;
}
}
$fo = new Foo($myarray)->getSum();
Here is how I do it. This way I can actually get a function to simulate returning multiple values;
function foo($array)
{
foreach($array as $_key => $_value)
{
$str .= "{$_key}=".$_value.'&';
}
return $str = substr($str, 0, -1);
}
/* Set the variables to pass to function, in an Array */
$waffles['variable1'] = "value1";
$waffles['variable2'] = "value2";
$waffles['variable3'] = "value3";
/* Call Function */
parse_str( foo( $waffles ));
/* Function returns multiple variable/value pairs */
echo $variable1 ."<br>";
echo $variable2 ."<br>";
echo $variable3 ."<br>";
Especially usefull if you want, for example all fields in a database
to be returned as variables, named the same as the database table fields.
See 'db_fields( )' function below.
For example, if you have a query
select login, password, email from members_table where id = $id
Function returns multiple variables:
$login, $password and $email
Here is the function:
function db_fields($field, $filter, $filter_by, $table = 'members_table') {
/*
This function will return as variable names, all fields that you request,
and the field values assigned to the variables as variable values.
$filter_by = TABLE FIELD TO FILTER RESULTS BY
$filter = VALUE TO FILTER BY
$table = TABLE TO RUN QUERY AGAINST
Returns single string value or ARRAY, based on whether user requests single
field or multiple fields.
We return all fields as variable names. If multiple rows
are returned, check is_array($return_field); If > 0, it contains multiple rows.
In that case, simply run parse_str($return_value) for each Array Item.
*/
$field = ($field == "*") ? "*,*" : $field;
$fields = explode(",",$field);
$assoc_array = ( count($fields) > 0 ) ? 1 : 0;
if (!$assoc_array) {
$result = mysql_fetch_assoc(mysql_query("select $field from $table where $filter_by = '$filter'"));
return ${$field} = $result[$field];
}
else
{
$query = mysql_query("select $field from $table where $filter_by = '$filter'");
while ($row = mysql_fetch_assoc($query)) {
foreach($row as $_key => $_value) {
$str .= "{$_key}=".$_value.'&';
}
return $str = substr($str, 0, -1);
}
}
}
Below is a sample call to function. So, If we need to get User Data for say $user_id = 12345, from the members table with fields ID, LOGIN, PASSWORD, EMAIL:
$filter = $user_id;
$filter_by = "ID";
$table_name = "members_table"
parse_str(db_fields('LOGIN, PASSWORD, EMAIL', $filter, $filter_by, $table_name));
/* This will return the following variables: */
echo $LOGIN ."<br>";
echo $PASSWORD ."<br>";
echo $EMAIL ."<br>";
We could also call like this:
parse_str(db_fields('*', $filter, $filter_by, $table_name));
The above call would return all fields as variable names.
You are not able to return 'multiple values' in PHP. You can return a single value, which might be an array.
function foo($test1, $test2, $test3)
{
return array($test1, $test2, $test3);
}
$test1 = "1";
$test2 = "2";
$test3 = "3";
$arr = foo($test1, $test2, $test3);
$test1 = $arr[0];
$test2 = $arr[1];
$test3 = $arr[2];
Another way is:
$NAME = "John";
$EMAIL = "John#gmail.com";
$USERNAME = "John123";
$PASSWORD = "1234";
$array = Array ("$NAME","$EMAIL","$USERNAME","$PASSWORD");
function getAndReturn (Array $array){
return $array;
}
print_r(getAndReturn($array));

Getting raw SQL query string from PDO prepared statements

Is there a way to get the raw SQL string executed when calling PDOStatement::execute() on a prepared statement? For debugging purposes this would be extremely useful.
I assume you mean that you want the final SQL query, with parameter values interpolated into it. I understand that this would be useful for debugging, but it is not the way prepared statements work. Parameters are not combined with a prepared statement on the client-side, so PDO should never have access to the query string combined with its parameters.
The SQL statement is sent to the database server when you do prepare(), and the parameters are sent separately when you do execute(). MySQL's general query log does show the final SQL with values interpolated after you execute(). Below is an excerpt from my general query log. I ran the queries from the mysql CLI, not from PDO, but the principle is the same.
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
You can also get what you want if you set the PDO attribute PDO::ATTR_EMULATE_PREPARES. In this mode, PDO interpolate parameters into the SQL query and sends the whole query when you execute(). This is not a true prepared query. You will circumvent the benefits of prepared queries by interpolating variables into the SQL string before execute().
Re comment from #afilina:
No, the textual SQL query is not combined with the parameters during execution. So there's nothing for PDO to show you.
Internally, if you use PDO::ATTR_EMULATE_PREPARES, PDO makes a copy of the SQL query and interpolates parameter values into it before doing the prepare and execute. But PDO does not expose this modified SQL query.
The PDOStatement object has a property $queryString, but this is set only in the constructor for the PDOStatement, and it's not updated when the query is rewritten with parameters.
It would be a reasonable feature request for PDO to ask them to expose the rewritten query. But even that wouldn't give you the "complete" query unless you use PDO::ATTR_EMULATE_PREPARES.
This is why I show the workaround above of using the MySQL server's general query log, because in this case even a prepared query with parameter placeholders is rewritten on the server, with parameter values backfilled into the query string. But this is only done during logging, not during query execution.
/**
* 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;
}
I modified the method to include handling output of arrays for statements like WHERE IN (?).
UPDATE: Just added check for NULL value and duplicated $params so actual $param values are not modified.
Great work bigwebguy and thanks!
/**
* 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;
}
A solution is to voluntarily put an error in the query and to print the error's message:
//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();
}
Standard output:
SQLSTATE[42000]: Syntax error or access violation: [...] near 'ELECT * FROM Person WHERE age=18' at line 1
It is important to note that it only prints the first 80 characters of the query.
A bit late probably but now there is PDOStatement::debugDumpParams
Dumps the informations contained by a prepared statement directly on
the output. It will provide the SQL query in use, the number of
parameters used (Params), the list of parameters, with their name,
type (paramtype) as an integer, their key name or position, and the
position in the query (if this is supported by the PDO driver,
otherwise, it will be -1).
You can find more on the official php docs
Example:
<?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();
?>
Added a little bit more to the code by Mike - walk the values to add single quotes
/**
* 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;
}
PDOStatement has a public property $queryString. It should be what you want.
I've just notice that PDOStatement has an undocumented method debugDumpParams() which you may also want to look at.
You can extend PDOStatement class to capture the bounded variables and store them for later use. Then 2 methods may be added, one for variable sanitizing ( debugBindedVariables ) and another to print the query with those variables ( 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);
}
}
And then you can use this inherited class for debugging purpouses.
$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());
Resulting in
SELECT user FROM users WHERE user = 'user_test'
Array (
[:test] => user_test
)
I spent a good deal of time researching this situation for my own needs. This and several other SO threads helped me a great deal, so I wanted to share what I came up with.
While having access to the interpolated query string is a significant benefit while troubleshooting, we wanted to be able to maintain a log of only certain queries (therefore, using the database logs for this purpose was not ideal). We also wanted to be able to use the logs to recreate the condition of the tables at any given time, therefore, we needed to make certain the interpolated strings were escaped properly. Finally, we wanted to extend this functionality to our entire code base having to re-write as little of it as possible (deadlines, marketing, and such; you know how it is).
My solution was to extend the functionality of the default PDOStatement object to cache the parameterized values (or references), and when the statement is executed, use the functionality of the PDO object to properly escape the parameters when they are injected back in to the query string. We could then tie in to execute method of the statement object and log the actual query that was executed at that time (or at least as faithful of a reproduction as possible).
As I said, we didn't want to modify the entire code base to add this functionality, so we overwrite the default bindParam() and bindValue() methods of the PDOStatement object, do our caching of the bound data, then call parent::bindParam() or parent::bindValue(). This allowed our existing code base to continue to function as normal.
Finally, when the execute() method is called, we perform our interpolation and provide the resultant string as a new property E_PDOStatement->fullQuery. This can be output to view the query or, for example, written to a log file.
The extension, along with installation and configuration instructions, are available on github:
https://github.com/noahheck/E_PDOStatement
DISCLAIMER:
Obviously, as I mentioned, I wrote this extension. Because it was developed with help from many threads here, I wanted to post my solution here in case anyone else comes across these threads, just as I did.
None of the existing answers seemed complete or safe, so I came up with this function, which has the following improvements:
works with both unnamed (?) and named (:foo) parameters.
using PDO::quote() to properly escape values which are not NULL, int, float or bool.
properly handles string values containing "?" and ":foo" without mistaking them for placeholders.
function interpolateSQL(PDO $pdo, string $query, array $params) : string {
$s = chr(2); // Escape sequence for start of placeholder
$e = chr(3); // Escape sequence for end of placeholder
$keys = [];
$values = [];
// Make sure we use escape sequences that are not present in any value
// to escape the placeholders.
foreach ($params as $key => $value) {
while( mb_stripos($value, $s) !== false ) $s .= $s;
while( mb_stripos($value, $e) !== false ) $e .= $e;
}
foreach ($params as $key => $value) {
// Build a regular expression for each parameter
$keys[] = is_string($key) ? "/$s:$key$e/" : "/$s\?$e/";
// Treat each value depending on what type it is.
// While PDO::quote() has a second parameter for type hinting,
// it doesn't seem reliable (at least for the SQLite driver).
if( is_null($value) ){
$values[$key] = 'NULL';
}
elseif( is_int($value) || is_float($value) ){
$values[$key] = $value;
}
elseif( is_bool($value) ){
$values[$key] = $value ? 'true' : 'false';
}
else{
$value = str_replace('\\', '\\\\', $value);
$values[$key] = $pdo->quote($value);
}
}
// Surround placehodlers with escape sequence, so we don't accidentally match
// "?" or ":foo" inside any of the values.
$query = preg_replace(['/\?/', '/(:[a-zA-Z0-9_]+)/'], ["$s?$e", "$s$1$e"], $query);
// Replace placeholders with actual values
$query = preg_replace($keys, $values, $query, 1, $count);
// Verify that we replaced exactly as many placeholders as there are keys and values
if( $count !== count($keys) || $count !== count($values) ){
throw new \Exception('Number of replacements not same as number of keys and/or values');
}
return $query;
}
I'm sure it can be improved further.
In my case, I eventually ended up just logging the actual "unprepared query" (i.e. SQL containing placeholders) along with JSON-encoded parameters. However, this code might come in use for some use cases where you really need to interpolate the final SQL query.
You can use sprintf(str_replace('?', '"%s"', $sql), ...$params);
Here is an example:
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";
}
Note this only works for PHP >= 5.6
The $queryString property mentioned will probably only return the query passed in, without the parameters replaced with their values. In .Net, I have the catch part of my query executer do a simple search replace on the parameters with their values which was supplied so that the error log can show actual values that were being used for the query. You should be able to enumerate the parameters in PHP, and replace the parameters with their assigned value.
I know this question is a bit old, but, I'm using this code since lot time ago (I've used response from #chris-go), and now, these code are obsolete with PHP 7.2
I'll post an updated version of these code (Credit for the main code are from #bigwebguy, #mike and #chris-go, all of them answers of this question):
/**
* 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;
}
Note the change on the code are on array_walk() function, replacing create_function by an anonymous function. This make these good piece of code functional and compatible with PHP 7.2 (and hope future versions too).
preg_replace didn't work for me and when binding_ was over 9, binding_1 and binding_10 was replaced with str_replace (leaving the 0 behind), so I made the replacements backwards:
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;
}
Hope someone finds it useful.
I need to log full query string after bind param so this is a piece in my code. Hope, it is useful for everyone hat has the same issue.
/**
*
* #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);
}
Mike's answer is working good until you are using the "re-use" bind value.
For example:
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)
The Mike's answer can only replace first :search but not the second.
So, I rewrite his answer to work with multiple parameters that can re-used properly.
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;
}

Categories