Skip to content

v2.8.0

Choose a tag to compare

@tommyknocker tommyknocker released this 01 Nov 00:22
· 357 commits to master since this release

🚀 PDOdb v2.8.0 Release

Release Date: November 1, 2025

Overview

PDOdb v2.8.0 introduces major new features: ActiveRecord Pattern, Query Performance Profiling, Materialized CTEs, and PSR-14 Event Dispatcher Integration. Also includes improvements to exception handling, memory management, and code quality.

🎯 Major Features

✨ ActiveRecord Pattern (Optional ORM)

Lightweight ORM for object-based database operations:

  • Magic attribute access - $user->name, $user->email
  • Automatic CRUD - save(), delete(), refresh()
  • Dirty tracking - Automatically tracks changed attributes
  • Declarative validation - Rules-based validation with extensible validators
  • Lifecycle events - PSR-14 events for save, insert, update, delete
  • ActiveQuery builder - Full QueryBuilder API through find() method
use tommyknocker\pdodb\orm\Model;

class User extends Model
{
    public static function tableName(): string
    {
        return 'users';
    }
    
    public static function rules(): array
    {
        return [
            [['name', 'email'], 'required'],
            ['email', 'email'],
        ];
    }
}

$user = new User();
$user->name = 'Alice';
$user->email = 'alice@example.com';
$user->save();

📚 Documentation: documentation/05-advanced-features/active-record.md
📖 Examples: examples/23-active-record/


📊 Query Performance Profiling

Built-in profiler for performance analysis:

  • Automatic tracking of all query executions
  • Execution time measurement (total, average, min, max)
  • Memory usage tracking per query
  • Slow query detection with configurable threshold
  • Query grouping by SQL structure
  • PSR-3 logger integration
$db->enableProfiling(0.5); // Threshold: 0.5 seconds

// Execute queries (automatically tracked)
$users = $db->find()->from('users')->get();

// Get statistics
$stats = $db->getProfilerStats(true);
echo "Avg time: " . round($stats['avg_time'] * 1000, 2) . " ms\n";

// Get slowest queries
$slowest = $db->getSlowestQueries(10);

📚 Documentation: documentation/05-advanced-features/query-profiling.md
📖 Examples: examples/21-query-profiling/


⚡ Materialized CTE Support

Performance optimization for expensive CTE queries:

  • PostgreSQL: Uses MATERIALIZED keyword
  • MySQL: Uses optimizer hints
  • Automatically caches expensive CTE computations
$results = $db->find()
    ->withMaterialized('customer_stats', function ($q) {
        $q->from('orders')
          ->select([
              'customer_id',
              'order_count' => Db::count('*'),
              'total_spent' => Db::sum('amount'),
          ])
          ->groupBy('customer_id');
    })
    ->from('customers')
    ->join('customer_stats', 'customers.id = customer_stats.customer_id')
    ->where('customer_stats.total_spent', 1000, '>')
    ->get();

📚 Documentation: documentation/03-query-builder/cte.md
📖 Examples: examples/17-cte/03-materialized-cte.php


🎪 PSR-14 Event Dispatcher Integration

Event-driven architecture for monitoring, auditing, and middleware:

  • ConnectionOpenedEvent - When connection is opened
  • QueryExecutedEvent - After successful query execution
  • QueryErrorEvent - When query error occurs
  • TransactionStartedEvent, TransactionCommittedEvent, TransactionRolledBackEvent
use tommyknocker\pdodb\events\QueryExecutedEvent;

$dispatcher->addListener(QueryExecutedEvent::class, function (QueryExecutedEvent $event) {
    echo sprintf(
        "Query: %s (%.2f ms, %d rows)\n",
        substr($event->getSql(), 0, 50),
        $event->getExecutionTime(),
        $event->getRowsAffected()
    );
});

📖 Examples: examples/19-events/


🔧 Improvements

Exception Handling

  • 113 new tests for exception handling (ExceptionTests, ErrorDetectionStrategyTests, ConstraintParserTests, ErrorCodeRegistryTests)
  • Improved ConstraintParser with better pattern matching
  • Enhanced constraint name extraction from error messages
  • Better handling of FOREIGN KEY and schema.table formats

Memory Management

  • Fixed memory leaks by properly closing PDOStatement cursors
  • All fetch methods automatically close cursors
  • Exception-safe cleanup using try/finally blocks
  • Production-tested with 50,000+ queries without memory accumulation

Code Quality

  • Infection mutation testing integrated for code quality assurance
  • SHA-256 hashing replaces all MD5 usage for better security
  • Simplified external reference detection (KISS/YAGNI refactoring)
  • Query compilation cache improvements
  • Zero skipped tests policy - all tests actively run

Bug Fixes

  • SQLite cache parameter validation (prevents invalid DSN parameters)
  • SQL identifier quoting in FileLoader for different dialects
  • LATERAL JOIN improvements with better external reference detection
  • Markdown EOF formatting consistency

📈 Statistics

  • Tests: 991 tests, 3951 assertions (+417 tests, +1425 assertions from 2.7.1)
  • Examples: 147/147 passing (49 files × 3 dialects each)
  • PHPStan: Level 8, zero errors
  • Backward Compatibility: 100% maintained

🔗 Links


⬇️ Installation

composer require tommyknocker/pdo-database-class:^2.8.0

🙏 Thank You

Special thanks to all contributors and users who helped make this release possible!


Full Changelog: v2.7.1...v2.8.0