mirror of
https://github.com/php/doc-pt_br.git
synced 2026-03-23 22:52:12 +01:00
1438 lines
46 KiB
XML
1438 lines
46 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: 9598935f21bc472f22383fb989625f0b22785331 Maintainer: leonardolara Status: ready -->
|
||
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="mysqlnd.plugin">
|
||
<title>API do plugin do Driver Nativo MySQL</title>
|
||
<simpara>
|
||
A API de plugin do Driver Nativo MySQL é um recurso do Driver Nativo
|
||
MySQL, ou <literal>mysqlnd</literal>. Os plugins <literal>Mysqlnd</literal>
|
||
operam na camada entre os aplicativos PHP e o servidor
|
||
MySQL. Isso é comparável ao Proxy MySQL. O Proxy MySQL opera em uma
|
||
camada entre qualquer aplicativo cliente MySQL, por exemplo, um aplicativo
|
||
PHP e o servidor MySQL. Os plugins <literal>Mysqlnd</literal>
|
||
podem realizar tarefas típicas do Proxy MySQL, como balanceamento de carga,
|
||
monitoramento e otimizações de desempenho. Devido à arquitetura e
|
||
localização diferentes, os plugins <literal>mysqlnd</literal> não
|
||
apresentam algumas das desvantagens do Proxy MySQL. Por exemplo, com plugins,
|
||
não há um ponto único de falha, nenhum servidor proxy dedicado para se implantar
|
||
e nenhuma nova linguagem de programação para aprender (Lua).
|
||
</simpara>
|
||
<simpara>
|
||
Um plugin <literal>mysqlnd</literal> pode ser pensado como uma extensão
|
||
do <literal>mysqlnd</literal>. Plugins podem interceptar a maioria das
|
||
funções do <literal>mysqlnd</literal>. As funções <literal>mysqlnd</literal>
|
||
são chamadas pelas extensões PHP MySQL como
|
||
<literal>ext/mysql</literal>, <literal>ext/mysqli</literal> e
|
||
<literal>PDO_MYSQL</literal>. Como resultado, é possível para um
|
||
plugin <literal>mysqlnd</literal> interceptar todas as chamadas feitas para essas
|
||
extensões a partir da aplicação cliente.
|
||
</simpara>
|
||
<simpara>
|
||
Chamadas internas de função <literal>mysqlnd</literal> também podem ser
|
||
interceptadas ou substituídas. Não há restrições na manipulação
|
||
de tabelas de funções internas do <literal>mysqlnd</literal>. É possível
|
||
configurar as coisas para que quando certas funções <literal>mysqlnd</literal>
|
||
sejam chamadas pelas extensões que usam
|
||
<literal>mysqlnd</literal>, a chamada seja direcionada para a função
|
||
apropriada no plugin <literal>mysqlnd</literal>. A capacidade de
|
||
manipular tabelas de funções internas do <literal>mysqlnd</literal> desta
|
||
forma permite máxima flexibilidade para plugins.
|
||
</simpara>
|
||
<simpara>
|
||
Plugins <literal>Mysqlnd</literal> são na verdade extensões PHP, escritas
|
||
em C, que usam a API do plugin <literal>mysqlnd</literal> (que está
|
||
embutida no Driver Nativo MySQL, <literal>mysqlnd</literal>) . Os plugins
|
||
podem ser 100% transparentes para aplicações PHP. Nenhuma alteração
|
||
na aplicação é necessária porque os plugins operam em uma camada diferente. O
|
||
plugin <literal>mysqlnd</literal> pode ser pensado como operando em uma
|
||
camada abaixo do <literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
A lista a seguir representa algumas aplicações possíveis de
|
||
plugins <literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Balanceamento de Carga
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Divisão de leitura/gravação. Um exemplo disso é a extensão PECL/mysqlnd_ms
|
||
(Master Slave). Esta extensão divide consultas de leitura/gravação
|
||
para uma configuração de replicação.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Failover
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Round-Robin, menos carregado
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Monitoramento
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Registro de Consultas
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Análise de Consultas
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Auditoria de Consultas. Um exemplo disso é a extensão PECL/mysqlnd_sip (Proteção
|
||
Contra Injeção SQL). Esta extensão inspeciona consultas
|
||
e executa apenas aquelas permitidas de acordo com um conjunto de regras.
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Desempenho
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Cache. Um exemplo disso é a extensão PECL/mysqlnd_qc (Cache de
|
||
Consultas).
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Redução de velocidade
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Fragmentação. Um exemplo disso é a extensão PECL/mysqlnd_mc (Multi
|
||
Conexão). Esta extensão tentará dividir uma instrução SELECT
|
||
em n partes, usando SELECT ... LIMIT part_1, SELECT
|
||
LIMIT part_n. Ele envia as consultas para servidores MySQL distintos e
|
||
mescla o resultado no cliente.
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
<emphasis role="bold">Plugins Disponíveis do Driver Nativo MySQL</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Existem vários plug-ins do mysqlnd já disponíveis. Esses
|
||
incluem:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_mc</emphasis> - plugin Multi
|
||
Conexão.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_ms</emphasis> - plugin Master
|
||
Slave.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_qc</emphasis> - plugin Query
|
||
Cache.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_pscache</emphasis> - plugin de Cache
|
||
de Manipulador de Instruções Preparadas.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_sip</emphasis> - plugin de Proteção Contra
|
||
Injeção SQL.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<emphasis role="bold">PECL/mysqlnd_uh</emphasis> - plugin de Manipulador
|
||
de Usuário.
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<section xml:id="mysqlnd.plugin.mysql-proxy">
|
||
<title>Uma comparação de plugins mysqlnd com Proxy MySQL</title>
|
||
<simpara>
|
||
Plugins <literal>Mysqlnd</literal> e Proxy MySQL são tecnologias
|
||
diferentes que usam abordagens diferentes. Ambas são ferramentas válidas para
|
||
resolver uma variedade de tarefas comuns, como balanceamento de carga, monitoramento
|
||
e melhorias de desempenho. Uma diferença importante é que o Proxy
|
||
MySQL funciona com todos os clientes MySQL, enquanto
|
||
os plugins <literal>mysqlnd</literal> são específicos para aplicações PHP.
|
||
</simpara>
|
||
<simpara>
|
||
Como uma extensão PHP, um plugin <literal>mysqlnd</literal> é
|
||
instalado no servidor de aplicativos PHP, junto com o restante do PHP.
|
||
O Proxy MySQL pode ser executado no servidor de aplicativos PHP ou pode ser
|
||
instalado em uma máquina dedicada para lidar com vários servidores de aplicações
|
||
PHP.
|
||
</simpara>
|
||
<simpara>
|
||
A implantação do Proxy MySQL no servidor de aplicativos tem duas vantagens:
|
||
</simpara>
|
||
<orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Nenhum ponto único de falha
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Fácil de escalonar horizontalmente (escalonar por cliente)
|
||
</simpara>
|
||
</listitem>
|
||
</orderedlist>
|
||
<simpara>
|
||
O proxy MySQL (e os plugins <literal>mysqlnd</literal>) podem resolver
|
||
facilmente problemas que de outra forma exigiriam alterações em
|
||
aplicações existentes.
|
||
</simpara>
|
||
<simpara>
|
||
No entanto, o Proxy MySQL tem algumas desvantagens:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Proxy MySQL é um novo componente e uma nova tecnologia para dominar e implantar.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
O Proxy MySQL requer conhecimento da linguagem de script Lua.
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
O proxy MySQL pode ser customizado com programação C e Lua. Lua é a
|
||
linguagem de script preferida do Proxy MySQL. Para a maioria dos especialistas em PHP, Lua
|
||
é uma nova linguagem para se aprender. Um plugin <literal>mysqlnd</literal> pode
|
||
ser escrito em C. Também é possível escrever plugins em PHP usando
|
||
<link xlink:href="&url.pecl.package;mysqlnd_uh">PECL/mysqlnd_uh</link>.
|
||
</simpara>
|
||
<simpara>
|
||
O proxy MySQL é executado como um daemon - um processo em segundo plano. O proxy MySQL pode
|
||
recuperar decisões anteriores, pois todo o estado pode ser retido. Entretanto, um
|
||
plugin <literal>mysqlnd</literal> está vinculado ao ciclo de vida baseado em
|
||
solicitações do PHP. O Proxy MySQL também pode compartilhar resultados computados
|
||
únicos entre vários servidores de aplicativos. Um
|
||
plugin <literal>mysqlnd</literal> precisaria armazenar dados em um
|
||
meio persistente para poder fazer isso. Outro daemon precisaria ser
|
||
usado para essa finalidade, como o Memcache. Isso dá ao Proxy MySQL uma
|
||
vantagem neste caso.
|
||
</simpara>
|
||
<simpara>
|
||
O proxy MySQL funciona sobre o protocolo de fio. Com o Proxy MySQL é
|
||
necessário analisar e fazer engenharia reversa do protocolo MySQL Client Server.
|
||
As ações são limitadas àquelas que podem ser alcançadas através da manipulação do
|
||
protocolo de comunicação. Se o protocolo de fio for alterado (o que acontece
|
||
muito raramente), os scripts do proxy MySQL também precisarão ser alterados.
|
||
</simpara>
|
||
<simpara>
|
||
Os plugins <literal>Mysqlnd</literal> funcionam sobre a API C, que
|
||
espelha o cliente <literal>libmysqlclient</literal>.
|
||
Esta API C é basicamente um encapsulador em torno do protocolo clienten-servidor
|
||
MySQL, ou protocolo de fio, como às vezes é chamado. Pode-se
|
||
interceptar todas as chamadas da API C. O PHP faz uso da API C, portanto
|
||
pode-se conectar todas as chamadas PHP, sem a necessidade de programar
|
||
no nível do protocolo de fio.
|
||
</simpara>
|
||
<simpara>
|
||
<literal>Mysqlnd</literal> implementa o protocolo de fio. Os plugins podem,
|
||
portanto, analisar, fazer engenharia reversa, manipular e até mesmo substituir o
|
||
protocolo de comunicação. No entanto, isso geralmente não é necessário.
|
||
</simpara>
|
||
<simpara>
|
||
Como os plugins permitem criar implementações que utilizam dois níveis (API
|
||
C e protocolo de fio), eles têm maior flexibilidade que o Proxy
|
||
MySQL. Se um plugin <literal>mysqlnd</literal> for implementado usando
|
||
a API C, quaisquer alterações subsequentes no protocolo de fio não requerem
|
||
alterações no próprio plugin.
|
||
</simpara>
|
||
</section>
|
||
<section xml:id="mysqlnd.plugin.obtaining">
|
||
<title>Obtendo a API do plugin mysqlnd</title>
|
||
<simpara>
|
||
A API do plugin <literal>mysqlnd</literal> é simplesmente parte da extensão
|
||
PHP do Driver Nativo MySQL, <literal>ext/mysqlnd</literal>.
|
||
O desenvolvimento da API do plugin <literal>mysqlnd</literal> começou em
|
||
dezembro de 2009. Ele é desenvolvido como parte do repositório de código-fonte PHP
|
||
e, como tal, está disponível ao público via Git ou por meio de
|
||
downloads de snapshots de código-fonte.
|
||
</simpara>
|
||
<simpara>
|
||
Os desenvolvedores do plugin podem determinar a versão do <literal>mysqlnd</literal>
|
||
acessando <literal>MYSQLND_VERSION</literal>, que é
|
||
uma string no formato <quote>mysqlnd 8.3.17</quote> ou por meio de
|
||
<literal>MYSQLND_VERSION_ID</literal>, que é um número inteiro como
|
||
50007. Os desenvolvedores podem calcular o número da versão da seguinte maneira:
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.version-id">
|
||
<title>Tabela de cálculo de MYSQLND_VERSION_ID</title>
|
||
<tgroup cols="2">
|
||
<thead>
|
||
<row>
|
||
<entry>Versão (parte)</entry>
|
||
<entry>Exemplo</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>Maior*10000</entry>
|
||
<entry>5*10000 = 50000</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Menor*100</entry>
|
||
<entry>0*100 = 0</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Correção</entry>
|
||
<entry>7 = 7</entry>
|
||
</row>
|
||
<row>
|
||
<entry>MYSQLND_VERSION_ID</entry>
|
||
<entry>50007</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
Durante o desenvolvimento, os desenvolvedores devem consultar o
|
||
número da versão do <literal>mysqlnd</literal> para compatibilidade e
|
||
testes de versão, já que diversas iterações do <literal>mysqlnd</literal>
|
||
podem ocorrer durante o tempo de vida de uma ramificação de desenvolvimento PHP com um
|
||
único número de versão PHP.
|
||
</simpara>
|
||
</section>
|
||
<section xml:id="mysqlnd.plugin.architecture">
|
||
<title>Arquitetura do Plugin do Driver Nativo MySQL</title>
|
||
<simpara>
|
||
Esta seção fornece uma visão geral da arquitetura do plugin
|
||
<literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Visão Geral do Driver Nativo MySQL</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Antes de desenvolver plugins <literal>mysqlnd</literal>, é útil
|
||
saber um pouco de como o próprio <literal>mysqlnd</literal> é organizado.
|
||
<literal>Mysqlnd</literal> consiste nos seguintes módulos:
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.orgchart">
|
||
<title>O organograma mysqlnd, por módulo</title>
|
||
<tgroup cols="2">
|
||
<thead>
|
||
<row>
|
||
<entry>Estatísticas de Módulos</entry>
|
||
<entry>mysqlnd_statistics.c</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>Conexão</entry>
|
||
<entry>mysqlnd.c</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Conjunto de resultados</entry>
|
||
<entry>mysqlnd_result.c</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Metadados do conjunto de resultados</entry>
|
||
<entry>mysqlnd_result_meta.c</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Declaração</entry>
|
||
<entry>mysqlnd_ps.c</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Rede</entry>
|
||
<entry>mysqlnd_net.c</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Protocolo de fio</entry>
|
||
<entry>mysqlnd_wireprotocol.c</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
<emphasis role="bold">Paradigma de C Orientado a Objetos</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
No nível do código, <literal>mysqlnd</literal> usa um padrão C para
|
||
implementar orientação a objetos.
|
||
</simpara>
|
||
<simpara>
|
||
Em C você usa uma <literal>struct</literal> para representar um objeto.
|
||
Os membros da estrutura representam propriedades do objeto. Membros da estrutura
|
||
que apontam para funções representam métodos.
|
||
</simpara>
|
||
<simpara>
|
||
Ao contrário de outras linguagens como C++ ou Java, não existem regras
|
||
fixas sobre herança no paradigma do C orientado a objetos. No entanto,
|
||
existem algumas convenções que precisam ser seguidas e que serão
|
||
discutidas posteriormente.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">O Ciclo de Vida do PHP</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Ao considerar o ciclo de vida do PHP, existem dois ciclos básicos:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Ciclo de inicialização e desligamento do mecanismo PHP
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Ciclo de requisição
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
Quando o mecanismo PHP for iniciado ele chamará a função de inicialização do módulo
|
||
(MINIT) de cada extensão registrada. Isso permite que cada
|
||
módulo configure variáveis e aloque recursos que existirão durante
|
||
a vida útil do processo do mecanismo PHP. Quando o mecanismo PHP for desligado,
|
||
ele chamará a função de desligamento do módulo (MSHUTDOWN) de cada
|
||
extensão.
|
||
</simpara>
|
||
<simpara>
|
||
Durante a vida útil do mecanismo PHP, ele receberá diversas
|
||
solicitações. Cada solicitação constitui outro ciclo de vida. Em cada
|
||
solicitação, o mecanismo PHP chamará a função de inicialização de solicitação
|
||
de cada extensão. A extensão pode executar qualquer configuração de variável e
|
||
alocação de recursos necessária para o processamento de solicitações. À medida que o
|
||
ciclo de solicitação termina, o mecanismo chama a função de desligamento de solicitação (RSHUTDOWN)
|
||
de cada extensão para que ela possa realizar qualquer limpeza necessária.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Como um plugin funciona</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Um plugin <literal>mysqlnd</literal> funciona interceptando chamadas feitas
|
||
para <literal>mysqlnd</literal> por extensões que usam
|
||
<literal>mysqlnd</literal>. Isto é conseguido obtendo a
|
||
tabela de funções <literal>mysqlnd</literal>, fazendo backup dela e
|
||
substituindo-a por uma tabela de funções customizada, que chama as funções do
|
||
plugin conforme necessário.
|
||
</simpara>
|
||
<simpara>
|
||
O código a seguir mostra como a tabela de funções <literal>mysqlnd</literal>
|
||
é substituída:
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* um lugar para armazenar a tabela de funções original */
|
||
struct st_mysqlnd_conn_methods org_methods;
|
||
|
||
void minit_register_hooks(TSRMLS_D) {
|
||
/* tabela de funções ativas */
|
||
struct st_mysqlnd_conn_methods * current_methods
|
||
= mysqlnd_conn_get_methods();
|
||
|
||
/* tabela de funções original de backup */
|
||
memcpy(&org_methods, current_methods,
|
||
sizeof(struct st_mysqlnd_conn_methods);
|
||
|
||
/* instala novos métodos */
|
||
current_methods->query = MYSQLND_METHOD(my_conn_class, query);
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
As manipulações da tabela de funções de conexão devem ser feitas durante a
|
||
inicialização do módulo (MINIT). A tabela de funções é um recurso global
|
||
compartilhado. Em um ambiente multithread, com construção de TSRM, a
|
||
manipulação de um recurso global compartilhado durante o processamento
|
||
da solicitação quase certamente resultará em conflitos.
|
||
</simpara>
|
||
<note>
|
||
<simpara>
|
||
Não use nenhuma lógica de tamanho fixo ao manipular a
|
||
tabela de funções <literal>mysqlnd</literal>: novos métodos podem ser adicionados
|
||
ao final da tabela de funções. A tabela de funções pode mudar a
|
||
qualquer momento no futuro.
|
||
</simpara>
|
||
</note>
|
||
<simpara>
|
||
<emphasis role="bold">Chamando métodos da classe pai</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Se for feito backup das entradas originais da tabela de funções, ainda será
|
||
possível chamar as entradas originais da tabela de funções - os métodos
|
||
pai.
|
||
</simpara>
|
||
<simpara>
|
||
Em alguns casos, como em
|
||
<literal>Connection::stmt_init()</literal>, é vital chamar o
|
||
método pai antes de qualquer outra atividade no método derivado.
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
MYSQLND_METHOD(my_conn_class, query)(MYSQLND *conn,
|
||
const char *query, unsigned int query_len TSRMLS_DC) {
|
||
|
||
php_printf("my_conn_class::query(query = %s)\n", query);
|
||
|
||
query = "SELECT 'query rewritten' FROM DUAL";
|
||
query_len = strlen(query);
|
||
|
||
return org_methods.query(conn, query, query_len); /* retorna com chamada ao método pai */
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
<emphasis role="bold">Estendendo propriedades</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Um objeto <literal>mysqlnd</literal> é representado por uma estrutura C. Não
|
||
é possível adicionar um membro a uma estrutura C em tempo de execução. Usuários de
|
||
objetos <literal>mysqlnd</literal> não podem simplesmente adicionar propriedades aos
|
||
objetos.
|
||
</simpara>
|
||
<simpara>
|
||
Dados arbitrários (propriedades) podem ser adicionados a
|
||
um objeto <literal>mysqlnd</literal> usando uma função apropriada
|
||
da família
|
||
<literal>mysqlnd_plugin_get_plugin_<object>_data()</literal>.
|
||
Ao alocar um objeto, <literal>mysqlnd</literal> reserva
|
||
espaço no final do objeto para conter um ponteiro <literal>void *</literal>
|
||
para dados arbitrários. <literal>mysqlnd</literal> reserva espaço
|
||
para um ponteiro <literal>void *</literal> por plugin.
|
||
</simpara>
|
||
<simpara>
|
||
A tabela a seguir mostra como calcular a posição do
|
||
ponteiro para um plugin específico:
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.pointercalc">
|
||
<title>Cálculo de ponteiro para mysqlnd</title>
|
||
<tgroup cols="2">
|
||
<thead>
|
||
<row>
|
||
<entry>Endereço de memória</entry>
|
||
<entry>Conteúdo</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>0</entry>
|
||
<entry>Início da estrutura C do objeto mysqlnd</entry>
|
||
</row>
|
||
<row>
|
||
<entry>n</entry>
|
||
<entry>Final da estrutura C do objeto mysqlnd</entry>
|
||
</row>
|
||
<row>
|
||
<entry>n + (m x sizeof(void*))</entry>
|
||
<entry>void* para dados do objeto do m-ésimo plugin</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
Se for planejada uma sub-classe de qualquer dos construtores de objeto <literal>mysqlnd</literal>,
|
||
que é permitido, deve-se ter isto em mente!
|
||
</simpara>
|
||
<simpara>
|
||
O código a seguir mostra extensão de propriedades:
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* qualquer dado que se queira associar */
|
||
typedef struct my_conn_properties {
|
||
unsigned long query_counter;
|
||
} MY_CONN_PROPERTIES;
|
||
|
||
/* id do plugin */
|
||
unsigned int my_plugin_id;
|
||
|
||
void minit_register_hooks(TSRMLS_D) {
|
||
/* obtém ID único para o plugin */
|
||
my_plugin_id = mysqlnd_plugin_register();
|
||
/* recorte - consulte Estendendo Conexão: métodos */
|
||
}
|
||
|
||
static MY_CONN_PROPERTIES** get_conn_properties(const MYSQLND *conn TSRMLS_DC) {
|
||
MY_CONN_PROPERTIES** props;
|
||
props = (MY_CONN_PROPERTIES**)mysqlnd_plugin_get_plugin_connection_data(
|
||
conn, my_plugin_id);
|
||
if (!props || !(*props)) {
|
||
*props = mnd_pecalloc(1, sizeof(MY_CONN_PROPERTIES), conn->persistent);
|
||
(*props)->query_counter = 0;
|
||
}
|
||
return props;
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
O desenvolvedor do plugin é responsável pelo gerenciamento de memória dos
|
||
dados do plugin.
|
||
</simpara>
|
||
<simpara>
|
||
O uso do alocador de memória <literal>mysqlnd</literal> é recomendado
|
||
para dados do plugin. Essas funções são nomeadas usando a convenção:
|
||
<literal>mnd_*loc()</literal>. O alocador <literal>mysqlnd</literal>
|
||
possui alguns recursos úteis, como a capacidade de usar um
|
||
alocador de depuração em uma compilação sem depuração.
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.subclass">
|
||
<title>Quando e como usar uma sub-classe</title>
|
||
<tgroup cols="4">
|
||
<thead>
|
||
<row>
|
||
<entry/>
|
||
<entry>Quando usar uma sub-classe?</entry>
|
||
<entry>Cada instância tem sua própria tabela de funções privadas?</entry>
|
||
<entry>Como usar uma sub-classe</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>Conexão (MYSQLND)</entry>
|
||
<entry>MINIT</entry>
|
||
<entry>Não</entry>
|
||
<entry>mysqlnd_conn_get_methods()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Conjunto de resultados (MYSQLND_RES)</entry>
|
||
<entry>MINIT ou depois</entry>
|
||
<entry>Sim</entry>
|
||
<entry>mysqlnd_result_get_methods() ou manipulação de tabela de funções
|
||
de métodos de objeto</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Metadados do conjunto de resultados (MYSQLND_RES_METADATA)</entry>
|
||
<entry>MINIT</entry>
|
||
<entry>Não</entry>
|
||
<entry>mysqlnd_result_metadata_get_methods()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Instrução (MYSQLND_STMT)</entry>
|
||
<entry>MINIT</entry>
|
||
<entry>Não</entry>
|
||
<entry>mysqlnd_stmt_get_methods()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Rede (MYSQLND_NET)</entry>
|
||
<entry>MINIT ou depois</entry>
|
||
<entry>Sim</entry>
|
||
<entry>mysqlnd_net_get_methods() ou manipulação de tabela de funções de métodos de objeto</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Protocolo de fio (MYSQLND_PROTOCOL)</entry>
|
||
<entry>MINIT ou depois</entry>
|
||
<entry>Sim</entry>
|
||
<entry>mysqlnd_protocol_get_methods() ou manipulação de tabela de funções
|
||
de métodos de objeto</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
Não se deve manipular tabelas de funções em nenhum momento posterior ao MINIT
|
||
se isso não for permitido de acordo com a tabela acima.
|
||
</simpara>
|
||
<simpara>
|
||
Algumas classes contêm um ponteiro para a tabela de funções do método. Todas as
|
||
instâncias dessa classe compartilharão a mesma tabela de funções. Para
|
||
evitar o caos, especialmente em ambientes com threads, tais tabelas
|
||
de funções só devem ser manipuladas durante o MINIT.
|
||
</simpara>
|
||
<simpara>
|
||
Outras classes usam cópias de uma tabela de funções compartilhada globalmente. A
|
||
cópia da tabela de funções de classe é criada junto com o objeto. Cada
|
||
objeto usa sua própria tabela de funções. Isso dá duas opções: pode-se
|
||
manipular a tabela de funções padrão de um objeto no MINIT, e
|
||
também pode-se refinar os métodos de um objeto sem afetar
|
||
outras instâncias da mesma classe.
|
||
</simpara>
|
||
<simpara>
|
||
A vantagem da abordagem de tabela de funções compartilhadas é o desempenho.
|
||
Não há necessidade de copiar uma tabela de funções para cada objeto.
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.constatus">
|
||
<title>Estado do construtor</title>
|
||
<tgroup cols="4">
|
||
<thead>
|
||
<row>
|
||
<entry>Tipo</entry>
|
||
<entry>Alocação, construção, redefinição</entry>
|
||
<entry>Pode ser modificada?</entry>
|
||
<entry>Chamadora</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>Conexão (MYSQLND)</entry>
|
||
<entry>mysqlnd_init()</entry>
|
||
<entry>Não</entry>
|
||
<entry>mysqlnd_connect()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Conjunto de resultados (MYSQLND_RES)</entry>
|
||
<entry><simpara>
|
||
Alocação:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Connection::result_init()
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
Redefinida e reinicializada durante:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Result::use_result()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Result::store_result
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist></entry>
|
||
<entry>Sim, mas deve-se chamar o método pai!</entry>
|
||
<entry><itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Connection::list_fields()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Statement::get_result()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Statement::prepare() (Somente metadados)
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Statement::resultMetaData()
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist></entry>
|
||
</row>
|
||
<row>
|
||
<entry>Metadados do conjunto de resultados (MYSQLND_RES_METADATA)</entry>
|
||
<entry>Connection::result_meta_init()</entry>
|
||
<entry>Sim, mas deve-se chamar o método pai!</entry>
|
||
<entry>Result::read_result_metadata()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Instrução (MYSQLND_STMT)</entry>
|
||
<entry>Connection::stmt_init()</entry>
|
||
<entry>Sim, mas deve-se chamar o método pai!</entry>
|
||
<entry>Connection::stmt_init()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Rede (MYSQLND_NET)</entry>
|
||
<entry>mysqlnd_net_init()</entry>
|
||
<entry>Não</entry>
|
||
<entry>Connection::init()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Protocolo de fio (MYSQLND_PROTOCOL)</entry>
|
||
<entry>mysqlnd_protocol_init()</entry>
|
||
<entry>Não</entry>
|
||
<entry>Connection::init()</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
É altamente recomendável que não se substitua totalmente um
|
||
construtor. Os construtores realizam alocações de memória. As alocações
|
||
de memória são vitais para a API do plugin <literal>mysqlnd</literal>
|
||
e para a lógica do objeto <literal>mysqlnd</literal>. Se não houver
|
||
preocupação com avisos e houver insistência em conectar os construtores, deve-se
|
||
pelo menos chamar o construtor pai antes de fazer qualquer coisa no
|
||
construtor.
|
||
</simpara>
|
||
<simpara>
|
||
Independentemente de todos os avisos, pode ser útil criar sub-classes de
|
||
construtores. Os construtores são o lugar perfeito para modificar as
|
||
tabelas de funções de objetos com tabelas de objetos não compartilhados, como
|
||
Conjunto de Resultados, Rede, Protocolo de Fio.
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.deststatus">
|
||
<title>Estado do destruidor</title>
|
||
<tgroup cols="3">
|
||
<thead>
|
||
<row>
|
||
<entry>Tipo</entry>
|
||
<entry>Método derivado deve chamar o pai?</entry>
|
||
<entry>Destruidor</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>Conexão</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>free_contents(), end_psession()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Conjunto de resultados</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>free_result()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Metadados do conjunto de resultados</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>free()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Instrução</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>dtor(), free_stmt_content()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Rede</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>free()</entry>
|
||
</row>
|
||
<row>
|
||
<entry>Protocolo de fio</entry>
|
||
<entry>sim, após execução do método</entry>
|
||
<entry>free()</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
Os destruidores são o local apropriado para liberar propriedades,
|
||
<literal>mysqlnd_plugin_get_plugin_<replaceable><object></replaceable>_data()</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
Os destruidores listados podem não ser equivalentes ao método
|
||
<literal>mysqlnd</literal> real que libera o próprio objeto. No entanto,
|
||
eles são o melhor lugar possível para se conectar e liberar os dados do
|
||
plugin. Tal como acontece com os construtores, pode-se substituir totalmente
|
||
os métodos, mas isso não é recomendado. Se vários métodos estiverem listados
|
||
na tabela acima, será necessário conectar todos os métodos listados
|
||
e liberar os dados do plugin em qualquer método chamado primeiro pelo
|
||
<literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
O método recomendado para plugins é simplesmente conectar os métodos,
|
||
liberar memória e chamar a implementação pai imediatamente
|
||
após isso.
|
||
</simpara>
|
||
</section>
|
||
<section xml:id="mysqlnd.plugin.api">
|
||
<title>A API do plugin mysqlnd</title>
|
||
<simpara>
|
||
A seguir está uma lista de funções fornecidas na API
|
||
do plugin <literal>mysqlnd</literal>:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_register()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_count()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_get_plugin_connection_data()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_get_plugin_result_data()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_get_plugin_stmt_data()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_get_plugin_net_data()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_plugin_get_plugin_protocol_data()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_conn_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_result_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_result_meta_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_stmt_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_net_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_protocol_get_methods()
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
Não existe uma definição formal do que é um plugin e como funciona
|
||
um mecanismo de plugin.
|
||
</simpara>
|
||
<simpara>
|
||
Os componentes frequentemente encontrados em mecanismos de plugins são:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Um gerenciador de plugin
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Uma API de plugin
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Serviços (ou módulos) de aplicação
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
APIs de serviço (ou APIs de módulo) de aplicação
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<simpara>
|
||
O conceito do plugin <literal>mysqlnd</literal> emprega esses recursos e,
|
||
adicionalmente, desfruta de uma arquitetura aberta.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Sem Restrições</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Um plugin tem acesso total ao funcionamento interno do
|
||
<literal>mysqlnd</literal>. Não há limites ou restrições de
|
||
segurança. Tudo pode ser sobrescrito para implementar algoritmos amigáveis ou
|
||
hostis. É recomendado que se implante apenas plugins de uma
|
||
fonte confiável.
|
||
</simpara>
|
||
<simpara>
|
||
Conforme discutido anteriormente, os plugins podem usar ponteiros livremente. Esses
|
||
ponteiros não são restritos de forma alguma e podem apontar para dados
|
||
de outro plugin. A aritmética de deslocamento simples pode ser usada para ler
|
||
os dados de outro plugin.
|
||
</simpara>
|
||
<simpara>
|
||
É recomendado que se escreva plugins cooperativos e que sempre se
|
||
chame o método pai. Os plugins devem sempre cooperar
|
||
com o próprio <literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<table xml:id="mysqlnd.plugin.chaining">
|
||
<title>Questões: um exemplo de encadeamento e cooperação</title>
|
||
<tgroup cols="3">
|
||
<thead>
|
||
<row>
|
||
<entry>Extensão</entry>
|
||
<entry>Ponteiro mysqlnd.query()</entry>
|
||
<entry>pilha de chamadas se estiver chamando o pai</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>ext/mysqlnd</entry>
|
||
<entry>mysqlnd.query()</entry>
|
||
<entry>mysqlnd.query</entry>
|
||
</row>
|
||
<row>
|
||
<entry>ext/mysqlnd_cache</entry>
|
||
<entry>mysqlnd_cache.query()</entry>
|
||
<entry><orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_cache.query()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd.query
|
||
</simpara>
|
||
</listitem>
|
||
</orderedlist></entry>
|
||
</row>
|
||
<row>
|
||
<entry>ext/mysqlnd_monitor</entry>
|
||
<entry>mysqlnd_monitor.query()</entry>
|
||
<entry><orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_monitor.query()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd_cache.query()
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
mysqlnd.query
|
||
</simpara>
|
||
</listitem>
|
||
</orderedlist></entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
<simpara>
|
||
Neste cenário, um cache (<literal>ext/mysqlnd_cache</literal>) e
|
||
um plugin de monitor (<literal>ext/mysqlnd_monitor</literal>) são carregados.
|
||
Ambas são sub-classes de <literal>Connection::query()</literal>. O registro
|
||
do plugin acontece em <literal>MINIT</literal> usando a lógica
|
||
mostrada anteriormente. O PHP chama extensões em ordem alfabética por
|
||
padrão. Os plugins não reconhecem uns aos outros e não definem dependências
|
||
de extensão.
|
||
</simpara>
|
||
<simpara>
|
||
Por padrão, os plugins chamam a implementação pai do método de
|
||
consulta em sua versão derivada do método.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Recapitulação da Extensão PHP</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Esta é uma recapitulação do que acontece ao usar um plugin de exemplo,
|
||
<literal>ext/mysqlnd_plugin</literal>, que expõe a API do
|
||
plugin C <literal>mysqlnd</literal> ao PHP:
|
||
</simpara>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Qualquer aplicação PHP MySQL tenta estabelecer uma conexão com
|
||
192.168.2.29
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
A aplicação PHP usará <literal>ext/mysql</literal>,
|
||
<literal>ext/mysqli</literal> ou <literal>PDO_MYSQL</literal>. Todas
|
||
as três extensões PHP MySQL usam <literal>mysqlnd</literal> para
|
||
estabelecer a conexão com 192.168.2.29.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<literal>Mysqlnd</literal> chama seu método de conexão, que agora é
|
||
uma sub-classe de <literal>ext/mysqlnd_plugin</literal>.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<literal>ext/mysqlnd_plugin</literal> chama o gancho do espaço de usuário
|
||
<literal>proxy::connect()</literal> registrado pelo usuário.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
O gancho do espaço de usuário altera o IP do host de conexão de 192.168.2.29
|
||
para 127.0.0.1 e retorna a conexão estabelecida por
|
||
<literal>parent::connect()</literal>.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<literal>ext/mysqlnd_plugin</literal> executa o equivalente a
|
||
<literal>parent::connect(127.0.0.1)</literal> chamando o
|
||
método <literal>mysqlnd</literal> original para estabelecer uma
|
||
conexão.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
<literal>ext/mysqlnd</literal> estabelece uma conexão e retorna
|
||
ao <literal>ext/mysqlnd_plugin</literal>.
|
||
<literal>ext/mysqlnd_plugin</literal> também retorna.
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Qualquer que seja a extensão PHP MySQL utilizada pela aplicação, ela
|
||
recebe uma conexão com 127.0.0.1. A própria extensão PHP MySQL
|
||
retorna ao aplicativo PHP. E o ciclo se fecha.
|
||
</simpara>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section xml:id="mysqlnd.plugin.developing">
|
||
<title>Começando a construir um plugin mysqlnd</title>
|
||
<simpara>
|
||
É importante lembrar que um plugin <literal>mysqlnd</literal>
|
||
é em si uma extensão PHP.
|
||
</simpara>
|
||
<simpara>
|
||
O código a seguir mostra a estrutura básica da função MINIT
|
||
que será usada no plugin <literal>mysqlnd</literal> típico:
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_php_mysqlnd_plugin.c */
|
||
|
||
static PHP_MINIT_FUNCTION(mysqlnd_plugin) {
|
||
/* globais, entradas ini, recursos, classes */
|
||
|
||
/* registra o plugin mysqlnd */
|
||
mysqlnd_plugin_id = mysqlnd_plugin_register();
|
||
|
||
conn_m = mysqlnd_get_conn_methods();
|
||
memcpy(org_conn_m, conn_m,
|
||
sizeof(struct st_mysqlnd_conn_methods));
|
||
|
||
conn_m->query = MYSQLND_METHOD(mysqlnd_plugin_conn, query);
|
||
conn_m->connect = MYSQLND_METHOD(mysqlnd_plugin_conn, connect);
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_mysqlnd_plugin.c */
|
||
|
||
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, query)(/* ... */) {
|
||
/* ... */
|
||
}
|
||
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, connect)(/* ... */) {
|
||
/* ... */
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
<emphasis role="bold">Análise de tarefa: do C ao espaço do usuário</emphasis>
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
class proxy extends mysqlnd_plugin_connection {
|
||
public function connect($host, ...) { .. }
|
||
}
|
||
mysqlnd_plugin_set_conn_proxy(new proxy());
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
Processo:
|
||
</simpara>
|
||
<orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
PHP: usuário registra chamada do plugin
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
PHP: usuário chama qualquer API PHP MySQL para se conectar ao MySQL
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
C: ext/*mysql* chama o método de mysqlnd
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
C: mysqlnd chega em ext/mysqlnd_plugin
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
C: ext/mysqlnd_plugin
|
||
<orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Chama a função de retorno no espaço de usuário
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Ou o método original de <literal>mysqlnd</literal>, se a função de retorno
|
||
no espaço de usuário não estiver definida
|
||
</simpara>
|
||
</listitem>
|
||
</orderedlist>
|
||
</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
<simpara>
|
||
Deve-se fazer o seguinte:
|
||
</simpara>
|
||
<orderedlist>
|
||
<listitem>
|
||
<simpara>
|
||
Escrever uma classe "mysqlnd_plugin_connection" em C
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Aceitar e registrar um objeto proxy através de
|
||
"mysqlnd_plugin_set_conn_proxy()"
|
||
</simpara>
|
||
</listitem>
|
||
<listitem>
|
||
<simpara>
|
||
Chamar métodos proxy do espaço do usuário a partir do C (otimização -
|
||
zend_interfaces.h)
|
||
</simpara>
|
||
</listitem>
|
||
</orderedlist>
|
||
<simpara>
|
||
Os métodos de objeto do espaço do usuário podem ser chamados usando
|
||
<literal>call_user_function()</literal> ou pode-se operar em um nível
|
||
mais próximo do Motor Zend e usar
|
||
<literal>zend_call_method()</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Otimização: chamando métrodos a partir do C usando zend_call_method</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
O trecho de código a seguir mostra o protótipo da
|
||
função <literal>zend_call_method</literal>, obtido de
|
||
<filename>zend_interfaces.h</filename>.
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
ZEND_API zval* zend_call_method(
|
||
zval **object_pp, zend_class_entry *obj_ce,
|
||
zend_function **fn_proxy, char *function_name,
|
||
int function_name_len, zval **retval_ptr_ptr,
|
||
int param_count, zval* arg1, zval* arg2 TSRMLS_DC
|
||
);
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
A API Zend suporta apenas dois argumentos. Pode ser necessário mais, por exemplo:
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
enum_func_status (*func_mysqlnd_conn__connect)(
|
||
MYSQLND *conn, const char *host,
|
||
const char * user, const char * passwd,
|
||
unsigned int passwd_len, const char * db,
|
||
unsigned int db_len, unsigned int port,
|
||
const char * socket, unsigned int mysql_flags TSRMLS_DC
|
||
);
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
Para contornar esse problema é necessário fazer uma cópia de
|
||
<literal>zend_call_method()</literal> e adicionar um recurso para
|
||
parâmetros adicionais. Pode-se fazer isso criando um conjunto de
|
||
macros <literal>MY_ZEND_CALL_METHOD_WRAPPER</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Chamando o espaço de usuário do PHP</emphasis>
|
||
</simpara>
|
||
<simpara>
|
||
Este trecho de código mostra o método otimizado para chamar uma função
|
||
de espaço do usuário a partir do C:
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_mysqlnd_plugin.c */
|
||
|
||
MYSQLND_METHOD(my_conn_class,connect)(
|
||
MYSQLND *conn, const char *host /* ... */ TSRMLS_DC) {
|
||
enum_func_status ret = FAIL;
|
||
zval * global_user_conn_proxy = fetch_userspace_proxy();
|
||
if (global_user_conn_proxy) {
|
||
/* chama o proxy de espaço de usuário */
|
||
ret = MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, host, /*...*/);
|
||
} else {
|
||
/* ou o método mysqlnd original = não faz nada, é transparente */
|
||
ret = org_methods.connect(conn, host, user, passwd,
|
||
passwd_len, db, db_len, port,
|
||
socket, mysql_flags TSRMLS_CC);
|
||
}
|
||
return ret;
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
<emphasis role="bold">Chamando o espaço de usuário: argumentos simples</emphasis>
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_mysqlnd_plugin.c */
|
||
|
||
MYSQLND_METHOD(my_conn_class,connect)(
|
||
/* ... */, const char *host, /* ...*/) {
|
||
/* ... */
|
||
if (global_user_conn_proxy) {
|
||
/* ... */
|
||
zval* zv_host;
|
||
MAKE_STD_ZVAL(zv_host);
|
||
ZVAL_STRING(zv_host, host, 1);
|
||
MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_host /*, ...*/);
|
||
zval_ptr_dtor(&zv_host);
|
||
/* ... */
|
||
}
|
||
/* ... */
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
<emphasis role="bold">Chamando o espaço de usuário: estruturas como argumentos</emphasis>
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_mysqlnd_plugin.c */
|
||
|
||
MYSQLND_METHOD(my_conn_class, connect)(
|
||
MYSQLND *conn, /* ...*/) {
|
||
/* ... */
|
||
if (global_user_conn_proxy) {
|
||
/* ... */
|
||
zval* zv_conn;
|
||
ZEND_REGISTER_RESOURCE(zv_conn, (void *)conn, le_mysqlnd_plugin_conn);
|
||
MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_conn, zv_host /*, ...*/);
|
||
zval_ptr_dtor(&zv_conn);
|
||
/* ... */
|
||
}
|
||
/* ... */
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
O primeiro argumento de muitos métodos <literal>mysqlnd</literal> é um "objeto"
|
||
C. Por exemplo, o primeiro argumento do método connect() é
|
||
um ponteiro para <literal>MYSQLND</literal>. A estrutura MYSQLND
|
||
representa um objeto de conexão <literal>mysqlnd</literal>.
|
||
</simpara>
|
||
<simpara>
|
||
O ponteiro do objeto de conexão <literal>mysqlnd</literal> pode ser
|
||
comparado a um identificador de arquivo de E/S padrão. Como um arquivo de E/S padrão,
|
||
um objeto de conexão <literal>mysqlnd</literal> deve ser vinculado
|
||
ao espaço do usuário usando o tipo de variável de recurso PHP.
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Do C para o espaço de usuário e vice-versa</emphasis>
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
class proxy extends mysqlnd_plugin_connection {
|
||
public function connect($conn, $host, ...) {
|
||
/* "pre" gancho */
|
||
printf("Conectando-se ao servidor = '%s'\n", $host);
|
||
debug_print_backtrace();
|
||
return parent::connect($conn);
|
||
}
|
||
|
||
public function query($conn, $query) {
|
||
/* "pós" gancho */
|
||
$ret = parent::query($conn, $query);
|
||
printf("Consulta = '%s'\n", $query);
|
||
return $ret;
|
||
}
|
||
}
|
||
mysqlnd_plugin_set_conn_proxy(new proxy());
|
||
]]>
|
||
</programlisting>
|
||
<simpara>
|
||
Os usuários de PHP devem ser capazes de chamar a implementação pai de um
|
||
método substituído.
|
||
</simpara>
|
||
<simpara>
|
||
Como resultado de se criar sub-classes, é possível refinar apenas métodos
|
||
selecionados e pode-se optar por ter ganchos "pré" ou "pós".
|
||
</simpara>
|
||
<simpara>
|
||
<emphasis role="bold">Classe integrada: mysqlnd_plugin_connection::connect()</emphasis>
|
||
</simpara>
|
||
<programlisting>
|
||
<![CDATA[
|
||
/* my_mysqlnd_plugin_classes.c */
|
||
|
||
PHP_METHOD("mysqlnd_plugin_connection", connect) {
|
||
/* ... simplificada! ... */
|
||
zval* mysqlnd_rsrc;
|
||
MYSQLND* conn;
|
||
char* host; int host_len;
|
||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
|
||
&mysqlnd_rsrc, &host, &host_len) == FAILURE) {
|
||
RETURN_NULL();
|
||
}
|
||
ZEND_FETCH_RESOURCE(conn, MYSQLND* conn, &mysqlnd_rsrc, -1,
|
||
"Mysqlnd Connection", le_mysqlnd_plugin_conn);
|
||
if (PASS == org_methods.connect(conn, host, /* simplificado! */ TSRMLS_CC))
|
||
RETVAL_TRUE;
|
||
else
|
||
RETVAL_FALSE;
|
||
}
|
||
]]>
|
||
</programlisting>
|
||
</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
|
||
-->
|