Declarative multi-layer file scaffolding for PHP projects
Distribute project files through Composer providers and manage them with a standalone Console CLI.
composer config allow-plugins.php-forge/scaffold true
composer require --dev php-forge/scaffold:^0.3Require the providers and list, in order, the ones allowed to write files into the project:
{
"require-dev": {
"php-forge/baseline": "^0.2",
"php-forge/coding-standard": "^0.3",
"php-forge/scaffold": "^0.3"
},
"config": {
"allow-plugins": {
"php-forge/scaffold": true
}
},
"extra": {
"scaffold": {
"allowed-packages": ["php-forge/baseline", "php-forge/coding-standard"]
}
}
}Run composer install. The plugin copies the provider files, merges the shared ones, writes scaffold-lock.json and
prints one summary line:
[scaffold] 10 files written, 1 unchanged, 1 warning.
Review the result with git diff and commit the files together with scaffold-lock.json.
| Setting | Default | Effect |
|---|---|---|
extra.scaffold.allowed-packages |
[] |
Providers allowed to write files, in order; the later provider wins when two ship the same file. |
extra.scaffold.auto |
true |
false disables the Composer hooks; apply the providers with vendor/bin/scaffold sync instead. |
Providers choose a mode per file: replace (provider-owned, skipped once you edit it), preserve (seeded once, then
yours), append (format-aware merge for .editorconfig, .gitignore and similar files) or prepend. The lock records
the owner, the mode and two hashes per file:
{
"providers": {
"php-forge/baseline": {
"version": "0.2.0",
"path": "vendor/php-forge/baseline"
}
},
"files": {
".styleci.yml": {
"hash": "sha256:825f8c744ffbd2bc2369712e7192b8694129a8ecbc3fd91dc9300fccecf827bd",
"provider": "php-forge/baseline",
"source": "metadata/.styleci.yml",
"mode": "replace",
"stub-hash": "sha256:825f8c744ffbd2bc2369712e7192b8694129a8ecbc3fd91dc9300fccecf827bd"
}
}
}hash is the file as scaffold last left it and stub-hash the provider source at that moment, so scaffold can tell
your edits apart from provider updates.
The plugin installs a standalone console application at vendor/bin/scaffold. Every command finds the project root by
walking up to the nearest composer.json; the global --working-dir (-d) option starts the search elsewhere.
| Command | What it does |
|---|---|
status [--format=table|json] [--exit-code] |
Shows each file as synced, pending, outdated, modified, conflict, missing, orphan, ejected or error. |
diff <file>|--all [--exit-code] |
Shows what applying the providers would change, as a unified diff from the current file. |
reapply [<file>] [--provider=<name>] [--force] [--dry-run] [--exit-code] |
Applies the stubs of tracked files again; --force also takes user-modified, preserve and ejected files. |
eject <file> [--yes] |
Marks a file as yours; no run writes it again until reapply <file> --force. |
prune [--force] [--delete-modified] [--exit-code] |
Lists files no allowed provider ships anymore; --force deletes the unmodified ones and drops the entries. |
sync [--dry-run] [--exit-code] [--force] |
Applies every allowed provider outside Composer, for auto: false, --no-plugins runs and bots. |
providers |
Lists the providers recorded in scaffold-lock.json with their file counts. |
Typical workflow after composer install or composer update:
vendor/bin/scaffold status # what needs attention
vendor/bin/scaffold diff --all # what applying the providers would change
vendor/bin/scaffold reapply .styleci.yml --force # take the provider version of a file you edited
vendor/bin/scaffold eject .styleci.yml --yes # or keep yours for good
vendor/bin/scaffold prune # review files the providers no longer shipGate CI on drift between the committed files and the providers:
vendor/bin/scaffold status --exit-codeUpgrading from yii2-extensions/scaffold 0.2? Read UPGRADE.md.