micro-module/null
A lightweight Laravel/PHP micro-module that provides a safe null/no-op implementation. Use it as a placeholder or null-object substitute to simplify conditionals, avoid null checks, and keep module behavior predictable.
micro-module/null is a no-op implementation of the PSR queue interfaces (Interop\Queue\*). Nothing is published, nothing is consumed, nothing is persisted. Its value is that it satisfies a type-hint without requiring a broker, which makes it useful in exactly two places: tests that only care about building a message, and bootstrap paths where a queue context must exist but must not actually do anything.
Minimal setup:
composer require micro-module/null
The package depends on queue-interop/queue-interop (the de-facto PSR queue spec), so Interop\Queue\Context, Producer, Consumer, Message and Destination become available to your code. Before writing a single line of integration code, open src/ in vendor/micro-module/null — the package is small enough to read end-to-end in a few minutes, and you want the exact class names for the null Context, Producer, Consumer, Message and Queue/Topic before you wire anything up. Do not assume naming from the README; confirm against the actual files, then type-hint against the Interop\Queue interfaces rather than the concrete null classes so you can swap implementations later.
First use case — a unit test for code that enqueues a job, where you only want to prove the message was constructed correctly and that no exception was thrown:
use Interop\Queue\Context;
$context = new \MicroModule\Null\NullContext(); // verify exact FQCN in src/
$producer = $context->createProducer();
$producer->send(
$context->createQueue('orders'),
$context->createMessage(json_encode(['order_id' => 42]))
);
// No broker contacted, no assertion needed beyond "did not throw".
Bind the interface in the test container. The most maintainable pattern is not to new the null context inside tests, but to swap the binding once:
// tests/TestCase.php or a dedicated TestServiceProvider
$this->app->instance(Context::class, new NullContext());
Every service that injects Interop\Queue\Context now silently discards. Tests that need a real transport override the binding locally; everything else inherits the null default. This keeps the null context out of production wiring by construction.
Offline / degraded-mode fallback. In apps that talk to AMQP or Redis through a PSR-queue adapter, the null context is a reasonable binding when no broker is configured (local dev, CI without services, a feature flag that disables outbound messaging). The app boots, message-building code paths execute, and nothing leaves the process. Pair it with a log line so "silently dropped" is visible rather than mysterious.
Run one contract suite against both transports. If you have application-level tests that exercise "build message → send", write them against the Context interface and run the suite twice: once with your real adapter, once with the null one. Anything that fails only on the null run is a hidden assumption about delivery (e.g. code that reads back a correlation ID from a broker response).
Decorate, don't fork, when you need assertions. The null context records nothing, so it cannot answer "what was sent?". Wrap it:
final class RecordingContext implements Context
{
/** @var array<int, array{string, Message}> */
public array $sent = [];
public function __construct(private Context $inner) {}
public function createProducer(): Producer
{
return new class($this, $this->inner->createProducer()) implements Producer {
public function __construct(private RecordingContext $ctx, private Producer $inner) {}
public function send(Destination $d, Message $m): void
{
$this->ctx->sent[] = [$d->getQueueName(), $m];
$this->inner->send($d, $m); // delegates to null
}
};
}
// ... delegate remaining Context methods to $this->inner
}
The inner null context still does the work of creating messages/queues, so you get the correct object graph plus visibility. This is the pattern the package is genuinely good for; the null classes are the substrate, not the test double itself.
Use it in library code, not app code. If you maintain a package that accepts a Context, depending on the null implementation (or its interfaces) in your own require-dev lets you test without imposing a broker on downstream users.
It is not a Laravel queue driver. It does not implement Illuminate\Contracts\Queue\Factory or Queue, so Queue::fake() / Queue::assertPushed() remain the right tools for anything going through dispatch(). Only reach for this package when your code is typed against Interop\Queue\Context — typically a third-party SQS/AMQP adapter used directly rather than through Laravel's queue layer.
Consumer returns nothing, forever. A null consumer will hand back null on every receive() / receiveNoWait(). If you accidentally register the null context in a worker loop, you get a tight spin loop burning CPU with zero output. Guard worker entry points and never bind the null context wherever queue:work-style processes resolve their context.
Green tests mean less than they look. Payload validation, serialization, schema and retry behaviour are all bypassed. A suite that only ever ran against the null transport proves your code compiles and constructs messages — not that a broker will accept them. Keep at least one integration test against a real (or local, e.g. filesystem/in-memory) transport.
Zero stars, one release (Feb 2024). Treat maintenance risk as real. The upside: MIT licence and a tiny surface, so vendoring the handful of classes into app/Support/Queue/Null/ and dropping the dependency is a legitimate, low-cost hedge if you want to keep it out of your dependency graph.
Pin the version. "micro-module/null": "0.1.*" or an explicit constraint rather than a floating range; with no downstream dependents, semver discipline is unproven.
Extension points are limited to decoration. There are no drivers, no config file, no service provider to hook. Your leverage is entirely in what you compose around the null context — recorders, spies, assertion helpers. Budget for that accordingly; if you need anything richer than "does nothing", you are writing it yourself.
How can I help you explore Laravel packages today?