Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Default Laravel Package

php-standard-library/default

DefaultInterface for PHP classes to provide standardized default instances. Part of the PHP Standard Library, it gives a consistent contract for exposing a default object instance without custom static factory methods or ad hoc conventions.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require php-standard-library/default

This package is a single contract, zero dependencies. You can read the entire source in one sitting — start with vendor/php-standard-library/default/src/DefaultInterface.php. That one file (plus the composer.json PHP version constraint) tells you everything you need to know; the linked docs site and the release notes are just redirects to the main php-standard-library/php-standard-library monorepo.

Where to look first

  1. The interface signature. Confirm whether the method is public function getDefault(): static (instance) or public static function getDefault(): static (static). The README/description text mixes both conventions (User::getDefault() in examples, instance method in the interface snippets). Read the actual file before you write code against it — this is the single most important five seconds of the evaluation.
  2. Your PHP version. Declaring static as a return type requires PHP 8.0+; readonly value objects (the natural home for these defaults) require 8.1+.

First use case — a Null Object for an API response

The fastest win in a Laravel app is a response/DTO that currently gets handed out as null in failure paths:

use Psl\Default\DefaultInterface;

final class ApiResponse implements DefaultInterface
{
    public function __construct(
        public readonly bool $success,
        public readonly mixed $data = null,
        public readonly array $errors = [],
    ) {}

    public function getDefault(): static
    {
        return new static(success: false);
    }
}

Then any controller, job, or service that can fail returns (new ApiResponse(success: true, data: $rows)) on success and ApiResponse::getDefault()-equivalent on failure. Callers stop writing if ($response === null) and start trusting a shape. That is the whole package.

Implementation Patterns

1. Null Object for value objects and DTOs

The canonical pattern: a class whose "empty" state is still a valid, fully-typed object. Money::zero(), Address::empty(), Filter::none(), Pagination::firstPage(). Implement DefaultInterface so generic code (middleware, logging, serializers) can ask any of them for a neutral instance without a switch:

function neutral(object $object, string $class): object
{
    return $object instanceof DefaultInterface
        ? $object->getDefault()
        : new $class();
}

2. Container bindings as the customization seam

The interface fixes the contract; the container fixes the value. Bind defaults in a service provider so tests, plugins, and multi-tenant setups can swap them without touching call sites:

// AppServiceProvider::register()
$this->app->bind('default-shipping-address', fn () => ShippingAddress::getDefault());
$this->app->bind(ReportFilter::class, fn () => ReportFilter::getDefault());

Because the interface is instance-level, avoid $this->app->singleton() unless the default is genuinely immutable — a shared default object that someone mutates is a cross-request bug waiting to happen in Octane.

3. Service-layer fallbacks

For findOrCreate-style methods, return the default instead of null:

public function find(int $id): UserProfile
{
    return UserProfile::query()->find($id)?->toProfile()
        ?? UserProfile::getDefault();
}

This is where the package earns its keep: it makes the fallback explicit and reviewable rather than an implicit ?? null scattered across twelve call sites.

4. Testing: defaults, not mocks

Where the package's PR material oversells is the testing story, and it's worth being precise. User::getDefault() is not a substitute for createMock() when you need to assert interaction, and it is not a substitute for User::factory()->create() when you need persisted state. It is a good substitute for new User([]) and for hand-built "empty" fixtures in unit tests that never touch the database:

// Fine: pure unit test of a formatter
$formatter = new UserNameFormatter();
$this->assertSame('Guest', $formatter->format(User::getDefault()));

// Wrong: this will not have an id, and any DB assertion will fail
$user = User::getDefault();
$this->actingAs($user)->get('/dashboard');   // will blow up

5. Eloquent models — use with care

Models are the tempting target and the sharpest edge. A default model instance is unsaved (exists === false), so it works fine for: guest-user display objects, form prefill, validation baselines, and Blade fallbacks. It does not work for authentication, relationships ($user->posts on an unsaved model returns an empty collection you may mistake for real data), or anything that triggers a save(). Define the neutral state in $attributes and let the default reflect it:

final class GuestUser extends User
{
    protected $attributes = ['name' => 'Guest', 'role' => 'guest'];

    public function getDefault(): static
    {
        return new static();
    }
}

6. Interop with the rest of the PS Standard Library

The interface is designed to compose with sibling packages (collections, result types, option types). If you already use Psl\Collection or Psl\Result, implementing DefaultInterface on your wrappers keeps a consistent idiom across the codebase — DefaultInterface for "neutral instance", Result for "may fail", and so on. Don't adopt it in isolation if you're not otherwise in that ecosystem; the value comes from the shared convention.

7. Extension point for plugins/modules

If you're building a modular Laravel app, let modules declare their own defaults and register them:

// In a module service provider
$this->app->bind(PaymentGateway::class, fn () => $module->defaultGateway());

The interface gives you a instanceof DefaultInterface check to validate that a module's registered default is well-formed at boot time — a cheap sanity check in a booted() callback.

Gotchas and Tips

Verify the actual method signature before writing a single call site. The promotional material shows both User::getDefault() (static call) and public function getDefault(): static (instance method). In PHP 8 a non-static method called statically throws Error: Non-static method ... cannot be called statically. If the interface declares an instance method, every Foo::getDefault() in the docs is wrong and you need (new Foo())->getDefault() — which in turn requires a zero-argument constructor. Decide which convention you're adopting and stick to it project-wide; mixing them is the #1 way this pattern becomes confusing.

Return a fresh instance every time. Do not memoize the default in a static property unless the object is genuinely immutable (readonly properties, no setters, no mutable arrays exposed by reference). One mutated default object shared across requests is exactly the class of bug this package exists to prevent. If construction is expensive, make it cheap instead of caching it.

Never put the default implementation in a base trait that assumes a no-arg constructor. A DefaultTrait with return new static(); will silently break the first time a subclass requires constructor arguments. Either implement getDefault() per class, or make the trait an abstract contract that forces the subclass to override.

Defaults must not swallow errors. A null-object default is a legitimate value — an anonymous guest user, an empty filter, a neutral response. If the default is what you get when you don't know what the value should be, you've built an error-silencing machine. Keep getDefault() for "this class's neutral state" and let genuine failures throw or return a Result.

Watch for defaults leaking into JSON. A default DTO that ends up on an Eloquent model as a relation, or is returned from an API resource, will serialize as real data. If a consumer can't distinguish "empty" from "intentionally this value," add an explicit flag on the default instance (success: false, isDefault: true) rather than relying on the absence of fields.

Do not confuse this with Laravel factories, Model::make(), or Collection::make(). Laravel already has excellent default-construction primitives; this package addresses cross-class convention, not object construction. If your pain is "I keep writing new Foo()," a factory or a container binding solves it without a new interface. Adopt the interface only when you have generic code that needs to ask unrelated classes for their neutral state.

Namespace and import hygiene. DefaultInterface is a generic name. Always import it explicitly (use Psl\Default\DefaultInterface; — check the exact namespace in the source) and never reference it via a global alias; a future framework or package with a same-named interface will create an ambiguity you'll only notice at runtime.

Maturity and supply-chain reality check. One star, zero dependents, and every release note just says "see the main repository." This is a read-only Composer split of the php-standard-library/php-standard-library monorepo — file issues and PRs upstream, never here. The upside: it's a zero-dependency interface of a few lines, so the risk of adoption is near zero, and vendoring the interface into your own codebase (or copying it and losing the Composer entry) is a completely defensible alternative if you want to avoid a dependency with no independent track record. Pin an explicit version constraint; the monorepo's 6.x line moves together, and a ^6.0 bump can arrive with unrelated changes from sibling packages.

Version constraint tip. The tagged releases (6.1.x through 6.3.0) are synchronized across the whole standard library. If you're only pulling in default, consider pinning to an exact minor (6.3.*) so a monorepo-wide release doesn't drag in behavior changes you didn't evaluate.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel