Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Auditor Laravel Package

damienharper/auditor

View on GitHub
Deep Wiki
Context7

Upgrading to 4.x from 3.x

PHP 8.4+ modernization and new features

This document describes the backward incompatible changes introduced in auditor 4.0 and how to adapt your code.

⚠️ Requirements Changes

PHP Version

[!IMPORTANT] Minimum PHP version is now 8.4 (was 8.2 in 3.x)

This is required by Symfony 8.0 and leverages PHP 8.4 features.

Symfony Version

[!IMPORTANT] Minimum Symfony version is now 8.0 (was 5.4 in 3.x)

Support for Symfony 5.4, 6.4, and 7.x has been dropped.

Doctrine Versions

Package 3.x Version 4.x Version
Doctrine DBAL >= 3.2 >= 4.0
Doctrine ORM >= 2.13 >= 3.2

PHPUnit Version

PHPUnit minimum version is now 12.0 (was 11.0 in 3.x)

🗑️ Removed Methods

DoctrineHelper

The following methods have been removed from DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper:

Removed Method Replacement
DoctrineHelper::createSchemaManager() $connection->createSchemaManager()
DoctrineHelper::introspectSchema() $schemaManager->introspectSchema()
DoctrineHelper::getMigrateToSql() See migration example below

These methods were compatibility shims for older Doctrine DBAL versions that are no longer needed.

📦 Namespace Changes

Annotation → Attribute

[!TIP] The Annotation namespace has been renamed to Attribute to align with PHP 8+ terminology.

Before (3.x) After (4.0)
DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable DH\Auditor\Provider\Doctrine\Auditing\Attribute\Auditable
DH\Auditor\Provider\Doctrine\Auditing\Annotation\Ignore DH\Auditor\Provider\Doctrine\Auditing\Attribute\Ignore
DH\Auditor\Provider\Doctrine\Auditing\Annotation\Security DH\Auditor\Provider\Doctrine\Auditing\Attribute\Security
DH\Auditor\Provider\Doctrine\Auditing\Annotation\AnnotationLoader DH\Auditor\Provider\Doctrine\Auditing\Attribute\AttributeLoader

Before (3.x):

use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;

#[Audit\Auditable]
class MyEntity
{
    #[Audit\Ignore]
    private string $ignoredField;
}

After (4.0):

use DH\Auditor\Provider\Doctrine\Auditing\Attribute as Audit;

#[Audit\Auditable]
class MyEntity
{
    #[Audit\Ignore]
    private string $ignoredField;
}

Attribute → Core Namespace (4.x)

[!TIP] PHP attributes have been promoted to the core DH\Auditor\Attribute namespace to be provider-agnostic (e.g. for future Eloquent provider support). The old provider-specific classes are now deprecated but still work as they extend the new ones.

Deprecated (4.x) Canonical (4.x)
DH\Auditor\Provider\Doctrine\Auditing\Attribute\Auditable DH\Auditor\Attribute\Auditable
DH\Auditor\Provider\Doctrine\Auditing\Attribute\Ignore DH\Auditor\Attribute\Ignore
DH\Auditor\Provider\Doctrine\Auditing\Attribute\Security DH\Auditor\Attribute\Security

Before (deprecated):

use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Auditable;
use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Ignore;
use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Security;

After (canonical):

use DH\Auditor\Attribute\Auditable;
use DH\Auditor\Attribute\Ignore;
use DH\Auditor\Attribute\Security;

📊 Model Changes

Entry Model - Property Hooks (PHP 8.4+)

[!NOTE] The Entry model now uses PHP 8.4 property hooks. Getter methods have been replaced by direct property access.

Before (3.x) After (4.0)
$entry->getId() $entry->id
$entry->getType() $entry->type
$entry->getObjectId() $entry->objectId
$entry->getDiscriminator() $entry->discriminator
$entry->getTransactionHash() $entry->transactionHash
$entry->getUserId() $entry->userId
$entry->getUsername() $entry->username
$entry->getUserFqdn() $entry->userFqdn
$entry->getUserFirewall() $entry->userFirewall
$entry->getIp() $entry->ip
$entry->getCreatedAt() $entry->createdAt
(new) $entry->extraData

