Skip to content

Add OnBeforePackageUninstall event #16916

@biz87

Description

@biz87

Problem

When a package is uninstalled via Package Manager, there is no way for plugins or the package itself to execute code before file deletion begins. The only existing event, OnPackageUninstall, fires after the entire uninstall process is complete — at that point, all component files and DB objects are already removed.

This creates a real problem for packages that need to:

  • Run database migration rollbacks (requires package classes to be loaded)
  • Perform data cleanup using package services
  • Gracefully disconnect from external services
  • Log or export data before removal

Technical analysis

The uninstall call chain in the current code:

Uninstall.php::process() (line 81)
  → $this->package->uninstall($options)
    → modTransportPackage::uninstall() (line 389)
      → xPDOTransport::uninstall()
        → Iterates vehicles in REVERSE order (line 296)
          → xPDOFileVehicle::_uninstallFiles():
              1. DELETE component files (line 129)
              2. resolve() — runs PHP resolvers (line 156)
          → xPDOObjectVehicle::_uninstallObject():
              1. REMOVE DB object (line 318)
              2. resolve() — runs PHP resolvers (line 342)
  → OnPackageUninstall (line 92) ← fires here, everything already gone

The problem exists at multiple levels:

  1. MODX level: OnPackageUninstall fires too late (after $this->package->uninstall() returns)
  2. xPDO level: within each vehicle, files are deleted before resolve() is called; within resolve(), file-type resolvers run before php-type resolvers

The xPDO-level issues are deeper architectural problems. But the MODX-level fix is straightforward and immediately useful.

Proposed solution

Add an OnBeforePackageUninstall event that fires in Uninstall.php::process() before $this->package->uninstall() is called. At this point, all package files and database objects still exist.

// In Processors/Workspace/Packages/Uninstall.php::process()

$this->modx->invokeEvent('OnBeforePackageUninstall', [
    'package' => $this->package,
]);

if ($this->package->uninstall($options) === false) {
    // ...
}

$this->modx->invokeEvent('OnPackageUninstall', [
    'package' => $this->package,
]);

This allows plugins to:

  • Detect which package is being uninstalled via $package->get('signature')
  • Load and use the package's classes (autoloader still works, files exist)
  • Run migration rollbacks, data exports, cleanup logic
  • Optionally cancel the uninstall via $modx->event->output(false) (consistent with OnBefore* patterns in other events)

Why this matters

Real-world example: MiniShop3 — an e-commerce package with complex data structures, migration system, and media files. When uninstalled via Package Manager:

  • PHP resolvers fail because component files are already deleted
  • No way to roll back database migrations
  • No way to clean up related data (orders, products, media)

The same applies to any complex package with services, migrations, or cleanup logic.

Scope

This proposal only covers the MODX-level fix (adding the event). The deeper xPDO issues (file deletion order within vehicles/resolvers) are a separate concern that could be addressed in the xPDO repository.

Additional consideration

For symmetry, OnBeforePackageInstall and OnBeforePackageRemove could also be added, but OnBeforePackageUninstall is the most critical since it addresses a real data loss scenario.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions