symfony/deprecation-contracts

A single global trigger_deprecation() function and convention for standardized PHP deprecation notices

Library
Composer
vv3.7.1
2,099stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
54/100Fair
Development Activity40
Maintenance32
Community44
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture55
Code Quality70
Innovation50
Learning Curve90

symfony/deprecation-contracts is a minimal, single-purpose package: it defines one global function, trigger_deprecation(), as a common convention for libraries to emit deprecation warnings in a structured, machine-parseable format (package name, version, message). Rather than every library inventing its own deprecation-logging approach, packages across the PHP ecosystem depend on this tiny package and call trigger_deprecation('vendor/package', '1.2', 'Message about %s', 'thing') to raise an E_USER_DEPRECATED notice with a standardized “Since vendor/package 1.2: …” prefix.

Because the function is silenced (@trigger_error) by default, it doesn’t break output in production unless the application installs a custom error handler — such as Symfony’s own ErrorHandler component — that specifically captures and logs E_USER_DEPRECATED notices for later discovery in both dev and prod environments. Applications that don’t want deprecation tracking can even declare an empty trigger_deprecation() function themselves to fully suppress it.

What You Get

  • A single global trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void function, autoloaded as a Composer “files” entry
  • A standardized message format (“Since $package $version: …”) that downstream tooling (like Symfony’s ErrorHandler or PHPUnit’s deprecation bridge) can parse consistently
  • Silenced-by-default @trigger_error(..., E_USER_DEPRECATED) behavior so calling it doesn’t disrupt normal output unless something is actively listening for deprecations
  • printf()-style argument interpolation in the deprecation message for dynamic details (which method, which config key, etc.)
  • An escape hatch: applications can declare an empty trigger_deprecation() themselves to fully suppress all deprecation notices from every dependent package at once

Common Use Cases

  • Library authors marking a method, class, or config option as deprecated with a machine-readable notice ahead of a breaking major version
  • Application teams using symfony/phpunit-bridge’s deprecation collector to fail CI when new deprecations are introduced by a dependency bump
  • Logging deprecation notices in production via a custom E_USER_DEPRECATED error handler to plan upgrade work proactively
  • Cross-package deprecation tracking in monorepo-split ecosystems (like Symfony itself) where dozens of components need one consistent convention

Under The Hood

Architecture — There’s effectively no architecture to speak of: the entire package is one PHP file (function.php, ~20 lines) declaring a single conditionally-defined global function. It’s loaded via Composer’s autoload.files mechanism (not PSR-4 classes) so the function is available globally without any use import, by design, since it’s meant to be callable from anywhere in a dependency’s codebase without adding an import statement per call site. The function itself is a thin wrapper around PHP’s built-in trigger_error(), prefixing the message with the standardized “Since $package $version:” text and applying vsprintf() formatting only when extra arguments are passed. Tech Stack — PHP 8.1+, zero runtime dependencies beyond PHP itself — deliberately dependency-free so that adopting it doesn’t pull in any transitive packages. It’s part of the symfony/contracts family of interface/convention packages (alongside things like symfony/cache-contracts, symfony/event-dispatcher-contracts) that exist purely to decouple a convention from any specific implementation. Code Quality — At this scope, code quality is really about correctness and stability of a public convention rather than test coverage of complex logic; the package has been stable across major versions with only additive changes, and its extreme simplicity (one function, no classes, no state) minimizes any surface for bugs. API Design — About as minimal an API surface as is possible: one function, three required positional arguments plus variadic interpolation args, no configuration object, no class to instantiate — designed explicitly so any library, regardless of its own architecture, can adopt it with a single require line and one function call per deprecation site.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search