Shared Composer-powered QA, refactoring, benchmark, release, hook, and CI tooling for Infocyph PHP projects.
PHPForge is installed as a dev dependency in PHP libraries and packages. It provides Composer commands under the ic:* namespace, ships default tool configuration, installs CaptainHook hooks, and exposes a reusable GitHub Actions workflow.
PHPForge brings these tools through one package:
| Tool | Used For |
|---|---|
| CaptainHook | Git hook installation and pre-commit checks |
| Pest | Test execution |
| Laravel Pint | Code style checks and fixes |
| PHP_CodeSniffer / PHPCBF | Semantic sniffing and fixable sniff repairs |
| PHPStan | Static analysis and cognitive complexity |
| Psalm | Security and taint analysis |
| Rector | Refactor checks and automated refactors |
| PHPBench | Benchmarks |
| Composer audit | Release/security audit guard |
Install in the consuming project:
composer require --dev infocyph/phpforge:dev-mainComposer may ask for plugin approval. If approval is needed, run:
composer config allow-plugins.infocyph/phpforge true
composer config allow-plugins.pestphp/pest-plugin true
composer installInspect the detected setup:
composer ic:doctorJSON diagnostics are available for automation:
composer ic:doctor --jsonFor coding agents, CI agents, or external automation working inside a project that uses PHPForge, start with:
vendor/infocyph/phpforge/AGENTS.md
It summarizes project commands, verification steps, config priority rules, workflow inputs, hook behavior, and agent expectations. If an agent only auto-discovers root-level instruction files, copy or reference it from the project root as AGENTS.md.
Common daily commands:
composer ic:tests
composer ic:process
composer ic:benchmark
composer ic:release:guardInitialize optional project files:
composer ic:initic:init is interactive by default. It uses selector prompts for common choices and keeps a custom option for project-specific values:
Install CaptainHook config?
Install GitHub Actions workflow wrapper?
PHPForge workflow ref
PHP version matrix
Dependency matrix
PHP extensions
Coverage driver
Extra Composer flags
PHPStan memory limit
Psalm threads
Enable SARIF code-scanning analysis?
Selector presets include:
| Prompt | Built-in Choices |
|---|---|
| PHPForge workflow ref | main, configured ref, or custom |
| PHP version matrix | supported, current, stable, or custom JSON. Presets resolve from Endoflife API (https://endoflife.date/api/php.json) with fallback to ["8.2","8.3","8.4","8.5"]. |
| Dependency matrix | full => ["prefer-lowest","prefer-stable"], stable => ["prefer-stable"], or custom JSON. Prompt shows resolved JSON beside each option. |
| PHP extensions | none => "", detected (from project composer.json ext-* entries in require, require-dev, and suggest), common, mysql, pgsql, mysql+pgsql, or custom |
| Coverage driver | none, xdebug, or pcov |
| Extra Composer flags | none => "", with-all-dependencies => --with-all-dependencies, ignore-ext-redis => --ignore-platform-req=ext-redis, or custom. Prompt explains each option effect. |
| PHPStan memory limit | 1G, 2G, 4G, or custom |
| Psalm threads | 1, 2, 4, or custom |
supported includes non-EOL PHP minor cycles (>= 8.2), current uses the latest two supported cycles, and stable uses the latest supported cycle.
PHP version, dependency matrix, PHP extensions, and Composer flags selectors show resolved values in the prompt and print the final resolved value after selection.
The generated files are:
captainhook.json
.github/workflows/security-standards.yml
After ic:init, run:
composer ic:testsIf captainhook.json was installed, hooks auto-install on the next composer install or composer update.
Use composer ic:hooks only when you want to install/update hooks immediately.
Use targeted or non-interactive init commands when needed:
composer ic:init --captainhook
composer ic:init --workflow --workflow-ref=main
composer ic:init --no-interaction-defaults
composer ic:init --force| Command | Purpose |
|---|---|
composer ic:tests |
Full project quality suite: syntax, Pest parallel tests, Pint check, PHPCS summary, PHPStan, Psalm security analysis, and Rector dry run. |
composer ic:tests:all |
Alias of ic:tests. |
composer ic:tests:details |
Runs detailed checks without the parallel Pest shortcut. |
composer ic:test:syntax |
Checks project PHP files while respecting .gitignore, .git/info/exclude, and global Git ignore rules. |
composer ic:test:code |
Runs Pest. |
composer ic:test:lint |
Runs Pint in check mode. |
composer ic:test:sniff |
Runs PHPCS with a full report. |
composer ic:test:static |
Runs PHPStan. |
composer ic:test:security |
Runs Psalm security analysis. |
composer ic:test:refactor |
Runs Rector in dry-run mode. |
composer ic:test:bench |
Runs PHPBench aggregate benchmarks. |
| Command | Purpose |
|---|---|
composer ic:ci |
Runs syntax, Pest, Pint, PHPCS, Rector, PHPStan, and Psalm. |
composer ic:ci --prefer-lowest |
Runs the CI set without PHPStan and Psalm for prefer-lowest dependency jobs. |
| Command | Purpose |
|---|---|
composer ic:process |
Runs Rector, Pint, and PHPCBF fixes. |
composer ic:process:all |
Alias of ic:process. |
composer ic:process:refactor |
Runs Rector fixes. |
composer ic:process:lint |
Runs Pint fixes. |
composer ic:process:sniff |
Runs PHPCBF fixes. |
composer ic:process:sniff:fix |
Alias of ic:process:sniff. |
| Command | Purpose |
|---|---|
composer ic:benchmark |
Runs PHPBench aggregate benchmarks. |
composer ic:bench:run |
Alias of ic:benchmark. |
composer ic:bench:quick |
Runs a shorter PHPBench pass. |
composer ic:bench:chart |
Runs PHPBench chart report. |
| Command | Purpose |
|---|---|
composer ic:release:audit |
Runs Composer audit. Security advisories fail; abandoned packages warn. |
composer ic:release:guard |
Runs Composer validation, audit, and the full test suite. |
| Command | Purpose |
|---|---|
composer ic:init |
Interactively sets up CaptainHook and the workflow wrapper. |
composer ic:init --captainhook |
Copies only captainhook.json. |
composer ic:init --workflow --workflow-ref=main |
Copies only the workflow wrapper and points it at the given PHPForge ref. |
composer ic:init --no-interaction-defaults |
Copies default init files without prompting. |
composer ic:init --force |
Overwrites existing copied files. |
composer ic:hooks |
Installs enabled CaptainHook hooks. |
composer ic:doctor |
Shows detected configs, vendor-dir, plugin permissions, hook status, and workflow wrapper validation warnings. |
composer ic:doctor --json |
Outputs doctor diagnostics as JSON, including workflow wrapper validation details. |
composer ic:list-config |
Lists config files and their resolution source. |
composer ic:list-config --json |
Outputs config resolution as JSON. |
composer ic:publish-config [file...] |
Copies selected bundled config files into the project. |
composer ic:publish-config --all |
Copies every bundled config file into the project. |
composer ic:publish-config --all --force |
Overwrites all project config files with bundled defaults. |
composer ic:clean |
Removes known PHPForge output files and cache directories. |
composer ic:version |
Shows PHPForge, PHP, PHP binary, and vendor-dir information. |
composer ic:phpstan:sarif input.json output.sarif |
Converts PHPStan JSON output to SARIF 2.1.0. |
Project config files always have priority over PHPForge bundled defaults.
| Tool | Lookup Order |
|---|---|
| Pest | pest.xml, then phpunit.xml, then pest.xml.dist, then phpunit.xml.dist, then bundled pest.xml |
| PHPBench | phpbench.json, then bundled phpbench.json |
| PHPCS / PHPCBF | phpcs.xml.dist, then bundled phpcs.xml.dist |
| PHPStan | phpstan.neon.dist, then bundled phpstan.neon.dist |
| Pint | pint.json, then bundled pint.json |
| Psalm | psalm.xml, then bundled psalm.xml |
| Rector | rector.php, then bundled rector.php |
| CaptainHook | captainhook.json, then bundled captainhook.json |
Check active config sources:
composer ic:list-config
composer ic:list-config --jsonPublish config only when a project needs custom rules:
composer ic:publish-config pint.json phpstan.neon.dist
composer ic:publish-config --allUse --force to overwrite existing files:
composer ic:publish-config psalm.xml --force| Variable | Default | Purpose |
|---|---|---|
IC_PEST_PROCESSES |
10 |
Controls Pest parallel processes for ic:tests. |
IC_PHPSTAN_MEMORY_LIMIT |
1G |
Controls PHPStan memory limit. |
IC_PSALM_THREADS |
1 |
Controls Psalm thread count. |
IC_HOOKS_STRICT |
1 |
Fails Composer when automatic CaptainHook install fails. Set to 0 for best-effort hook installation. |
Example:
IC_PEST_PROCESSES=4 composer ic:tests
IC_PHPSTAN_MEMORY_LIMIT=2G composer ic:test:static
IC_HOOKS_STRICT=0 composer installInstall the bundled CaptainHook configuration:
composer ic:init --captainhook
composer ic:hooksThe bundled pre-commit hook runs:
composer validate --strict
composer ic:release:audit
composer ic:testsThis package also has a root post-autoload-dump script:
"post-autoload-dump": "captainhook install --only-enabled -nf"That keeps hooks installed for this repository. Consuming projects get automatic hook installation from the PHPForge Composer plugin when captainhook.json exists.
PHPForge publishes a reusable workflow:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@mainInstall a wrapper workflow into a consuming project:
composer ic:initFor automated setup, skip prompts and choose the reusable workflow ref:
composer ic:init --workflow --workflow-ref=main --no-interaction-defaultsGenerated wrapper shape:
name: "Security & Standards"
on:
schedule:
- cron: "0 0 * * 0"
push:
branches: [ "main", "master" ]
pull_request:
branches: [ "main", "master", "develop", "development" ]
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
permissions:
security-events: write
actions: read
contents: read
with:
php_versions: '["8.2","8.3","8.4","8.5"]'
dependency_versions: '["prefer-lowest","prefer-stable"]'
php_extensions: ""
coverage: "none"
composer_flags: ""
phpstan_memory_limit: "1G"
psalm_threads: "1"
run_analysis: trueWorkflow inputs:
| Input | Default | Purpose |
|---|---|---|
php_versions |
["8.2","8.3","8.4","8.5"] |
PHP matrix as a JSON array string. |
dependency_versions |
["prefer-lowest","prefer-stable"] |
Composer dependency modes as a JSON array string. |
php_extensions |
"" |
Comma-separated PHP extensions passed to shivammathur/setup-php. |
coverage |
none |
Coverage driver passed to shivammathur/setup-php; use xdebug, pcov, or none. |
composer_flags |
"" |
Extra flags appended to Composer install/update commands. |
phpstan_memory_limit |
1G |
PHPStan memory limit used by workflow analysis. |
psalm_threads |
1 |
Psalm thread count used by workflow analysis. |
run_analysis |
true |
Runs SARIF upload jobs for PHPStan and Psalm. Set to false for CI-only runs. |
php_versions must be a JSON array string because reusable workflow inputs are strings:
with:
php_versions: '["8.3","8.4","8.5"]'Use a smaller matrix for faster daily CI, or the full supported range for release confidence.
dependency_versions controls Composer update mode:
with:
dependency_versions: '["prefer-stable"]'For release confidence, keep both modes:
with:
dependency_versions: '["prefer-lowest","prefer-stable"]'When the matrix entry is prefer-lowest, PHPForge runs composer ic:ci --prefer-lowest, skipping heavyweight PHPStan and Psalm checks for that entry.
php_extensions is passed to shivammathur/setup-php:
with:
php_extensions: "mbstring, intl, bcmath, pdo_mysql, pdo_pgsql"Leave it empty when no extra extensions are needed:
with:
php_extensions: ""coverage controls the setup-php coverage driver:
with:
coverage: "none"Common values:
coverage: "none"
coverage: "xdebug"
coverage: "pcov"composer_flags appends extra flags to Composer install/update:
with:
composer_flags: "--ignore-platform-req=ext-redis"Multiple flags can be passed as one string:
with:
composer_flags: "--ignore-platform-req=ext-redis --with-all-dependencies"phpstan_memory_limit controls PHPStan memory in both quality gates and SARIF generation:
with:
phpstan_memory_limit: "2G"psalm_threads controls Psalm parallelism:
with:
psalm_threads: "2"run_analysis controls the SARIF upload job:
with:
run_analysis: falseSet it to false when the repository does not use GitHub code scanning, does not grant security-events: write, or wants CI-only runs.
Fast CI for active development:
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
with:
php_versions: '["8.4","8.5"]'
dependency_versions: '["prefer-stable"]'
run_analysis: falseRelease confidence matrix:
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
permissions:
security-events: write
actions: read
contents: read
with:
php_versions: '["8.2","8.3","8.4","8.5"]'
dependency_versions: '["prefer-lowest","prefer-stable"]'
run_analysis: trueProject with extensions and no SARIF upload:
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
with:
php_versions: '["8.3","8.4"]'
php_extensions: "mbstring, intl, pdo_mysql"
composer_flags: "--ignore-platform-req=ext-redis"
run_analysis: falseProject with extensions, coverage, and larger analysis limits:
jobs:
phpforge:
uses: infocyph/phpforge/.github/workflows/security-standards.yml@main
permissions:
security-events: write
actions: read
contents: read
with:
php_versions: '["8.3","8.4"]'
dependency_versions: '["prefer-stable"]'
php_extensions: "mbstring, intl, bcmath, pdo_mysql"
coverage: "xdebug"
composer_flags: "--ignore-platform-req=ext-redis"
phpstan_memory_limit: "2G"
psalm_threads: "2"
run_analysis: trueFor code scanning, project-local phpstan.neon.dist and psalm.xml are used when present; otherwise the workflow falls back to PHPForge defaults.
Replace individual QA dependencies with PHPForge.
Before:
"require-dev": {
"captainhook/captainhook": "^5.29.2",
"laravel/pint": "^1.29",
"pestphp/pest": "^4.6.3",
"pestphp/pest-plugin-drift": "^4.1",
"phpbench/phpbench": "^1.6.1",
"phpstan/phpstan": "^2.1.50",
"rector/rector": "^2.4.2",
"squizlabs/php_codesniffer": "^4.0.1",
"symfony/var-dumper": "^7.3 || ^8.0.8",
"tomasvotruba/cognitive-complexity": "^1.1",
"vimeo/psalm": "^6.16.1"
}After:
"require-dev": {
"infocyph/phpforge": "^1.0"
}Remove old local QA scripts such as:
test:*
process:*
bench:*
tests
process
benchmark
release:audit
release:guard
post-autoload-dump
Replace commands:
| Old command | New command |
|---|---|
composer tests / composer test:all |
composer ic:tests |
composer test:details |
composer ic:tests:details |
composer test:syntax |
composer ic:test:syntax |
composer test:code |
composer ic:test:code |
composer test:lint |
composer ic:test:lint |
composer test:sniff |
composer ic:test:sniff |
composer test:static |
composer ic:test:static |
composer test:security |
composer ic:test:security |
composer test:refactor |
composer ic:test:refactor |
composer process / composer process:all |
composer ic:process |
composer process:lint |
composer ic:process:lint |
composer process:sniff:fix |
composer ic:process:sniff:fix |
composer process:refactor |
composer ic:process:refactor |
composer benchmark / composer bench:run |
composer ic:benchmark |
composer bench:quick |
composer ic:bench:quick |
composer bench:chart |
composer ic:bench:chart |
composer release:audit |
composer ic:release:audit |
composer release:guard |
composer ic:release:guard |
Old helper scripts are no longer needed:
.github/scripts/syntax.php
.github/scripts/composer-audit-guard.php
.github/scripts/phpstan-sarif.php
PHPForge provides those through:
composer ic:test:syntax
composer ic:release:audit
composer ic:phpstan:sarif phpstan-results.json phpstan-results.sarifThe plugin is not active. Enable plugin permissions and reinstall:
composer config allow-plugins.infocyph/phpforge true
composer installBy default hook installation is strict. To make it best-effort:
IC_HOOKS_STRICT=0 composer installThen inspect manually:
composer ic:doctor
composer ic:hooksSet run_analysis: false in the workflow wrapper if the repository does not have SARIF upload permission:
with:
run_analysis: falsePublish the relevant config and edit it in the project:
composer ic:publish-config phpstan.neon.dist
composer ic:publish-config psalm.xmlProject config files always take priority over bundled defaults.