Driver SQLite para Phobos Framework Database Layer. Cero configuración de servidor: conecta a un archivo .sqlite o a una base de datos en memoria (:memory:), con las claves foráneas activadas por defecto y los PRAGMAs más útiles expuestos como opciones de conexión.
Es el driver ideal para tests (SQLite en memoria, rápido y aislado), prototipos y aplicaciones de escritorio o embebidas.
- 🪶 Sin servidor - Un archivo o
:memory:, sin host, puerto ni credenciales - 🔒 Foreign keys activas por defecto - SQLite las trae apagadas; este driver las enciende
- ⚡ PRAGMAs comunes -
journal_mode(WAL),busy_timeout,synchronous - 🎯 Savepoints - Transacciones anidadas completas
- 🧪 Ideal para testing -
:memory:da una BD limpia por proceso - 🛡️ Valores validados - Los PRAGMAs pasan por lista blanca para evitar inyección
- 🏷️ Identificadores estándar - Citado con comillas dobles
composer require mongoose-studio/phobos-framework-database-sqliteEste driver requiere:
mongoose-studio/phobos-framework^3.1mongoose-studio/phobos-framework-database^3.2- Extensión
ext-pdohabilitada - Extensión
ext-pdo_sqlitehabilitada
En tu config/database.php:
<?php
return [
'default' => 'sqlite',
'drivers' => [
'sqlite' => PhobosFramework\Database\Drivers\SQLite\SQLiteDriver::class,
],
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => storage_path('database/app.sqlite'),
'foreign_keys' => true,
'journal_mode' => 'WAL',
],
],
];En tu archivo .env:
DB_CONNECTION=sqlite
DB_DATABASE=/ruta/absoluta/a/database/app.sqliteA diferencia de MySQL, SQLite no usa host, port, username, password, charset ni collation. La única opción obligatoria es database.
'sqlite' => [
'driver' => 'sqlite',
'database' => '/var/data/app.sqlite', // Ruta al archivo
// 'database' => ':memory:', // Base de datos en memoria
]| Valor | Resultado |
|---|---|
| Ruta a un archivo | sqlite:/var/data/app.sqlite — persiste en disco |
:memory: |
sqlite::memory: — vive en RAM, se pierde al cerrar la conexión |
Nota sobre
:memory:: cada conexión PDO nueva crea una base de datos en memoria independiente. Mientras reutilices la misma conexión (como hace elConnectionManager), los datos persisten durante todo el proceso — perfecto para una suite de tests.
SQLite trae las claves foráneas desactivadas por defecto (una de las trampas más comunes del motor). Este driver las activa salvo que lo indiques explícitamente:
'sqlite' => [
// ...
'foreign_keys' => true, // Default. Ejecuta PRAGMA foreign_keys = ON
// 'foreign_keys' => false, // Desactiva las restricciones FK
]Controla el modo de journaling. WAL (Write-Ahead Logging) suele ser la mejor opción para concurrencia:
'sqlite' => [
// ...
'journal_mode' => 'WAL',
]Valores válidos: DELETE, TRUNCATE, PERSIST, MEMORY, WAL, OFF.
WAL- Mejor concurrencia (lectores no bloquean al escritor). Recomendado para archivos.MEMORY- Journal en RAM, más rápido pero menos seguro ante caídas.DELETE- Comportamiento clásico por defecto de SQLite.
Milisegundos que SQLite espera ante un bloqueo antes de devolver SQLITE_BUSY:
'sqlite' => [
// ...
'busy_timeout' => 5000, // Espera hasta 5 segundos
]Nivel de sincronización con disco. Compromiso entre durabilidad y velocidad:
'sqlite' => [
// ...
'synchronous' => 'NORMAL',
]Valores válidos: OFF, NORMAL, FULL, EXTRA (o sus equivalentes numéricos 0, 1, 2, 3).
FULL- Máxima durabilidad (default de SQLite).NORMAL- Buen equilibrio, recomendado con WAL.OFF- El más rápido, pero puede corromper datos ante un corte de energía.
'sqlite' => [
// ...
'options' => [
PDO::ATTR_TIMEOUT => 5,
],
]El driver usa comillas dobles para identificadores (el estándar SQL):
// quoteIdentifier() genera:
"users"
"weird ""column""" // escapa comillas dobles internasSQLite también acepta backticks (por compatibilidad con MySQL), por lo que el Query Builder del núcleo funciona sin cambios sobre este driver.
SQLite soporta savepoints, así que las transacciones anidadas del TransactionManager funcionan igual que en MySQL:
$tm = db()->getTransactionManager();
$tm->begin(); // BEGIN
try {
// ... operaciones ...
$sp = $tm->begin(); // SAVEPOINT anidado
try {
// ... más operaciones ...
$tm->commit($sp); // RELEASE SAVEPOINT
} catch (Exception $e) {
$tm->rollback($sp); // ROLLBACK TO SAVEPOINT (solo lo anidado)
}
$tm->commit(); // COMMIT de la transacción externa
} catch (Exception $e) {
$tm->rollback(); // ROLLBACK completo
}SQLite es SERIALIZABLE de forma nativa. A diferencia de MySQL, no soporta READ COMMITTED ni REPEATABLE READ. Lo único que puede relajarse es a READ UNCOMMITTED (y solo tiene efecto en modo shared-cache):
$driver = new SQLiteDriver();
$driver->getSetIsolationLevelSQL('SERIALIZABLE'); // PRAGMA read_uncommitted = 0
$driver->getSetIsolationLevelSQL('READ UNCOMMITTED'); // PRAGMA read_uncommitted = 1
$driver->getSetIsolationLevelSQL('READ COMMITTED'); // ❌ InvalidArgumentException
$driver->getSetIsolationLevelSQL('REPEATABLE READ'); // ❌ InvalidArgumentExceptionPedir un nivel que SQLite no soporta lanza una InvalidArgumentException explícita, en vez de generar SQL inválido silenciosamente.
La configuración de oro para una suite de tests: rápida, aislada y sin dejar rastro en disco.
'sqlite_test' => [
'driver' => 'sqlite',
'database' => ':memory:',
'foreign_keys' => true,
]use PhobosFramework\Database\Connection\ConnectionManager;
use PhobosFramework\Database\Drivers\SQLite\SQLiteDriver;
$manager = ConnectionManager::getInstance();
$manager->registerDriver('sqlite', new SQLiteDriver());
$manager->addConnection('default', [
'driver' => 'sqlite',
'database' => ':memory:',
]);
$manager->setDefaultConnection('default');
// Crear el esquema una vez y ejercitar tus entidades contra él
$manager->getConnection()->getPDO()->exec('
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL
)
');'sqlite' => [
'driver' => 'sqlite',
'database' => storage_path('database/app.sqlite'),
'foreign_keys' => true,
'journal_mode' => 'WAL', // Mejor concurrencia
'synchronous' => 'NORMAL', // Equilibrio con WAL
'busy_timeout' => 5000, // Tolera bloqueos breves
]SQLiteDriver
├── getDSN() - Construye el DSN (archivo o :memory:)
├── getPDOOptions() - Opciones PDO seguras (heredadas de AbstractDriver)
├── configure() - Aplica PRAGMAs post-conexión
├── getName() - Retorna 'sqlite'
├── supportsSavepoints() - Retorna true
├── quoteIdentifier() - Envuelve en comillas dobles
├── getSetIsolationLevelSQL() - SERIALIZABLE / READ UNCOMMITTED (vía PRAGMA)
└── getLastInsertId() - ROWID de la última inserción (ignora la secuencia)
-
DSN Generation:
getDSN()construye la cadena- Archivo:
sqlite:/ruta/a/app.sqlite - Memoria:
sqlite::memory:
- Archivo:
-
PDO Creation: Se crea la instancia PDO con las opciones seguras por defecto (modo de error por excepción, fetch asociativo, prepares nativos).
-
Post-Connection Config:
configure()ejecuta los PRAGMAsPRAGMA foreign_keys(ON por defecto)PRAGMA journal_modesi se configuróPRAGMA busy_timeoutsi se configuróPRAGMA synchronoussi se configuró
- SQLite 3.x (vía
ext-pdo_sqlite) - Soporte completo de savepoints y claves foráneas (SQLite 3.6.19+)
- WAL disponible desde SQLite 3.7.0+
Si esperabas que un INSERT con una FK inválida fallara y no lo hace, revisa que no hayas puesto 'foreign_keys' => false. Recuerda que SQLite las trae apagadas por defecto: este driver las activa, pero solo si no las desactivas explícitamente.
SQLite bloquea el archivo completo durante una escritura. Mitiga con:
'journal_mode' => 'WAL', // Lectores no bloquean al escritor
'busy_timeout' => 5000, // Espera en vez de fallar de inmediatoSQLSTATE[HY000] [14] unable to open database file
- Verifica que el directorio del archivo exista (SQLite crea el archivo, no las carpetas).
- Verifica permisos de escritura en el directorio.
- Usa una ruta absoluta para evitar sorpresas con el directorio de trabajo.
Cada conexión PDO nueva a :memory: es una base distinta. Asegúrate de reutilizar la misma conexión (el ConnectionManager la cachea). No abras un PDO nuevo esperando ver los datos de otro.
Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.
Marcel Rojas
marcelrojas16@gmail.com
Mongoose Studio
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/amazing-feature) - Commit tus cambios (
git commit -m 'Add amazing feature') - Push a la rama (
git push origin feature/amazing-feature) - Abre un Pull Request
Phobos Framework by Mongoose Studio