PDOdb is a lightweight, framework-agnostic PHP database library providing a unified API across MySQL, MariaDB, PostgreSQL, SQLite, Microsoft SQL Server (MSSQL), and Oracle.
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, Oracle)
- 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
Perfect for:
- ✅ Beginners - Simple, intuitive API with zero configuration needed
- ✅ Cross-database projects - Switch between MySQL, PostgreSQL, SQLite, MSSQL, Oracle 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
- Requirements
- Installation
- 📖 Documentation
- 📚 Examples
- Quick Example
- 5-Minute Tutorial
- Configuration
- Quick Start
- JSON Operations
- Advanced Usage
- CLI Tools
- Error Handling
- Performance Tips
- Helper Functions
- API Reference
- Dialect Differences
- Frequently Asked Questions
- Migration Guide
- Troubleshooting
- Testing
- Contributing
- License
- PHP: 8.4 or higher
- PDO Extensions:
pdo_mysqlfor MySQL/MariaDBpdo_pgsqlfor PostgreSQLpdo_sqlitefor SQLitesqlsrvfor Microsoft SQL Server (requires Microsoft ODBC Driver for SQL Server)ocifor Oracle Database
- Supported Databases:
- MySQL 5.7+ / MariaDB 10.3+
- PostgreSQL 9.4+
- SQLite 3.38+
- Microsoft SQL Server 2019+ / Azure SQL Database
- Oracle Database 12c+
Check if your SQLite has JSON support:
sqlite3 :memory: "SELECT json_valid('{}')"Install via Composer:
composer require tommyknocker/pdo-database-classFor 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-masterFastest way to get started: Use the interactive wizard to configure your project:
vendor/bin/pdodb initThe wizard will guide you through:
- Database connection settings (MySQL, PostgreSQL, SQLite, MSSQL, Oracle)
- Configuration file format (
.envorconfig/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.
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
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.shFastest 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.
composer require tommyknocker/pdo-database-classUse the interactive wizard:
vendor/bin/pdodb initAlternative: 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'
]);// 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')
]);// 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.
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,
]);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 masterQuery 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.
Note: All query examples start with $db->find() which returns a QueryBuilder instance.
// 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();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();$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.
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.
$users = $db->rawQuery(
'SELECT * FROM users WHERE age > :age',
['age' => 18]
);$users = $db->find()
->from('users')
->whereIn('id', function($query) {
$query->from('orders')
->select('user_id')
->where('total', 1000, '>');
})
->get();// 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);// 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);
}// Cache for 1 hour
$products = $db->find()
->from('products')
->where('category', 'Electronics')
->cache(3600)
->get();See Advanced Features Documentation for complete guide.
PDOdb provides convenient command-line tools for common development tasks:
vendor/bin/pdodb <command> [subcommand] [arguments] [options]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 migrationsschema- Inspect database schemaquery- Test SQL queries interactively (REPL)model- Generate ActiveRecord modelstable- Manage tables (info, create, drop, truncate)monitor- Monitor database queries and performance
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# 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/ModelsSee CLI Tools Documentation for complete guide.
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.
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.
// ❌ Slow: Multiple single inserts
foreach ($users as $user) {
$db->find()->table('users')->insert($user);
}
// ✅ Fast: Single batch insert
$db->find()->table('users')->insertMulti($users);// ✅ Safe: Limited results
$users = $db->find()->from('users')->limit(1000)->get();// Process in chunks
foreach ($db->find()->from('users')->batch(100) as $batch) {
processBatch($batch);
}See Performance Documentation for more tips.
PDOdb provides 80+ helper functions for common SQL operations:
Core Helpers:
Db::raw()- Raw SQL expressionsDb::concat()- String concatenationDb::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.
| 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 |
Table & Selection:
table(string)/from(string)- Set target tableselect(array|string)- Specify columns to select
Filtering:
where(...)/andWhere(...)/orWhere(...)- Add WHERE conditionswhereIn(...)/whereNotIn(...)- IN / NOT IN conditionswhereNull(...)/whereNotNull(...)- NULL checkswhereBetween(...)- BETWEEN conditionsjoin(...)/leftJoin(...)/rightJoin(...)- Add JOIN clauses
Data Manipulation:
insert(array)- Insert single rowinsertMulti(array)- Insert multiple rowsupdate(array)- Update rowsdelete()- Delete rows
Execution:
get()- Execute SELECT, return all rowsgetOne()- Execute SELECT, return first rowgetValue()- Execute SELECT, return single value
Full Reference: See API Reference Documentation for complete method list and signatures.
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.
No, PDOdb is a query builder with optional ActiveRecord pattern. It's lighter than full ORMs like Eloquent or Doctrine.
Yes! Use rawQuery() for complete control:
$users = $db->rawQuery('SELECT * FROM users WHERE age > :age', ['age' => 18]);Yes! PDOdb is framework-agnostic. Works with Laravel, Symfony, Yii, or no framework at all.
Yes! 2325+ tests, PHPStan level 8, used in production environments.
All queries use prepared statements automatically. SQL injection protection is built-in.
Yes! Pass your PDO instance:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$db = new PdoDb('mysql', ['pdo' => $pdo]);SQLite is perfect for development - no server setup needed:
$db = new PdoDb('sqlite', ['path' => ':memory:']);Yes! Full transaction support with savepoints:
$db->startTransaction();
try {
// Your operations
$db->commit();
} catch (\Exception $e) {
$db->rollBack();
}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();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.
Solution: Install the required PHP extension:
sudo apt-get install php8.4-mysql php8.4-pgsql php8.4-sqlite3Solution: Check if JSON support is available:
sqlite3 :memory: "SELECT json_valid('{}')"Problem: Using OFFSET without LIMIT in SQLite.
Solution: Always use LIMIT with OFFSET:
// ✅ Works
$db->find()->from('users')->limit(20)->offset(10)->get();Solution: Use batch processing or streaming:
foreach ($db->find()->from('users')->batch(100) as $batch) {
processBatch($batch);
}See Troubleshooting Documentation for more solutions.
The project includes comprehensive PHPUnit tests for all supported databases.
# 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- 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
- Oracle: Running instance on localhost:1521 (requires Oracle Instant Client and
pdo_ociextension)
Contributions are welcome! Please follow these guidelines:
- Open an issue first for new features or bug reports
- Include failing tests that demonstrate the problem
- Follow PSR-12 coding standards
- Write tests for all new functionality
- Test against all six dialects (MySQL, MariaDB, PostgreSQL, SQLite, MSSQL, Oracle)
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is open source. See LICENSE file for details.
Inspired by ThingEngineer/PHP-MySQLi-Database-Class and Yii2 framework
Built with ❤️ for the PHP community.