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.jsonfile 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.jsonfor 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, thetestscript occurs 26658 times, so a command had to occur at least 80 times for it to be recorded as atestcommand.
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 theinfectionmutation testing toolunit-- runsphpunitin various configurationstest-unit-- runsphpunitin various configurationstest:integration-- runsphpunitfor integration (not unit) teststester-- runs the Nettetestertoolunit-test-- runsphpunitin various configurationsphpspec-- runs thephpspecBDD tooltest:phpunit-- runsphpunitin various configurationsbehat-- runs thebehatBDD testing toolunit-tests-- runsphpunitin various configurationsrun-tests-- runsphpunitin various configurationstest-f-- runsphpunitwith a--filterflag
Additionally, the following scripts represent some form of testing with
coverage enabled, almost always via phpunit --coverage-*, in various
configurations:
test-coveragecoveragetest-citest:coveragetest:cicovertest-covercoverage-htmltest-with-coverage
These scripts run some form of static analysis tool, such as phpstan, psalm,
etc.
phpstan-- runsphpstanin various configurationsanalyse-- runsphpstanin various configurationspsalm-- runspsalmin various configurationsstan-- runsphpstanin various configurationsanalyze-- runsphpstanin various configurationsphan-- runsphanin various configurationsstatic-analysis-- runs one of several static analysis toolstest:types-- runsphpstanin 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-- runsphpcbfin various configurationscs-check-- runsphpcsin various configurationsphpcs-- runsphpcs, typically in a default configurationfix-style-- runsphpcbfin various configurationscheck-style-- runsphpcsin various configurationsformat-- runsphp-cs-fixerin various configurationscs-- runsphpcsorphp-cs-fixerin various configurationsfix-- runsphp-cs-fixerin various configurationsphpcbf-- runsphpcbf, typically in a default configurationfixer-- runsphp-cs-fixer, typically in a default configurationfix-cs-- runsecsorphp-cs-fixerin various configurationscheck-cs-- runsecsorphp-cs-fixerin various configurationsphp-cs-fixer-- runsphp-cs-fixerin various configurationssniff-- runsphpcsin various configurationscsfix-- runs one of several style fixersstyle-- runs one of several style fixerscs-fixer-- runsecsorphp-cs-fixerin various configurationscs:fix-- runsphp-cs-fixerin various configurationscbf-- runsphpcbfin various configurationsstyle-check-- runsphp-cs-fixerin various configurationscs:check-- runsphp-cs-fixerin various configurationsecs-- runsecsin 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-fixerphplint-- runsphp -l,parallel-lint, orphplintlint:fix-- runsphpcbfci:php:lint-- runsphp -llint-php-- runsparallel-lintorphplintlint-clean-- runsphpcbftest: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.
checkcibuildallqa
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 thephpmdtoodinspect-- runs one ofdeptrac,phpstan,psalm,php-cs-fixer, etc.metrics-- runs thephpmetricstoolphpcpd-- runs thephpcpdtool
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-cmdornpmclean-- clears one or more cachescghooks-- runs thechookstoolpost-merge-- runscomposer installrector-- runs therectortoolwatch-- runs thephpunit-watchertooltest-watch-- runs thephpunit-watchertooldevelopment-(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
makeis 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
checkas 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
MUSTregarding dashes as word separators in the naming convention. The reviewer feltSHOULDwas 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.