MySQL returning PDO placeholder names - php

There's a [similar post][1], but without a solution.
The following code is resulting in a MySQL query containing the placeholder names:
$the_image_itself = "abcde123def.jpg";
$title = "A Book";
$description = "Something to Read";
$the_image_itself = "%".$the_image_itself;
$stmt = $db->prepare("UPDATE nky_posts SET `post_title`=:title, `post_content`=:description WHERE `guid` LIKE :the_image_itself");
$stmt->bindParam(':title', $title);
$stmt->bindParam(':description', $description);
$stmt->bindValue(':the_image_itself', $the_image_itself, PDO::PARAM_STR);
$stmt->execute();
$stmt->debugDumpParams();
echo "<hr/>";
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$affected_rows = $stmt->rowCount();
The result looks like:
start SQL: [105] UPDATE nky_posts SET `post_title`=:title,
`post_content`=:description
WHERE `guid` LIKE :the_image_itself
Params: 3 Key:
Name: [6]
:title paramno=-1
name=[6] ":title"
is_param=1 param_type=2
Key: Name: [12] :description
paramno=-1 name=[12] ":description"
is_param=1 param_type=2
Key: Name: [17] :the_image_itself paramno=-1
name=[17] ":the_image_itself"
is_param=1 param_type=2
This is the object call:
try{
$db=new PDO('mysql:host=localhost;dbname=viewingr_ssdevwp; charset=utf8',$db_username,$db_password);
}
catch(PDOException $e){
echo 'Error connecting to MySQL!: '.$e->getMessage();
exit();
}

I don't know where you got the impression that debugDumpParams() will display the raw SQL query -- it will not. When using parameterized queries, you create a prepared statement at the database, and then send the parameter values alone. They are not sent together, meaning there's no way print the raw SQL query.
debugDumpParams() will only display the list of parameters, their names, types etc. but not their values. One thing you can do, however, is to inspect your MySQL query log to see the raw SQL query that was executed.
Once you've found the logs, you can use the following command to see the recently executed queries (provided you have SSH access):
$ sudo tail -f /usr/local/mysql/data/yourQueryLog.log
The above path is just an example. The actual path might be different on your system.

Well the "answer" is posted below, but the real answer is that I should have ceased banging my head against this problem and come back to it at a later date, which seems to be one of the most difficult things for me to do. At one point in my obsession I discovered a mysterious <br/> followed by some whitespaces in one of the placeholder values. I ended up doing substr($var, 0, -6) on the variable to remove the anomaly until noticing that I had inadvertently concatenated a <br/> to the end of the line that populated the variable; .<br/> - probably when deleting a line of output code for testing.
I was on the line with hostMonster tech support to try and get to MySQL logs because people say that that is the only place one can find out exactly WHAT MySQL is "seeing" when you use placeholders, but they don't log MySQL queries, because the file would be in the terrabytes.
At 3 or 4 am, I gave up.
Came back to it with a fresh head today and went through the following steps confirming each worked:
Create a simple SELECT statement without WHERE or placeholders:
$sql = "SELECT * FROM nky_posts";
Add a WHERE clause using "=" (not LIKE) with variable being something literal I know is in the DB:
$the_image = "image_url_from_phpMyAdmin";
$sql = "SELECT post_title FROM nky_posts WHERE guid = $the_image";
Substitute the literal variable with a single placeholder holding a known value:
$the_image = "image_url_from_phpMyAdmin";
$stmt->bindParam(':the_image', $the_image, PDO::PARAM_STR);
$sql = "SELECT post_title FROM nky_posts WHERE guid = :the_image";
Add the LIKE instead of = (remembering to concatenate placeholder variable with "%")
$the_image = "%" . $the_image . "%";
$stmt->bindParam(':the_image', $the_image, PDO::PARAM_STR);
$sql = "SELECT post_title FROM nky_posts WHERE guid LIKE :the_image_itself";
Replace the "known" variable with dynamic variable (from XML result in this case):
basename($oBookNode->getElementsByTagName('actual-link')->item(0)->nodeValue);
(Using basename() function to return just the image name from URL string in wordpress database)
Finally replace the SELECT statement with my UPDATE statement, adding two additional placeholders to hold the variables to be inserted. Final code:
$sql = "UPDATE nky_posts SET post_title=:title, post_content=:description WHERE guid LIKE :the_image";
$stmt = $db->prepare($sql);
//foreach loop begins here
foreach ($oDOM->getElementsByTagName('item') as $oBookNode)
{
$the_image = basename($oBookNode->getElementsByTagName('actual-link')->item(0)->nodeValue);
$title = $oBookNode->getElementsByTagName('title')->item(0)->nodeValue;
$description = $oBookNode->getElementsByTagName('actual-content')->item(0)->nodeValue;
//concat % to variable for LIKE clause (probably only needed first one in this case, but...)
$the_image = "%" . $the_image . "%";
$stmt->bindParam(':title', $title, PDO::PARAM_STR);
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
$stmt->bindParam(':the_image_itself', $another_image_itself, PDO::PARAM_STR);
$stmt->execute();
//end foreach loop
}
Thanks for the help, everyone.

