How to change mysql to mysqli?
The ultimate guide to upgrading mysql_*
functions to MySQLi API
The reason for the new mysqli extension was to take advantage of new features found in MySQL systems versions 4.1.3 and newer. When changing your existing code from mysql_*
to mysqli API you should avail of these improvements, otherwise your upgrade efforts could go in vain.
The mysqli extension has a number of benefits, the key enhancements over the mysql extension being:
- Object-oriented interface
- Support for Prepared Statements
- Enhanced debugging capabilities
When upgrading from mysql_*
functions to MySQLi, it is important to take these features into consideration, as well as some changes in the way this API should be used.
1. Object-oriented interface versus procedural functions.
The new mysqli object-oriented interface is a big improvement over the older functions and it can make your code cleaner and less susceptible to typographical errors. There is also the procedural version of this API, but its use is discouraged as it leads to less readable code, which is more prone to errors.
To open new connection to the database with MySQLi you need to create new instance of MySQLi class.
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
Using procedural style it would look like this:
$mysqli = mysqli_connect($host, $user, $password, $dbName);
mysqli_set_charset($mysqli, 'utf8mb4');
Keep in mind that only the first 3 parameters are the same as in mysql_connect
. The same code in the old API would be:
$link = mysql_connect($host, $user, $password);
mysql_select_db($dbName, $link);
mysql_query('SET NAMES utf8');
If your PHP code relied on implicit connection with default parameters defined in php.ini, you now have to open the MySQLi connection passing the parameters in your code, and then provide the connection link to all procedural functions or use the OOP style.
For more information see the article: How to connect properly using mysqli
2. Support for Prepared Statements
This is a big one. MySQL has added support for native prepared statements in MySQL 4.1 (2004). Prepared statements are the best way to prevent SQL injection. It was only logical that support for native prepared statements was added to PHP. Prepared statements should be used whenever data needs to be passed along with the SQL statement (i.e. WHERE
, INSERT
or UPDATE
are the usual use cases).
The old MySQL API had a function to escape the strings used in SQL called mysql_real_escape_string
, but it was never intended for protection against SQL injections and naturally shouldn't be used for the purpose.
The new MySQLi API offers a substitute function mysqli_real_escape_string
for backwards compatibility, which suffers from the same problems as the old one and therefore should not be used unless prepared statements are not available.
The old mysql_* way:
$login = mysql_real_escape_string($_POST['login']);
$result = mysql_query("SELECT * FROM users WHERE user='$login'");
The prepared statement way:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE user=?');
$stmt->bind_param('s', $_POST['login']);
$stmt->execute();
$result = $stmt->get_result();
Prepared statements in MySQLi can look a little off-putting to beginners. If you are starting a new project then deciding to use the more powerful and simpler PDO API might be a good idea.
3. Enhanced debugging capabilities
Some old-school PHP developers are used to checking for SQL errors manually and displaying them directly in the browser as means of debugging. However, such practice turned out to be not only cumbersome, but also a security risk. Thankfully MySQLi has improved error reporting capabilities.
MySQLi is able to report any errors it encounters as PHP exceptions. PHP exceptions will bubble up in the script and if unhandled will terminate it instantly, which means that no statement after the erroneous one will ever be executed. The exception will trigger PHP Fatal error and will behave as any error triggered from PHP core obeying the display_errors
and log_errors
settings. To enable MySQLi exceptions use the line mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)
and insert it right before you open the DB connection.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
If you were used to writing code such as:
$result = mysql_query('SELECT * WHERE 1=1');
if (!$result) {
die('Invalid query: ' . mysql_error());
}
or
$result = mysql_query('SELECT * WHERE 1=1') or die(mysql_error());
you no longer need to die()
in your code.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli($host, $user, $password, $dbName);
$mysqli->set_charset('utf8mb4');
$result = $mysqli->query('SELECT * FROM non_existent_table');
// The following line will never be executed due to the mysqli_sql_exception being thrown above
foreach ($result as $row) {
// ...
}
If for some reason you can't use exceptions, MySQLi has equivalent functions for error retrieval. You can use mysqli_connect_error()
to check for connection errors and mysqli_error($mysqli)
for any other errors. Pay attention to the mandatory argument in mysqli_error($mysqli)
or alternatively stick to OOP style and use $mysqli->error
.
$result = $mysqli->query('SELECT * FROM non_existent_table') or trigger_error($mysqli->error, E_USER_ERROR);
See these posts for more explanation:
mysqli or die, does it have to die?
How to get MySQLi error information in different environments?
4. Other changes
Unfortunately not every function from mysql_*
has its counterpart in MySQLi only with an "i" added in the name and connection link as first parameter. Here is a list of some of them:
mysql_client_encoding()
has been replaced bymysqli_character_set_name($mysqli)
mysql_create_db
has no counterpart. Use prepared statements ormysqli_query
insteadmysql_drop_db
has no counterpart. Use prepared statements ormysqli_query
insteadmysql_db_name
&mysql_list_dbs
support has been dropped in favour of SQL'sSHOW DATABASES
mysql_list_tables
support has been dropped in favour of SQL'sSHOW TABLES FROM dbname
mysql_list_fields
support has been dropped in favour of SQL'sSHOW COLUMNS FROM sometable
mysql_db_query
-> usemysqli_select_db()
then the query or specify the DB name in the querymysql_fetch_field($result, 5)
-> the second parameter (offset) is not present inmysqli_fetch_field
. You can usemysqli_fetch_field_direct
keeping in mind the different results returnedmysql_field_flags
,mysql_field_len
,mysql_field_name
,mysql_field_table
&mysql_field_type
-> has been replaced withmysqli_fetch_field_direct
mysql_list_processes
has been removed. If you need thread ID usemysqli_thread_id
mysql_pconnect
has been replaced withmysqli_connect()
withp:
host prefixmysql_result
-> usemysqli_data_seek()
in conjunction withmysqli_field_seek()
andmysqli_fetch_field()
mysql_tablename
support has been dropped in favour of SQL'sSHOW TABLES
mysql_unbuffered_query
has been removed. See this article for more information Buffered and Unbuffered queries
The first thing to do would probably be to replace every mysql_*
function call with its equivalent mysqli_*
, at least if you are willing to use the procedural API -- which would be the easier way, considering you already have some code based on the MySQL API, which is a procedural one.
To help with that, the MySQLi Extension Function Summary is definitely something that will prove helpful.
For instance:
mysql_connect
will be replaced bymysqli_connect
mysql_error
will be replaced bymysqli_error
and/ormysqli_connect_error
, depending on the contextmysql_query
will be replaced bymysqli_query
- and so on
Note: For some functions, you may need to check the parameters carefully: Maybe there are some differences here and there -- but not that many, I'd say: both mysql and mysqli are based on the same library (libmysql ; at least for PHP <= 5.2)
For instance:
- with mysql, you have to use the
mysql_select_db
once connected, to indicate on which database you want to do your queries - mysqli, on the other side, allows you to specify that database name as the fourth parameter to
mysqli_connect
. - Still, there is also a
mysqli_select_db
function that you can use, if you prefer.
Once you are done with that, try to execute the new version of your script... And check if everything works ; if not... Time for bug hunting ;-)
(I realise this is old, but it still comes up...)
If you do replace mysql_*
with mysqli_*
then bear in mind that a whole load of mysqli_*
functions need the database link to be passed.
E.g.:
mysql_query($query)
becomes
mysqli_query($link, $query)
I.e., lots of checking required.