mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-19 10:28:54 +00:00
476 lines
17 KiB
XML
476 lines
17 KiB
XML
![]() |
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<!-- $Revision: 317722 $ -->
|
||
|
<chapter xml:id="mysqlnd-uh.quickstart" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
|
<title>Quickstart and Examples</title>
|
||
|
<para>
|
||
|
The mysqlnd user handler plugin can be understood as a client-side proxy
|
||
|
for all PHP MySQL extensions (<link linkend="ref.mysqli">mysqli</link>,
|
||
|
<link linkend="ref.mysql">mysql</link>,
|
||
|
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>), if they are compiled to
|
||
|
use the <link linkend="book.mysqlnd">mysqlnd</link> library. The
|
||
|
extensions use the <literal>mysqlnd</literal> library internally, at the C
|
||
|
level, to communicate with the MySQL server. PECL/mysqlnd_uh
|
||
|
allows it to hook many <literal>mysqlnd</literal> calls. Therefore,
|
||
|
most activities of the PHP MySQL extensions can be monitored.
|
||
|
</para>
|
||
|
<para>
|
||
|
Because monitoring happens at the level of the library, at a layer below the
|
||
|
application, it is possible to monitor applications without changing them.
|
||
|
</para>
|
||
|
<para>
|
||
|
<table>
|
||
|
<title>PECL/mysqlnd_uh as a client-side proxy</title>
|
||
|
<tgroup cols="1">
|
||
|
<thead>
|
||
|
<row>
|
||
|
<entry align="center">Any <literal>PHP 5.3.3 or later</literal> application using MySQL</entry>
|
||
|
</row>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
<row>
|
||
|
<entry align="center">using any of</entry>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entrytbl cols='3'>
|
||
|
<tbody>
|
||
|
<row>
|
||
|
<entry><link linkend="ref.mysql">mysql</link></entry>
|
||
|
<entry><link linkend="ref.mysqli">mysqli</link></entry>
|
||
|
<entry><link linkend="ref.pdo-mysql">PDO_MySQL</link></entry>
|
||
|
</row>
|
||
|
</tbody>
|
||
|
</entrytbl>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entry align="center">compiled to use</entry>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entry align="center"><link linkend="book.mysqlnd">mysqlnd</link> library</entry>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entry align="center">PECL/mysqlnd_uh</entry>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entry align="center">communicates with</entry>
|
||
|
</row>
|
||
|
<row>
|
||
|
<entry align="center">MySQL Server</entry>
|
||
|
</row>
|
||
|
</tbody>
|
||
|
</tgroup>
|
||
|
</table>
|
||
|
</para>
|
||
|
<para>
|
||
|
On the C level, the <literal>mysqlnd</literal> library is structured in modules
|
||
|
or classes. The extension hooks almost all methods of the <literal>mysqlnd</literal>
|
||
|
internal <literal>connection</literal> class and exposes them through the
|
||
|
user space class <literal>MysqlndUhConnection</literal>. Some few methods of
|
||
|
the mysqlnd internal <literal>statement</literal> class are made available
|
||
|
to the PHP user with the class <literal>MysqlndUhPreparedStatement</literal>.
|
||
|
By subclassing the classes <literal>MysqlndUhConnection</literal> and
|
||
|
<literal>MysqlndUhPreparedStatement</literal> users get access to
|
||
|
<literal>mysqlnd</literal> internal function calls.
|
||
|
</para>
|
||
|
<note>
|
||
|
<para>
|
||
|
None of the <literal>mysqlnd</literal> internal function calls has been designed
|
||
|
to ever by 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 <literal>MysqlndUhConnection</literal>
|
||
|
or <literal>MysqlndUhPreparedStatement</literal>. To prevent the worst
|
||
|
case, the extension performs some sanity checks. Please, see also
|
||
|
<link linkend="mysqlnd-uh.configuration">Mysqlnd_uh &ConfigureOptions;</link>.
|
||
|
</para>
|
||
|
</note>
|
||
|
|
||
|
|
||
|
<section xml:id="mysqlnd-uh.quickstart.configuration">
|
||
|
<title>Setup</title>
|
||
|
<para>
|
||
|
The plugin is implemented as a PHP extension.
|
||
|
Please, follow the
|
||
|
<link linkend="mysqlnd-uh.installation">installation instructions</link> to
|
||
|
install the
|
||
|
<link xlink:href="&url.pecl.package;mysqlnd_ms">PECL/mysqlnd_uh</link> extension.
|
||
|
Then, load the extension into PHP and activate the plugin in the PHP configuration
|
||
|
file using the PHP configuration directive named
|
||
|
<link linkend="ini.mysqlnd-uh.enable">mysqlnd_uh.enable</link>.
|
||
|
The below example shows the default settings of the extension.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Enabling the plugin (php.ini)</title>
|
||
|
<programlisting role="ini">
|
||
|
<![CDATA[
|
||
|
mysqlnd_uh.enable=1
|
||
|
mysqlnd_uh.report_wrong_types=1
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
</para>
|
||
|
</section>
|
||
|
|
||
|
<section xml:id="mysqlnd-uh.quickstart.how_it_works">
|
||
|
<title>How it works</title>
|
||
|
<para>
|
||
|
<note>
|
||
|
<para>
|
||
|
You can skip the following and move on to the examples for now, if you
|
||
|
are not interested in the inner workings. Please, get back to the
|
||
|
background information and read it carefully, if you plan to use
|
||
|
PECL/mysqlnd_uh intensively in the future.
|
||
|
</para>
|
||
|
</note>
|
||
|
</para>
|
||
|
<para>
|
||
|
Two classes are provided by the extension: <literal>MysqlndUhConnection</literal>
|
||
|
and <literal>MysqlndUhPreparedStatement</literal>. The first one lets
|
||
|
you access almost all methods of the <literal>mysqlnd</literal>
|
||
|
internal <literal>connection</literal> class. The latter exposes some, selected
|
||
|
methods of the <literal>mysqlnd</literal> internal <literal>statement</literal> class.
|
||
|
For example, <function>MysqlndUhConnection::connect</function> maps to
|
||
|
the <literal>mysqlnd</literal> library C function
|
||
|
<literal>mysqlnd_conn__connect</literal>.
|
||
|
</para>
|
||
|
<para>
|
||
|
As a mysqlnd plugin, the PECL/mysqlnd_uh extensions replaces <literal>mysqlnd</literal>
|
||
|
library C functions with its own functions. Whenever a
|
||
|
PHP MySQL extension compiled to use <literal>mysqlnd</literal>, calls
|
||
|
a mysqlnd function, the functions installed by the plugin are executed
|
||
|
instead of the original <literal>mysqlnd</literal> ones. If, for example,
|
||
|
<function>mysqli_connect</function> invokes <literal>mysqlnd_conn__connect</literal>,
|
||
|
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.
|
||
|
</para>
|
||
|
<para>
|
||
|
The built-in PHP classes and their methods do nothing but call their
|
||
|
<literal>mysqlnd</literal> C library counterparts, to behave exaclty
|
||
|
like the original <literal>mysqlnd</literal> function they replace.
|
||
|
The code below illustrates in pseudo-code what the extension does.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Pseudo-code: what a built-in class does</title>
|
||
|
<programlisting>
|
||
|
<![CDATA[
|
||
|
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, ...));
|
||
|
}
|
||
|
}
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
</para>
|
||
|
<para>
|
||
|
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
|
||
|
<literal>MysqlndUhConnection</literal> or
|
||
|
<literal>MysqlndUhPreparedStatement</literal> to extend the functionality
|
||
|
of the proxy, followed by registering a new proxy object.
|
||
|
Proxy objects are installed by
|
||
|
<link linkend="function.mysqlnd-uh-set-connection-proxy"><function>mysqlnd_uh_set_connection_proxy</function></link>
|
||
|
and
|
||
|
<link linkend="function.mysqlnd-uh-set-statement-proxy"><function>mysqlnd_uh_set_statement_proxy</function></link>.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Installing a proxy</title>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?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");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
&example.outputs;
|
||
|
<screen>
|
||
|
<![CDATA[
|
||
|
proxy::connect(array (
|
||
|
0 => NULL,
|
||
|
1 => 'localhost',
|
||
|
2 => 'root',
|
||
|
3 => '',
|
||
|
4 => 'test',
|
||
|
5 => 3306,
|
||
|
6 => NULL,
|
||
|
7 => 131072,
|
||
|
))
|
||
|
proxy::connect returns true
|
||
|
]]>
|
||
|
</screen>
|
||
|
</example>
|
||
|
</para>
|
||
|
</section>
|
||
|
|
||
|
<section xml:id="mysqlnd-uh.quickstart.proxy_installation">
|
||
|
<title>Installing a proxy</title>
|
||
|
<para>
|
||
|
The extension provides two built-in classes: <literal>MysqlndUhConnection</literal>
|
||
|
and <literal>MysqlndUhPreparedStatement</literal>. The classes are used
|
||
|
for hooking <literal>mysqlnd</literal> library calls. Their methods correspond
|
||
|
to <literal>mysqlnd</literal> internal functions. By default they act like
|
||
|
a transparent proxy and do nothing but call their <literal>mysqlnd</literal> counterparts.
|
||
|
By subclassing the classes you can install your own proxy to monitor <literal>mysqlnd</literal>.
|
||
|
</para>
|
||
|
<para>
|
||
|
Please, see <link linkend="mysqlnd-uh.quickstart.how_it_works">How it works</link>
|
||
|
for details. However, to get started you do not need to look at the details.
|
||
|
Copy the patterns shown in the quickstart instead.
|
||
|
</para>
|
||
|
<para>
|
||
|
Connections proxies are objects of the type <literal>MysqlndUhConnection</literal>.
|
||
|
Connection proxy objects are installed by
|
||
|
<link linkend="function.mysqlnd-uh-set-connection-proxy"><function>mysqlnd_uh_set_connection_proxy</function></link>.
|
||
|
If you install the built-in class <literal>MysqlndUhConnection</literal>
|
||
|
as a proxy, nothing happens. It behaves like a transparent proxy.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Proxy registration, mysqlnd_uh.enable=1</title>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?php
|
||
|
mysqlnd_uh_set_connection_proxy(new MysqlndUhConnection());
|
||
|
$mysqli = new mysqli("localhost", "root", "", "test");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
</example>
|
||
|
</para>
|
||
|
<para>
|
||
|
The <literal>PHP_INI_SYSTEM</literal> configuration setting
|
||
|
<literal><link linkend="ini.mysqlnd-uh.enable">mysqlnd_uh.enable</link></literal>
|
||
|
setting controls whether a proxy may be set. If disabled, the extension
|
||
|
will throw errors of type <literal>E_WARNING</literal>
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Proxy installation disabled</title>
|
||
|
<programlisting role="ini">
|
||
|
<![CDATA[
|
||
|
mysqlnd_uh.enable=0
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?php
|
||
|
mysqlnd_uh_set_connection_proxy(new MysqlndUhConnection());
|
||
|
$mysqli = new mysqli("localhost", "root", "", "test");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
&example.outputs;
|
||
|
<screen>
|
||
|
<![CDATA[
|
||
|
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
|
||
|
]]>
|
||
|
</screen>
|
||
|
</example>
|
||
|
</para>
|
||
|
<para>
|
||
|
To monitor <literal>mysqlnd</literal> you have to write your own
|
||
|
proxy object subclassing <literal>MysqlndUhConnection</literal>.
|
||
|
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 <literal>MysqlndUhConnection</literal>.
|
||
|
</para>
|
||
|
<para>
|
||
|
Create a new class <literal>proxy</literal>. Derive it from the
|
||
|
built-in class <literal>MysqlndUhConnection</literal>.
|
||
|
Replace the
|
||
|
<link linkend="mysqlnduhconnection.connect"><function>MysqlndUhConnection::connect</function></link>.
|
||
|
method. Print out the host parameter value passed to the method.
|
||
|
Make sure that you call the parent implementation of the <literal>connect</literal>
|
||
|
method. Failing to do so may give unexpected and undesired results, including
|
||
|
memory leaks and crashes.
|
||
|
</para>
|
||
|
<para>
|
||
|
Register your proxy and open three connections using the PHP MySQL
|
||
|
extensions <link linkend="ref.mysqli">mysqli</link>,
|
||
|
<link linkend="ref.mysql">mysql</link>,
|
||
|
<link linkend="ref.pdo-mysql">PDO_MYSQL</link>. If the extensions have been
|
||
|
compiled to use the <literal>mysqlnd</literal> library, the
|
||
|
<literal>proxy::connect</literal> method will be called three times, once
|
||
|
for each connection opened.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Connection proxy</title>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?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", "");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
&example.outputs;
|
||
|
<screen>
|
||
|
<![CDATA[
|
||
|
Connection opened to 'localhost'
|
||
|
Connection opened to 'localhost'
|
||
|
Connection opened to 'localhost'
|
||
|
]]>
|
||
|
</screen>
|
||
|
</example>
|
||
|
</para>
|
||
|
<para>
|
||
|
The use of prepared statement proxies follows the same pattern: create a
|
||
|
proxy object of the type <literal>MysqlndUhPreparedStatement</literal>
|
||
|
and install the proxy using
|
||
|
<link linkend="function.mysqlnd-uh-set-statement-proxy"><function>mysqlnd_uh_set_statement_proxy</function></link>.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Prepared statement proxy</title>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?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");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
&example.outputs;
|
||
|
<screen>
|
||
|
<![CDATA[
|
||
|
stmt_proxy::prepare(SELECT 'mysqlnd hacking made easy' AS _msg FROM DUAL)
|
||
|
]]>
|
||
|
</screen>
|
||
|
</example>
|
||
|
</para>
|
||
|
</section>
|
||
|
|
||
|
<section xml:id="mysqlnd-uh.quickstart.query_monitoring">
|
||
|
<title>Basic query monitoring</title>
|
||
|
<para>
|
||
|
Basic monitoring of a query statement is easy with PECL/mysqlnd_uh.
|
||
|
Combined with <function>debug_print_backtrace</function> 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
|
||
|
refactorings to find code still accessing deprecated databases or
|
||
|
tables. The latter may be a complicated matter to do otherwise, if
|
||
|
the application uses auto-generated queries.
|
||
|
</para>
|
||
|
<para>
|
||
|
<example>
|
||
|
<title>Basic Monitoring</title>
|
||
|
<programlisting role="php">
|
||
|
<![CDATA[
|
||
|
<?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");
|
||
|
?>
|
||
|
]]>
|
||
|
</programlisting>
|
||
|
&example.outputs;
|
||
|
<screen>
|
||
|
<![CDATA[
|
||
|
#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]
|
||
|
]]>
|
||
|
</screen>
|
||
|
</example>
|
||
|
</para>
|
||
|
<para>
|
||
|
For basic query monitoring you should install a connection and a prepared statement
|
||
|
proxy. The connection proxy should subclass <function>MysqlndUhConnection::query</function>.
|
||
|
All database queries not using native prepared statements will call this method.
|
||
|
In the example the <literal>query</literal> function is invoked by a PDO call.
|
||
|
By default, <literal>PDO_MySQL</literal> is using prepared statement emulation.
|
||
|
</para>
|
||
|
<para>
|
||
|
All native prepared statements are prepared with the <literal>prepare</literal>
|
||
|
method of <literal>mysqlnd</literal> exported through
|
||
|
<function>MysqlndUhPreparedStatement::prepare</function>. Subclass
|
||
|
<literal>MysqlndUhPreparedStatement</literal> and overwrite <literal>prepare</literal>
|
||
|
for native prepared statement monitoring.
|
||
|
</para>
|
||
|
</section>
|
||
|
|
||
|
|
||
|
</chapter>
|
||
|
<!-- Keep this comment at the end of the file
|
||
|
Local variables:
|
||
|
mode: sgml
|
||
|
sgml-omittag:t
|
||
|
sgml-shorttag:t
|
||
|
sgml-minimize-attributes:nil
|
||
|
sgml-always-quote-attributes:t
|
||
|
sgml-indent-step:1
|
||
|
sgml-indent-data:t
|
||
|
indent-tabs-mode:nil
|
||
|
sgml-parent-document:nil
|
||
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
|
sgml-exposed-tags:nil
|
||
|
sgml-local-catalogs:nil
|
||
|
sgml-local-ecat-files:nil
|
||
|
End:
|
||
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
|
vim: et tw=78 syn=sgml
|
||
|
vi: ts=1 sw=1
|
||
|
-->
|