Copyright 1997-2012 the PHP Documentation Group.
The mysqlnd user handler plugin can be understood as a client-side
proxy for all PHP MySQL extensions
(mysqli,
mysql,
PDO_MYSQL), if they are
compiled to use the
mysqlnd library. The
extensions use the mysqlnd library internally,
at the C level, to communicate with the MySQL server.
PECL/mysqlnd_uh allows it to hook many mysqlnd
calls. Therefore, most activities of the PHP MySQL extensions can
be monitored.
Because monitoring happens at the level of the library, at a layer below the application, it is possible to monitor applications without changing them.
On the C level, the mysqlnd library is
structured in modules or classes. The extension hooks almost all
methods of the mysqlnd internal
connection class and exposes them through the
user space class MysqlndUhConnection. Some
few methods of the mysqlnd internal statement
class are made available to the PHP user with the class
MysqlndUhPreparedStatement. By subclassing
the classes MysqlndUhConnection and
MysqlndUhPreparedStatement users get access
to mysqlnd internal function calls.
The internal mysqlnd function calls are not
designed to be exposed to the PHP user. Manipulating their
activities may cause PHP to crash or leak memory. Often, this is
not considered a bug. Please, keep in mind that you are
accessing C library functions through PHP which are expected to
take certain actions, which you may not be able to emulate in
user space. Therefore, it is strongly recommended to always call
the parent method implementation when subclassing
MysqlndUhConnection or
MysqlndUhPreparedStatement. To prevent
the worst case, the extension performs some sanity checks.
Please, see also the
Mysqlnd_uh
Configure Options.
Copyright 1997-2012 the PHP Documentation Group.
The plugin is implemented as a PHP extension. See the installation instructions to install the PECL/mysqlnd_uh extension. Then, load the extension into PHP and activate the plugin in the PHP configuration file using the PHP configuration directive named mysqlnd_uh.enable. The below example shows the default settings of the extension.
Copyright 1997-2012 the PHP Documentation Group.
This describes the background and inner workings of the mysqlnd_uh extension.
Two classes are provided by the extension:
MysqlndUhConnection and
MysqlndUhPreparedStatement.
MysqlndUhConnection lets you access
almost all methods of the mysqlnd internal
connection class. The latter exposes some
selected methods of the mysqlnd internal
statement class. For example,
MysqlndUhConnection::connect
maps to the mysqlnd library C function
mysqlnd_conn__connect.
As a mysqlnd plugin, the PECL/mysqlnd_uh extension replaces
mysqlnd library C functions with its own
functions. Whenever a PHP MySQL extension compiled to use
mysqlnd calls a mysqlnd function, the
functions installed by the plugin are executed instead of the
original mysqlnd ones. For example,
mysqli_connect
invokes mysqlnd_conn__connect, so the connect
function installed by PECL/mysqlnd_uh will be called. The
functions installed by PECL/mysqlnd_uh are the methods of the
built-in classes.
The built-in PHP classes and their methods do nothing but call
their mysqlnd C library counterparts, to
behave exactly like the original mysqlnd
function they replace. The code below illustrates in pseudo-code
what the extension does.
Example 21.330. Pseudo-code: what a built-in class does
class MysqlndUhConnection {
public function connect(($conn, $host, $user, $passwd, $db, $port, $socket, $mysql_flags) {
MYSQLND* c_mysqlnd_connection = convert_from_php_to_c($conn);
...
return call_c_function(mysqlnd_conn__connect(c_mysqlnd_connection, ...));
}
}
The build-in classes behave like a transparent proxy. It is
possible for you to replace the proxy with your own. This is
done by subclassing MysqlndUhConnection
or MysqlndUhPreparedStatement to extend
the functionality of the proxy, followed by registering a new
proxy object. Proxy objects are installed by
mysqlnd_uh_set_connection_proxy
and
mysqlnd_uh_set_statement_proxy.
Example 21.331. Installing a proxy
<?php
class proxy extends MysqlndUhConnection {
public function connect($res, $host, $user, $passwd, $db, $port, $socket, $mysql_flags) {
printf("%s(%s)\n", __METHOD__, var_export(func_get_args(), true));
$ret = parent::connect($res, $host, $user, $passwd, $db, $port, $socket, $mysql_flags);
printf("%s returns %s\n", __METHOD__, var_export($ret, true));
return $ret;
}
}
mysqlnd_uh_set_connection_proxy(new proxy());
$mysqli = new mysqli("localhost", "root", "", "test");
?>
The above example will output:
proxy::connect(array ( 0 => NULL, 1 => 'localhost', 2 => 'root', 3 => '', 4 => 'test', 5 => 3306, 6 => NULL, 7 => 131072, )) proxy::connect returns true
Copyright 1997-2012 the PHP Documentation Group.
The extension provides two built-in classes:
MysqlndUhConnection and
MysqlndUhPreparedStatement. The classes
are used for hooking mysqlnd library calls.
Their methods correspond to mysqlnd internal
functions. By default they act like a transparent proxy and do
nothing but call their mysqlnd counterparts.
By subclassing the classes you can install your own proxy to
monitor mysqlnd.
See also the How it works guide to learn about the inner workings of this extension.
Connection proxies are objects of the type
MysqlndUhConnection. Connection proxy
objects are installed by
mysqlnd_uh_set_connection_proxy.
If you install the built-in class
MysqlndUhConnection as a proxy, nothing
happens. It behaves like a transparent proxy.
Example 21.332. Proxy registration, mysqlnd_uh.enable=1
<?php
mysqlnd_uh_set_connection_proxy(new MysqlndUhConnection());
$mysqli = new mysqli("localhost", "root", "", "test");
?>
The PHP_INI_SYSTEM configuration setting
mysqlnd_uh.enable
controls whether a proxy may be set. If disabled, the extension
will throw errors of type E_WARNING
Example 21.333. Proxy installation disabled
mysqlnd_uh.enable=0
<?php
mysqlnd_uh_set_connection_proxy(new MysqlndUhConnection());
$mysqli = new mysqli("localhost", "root", "", "test");
?>
The above example will output:
PHP Warning: MysqlndUhConnection::__construct(): (Mysqlnd User Handler) The plugin has been disabled by setting the configuration parameter mysqlnd_uh.enabled = false. You must not use any of the base classes in %s on line %d PHP Warning: mysqlnd_uh_set_connection_proxy(): (Mysqlnd User Handler) The plugin has been disabled by setting the configuration parameter mysqlnd_uh.enable = false. The proxy has not been installed in %s on line %d
To monitor mysqlnd, you have to write your
own proxy object subclassing
MysqlndUhConnection. Please, see the
function reference for a the list of methods that can be
subclassed. Alternatively, you can use reflection to inspect the
built-in MysqlndUhConnection.
Create a new class proxy. Derive it from the
built-in class MysqlndUhConnection.
Replace the
MysqlndUhConnection::connect.
method. Print out the host parameter value passed to the method.
Make sure that you call the parent implementation of the
connect method. Failing to do so may give
unexpected and undesired results, including memory leaks and
crashes.
Register your proxy and open three connections using the PHP
MySQL extensions
mysqli,
mysql,
PDO_MYSQL. If the
extensions have been compiled to use the
mysqlnd library, the
proxy::connect method will be called three
times, once for each connection opened.
Example 21.334. Connection proxy
<?php
class proxy extends MysqlndUhConnection {
public function connect($res, $host, $user, $passwd, $db, $port, $socket, $mysql_flags) {
printf("Connection opened to '%s'\n", $host);
/* Always call the parent implementation! */
return parent::connect($res, $host, $user, $passwd, $db, $port, $socket, $mysql_flags);
}
}
mysqlnd_uh_set_connection_proxy(new proxy());
$mysqli = new mysqli("localhost", "root", "", "test");
$mysql = mysql_connect("localhost", "root", "");
$pdo = new PDO("mysql:host=localhost;dbname=test", "root", "");
?>
The above example will output:
Connection opened to 'localhost' Connection opened to 'localhost' Connection opened to 'localhost'
The use of prepared statement proxies follows the same pattern:
create a proxy object of the type
MysqlndUhPreparedStatement and install
the proxy using
mysqlnd_uh_set_statement_proxy.
Example 21.335. Prepared statement proxy
<?php
class stmt_proxy extends MysqlndUhPreparedStatement {
public function prepare($res, $query) {
printf("%s(%s)\n", __METHOD__, $query);
return parent::prepare($res, $query);
}
}
mysqlnd_uh_set_statement_proxy(new stmt_proxy());
$mysqli = new mysqli("localhost", "root", "", "test");
$stmt = $mysqli->prepare("SELECT 'mysqlnd hacking made easy' AS _msg FROM DUAL");
?>
The above example will output:
stmt_proxy::prepare(SELECT 'mysqlnd hacking made easy' AS _msg FROM DUAL)
Copyright 1997-2012 the PHP Documentation Group.
Basic monitoring of a query statement is easy with
PECL/mysqlnd_uh. Combined with
debug_print_backtrace
it can become a powerful tool, for example, to find the origin
of certain statement. This may be desired when searching for
slow queries but also after database refactoring to find code
still accessing deprecated databases or tables. The latter may
be a complicated matter to do otherwise, especially if the
application uses auto-generated queries.
Example 21.336. Basic Monitoring
<?php
class conn_proxy extends MysqlndUhConnection {
public function query($res, $query) {
debug_print_backtrace();
return parent::query($res, $query);
}
}
class stmt_proxy extends MysqlndUhPreparedStatement {
public function prepare($res, $query) {
debug_print_backtrace();
return parent::prepare($res, $query);
}
}
mysqlnd_uh_set_connection_proxy(new conn_proxy());
mysqlnd_uh_set_statement_proxy(new stmt_proxy());
printf("Proxies installed...\n");
$pdo = new PDO("mysql:host=localhost;dbname=test", "root", "");
var_dump($pdo->query("SELECT 1 AS _one FROM DUAL")->fetchAll(PDO::FETCH_ASSOC));
$mysqli = new mysqli("localhost", "root", "", "test");
$mysqli->prepare("SELECT 1 AS _two FROM DUAL");
?>
The above example will output:
#0 conn_proxy->query(Resource id #19, SELECT 1 AS _one FROM DUAL)
#1 PDO->query(SELECT 1 AS _one FROM DUAL) called at [example.php:19]
array(1) {
[0]=>
array(1) {
["_one"]=>
string(1) "1"
}
}
#0 stmt_proxy->prepare(Resource id #753, SELECT 1 AS _two FROM DUAL)
#1 mysqli->prepare(SELECT 1 AS _two FROM DUAL) called at [example.php:22]
For basic query monitoring you should install a connection and a
prepared statement proxy. The connection proxy should subclass
MysqlndUhConnection::query.
All database queries not using native prepared statements will
call this method. In the example the query
function is invoked by a PDO call. By default,
PDO_MySQL is using prepared statement
emulation.
All native prepared statements are prepared with the
prepare method of mysqlnd
exported through
MysqlndUhPreparedStatement::prepare.
Subclass MysqlndUhPreparedStatement and
overwrite prepare for native prepared
statement monitoring.

User Comments
Add your own comment.