PHPackages                             sediment/analyzer - 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. [Database &amp; ORM](/categories/database)
4. /
5. sediment/analyzer

ActiveProject[Database &amp; ORM](/categories/database)

sediment/analyzer
=================

A static analyzer that reads a WordPress plugin's source code and reports exactly what it leaves behind in your database.

v0.6.0(today)10GPL-2.0-or-laterPHPPHP ^8.3CI passing

Since Jul 22Pushed todayCompare

[ Source](https://github.com/SNO7E-G/Sediment)[ Packagist](https://packagist.org/packages/sediment/analyzer)[ Docs](https://github.com/SNO7E-G/Sediment)[ RSS](/packages/sediment-analyzer/feed)WikiDiscussions main Synced today

READMEChangelog (9)Dependencies (3)Versions (10)Used By (0)

Sediment
========

[](#sediment)

*Everything your plugins left behind, layer by layer.*

A static analyzer that reads a WordPress plugin's source and reports exactly what it leaves behind in your database.

 [![Tests](https://github.com/SNO7E-G/Sediment/actions/workflows/tests.yml/badge.svg)](https://github.com/SNO7E-G/Sediment/actions/workflows/tests.yml) [![Latest release](https://camo.githubusercontent.com/b780493fcc7e7dd38e30baeac8ca0bb3c2f37332bd6aad48b954f87ff4b7ae0e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f534e4f37452d472f536564696d656e743f696e636c7564655f70726572656c6561736573266c6162656c3d72656c6561736526636f6c6f723d626c7565)](https://github.com/SNO7E-G/Sediment/releases) [![Packagist](https://camo.githubusercontent.com/da03f883f0c0b4d8a92826bf0d761c17eae6a5f5a67d0077beb1322a9b4fab17/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736564696d656e742f616e616c797a65723f6c6162656c3d7061636b616769737426636f6c6f723d6f72616e6765)](https://packagist.org/packages/sediment/analyzer) [![Status: alpha](https://camo.githubusercontent.com/0c0cdc05ee26eaad233916669c823cf073123a679f2e1e6e4fa0d7e4759d5031/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374617475732d616c7068612d6f72616e67652e737667)](https://camo.githubusercontent.com/0c0cdc05ee26eaad233916669c823cf073123a679f2e1e6e4fa0d7e4759d5031/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374617475732d616c7068612d6f72616e67652e737667) [![PHP 8.3+](https://camo.githubusercontent.com/c625f9110e52b20c52e78dd7521c1e7b4c925a48e85be6c2170469a618c9d2e9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e332532422d3737376262342e737667)](https://camo.githubusercontent.com/c625f9110e52b20c52e78dd7521c1e7b4c925a48e85be6c2170469a618c9d2e9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e332532422d3737376262342e737667) [![License: GPL-2.0-or-later](https://camo.githubusercontent.com/26f8b6541ea045cc1dbc2267208158b5a7ebbf5cf437c4b486d80fee9386f77e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d47504c2d2d322e302d2d6f722d2d6c617465722d626c75652e737667)](LICENSE)

---

Uninstalling a WordPress plugin deletes its files and almost nothing else. The options it registered keep autoloading on every request, its custom tables keep bloating your backups, and its cron events keep firing hooks whose code is long gone. Analysis of the plugin directory has found that more than 40% of plugins leave orphaned data behind, and only 28.6% remove it cleanly — a problem that has been documented since 2008 and remains unsolved.

The hard part was never deletion. It is attribution: knowing that a stray `smk_last_sync_ts` row belongs to a plugin you removed years ago, and is therefore safe to drop. Cleanup tools guess at this by matching name prefixes, and a wrong guess in a tool that deletes data will wreck a site.

Sediment takes a different route. It reads the source. A plugin's own code contains the literal calls that create its data — `add_option()`, `dbDelta()`, `wp_schedule_event()` — so parsing them statically turns attribution from a guess into something you can point at: `includes/setup.php:412`.

What sets it apart
------------------

[](#what-sets-it-apart)

It reads code but never runs it. There is no WordPress install, no database, and nothing executed; a scan works anywhere PHP 8.3 or newer runs. Every finding carries a confidence level, and the report tells you what fraction of write calls it could resolve, so the tool is candid about its own coverage instead of pretending to certainty it doesn't have. And it is real AST analysis built on `nikic/php-parser` — not a pile of regular expressions.

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

[](#installation)

Download the PHAR from the [latest release](https://github.com/SNO7E-G/Sediment/releases/latest) and run it — one file, no Composer, no dependencies beyond PHP 8.3:

```
php sediment.phar scan path/to/plugin
```

Or add it to a plugin project as a development dependency:

```
composer require --dev sediment/analyzer
```

Or work from a clone:

```
git clone https://github.com/SNO7E-G/Sediment.git
cd Sediment
composer install
php bin/sediment scan path/to/plugin
```

Usage
-----

[](#usage)

Point `scan` at a plugin directory:

```
php bin/sediment scan path/to/plugin
```

```
Options (3)
 function        key             confidence   autoload   cleaned   source
 add_option      acme_version    verified     no         yes       acme.php:31
 add_option      acme_settings   verified     yes        no        acme.php:33
 update_option   acme_pro_*      pattern      unknown    no        pro.php:88

Tables (1)
 function   key                confidence   cleaned   source
 dbDelta    {prefix}acme_logs  resolved     no        install.php:12

 Resolution rate: 92.0% (23 of 25 write calls resolved to a key).
 Cleanup path: uninstall.php — 8 of 12 detected artifacts removed on uninstall.

```

Grade a plugin, generate the `uninstall.php` it should have shipped with, emit a machine-readable manifest, or gate your CI on the result:

```
php bin/sediment grade path/to/plugin
php bin/sediment uninstall path/to/plugin > uninstall.php
php bin/sediment scan path/to/plugin --json > manifest.json
php bin/sediment check path/to/plugin --fail-on=C   # exit 1 if worse than C
```

Guard a release against footprint creep, or scan a whole collection at once:

```
php bin/sediment diff manifest.json path/to/plugin   # exit 1 if the footprint got worse
php bin/sediment batch path/to/plugins --out ./manifests
```

`diff` compares against a manifest you committed earlier: a new artifact that isn't cleaned up, one that stopped being cleaned up, or a worse grade fails the build. Adding data you also remove on uninstall doesn't. `batch` writes one manifest per plugin plus a grade distribution.

The manifest carries the grade, the coverage counts, and every artifact with its confidence, `cleaned` flag, and source lines — it is the contract other tools build on, documented in [`docs/manifest-schema.md`](docs/manifest-schema.md). `check` is the same analysis wired for CI, so a plugin author can fail a build on their database footprint the way they already fail it on tests.

What it detects
---------------

[](#what-it-detects)

Options (with the autoload flag), custom tables, cron events, transients, post/user/term/comment metadata, roles and capabilities, custom post types and taxonomies, directories, rewrite rules, and Action Scheduler jobs.

Custom post types are the group prefix-matching tools structurally cannot reach. Uninstall an e-commerce plugin and its products stay behind as unreachable rows in `wp_posts` — often tens of thousands. No prefix reveals that. Reading the source does.

Sediment also reads *how* a plugin cleans up. A routine gated behind a "delete my data on uninstall" setting is reported as conditional — with the exact option and the value it defaults to — because a plugin that only cleans up for the few users who found that checkbox is clean in its code and dirty on real sites.

`grade` returns a letter and a weighted-damage score. `uninstall` writes a teardown that removes only the artifacts Sediment attributed with high confidence — never a WordPress core key, an already-cleaned key, or a guess.

How confidence works
--------------------

[](#how-confidence-works)

Sediment labels every finding by how certain the attribution is, and it never presents a guess as a fact:

LevelMeaning`verified`The key is a literal string in the source.`resolved`The key resolves from a constant, class constant, or literal property.`pattern`The key is partly dynamic but exposes a stable prefix, such as `_mp_*`.`dynamic`The key is only knowable at runtime. It is recorded honestly and never acted on.Grading
-------

[](#grading)

A grade reflects what a plugin leaves behind, weighted by real-world cost rather than a raw count — one orphaned autoloaded option matters more than twenty small rows nobody reads:

GradeMeaning**A**Removes everything it creates, unconditionally, on uninstall.**B**Removes everything, but only when the user opts in (conditionally clean) — the grade names the setting.**C**Removes some data; leaves a few harmless rows — none autoloaded, no tables or cron.**D**Leaves tables, autoloaded options, cron events, or orphaned content behind.**F**Ships no uninstall routine at all.The rubric is published in full at [`docs/grading.md`](docs/grading.md) so a grade is always something you can explain, not a black box.

Limitations
-----------

[](#limitations)

Static analysis cannot see a key that is assembled entirely at runtime, so those are reported as `dynamic` and never acted on — the resolution rate tells you how much of a plugin fell into that bucket.

Across a corpus of ten real plugins the median resolves at 82%, but the largest ones resolve least: Yoast SEO manages 62% and WooCommerce 78%, because both funnel writes through their own settings layers. Sediment publishes those numbers rather than papering over them, and every unresolved write is listed with its source line so a human can settle it. The full corpus and its results are in [`docs/corpus.md`](docs/corpus.md). A handful of narrower edges (cron events scheduled with arguments, an aliased `$wpdb` handle) are documented plainly in [`docs/limitations.md`](docs/limitations.md). Publishing these is deliberate: an audit tool earns trust by being honest about its own blind spots.

Roadmap
-------

[](#roadmap)

Sediment is built in stages, each useful on its own:

1. **Analyzer** (this repository) — detect what a plugin writes, diff it against the plugin's cleanup routine, grade the result, and generate the `uninstall.php` it should have shipped with.
2. **The Index** — a public, openly licensed dataset mapping thousands of plugins to the data they create, so a leftover row can be traced back to the plugin that made it.
3. **Inspector** — a read-only WordPress plugin that scans installed plugins on disk and shows what each will leave behind *before* you click Delete.

The full plan, with what each release has to prove before the next begins, is in [ROADMAP.md](ROADMAP.md).

Status
------

[](#status)

**Alpha.** Detection across every artifact type above, static resolution, the cleanup diff (including conditional cleanup), grading, the manifest, the CI check, and the `uninstall.php` generator all work today, backed by a broad test suite. Releases are cut when a meaningful body of work is ready rather than per change, so each one carries real features — see the [changelog](CHANGELOG.md) and the [releases](https://github.com/SNO7E-G/Sediment/releases). Public interfaces, including the manifest schema, may still change before 1.0.

Development
-----------

[](#development)

```
composer install
composer test
php bin/sediment scan tests/fixtures/dirty-plugin
```

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for how a change should look — especially the rule that nothing may be reported with more confidence than the source justifies.

License
-------

[](#license)

Released under [GPL-2.0-or-later](LICENSE). The forthcoming Index dataset will be released under CC0, so the open data can never be walled off.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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

9

Last Release

0d ago

### Community

Maintainers

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

---

Top Contributors

[![SNO7E-G](https://avatars.githubusercontent.com/u/182176867?v=4)](https://github.com/SNO7E-G "SNO7E-G (40 commits)")

---

Tags

analyzerclean-codedatabasephpsecuritywordpressworpdress-plugincliwordpressstatic analysisdatabasecleanupphp-parseruninstall

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/sediment-analyzer/health.svg)

```
[![Health](https://phpackages.com/badges/sediment-analyzer/health.svg)](https://phpackages.com/packages/sediment-analyzer)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M348](/packages/psalm-plugin-laravel)[rector/rector-src

Instant Upgrade and Automated Refactoring of any PHP code

136406.3k14](/packages/rector-rector-src)[ssch/typo3-rector

Instant fixes for your TYPO3 PHP code by using Rector.

2603.2M445](/packages/ssch-typo3-rector)[laraveldaily/filacheck

Static analysis for Filament projects - detect deprecated patterns and code issues

12175.6k1](/packages/laraveldaily-filacheck)[jolicode/castor

A lightweight and modern task runner. Automate everything. In PHP.

54643.3k4](/packages/jolicode-castor)[aeliot/todo-registrar

Register TODOs from source code in issue tracker

153.0k](/packages/aeliot-todo-registrar)

PHPackages © 2026

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