knowledge-base/records/plugin-dev/KB-PLUGIN-032-glpi11-second-dbmysql-connection.md
Rodolpho Lopes f1ec0c6405 KB-PLUGIN-032/033: segunda conexão DBmysql + comandos de console de plugin (GLPI 11)
Aprendizados validados no desenvolvimento do plugin sync:
- KB-PLUGIN-032: subclasse DBmysql read-only para banco externo (gotchas de
  senha rawurldecode, query() guard, flag connected).
- KB-PLUGIN-033: comandos de console de plugin exigem src/ + namespace de nome
  plugins:<key>:.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 17:16:46 +00:00

98 lines
3.4 KiB
Markdown

---
id: KB-PLUGIN-032
title: "GLPI 11 — Segunda conexão de banco read-only via subclasse de DBmysql"
domain: plugin-dev
tags:
- glpi11
- plugin
- database
- dbmysql
- connection
- read-only
- external-db
- sync
status: active
severity: high
created_at: 2026-06-16
updated_at: 2026-06-16
applies_to:
- plugins GLPI 11.x que precisam ler de um banco GLPI externo
- plugin Sync (ingestão PROD legado → QA)
related_records:
- KB-PLUGIN-030
- KB-PLUGIN-014
---
# GLPI 11 — Segunda conexão de banco via subclasse de DBmysql
## Contexto
Um plugin pode precisar ler de um **segundo banco** (ex.: o PROD legado de um
cliente) sem tocar na conexão padrão do GLPI (`global $DB`). O core já oferece
toda a infraestrutura: basta uma subclasse de `\DBmysql` apontando para o outro
host. Reaproveita-se `doQuery()`, `request()`, `fetchAssoc()`, `tableExists()` etc.
## Como fazer
`DBmysql::connect()`**propriedades de instância**: `$dbhost`, `$dbuser`,
`$dbpassword`, `$dbdefault`. O construtor nativo (`__construct($choice)`) chama
`connect()` imediatamente — portanto **não chame `parent::__construct()`**:
defina as propriedades primeiro e só então chame `$this->connect()`.
```php
namespace GlpiPlugin\Meuplugin;
use DBmysql;
class SourceDB extends DBmysql
{
public function __construct(string $host, string $dbname, string $user, string $password, int $port = 3306, bool $use_utf8mb4 = true)
{
$this->dbhost = $port > 0 ? $host . ':' . $port : $host;
$this->dbuser = $user;
$this->dbpassword = rawurlencode($password); // ver gotcha abaixo
$this->dbdefault = $dbname;
$this->use_utf8mb4 = $use_utf8mb4;
$this->connect();
}
public function isConnected(): bool
{
return (bool) $this->connected;
}
}
```
## Gotchas confirmados
1. **Senha é rawurldecoded em `connect()`.** A linha real do core é
`real_connect($host, $this->dbuser, rawurldecode($this->dbpassword), ...)`.
Portanto guarde a senha **`rawurlencode()`-ada** na propriedade (idêntico ao
`config/config_db.php` nativo). Senha com `%`, `+`, etc. quebra se passada crua.
2. **Host com porta:** `connect()` faz `explode(':', $host)`. Passe `"host:porta"`
numa string só; não há parâmetro de porta separado.
3. **`query()` SEMPRE lança exceção** (`Executing direct queries is not allowed!`)
— é um guard, não o executor. Para **SELECT** use **`doQuery()`** (retorna
`mysqli_result`) + `fetchAssoc()`/`fetchArray()`, ou `request()` (iterator).
Para DDL, ver [KB-PLUGIN-030].
4. **Sucesso da conexão:** `connect()` seta `$this->connected = true` só em caso de
sucesso; em falha permanece `false` sem lançar exceção. Cheque `connected`
(a falha de credencial NÃO crasha — retorna conexão não-conectada).
5. **Defaults seguros** em DBmysql: `dbssl=false`, `use_utf8mb4=false`,
`connected=false`. Para ler bancos GLPI 10.0.6+ defina `use_utf8mb4=true`.
## Validação
Comprovado no plugin `sync` (2026-06-16): subclasse `SourceDB` conectou a um
GLPI 11.0.7 externo via `bin/console plugins:sync:test-connection`, leu
`VERSION()`, `glpi_configs` (versão) e `COUNT(*)` de `glpi_tickets`. Senha
inválida retornou `connected=false` com exit code 1, sem fatal.
## Classificação
- Tipo: padrão de implementação reutilizável.
- Reutilização: qualquer plugin com fonte de dados em segundo banco.