A Comprehensive Guide on mysqli_thread_safe Function in PHP
When it comes to working with MySQL databases in PHP, the mysqli extension provides a variety of functions to perform various operations. One such function is
When working with MySQL databases in PHP through the mysqli extension, a question that comes up often is: "Is mysqli thread-safe, and is there a mysqli_thread_safe() function I can call to check?"
The short answer is that mysqli_thread_safe() is not a function in PHP, and thread safety is not something you query at runtime. It is a property of how your PHP binary was built. This guide explains what "thread-safe" actually means for PHP, why mysqli thread safety is a compile-time decision, when it matters in practice, and how to verify it in your environment.
What "Thread-Safe" Means in PHP
A program is thread-safe when multiple threads can run the same code at the same time without corrupting shared data. In a thread-safe build, the PHP engine guards internal global state (the symbol table, memory manager, extension globals, etc.) so that two threads executing inside the same process cannot trample each other's data.
PHP ships in two flavors, decided when the binary is compiled:
- ZTS (Zend Thread Safety) — also called a Thread Safe (TS) build. The engine adds locking and per-thread copies of global state so PHP can run inside a multi-threaded host process.
- NTS (Non-Thread-Safe) — the engine assumes one request per process and skips that overhead, so it runs faster.
You cannot switch between them at runtime, and there is no mysqli_thread_safe() to toggle or report it. The build type is fixed when ./configure --enable-maintainer-zts (or the platform equivalent) is chosen during compilation.
Why It Is a Compile-Time Setting, Not a Function
People expect a mysqli_thread_safe() function because some C libraries expose a mysql_thread_safe() call. PHP does not surface one, because the answer never changes for a given binary — querying it at runtime would always return the same value. Whether mysqli is safe in threads is inherited directly from the ZTS/NTS choice baked into the PHP build, plus the underlying client library (modern PHP uses mysqlnd, the native driver, which has no separate thread-safety toggle of its own).
So instead of calling a function, you inspect the build.
When Thread Safety Actually Matters
For the vast majority of PHP applications, you should use the NTS build and thread safety is a non-issue:
| Setup | Build to use | Why |
|---|---|---|
| Nginx + PHP-FPM | NTS | Each worker is a single-threaded process; no shared threads. |
Apache with mpr_prefork | NTS | Each request gets its own process. |
| CLI scripts, cron jobs | NTS | One process, one thread. |
Apache with worker / event MPM + mod_php | ZTS | mod_php runs inside Apache's threaded workers. |
ext like parallel / pthreads (legacy) | ZTS | They spawn PHP threads in one process. |
The classic real-world trap is Apache + mod_php on a threaded MPM: if you load a non-thread-safe PHP into a threaded Apache, the server can crash or corrupt data under load. Using PHP-FPM instead of mod_php sidesteps this entirely, which is why FPM + NTS is the standard modern deployment. See the PHP installation guide for how builds are chosen.
Handling mysqli in a Multi-Threaded Context
Even on a correctly built ZTS PHP, the mysqli connection object itself is not meant to be shared between threads. A mysqli link holds buffered results, prepared-statement state, and an open socket — concurrent use from two threads produces commands-out-of-sync errors or garbage results.
The rule is simple: one connection per thread. Open the connection inside the thread that uses it rather than passing a shared handle around.
<?php
// Each worker/thread creates and owns its own connection.
function runWorkerTask(int $workerId): void
{
// New, independent connection for THIS thread.
$db = new mysqli('localhost', 'user', 'password', 'shop');
if ($db->connect_errno) {
// Handle per-thread connection failure locally.
error_log("Worker {$workerId} failed: {$db->connect_error}");
return;
}
$result = $db->query('SELECT COUNT(*) AS total FROM orders');
$row = $result->fetch_assoc();
echo "Worker {$workerId} sees {$row['total']} orders\n";
$db->close(); // Release the connection when the thread is done.
}For the basics of opening and checking a connection, see Connect to MySQL with mysqli and mysqli_connect_errno().
How to Verify Thread Safety in Your Environment
Because there is no runtime function, use one of these to read the build setting.
Using phpinfo()
Create a one-line script and open it in the browser, or run it from the CLI:
<?php
phpinfo();In the output, find the top table and look at the Thread Safety row (it sits near the Zend Engine / build information). It reads either enabled (ZTS) or disabled (NTS).
Command-Line Verification
From a terminal, filter the full configuration dump:
php -i | grep "Thread Safety"This prints one of:
Thread Safety => enabled
Thread Safety => disabledInside Running PHP Code
If you want the value programmatically — for a diagnostics page, for example — read the PHP_ZTS constant instead of looking for a non-existent function:
<?php
// PHP_ZTS is 1 on a Thread Safe (ZTS) build, 0 on a Non-Thread-Safe (NTS) build.
echo PHP_ZTS === 1 ? "Thread-safe (ZTS) build\n" : "Non-thread-safe (NTS) build\n";This is the correct, supported replacement for the imaginary mysqli_thread_safe() call.
Common Mistakes
- Calling
mysqli_thread_safe()— it does not exist and throws anError: Call to undefined function. UsePHP_ZTSorphpinfo()instead. - Sharing one
mysqliconnection across threads — always open a separate connection per thread. - Loading an NTS PHP into a threaded Apache MPM — match the build to the server, or move to PHP-FPM.
- Assuming ZTS is "better" — it is slower and only needed for genuinely threaded hosts; prefer NTS otherwise.
Conclusion
mysqli_thread_safe() is a function that does not exist. Thread safety in PHP is fixed at compile time by the Zend Thread Safety (ZTS) choice, not toggled or reported by a runtime call. Most modern stacks (Nginx/Apache-prefork + PHP-FPM) use the faster NTS build and never need ZTS. When you do need to check, read it with phpinfo(), php -i | grep "Thread Safety", or the PHP_ZTS constant — and always give each thread its own mysqli connection to keep data consistent.