Composer defines its own set of event scripts, but allows package authors to define custom scripts as well. This research analyzes the Composer package ecosystem to find the names and purposes of those custom scripts, in hopes of revealing standard conventions within that ecosystem.
This research started with the expectation of finding mostly quality-assurance and continuous-integration scripts of these types:
- Testing
- Static Analysis
- Style Fixing
- Linting
- "Suites" to style-fix, lint, static-analyze, and test.
This expectation was met; research revealed other less-common custom scripts as well. It also revealed a common script naming convention.
-
Get the list of all packages on Packagist: vendors/list.json.
-
For each of those, fetch the package JSON files from Packagist ...
<https://packagist.org/p/{$VENDOR}/{$PACKAGE}.json>
... and retain them. -
For each of the fetched package JSON files ...
- skip the package if it has no
"packages":
entry; - skip the package if it has no
"{$VENDOR}/{$PACKAGE}":
entry; - skip the package if it has no
"branches":
entry; - examine first
"branches":
entry; - skip the package if is marked as
abandoned
; - skip the package if it is not hosted at Github (this is to minimize tooling necessary to analyze repositories without downloading them).
- skip the package if it has no
-
For each of the fetched packages, scrape the Github page for the first branch source URL, to download and retain the
composer.json
file for that pacakge.
Whereas the Packagist list.json
file indicates 350642 packages total,
collection netted only 229662 composer.json
files, for a 34.5% attrition
rate. The attrition is due to the conditions for skipping packages as noted
above:
- Lost 76 packages because they could not be retrieved from Packagist.
- Lost 76 packages because the Packagist "packages" entry was empty.
- Lost 84265 packages because the Packagist "{$VENDOR}/{$PACKAGE}" entry was empty.
- Lost 23061 packages because package was marked as "abandoned".
- Lost 10641 packages because they were not hosted at Github.
- Lost 2861 packages because no composer.json href could be found at Github.
The attrition results are are at results/attrition.json.
To aid analysis, collate all script
definitions in all collected
composer.json
files by package name and script name.
The collated results indicate that of the 229662 packages collected:
-
41036 packages define at least one script (including Composer event scripts);
-
36521 packages define at least one non-event script.
The results are at results/collated.json.
Using the collated results ...
-
Find how many packages define at least one script, including Composer event scripts. (The count is 41036 packages).
-
Find how many packages define at least one custom non-event script. (The count is 36251 packages.)
-
Find the most-common script name that is not a Composer event script. (That script name is
test
, occurring 26658 times among the 36251 packages with at least one non-event script.) -
Record all other script names occuring at least 99.7% (roughly 3 sigma) as often as the most-common script name. That is, for a script to be recorded below, it had to occur at least 80 times.
-
Record the underlying commands in
composer.json
for each script name. For a command to be recorded below, it had to occur at least 99.7% (roughly 3 sigma) as often as the script name itself. For example, thetest
script occurs 26658 times, so a command had to occur at least 80 times for it to be recorded as atest
command.
The script names and counts, with their underlying commands and counts, are at results/analyzed.json.
Given the research expectations, it was straightforward to group the script intentions into one of the following categories:
- Testing
- Static Analysis
- Style Fixing
- Linting
- "Suites" to style-fix, lint, static-analyze, and test.
There were several other unexpected categories, noted below.
These scripts represent some form of testing, typically via phpunit
:
test
-- runsphpunit
, typically in a default configurationphpunit
-- runsphpunit
, typically in a default configurationtest:unit
-- runspest
,phpunit
, or another testing tool in various configurationsinfection
-- runs theinfection
mutation testing toolunit
-- runsphpunit
in various configurationstest-unit
-- runsphpunit
in various configurationstest:integration
-- runsphpunit
for integration (not unit) teststester
-- runs the Nettetester
toolunit-test
-- runsphpunit
in various configurationsphpspec
-- runs thephpspec
BDD tooltest:phpunit
-- runsphpunit
in various configurationsbehat
-- runs thebehat
BDD testing toolunit-tests
-- runsphpunit
in various configurationsrun-tests
-- runsphpunit
in various configurationstest-f
-- runsphpunit
with a--filter
flag
Additionally, the following scripts represent some form of testing with
coverage enabled, almost always via phpunit --coverage-*
, in various
configurations:
test-coverage
coverage
test-ci
test:coverage
test:ci
cover
test-cover
coverage-html
test-with-coverage
These scripts run some form of static analysis tool, such as phpstan
, psalm
,
etc.
phpstan
-- runsphpstan
in various configurationsanalyse
-- runsphpstan
in various configurationspsalm
-- runspsalm
in various configurationsstan
-- runsphpstan
in various configurationsanalyze
-- runsphpstan
in various configurationsphan
-- runsphan
in various configurationsstatic-analysis
-- runs one of several static analysis toolstest:types
-- runsphpstan
in various configurationssa
-- runs one of several static analysis tools
These scripts run some form of coding-style fixer, such as phpcbf
, phpcs
,
php-cs-fixer
, and ecs
.
cs-fix
-- runsphpcbf
in various configurationscs-check
-- runsphpcs
in various configurationsphpcs
-- runsphpcs
, typically in a default configurationfix-style
-- runsphpcbf
in various configurationscheck-style
-- runsphpcs
in various configurationsformat
-- runsphp-cs-fixer
in various configurationscs
-- runsphpcs
orphp-cs-fixer
in various configurationsfix
-- runsphp-cs-fixer
in various configurationsphpcbf
-- runsphpcbf
, typically in a default configurationfixer
-- runsphp-cs-fixer
, typically in a default configurationfix-cs
-- runsecs
orphp-cs-fixer
in various configurationscheck-cs
-- runsecs
orphp-cs-fixer
in various configurationsphp-cs-fixer
-- runsphp-cs-fixer
in various configurationssniff
-- runsphpcs
in various configurationscsfix
-- runs one of several style fixersstyle
-- runs one of several style fixerscs-fixer
-- runsecs
orphp-cs-fixer
in various configurationscs:fix
-- runsphp-cs-fixer
in various configurationscbf
-- runsphpcbf
in various configurationsstyle-check
-- runsphp-cs-fixer
in various configurationscs:check
-- runsphp-cs-fixer
in various configurationsecs
-- runsecs
in various configurationsstyle-fix
-- runs one of several style fixersphpcs-fix
-- runs one of several style fixers
Surprisingly, scripts with lint
in the name only rarely represent syntax
linting proper (a la php -l
). Instead, the term lint
occurs mostly as a
synonym for style fixes.
lint
-- runsphpcs
,php-cs-fixer
,phplint
,parallel-lint
, etc.lint-fix
-- runsphpcbf
,ecs
, orphp-cs-fixer
phplint
-- runsphp -l
,parallel-lint
, orphplint
lint:fix
-- runsphpcbf
ci:php:lint
-- runsphp -l
lint-php
-- runsparallel-lint
orphplint
lint-clean
-- runsphpcbf
test:lint
-- runsphp-cs-fixer
,php -l
, orpint
These scripts represent some form of a "suite" of checks, combining two or more other scripts for testing, static analysis, style fixes, and other checks.
check
ci
build
all
qa
There is very little commonality regarding the commands, and their order, from package to package.
These scripts were not expected at the beginning of this research, and are recorded below for further research if warranted. They occur much less frequently than the above categories.
Other metrics tooling:
phpmd
-- runs thephpmd
toodinspect
-- runs one ofdeptrac
,phpstan
,psalm
,php-cs-fixer
, etc.metrics
-- runs thephpmetrics
toolphpcpd
-- runs thephpcpd
tool
Documentation generators:
docs
-- runs one ofphpdoc
,sami
,swagger
, etcdoc
-- runs one ofsami
,phpdox
,phpdoc
,doctum
,apigen
, etc.
Built-in server commands:
serve
-- starts the built-in serverstart
-- starts the built-in server
Other or uncategorized:
auto-scripts
-- runssymfony-cmd
ornpm
clean
-- clears one or more cachescghooks
-- runs thechooks
toolpost-merge
-- runscomposer install
rector
-- runs therector
toolwatch
-- runs thephpunit-watcher
tooltest-watch
-- runs thephpunit-watcher
tooldevelopment-(enable|disable|status)
-- // Laminas-related commandsstan-setup
-- a phpstan setup commandrelease
-- runs a release processupload-coverage
-- uploads test coverage filescoveralls
-- uploads test coverage files
This document recommends using the most-commonly occurring script name for each indicated purpose, independent of any particular tool being used as the script name. Thus:
If the composer.json file defines a script to ... |
... then it MUST be named: |
---|---|
Run tests using a default configuration | test |
Run tests using a default configuration, with coverage generation | test-coverage |
Run tests using alternative configurations or approaches | test-* (1) |
Run a coding style fixer and/or code linter using a default configuration | cs-fix |
Run static analysis using a default configuration | analyse OR analyze (2) |
Run multiple QA scripts or commands in sequence (3) | check |
Notes:
-
E.g.:
test-integration
,test-system
,test-filter
,test-behavior
, etc. -
This allows for both British and American English common usage.
-
Neither the particular tools to be run, nor the order in which to run them, are specified by this document.
Finally, one unexpected result is the appearance of a naming convention for custom Composer scripts.
-
All non-event scripts used lower-case only for their names.
-
For multi-word non-event script names, dashes occurred 35 times, colons occurred 12 times, and underscores did not occur at all.
Thus, this document recommends that script names MUST use all lower-case, with dashes (not colons or underscores) as word separators.
None of the six private reviewers noted faults with the methodology. There were two observations regarding the data itself:
-
One reviewer observed how few packages use non-event scripts. Of 229662 packages, only 36521 (15%) define at least one non-event script. (This reviewer also opined that using
make
is a superior alternative to using a package manager to run scripts.) -
One reviewer observed the increased attrition rate from the previous PDS publication (pds/skeleton). The prior attrition rate was 8.5%, whereas the rate here is 34.5%.
There were two objections to the initial recommendation:
-
One reviewer objected to including
check
as the recommended script name for running two or more QA tools in sequence, due to its low absolute frequency (1899 uses out of 36521 script names, or about 5%). -
One reviewer objected to using
MUST
regarding dashes as word separators in the naming convention. The reviewer feltSHOULD
was more appropriate, in deference to the Symfony practice of using colons as separators in some cases.
No other objections to the initial recommendation were raised by the private reviewers.
Finally, the reviewers indicated some drafting errors, such as typos.
TBD.
TBD.