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, exposes a reusable GitHub Actions workflow and includes starter templates for GitLab CI, Bitbucket Pipelines and Forgejo Actions.
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 |
| PHPProbe | Git-aware PHP syntax validation and duplicate detection |
| Deptrac | Architecture boundary checks |
| PHPStan | Static analysis and cognitive complexity |
| Psalm | Security and taint analysis |
| Rector | Refactor checks and automated refactors |
| PHPBench | Benchmarks |
| Composer Normalize | composer.json normalization |
| Composer audit | Release/security audit guard |
Install in the consuming project (you will go through some series of approval, check and allow):
composer require --dev infocyph/phpforge:dev-mainIf approval is needed (if not allowed in primary run or missed somehow), run:
composer config allow-plugins.infocyph/phpforge true
composer config allow-plugins.ergebnis/composer-normalize 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/resources/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 pre-commit config (validate, audit, parallel CI)?
Install GitHub Actions workflow wrapper (parallel CI, SARIF, SVG report)?
Install GitLab CI pipeline (.gitlab-ci.yml)?
Install Bitbucket pipeline (bitbucket-pipelines.yml)?
Install Forgejo workflow (.forgejo/workflows/security-standards.yml)?
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 job?
Generate SVG security and quality report artifacts?
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 live 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.
Depending on your selections, ic:init can generate:
captainhook.json
.github/workflows/security-standards.yml
.gitlab-ci.yml
bitbucket-pipelines.yml
.forgejo/workflows/security-standards.yml
ic:init sets up hook/workflow wrappers only. Publish checker or architecture config separately with composer ic:publish-config phpprobe.json deptrac.yaml when customization is needed.
After ic:init, run:
composer ic:cicomposer ic:ci is the same path used by the generated workflow and bundled pre-commit hook; it runs syntax first, then the remaining normal quality checks with bounded parallelism.
If 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 --gitlab-ci
composer ic:init --bitbucket-ci
composer ic:init --forgejo-workflow
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, duplicate detection, Deptrac, PHPStan, Psalm security analysis, and Rector dry run. |
composer ic:tests:all |
Alias of ic:tests. |
composer ic:tests:parallel |
Runs syntax first, then executes the remaining quality checks with bounded parallelism and a buffered PASS/FAIL summary. |
composer ic:tests:details |
Runs detailed checks without the parallel Pest shortcut. |
composer ic:test:syntax |
Runs the PHP syntax checker using phpprobe.json, Git ignores, and configured excludes. |
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 against the project root and bundled/project excludes. |
composer ic:test:duplicates |
Runs duplicate detection using phpprobe.json. |
composer ic:test:architecture |
Runs Deptrac architecture checks using deptrac.yaml. |
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. |
Syntax and duplicate settings live in phpprobe.json, with the bundled default used when a project-local file is not present.
PHPForge delegates these checks to vendor/bin/phpprobe; the phpforge syntax and phpforge duplicates commands are thin compatibility gateways that pass the same config to PHPProbe.
Both checks are root-scoped by default because their bundled paths lists are empty; Git-aware PHP discovery is then filtered by the configured exclude lists.
Duplicate detection defaults are aligned with PhpStorm-style clone analysis: variable/literal normalization, fuzzy identifier/call anonymization, structural audit mode, near-miss matching, and a mid-sensitivity token window are enabled.
Use the lower-level binary for custom scans; CLI paths override configured paths, while CLI excludes are added to configured excludes:
php vendor/bin/phpprobe syntax --config=phpprobe.json --exclude=storage
php vendor/bin/phpprobe duplicates --config=phpprobe.json --min-lines=5 --min-tokens=70
php vendor/bin/phpprobe duplicates --config=phpprobe.json --mode=audit --near-miss --json --exclude=tests
php vendor/bin/phpprobe duplicates --config=phpprobe.json --write-baseline=.phpprobe-duplicates-baseline.json
php vendor/bin/phpprobe duplicates --config=phpprobe.json --baseline=.phpprobe-duplicates-baseline.jsonUseful checker options:
| Option | Applies To | Purpose |
|---|---|---|
--config=FILE |
Syntax, duplicates | Reads checker settings from a custom phpprobe.json file. |
--exclude=PATH |
Syntax, duplicates | Excludes one path; repeat it for multiple one-off exclusions. |
--exact |
Duplicates | Disables variable/literal normalization. |
--fuzzy |
Duplicates | Also normalizes identifiers and calls for renamed-code scans. |
--mode=audit |
Duplicates | Enables statement-window matching in addition to token matching. |
--near-miss |
Duplicates | Enables bounded statement/shape similarity for edited clones. |
--min-lines=N |
Duplicates | Sets the minimum duplicated line span. |
--min-tokens=N |
Duplicates | Sets the token fingerprint window size. |
--min-statements=N |
Duplicates | Sets the structural statement window size for audit matching. |
--min-similarity=0.85 |
Duplicates | Sets the near-miss similarity threshold. |
--baseline=FILE |
Duplicates | Suppresses clone groups already captured in a baseline. |
--write-baseline[=FILE] |
Duplicates | Writes the current clone groups as the baseline and exits successfully. |
--json |
Duplicates | Emits machine-readable JSON. |
| Command | Purpose |
|---|---|
composer ic:ci |
Runs the normal CI suite through the same bounded parallel runner as ic:tests:parallel. |
composer ic:ci --prefer-lowest |
Runs the CI set without PHPStan and Psalm for prefer-lowest dependency jobs. |
| Command | Purpose |
|---|---|
composer ic:process |
Runs Composer Normalize, 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 pre-commit and workflow wrappers. |
composer ic:init --captainhook |
Copies only the CaptainHook pre-commit config. |
composer ic:init --workflow --workflow-ref=main |
Copies only the GitHub Actions wrapper and points it at the given PHPForge ref. |
composer ic:init --gitlab-ci |
Copies .gitlab-ci.yml starter pipeline. |
composer ic:init --bitbucket-ci |
Copies bitbucket-pipelines.yml starter pipeline. |
composer ic:init --forgejo-workflow |
Copies .forgejo/workflows/security-standards.yml starter workflow. |
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 phpprobe.json --phpprobe-preset=strict |
Publishes phpprobe.json using a named duplicate-detection preset (phpstorm, standard, strict). |
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.
For every bundled PHPForge config in resources/, lookup is:
- Project root config, for example
pint.jsonorphpstan.neon.dist. - Installed package config under
vendor/infocyph/phpforge/resources. - PHPForge source-tree
resources/only when the current project itself isinfocyph/phpforge.
If none of those exists outside the PHPForge source project, PHPForge fails instead of silently inventing a config path.
| Tool | Lookup Order |
|---|---|
| Pest / PHPUnit | pest.xml, then phpunit.xml, then pest.xml.dist, then phpunit.xml.dist, then bundled pest.xml |
| PHPBench | phpbench.json, then bundled phpbench.json |
| PHPProbe checker tasks | phpprobe.json, then bundled phpprobe.json |
| Deptrac | deptrac.yaml, then bundled deptrac.yaml |
| 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 |
phpprobe.json configures PHPProbe syntax and duplicate-code checks.
Both sections use root-scoped discovery when paths is empty: PHPProbe asks Git for tracked/unignored PHP files, then falls back to recursively scanning the project root if Git is unavailable.
Use exclude to keep tests, generated files, caches, vendor packages, and other noisy paths out of the checker tasks.
Bundled default:
{
"syntax": {
"paths": [],
"exclude": [
"tests",
"vendor",
"node_modules",
".git",
".idea",
".vscode",
"coverage",
".phpunit.cache",
".psalm-cache",
"build",
"dist",
"tmp",
".tmp",
"storage",
"bootstrap/cache",
"var/cache"
]
},
"duplicates": {
"paths": [],
"exclude": [
"tests",
"vendor",
"node_modules",
".git",
".idea",
".vscode",
"coverage",
".phpunit.cache",
".psalm-cache",
"build",
"dist",
"tmp",
".tmp",
"storage",
"bootstrap/cache",
"var/cache",
"storage/framework/views"
],
"mode": "audit",
"normalize": true,
"fuzzy": true,
"near_miss": true,
"min_lines": 5,
"min_tokens": 90,
"min_statements": 4,
"min_similarity": 0.85,
"baseline": "",
"write_baseline": "",
"json": false
}
}| Key | Purpose |
|---|---|
syntax.paths |
PHP files/directories checked by phpforge syntax. Empty means Git-aware project-root discovery. |
syntax.exclude |
Paths removed from syntax discovery. The bundled config excludes tests, vendor, caches, build output, storage, and common IDE/tool directories. |
duplicates.paths |
PHP files/directories scanned by phpforge duplicates. Empty means Git-aware project-root discovery. |
duplicates.exclude |
Paths removed from duplicate detection. The bundled config excludes tests, vendor, caches, build output, storage, and generated framework views. |
duplicates.mode |
audit enables structural statement matching and near-miss matching; gate is a stricter deterministic CI mode. |
duplicates.normalize |
Replaces noisy variable names and literals before matching. Set false for exact-style matching. |
duplicates.fuzzy |
Also normalizes identifiers/calls for renamed-code audits. More sensitive, potentially noisier. |
duplicates.near_miss |
Finds edited clones through bounded statement/shape similarity. Mostly useful with audit mode. |
duplicates.min_lines |
Minimum duplicated lines before a clone group can be reported. |
duplicates.min_tokens |
Token fingerprint window size. The bundled 90 is PhpStorm-like middle sensitivity; higher values are quieter, lower values are more sensitive. |
duplicates.min_statements |
Statement-window size used by audit/structural matching. |
duplicates.min_similarity |
Near-miss threshold from 0.0 to 1.0; 0.85 means 85 percent similar. |
duplicates.baseline |
Baseline file whose known clone fingerprints are suppressed. |
duplicates.write_baseline |
Writes current clone groups to the given baseline and exits successfully. Usually use CLI for this. |
duplicates.json |
Emits machine-readable JSON instead of human text. Best for automation/reporting. |
exclude_paths is also accepted as an alias for exclude in either section.
Snake case, kebab case, and camel case are accepted for checker config keys, so min_tokens, min-tokens, and minTokens resolve to the same setting.
Explicit CLI paths override configured paths; configured and CLI excludes are combined.
Presets for phpprobe.json publishing:
| Preset | Duplicate Policy |
|---|---|
phpstorm |
PhpStorm-like default: audit mode, normalized/fuzzy tokens, near-miss matching, min_tokens: 90. |
standard |
Balanced CI gate: deterministic gate mode, normalized/fuzzy tokens, no near-miss matching, min_tokens: 100. |
strict |
More sensitive audit mode: near-miss matching enabled with lower size thresholds, min_tokens: 70. |
composer ic:publish-config phpprobe.json --phpprobe-preset=standarddeptrac.yaml configures architecture dependency boundaries. The bundled default scans from the project root, excludes the same noisy/generated paths as the other PHPForge configs, and collects project classes through a generic Project layer instead of hard-coding a package namespace. Publish it when a project is ready to split that baseline into real domain, package, or framework layers.
composer ic:test:architecture
composer ic:publish-config deptrac.yamlCheck active config sources:
composer ic:list-config
composer ic:list-config --jsonPublish config only when a project needs custom rules:
composer ic:publish-config phpprobe.json pint.json phpstan.neon.dist
composer ic:publish-config --allSupported publishable config files:
captainhook.json
deptrac.yaml
pest.xml
phpunit.xml
phpbench.json
phpcs.xml.dist
phpprobe.json
phpstan.neon.dist
pint.json
psalm.xml
rector.php
Use --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_TEST_CONCURRENCY |
3 |
Controls the maximum concurrently running tools for ic:tests:parallel. |
PHPFORGE_PARALLEL |
3 |
Alias for IC_TEST_CONCURRENCY; useful in generic CI parallelism settings. |
PHPFORGE_QUALITY_SUMMARY |
none | Writes an aggregate per-tool quality result JSON file for ic:ci, ic:tests, and ic:tests:parallel. |
IC_QUALITY_SUMMARY |
none | Alias for PHPFORGE_QUALITY_SUMMARY. |
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_TEST_CONCURRENCY=4 composer ic:tests:parallel
PHPFORGE_QUALITY_SUMMARY=var/quality.json composer ic:ci
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 normalize --dry-run
composer ic:release:audit
composer ic:ciThis package also has a root post-autoload-dump script:
"post-autoload-dump": "@php bin/install-captainhook.php"That helper keeps hooks installed for this repository. Consuming projects get automatic hook installation from the PHPForge Composer plugin: it uses project captainhook.json when present, otherwise it copies the bundled captainhook.json into project root and installs hooks from there.
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: true
run_svg_report: true
artifact_retention_days: 61Workflow 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. |
run_svg_report |
true |
Generates security-report.svg and security-summary.json with benchmark status, per-version matrix results, and tool versions. |
artifact_retention_days |
61 |
Artifact retention days for uploaded security-report artifacts. |
php_versions must be a JSON array string because reusable workflow inputs are strings:
with:
php_versions: '["8.2","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"]'Normal workflow workers run composer ic:ci, which delegates to the same bounded parallel runner as ic:tests:parallel.
When the matrix entry is prefer-lowest, PHPForge still runs composer ic:ci --prefer-lowest, skipping heavyweight PHPStan and Psalm checks for that dependency edge.
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.
run_svg_report controls the SVG reporting artifact job:
with:
run_svg_report: trueartifact_retention_days controls how long uploaded report artifacts are kept:
with:
artifact_retention_days: 61Set a shorter value for active development branches or a longer value for scheduled/release runs.
When enabled on main or master, the workflow uploads one artifact:
security-report(containssecurity-report.svgandsecurity-summary.json)
security-summary.json includes:
tested_php_versionsmatrix_results(per PHP version:code_analysis_prefer_lowest,code_analysis_prefer_stable,security_analysis)quality_results(uploaded per-tool JSON summaries from CI workers when available)benchmark_resultbenchmark_commandbenchmark_php_versiontools(toolname, package, resolved version)
security-report.svg renders the same high-level status, per-version matrix check results, per-tool quality chips when quality summaries are available, and resolved tool versions.
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: false
run_svg_report: trueRelease 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.2","8.3"]'
php_extensions: "mbstring, intl, pdo_mysql"
composer_flags: "--ignore-platform-req=ext-redis"
run_analysis: false
run_svg_report: trueProject 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.2","8.3"]'
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 configs (phpstan.neon, then phpstan.neon.dist) and Psalm configs (psalm.xml, then psalm.xml.dist) are used when present; otherwise the workflow falls back to PHPForge defaults.
Generate starter CI files with ic:init:
composer ic:init --gitlab-ci
composer ic:init --bitbucket-ci
composer ic:init --forgejo-workflowGenerated files:
.gitlab-ci.yml(GitLab CI)bitbucket-pipelines.yml(Bitbucket Pipelines).forgejo/workflows/security-standards.yml(Forgejo Actions)
Each template installs dependencies and runs:
composer ic:ciReplace individual QA dependencies with PHPForge.
Before:
"require-dev": {
"captainhook/captainhook": "^5.29.2",
"ergebnis/composer-normalize": "^2.51",
"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": "dev-main"
}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:duplicates |
composer ic:test:duplicates |
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:test:duplicates
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: falseEnsure run_svg_report: true is present in the workflow wrapper:
with:
run_svg_report: trueThen open the workflow run and download security-report.
Publish the relevant config and edit it in the project:
composer ic:publish-config phpprobe.json
composer ic:publish-config phpstan.neon.dist
composer ic:publish-config psalm.xmlProject config files always take priority over bundled defaults.
For syntax or duplicate detector noise, adjust phpprobe.json paths/excludes or duplicate thresholds first.