Note: getDiffs() and getExtraData() methods remain as they contain business logic. extraData is also available as a virtual property.

Configuration - Asymmetric Visibility (PHP 8.4+)

The Configuration class now uses PHP 8.4 asymmetric visibility:

Before (3.x) After (4.0)
$config->isEnabled() $config->enabled
$config->getTimezone() $config->timezone

User Model - Property Hooks (PHP 8.4+)

The UserInterface now uses PHP 8.4 property hooks:

Before (3.x) After (4.0)
$user->getIdentifier() $user->identifier
$user->getUsername() $user->username

If you have custom UserInterface implementations, update them:

Before (3.x):

use DH\Auditor\User\UserInterface;

class MyUser implements UserInterface
{
    public function getIdentifier(): ?string { return $this->id; }
    public function getUsername(): ?string { return $this->name; }
}

After (4.0):

use DH\Auditor\User\UserInterface;

class MyUser implements UserInterface
{
    public ?string $identifier { get => $this->id; }
    public ?string $username { get => $this->name; }
}

🔡 UTF-8 Conversion Now Opt-In

[!IMPORTANT] The automatic UTF-8 re-encoding of audit diffs has been removed from the default path.

What Changed

Previously, auditor always called mb_convert_encoding() recursively on the entire diff array before persisting each audit entry. This guaranteed that any non-UTF-8 byte sequence in entity field values would be sanitized before being stored.

This pass now only runs when the utf8_convert option is explicitly enabled.

Who Is Affected

You are affected if your application:

  • Uses a database connection that was not explicitly configured as UTF-8, and
  • Persists entity fields that may contain non-UTF-8 strings (e.g. data imported from ISO-8859-1 sources, legacy BLOBs, etc.)

Applications running on a modern stack (DBAL 4 + PHP 8.4 + UTF-8 database charset) are not affected — the conversion was already a no-op for them.

How to Fix

Add 'utf8_convert' => true to your DoctrineProvider configuration:

use DH\Auditor\Provider\Doctrine\Configuration;

$configuration = new Configuration([
    // ... other options ...
    'utf8_convert' => true,
]);

Why the Default Is false

DBAL 4 mandates PHP 8.4+ and enforces UTF-8 database connections. Traversing and re-encoding every diff array on every flush was unnecessary overhead for the vast majority of users. The option is preserved for applications that still process legacy non-UTF-8 data.

🆕 New extra_data Column

Schema Change

[!IMPORTANT] A new nullable JSON column extra_data has been added to all audit tables. Run the schema update command after upgrading.

php bin/console audit:schema:update --dump-sql   # Preview
php bin/console audit:schema:update --force       # Apply

Two ways to populate extra_data

Option 1 — Global provider (new in 4.0): set a callable on Configuration once and it runs automatically for every audit entry produced during a flush:

$configuration->setExtraDataProvider(function (): ?array {
    return [
        'ip'   => $this->requestStack->getCurrentRequest()?->getClientIp(),
        'role' => $this->security->getUser()?->getRoles(),
    ];
});

Option 2 — Per-entity listener: LifecycleEvent now exposes $event->entity (the audited object), allowing listeners to add entity-specific context:

#[AsEventListener(event: LifecycleEvent::class, priority: 10)]
final class AuditExtraDataListener
{
    public function __invoke(LifecycleEvent $event): void
    {
        $payload = $event->getPayload();

        if ($payload['entity'] === User::class && null !== $event->entity) {
            $payload['extra_data'] = json_encode([
                'department' => $event->entity->getDepartment(),
            ], JSON_THROW_ON_ERROR);
            $event->setPayload($payload);
        }
    }
}

The provider runs first; the listener can override or extend its output. See the Extra Data guide for precedence rules, merging strategies, and caveats.

🔢 TransactionType Enum

[!TIP] Transaction type constants have been replaced by a backed enum for better type safety.

Before (3.x) After (4.0)
Transaction::INSERT TransactionType::Insert->value
Transaction::UPDATE TransactionType::Update->value
Transaction::REMOVE TransactionType::Remove->value
Transaction::ASSOCIATE TransactionType::Associate->value
Transaction::DISSOCIATE TransactionType::Dissociate->value

Before (3.x):

use DH\Auditor\Model\Transaction;

if ($entry->getType() === Transaction::INSERT) {
    // ...
}

After (4.0):

use DH\Auditor\Model\TransactionType;

if ($entry->type === TransactionType::Insert->value) {
    // ...
}

🎵 Symfony Attributes

Commands

Console commands now use #[AsCommand] attribute:

#[AsCommand(
    name: 'audit:schema:update',
    description: 'Update audit tables structure',
)]
final class UpdateSchemaCommand extends Command

Event Listeners

Event subscriber has been replaced by event listeners using #[AsEventListener]:

Before (3.x):

class AuditEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [LifecycleEvent::class => 'onAuditEvent'];
    }
}

After (4.0):

#[AsEventListener(event: LifecycleEvent::class, method: 'onAuditEvent')]
final class AuditEventSubscriber
{
    public function onAuditEvent(LifecycleEvent $event): void { }
}

🔄 Migration Example: getMigrateToSql

Before (3.x):

use DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper;

$sqls = DoctrineHelper::getMigrateToSql($connection, $fromSchema, $toSchema);

After (4.0):

use Doctrine\DBAL\Schema\Comparator;

$platform = $connection->getDatabasePlatform();
$sqls = $platform->getAlterSchemaSQL(
    (new Comparator($platform))->compareSchemas($fromSchema, $toSchema)
);

DoctrineHelper::getRealClassName()

This method now only handles __CG__ proxies (Doctrine Common Gateway marker).

With PHP 8.4+, Doctrine ORM uses native lazy objects instead of proxy classes, so the previous proxy handling logic has been simplified.

Composer Scripts Changes

The following composer scripts have been removed:

Removed Script Purpose
setup5 Symfony 5.4 setup
setup6 Symfony 6.4 setup
setup7 Symfony 7.x setup

Use the new unified setup script instead:

composer setup

Internal Changes

These changes are internal and should not affect most users, but are documented for completeness:

DoctrineHelper::setPrimaryKey()

Now uses PrimaryKeyConstraint directly without fallback to deprecated setPrimaryKey() method.

DoctrineHelper::getReflectionPropertyValue()

Now uses getPropertyAccessor() directly without fallback to deprecated getReflectionProperty() method.

PlatformHelper::getServerVersion()

Simplified to use getNativeConnection() directly. The getWrappedConnection() fallback has been removed.

🚀 PHP 8.4+ Modernization Changes

TransactionType Enum

[!NOTE] Transaction type constants have been moved from Transaction model to a native PHP enum TransactionType.

Before (3.x):

use DH\Auditor\Model\Transaction;

$type = Transaction::INSERT;
$type = Transaction::UPDATE;
$type = Transaction::REMOVE;
$type = Transaction::ASSOCIATE;
$type = Transaction::DISSOCIATE;

After (4.0):

use DH\Auditor\Model\TransactionType;

// Using constants (recommended - backward compatible syntax)
$type = TransactionType::INSERT;
$type = TransactionType::UPDATE;
$type = TransactionType::REMOVE;
$type = TransactionType::ASSOCIATE;
$type = TransactionType::DISSOCIATE;

// Or using enum cases (when you need the enum instance)
$type = TransactionType::Insert;
$type = TransactionType::Update;
// etc.

Entry Model - Property Hooks

The Entry model now uses PHP 8.4 property hooks. Getter methods have been replaced by direct property access.

Before (3.x) After (4.0)
$entry->getId() $entry->id
$entry->getType() $entry->type
$entry->getObjectId() $entry->objectId
$entry->getDiscriminator() $entry->discriminator
$entry->getTransactionHash() $entry->transactionHash
$entry->getUserId() $entry->userId
$entry->getUsername() $entry->username
$entry->getUserFqdn() $entry->userFqdn
$entry->getUserFirewall() $entry->userFirewall
$entry->getIp() $entry->ip
$entry->getCreatedAt() $entry->createdAt

Note: getDiffs() method is preserved as it contains business logic.

User Interface - Property Hooks

The UserInterface now uses PHP 8.4 interface properties.

Before (3.x):

interface UserInterface
{
    public function getIdentifier(): ?string;
    public function getUsername(): ?string;
}

// Usage
$user->getIdentifier();
$user->getUsername();

After (4.0):

interface UserInterface
{
    public ?string $identifier { get; }
    public ?string $username { get; }
}

// Usage
$user->identifier;
$user->username;

Configuration - Asymmetric Visibility

The Configuration class now uses asymmetric visibility for some properties.

Before (3.x) After (4.0)
$config->isEnabled() $config->enabled
$config->getTimezone() $config->timezone

Note: getUserProvider(), getSecurityProvider(), and getRoleChecker() methods are preserved.

Attribute Namespace Rename

The Annotation namespace has been renamed to Attribute to better reflect PHP 8+ native attributes.

Before (3.x):

use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Ignore;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Security;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation\AnnotationLoader;

After (4.0):

use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Auditable;
use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Ignore;
use DH\Auditor\Provider\Doctrine\Auditing\Attribute\Security;
use DH\Auditor\Provider\Doctrine\Auditing\Attribute\AttributeLoader;

Event Subscribers → Event Listeners

Event subscribers now use Symfony's #[AsEventListener] attribute instead of EventSubscriberInterface.

Before (3.x):

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class AuditEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [LifecycleEvent::class => ['onAuditEvent', -1000000]];
    }
    
    public function onAuditEvent(LifecycleEvent $event): LifecycleEvent { /* ... */ }
}

After (4.0):

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener(event: LifecycleEvent::class, priority: -1_000_000)]
class AuditEventSubscriber
{
    public function __invoke(LifecycleEvent $event): LifecycleEvent { /* ... */ }
}

Console Commands with #[AsCommand]

Console commands now use the #[AsCommand] attribute for metadata.

Before (3.x):

class CleanAuditLogsCommand extends Command
{
    protected function configure(): void
    {
        $this
            ->setName('audit:clean')
            ->setDescription('Cleans audit tables')
            // ...
    }
}

After (4.0):

#[AsCommand(name: 'audit:clean', description: 'Cleans audit tables')]
class CleanAuditLogsCommand extends Command
{
    protected function configure(): void
    {
        // Only options and arguments, no setName/setDescription
    }
}

📋 Migration Steps

1️⃣ Update PHP Version

Ensure you're running PHP 8.4 or higher:

php -v
# PHP 8.4.x ...

2️⃣ Update Symfony Dependencies

Update your composer.json:

{
    "require": {
        "symfony/framework-bundle": "^8.0"
    }
}

3️⃣ Update Doctrine Dependencies

{
    "require": {
        "doctrine/dbal": "^4.0",
        "doctrine/orm": "^3.2"
    }
}

4️⃣ Update auditor

composer require damienharper/auditor:^4.0

5️⃣ Update Your Code

Replace any removed method calls:

// Before
$schemaManager = DoctrineHelper::createSchemaManager($connection);
$schema = DoctrineHelper::introspectSchema($schemaManager);

// After
$schemaManager = $connection->createSchemaManager();
$schema = $schemaManager->introspectSchema();

6️⃣ Run Tests

./vendor/bin/phpunit

7️⃣ Update Audit Schema

[!IMPORTANT] After upgrading, ensure your audit tables are up to date.

php bin/console audit:schema:update --dump-sql
php bin/console audit:schema:update --force

🔧 Troubleshooting

"Class not found" errors

[!TIP] Ensure all dependencies are updated by running composer update.

Schema issues

[!NOTE] Run the schema update command: php bin/console audit:schema:update --force

Test failures

If tests fail after upgrading:

  1. Check PHPUnit version compatibility
  2. Review any mocked classes for API changes
  3. Check for deprecated method usage

❓ Need Help?

If you encounter issues during the upgrade:

  1. Check the GitHub Issues
  2. Review the Release Notes
  3. Open a new issue with:
    • PHP version
    • Symfony version
    • Error messages
    • Steps to reproduce
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui