Getting started

The CLI

Check, build, and clean a mixed project with predictable selection and machine-readable diagnostics.

Two commands do the daily work: check finds problems in your source, and build produces the PHP that runs. Both understand the complete project even when you focus the report or the output on one path.

Command map

Command Purpose Writes generated PHP
ppphp --version Print the installed compiler version No
ppphp init Create the project configuration and generated-code directories No
ppphp composer:configure [--dry-run] Maintain Composer runtime mapping to generated output Updates Composer configuration when applied
ppphp check [path] Check selected source with the rest of the project No
ppphp build [path] Check, compile .ppphp, and copy .php source Yes
ppphp clean [--dry-run] Remove generated output and compiler cache paths Deletes generated files

Use the Composer-installed executable in project scripts:

vendor/bin/ppphp --version
vendor/bin/ppphp check
vendor/bin/ppphp build

Include the --version output in bug reports so it is clear which compiler produced a diagnostic or build.

Complete-project and focused commands

With no path, check validates every .ppphp and .php file under the configured source roots. A path narrows the diagnostic selection:

vendor/bin/ppphp check src/Checkout
vendor/bin/ppphp check src/Checkout/CheckoutService.ppphp

The compiler still loads valid unselected source, Composer metadata, dependencies, PHPDoc, and configured stubs when they are needed to resolve the selected code. An unrelated invalid file outside the selection does not block a focused command, but a project-wide name conflict can still make the selected target ambiguous.

build follows the same selection model:

vendor/bin/ppphp build
vendor/bin/ppphp build src/Checkout
vendor/bin/ppphp build src/Checkout/CheckoutService.ppphp
vendor/bin/ppphp build src/Legacy/Formatter.php

A complete build writes every file from your configured source directories. Give it a directory to build just that subtree, or a file to build just that file. A .ppphp file is compiled; a .php file is copied byte-for-byte. Related files are checked as needed but are not added to a focused build's output. Run ppphp build without a path for a complete deployable application.

Read a diagnostic

Console diagnostics point at the original .ppphp source. This is the complete output for an assignment that breaks a declared local type:

Error[P2009]: Assignment Is Not Assignable To Declared Type

Value of type string is not assignable to $attempts of type int.

  --> src/Attempts.ppphp:6:17
5 |     int $attempts = 0;
6 |     $attempts = 'three';
  |                 ^^^^^^^ Value of type string is not assignable to $attempts of type int.
7 |

Related: $attempts is declared here.
  --> src/Attempts.ppphp:5:5
4 | {
5 |     int $attempts = 0;
  |     ^^^^^^^^^^^^^^^^^^
6 |     $attempts = 'three';

Help: Correct the highlighted declaration or expression so it satisfies the stated type contract.

Every diagnostic has the same shape: a severity and code, a title, a one-sentence message, the location as file:line:column, a frame of the source with the problem underlined, any related locations, and a help line. The code is stable across releases, so it is the thing to search for or paste into an issue. The Diagnostics reference lists every code.

Diagnostic families make failures recognizable across CLI, editor, CI, and playground integrations:

Range Concern
P0xxx project and configuration
P1xxx syntax
P2xxx variables and strict types
P3xxx generics and typed arrays
P4xxx checked errors
P5xxx when expressions
P6xxx PHP and Composer interoperability
P7xxx emitted PHP
P9xxx guarded compiler failures

Use JSON in automation

Every project command accepts --format=json:

vendor/bin/ppphp check --format=json

The response has a versioned envelope, source ranges, related labels, help, and a summary:

{
    "version": 1,
    "diagnostics": [],
    "summary": {
        "errors": 0,
        "warnings": 0,
        "notes": 0
    }
}

In a CI script or editor integration, read fields such as code, severity, and location from the JSON instead of parsing console text. Paths and ranges refer to your original source, so annotations can link directly to the line you need to fix.

The documented process statuses are useful in CI:

Exit Meaning
0 success
1 source diagnostics were reported
2 invalid command, project, or configuration
3 generated output validation failed
70 guarded internal compiler failure

Common options

Every project command accepts these options:

Option Effect
--working-directory=<path> Use another project root instead of the current directory
--config=<path> Read a configuration file other than ppphp.json, relative to the project root
--format=console\|json Choose human-readable or machine-readable diagnostics
--ansi / --no-ansi Force or disable coloured console output; colour is also disabled when NO_COLOR is set
--debug Include internal failure detail in guarded compiler failures
-n, --no-interaction Never prompt; init in particular is safe to script with this flag

Work with another project root

You do not have to change the process directory:

vendor/bin/ppphp check \
  --working-directory=/srv/example \
  --config=config/ppphp.json

Relative source selections resolve from the selected project root and must remain beneath a configured source root.

Maintain the runtime mapping

To run the compiled application, Composer must load classes from generated PHP rather than .ppphp source. Use composer:configure to update those paths. Preview the change, apply it, then refresh the lock file and autoloader:

vendor/bin/ppphp composer:configure --dry-run
vendor/bin/ppphp composer:configure
composer update --lock --no-interaction --no-scripts
vendor/bin/ppphp build
composer dump-autoload --optimize

Application entrypoints then load classes and functions from build/ppphp. Source .ppphp files are never executed directly.

See Project configuration for the complete ppphp.json field reference and the contract between runtime mappings and extra.ppphp source metadata.

Clean safely

Preview cleanup before deleting generated files and cached data:

vendor/bin/ppphp clean --dry-run
vendor/bin/ppphp clean

clean validates every target, refuses paths outside the project root, and never deletes a source directory. It does not remove Composer dependencies or application-owned build assets.

Next, read Strict project-wide types to understand what check proves, or Migrating from PHP to plan a gradual migration.