nils-framework/nils-database
Composer 安装命令:
composer require nils-framework/nils-database
包简介
Composant d'accès aux données, système de migration et Query Builder fluide pour le framework NILS
README 文档
README
Couche d'accès aux données du framework NILS : un module léger, sans dépendance lourde, construit directement au-dessus de PDO. Il fournit une façade de connexion résiliente, un générateur de requêtes fluide, un système de migrations et un outil d'introspection de schéma, le tout compatible MySQL, MariaDB, PostgreSQL et SQLite.
La philosophie est volontairement minimaliste : pas d'attributs, pas de génération de proxys, pas de conteneur. Une API statique simple et des requêtes préparées partout.
Sommaire
- Caractéristiques
- Prérequis
- Installation
- Initialisation
- Exécution de requêtes
- Transactions
- QueryBuilder
- Migrations & schéma
- Introspection (SchemaReader)
- Pilotes (Drivers)
- Sécurité
- Gestion des erreurs
- Limitations & notes importantes
- Architecture
- Tests
- Référence rapide de l'API
Caractéristiques
- Multi-SGBD : MySQL, MariaDB, PostgreSQL, SQLite via une interface de pilote unifiée.
- Connexion paresseuse (lazy loading) en pattern Singleton, compatible workers asynchrones (Swoole, RoadRunner, FrankenPHP).
- Rejeu réseau sécurisé : reconnexion automatique sur coupure, restreinte aux requêtes de lecture hors transaction.
- Requêtes préparées systématiques et échappement strict des identifiants (anti-injection SQL).
- QueryBuilder fluide pour les SELECT, avec liste blanche d'opérateurs et bindings nommés anti-collision.
- Migrations déclaratives via un
Blueprintorienté objet. - Introspection (rétro-ingénierie) du schéma existant vers des
Blueprint. - Curseur paresseux par générateur pour le parcours de gros volumes.
Prérequis
- PHP ≥ 8.0 (utilise
match, la promotion de propriétés, les arguments nommés, l'opérateur nullsafe). - Extension PDO activée, ainsi que le pilote PDO correspondant à votre SGBD :
pdo_mysql(MySQL / MariaDB)pdo_pgsql(PostgreSQL)pdo_sqlite(SQLite)
Installation
Via Composer (ajustez le nom du paquet à celui déclaré dans votre composer.json) :
composer require nils/database
Puis chargez l'autoloader :
require 'vendor/autoload.php';
L'autoload PSR-4 mappe le namespace NilsDatabase\ sur le dossier src/.
Initialisation
Le module est agnostique vis-à-vis de votre gestion de configuration : on lui injecte un simple tableau associatif via Database::initialiser(). La connexion réelle n'est ouverte qu'au premier accès (lazy loading).
use NilsDatabase\Database;
Database::initialiser([
'driver' => 'mysql', // mysql | mariadb | pgsql | postgresql | sqlite
'host' => '127.0.0.1',
'port' => 3306, // optionnel (défaut : 3306, ou 5432 pour PostgreSQL)
'database' => 'nils_core',
'username' => 'root',
'password' => 'secret',
'charset' => 'utf8mb4', // optionnel (défaut : utf8mb4)
'timeout' => 5, // optionnel (défaut : 5 s)
]);
SQLite
Database::initialiser([
'driver' => 'sqlite',
'database' => '/chemin/vers/base.sqlite', // ou ':memory:'
]);
Clés de configuration
| Clé | Obligatoire | Défaut | Remarque |
|---|---|---|---|
driver | non | mysql | mysql, mariadb, pgsql, postgresql, sqlite |
host | non | 127.0.0.1 | ignoré pour SQLite |
port | non | 3306 / 5432 | ignoré pour SQLite |
database | oui | — | nom de la base, ou chemin du fichier pour SQLite |
username | non | null | ignoré pour SQLite |
password | non | null | ignoré pour SQLite |
charset | non | utf8mb4 | liste blanche : utf8mb4, utf8, utf8mb3, latin1, ascii |
timeout | non | 5 | délai de connexion en secondes |
Cycle de vie de la connexion
Dans un contexte de worker persistant (Swoole, RoadRunner…), fermez la connexion entre deux cycles afin de réinitialiser l'état du Singleton :
Database::fermer();
Exécution de requêtes
Toutes les méthodes acceptent des marqueurs nommés (:nom) ou anonymes (?) et lient les valeurs de manière sécurisée.
// Plusieurs lignes -> array[]
$lignes = Database::recupererTout(
"SELECT * FROM utilisateurs WHERE actif = :actif",
['actif' => 1]
);
// Une seule ligne -> array|null
$user = Database::recupererUn(
"SELECT * FROM utilisateurs WHERE id = :id",
['id' => 42]
);
// Insertion -> int (identifiant généré)
$id = Database::inserer(
"INSERT INTO utilisateurs (nom, email) VALUES (:nom, :email)",
['nom' => 'Léa', 'email' => 'lea@exemple.fr']
);
// Mise à jour -> int (nombre de lignes affectées)
$n = Database::mettreAJour(
"UPDATE utilisateurs SET actif = :a WHERE id = :id",
['a' => 0, 'id' => 42]
);
// Suppression -> int (nombre de lignes supprimées)
$n = Database::supprimer(
"DELETE FROM utilisateurs WHERE id = :id",
['id' => 42]
);
// Méthode bas niveau -> \PDOStatement
$stmt = Database::executer("SELECT COUNT(*) AS total FROM utilisateurs");
Curseur paresseux (gros volumes)
Pour parcourir un grand jeu de résultats sans tout charger d'un coup :
foreach (Database::curseur("SELECT * FROM journal") as $ligne) {
traiter($ligne);
}
Note de performance. Sur MySQL / MariaDB, PDO bufferise par défaut l'ensemble du résultat côté client. Pour un véritable streaming sur ces moteurs, il faut activer
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY = false(non activé par défaut car la connexion du Singleton est partagée). Sur PostgreSQL et SQLite, le comportement diffère.
Transactions
Database::debut();
try {
Database::inserer("INSERT INTO comptes (solde) VALUES (:s)", ['s' => 100]);
Database::mettreAJour("UPDATE comptes SET solde = solde - :m WHERE id = :id", ['m' => 30, 'id' => 1]);
Database::valider(); // COMMIT
} catch (\Throwable $e) {
Database::annuler(); // ROLLBACK
throw $e;
}
L'ouverture est idempotente : debut() ne tente pas de transaction imbriquée si une transaction est déjà active. valider() et annuler() ne font rien s'il n'y a aucune transaction en cours.
QueryBuilder
Générateur fluide pour les requêtes SELECT. Il s'instancie (il n'est pas statique) et s'auto-réinitialise après chaque exécution.
use NilsDatabase\QueryBuilder;
$resultats = (new QueryBuilder())
->table('utilisateurs')
->select(['id', 'nom', 'email'])
->where('actif', '=', 1)
->where('nom', 'LIKE', '%traore%')
->whereIn('role', ['admin', 'editeur'])
->orderBy('created_at DESC')
->limit(20)
->offset(0)
->recupererTout();
$premier = (new QueryBuilder())
->table('utilisateurs')
->where('email', '=', 'lea@exemple.fr')
->recupererUn(); // array|null
Opérateurs autorisés (clause where)
Liste blanche stricte : =, !=, <, >, <=, >=, LIKE, NOT LIKE. Tout autre opérateur lève une QueryException.
Conditions ensemblistes (IN / NOT IN)
Les comparaisons à une liste passent par whereIn() (et non par where(), qui rejette les tableaux) :
$qb->whereIn('statut', ['actif', 'en_attente']); // IN (...)
$qb->whereIn('statut', ['supprime', 'banni'], true); // NOT IN (...)
Une liste vide produit une condition toujours fausse (IN) ou toujours vraie (NOT IN), ce qui évite un SQL invalide.
Tri (orderBy)
Comme les clauses de tri ne sont pas paramétrables, la chaîne est validée par une expression régulière stricte n'autorisant que colonne ou table.colonne, suivie en option de ASC / DESC. Toute autre forme lève une QueryException.
Méthodes utilitaires
$sql = $qb->versSql(); // chaîne SQL compilée (débogage)
$qb->reinitialiser(); // remet l'instance à zéro (appelée automatiquement après recupererTout/recupererUn)
Migrations & schéma
Définir une migration
Chaque migration hérite de la classe abstraite Migration et implémente up() (application) et down() (retour arrière).
use NilsDatabase\Migration\Migration;
use NilsDatabase\Migration\Schema;
use NilsDatabase\Migration\Blueprint;
class CreerTableArticles extends Migration
{
public function up(): void
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('titre', 200);
$table->string('slug', 200);
$table->integer('auteur_id');
$table->boolean('publie');
$table->timestamps();
$table->unique('slug');
$table->index('auteur_id');
$table->foreign('auteur_id', 'utilisateurs', 'id')
->onDelete('cascade')
->onUpdate('cascade');
});
}
public function down(): void
{
Schema::dropIfExists('articles');
}
}
Schema::create() compile le Blueprint puis exécute les requêtes dans une transaction (voir l'avertissement DDL dans les limitations).
API du Blueprint
| Méthode | Effet |
|---|---|
id(string $nom = 'id') | Clé primaire entière auto-incrémentée |
string(string $nom, int $longueur = 255) | Colonne VARCHAR |
integer(string $nom) | Colonne INT |
boolean(string $nom) | Colonne booléenne (BOOLEAN / TINYINT(1)) |
timestamps() | Colonnes created_at et updated_at |
unique(string $colonne) | Contrainte UNIQUE |
index(string $colonne) | Index de recherche (CREATE INDEX séparé) |
foreign(string $local, string $tableRef, string $colRef = 'id') | Clé étrangère, renvoie un ForeignKey chaînable |
Sur l'objet ForeignKey :
->onDelete('cascade') // RESTRICT (défaut) | CASCADE | SET NULL | NO ACTION
->onUpdate('cascade')
Supprimer une table
Schema::dropIfExists('articles');
Introspection (SchemaReader)
Rétro-ingénierie d'une base existante vers des Blueprint.
use NilsDatabase\Migration\SchemaReader;
// Une table -> Blueprint
$blueprint = SchemaReader::lireTable('utilisateurs');
// Toute la base -> ['nom_table' => Blueprint, ...]
$schema = SchemaReader::lireBaseDeDonnees();
// Le nom de base est facultatif : à défaut, celui passé à Database::initialiser() est utilisé.
L'introspection interroge INFORMATION_SCHEMA (MySQL / PostgreSQL), sqlite_master et les commandes PRAGMA (SQLite).
Pilotes (Drivers)
Chaque pilote implémente DriverInterface :
| Pilote | DSN | Échappement des identifiants |
|---|---|---|
MysqlDriver | mysql:host=…;port=…;dbname=…;charset=… | backticks ` (dédoublés) |
MariaDbDriver | hérité de MysqlDriver | hérité de MysqlDriver |
PostgresDriver | pgsql:host=…;port=…;dbname=… | guillemets doubles " (dédoublés) |
SqliteDriver | sqlite:/chemin ou sqlite::memory: | guillemets doubles " (dédoublés) |
DriverInterface expose deux méthodes : construireDsn() et echapperIdentifiant(). Le pilote actif est récupérable via Database::obtenirPilote().
Sécurité
- Requêtes préparées sur tous les chemins d'exécution : les valeurs ne sont jamais interpolées directement.
- Échappement des identifiants : noms de tables et de colonnes encadrés par les délimiteurs natifs du SGBD, avec dédoublement des délimiteurs internes (interdit la rupture de chaîne).
- Liste blanche d'opérateurs dans le
QueryBuilder. - Validation stricte de la clause
ORDER BY(regex) et du jeu de caractères (liste blanche), tous deux non paramétrables et donc validés en amont. - Valeurs
DEFAULTdes migrations : encadrées et échappées (guillemets simples dédoublés) lors de la compilation du schéma.
XSS — hors périmètre. Cette couche protège contre l'injection SQL (en entrée vers la base). La défense XSS se traite à l'affichage (échappement HTML au rendu), pas ici : stockez vos données brutes et échappez-les en sortie.
Gestion des erreurs
| Exception | Levée par | Fabriques / cas |
|---|---|---|
DatabaseException | connexion / configuration | connexionEchouee(), driverNonSupporte() |
QueryException | exécution de requêtes, QueryBuilder | requeteEchouee() (opérateur refusé, table absente, échec SQL…) |
\InvalidArgumentException | Blueprint::compiler() | type de colonne inconnu |
Le mode d'erreur PDO est ERRMODE_EXCEPTION et l'émulation des requêtes préparées est désactivée.
Limitations & notes importantes
- DDL non transactionnel sous MySQL / MariaDB.
Schema::create()enveloppe ses requêtes dans une transaction, mais sur ces moteurs les instructions DDL (CREATE TABLE,CREATE INDEX…) provoquent un COMMIT implicite : un échec en cours de route ne peut pas être annulé. La protection par rollback n'est réellement effective que sur PostgreSQL et SQLite. lastInsertId()sous PostgreSQL nécessite le nom de la séquence ; préférezINSERT … RETURNING idconsommé viarecupererUn().- Nullabilité / valeurs par défaut. Les colonnes sont
NOT NULLpar défaut ; l'API fluide duBlueprintn'expose pas encore de réglagenullable()oudefault()(seules les clés étrangères sont chaînables). QueryBuildercouvre uniquement le SELECT ; les écritures passent parDatabase::inserer/mettreAJour/supprimer.- Détection de dialecte par nom de classe.
BlueprintetSchemaReaderdéterminent le SGBD viastr_contains(get_class($driver), …), ce qui reste sensible à un renommage de classe. curseur()et le buffering MySQL : voir la note de la section Exécution de requêtes.
Architecture
nils-database
├── src
│ ├── Database.php # Façade de connexion + moteur d'exécution
│ ├── QueryBuilder.php # Générateur fluide (SELECT)
│ ├── Driver
│ │ ├── DriverInterface.php
│ │ ├── MysqlDriver.php
│ │ ├── MariaDbDriver.php
│ │ ├── PostgresDriver.php
│ │ └── SqliteDriver.php
│ ├── Exception
│ │ ├── DatabaseException.php
│ │ └── QueryException.php
│ └── Migration
│ ├── Migration.php # Classe abstraite up()/down()
│ ├── Schema.php # Façade create()/dropIfExists()
│ ├── Blueprint.php # Modélisation + compiler()
│ ├── Colonne.php # DTO de colonne
│ ├── ForeignKey.php # Contrainte relationnelle
│ └── SchemaReader.php # Introspection
└── tests
├── Unit
├── Integration
└── Functional
Tests
La suite repose sur PHPUnit :
vendor/bin/phpunit
Trois familles de tests : Unit (logique pure : QueryBuilder, Blueprint), Integration (connexion réelle, persistance, exécution de schéma) et Functional (SchemaReader).
Référence rapide de l'API
Database (statique)
initialiser(array $config): void
connecter(): void
fermer(): void
executer(string $sql, array $params = []): \PDOStatement
curseur(string $sql, array $params = []): \Generator
recupererTout(string $sql, array $params = []): array
recupererUn(string $sql, array $params = []): ?array
inserer(string $sql, array $params = []): int
mettreAJour(string $sql, array $params = []): int
supprimer(string $sql, array $params = []): int
debut(): void
valider(): void
annuler(): void
obtenirPilote(): DriverInterface
obtenirNomBase(): string
QueryBuilder (instancié)
table(string $table): self
select(array $colonnes): self
where(string $colonne, string $operateur, mixed $valeur): self
whereIn(string $colonne, array $valeurs, bool $negation = false): self
orderBy(string $clause): self
limit(int $limit): self
offset(int $offset): self
recupererTout(): array
recupererUn(): ?array
versSql(): string
reinitialiser(): self
Schema (statique)
create(string $table, callable $callback): void
dropIfExists(string $table): void
SchemaReader (statique)
lireTable(string $table): Blueprint
lireBaseDeDonnees(string $nomBase = ""): Blueprint[]
Module nils-database — framework NILS. Auteur : Traore.
nils-framework/nils-database 适用场景与选型建议
nils-framework/nils-database 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 06 月 10 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「framework」 「database」 「migrations」 「pdo」 「query-builder」 「nils」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 nils-framework/nils-database 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 nils-framework/nils-database 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 nils-framework/nils-database 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
PHP Framework HLEB2 is the foundation of the web application. Provides ease of development and application performance.
Dibi is Database Abstraction Library for PHP
Store your language lines in the database, yaml or other sources
Symfony ClickhouseMigrationsBundle
Very simple SQL-based database migrations tool - a heavily stripped down fork of robmorgan/phinx
A package for automatically encrypting and decrypting Eloquent attributes in Laravel 5.5+, based on configuration settings.
统计信息
- 总下载量: 1
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 41
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-10