symplify/vendor-patches
Symplify Vendor Patches generates vendor patches for your Composer packages with a single command. Copy a vendor file with a .old suffix, edit the original, then run generate to create patch files and auto-configure cweagans/composer-patches.
ServiceProvider hook, container binding, Macroable, or Model::resolveRelationUsing can do it, you don't patch. This package supports a defensible "configure first, patch last" policy without forcing a rewrite when the policy fails.cweagans/composer-patches a production dependency or not? For Laravel shops on Forge/Envoyer/Vapor/Cloud running composer install --no-dev, it must be a real require, which is a procurement/compliance and build-pipeline conversation, not just a dev-tooling one.--resolve-from-directory and --patches-file options let you patch packages installed from private VCS or path repositories, or keep the patch map in a separate reviewable artifact — both relevant when you're maintaining internal shared packages across multiple Laravel apps.Adopt when all of these hold:
composer install rather than promoting a cached vendor/ artifact. If you cache vendor/ between CI jobs, the patch model quietly breaks.Look elsewhere when:
cweagans/composer-patches. Evaluate that dependency on its own merits — it's the one that runs in production.Macroable, model relation resolvers, published config, or a thin adapter/wrapper around the dependency will always beat a patch on upgrade cost.Hunk #N FAILED on composer install, diagnosed with -vvv and fixed by hand. If nobody has budget for that, the patch becomes an outage waiting for a routine upgrade.patch can hang applying patches; gpatch via Homebrew is a hard prerequisite on developer machines. Small, but it's a real onboarding friction item.cweagans/composer-patches v2 and haven't budgeted the migration. The workflow gains composer patches-relock and composer patches-repatch, and v1 patches starting with --- /dev/null must be rewritten to --- <file-path> or they fail with a confusing path-stripping error. That's migration work, not a drop-in.Adoption prerequisites to confirm before saying yes: vendor/bin/vendor-patches exists after install; cweagans/composer-patches is in require (not require-dev) if you deploy with --no-dev; allow-plugins includes cweagans/composer-patches; patches/, composer.json, and composer.lock are committed together; composer update --lock is part of the documented workflow (the lock hash changes whenever extra.patches does).
For executives — one paragraph: We have a small, recurring class of problem where a third-party library has a bug we can't wait on and can't afford to fork. This tool lets us ship a reviewed, version-controlled fix in a day, roughly one command, and it disappears automatically once the vendor releases the real fix. Cost is low and reversible; the main expense is a small amount of upkeep each time we upgrade the affected package. It is a release-unblocker, not a product capability, and it should never appear in customer-facing plans.
For engineering leadership — the trade: You accept a maintenance liability (patch drift on every bump of a patched package) in exchange for schedule independence. The liability is bounded and visible — patches live in patches/, they're plain diffs, they show up in code review, and each one has an obvious exit (upstream release → delete patch → composer update --lock). The failure mode is loud, not silent: a bad patch breaks composer install before it breaks production. Mitigate by capping patch lifetime, tracking patches per package as a dependency-health metric, and requiring an upstream PR link in the commit that adds each patch.
For the engineering team — the mechanics: Install symplify/vendor-patches as a dev dependency and cweagans/composer-patches as a real dependency if you deploy with --no-dev, plus the allow-plugins entry. Then: copy the file to <file>.old (never autoloaded, so you can run the app while you work), edit the original, run vendor/bin/vendor-patches generate. On composer-patches v2, follow with composer patches-relock and composer patches-repatch, then composer update --lock, then verify with composer install --verbose from a clean tree. Commit the patch and the lockfile together. Treat patch files as source in review. Batch all edits to a package before generating — the tool keys on file path and a second run overwrites the first. Use --resolve-from-directory for private/path repositories or the patch will silently apply to nothing. On long-running processes, restart workers and reload Octane after deploying a patch.
The honest caveat to state up front: This is a generator with no manager, no dependents, and readme-level maturity. That's fine — it's a thin, auditable script — but it means the operational discipline is entirely ours. If we can't commit to owning patch revalidation and removal, we should configure around the problem or replace the dependency instead.
How can I help you explore Laravel packages today?