Before modernizing an old PHP application, there is a question I think we should ask much earlier than:
“Which framework should we migrate to?”
The better first question is:
“What exactly are we dealing with?”
A mature PHP system can contain years of assumptions that are not obvious from looking at a few controllers.
Composer dependencies.
Old PHP constraints.
Static state.
Direct session access.
Global variables.
Runtime side effects.
Unexpected autoloading.
Abandoned packages.
Framework coupling.
Files that do not fit the structure you expected.
Before changing any of that, I want evidence.
That is the problem Evolve Audit is intended to help with.
Audit before you modernize
Evolve Audit is part of EvolvePHP's development tooling, but an important design goal is that the application being inspected does not need to be an EvolvePHP application.
The current experimental tool can be pointed at an existing PHP project:
evolve-audit /path/to/projectOr produce machine-readable evidence:
evolve-audit /path/to/project --format=jsonThe important part is what happens next.
Or more accurately:
what does not happen.
Audit does not bootstrap the target application.
It does not include its PHP files.
It does not load its Composer autoloader.
It does not start Laravel, Symfony, CakePHP, Yii or EvolvePHP.
It does not load .env.
It does not run Composer scripts.
And it does not modify the target project.
It treats the application as data to inspect.
Why read-only analysis matters
Imagine auditing a ten-year-old production application.
You may not know whether simply booting it will:
Connect to production services
Load environment secrets
Write cache files
Open sessions
Execute framework boot hooks
Register shutdown handlers
Contact external APIsFor an initial architecture assessment, I do not necessarily want any of that.
I want the safest possible first look.
So Audit works from things that can be inspected statically.
For example:
composer.json
composer.lock
PHP source files
namespace declarations
class/interface/trait/enum declarations
runtime-related lexical patternsThis does not tell us everything.
But it tells us quite a lot before we have trusted the application enough to execute it.
Composer already tells a story
One of the first things Audit examines is Composer evidence.
A project's composer.json can reveal:
PHP requirements
Framework dependencies
Development dependencies
Autoload configuration
Files loaded automatically
Platform overridesThe lockfile can reveal more:
Resolved packages
Resolved versions
Dependency relationships
Extension requirements
Composer plugins
Packages marked abandonedThat information can immediately raise useful modernization questions.
For example, a project might claim:
"php": "^7.4"while its deployment environment is running something completely different.
Or config.platform.php might be forcing Composer to resolve dependencies as though another PHP version were installed.
That is evidence worth investigating.
But Audit deliberately does not turn that into:
“Your application is compatible.”
A Composer constraint is evidence.
It is not certification.
That distinction is important.
Source code reveals different risks
Composer tells us about dependencies.
The source code tells us about coupling.
The current Audit foundation looks for lexical evidence including things such as:
$_SESSION
$_SERVER
$_POST
$GLOBALS
global $something;It can also surface static-state declarations and static property access.
Why care?
Because code like:
CurrentTenant::$tenant = $tenant;may work perfectly well in a traditional short-lived PHP request.
But if the application is ever considered for a persistent worker model, that state deserves review.
The same applies to direct session coupling, process-wide mutations and other assumptions whose lifetime may be broader than one execution.
Audit is not saying:
“This code is broken.”
It is saying:
“There is something here a human should understand.”
That is a much safer claim.
Runtime hazards can also leave fingerprints
Some runtime behavior can be identified lexically without executing the application.
Things such as:
session_start();
header(...);
setlocale(...);
register_shutdown_function(...);
exit;
eval(...);
require ...;can matter when evaluating an application's architecture or considering a different runtime model.
Again, context matters.
exit is not automatically a bug.
session_start() is not automatically unsafe.
A static property is not automatically bad architecture.
But when modernizing a system, knowing where these assumptions exist can save a lot of manual exploration.
Structure matters too
Audit also inspects source structure.
Namespaces.
Named classes.
Interfaces.
Traits.
Enums.
Functions.
Files containing global declarations.
Files containing multiple namespaces.
That is useful because modernization often begins with a deceptively simple question:
“Where are the boundaries in this application?”
Audit cannot currently discover business capabilities automatically.
A namespace is not necessarily a module.
A directory is not necessarily a domain boundary.
But structural evidence can help a developer begin building that map.
For example:
App\Billing\
App\Orders\
App\Customers\might indicate useful boundaries.
Or it might simply be folder organization.
A human still has to decide.
What Audit intentionally does not do
This may be as important as what it does.
Evolve Audit currently does not:
- rewrite code,
- automatically fix findings,
- perform vulnerability lookups,
- execute Composer,
- infer route ownership,
- infer data ownership,
- generate a complete migration plan,
- prove persistent-worker safety,
- certify framework compatibility,
- decide whether something can safely move through Evolve Bridge.
I want those limits to remain visible.
A modernization tool becomes dangerous if it turns incomplete evidence into confident conclusions.
Static analysis can tell us what is present.
It cannot always tell us what happens at runtime.
Audit is the first step, not the answer
The broader EvolvePHP modernization model is moving toward something like:
Audit
↓
Understand evidence
↓
Doctor
↓
Plan adoption
↓
Bridge where appropriate
↓
Modernize incrementallyAudit answers:
“What evidence can we gather about this system without disturbing it?”
Doctor asks different questions about the runtime and environment.
Adoption planning asks what we actually intend to change.
Bridge deals with coexistence between old and new architecture.
Those responsibilities should not be collapsed into one magical command.
Why I think this is useful beyond EvolvePHP
Even if someone never migrates an application to EvolvePHP, I think the underlying idea is useful.
Before rewriting a legacy system:
inspect it.
Before changing the runtime:
understand its state assumptions.
Before replacing dependencies:
map them.
Before drawing new module boundaries:
look at the structure that already exists.
Modernization should begin with evidence, not enthusiasm.
Evolve Audit is still experimental, and the EvolvePHP packages are not yet independently published, so I would not present it as a finished modernization platform today.
But this is exactly the kind of tooling I want EvolvePHP to grow into.
Not a button that promises to modernize your application automatically.
A tool that helps you understand the application well enough to make the next decision with better evidence.
Because before you can safely evolve a system, you first need to know what you actually have.

No comments:
Post a Comment