PHP Package Dependency Transparency

This page documents every PHP package PayCal directly depends on, why each is included, and the philosophy behind our approach to third-party dependencies.

Verification Metadata

Our Dependency Philosophy

PayCal's position on third-party dependencies is: use the minimum possible, own what is practical to own, and pin everything else. This is not a reaction to a specific incident — it is the standing default.

  • Every package must justify its existence by active call sites. A zero-reference dependency is removed regardless of CVE status.
  • When a package is only used at one or two narrow call sites, we evaluate whether a first-party implementation is practical. Fewer moving parts means a smaller attack surface.
  • All packages are pinned in composer.lock. No floating versions. No silent upgrades.
  • Transitive dependencies are reviewed as a side effect of direct dependency audits. Orphaned vendor directories are removed.

Runtime Dependencies

These are the only PHP packages loaded in a production request. Each is justified by active usage and has no alternative that would reduce the dependency further without significant first-party engineering effort.

Package Version Purpose Transitive packages pulled in
lbuchs/webauthn 2.2.0 Passkey and FIDO2 registration and authentication for passwordless sign-in. 0
phpoffice/phpspreadsheet 5.7.0 XLSX generation for earnings export downloads. ~9 (maennchen/zipstream-php, markbaker/complex, markbaker/matrix, myclabs/deep-copy, psr/simple-cache, symfony/polyfill-*)
stripe/stripe-php 20.1.0 Stripe billing API: subscription management, webhook verification, customer and payment intent operations. 0

Development-Only Dependencies

Dev-time dependencies are never loaded in production. The following packages are used exclusively during development and CI.

Package Version Purpose Transitive packages pulled in
phpstan/phpstan 2.1.54 Static analysis at Level 9. Enforced in pre-push hook and CI. No baseline file permitted. ~8 (nikic/php-parser, react/*, clue/ndjson-react, fidry/cpu-core-counter, etc.)
phpunit/phpunit 13.1.8 Unit and integration test runner. 1,527 tests across Domain, Controllers, Infrastructure, and Observability layers. ~15 (sebastian/*, phpunit/php-*, phar-io/*, myclabs/deep-copy, etc.)
friendsofphp/php-cs-fixer 3.95.1 Code style enforcement. Dry-run check runs on every staged PHP file in the pre-commit hook and in CI. ~12 (symfony/console, symfony/finder, symfony/event-dispatcher, ergebnis/agent-detector, etc.)

CVE Review and the 2026-05 Audit

Periodically GitHub Dependabot alerts flag packages as vulnerable. Before removing or replacing a package, PayCal verifies the specific CVE status at the installed version using the upstream advisory and the Packagist security database. During the 2026-05 audit, four moderate Dependabot alerts were raised. Investigation confirmed all four were already patched at the installed versions — no remediation action was required.

Separately, two packages — erusev/parsedown and yupmin/magoo — were found to have no usage anywhere in the codebase. They were removed regardless of CVE status. A package that is not loaded is a package that cannot be exploited.

Why We Replaced vlucas/phpdotenv

PayCal previously loaded .env files using vlucas/phpdotenv, a widely-used library with three transitive dependencies (phpoption, graham-campbell/result-type). The library was used at exactly two call sites: one safeLoad in the bootstrap file, and one load in config.php.

Rather than maintain an external dependency for 11 lines of active usage, we wrote PayCal\Infrastructure\Env\Dotenv — a 140-line first-party class that covers the full surface we actually use: KEY=VALUE parsing, blank and comment skipping, optional quote stripping, inline comment stripping, immutable mode, and a forceKeys override for specific keys (which eliminated a separate 20-line manual workaround that had existed in config.php for the SMTP password).

The replacement removed 63 vendor files and three packages from the dependency graph. The public source for PayCal\Infrastructure\Env\Dotenv is available in the public repository.

CI/CD Controls

Every change must pass PHPStan Level 9 and the full PHPUnit test suite before it can be merged. Composer is locked to a deterministic dependency graph — no floating versions in production.

For the broader CI/CD operating model beyond PHP packages, see CI/CD Tooling and Release Governance.

  • .github/workflows/phpstan.yml: PHPStan Level 9 on PHP 8.4 and 8.5, includes format:check.
  • .github/workflows/phpunit.yml: fast gate (PHP 8.4) → full validation (PHP 8.5) → deep verification (scheduled).
  • Pre-commit hook: PHPStan baseline check, AST snapshot, quick tests (1,055 tests).
  • Pre-push hook: full PHPStan Level 9 + quick tests. composer audit --locked advisory check.

How to Verify

# List all direct dependencies
composer show --direct

# Check for known security advisories
composer audit --locked

# Review the full dependency graph
composer show

# Inspect first-party .env loader (replaced vlucas/phpdotenv)
cat html/src/Infrastructure/Env/Dotenv.php

Related transparency pages: CI/CD Tooling and Release Governance, Dependency and CI/CD Governance, and Framework and Backend Change Ledger.