composer install
npm installPHP 8.2 is the minimum; CI runs 8.2 through 8.4. Beacon has no runtime
dependencies and should keep none — the framework packages are dev dependencies
here and suggested there.
composer check # style, PHPStan level 8, PHPUnit
npm test # the browser agent, in jsdomIndividually: composer cs, composer cs:fix, composer stan, composer test.
All of it runs in CI on every pull request.
SQLite always runs. MySQL and PostgreSQL run the same contract when you point the tests at a server and skip when you do not, so a green local run does not mean the dialects are fine. CI points them at a server and treats a skip as a failure.
export BEACON_TEST_MYSQL_DSN='mysql:host=127.0.0.1;dbname=beacon;charset=utf8mb4'
export BEACON_TEST_MYSQL_USER=root BEACON_TEST_MYSQL_PASSWORD=secret
export BEACON_TEST_PGSQL_DSN='pgsql:host=127.0.0.1;dbname=beacon'
export BEACON_TEST_PGSQL_USER=postgres BEACON_TEST_PGSQL_PASSWORD=secretCI runs MySQL at +05:00, because a driver that reads the session time zone
passes against a UTC server and fails against that one. Point your local MySQL
off UTC too if you are touching the timeline queries.
src/Assets/agent.js is a classic IIFE, inlined verbatim — no build step, no
transpiler. Write it for the oldest browser you are willing to support.
tests/js/agent.js stands up PerformanceObserver and sendBeacon, which
jsdom lacks, as recorders, and takes the agent's global listeners back off
between tests.
Both real drivers extend tests/StorageContract.php, which is the interface
written down as behaviour. A new driver extends it too.
- The privacy claims are testable, so test them. A change that touches what
is stored needs a test asserting the address and user agent are absent in
anonymousmode.IngestTesthas the pattern. - The safe reading wins. No secret means no visitor token, not a weak one. An unknown mode is off.
- Ingest never throws. Refuse and return zero.
Storage::DIMENSIONSis a security boundary. Dimensions are interpolated into SQL, so anything added there must be a real column name.- Comments are one sentence, or absent. Reasoning goes in the commit message.
Commit subjects are imperative and describe the change, not the file
(fix: count the day files without materialising them). Explain in the body why
the change is right, not what the diff already shows. Mark breaking changes with
!. One concern per pull request.
Add the section to CHANGELOG.md first, then push the tag:
git tag -a v0.2.0 -m v0.2.0 && git push origin v0.2.0A workflow turns the tag into a GitHub release using the matching ## [0.2.0]
section and fails when there is none. It needs no repository secrets.
Packagist reads tags rather than releases, so the version appears as soon as the tag is pushed, provided the repository has the Packagist GitHub App installed.