Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm working on a project and I have to retrieve a MySQL data by a php code. I've already put all the informations in the database, put it isn't showing when I compile the php. Can someone help and tell me what is possibly wrong?
<?php
$dbName = "dbname";
$serverName = "xxx.xxx.xxx";
$mySqlUser = "user";
$mySqlSenha = "password";
$mConn = mysqli_connect($serverName, $mySqlUser, $mySqlSenha, $dbName);
if ($mConn) {
echo "Conexão aberta com sucesso";
}
else {
echo "A conexão não foi aberta";
}
$sql = "SELECT ponto_trajeto_latitude, ponto_trajeto_longitude FROM tb_pontos_trajeto";
$result = mysqli_query($mConn, $sql);
$responde = array();
echo "<table border = '3'>";
echo "<tr>";
echo "<th> Latitude </th>";
echo "<th> Longitude </th>";
echo "</tr>";
if($result = mysqli_query($mConn, $sql)) {
if(mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_array($result)) {
$response = array("latitude" => $row[0], "longitude" => $row[1]);
echo "<tr>";
echo "<td>". $row['latitude'] ."</td>";
echo "<td>". $row['longitude'] ."</td>";
echo "</tr>";
}
} else {
echo "<br><br>Não há registros.";
}
} else {
echo "ERRO: Não foi possível executar o $sql" . mysqli_error($mConn);
}
echo "</table>";
echo JSON_encode(array("data" =>$responde));
?>
OKAY, so as you asked, here it is what I'm seeing. And sorry for any little problems, I'm new on it and new on this site. And, I just want to show the latitude and longitude, so I thought that the others informations isn't necessary. What i'm doing is a project that it envolves Android studio as well, but I've been told to do the php thing and test it if it's working before to do something in android.
The data
And the php, "não há registro" means that doesn't have registers, but as you can see by the picture above, it has
The problems in your code:
Problem 1:
It should be:
echo "<td>" . $response['latitude'] . "</td>";
echo "<td>" . $response['longitude'] . "</td>";
or:
echo "<td>" . $row['ponto_trajeto_latitude'] . "</td>";
echo "<td>" . $row['ponto_trajeto_longitude'] . "</td>";
or:
echo "<td>" . $row[0] . "</td>";
echo "<td>" . $row[1] . "</td>";
Problem 2:
It should be $response instead of $responde overall. Or $responde instead of $response. But not both.
A code proposal:
Don't worry, the comments are long, but the actual code is short.
Use the object oriented mysqli.
Always prepare your sql statements to avoid SQL injection.
Apply error reporting to discover eventual errors/exceptions. See this and this.
Feel free to ask anything.
Good luck.
<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'dbname');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // SET IT TO 0 ON A LIVE SERVER!
/**
* Enable internal report functions. This enables the exception handling,
* e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
* (mysqli_sql_exception). They are catched in the try-catch block.
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* See: http://php.net/manual/en/class.mysqli-driver.php
* See: http://php.net/manual/en/mysqli-driver.report-mode.php
* See: http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/**
* Create a new db connection.
*
* #see http://php.net/manual/en/mysqli.construct.php
*/
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);
$sql = 'SELECT
ponto_trajeto_latitude,
ponto_trajeto_longitude
FROM tb_pontos_trajeto';
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Execute the prepared SQL statement.
* When executed any parameter markers which exist will
* automatically be replaced with the appropriate data.
*
* See: http://php.net/manual/en/mysqli-stmt.execute.php
*/
$executed = $statement->execute();
/*
* Get the result set from the prepared statement.
*
* NOTA BENE:
* Available only with mysqlnd ("MySQL Native Driver")! If this
* is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in
* PHP config file (php.ini) and restart web server (I assume Apache) and
* mysql service. Or use the following functions instead:
* mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
*
* See:
* http://php.net/manual/en/mysqli-stmt.get-result.php
* https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = $statement->get_result();
/*
* Fetch data - all at once - and save it into $fetchedData array (associative array).
* It looks like this. Each array item represents a record in db table.
*
* Array
* (
* [0] => Array
* (
* [ponto_trajeto_latitude] => 50.43
* [ponto_trajeto_longitude] => 8.368
* )
*
* [1] => Array
* (
* [ponto_trajeto_latitude] => 48.34
* [ponto_trajeto_longitude] => 8.23476
* )
*
* )
*
* See: http://php.net/manual/en/mysqli-result.fetch-array.php
*/
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);
// ... OR fetch one row at a time, instead of using fetch_all().
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
// $fetchedData[] = $row;
// }
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* See: http://php.net/manual/en/mysqli-result.free.php
*/
$result->close();
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results, it cancels them
* so that the next query can be executed.
*
* See: http://php.net/manual/en/mysqli-stmt.close.php
*/
$statementClosed = $statement->close();
// Just for testing (delete it): Display fetched data.
echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';
echo json_encode($fetchedData);
Code differences:
The main difference:
As you probably know, each input value coming from the user (e.g. browser) is a potential threat to your system. The process of passing such harmful values into the data access layer is named "SQL injection". So they should/must be validated, filtered, sanitized, escaped, (encoded if needed).
mysqli_query can not "secure" the input values passed to the sql statement. So, each value must be treated separately from this point(s) of view, before being passed into the sql statement. Especially the escaping mechanism is important in avoiding eventual harmful effects.
In comparison, by preparing an sql statement using prepare() (followed by bind_param() and execute()), all potentially harmful input values are automatically escaped by the PHP engine, and are therefore transformed in harmless values.
That's the big picture. For more details regarding the SQL injection see the (accepted) answers from here and here, and read How to use mysqli properly, for example.
In your code you didn't pass any input values into the sql statement. E.g: you have, for example, no WHERE clause. So, you could have further used mysqli_query() without any security issues. But, if you would have needed something like ... WHERE id = $pontoTrajetoId ..., then mysqli_query would not have been able to protect your system from an eventually harmful value of the variable $pontoTrajetoId. Instead, prepare() would have done a great job on this part.
The second difference:
The 2nd difference relies in the use of fetch_all() in my code, compared with the need of a while($row = mysqli_fetch_array($result)) {...} loop in your code. If you read my code, under fetch_all() you'll discover the same while loop as an alternative (commented). The difference is just, that fetch_all() fetches all records in an array, at once. On the other hand, fetch_array() reads only one record (at a time).
The third difference:
The difference resides in the "activation" of error reporting - in my code, with two-three lines of code.
By default, the PHP engine triggers an error every time a failure occurs in any PHP function. Please note that there are multiple error types/levels in PHP: notices, warnings, fatal errors, etc. By using
error_reporting(E_ALL);
ini_set('display_errors', 1); // SET IT TO 0 ON A LIVE SERVER!
I am just telling PHP that I need it to display all errors, e.g. of any level, on screen.
Because all mysqli functions are part of PHP, they throw anyway errors (of type warning) on failure. But, by using
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
I am telling PHP to treat and throw them as exceptions. Read this.
Now, here you'll have to be very careful, to NOT display any data from the original error on the screen when you are deploying your website on the live server. That's why I commented like this:
ini_set('display_errors', 1); // SET IT TO 0 ON A LIVE SERVER!
Why? Because many errors are described by a lot of sensitive details (error message, error number, the file and line in which the error was raised, etc), which can provide too much informations regarding your sytem to an unknown user. The proper way to go with this would be to activate the logging of the errors in log files and either display a general, user-friendly error message to the user, or show a user-friendly html page containing a general error message in it - like "Unfortunately an error occurred during your request. Please try again or contact our support team".
The logging and the error/exception page/message display should be handled inside two functions of your choice. Condition is, that the names of the two functions are to be passed as arguments to the following functions: set_error_handler() and set_exception_handler(). If needed, you can involve a third function too. Its name should be then passed to register_shutdown_function(), which "* Registers a callback to be executed after script execution finishes or exit() is called.*".
In order to perfectly understand how you should correctly proceed with the error reporting, read this and this. It's easy to understand and follow.
Bottom line, instead of validating the result of a mysqli_query(), or checking the presence of a connection error by calling mysqli_error() - as you did in your code, I just let the PHP engine do his job: to throw an error/exception by itself and to display it on screen. Again, read the error reporting links that I gave you.
Well, I hope I could guide your next steps and, if you have more questions, just ask. I'll be happy to answer.
$row = mysqli_fetch_array($result, MYSQLI_NUM)
try this in your while loop
Before moving to PDO, I created SQL queries in PHP by concatenating strings. If I got database syntax error, I could just echo the final SQL query string, try it myself on the database, and tweak it until I fixed the error, then put that back into the code.
Prepared PDO statements are faster and better and safer, but one thing bothers me: I never see the final query as it's sent to the database. When I get errors about the syntax in my Apache log or my custom log file (I log errors inside a catch block), I can't see the query that caused them.
Is there a way capture the complete SQL query sent by PDO to the database and log it to a file?
You say this :
I never see the final query as it's
sent to the database
Well, actually, when using prepared statements, there is no such thing as a "final query" :
First, a statement is sent to the DB, and prepared there
The database parses the query, and builds an internal representation of it
And, when you bind variables and execute the statement, only the variables are sent to the database
And the database "injects" the values into its internal representation of the statement
So, to answer your question :
Is there a way capture the complete
SQL query sent by PDO to the database
and log it to a file?
No : as there is no "complete SQL query" anywhere, there is no way to capture it.
The best thing you can do, for debugging purposes, is "re-construct" an "real" SQL query, by injecting the values into the SQL string of the statement.
What I usually do, in this kind of situations, is :
echo the SQL code that corresponds to the statement, with placeholders
and use var_dump (or an equivalent) just after, to display the values of the parameters
This is generally enough to see a possible error, even if you don't have any "real" query that you can execute.
This is not great, when it comes to debugging -- but that's the price of prepared statements and the advantages they bring.
Looking in the database log
Although Pascal MARTIN is correct that PDO doesn't send the complete query to the database all at once, ryeguy's suggestion to use the DB's logging function actually allowed me to see the complete query as assembled and executed by the database.
Here's how:
(These instructions are for MySQL on a Windows machine - your mileage may vary)
In my.ini, under the [mysqld] section, add a log command, like log="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
Restart MySQL.
It will start logging every query in that file.
That file will grow quickly, so be sure to delete it and turn off logging when you're done testing.
Sure you can debug using this mode {{ PDO::ATTR_ERRMODE }}
Just add new line before your query then you will show the debug lines.
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$db->query('SELECT *******');
Probably what you want to do is use debugDumpParams() on the statement handle. You can run that any time after binding values to the prepared query (no need to execute() the statement).
It doesn't build the prepared statement for you, but it will show your parameters.
An old post but perhaps someone will find this useful;
function pdo_sql_debug($sql,$placeholders){
foreach($placeholders as $k => $v){
$sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
}
return $sql;
}
Here's a function to see what the effective SQL will be, adpated from a comment by "Mark" at php.net:
function sql_debug($sql_string, array $params = null) {
if (!empty($params)) {
$indexed = $params == array_values($params);
foreach($params as $k=>$v) {
if (is_object($v)) {
if ($v instanceof \DateTime) $v = $v->format('Y-m-d H:i:s');
else continue;
}
elseif (is_string($v)) $v="'$v'";
elseif ($v === null) $v='NULL';
elseif (is_array($v)) $v = implode(',', $v);
if ($indexed) {
$sql_string = preg_replace('/\?/', $v, $sql_string, 1);
}
else {
if ($k[0] != ':') $k = ':'.$k; //add leading colon if it was left out
$sql_string = str_replace($k,$v,$sql_string);
}
}
}
return $sql_string;
}
No. PDO queries are not prepared on the client side. PDO simply sends the SQL query and the parameters to the database server. The database is what does the substitution (of the ?'s). You have two options:
Use your DB's logging function (but even then it's normally shown as two separate statements (ie, "not final") at least with Postgres)
Output the SQL query and the
paramaters and piece it together
yourself
almost nothing was said about error displaying except check error logs,
but there's a rather helpful functionality:
<?php
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
echo "\PDO::errorInfo():\n";
print_r($dbh->errorInfo());
}
?>
(source link)
it is clear that this code can be modified to be used as exception message
or any other kind of error handling
for example you have this pdo statement :
$query="insert into tblTest (field1, field2, field3)
values (:val1, :val2, :val3)";
$res=$db->prepare($query);
$res->execute(array(
':val1'=>$val1,
':val2'=>$val2,
':val3'=>$val3,
));
now you can get the executed query by defining an array like this :
$assoc=array(
':val1'=>$val1,
':val2'=>$val2,
':val3'=>$val3,
);
$exQuery=str_replace(array_keys($assoc), array_values($assoc), $query);
echo $exQuery;
Searching internet I found this as an acceptable solution. A different class is used instead of PDO and PDO functions are called through magic function calls. I am not sure this creates serious performance problems. But it can be used until a sensible logging feature is added to PDO.
So as per this thread, you can write a wrapper for your PDO connection which can log and throws an exception when you get a error.
Here is simple example:
class LoggedPDOSTatement extends PDOStatement {
function execute ($array) {
parent::execute ($array);
$errors = parent::errorInfo();
if ($errors[0] != '00000'):
throw new Exception ($errors[2]);
endif;
}
}
so you can use that class instead of PDOStatement:
$this->db->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('LoggedPDOStatement', array()));
Here a mentioned PDO decorator implementation:
class LoggedPDOStatement {
function __construct ($stmt) {
$this->stmt = $stmt;
}
function execute ($params = null) {
$result = $this->stmt->execute ($params);
if ($this->stmt->errorCode() != PDO::ERR_NONE):
$errors = $this->stmt->errorInfo();
$this->paint ($errors[2]);
endif;
return $result;
}
function bindValue ($key, $value) {
$this->values[$key] = $value;
return $this->stmt->bindValue ($key, $value);
}
function paint ($message = false) {
echo '<pre>';
echo '<table cellpadding="5px">';
echo '<tr><td colspan="2">Message: ' . $message . '</td></tr>';
echo '<tr><td colspan="2">Query: ' . $this->stmt->queryString . '</td></tr>';
if (count ($this->values) > 0):
foreach ($this->values as $key => $value):
echo '<tr><th align="left" style="background-color: #ccc;">' . $key . '</th><td>' . $value . '</td></tr>';
endforeach;
endif;
echo '</table>';
echo '</pre>';
}
function __call ($method, $params) {
return call_user_func_array (array ($this->stmt, $method), $params);
}
}
To log MySQL in WAMP, you will need to edit the my.ini (e.g. under wamp\bin\mysql\mysql5.6.17\my.ini)
and add to [mysqld]:
general_log = 1
general_log_file="c:\\tmp\\mysql.log"
Here is a function I made to return a SQL query with "resolved" parameters.
function paramToString($query, $parameters) {
if(!empty($parameters)) {
foreach($parameters as $key => $value) {
preg_match('/(\?(?!=))/i', $query, $match, PREG_OFFSET_CAPTURE);
$query = substr_replace($query, $value, $match[0][1], 1);
}
}
return $query;
$query = "SELECT email FROM table WHERE id = ? AND username = ?";
$values = [1, 'Super'];
echo paramToString($query, $values);
Assuming you execute like this
$values = array(1, 'SomeUsername');
$smth->execute($values);
This function DOES NOT add quotes to queries but does the job for me.
I've created a modern Composer-loaded project / repository for exactly this here:
pdo-debug
Find the project's GitHub home here, see a blog post explaining it here. One line to add in your composer.json, and then you can use it like this:
echo debugPDO($sql, $parameters);
$sql is the raw SQL statement, $parameters is an array of your parameters: The key is the placeholder name (":user_id") or the number of the unnamed parameter ("?"), the value is .. well, the value.
The logic behind: This script will simply grad the parameters and replace them into the SQL string provided. Super-simple, but super-effective for 99% of your use-cases. Note: This is just a basic emulation, not a real PDO debugging (as this is not possible as PHP sends raw SQL and parameters to the MySQL server seperated).
A big thanks to bigwebguy and Mike from the StackOverflow thread Getting raw SQL query string from PDO for writing basically the entire main function behind this script. Big up!
How to debug PDO mysql database queries in Ubuntu
TL;DR Log all your queries and tail the mysql log.
These directions are for my install of Ubuntu 14.04. Issue command lsb_release -a to get your version. Your install might be different.
Turn on logging in mysql
Go to your dev server cmd line
Change directories cd /etc/mysql. You should see a file called my.cnf. That’s the file we’re gonna change.
Verify you’re in the right place by typing cat my.cnf | grep general_log. This filters the my.cnf file for you. You should see two entries: #general_log_file = /var/log/mysql/mysql.log && #general_log = 1.
Uncomment those two lines and save via your editor of choice.
Restart mysql: sudo service mysql restart.
You might need to restart your webserver too. (I can’t recall the sequence I used). For my install, that’s nginx: sudo service nginx restart.
Nice work! You’re all set. Now all you have to do is tail the log file so you can see the PDO queries your app makes in real time.
Tail the log to see your queries
Enter this cmd tail -f /var/log/mysql/mysql.log.
Your output will look something like this:
73 Connect xyz#localhost on your_db
73 Query SET NAMES utf8mb4
74 Connect xyz#localhost on your_db
75 Connect xyz#localhost on your_db
74 Quit
75 Prepare SELECT email FROM customer WHERE email=? LIMIT ?
75 Execute SELECT email FROM customer WHERE email='a#b.co' LIMIT 5
75 Close stmt
75 Quit
73 Quit
Any new queries your app makes will automatically pop into view, as long as you continue tailing the log. To exit the tail, hit cmd/ctrl c.
Notes
Careful: this log file can get huge. I’m only running this on my dev server.
Log file getting too big? Truncate it. That means the file stays, but the contents are deleted. truncate --size 0 mysql.log.
Cool that the log file lists the mysql connections. I know one of those is from my legacy mysqli code from which I'm transitioning. The third is from my new PDO connection. However, not sure where the second is coming from. If you know a quick way to find it, let me know.
Credit & thanks
Huge shout out to Nathan Long’s answer above for the inspo to figure this out on Ubuntu. Also to dikirill for his comment on Nathan’s post which lead me to this solution.
Love you stackoverflow!
The problem I had with the solution to catch PDO exemptions for debuging purposes is that it only caught PDO exemptions (duh), but didn't catch syntax errors which were registered as php errors (I'm not sure why this is, but "why" is irrelevant to the solution). All my PDO calls come from a single table model class that I extended for all my interactions with all tables... this complicated things when I was trying to debug code, because the error would register the line of php code where my execute call was called, but didn't tell me where the call was, actually, being made from. I used the following code to solve this problem:
/**
* Executes a line of sql with PDO.
*
* #param string $sql
* #param array $params
*/
class TableModel{
var $_db; //PDO connection
var $_query; //PDO query
function execute($sql, $params) {
//we're saving this as a global, so it's available to the error handler
global $_tm;
//setting these so they're available to the error handler as well
$this->_sql = $sql;
$this->_paramArray = $params;
$this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->_query = $this->_db->prepare($sql);
try {
//set a custom error handler for pdo to catch any php errors
set_error_handler('pdoErrorHandler');
//save the table model object to make it available to the pdoErrorHandler
$_tm = $this;
$this->_query->execute($params);
//now we restore the normal error handler
restore_error_handler();
} catch (Exception $ex) {
pdoErrorHandler();
return false;
}
}
}
So, the above code catches BOTH PDO exceptions AND php syntax errors and treats them the same way. My error handler looks something like this:
function pdoErrorHandler() {
//get all the stuff that we set in the table model
global $_tm;
$sql = $_tm->_sql;
$params = $_tm->_params;
$query = $tm->_query;
$message = 'PDO error: ' . $sql . ' (' . implode(', ', $params) . ") \n";
//get trace info, so we can know where the sql call originated from
ob_start();
debug_backtrace(); //I have a custom method here that parses debug backtrace, but this will work as well
$trace = ob_get_clean();
//log the error in a civilized manner
error_log($message);
if(admin(){
//print error to screen based on your environment, logged in credentials, etc.
print_r($message);
}
}
If anyone has any better ideas on how to get relevant info to my error handler than setting the table model as a global variable, I would be happy to hear it and edit my code.
this code works great for me :
echo str_replace(array_keys($data), array_values($data), $query->queryString);
Don't forget to replace $data and $query by your names
i use this class to debug PDO (with Log4PHP)
<?php
/**
* Extends PDO and logs all queries that are executed and how long
* they take, including queries issued via prepared statements
*/
class LoggedPDO extends PDO
{
public static $log = array();
public function __construct($dsn, $username = null, $password = null, $options = null)
{
parent::__construct($dsn, $username, $password, $options);
}
public function query($query)
{
$result = parent::query($query);
return $result;
}
/**
* #return LoggedPDOStatement
*/
public function prepare($statement, $options = NULL)
{
if (!$options) {
$options = array();
}
return new \LoggedPDOStatement(parent::prepare($statement, $options));
}
}
/**
* PDOStatement decorator that logs when a PDOStatement is
* executed, and the time it took to run
* #see LoggedPDO
*/
class LoggedPDOStatement
{
/**
* The PDOStatement we decorate
*/
private $statement;
protected $_debugValues = null;
public function __construct(PDOStatement $statement)
{
$this->statement = $statement;
}
public function getLogger()
{
return \Logger::getLogger('PDO sql');
}
/**
* When execute is called record the time it takes and
* then log the query
* #return PDO result set
*/
public function execute(array $params = array())
{
$start = microtime(true);
if (empty($params)) {
$result = $this->statement->execute();
} else {
foreach ($params as $key => $value) {
$this->_debugValues[$key] = $value;
}
$result = $this->statement->execute($params);
}
$this->getLogger()->debug($this->_debugQuery());
$time = microtime(true) - $start;
$ar = (int) $this->statement->rowCount();
$this->getLogger()->debug('Affected rows: ' . $ar . ' Query took: ' . round($time * 1000, 3) . ' ms');
return $result;
}
public function bindValue($parameter, $value, $data_type = false)
{
$this->_debugValues[$parameter] = $value;
return $this->statement->bindValue($parameter, $value, $data_type);
}
public function _debugQuery($replaced = true)
{
$q = $this->statement->queryString;
if (!$replaced) {
return $q;
}
return preg_replace_callback('/:([0-9a-z_]+)/i', array($this, '_debugReplace'), $q);
}
protected function _debugReplace($m)
{
$v = $this->_debugValues[$m[0]];
if ($v === null) {
return "NULL";
}
if (!is_numeric($v)) {
$v = str_replace("'", "''", $v);
}
return "'" . $v . "'";
}
/**
* Other than execute pass all other calls to the PDOStatement object
* #param string $function_name
* #param array $parameters arguments
*/
public function __call($function_name, $parameters)
{
return call_user_func_array(array($this->statement, $function_name), $parameters);
}
}
In Debian NGINX environment i did the following.
Goto /etc/mysql/mysql.conf.d edit mysqld.cnf if you find log-error = /var/log/mysql/error.log add the following 2 lines bellow it.
general_log_file = /var/log/mysql/mysql.log
general_log = 1
To see the logs goto /var/log/mysql and tail -f mysql.log
Remember to comment these lines out once you are done with debugging if you are in production environment delete mysql.log as this log file will grow quickly and can be huge.
I'm trying to figure out how to convert my history script from mysql_query() to PDO. I have a form with 4 input fields which you can randomly select. Which means there can be 0, 1, 2, 3, 4 fields selected depending on which info you're trying to get.
I've tried to query db like this:
$q = $db->prepare('SELECT date,
name,
action
FROM history
WHERE name = :name
AND action = :action');
$q->bindParam(':name', $Name, PDO::PARAM_STR, 20);
$q->bindParam(':action', $Action, $PDO::PARAM_STR, 20);
$q->execute();
But this doesn't work if I don't have any fields selected and want the whole history shown.
With mysql_query() I'd just do this:
mysql_query('SELECT date,
name,
action
FROM history
$Name
$Action');
Which means if there's no $Name or $Action they're simply not included in the query.
Should I just copy/paste the old query into $q = $db-query('')? But that kind of defeats the purpose of using PDO.
You could always assign default values to the params which conform to the column names.
That way your query will in the default case end up as where column = column and when there is a value present it will be where column = value.
EDIT:
Of course, my logic was slightly flawed, since bindParam does not work that way. Instead, you should incrementally build your statement according to the params set.
/* Start with the most general case for the sql query.
* The where part always evaluates to true and will thus
* always return all rows and exists only to make appending
* further conditions easier.
*/
$q = 'SELECT date, name, action FROM history WHERE 1';
/* Prepare a params array in any way you wish. A loop might be more
* efficient if it is possible, but since in this example you have
* only 2 variables, it didn't seem necessary
*/
$params = array();
if (! empty($Name)) {
$params['name'] = $Name;
}
if (! empty($Action)) {
$params['action'] = $Action;
}
/* When the params array is populated, complete the sql statement by
* appending the param names joined with ANDs
*/
foreach ($params as $key => $value) {
$q .= sprintf(' AND `%s` = :%s', $key, $key);
}
/* When the query is complete, we can prepare it */
$stmt = $db->prepare($q);
/* Then bind the values to the prepared statement
*/
foreach ($params as $key => $value) {
// Using bindValue because bindParam binds a reference, which is
// only evaluated at the point of execute
$stmt->bindValue(':'.$key, $value);
}
/* Now we're ready to execute */
$stmt->execute();
In this example, the empty check could've been done in the loop where we complete the sql statement, but that would've given you a less general example.
This example also leaves out the type param to bindValue, but that would be easily implemented, e.g. by changing the array value to an object or array having the type as a member, or by duck typing inside the assigning loop.
The query building could in this form easily be put in a function that would work for all your database querying needs, as long as you provide it the initial (general case) query along with the params array.
The Problem
So I'm writing my web based application and it dawns on me "Durr, your stuff is wide open to SQL injection and whatnot! Rewrite db class!"
I'm currently re-writing my $db class and I am having a significant amount of trouble understanding how I'm supposed to implement prepared statements.
Previously...
I used to use something like this:
$db->runQuery("SELECT * FROM someTable WHERE someField = '$var1'");
while ($result = mysql_fetch_array($db->result){
// ... ugh, tedious
}
Invariably, when performing select statements, I'm grabbing an array, and looping through the results.
I understand that...
I should be burnt at the stake for using non-prepared statements in MySQL.
I have to let mysql know what type of parameter each variable is. (Or do I)?
I'd like to...
Be able to pass my query, and values to my new function (let's use select as an example) which would then return a result for me to work with (as an assoc. array of values);
$query = "SELECT * FROM someTable WHERE someField = ? AND anotherField = ?";
$params = array($var1, $var2);
$result = $db->doSelect($query, $params);
// Then do all sorts of neat stuff with $result - huzzah!
I'm having trouble with...
Understanding how I would bring all the information together.
How do I present an array of values and have that mushed together with my prepared statement?
With said mushed statement, how do I run it (execute()?) and have it return an array?
I'm sorry if my question is somewhat roundabout, however I'm frazzled from trying to understand it. If more information is required, please let me know and I'll add it.
Here is what I've written for a Prepare/Execute function set. These are of course part of a larger DB object.
/**
* Prepares a query to be run, storing the data in $this->preparedTokens
* Use the following characters to indicate how the data is to be put into SQL statement
* ? -> escaped and quoted (with single quotes) before inserting
* ^ -> inserted as is
* & -> implodes the array escpaping each value
* # -> implodes the array (no escaping)
*
* #param string $sql The SQL statement to prepare
*
* #return int The key of prepare sql query to be passed to $this->Execute()
*/
public function Prepare($sql) {
$tokens = preg_split('/((?<!\\\)[#&?^])/', $sql, -1, PREG_SPLIT_DELIM_CAPTURE);
// loop through removing any escaped values
foreach ($tokens as $key => $val) {
switch ($val) {
case '?' :
case '&' :
case '#' :
break;
default :
$tokens[$key] = preg_replace('/\\\([#&?^])/', "\\1", $val);
break;
} // switch
} // foreach
$this->preparedTokens[] = $tokens;
end($this->preparedTokens);
return key($this->preparedTokens);
} // function Prepare
/**
* Creates the SQL placing the data in the appropriate places and then runs the sql
*
* #param int $preparedKey The key of the prepared sql
* #param array $data The array of data to put into the query (the count of this array must match that of the prepared query)
*
* #return object false if the $preparedKey does not exist in $this->preparedTokens
* false if count of needed values in sql statement does not equal the number of keys in the data array
* otherwise, the result of $this->Query()
*/
public function Execute($preparedKey, $data) {
if (isset($this->preparedTokens[$preparedKey])) {
$tokens = $this->preparedTokens[$preparedKey];
$query = '';
$dataKey = 0;
$count = 0;
// count the number of tokens we have
$validTokens = array('?', '^', '&', '#');
foreach ($tokens as $val) {
if (in_array($val, $validTokens)) {
++$count;
} // if
} // foreach
// check to ensure we have the same number of tokens as data keys
if ($count != count($data)) {
trigger_error('Query Error: The number of values received in execute does not equal the number of values needed for the query', E_USER_ERROR);
return false;
} // if
// loop through the tokens creating the sql statement
foreach ($tokens as $val) {
switch ($val) {
case '?' :
$query .= "'" . $this->EscapeString($data[$dataKey++]) . "'";
break;
case '^' :
$query .= $data[$dataKey++];
break;
case '&' :
$query .= $this->ImplodeEscape($data[$dataKey++]);
break;
case '#' :
$query .= implode(',', $data[$dataKey++]);
break;
default :
$query .= $val;
break;
} // switch
} // foreach
return $this->Query($query);
} else {
return false;
} // if
} // function Execute
/**
* Runs $this->Prepare() then $this->Execute() for the sql and the data
* Use the following characters to indicate how the data is to be put into SQL statement
* ? -> escaped and quoted (with single quotes) before inserting
* ^ -> inserted as is
* & -> implodes the array escpaping each value
* # -> implodes the array (no escaping)
*
* #param string $sql The SQL statement to prepare
* #param array $data The array of data to put into the query (the count of this array must match that of the prepared query)
*
* #return object returns value from $this->Query() if Execute was successful
* otherwise it'll be false
*/
public function PrepareExecute($sql, $data) {
return $this->Execute($this->Prepare($sql), $data);
} // function PrepareExecute
$this->Query() executes the MySQL statement and then returns different values depending on what the statement is (based on the first 6 characters of the statement, trimmed):
false if failed (use $this->GetError() to get error message)
if successful INSERT, then the insert id
if successful DELETE or UPDATE or REPLACE, then the number of affected rows
if successful SELECT or any other query type, then the Query object
I'm not sure if this is what you are looking for, but it might help.
Forgot to mention this, but most of the ideas came from the Pear::DB class: http://pear.php.net/package/DB
See if you can make use of following.
Let me know if you need a more detailed implementation
call_user_func_array(array($stmt,"bind_result"), $params);