stevebauman/unfinalize
Unfinalize is a Composer plugin for Laravel and PHP projects that strips the final keyword from vendor classes, making third-party classes easier to extend, decorate, or mock in tests without editing package source.
Build vs. buy (or extend vs. fork). When a vendor package marks a class or method final, PMs face a fork-or-workaround decision. This package enables a third option — patching the final keyword at install time — which is typically a fraction of the cost of maintaining a hard fork. It shifts the question from "do we fork?" to "do we patch or wait for upstream?"
Testability as a feature gate. Some capabilities (mocking a final class in a unit test, subclassing a vendor service) are blocked by final declarations. This package unblocks test coverage or extension without touching upstream source control, which can influence whether a test strategy is viable within the current sprint or requires architectural rework.
Time-to-market for integrations. When a third-party SDK or Laravel package needs a small behavioral extension, patching final can ship a feature in days instead of weeks. Useful for roadmap de-risking when upstream releases are slow or the vendor is unresponsive.
Upgrade/replatforming triage. During a Laravel or PHP version bump, existing patches may break. Treat this package as a signal of technical debt: any dependency relying on it should appear on a roadmap as "replace or contribute upstream" rather than a permanent dependency. PMs should track which vendor packages are patched and their upstream maintenance health.
Contribution strategy. Presence of this package in a codebase is a data point for whether your team should invest in open-source contribution (PRs to remove final where appropriate) versus continuing to patch. It informs whether to budget engineering time for upstream engagement.
Adopt when:
final, and the vendor has no near-term plan to change it.Look elsewhere when:
final — that is a cleaner, permanent outcome.final declaration communicates intentional extension constraints (e.g., internal invariants) that your patch would undermine.Operational fit: best suited to teams with a healthy dependency-pinning culture, a reliable CI test suite, and a documented inventory of patched packages. It should be scoped as a bridge, not a durable architectural choice.
For executives: This is a small but high-leverage utility that lets engineering extend third-party code the vendor has locked down, without the cost and ongoing burden of a hard fork. It converts a potentially multi-week rework or a stalled integration into a same-sprint change. The tradeoffs are known and manageable: it introduces a maintenance surface that we should track as technical debt and retire once upstream accepts a change. Recommended as a tactical enabler, gated by engineering ownership of the patch list and CI coverage.
For engineering leadership:
The package removes final keywords from vendor files, typically at install time, enabling subclassing, overriding, and mocking of otherwise sealed classes. It is actively maintained and MIT-licensed, with a small but real user base. Adoption criteria: the vendor has no near-term path to removing final, the change is not security-sensitive, and we have a CI gate plus a pinned-version policy for affected dependencies. It is not a substitute for forking on core dependencies and should not appear in production-critical paths without a documented exit plan.
For engineering teams:
Treat this as a scoped, time-boxed workaround. Every patched vendor package should be listed with the reason, the upstream status, and the removal trigger. Pair it with tests that would catch a vendor update silently reintroducing final or changing signature. Prefer upstream PRs as the durable fix, and budget time to retire the patch rather than letting it calcify.
How can I help you explore Laravel packages today?