Skip to content

Lightweight PHP database library with unified API for MySQL, MariaDB, PostgreSQL, SQLite & MSSQL. QueryBuilder, caching, sharding, window functions, CTEs, JSON, migrations, ActiveRecord. Zero dependencies.

License

Notifications You must be signed in to change notification settings

tommyknocker/pdo-database-class

PDOdb

PHP Version Latest Version Tests PHPStan Level 8 Coverage License Downloads GitHub Stars

PDOdb is a lightweight, framework-agnostic PHP database library providing a unified API across MySQL, MariaDB, PostgreSQL, SQLite, and Microsoft SQL Server (MSSQL).

Built on top of PDO with zero external dependencies, it offers:

Core Features:

  • Fluent Query Builder - Intuitive, chainable API for all database operations
  • Cross-Database Compatibility - Automatic SQL dialect handling (MySQL, MariaDB, PostgreSQL, SQLite, MSSQL)
  • 80+ Helper Functions - SQL helpers for strings, dates, math, JSON, aggregations, and more (REPEAT, REVERSE, LPAD, RPAD emulated for SQLite; REGEXP operations supported across all dialects)

Performance:

  • Query Caching - PSR-16 integration for result caching (10-1000x faster repeated queries)
  • Query Compilation Cache - Cache compiled SQL strings (10-30% performance improvement)
  • Prepared Statement Pool - Automatic statement caching with LRU eviction (20-50% faster repeated queries)
  • Query Performance Profiling - Built-in profiler for tracking execution times, memory usage, and slow query detection

Advanced Features:

  • Window Functions - Advanced analytics with ROW_NUMBER, RANK, LAG, LEAD, running totals, moving averages
  • Common Table Expressions (CTEs) - WITH clauses for complex queries, recursive CTEs for hierarchical data, materialized CTEs for performance optimization
  • LATERAL JOINs - Correlated subqueries in FROM clause for PostgreSQL, MySQL, and MSSQL (CROSS APPLY/OUTER APPLY)
  • Set Operations - UNION, INTERSECT, EXCEPT for combining query results with automatic deduplication
  • JSON Operations - Native JSON support with consistent API across all databases
  • Full-Text Search - Cross-database FTS with unified API (MySQL FULLTEXT, PostgreSQL tsvector, SQLite FTS5)
  • Read/Write Splitting - Horizontal scaling with master-replica architecture and load balancing
  • Sharding - Horizontal partitioning across multiple databases with automatic query routing (range, hash, modulo strategies)
  • ActiveRecord Pattern - Optional lightweight ORM for object-based database operations with relationships (hasOne, hasMany, belongsTo, hasManyThrough), eager/lazy loading, and query scopes

Developer Experience:

  • CLI Tools - Database management, user management, dump/restore, migration generator, seed generator, model generator, schema inspector, and interactive query tester (REPL)
  • Enhanced EXPLAIN - Automatic detection of full table scans, missing indexes, and optimization recommendations
  • Exception Hierarchy - Typed exceptions for precise error handling
  • Enhanced Error Diagnostics - Query context, sanitized parameters, and debug information in exceptions
  • SQL Formatter/Pretty Printer - Human-readable SQL output for debugging with indentation and line breaks
  • Query Debugging - Comprehensive debug information and query inspection tools
  • PSR-14 Event Dispatcher - Event-driven architecture for monitoring, auditing, and middleware
  • Plugin System - Extend PdoDb with custom plugins for macros, scopes, and event listeners

Production Ready:

  • Fully Tested - 2325 tests, 7759 assertions across all dialects
  • Type-Safe - PHPStan level 8 validated, PSR-12 compliant
  • Zero Memory Leaks - Production-tested memory management with automatic cursor cleanup
  • Connection Retry - Automatic retry with exponential backoff
  • Transactions & Locking - Full transaction support with table locking and savepoints for nested transactions
  • Batch Processing - Memory-efficient generators for large datasets with zero memory leaks

Additional Capabilities:

  • Bulk Operations - CSV/XML/JSON loaders, multi-row inserts, UPSERT support
  • INSERT ... SELECT - Fluent API for copying data between tables with QueryBuilder, subqueries, and CTE support
  • UPDATE/DELETE with JOIN - Update and delete operations with JOIN clauses (MySQL/MariaDB/PostgreSQL/MSSQL)
  • MERGE Statements - INSERT/UPDATE/DELETE based on match conditions (PostgreSQL/MSSQL native, MySQL/SQLite emulated)
  • Schema Introspection - Query indexes, foreign keys, and constraints programmatically
  • DDL Query Builder - Production-ready fluent API for creating, altering, and managing database schema (tables, columns, indexes, foreign keys, constraints) with Yii2-style methods, partial indexes, fulltext/spatial indexes, cross-dialectal support, and dialect-specific types (MySQL ENUM/SET, PostgreSQL UUID/JSONB/arrays, MSSQL UNIQUEIDENTIFIER/NVARCHAR, SQLite type affinity)
  • Database Migrations - Version-controlled schema changes with rollback support (Yii2-inspired)
  • Database Seeds - Populate database with initial or test data, batch tracking, rollback support
  • Advanced Pagination - Full, simple, and cursor-based pagination with metadata
  • Export Helpers - Export results to JSON, CSV, and XML formats
  • DISTINCT & DISTINCT ON - Remove duplicates with full PostgreSQL DISTINCT ON support
  • FILTER Clause - Conditional aggregates (SQL:2003 standard) with automatic MySQL fallback to CASE WHEN

Inspired by ThingEngineer/PHP-MySQLi-Database-Class and Yii2 framework


Why PDOdb?

Perfect for:

  • Beginners - Simple, intuitive API with zero configuration needed
  • Cross-database projects - Switch between MySQL, PostgreSQL, SQLite, MSSQL without code changes
  • Performance-critical apps - Built-in caching, query optimization, profiling
  • Modern PHP - Type-safe, PSR-compliant, PHP 8.4+ features

vs. Raw PDO:

  • ✅ Fluent query builder instead of manual SQL strings
  • ✅ Automatic parameter binding (SQL injection protection built-in)
  • ✅ Cross-database compatibility out of the box
  • ✅ Helper functions for common operations

vs. Eloquent/Doctrine:

  • ✅ Zero dependencies (no framework required)
  • ✅ Lightweight (no ORM overhead)
  • ✅ Direct SQL access when needed
  • ✅ Better performance for complex queries
  • ✅ Optional ActiveRecord pattern available

Table of Contents


Requirements

  • PHP: 8.4 or higher
  • PDO Extensions:
    • pdo_mysql for MySQL/MariaDB
    • pdo_pgsql for PostgreSQL
    • pdo_sqlite for SQLite
    • sqlsrv for Microsoft SQL Server (requires Microsoft ODBC Driver for SQL Server)
  • Supported Databases:
    • MySQL 5.7+ / MariaDB 10.3+
    • PostgreSQL 9.4+
    • SQLite 3.38+
    • Microsoft SQL Server 2019+ / Azure SQL Database

Check if your SQLite has JSON support:

sqlite3 :memory: "SELECT json_valid('{}')"

Installation

Install via Composer:

composer require tommyknocker/pdo-database-class

For specific versions:

# Latest 2.x version composer require tommyknocker/pdo-database-class:^2.0 # Latest 1.x version composer require tommyknocker/pdo-database-class:^1.0 # Development version composer require tommyknocker/pdo-database-class:dev-master

Quick Setup with pdodb init

Fastest way to get started: Use the interactive wizard to configure your project:

vendor/bin/pdodb init

The wizard will guide you through:

  • Database connection settings (MySQL, PostgreSQL, SQLite, MSSQL)
  • Configuration file format (.env or config/db.php)
  • Directory structure creation (migrations, models, repositories, services)
  • Connection testing
  • Advanced options (caching, table prefix, multiple connections)

See CLI Tools Documentation for more details.


📖 Documentation

Complete documentation is available in the documentation/ directory with 56+ detailed guides covering all features:

  • Getting Started - Installation, configuration, your first connection
  • Core Concepts - Connection management, query builder, parameter binding, dialects
  • Query Builder - SELECT, DML, filtering, joins, aggregations, subqueries
  • JSON Operations - Working with JSON across all databases
  • Advanced Features - Transactions, batch processing, bulk operations, UPSERT, query scopes, query macros, plugin system
  • Error Handling - Exception hierarchy, enhanced error diagnostics with query context, retry logic, logging, monitoring
  • Helper Functions - Complete reference for all helper functions
  • Best Practices - Security, performance, memory management, code organization
  • API Reference - Complete API documentation
  • Cookbook - Common patterns, real-world examples, troubleshooting

Each guide includes working code examples, dialect-specific notes, security considerations, and best practices.

Start here: Documentation Index

📚 Examples

Comprehensive, runnable examples are available in the examples/ directory:

  • Basic - Connection, CRUD, WHERE conditions
  • Intermediate - JOINs, aggregations, pagination, transactions, savepoints
  • Advanced - Connection pooling, bulk operations, UPSERT, subqueries, MERGE, window functions, CTEs
  • JSON Operations - Complete guide to JSON features
  • Helper Functions - String, math, date/time, NULL, comparison helpers
  • Performance - Query caching, compilation cache, profiling, EXPLAIN analysis
  • ActiveRecord - Object-based operations, relationships, scopes

Each example is self-contained with setup instructions. See examples/README.md for the full catalog.

Quick start:

cd examples # SQLite (ready to use, no setup required) php 01-basic/02-simple-crud.php # MySQL (update config.mysql.php with your credentials) PDODB_DRIVER=mysql php 01-basic/02-simple-crud.php # Test all examples on all available databases ./scripts/test-examples.sh

Quick Example

Fastest start: Run vendor/bin/pdodb init for interactive setup, or get started manually:

use tommyknocker\pdodb\PdoDb; // Connect (SQLite - no setup needed!) $db = new PdoDb('sqlite', ['path' => ':memory:']); // Create table $db->rawQuery('CREATE TABLE users (  id INTEGER PRIMARY KEY AUTOINCREMENT,  name TEXT,  email TEXT,  age INTEGER )'); // Insert $id = $db->find()->table('users')->insert([ 'name' => 'John', 'email' => 'john@example.com', 'age' => 30 ]); // Query $users = $db->find() ->from('users') ->where('age', 18, '>') ->orderBy('name', 'ASC') ->limit(10) ->get(); // Update $db->find() ->table('users') ->where('id', $id) ->update(['age' => 31]);

That's it! No configuration, no dependencies, just works.


5-Minute Tutorial

Step 1: Install

composer require tommyknocker/pdo-database-class

Step 2: Initialize Project

Use the interactive wizard:

vendor/bin/pdodb init

Alternative: Manual configuration

use tommyknocker\pdodb\PdoDb; // SQLite (easiest - no database server needed) $db = new PdoDb('sqlite', ['path' => ':memory:']); // Or MySQL/PostgreSQL $db = new PdoDb('mysql', [ 'host' => 'localhost', 'dbname' => 'mydb', 'username' => 'user', 'password' => 'pass' ]);

Step 3: Create Table

// Simple approach (raw SQL) $db->rawQuery('CREATE TABLE users (  id INTEGER PRIMARY KEY AUTOINCREMENT,  name TEXT,  email TEXT,  age INTEGER )'); // Or use DDL Query Builder $db->schema()->createTable('products', [ 'id' => $db->schema()->primaryKey(), 'name' => $db->schema()->string(255)->notNull(), 'status' => $db->schema()->enum(['draft', 'published', 'archived']) ->defaultValue('draft'), 'created_at' => $db->schema()->timestamp()->defaultExpression('CURRENT_TIMESTAMP') ]);

Step 4: CRUD Operations

// Create $id = $db->find()->table('users')->insert([ 'name' => 'Alice', 'email' => 'alice@example.com', 'age' => 30 ]); // Read $users = $db->find()->from('users')->get(); $user = $db->find()->from('users')->where('id', $id)->getOne(); // Update $db->find()->table('users') ->where('id', $id) ->update(['name' => 'Bob']); // Delete $db->find()->table('users')->where('id', $id)->delete();

Next: See Quick Start for more examples.


Configuration

Basic Configuration

use tommyknocker\pdodb\PdoDb; // MySQL $db = new PdoDb('mysql', [ 'host' => '127.0.0.1', 'username' => 'testuser', 'password' => 'testpass', 'dbname' => 'testdb', 'port' => 3306, 'charset' => 'utf8mb4', ]); // PostgreSQL $db = new PdoDb('pgsql', [ 'host' => '127.0.0.1', 'username' => 'testuser', 'password' => 'testpass', 'dbname' => 'testdb', 'port' => 5432, ]); // SQLite $db = new PdoDb('sqlite', [ 'path' => '/path/to/database.sqlite', // or ':memory:' for in-memory ]); // MSSQL $db = new PdoDb('sqlsrv', [ 'host' => 'localhost', 'username' => 'testuser', 'password' => 'testpass', 'dbname' => 'testdb', 'port' => 1433, ]);

Advanced Configuration

Connection Pooling:

$db = new PdoDb(); $db->addConnection('mysql_main', ['driver' => 'mysql', ...]); $db->addConnection('pgsql_analytics', ['driver' => 'pgsql', ...]); $db->connection('mysql_main')->find()->from('users')->get();

Read/Write Splitting:

$db->enableReadWriteSplitting(new RoundRobinLoadBalancer()); $db->addConnection('master', [...], ['type' => 'write']); $db->addConnection('replica-1', [...], ['type' => 'read']); // SELECTs automatically go to replicas, DML to master

Query Caching:

$cache = CacheFactory::create(['type' => 'filesystem', 'directory' => '/var/cache']); $db = new PdoDb('mysql', $config, [], null, $cache); $users = $db->find()->from('users')->cache(3600)->get();

See Configuration Documentation for complete configuration options.


Quick Start

Note: All query examples start with $db->find() which returns a QueryBuilder instance.

Basic CRUD Operations

// SELECT $user = $db->find() ->from('users') ->where('id', 10) ->getOne(); $users = $db->find() ->from('users') ->where('age', 18, '>=') ->get(); // INSERT $id = $db->find()->table('users')->insert([ 'name' => 'Alice', 'email' => 'alice@example.com', 'age' => 30 ]); // UPDATE $db->find() ->table('users') ->where('id', $id) ->update(['age' => 31]); // DELETE $db->find() ->table('users') ->where('id', $id) ->delete();

Filtering and Joining

use tommyknocker\pdodb\helpers\Db; // WHERE conditions $users = $db->find() ->from('users') ->where('status', 'active') ->andWhere('age', 18, '>') ->andWhere(Db::like('email', '%@example.com')) ->get(); // JOIN and GROUP BY $stats = $db->find() ->from('users AS u') ->select(['u.id', 'u.name', 'total' => Db::sum('o.amount')]) ->leftJoin('orders AS o', 'o.user_id = u.id') ->groupBy('u.id') ->having(Db::sum('o.amount'), 1000, '>') ->get();

Transactions

$db->startTransaction(); try { $userId = $db->find()->table('users')->insert(['name' => 'Alice']); $db->find()->table('orders')->insert(['user_id' => $userId, 'total' => 100]); $db->commit(); } catch (\Exception $e) { $db->rollback(); }

See Query Builder Documentation for more examples.


JSON Operations

PDOdb provides a unified JSON API that works consistently across all databases.

use tommyknocker\pdodb\helpers\Db; // Create JSON data $db->find()->table('users')->insert([ 'name' => 'John', 'meta' => Db::jsonObject(['city' => 'NYC', 'age' => 30]), 'tags' => Db::jsonArray('php', 'mysql', 'docker') ]); // Query JSON $adults = $db->find() ->from('users') ->where(Db::jsonPath('meta', ['age'], '>', 25)) ->get(); // Extract JSON values $users = $db->find() ->from('users') ->select([ 'id', 'name', 'city' => Db::jsonGet('meta', ['city']) ]) ->get(); // Update JSON $db->find() ->table('users') ->where('id', 1) ->update([ 'meta' => Db::jsonSet('meta', ['city'], 'London') ]);

See JSON Operations Documentation for complete guide.


Advanced Usage

Raw Queries

$users = $db->rawQuery( 'SELECT * FROM users WHERE age > :age', ['age' => 18] );

Subqueries

$users = $db->find() ->from('users') ->whereIn('id', function($query) { $query->from('orders') ->select('user_id') ->where('total', 1000, '>'); }) ->get();

Pagination

// Full pagination (with total count) $result = $db->find() ->from('posts') ->orderBy('created_at', 'DESC') ->paginate(20, 1); // Cursor pagination (most efficient) $result = $db->find() ->from('posts') ->orderBy('id', 'DESC') ->cursorPaginate(20);

Batch Processing

// Process in batches foreach ($db->find()->from('users')->batch(100) as $batch) { foreach ($batch as $user) { processUser($user); } } // Stream results (minimal memory) foreach ($db->find()->from('users')->stream() as $user) { processUser($user); }

Query Caching

// Cache for 1 hour $products = $db->find() ->from('products') ->where('category', 'Electronics') ->cache(3600) ->get();

See Advanced Features Documentation for complete guide.


CLI Tools

PDOdb provides convenient command-line tools for common development tasks:

vendor/bin/pdodb <command> [subcommand] [arguments] [options]

Available Commands

  • db - Manage databases (create, drop, list, check existence)
  • user - Manage database users (create, drop, grant/revoke privileges)
  • dump - Dump and restore database (with compression, auto-naming, rotation)
  • migrate - Manage database migrations
  • schema - Inspect database schema
  • query - Test SQL queries interactively (REPL)
  • model - Generate ActiveRecord models
  • table - Manage tables (info, create, drop, truncate)
  • monitor - Monitor database queries and performance

Bash Completion

Install bash completion for enhanced CLI experience:

# Temporary (current session) source <(curl -s https://raw.githubusercontent.com/tommyknocker/pdo-database-class/refs/heads/master/scripts/pdodb-completion.bash) # Permanent curl -o ~/.pdodb-completion.bash https://raw.githubusercontent.com/tommyknocker/pdo-database-class/refs/heads/master/scripts/pdodb-completion.bash echo "source ~/.pdodb-completion.bash" >> ~/.bashrc

Examples

# Create database vendor/bin/pdodb db create myapp # Dump with compression and rotation vendor/bin/pdodb dump --auto-name --compress=gzip --rotate=7 # Create migration vendor/bin/pdodb migrate create create_users_table # Generate model vendor/bin/pdodb model make User users app/Models

See CLI Tools Documentation for complete guide.


Error Handling

PDOdb provides a comprehensive exception hierarchy for better error handling:

use tommyknocker\pdodb\exceptions\{ DatabaseException, ConnectionException, QueryException, ConstraintViolationException, TransactionException }; try { $users = $db->find()->from('users')->get(); } catch (ConnectionException $e) { // Handle connection errors if ($e->isRetryable()) { // Implement retry logic } } catch (QueryException $e) { // Handle query errors error_log("Query: " . $e->getQuery()); error_log("Context: " . $e->getDescription()); } catch (ConstraintViolationException $e) { // Handle constraint violations error_log("Constraint: " . $e->getConstraintName()); }

All exceptions extend PDOException for backward compatibility and provide rich context information.

See Error Handling Documentation for complete guide.


Performance Tips

Enable Query Caching

For applications with repeated queries, enable result caching:

$cache = new Psr16Cache(new FilesystemAdapter()); $db = new PdoDb('mysql', $config, [], null, $cache); $products = $db->find() ->from('products') ->where('category', 'Electronics') ->cache(3600) ->get();

Performance Impact: 65-97% faster for repeated queries with cache hits.

Use Batch Operations

// ❌ Slow: Multiple single inserts foreach ($users as $user) { $db->find()->table('users')->insert($user); } // ✅ Fast: Single batch insert $db->find()->table('users')->insertMulti($users);

Always Limit Result Sets

// ✅ Safe: Limited results $users = $db->find()->from('users')->limit(1000)->get();

Use Batch Processing for Large Datasets

// Process in chunks foreach ($db->find()->from('users')->batch(100) as $batch) { processBatch($batch); }

See Performance Documentation for more tips.


Helper Functions

PDOdb provides 80+ helper functions for common SQL operations:

Core Helpers:

  • Db::raw() - Raw SQL expressions
  • Db::concat() - String concatenation
  • Db::now() - Current timestamp

String Operations:

  • Db::upper(), Db::lower(), Db::trim(), Db::substring(), Db::replace()

Numeric Operations:

  • Db::inc(), Db::dec(), Db::abs(), Db::round(), Db::mod()

Date/Time Functions:

  • Db::now(), Db::date(), Db::year(), Db::month(), Db::day()

JSON Operations:

  • Db::jsonObject(), Db::jsonArray(), Db::jsonGet(), Db::jsonPath(), Db::jsonContains()

Aggregate Functions:

  • Db::count(), Db::sum(), Db::avg(), Db::min(), Db::max()

Full Reference: See Helper Functions Documentation for complete list and examples.


API Reference

PdoDb Main Class

Method Description
find() Returns QueryBuilder instance
rawQuery(string, array) Execute raw SQL, returns array of rows
rawQueryOne(string, array) Execute raw SQL, returns first row
startTransaction() Begin transaction
commit() Commit transaction
rollBack() Roll back transaction
describe(string) Get table structure
indexes(string) Get all indexes for a table
keys(string) Get foreign key constraints

QueryBuilder Methods

Table & Selection:

  • table(string) / from(string) - Set target table
  • select(array|string) - Specify columns to select

Filtering:

  • where(...) / andWhere(...) / orWhere(...) - Add WHERE conditions
  • whereIn(...) / whereNotIn(...) - IN / NOT IN conditions
  • whereNull(...) / whereNotNull(...) - NULL checks
  • whereBetween(...) - BETWEEN conditions
  • join(...) / leftJoin(...) / rightJoin(...) - Add JOIN clauses

Data Manipulation:

  • insert(array) - Insert single row
  • insertMulti(array) - Insert multiple rows
  • update(array) - Update rows
  • delete() - Delete rows

Execution:

  • get() - Execute SELECT, return all rows
  • getOne() - Execute SELECT, return first row
  • getValue() - Execute SELECT, return single value

Full Reference: See API Reference Documentation for complete method list and signatures.


Dialect Differences

PDOdb handles most differences automatically, but here are some key points:

UPSERT:

  • MySQL: ON DUPLICATE KEY UPDATE
  • PostgreSQL/SQLite: ON CONFLICT ... DO UPDATE SET

Use onDuplicate() for portable UPSERT:

$db->find()->table('users')->onDuplicate([ 'age' => Db::inc() ])->insert(['email' => 'user@example.com', 'age' => 25]);

JSON Functions:

  • MySQL: Uses JSON_EXTRACT, JSON_CONTAINS
  • PostgreSQL: Uses ->, ->>, @> operators
  • SQLite: Uses json_extract, json_each

All handled transparently through Db::json*() helpers.

Full Reference: See Dialect Differences Documentation for complete guide.


Frequently Asked Questions

Is PDOdb an ORM?

No, PDOdb is a query builder with optional ActiveRecord pattern. It's lighter than full ORMs like Eloquent or Doctrine.

Can I use raw SQL?

Yes! Use rawQuery() for complete control:

$users = $db->rawQuery('SELECT * FROM users WHERE age > :age', ['age' => 18]);

Does it work with frameworks?

Yes! PDOdb is framework-agnostic. Works with Laravel, Symfony, Yii, or no framework at all.

Is it production-ready?

Yes! 2325+ tests, PHPStan level 8, used in production environments.

What about security?

All queries use prepared statements automatically. SQL injection protection is built-in.

Can I use it with existing PDO connections?

Yes! Pass your PDO instance:

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); $db = new PdoDb('mysql', ['pdo' => $pdo]);

Which database should I use for development?

SQLite is perfect for development - no server setup needed:

$db = new PdoDb('sqlite', ['path' => ':memory:']);

Does it support transactions?

Yes! Full transaction support with savepoints:

$db->startTransaction(); try { // Your operations $db->commit(); } catch (\Exception $e) { $db->rollBack(); }

Migration Guide

From Raw PDO

Before:

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); $stmt = $pdo->prepare('SELECT * FROM users WHERE age > :age'); $stmt->execute(['age' => 18]); $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

After:

$db = new PdoDb('mysql', [ 'host' => 'localhost', 'dbname' => 'test', 'username' => 'user', 'password' => 'pass' ]); $users = $db->find()->from('users')->where('age', 18, '>')->get();

From Eloquent (Laravel)

Before:

User::where('active', 1) ->where('age', '>', 18) ->orderBy('name') ->limit(10) ->get();

After:

$db->find() ->from('users') ->where('active', 1) ->andWhere('age', 18, '>') ->orderBy('name', 'ASC') ->limit(10) ->get();

See Migration Guide Documentation for more examples.


Troubleshooting

"Driver not found" Error

Solution: Install the required PHP extension:

sudo apt-get install php8.4-mysql php8.4-pgsql php8.4-sqlite3

"JSON functions not available" (SQLite)

Solution: Check if JSON support is available:

sqlite3 :memory: "SELECT json_valid('{}')"

"SQLSTATE[HY000]: General error: 1 near 'OFFSET'"

Problem: Using OFFSET without LIMIT in SQLite.

Solution: Always use LIMIT with OFFSET:

// ✅ Works $db->find()->from('users')->limit(20)->offset(10)->get();

Memory Issues with Large Result Sets

Solution: Use batch processing or streaming:

foreach ($db->find()->from('users')->batch(100) as $batch) { processBatch($batch); }

See Troubleshooting Documentation for more solutions.


Testing

The project includes comprehensive PHPUnit tests for all supported databases.

Running Tests

# Run all tests ./vendor/bin/phpunit # Run specific dialect tests ./vendor/bin/phpunit tests/PdoDbMySQLTest.php # Run with coverage ./vendor/bin/phpunit --coverage-html coverage

Test Requirements

  • MySQL/MariaDB: Running instance on localhost:3306
  • PostgreSQL: Running instance on localhost:5432
  • SQLite: No setup required (uses :memory:)
  • MSSQL: Running instance on localhost:1433

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Open an issue first for new features or bug reports
  2. Include failing tests that demonstrate the problem
  3. Follow PSR-12 coding standards
  4. Write tests for all new functionality
  5. Test against all five dialects (MySQL, MariaDB, PostgreSQL, SQLite, MSSQL)

Pull Request Process

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is open source. See LICENSE file for details.


Acknowledgments

Inspired by ThingEngineer/PHP-MySQLi-Database-Class and Yii2 framework

Built with ❤️ for the PHP community.

About

Lightweight PHP database library with unified API for MySQL, MariaDB, PostgreSQL, SQLite & MSSQL. QueryBuilder, caching, sharding, window functions, CTEs, JSON, migrations, ActiveRecord. Zero dependencies.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •