PHPackages                             youssefmansour9/audit-trail-package - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. youssefmansour9/audit-trail-package

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

youssefmansour9/audit-trail-package
===================================

Production-grade audit trail package for tracking entity changes with Clean Architecture, CQRS, and PDO storage

v1.1.2(1mo ago)09MITPHPPHP &gt;=8.3CI passing

Since Jun 6Pushed 1mo agoCompare

[ Source](https://github.com/YoussefMansour9/audit-trail-package)[ Packagist](https://packagist.org/packages/youssefmansour9/audit-trail-package)[ RSS](/packages/youssefmansour9-audit-trail-package/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (4)Used By (0)

Audit Trail Package
===================

[](#audit-trail-package)

[![PHP](https://camo.githubusercontent.com/57e7ffbb2feed38547ae7ffe62bb05368ca547bf63844e19a57c1ed9bd667b14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e332b2d3666346338653f6c6f676f3d706870)](https://php.net)[![CI](https://github.com/YoussefMansour9/audit-trail-package/actions/workflows/ci.yml/badge.svg)](https://github.com/YoussefMansour9/audit-trail-package/actions/workflows/ci.yml)[![PHPStan](https://camo.githubusercontent.com/942bdbddc7b2adea1d63ed80793492d06d72ef41911edcba33310d0745581548/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d4c6576656c253230392d627269676874677265656e)](https://phpstan.org)[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](LICENSE)

A **production-grade** Composer package for tracking entity changes with complete audit history. Built with **Clean Architecture**, **CQRS**, **Repository Pattern**, and **Dependency Injection**.

---

Features
--------

[](#features)

- **Record** entity changes (CREATE, UPDATE, DELETE) with full before/after state
- **Query** audit history by entity, user, date range, or action type
- **Immutable entries** — once written, never modified
- **Batch recording** with all-or-nothing validation
- **Storage-agnostic** architecture (PDO implementation included)
- **PSR-3 logging** — plug any logger (Monolog, Sentry, etc.)
- **PHP 8.3+** with strict types everywhere
- **Zero impact** on your entity tables — audit data is stored separately

---

Requirements
------------

[](#requirements)

- PHP 8.3+
- PDO extension (for the included MySQL implementation)
- MySQL 5.7+ or MariaDB 10.2+ (for JSON column support)

---

Installation
------------

[](#installation)

```
composer require youssefmansour9/audit-trail-package
```

---

Quick Start
-----------

[](#quick-start)

```
use AuditTrail\AuditTrail;

// 1. Connect to your database
$pdo = new \PDO('mysql:host=127.0.0.1;dbname=myapp;charset=utf8mb4', 'root', '');
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

// 2. Create the audit_log table (see src/Infrastructure/Persistence/Schema/mysql.sql)
//    or run: mysql -u root myapp < vendor/youssefmansour9/audit-trail-package/src/Infrastructure/Persistence/Schema/mysql.sql

// 3. Create the audit trail instance
$auditTrail = AuditTrail::createWithPdo($pdo);

// 4. Start tracking
$oldState = ['status' => 'pending', 'total' => 100.00];
$newState = ['status' => 'shipped', 'total' => 100.00];

$entry = $auditTrail->recordChange(
    aggregateType: 'order',
    aggregateId: '42',
    action: 'UPDATE',
    oldState: $oldState,
    newState: $newState,
    performedBy: 'user-abc-123',
);

// 5. Query history
$history = $auditTrail->getHistory('order', '42');
```

---

Architecture
------------

[](#architecture)

```
┌─────────────────────────────────────────────────┐
│                Consumer Code                     │
│  (Controller, CLI, Framework)                    │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────┐
│            AuditTrail (Facade)                   │
│  Public API — the only class you instantiate     │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────┐
│            AuditService (Application)            │
│  Orchestrates use cases, validates transitions   │
│  Depends only on interfaces (ports)              │
└──────────────────────┬──────────────────────────┘
                       │
          ┌────────────┴────────────┐
          ▼                         ▼
┌──────────────────┐     ┌──────────────────┐
│ AuditRepository  │     │ LoggerInterface  │
│   (Port/Interface)│     │  (PSR-3 Port)    │
└──────────────────┘     └──────────────────┘
          │                         │
          ▼                         ▼
┌──────────────────┐     ┌──────────────────┐
│ PDOAuditRepo     │     │ Monolog / Null   │
│  (Infrastructure)│     │  (Infrastructure) │
└──────────────────┘     └──────────────────┘

```

### Clean Architecture layers

[](#clean-architecture-layers)

LayerFolderResponsibility**Domain**`src/Domain/`Business value objects, enums, exceptions. Zero dependencies.**Port**`src/Port/`Interface contracts (Repository, Logger). Inverts dependencies.**Application**`src/Application/`Use-case orchestration, validation rules. Depends only on Ports.**Infrastructure**`src/Infrastructure/`Concrete implementations (PDO). Swappable.**Facade**`src/AuditTrail.php`Single entry point. DI-friendly.### Design patterns

[](#design-patterns)

PatternWherePurposeClean ArchitectureEntire structureSeparation of concerns, testability, swappabilityCQRS`AuditRepository`Command methods (`append`) separated from Query methods (`findBy*`)Repository`AuditRepository` + `PDOAuditRepository`Abstracts storage behind a collection-like interfaceDependency InjectionEvery classDependencies provided via constructor, never created internallyValue Object`AuditEntry`Immutable, self-validating, equality by dataFacade`AuditTrail`Simplified public API hiding internal complexityStatic Factory`AuditEntry::record()`, `AuditEntry::fromArray()`Named constructors for different creation contextsPrimary Constructor`AuditEntry`Private constructor + public named constructorsAdapter`PDOAuditRepository`Adapts PDO to the Repository interfaceNull Object`NullLogger`Default no-op logger when none provided---

Usage
-----

[](#usage)

### Recording changes

[](#recording-changes)

```
// CREATE — oldState must be null, newState must be provided
$auditTrail->recordChange('order', '42', 'CREATE', null, ['status' => 'pending'], 'user-1');

// UPDATE — both oldState and newState must be provided
$auditTrail->recordChange('order', '42', 'UPDATE', $oldState, $newState, 'user-1');

// DELETE — newState must be null, oldState must be provided
$auditTrail->recordChange('order', '42', 'DELETE', $oldState, null, 'user-1');
```

### Batch recording (atomic)

[](#batch-recording-atomic)

```
use AuditTrail\Application\DTO\ChangeRequest;

$auditTrail->recordBatch([
    new ChangeRequest('order', '1', 'CREATE', null,             ['status' => 'pending'],  'user-1'),
    new ChangeRequest('order', '1', 'UPDATE', ['status' => 'pending'], ['status' => 'shipped'], 'user-1'),
    new ChangeRequest('order', '1', 'DELETE', ['status' => 'shipped'], null,                     'user-1'),
]);
```

All entries are validated before any are persisted. If the second entry fails validation, **no** entries are written.

### Querying history

[](#querying-history)

```
// Full revision history of an entity (oldest first)
$history = $auditTrail->getHistory('order', '42');
foreach ($history as $entry) {
    echo $entry->performedAt()->format('Y-m-d H:i:s') . ' — ' . $entry->action()->value;
}

// Specific entry by ID
$entry = $auditTrail->getEntry('uuid-here');

// All changes by a user (newest first)
$entries = $auditTrail->getEntriesByUser('user-abc-123');

// All changes in a date range
$entries = $auditTrail->getEntriesByDateRange(
    new \DateTimeImmutable('-7 days'),
    new \DateTimeImmutable('now'),
);

// Count changes for an entity
$count = $auditTrail->countByAggregate('order', '42');
```

### With Monolog

[](#with-monolog)

```
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use AuditTrail\AuditTrail;

$logger = new Logger('audit');
$logger->pushHandler(new StreamHandler('/var/log/audit.log', Logger::INFO));

// Pass it to the factory
$auditTrail = AuditTrail::createWithPdo($pdo, $logger);

// Or wire manually with any PSR-3 logger
use AuditTrail\Infrastructure\RandomBytesIdGenerator;

$service = new AuditService(
    new PDOAuditRepository($pdo),
    $logger,
    new RandomBytesIdGenerator(),
);
$auditTrail = new AuditTrail($service);
```

---

Database Schema
---------------

[](#database-schema)

```
CREATE TABLE audit_log (
    id              VARCHAR(36)    NOT NULL PRIMARY KEY,
    aggregate_type  VARCHAR(255)   NOT NULL,
    aggregate_id    VARCHAR(255)   NOT NULL,
    action          VARCHAR(10)    NOT NULL,
    old_state       JSON           DEFAULT NULL,
    new_state       JSON           DEFAULT NULL,
    performed_by    VARCHAR(255)   NOT NULL,
    performed_at    DATETIME(6)    NOT NULL,
    metadata        JSON           DEFAULT NULL,

    INDEX idx_audit_aggregate   (aggregate_type, aggregate_id),
    INDEX idx_audit_performed_by (performed_by),
    INDEX idx_audit_performed_at (performed_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

The schema file is located at `src/Infrastructure/Persistence/Schema/mysql.sql`.

### Column guide

[](#column-guide)

ColumnTypePurpose`id`UUID v4Unique identifier for the audit entry`aggregate_type`stringEntity type (e.g., "order", "user", "product")`aggregate_id`stringEntity identifier (e.g., "42", "uuid-value")`action`enum stringOne of: CREATE, UPDATE, DELETE`old_state`JSON or nullThe entity state before this change`new_state`JSON or nullThe entity state after this change`performed_by`stringWho performed the action`performed_at`datetime(6)Microsecond-precision timestamp`metadata`JSON or nullExtra context (IP, request ID, tags)---

Testing
-------

[](#testing)

```
# Run all tests
vendor/bin/phpunit

# Run static analysis
vendor/bin/phpstan analyse src --level 9

# Code coverage (requires xdebug)
vendor/bin/phpunit --coverage-html coverage/
```

Tests use **SQLite in-memory** — no database server required. The test suite runs on CI with zero external dependencies.

### Test structure

[](#test-structure)

Test suiteTestsWhat it covers`Domain\ActionTest`6Enum values, cases, from/tryFrom`Domain\AuditEntryTest`20Creation, serialization, reconstruction, validation`Domain\Exception\ExceptionTest`5Exception hierarchy and messages`Application\AuditServiceTest`17Use cases, validation rules, batch atomicity`Infrastructure\PDOAuditRepositoryTest`17Full CRUD with SQLite, ordering, pagination, batch rollback`AuditTrailTest`8Public facade delegation, createWithPdo factory---

PHPStan
-------

[](#phpstan)

This project enforces **level 9** (the maximum — property type hints, return type hints, generic array annotations, mixed type warnings, and strict comparison rules):

```
vendor/bin/phpstan analyse src --level 9
```

---

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md).

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

Copyright (c) 2026 Youssef Mansour

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d6d3694558eab3a95270545282b736db09669a7ff96b37d696444c18370c775b?d=identicon)[YoussefMansour9](/maintainers/YoussefMansour9)

---

Tags

loggingAuditcqrsaudit-trailclean architectureentity-history

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/youssefmansour9-audit-trail-package/health.svg)

```
[![Health](https://phpackages.com/badges/youssefmansour9-audit-trail-package/health.svg)](https://phpackages.com/packages/youssefmansour9-audit-trail-package)
```

###  Alternatives

[sentry/sentry

PHP SDK for Sentry (http://sentry.io)

1.9k247.1M346](/packages/sentry-sentry)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k54](/packages/ecotone-ecotone)[illuminate/log

The Illuminate Log package.

6225.3M647](/packages/illuminate-log)[pagemachine/typo3-formlog

Form log for TYPO3

23238.6k8](/packages/pagemachine-typo3-formlog)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
