orchestra/support-core
Core support utilities for the Orchestra Platform, providing shared helpers, service providers, and foundational traits that other Orchestra packages build on. Lightweight, framework-agnostic glue for Laravel applications that need a consistent base for extension development.
Install — you'll usually inherit it. This is a foundation package, not a feature you reach for. In practice you pull it in transitively via orchestra/testbench, an Orchestra Platform component, or another package that subclasses its providers. Reach for a direct composer require only when you're building an Orchestral extension or deliberately standardizing on its base classes:
composer require orchestra/support-core:^7.0
v7.0.0's only release note is "Update support for Laravel Framework v9+" — so the floor is Laravel 9 / PHP 8.0. Do not attempt this on Laravel 8.
Look here first, in this order:
The actual constraint — the release note doesn't say whether "9+" means "9 only" or "9 through 12," and that decides whether your next framework bump is a bump or a migration.
composer show orchestra/support-core
grep -n '"illuminate\|"php' vendor/orchestra/support-core/composer.json
The real API surface — the package is small; read it instead of guessing. Expect service-provider base classes, traits, and a helpers.php (or similar) autoloaded via files.
find vendor/orchestra/support-core/src -maxdepth 3 -name '*.php' | sort
grep -rn '^function ' vendor/orchestra/support-core/src/helpers.php 2>/dev/null
Whether it auto-registers — check composer.json extra.laravel.providers. Support libraries usually don't register anything globally; confirm before assuming a binding exists.
First use case: kill your boilerplate service provider. The single highest-value day-to-day leverage is subclassing the provider base instead of rewriting mergeConfigFrom() / publishes() scaffolding for every internal package:
namespace Acme\Reports;
use Orchestra\Support\ServiceProvider; // confirm FQCN in your installed version
class ReportsServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->mergeConfigFrom(__DIR__.'/../config/reports.php', 'reports');
}
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/reports.php' => config_path('reports.php'),
], 'reports-config');
}
}
Validate with php artisan about, php artisan config:cache, and one feature test resolving your binding. Because v7 changes no behavior, a green suite after the bump is the verification.
Pattern 1 — Provider base class for every internal package. Standardize all internal packages on the Orchestra provider base so config merge, publish tags, and deferred-provider handling look identical across repos. This is the highest-value, lowest-coupling use: you touch one base class, not forty call sites.
Pattern 2 — Wrap helpers behind an internal adapter. The helpers are the volatile surface — they end up in application code where replacement is a codebase-wide edit. Put a one-file fence in on day one:
namespace Acme\Support;
use function Orchestra\Support\str_slug; // verify the exact helper name — do not assume
final class Text
{
public static function slug(string $value): string
{
return str_slug($value);
}
}
Now if the package is abandoned or dropped at the next Laravel major, you swap one file. This is the most important pattern here.
Pattern 3 — Trait composition only on classes you own. Traits are compile-time coupling: once use'd inside a model or job, the package is in your class hierarchy and mocking it in tests becomes invasive. Apply trait-based behavior to your classes — not vendor classes you don't control.
Pattern 4 — Only replace a bespoke helper package if you have ≥2 codebases. The real build-vs-buy question is "do we maintain our own Str/Arr/HTML duplication, or consolidate?" One service on Laravel: use Illuminate\Support\Str, Arr, and HtmlString — adding a low-adoption dependency is a net negative. Several services with visible drift (three HTML escapers, four incompatible interfaces): consolidation is the win.
Pattern 5 — Contract-first integration. Where the package advertises framework-agnostic contracts, bind your own implementations in your provider rather than extending its concrete classes. Keeps Orchestra out of your type signatures, so a future replacement is a rebind, not a rewrite:
$this->app->bind(\Acme\Contracts\Sanitizer::class, \Acme\Support\HtmlSanitizer::class);
Pattern 6 — Test with Testbench. If you're writing a package against this, match the Laravel/PHP combination its own CI uses. Run vendor/bin/testbench locally so you catch provider-boot ordering issues before they hit an application.
Integration tips.
composer update.^7.0 explicitly in every composer.json that inherits it, even transitively, so resolver drift is visible in the diff.grep -rn 'Orchestra\\Support' app/ packages/ before and after every bump; that list is your blast radius.The "9+" ambiguity is the real gotcha. Read composer.json yourself. ^9.0 dead-ends at Laravel 10; ^9|^10|^11 is fine for a while. Do not plan a Laravel 11/12 roadmap on the assumption that "9+" means "and everything after."
No changelog beyond one line. A major bump whose only stated change is framework support typically also raises PHP minimums, tightens illuminate/* constraints, and may reshape helper signatures. Assume an undisclosed breaking-change surface — diff src/ between your locked v6 and v7 rather than trusting the note.
Global helper collisions. files-autoloaded helpers fatally collide if you define a same-named function. Guard anything you add:
if (! function_exists('acme_slug')) {
function acme_slug(string $value): string { /* ... */ }
}
Also remember files autoloads are not affected by config:cache — a helper change means composer dump-autoload, not php artisan config:clear.
Escaping behavior is security-relevant. HTML/string helpers sit on rendering paths. After the v7 bump, write explicit regression tests for entities, quotes, and multi-byte input — this is where a silent behavior change actually hurts. Never bypass Blade's {{ }} with a raw helper call on user input.
Config and publish drift. Published config/assets do not auto-update. vendor:publish --force overwrites your edits — check the diff before running it in production-adjacent environments.
Deferred providers and ordering. If the base provider defers registration, bindings you expect at boot() may not exist yet. A BindingResolutionException on a class that clearly exists usually means check deferral before restructuring.
Debugging checklist, in order: composer why orchestra/support-core (is it transitive? from what? can I exclude it?) → composer show orchestra/support-core (which version resolved) → grep the constraint → php artisan about → php artisan config:cache → one feature test through the binding. Nine times out of ten it's a stale composer.lock or a transitive constraint you didn't know you had.
Lifecycle reality. Zero listed dependents, low composite score, single-maintainer ecosystem project, no published deprecation or support policy. There is no vendor support tier. Own the helper surface yourself: document which helpers and traits you use, keep the wrapper layer, and have a rollback path that is a one-line composer require — not a refactor. Approve it as scoped foundation for Orchestra-based work; don't let it become the platform-wide standard by accident.
How can I help you explore Laravel packages today?