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>
3.4 KiB
| id | title | domain | tags | status | severity | created_at | updated_at | applies_to | related_records | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| KB-PLUGIN-032 | GLPI 11 — Segunda conexão de banco read-only via subclasse de DBmysql | plugin-dev |
|
active | high | 2026-06-16 | 2026-06-16 |
|
|
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() lê 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().
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
-
Senha é rawurldecoded em
connect(). A linha real do core éreal_connect($host, $this->dbuser, rawurldecode($this->dbpassword), ...). Portanto guarde a senharawurlencode()-ada na propriedade (idêntico aoconfig/config_db.phpnativo). Senha com%,+, etc. quebra se passada crua. -
Host com porta:
connect()fazexplode(':', $host). Passe"host:porta"numa string só; não há parâmetro de porta separado. -
query()SEMPRE lança exceção (Executing direct queries is not allowed!) — é um guard, não o executor. Para SELECT usedoQuery()(retornamysqli_result) +fetchAssoc()/fetchArray(), ourequest()(iterator). Para DDL, ver [KB-PLUGIN-030]. -
Sucesso da conexão:
connect()seta$this->connected = truesó em caso de sucesso; em falha permanecefalsesem lançar exceção. Chequeconnected(a falha de credencial NÃO crasha — retorna conexão não-conectada). -
Defaults seguros em DBmysql:
dbssl=false,use_utf8mb4=false,connected=false. Para ler bancos GLPI 10.0.6+ definause_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.