The output from the debugDumpParams function call looks right.
That debugDumpParams function doesn't display the values of the bind parameters; it only shows the SQL text along with the placeholder names/positions, and their respective datatypes.
I'm not sure I understood the question you asked.
There's no need to invoke the fetchAll method on stmt, since it is an UPDATE statement.
Note that for rowCount, MySQL returns the number of rows that were actually changed by the statement, not the number of rows matched. That is, if the values in the columns being set were already set to the specified value, then MySQL doesn't "count" that row as being affected.

Related

How to deal with apostrophes and double quotes simultaneously in PHP

I have a HTML form, from which a PHP script extracts values, as shown below:
$dbc = mysqli_connect("all required info here...") or die("Error occurred");
$sent = "Any sentence here...which may contain apostrophe or double quotes or both";
$query = "SELECT * FROM myrecord WHERE sentence = '$sent'";
$result = mysqli_query($dbc, $query);
$data = mysqli_fetch_array($result);
mysqli_close($dbc);
The problem is, that the variable $sent can contain any string with a combination of either apostrophe or double quotes or both. This gives an error when going for execution of mysqli_query().
So even if I escape double quotes in initialization of $sent it will still create problem for execution of mysqli_query(). And if I escape both for ' and " then value of $sent does not remains what it actually needs to be (although I am not sure about whether escaping both ' and " will work or not).
Is there any built in function that automatically escapes all special characters of a string? Or any workaround that solves this problem?
[P.S. I have already searched some previous questions on stackoverflow and haven't been able to find a solution.]
What you want, and what you should do is used prepared statements (parameterized queries). With PDO, that would look something like this:
$stmt = $pdo->prepare('SELECT * FROM myrecord WHERE sentence = :sentence');
$stmt->execute([':sentence' => $sentence]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
//do stuff
}
mysqli supports them, too, but the API is slightly more cumbersome (IMO) - see mysqli_prepare docs for details:
$stmt = $mysqli->prepare('SELECT * FROM myrecord WHERE sentence = ?');
//or $stmt = mysqli_prepare($connection, $query);
Then, you bind the parameter (the value to be used in the WHERE clause) using bind_param:
$stmt->bind_param('s', $sentence);
//or mysqli_stmt_bind_param($stmt, 's', $sentence);
Then call execute - or mysqli_stmt_execute, and fetch the results using fetch - or mysqli_stmt_fetch...
As mentioned in the comments: the parameters and query string needn't be quoted in any way, because they're treated as separate entities. The result being that you can re-use the same prepared statement with different paramters:
$stmt = $pdo->prepare('SELECT * FROM table WHERE field = :field');
$fieldVals = [123, 46, 32]; // three values:
$results = array_fill_keys($fieldVals, null);
foreach ($fieldVals as $val) {
$stmt->execute([':field' => $val]);//execute with each value in $fieldVals array
$results[$val] = $stmt->fetchAll(PDO::FETCH_ASSOC); // fetch results for this field value
//optional, but good form:
$stmt->closeCursor();
}
you've now used the same statement 3 times, but only had to send the query string once. The query had to be parsed and processed once, and after that, you merely sent the paramters to the DB. This approach is generally faster, safer (prepared statements protect agains most injection attacks), and just all round better.

Difficulty linking proper $var values and db rows in this PHP foreach statement

I'm trying to insert a PHP function into a foreach loop in order to generate values for each row fetched from the db for the variable $Match.
The db query itself works properly, and the function which assigns values to variable $Match works properly when I test it with hard-coded values, but when I try combining it with the rest of the code in order to use db values it stops working properly. Specifically: 1) It only runs the first IF statement; and 2) If that statement is true, it's adding the same value for every row.
I've uploaded a functional example with hard-coded values to this sandbox http://sandbox.onlinephpfunctions.com/code
Declaring values for test case:
$User_Waist = "26";
$User_Hip = "38";
$Match = Null;
$waistMatch = Null;
$hipMatch = Null;
Query database & fetchAll
$stmt = $conn - > prepare("SELECT * FROM SizeChart WHERE FIND_IN_SET($User_Waist, Waist_Measurement) > 0 OR FIND_IN_SET($User_Hip, Hip_Measurement) > 0;");
$stmt - > bindValue(':Waist_Measurement', $Waist_Measurement, PDO::PARAM_STR);
$stmt - > bindValue(':Hip_Measurement', $Hip_Measurement, PDO::PARAM_STR);
$stmt - > execute();
$rows = $stmt - > fetchAll(PDO::FETCH_ASSOC);
Loop through results
$count = 0;
foreach($rows as $row) {
$count++;
Adds value to variable $Match
if (strpos($row['Waist_Measurement'], $User_Waist) !== false) {
$waistMatch = 'waistFit';
}
if (strpos($Hip_Measurement, $User_Hip) !== false) {
$hipMatch = 'hipFit';
}
$Match = $waistMatch.', '.$hipMatch;
Display Results
echo "Size #: ".$row['Size']."; Fit Matches: ".' '.$Match."; Waist: ".$row['Waist_Measurement'], "; Hip: ".$row['Hip_Measurement'], ".<br />";
The SQL text doesn't contain bind placeholders :Waist_Measurement or :Hip_Measurement.
The bindValue calls aren't going to work, since there's no placeholder of the specified name to bind a value to.
Here's an example that uses a bind placeholder named :fum. Note that this string appears both in the SQL text and as an argument to bindValue or bindParam.
$foo = "bar";
$sql = "SELECT fee FROM fi WHERE fo = :fum ";
// ^^^^
$sth = $dbh->prepare($sql);
$sth->bindValue(":fum", $foo, PDO::PARAM_STR);
// ^^^^
$sth->execute();
FOLLOWUP
This is the SQL text in your prepare.
(I notice that there's a semicolon at the end of the SQL text, and that may be causing an error; I normally don't include a trailing semicolon in my SQL text.)
SELECT *
FROM SizeChart
WHERE FIND_IN_SET($User_Waist, Waist_Measurement) > 0
OR FIND_IN_SET($User_Hip, Hip_Measurement) > 0
But the point is that there aren't any bind placeholders in that SQL text. When you do a:
->bindValue(":Waist_Measurement",...
^^^^^^^^^^^^^^^^^^
That's essentially saying "Hey! There's a string literal ':Waist_Measurement' in the SQL text of the prepared statement", and saying "in place of that string literal, use this value...".
But the thing is, that string literal does not appear in your SQL text. There's no bind placeholder in the statement. (There's not even a placeholder of a different name, I don't see any colon characters anywhere in the SQL.)
I'm surprised that PDO isn't throwing an error. Actually, PDO probably is throwing an error, but your code is ignoring it. If your code isn't going to check the return from prepare, execute, et al. then you can have PDO do the check and throw the exception for you, by specifying an attribute on the connection.
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Also...
The inclusion of PHP variables $User_Waist and $User_Hip is a little unusual in a prepared statement. One of the benefits of prepared statements is that variables representing values can be replaced with bind placeholders.
(I'm confused by what you are trying to do, I can't tell you how to fix it.)

MySQL Statement with question mark and colon

I am trying to decipher a particular statement that is in PHP program that is doing a Mysql query. The query has ?: and ? in it. What is there function?
$data = db_get_field("SELECT data FROM ?:order_data WHERE order_id = ?i AND type = 'A'", $order_id);
Thanks for any help.
Chris
They look like placeholders for prepared statements. You provide the ? where you will later provide an actual value. Consider this example from the PHP documentation:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
Source: http://php.net/manual/en/pdo.prepared-statements.php
Your particular example appears to be from CS-Cart. For instance, according to their documentation ?i forces the value passed into that placeholder to be converted to an integer (this is similar to the way sprintf works).
$number = 9;
echo sprintf( "I have %d tasks.", $number );
// Outputs "I have 9 tasks."
In the above case, $number is treated as an integer. If I tried to pass a string into that slot, I'd get very different results:
$number = "completed";
echo sprintf( "I have %d tasks.", $number );
// Outputs "I have 0 tasks."
Note here how my types didn't match up, and therefore 0 replaced completed in the output.
Bottom line, these are placeholders.
The characters ? are : identifiers to form a parametrized queries. Later you bind values to variables and the engine performs the query for multiple values. This works faster, as the query is compiled once by server rather than compiling it for each variable=value set.

PHP PDO Prepared Statements and Value Binding Gives Invalid Parameter Number Error

I'm having a slight problem with the PHP PDO library and prepared statements. As far as I can see the prepared statement below should work but it doesn't, instead I get: "PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens".
My PHP code for this section looks like:
$sql = 'INSERT INTO '.POLYGON_TABLE.' (user_id, polygon, polygon_type) VALUES (:userId, PolygonFromText(\'POLYGON((:polygonArea))\'), :polygonType)';
$sth = $this->pdo->prepare($sql);
$sth->bindValue(':userId', $polygon->getUserId(), \PDO::PARAM_INT);
$sth->bindValue(':polygonArea', $polygon->getPolygonAsText(), \PDO::PARAM_STR);
$sth->bindValue(':polygonType', $polygon->getPolygonType(), \PDO::PARAM_STR);
if($sth->execute()) {
return true;
} else {
return false;
}
I have done a var_dump of $polygon->getUserId(), $polygon->getPolygonAsText() and $polygon->getPolygonType() and get the following:
string(1) "1"
string(226) "53.897910476098765 -1.739655277929728, 53.865530797116 -2.080231449804728, 53.67235280490181 -2.006073734960978, 53.68862047002787 -1.621552250585978, 53.89305512284903 -1.539154789648478, 53.897910476098765 -1.739655277929728"
string(7) "commute"
The issue is with $polygon->getPolygonAsText() as commenting out this particular bindValue call and the PolygonFromText(\'POLYGON((:polygonArea))\') from the SQL statement causes the query to work.
I'm now completely at a loss. Anyone know what's wrong here? I can't see anything wrong with the text contained within $polygon->getPolygonAsText(). I have searched high and low for a solution to this and spent several hours this evening tinkering with the code but to no avail.
I have even tried the suggestions in these 2 stack overflow topics but they didn't work either:
Invalid parameter number on PDO Prepared Statement
PHP PDO prepared statements
Any help would be much appreciated...
Did you try passing in the entire expression as the bind value?
$sql = 'INSERT INTO '.POLYGON_TABLE.' (user_id, polygon, polygon_type) VALUES (:userId, PolygonFromText(:polygonArea), :polygonType)';
$sth = $this->pdo->prepare($sql);
$area = sprintf("POLYGON((%s))", $polygon->getPolygonAsText());
$sth->bindValue(':userId', $polygon->getUserId(), \PDO::PARAM_INT);
$sth->bindValue(':polygonArea', $area, \PDO::PARAM_STR);
$sth->bindValue(':polygonType', $polygon->getPolygonType(), \PDO::PARAM_STR);
It appears that you're trying to use a named parameter inside a string:
PolygonFromText(\'POLYGON((:polygonArea))\')
This would be akin to doing something like this:
UPDATE foo SET bar = 'blah blah :wontwork blah blah'
What you should try instead is binding the whole string in the query:
PolygonFromText(:polygonArea)
And then including the rest of the string in the bound value:
$sth->bindValue(':polygonArea', 'POLYGON((' . $polygon->getPolygonAsText() . '))', \PDO::PARAM_STR);
Last resort you could do this:
$sql = "INSERT INTO ".POLYGON_TABLE." (user_id, polygon, polygon_type) "
."VALUES (:userId, PolygonFromText('POLYGON(". $polygon->$getPolygonAsText
.")'),:polygonType)";
But I think you should try the ? params first and see how that goes.
$sql = "INSERT INTO ".POLYGON_TABLE." (user_id, polygon, polygon_type) "
."VALUES (?, PolygonFromText('POLYGON(?)'), ?);";
$data = array($polygon->getUserId(), $polygon->getPolygonAsText(), $polygon->getPolygonType());
$query->execute($data);
Btw, I also think those single quotes around the POLYGON(?) function are dodgy... usually you don't quote a method call do you?

Why doesn't this prepare statement work in MYSQLI?

I created this code:
$statement = $db->prepare("SELECT * FROM phptech_contact");
$statement->execute();
$result = $statement->result_metadata();
$object = $result->fetch_object();
print_r( $object );
When I run it, it doesn't work. Can anybody tell me why it doesn't work?
I have 20 rows in this table so data should be returned.
From http://ch.php.net/manual/en/mysqli-stmt.result-metadata.php
Note: The result set returned by mysqli_stmt_result_metadata() contains only metadata. It does not contain any row results. The rows are obtained by using the statement handle with mysqli_stmt_fetch().
As long as you don't need this meta data you don't need to call this method.
$statement = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$statement->execute();
$stmt->bind_result($fld1, $fld2);
while ($stmt->fetch()) {
echo "$fld1 and $fld2<br />";
}
But I really dislike the mysqli extension. PDO is much cooler ... ;-)
$db = new PDO('...');
$stmt = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$stmt->execute();
while ($obj = $stmt->fetchObject()) {
// ...
}
or
$objs = stmt->fetchAll(PDO::FETCH_OBJ);
if you're trying to get the rows from the database, the function you need is mysqli_stmt::fetch(), not mysqli_stmt::fetch_metadata()
You're also missing a few steps. When using prepared statements, you must specify the fields you would like to return instead of using the star wildcard, and then use mysqli_stmt::bind_result() to specify which variables the database fields should be placed in.
If you're more familiar with the original MySQL extension, prepared statements have a different process to use. If your select statement has a parameter (eg., "WHERE value=?") prepared statements are definitely recommended, but for your simple query, mysqli:query() would be sufficient, and not very different from the process of mysql_query()
I believe the problem is that mysqli_stmt::result_metadata() returns a mysqli_result object without any of the actual results — it only holds metadata.
So what you want to do is use $result = $statement->bind_result(...) and then call $result->fetch() repeatedly to get the results.
One of the comments under the bind-result() article shows how to do this for a query like yours, where you don't necessarily know all of the columns being returned.

Categories