Getting Started - ValentinNikolaev/php-upgrade-preflight GitHub Wiki
Getting Started
This guide takes you from an empty tools directory to a report you can explain to a developer or a technical manager. PHP Upgrade Preflight analyzes an upgrade; it does not perform one.
Current release line: v0.3.x. The latest published release recorded by the repository is v0.3.5. It produces canonical JSON schema 0.8 and requires PHP
^8.0on the machine that runs the analyzer.
The result in one sentence
You give the analyzer an existing Composer project, a desired package or PHP target, and optional target-platform facts. It copies the project metadata to temporary workspaces, asks Composer what can resolve, scans selected PHP source, and writes an evidence-backed report without changing the target tree.
The report is planning input. It is not proof that the application boots, passes tests, is secure, or is ready to deploy.
Before you install
Check the analyzer host:
php --version
composer --version
php --version
composer --version
The analyzer host needs PHP 8.0 or newer and Composer 2. A target project may use an older PHP version because the analyzer can be installed in a separate tools directory.
Host installability is not target compatibility
These are different questions:
| Question | Example | Answered by |
|---|---|---|
| Can the analyzer package run here? | Can the Laravel adapter be installed into this Laravel 10 application? | Host PHP and host Laravel package constraints |
| What deployment platform should Composer model? | Can this project resolve for PHP 8.3 with ext-curl? |
--target-php, extension assumptions, or a platform profile |
| Will the upgraded application work? | Does checkout still complete after the upgrade? | Your own tests and runtime validation, not this analyzer |
The Laravel adapter is project-locally installable with Laravel 8β13, subject to Laravel's own PHP floor. Laravel 7 should be analyzed from an external PHP 8 tools directory. An external analyzer can model a newer PHP target even if its own host uses another supported PHP 8 version.
Choose an installation layout
Recommended: a separate tools directory
Use this layout when the target runs PHP 7, has tight dependency constraints, or must remain byte-for-byte unchanged.
Linux or macOS:
mkdir -p "$HOME/php-upgrade-tools"
cd "$HOME/php-upgrade-tools"
composer require php-upgrade-preflight/cli:^0.3 php-upgrade-preflight/laravel:^0.3
vendor/bin/upgrade-intel --help
Windows PowerShell:
New-Item -ItemType Directory -Force "$env:USERPROFILE\php-upgrade-tools" | Out-Null
Set-Location "$env:USERPROFILE\php-upgrade-tools"
composer require php-upgrade-preflight/cli:^0.3 php-upgrade-preflight/laravel:^0.3
vendor\bin\upgrade-intel.bat --help
Install every framework adapter beside the CLI. Nothing has to be installed into the target project.
Project-local development dependency
Use this only when the application's dependency graph accepts the analyzer packages and changing composer.json and composer.lock during installation is acceptable.
composer require --dev php-upgrade-preflight/cli:^0.3 php-upgrade-preflight/laravel:^0.3
vendor/bin/upgrade-intel --help
composer require --dev php-upgrade-preflight/cli:^0.3 php-upgrade-preflight/laravel:^0.3
vendor\bin\upgrade-intel.bat --help
Project-local installation changes the project before analysis. The analyzer's read-only promise begins after installation; it cannot make the composer require step immutable.
The published packages are ordinary Composer packages. There is no supported PHAR or versioned runtime container image. The repository Docker files are development and verification tooling.
Prepare safe paths
The analyzed path must contain a valid composer.json. A composer.lock provides much stronger current-state evidence.
Create the report directory outside the project. The parent directory must already exist.
mkdir -p /work/upgrade-reports
New-Item -ItemType Directory -Force C:\work\upgrade-reports | Out-Null
Do not write the report under the analyzed project. The command rejects that destination to preserve the read-only input contract.
Guided first run
In a terminal, start the interactive wizard:
vendor/bin/upgrade-intel wizard
vendor\bin\upgrade-intel.bat wizard
It shows available project evidence, asks whether to model PHP, packages, or both, and requires an explicit Composer analysis mode. For package targets, the default reads only composer.json. You may instead request a local-cache-only metadata check or configured project repositories; the latter can use network access and credentials. A package that is explicitly absent or has no matching discovered version must be corrected. Offline, timeout, and metadata failures are shown as unverified rather than falsely labeled nonexistent.
The default report format is readable Markdown. The wizard always keeps the report on stdout and can also save the same bytes to a validated path outside the project. It shows the equivalent flag-based analyze command before confirmation. Use that explicit command in CI or any redirected/non-TTY session. Enter cancel, quit, or q to stop before analysis.
Run a first PHP-only analysis
Linux or macOS:
vendor/bin/upgrade-intel analyze \
--path=/work/legacy-app \
--from-php=7.4 \
--target-php=8.2 \
--format=json \
--output=/work/upgrade-reports/php-82.json
Windows PowerShell:
vendor\bin\upgrade-intel.bat analyze `
--path=C:\work\legacy-app `
--from-php=7.4 `
--target-php=8.2 `
--format=json `
--output=C:\work\upgrade-reports\php-82.json
All standalone CLI values use --name=value. --path C:\work\legacy-app is not accepted. Only flags such as --debug omit =value.
Run a first Laravel analysis
The Laravel adapter must be installed beside the CLI.
vendor/bin/upgrade-intel analyze \
--path=/work/legacy-app \
--from-php=8.1 \
--target=laravel/framework:^11.0 \
--target-php=8.2 \
--framework=laravel \
--format=json \
--output=/work/upgrade-reports/laravel-11.json
vendor\bin\upgrade-intel.bat analyze `
--path=C:\work\legacy-app `
--from-php=8.1 `
--target=laravel/framework:^11.0 `
--target-php=8.2 `
--framework=laravel `
--format=json `
--output=C:\work\upgrade-reports\laravel-11.json
Without --framework, installed adapters may activate by detecting the project. Supplying --framework=laravel is clearer in automation and fails early with exit code 2 if the adapter is missing.
Understand the two kinds of status
The shell exit code answers, βDid the command produce a valid report?β It does not answer, βCan the upgrade resolve?β
| Exit code | Meaning |
|---|---|
0 |
Help was shown or a complete canonical report was produced |
1 |
An internal or operational failure prevented report production |
2 |
The invocation, target, path, format, framework, profile, or destination was invalid |
130 |
The interactive wizard was cancelled before analysis |
A solver-blocked upgrade is successful analysis output, so it returns 0.
The report field resolution.status answers the direct final-target Composer question:
resolution.status |
Meaning |
|---|---|
feasible |
A determining final-target scenario succeeded without package changes |
feasible_with_changes |
A determining final-target scenario succeeded with candidate lock changes |
blocked |
Reproducible Composer blockers prevent the requested final target |
unknown |
Operational or evidence limits prevented a reliable conclusion |
Example shell logic for JSON output:
vendor/bin/upgrade-intel analyze --path=/work/app --target-php=8.2 --format=json --output=/work/reports/app.json
command_code=$?
if [ "$command_code" -ne 0 ]; then
echo "No valid report was produced; command exit code: $command_code" >&2
exit "$command_code"
fi
jq -r '.resolution.status' /work/reports/app.json
vendor\bin\upgrade-intel.bat analyze --path=C:\work\app --target-php=8.2 --format=json --output=C:\work\reports\app.json
$commandCode = $LASTEXITCODE
if ($commandCode -ne 0) {
throw "No valid report was produced; command exit code: $commandCode"
}
$report = Get-Content -Raw C:\work\reports\app.json | ConvertFrom-Json
$report.resolution.status
For Laravel, also read transition.framework_guidance[].status and staged_resolution.execution_state plus staged_resolution.status. Guidance coverage, direct feasibility, and adjacent-stage feasibility are independent and may legitimately disagree.
Try the checked-in five-minute demo
The repository demo models Laravel 10β13 using local path repositories. It does not contact Packagist and does not modify its target.
From a tools directory on Linux or macOS:
COMPOSER_ROOT_VERSION=1.0.0 vendor/bin/upgrade-intel analyze \
--path=/path/to/php-upgrade-preflight/examples/five-minute-demo/target \
--from-php=8.1 \
--target=laravel/framework:^13.0 \
--target-php=8.3 \
--without-extension=ext-preflight-stage \
--composer-mode=restricted \
--framework=laravel \
--format=json \
--output=/tmp/php-upgrade-preflight-demo.json
PowerShell:
$env:COMPOSER_ROOT_VERSION = '1.0.0'
vendor\bin\upgrade-intel.bat analyze `
--path=C:\src\php-upgrade-preflight\examples\five-minute-demo\target `
--from-php=8.1 `
--target=laravel/framework:^13.0 `
--target-php=8.3 `
--without-extension=ext-preflight-stage `
--composer-mode=restricted `
--framework=laravel `
--format=json `
--output="$env:TEMP\php-upgrade-preflight-demo.json"
Remove-Item Env:COMPOSER_ROOT_VERSION
Expected interpretation:
- process exit code:
0, because a valid report was written; - direct
resolution.status:blocked; - aggregate
staged_resolution.status:blocked; - 10β11 and 11β12 stages:
feasible_with_changes; - 12β13 stage:
blockedby the deliberately absentext-preflight-stage; - Laravel guidance: supported for all three hops.
This is the clearest example of why an exit code is not an upgrade verdict.
Choose JSON or Markdown
Use JSON for automation, storage, comparisons, and evidence traversal:
--format=json --output=/work/reports/app.json
Use Markdown for a review meeting or ticket attachment:
--format=markdown --output=/work/reports/app.md
JSON is canonical. Markdown is a human-readable projection of the same report, not a second analysis.
Quick JSON inspection without extra tools
PHP itself can print the direct status when jq is unavailable:
php -r '$r=json_decode(file_get_contents($argv[1]), true, 512, JSON_THROW_ON_ERROR); echo $r["resolution"]["status"], PHP_EOL;' /work/reports/app.json
PowerShell has a built-in JSON reader:
$report = Get-Content -Raw C:\work\reports\app.json | ConvertFrom-Json
$report.metadata.schema_version
$report.resolution.status
$report.uncertainties
Always confirm metadata.schema_version before an automated consumer assumes field names or enums. v0.3.x writes schema 0.8.
A practical team workflow
- A developer records the exact current PHP with
--from-phpand desired deployment PHP with--target-php. - The platform owner supplies verified extension assumptions or a target-platform profile.
- CI runs JSON output and checks the process exit code first.
- A developer reads direct resolution, staged resolution, blockers, source impact, and uncertainties.
- A technical manager uses risk, effort, confidence, and unresolved uncertainty for planning.
- The team performs the real upgrade in a separate branch.
- The application is installed and tested on the target runtime.
- The analyzer is rerun after each meaningful remediation or stage.
First-run checklist
- The analyzer host runs PHP 8.0+ and Composer 2.
- The target path is correct and contains
composer.json. - The report directory exists outside the target.
- At least one package target, target PHP, or target-platform profile is supplied.
- Every standalone option uses
--name=value. - The Laravel adapter is installed if Laravel guidance is requested.
- Exit code and report statuses are interpreted separately.
-
uncertaintiesis reviewed before decisions are made. -
--debugis off for any report that will be shared.
Next steps
- CLI Reference documents every standalone option.
- Artisan Command covers project-hosted Laravel use.
- Safety and Trust Boundaries explains credentials, restricted mode, paths, and untrusted projects.
- Troubleshooting and FAQ maps common symptoms to fixes.
- Reading the Report explains schema 0.8 for developers and managers.