mysql_real_escape_string method really slow (nearly 1 second) - php

i do have a Query here (PHP with Zend Framework on a MySQL Database) using one POST Parameter as an argument in an SQL Statement.
So i do have a local XAMPP Installation on my development machine and the runtime of this small Script ist something like 150ms no matter if i pass the argument with or without using mysql_real_escape_string() on that argument before.
Then i do have a virtual server with BitNami-WAMP-Stack installed. When i run the Script there (100% same database content) it takes around 260ms without mysql_real_escape_string, this is not as "fast" (i know that 150ms isn't really fast at all) as my local machine but would be okay. But if i do add only one mysql_real_escape_string() to the argument from the POST variable the whole thing takes 1.2 seconds.
And i further noticed that every call to mysql_real_escape_string makes the script run around 1 second slower on the virtual server. On my local machine this does not have any effect.
How can this be? Is this a MySQL Setup thing or a PHP.ini thing or what? Because i do have the 100% same database and PHP source on both machines i guess it can only be parametrization?
Thanks for any help in advance!
EDIT:
So here is what i do, first connecting the DB (in Bootstrap.php):
$GLOBALS["db"]= new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => "localhost",
'username' => "user",
'password' => "password",
'dbname' => "database"
));
And then later in want to query the database:
global $db;
$query = sprintf("SELECT * FROM table WHERE id = '%s'", mysql_real_escape_string($id) );
$db->query("SET NAMES 'utf8'");
$db->fetchAll($query);
I just made another test: when i add this simple test-line to my code, it makes the script ~600ms slower on the virtual machine:
mysql_real_escape_string("TEST");

use prepared statements to do this:
http://framework.zend.com/manual/en/zend.db.statement.html

The long-term solution must be switching to prepared statements, that is right. An equivalent to mysql_real_escape_string for PDO connections seems to be PDO::quote:
http://php.net/manual/de/pdo.quote.php
Any disadvantages compared to a mysql_real_escape_string based solution?

I had the same delay on first call mysql_real_escape_string method, solution in my case was information from manual: http://php.net/manual/en/function.mysql-real-escape-string.php
"A MySQL connection is required before using mysql_real_escape_string() otherwise an error of level E_WARNING is generated, and FALSE is returned. If link_identifier isn't defined, the last MySQL connection is used."
The only thing missig was information about delay if connection is not estabilished.
In your case I suppose that if you are using PDO for connection then you should use PDO::quote. If you are using mysql_pconnect/mysql_connect then you can use mysql_real_escape_string without delay.

Related

Php + mysql - connection dies consistantly after 66,946 chars of query input

I've been trying to debug a randomly dropping mysql connection in my php project.
Things I've discovered so far:
Once the input over a single mysql connection (total chars used in queries over a single mysql connection) passes 66,946 chars, php just locks up indefinitely on the query that caused it to go over.
If I reconnect the mysql server occasionally rather than re-using the existing connection for the whole time, the mysql connection won't drop as long as I don't go over 66946 chars in the input for any single mysql connection.
It doesn't matter whether it's a single query, or a bunch of little queries. As soon as the "66947" threshold is passed on a single mysql connection, php hangs indefinitely.
It's not a memory issue. Php is only taking 10Mb of memory at most, and it has a max memory of 512Mb.
The mysql connection is remote, if that matters.
This exact code works on my local dev environment (with any length of query, same remote connection), but not on the production server with queries adding up to over 66,946 chars in length
The php version & config files for my dev environment and the live environment are identical, and both are running Ubuntu (well, locally it's technically WSL)
Switching between mysqli and PDO doesn't make a significant difference (just a different number of input chars before it crashes, less chars with PDO than mysqli)
(update): I also tried this script on another similar ubuntu host with the same version of PHP, using the same mysql host. It worked flawlessly there... so I'm really lost as to what the issue could be.
I've narrowed it down to this minimal reproduction case:
<?php
if ( ! empty($argv[1])) {
$count = intval($argv[1]);
}
if (empty($count) || $count < 34) {
$count = 34;
}
$longText = str_repeat("a", $count - 34);
$query = "select * from abcdef limit 1/* {$longText} */"; // where "abcdef" is my table name
$mysqliConnection = mysqli_connect("my_host", "my_username", "my_password", "my_database");
echo "query length: " . strlen($query);
$result = mysqli_query($mysqliConnection, $query);
echo "\n\nSuccess!\n\n";
providing the argument 66946 returns "success!" instantly, and 66947 just makes the production server hang indefinitely (but works fine in my local box!).
Why is there some mysterious input limit on my php mysql connections? How can I stop it (whatever "it" is) from limiting my php mysql connection input length?
You're probably looking for max_allowed_packet. It's defined in my.cnf.
Use SHOW VARIABLES LIKE 'max_allowed_packet'; to check it.
It turns out there was a misconfiguration in the routing table for the server. Fixing the routing table solved the issue.
Took many hours to come to this conclusion, everything was pointing to a php fpm or mysql setting being misconfigured, I didn't even think it could be a routing issue. Hopefully this helps someone in the future.

PHP + MS SQL Server: nested stored procedures acting differently than when running directly on the database

This is unfortunately going to be a bit vague due that I can't share my company's code so I apologize in advance.
I'm seeing a discrepancy in the way a stored procedure (that calls other stored procedures) stores employees in our MS SQL Server database. When run directly on the database (using my PHPStorm console) when attempting to add any number of employees it adds them all correctly. However, when run through PHP, only a random number are actually inserted. My PHP setup looks similar to:
$con = sqlsrv_connect($host . ',' . $port, [
"Database" => $database,
"UID" => $user,
"PWD" => $password,
"ReturnDatesAsStrings" => true,
"CharacterSet" => "UTF-8"
]);
$sql = "EXEC EmployeeAdd '[{ json data here }]'"
$stmt = sqlsrv_query($con, $sql);
I also tried the PDO library and saw the same results.
Has anyone had trouble running nested stored procedures using the sqlsrv or PDO libraries in PHP? If so, what did you do to resolve the issue? Thanks in advance.
This is fun! We had a handful of print statements that caused the issue. Removing them fixed the issue and I was able to import the employees as expected. Thanks for your help #Zhorov.
More information: by default, PHP sets WarningsReturnAsErrors to on. Because print statements generate a warning of 01000 with error code #0, PHP treats this as an error and immediately halts execution. To fix this without removing print statements, you can either:
In a PHP script, set
sqlsrv_configure("WarningsReturnAsErrors", 0);
Update your php.ini to include
sqlsrv.WarningsReturnAsErrors = 0

Get DB Instance

What is the correct way to execute this code in Yii2. I get oci_new_cursor() expects parameter 1 to be resource, object given.
// get DB instance
$connection = Yii::$app->db;
// set cursor
$refcur = oci_new_cursor($connection);
$q = "BEGIN :RC := REPORT_CARDS.GET_DATA(:I_YEAR); END;";
...
There are some important facts to keep in mind:
Yii uses PDO
PDO and OCI8 are completely different extensions and you cannot mix their use
the PDO driver for Oracle databases (PDO_OCI) has limitations; for instance it seems that cursors aren't really supported and its use is in general not really recommended
see also this example of attempted cursor use with PDO
You can get the PDO instance used by Yii with \Yii::$app->db->pdo, but as per point 2 above, this does not help you with the OCI8 functions you are trying to use in your example. As per point 3, it further does not seem a great idea to commit resources to learning to use PDO just to replace OCI8.
Still, if you want to give the 'Yii way' another shot for those problems that it can still solve, you can try and see how far you get with yii\db\Command. The PDO counterpart for oci_bind_by_name() you refer to in your comment is PDOStatement::bindParam which is basically proxied by yii\db\Command::bindParam. So, a very basic use example for a custom-built SQL query would be something like
// $var1 and $var2 somewhere
$command = \Yii::$app->db->createCommand('some query with :bound :values');
$command->bindParam(':bound', $var1, \PDO::PARAM_STR);
$command->bindParam(':values', $var2, \PDO::PARAM_STR || \PDO::PARAM_INPUT_OUTPUT);
$command->execute();
I can't really test with your specific example, and based on what I've read I can't guarantee success if you try for yourself, but I wish you luck with the exploration. If it falls short of success, then I don't see an easy way around it; you'll just have to use a separate connection of your own, manually initialized with oci_connect or whatever, and then operate as usual through OCI8.

Can I blindly replace all mysql_ functions with mysqli_?

I have used mysql_query() throughout my project; but I've just learned that mysql_ was deprecated as of PHP 5.5, has been removed in PHP 7.
So, I would like to know if I can replace all mysql_ functions with mysqli_ in my project blindly? For example, just replacing mysql_query() with mysqli_query(). Is there any adverse effect?
The short answer is no, the functions are not equivalent.
The good news is there is a converter tool that will help you if you've got a lot of calls/projects to change. This will allow your scripts to work right away.
https://github.com/philip/MySQLConverterTool
It's a forked version of the Oracle original version, and it's kosher.
That said, it's not too difficult to update your code, and you might want to migrate to an object orientated methodology anyway ...
1) The Connection
For all intents and purposes, you need a new connection function that saves the connection as a PHP variable, for example;
$mysqli = new mysqli($host, $username, $password, $database);
Notice I've saved the connection to $mysqli. You can save to $db or whatever you like, but you should use this throughout your code to reference the connection.
Remember to enable error reporting for mysqli before opening the connection;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
2) The Query
Note: You should protect against SQL injection with prepared statements, which are available in MySQLi. Take a look at How can I prevent SQL injection in PHP?, but I'm just going to cover the basics here.
You now have to include the connection as an argument in your query, and other mysqli_ functions. In procedural code it's the first argument, in OO you write it like a class method.
Procedural:
$result = mysqli_query($mysqli, $sql);
OO:
$result = $mysqli->query($sql);
3) Fetch Result
The fetching of the result is similar to the old mysql_ function in procedural;
while ($row = mysqli_fetch_assoc($result))
but as $result is now an object in mysqli, you can use the object function call;
while ($row = $result->fetch_assoc())
4) Close Connection
So as before, you need to include the connection in the close function; as an argument in procedural;
mysqli_close($mysqli);
and as the object that you run the function on in OO;
$mysqli->close();
I would be here forever if I went through them all, but you get the idea. Take a look at the documentation for more information. Don't forget to convert any connection close, result release, or error and row counting functions you have.
The basic rule of thumb is for functions that use the database connection, you need to include it in the function now (either as the first argument in procedural, or the object you use to call the function in OO), or for a result set you can just change the function to mysqli_ or use the result set as the object.
If you cannot convert all calls to the mysqli functions on a old project, you could install and include the library php7-mysql-shim.
It will try to create a transparent replacement for mysql on PHP 7 using mysqli.
Obviously the performance is slower, but it's a solution to get around the problem in a couple of minutes.
You may safely include the library in projects working with PHP 5.6 (it will be ignored).
if (defined('PHP_VERSION_ID') && (PHP_VERSION_ID >= 50600)) { require_once "mysql-shim.php"; }
You can't. some of the functions of mysql and mysqli require different parameters. So you should know which will use the same parameters.

What is the PDO equivalent of function mysql_real_escape_string?

I am modifying my code from using mysql_* to PDO. In my code I had mysql_real_escape_string(). What is the equivalent of this in PDO?
Well No, there is none!
Technically there is PDO::quote() but it is rarely ever used and is not the equivalent of mysql_real_escape_string()
That's right! If you are already using PDO the proper way as documented using prepared statements, then it will protect you from MySQL injection.
# Example:
Below is an example of a safe database query using prepared statements (pdo)
try {
// first connect to database with the PDO object.
$db = new \PDO("mysql:host=localhost;dbname=xxx;charset=utf8", "xxx", "xxx", [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
} catch(\PDOException $e){
// if connection fails, show PDO error.
echo "Error connecting to mysql: " . $e->getMessage();
}
And, now assuming the connection is established, you can execute your query like this.
if($_POST && isset($_POST['color'])){
// preparing a statement
$stmt = $db->prepare("SELECT id, name, color FROM Cars WHERE color = ?");
// execute/run the statement.
$stmt->execute(array($_POST['color']));
// fetch the result.
$cars = $stmt->fetchAll(\PDO::FETCH_ASSOC);
var_dump($cars);
}
Now, as you can probably tell, I haven't used anything to escape/sanitize the value of $_POST["color"]. And this code is secure from myql-injection thanks to PDO and the power of prepared statements.
It is worth noting that you should pass a charset=utf8 as attribute, in your DSN as seen above, for security reasons, and always enable
PDO to show errors in the form of exceptions.
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
so errors from you database queries won't reveal sensitive data like your directory structure, database username etc.
Last but not least, there are moments when you should not trust PDO 100%, and will be bound to take some extra measures to prevent sql injection, one of those cases is, if you are using an outdated versions of mysql [ mysql =< 5.3.6 ] as described in this answer
But, using prepared statements as shown above will always be safer, than using any of the functions that start with mysql_
Good reads
PDO Tutorial for MySQL Developers
There is none*! The object of PDO is that you don’t have to escape anything; you just send it as data. For example:
$query = $link->prepare('SELECT * FROM users WHERE username = :name LIMIT 1;');
$query->execute([':name' => $username]); # No need to escape it!
As opposed to:
$safe_username = mysql_real_escape_string($username);
mysql_query("SELECT * FROM users WHERE username = '$safe_username' LIMIT 1;");
* Well, there is one, as Michael Berkowski said! But there are better ways.
$v = '"'.mysql_real_escape_string($v).'"';
is the equivalent of $v = $this->db->quote($v);
be sure you have a PDO instance in $this->db so you can call the pdo method quote()
There is no need of mysql_real_escape_string in PDO.
PDO itself adjust special character in mysql query ,you only need to pass anonymous parameter and bind it run time.like this
Suppose you have user table with attribute name,email and password and you have to insert into this use prepare statement like this
you can pass name as => $name="Rajes'h ";
it should execute there is no need of equivalent of mysql_real_escape_string
$stmt="INSERT into user(name,email,password) VALUES(:name,:email,:password)";
try{
$pstmt=$dbh->prepare($stmt);//$dbh database handler for executing mysql query
$pstmt->bindParam(':name',$name,PDO::PARAM_STR);
$pstmt->bindParam(':email',$email,PDO::PARAM_STR);
$pstmt->bindParam(':password',$password,PDO::PARAM_STR);
$status=$pstmt->execute();
if($status){
//next line of code
}
}catch(PDOException $pdo){
echo $pdo->getMessage();
}
The simplest solution I've found for porting to PDO is the replacement for mysql_real_escape_string() given at https://www.php.net/manual/en/mysqli.real-escape-string.php#121402. This is by no means perfect, but it gets legacy code running with PDO quickly.
#samayo pointed out that PDO::quote() is similar but not equivalent to mysql_real_escape_string(), and I thought it might be preferred to a self-maintained escape function, but because quote() adds quotes around the string it is not a drop in replacement for mysql_real_escape_string(); using it would require more extensive changes.
In response to a lot of people's comments on here, but I can't comment directly yet (not reached 50 points), there ARE ACTUALLY needs to use the $dbh->quote($value) EVEN when using PDO and they are perfectly justifiable reasons...
If you are looping through many records building a "BULK INSERT" command, (I usually restart on 1000 records) due to exploiting InnoDb tables in MySQL/Maria Db. Creating individual insert commands using prepared statements is neat, but highly inefficient when doing bulk tasks!
PDO can't yet deal with dynamic IN(...) structures, so when you are building a list of IN strings from a list of user variables, YOU WILL NEED TO $dbh->quote($value) each value in the list!
So yes, there is a need for $dbh->quote($value) when using PDO and is probably WHY the command is available in the first place.
PS, you still don't need to put quotes around the command, the $dbh->quote($value) command also does that for you.
Out.
If to answer the original question, then this is the PDO equivalent for mysql_real_escape_string:
function my_real_escape_string($value, $connection) {
/*
// this fails on: value="hello'";
return trim ($connection->quote($value), "'");
*/
return substr($connection->quote($value), 1, -1);
}
btw, the mysqli equivalent is:
function my_real_escape_string($value, $connection) {
return mysqli_real_escape_string($connection, $value);
}

Categories