Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d5deef2
Plan framework enum identifier normalization
binaryfire Jul 16, 2026
6c0170d
Converge manager callbacks and identifier contracts
binaryfire Jul 16, 2026
16b26a7
Isolate logger context and recursion state
binaryfire Jul 16, 2026
9de2b21
Isolate buffered logging state and request identifiers
binaryfire Jul 16, 2026
4325f51
Make Monolog stream operations coroutine-safe
binaryfire Jul 16, 2026
27279a8
Track exception reporting within coroutine context
binaryfire Jul 16, 2026
997eb4a
Correct log channel construction and lifecycle
binaryfire Jul 16, 2026
71e24e2
Capture queued payloads at the dispatch boundary
binaryfire Jul 16, 2026
fc4b80f
Delegate Sentry logging through framework handlers
binaryfire Jul 16, 2026
fd44bfe
Normalize cache identifiers at string boundaries
binaryfire Jul 16, 2026
b5de158
Preserve identifiers across bus and event carriers
binaryfire Jul 16, 2026
c5c0982
Preserve queue identifiers through backend execution
binaryfire Jul 16, 2026
a24730b
Preserve queue identifiers in Horizon
binaryfire Jul 16, 2026
09d6d50
Normalize database and pipeline identifiers
binaryfire Jul 16, 2026
44e4ad1
Correct scheduler identifier boundaries
binaryfire Jul 16, 2026
5ec2c70
Preserve authorization and permission identifiers
binaryfire Jul 16, 2026
59de61d
Normalize cookie identifiers at request boundaries
binaryfire Jul 16, 2026
729e1ea
Preserve Reverb presence identifiers
binaryfire Jul 16, 2026
7d81ce6
Correct service identifier boundaries
binaryfire Jul 16, 2026
3521a6b
Correct routing and translation zero identifiers
binaryfire Jul 16, 2026
30a648e
Correct generator and Testbench identifiers
binaryfire Jul 16, 2026
5572abe
Record completed Log and identifier audits
binaryfire Jul 16, 2026
f7b1a7d
Guard recursive log event listeners
binaryfire Jul 16, 2026
4be7f9a
Align Reverb presence integration assertion
binaryfire Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -990,9 +990,9 @@ An exceptionally large shared work unit may receive its own linked detail plan w

This compact index routes the completed-work history that must be consulted with the full plan after compaction. Detailed history remains in the [companion ledger](2026-07-12-framework-coroutine-state-lifecycle-audit-ledger.md).

- **Active package or work unit:** `log`
- **Ledger entries required for the active work:** `Coordinate shared container construction and complete current contextual resolution` (`container-09`, `container-10`)
- **Pending revalidation carried into the active work:** `container-09`, `container-10`
- **Active package or work unit:** `support`
- **Ledger entries required for the active work:** `Isolate logging state and capture queued payloads deterministically` (`support-01`) and `Normalize framework enum identifiers at string boundaries` (`support-02`)
- **Pending revalidation carried into the active work:** callback rebinding across every direct Manager consumer; enum identifier ownership across Support managers and their consumers; later full `foundation`, `queue`, and `sentry` audits must retain the completed Log boundaries

Update these three lines when a package starts, completes, or gains a cross-package dependency. Name exact work-unit headings or shared finding IDs from the companion ledger; never use “see recent entries” or require a full-ledger reread.

Expand Down Expand Up @@ -1031,6 +1031,7 @@ Add one row only for a shared finding or changed lower-level assumption that ano
| `queue-11` | `queue` | `events` (revalidation complete), `broadcasting`; later full `queue` and `broadcasting` audits | `Correct event dispatch, queued-consumer isolation, and queue interoperability`; finding `queue-11` |
| `queue-12` | `bus`, `queue` | `events` (revalidation complete), `broadcasting`; later full `bus`, `queue`, and `broadcasting` audits | `Correct event dispatch, queued-consumer isolation, and queue interoperability`; finding `queue-12` |
| `foundation-01` | `foundation` | `support`; later full `foundation` and `support` audits | `Correct event dispatch, queued-consumer isolation, and queue interoperability`; finding `foundation-01` |
| `support-02` | `support` | `auth`, `broadcasting`, `bus`, `cache`, `concurrency`, `console`, `container`, `contracts`, `cookie`, `database`, `events`, `filesystem`, `foundation`, `hashing`, `horizon`, `inertia`, `jwt`, `log`, `mail`, `notifications`, `permission`, `pipeline`, `queue`, `redis`, `reverb`, `routing`, `sanctum`, `scout`, `session`, `socialite`, `telescope`, `testbench`, `translation`; later full consumer audits | `Normalize framework enum identifiers at string boundaries`; finding `support-02`; sibling findings `translation-01` and `reverb-03`; linked detail plan `2026-07-15-framework-enum-identifier-contracts.md` |

## Package checklist

Expand Down Expand Up @@ -1063,7 +1064,7 @@ The order is lower-level first where practical. Hypervel has cross-cutting depen
- [x] `context`
- [x] `di`
- [x] `events`
- [ ] `log`
- [x] `log`
- [ ] `support`

### Security and request primitives
Expand Down
428 changes: 428 additions & 0 deletions docs/plans/2026-07-15-framework-enum-identifier-contracts.md

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion src/auth/src/AuthManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
use Hypervel\Contracts\Auth\Guard;
use Hypervel\Contracts\Auth\StatefulGuard;
use Hypervel\Contracts\Container\Container;
use Hypervel\Support\RebindsCallbacksToSelf;
use InvalidArgumentException;
use ReflectionException;
use RuntimeException;
use UnitEnum;

use function Hypervel\Support\enum_value;
Expand All @@ -22,6 +25,7 @@
class AuthManager implements FactoryContract
{
use CreatesUserProviders;
use RebindsCallbacksToSelf;

/**
* Context key for the default guard override.
Expand Down Expand Up @@ -326,7 +330,14 @@ public function redirectTo(callable|string|null $guests = null, callable|string|
*/
public function extend(string $driver, Closure $callback): static
{
$this->customCreators[$driver] = $callback->bindTo($this, $this);
try {
$callback = $this->bindCallbackToSelf($callback)
?? throw new RuntimeException('Unable to bind custom driver callback');
} catch (ReflectionException $e) {
throw new RuntimeException('Unable to bind custom driver callback', previous: $e);
}

$this->customCreators[$driver] = $callback;

return $this;
}
Expand Down
4 changes: 4 additions & 0 deletions src/boost/docs/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ Log::stack(['slack', $channel])->info('Something happened!');

Sometimes you may need complete control over how Monolog is configured for an existing channel. For example, you may want to configure a custom Monolog `FormatterInterface` implementation for Hypervel's built-in `single` channel.

Configured channels and their Monolog handlers and processors are reused for the lifetime of a worker. Custom handlers and processors may therefore keep only immutable worker-wide state or state that is safely isolated between coroutines. Mutable request or record state stored directly on a custom component can leak between concurrent requests.

To get started, define a `tap` array on the channel's configuration. The `tap` array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created. There is no conventional location where these classes should be placed, so you are free to create a directory within your application to contain these classes:

```php
Expand Down Expand Up @@ -414,6 +416,8 @@ class CustomizeFormatter
> [!NOTE]
> All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected.

The `getLogger` method returns the underlying Monolog instance when direct Monolog access is required. Messages written directly to that instance bypass Hypervel's shared log context, log event dispatch, and recursion protection, so normal application logging should use the `Hypervel\Log\Logger` wrapper.

<a name="creating-monolog-handler-channels"></a>
### Creating Monolog Handler Channels

Expand Down
4 changes: 3 additions & 1 deletion src/broadcasting/src/AnonymousEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ public function send(): void
*/
public function broadcastAs(): string
{
return $this->name ?: class_basename($this);
return $this->name === null || $this->name === ''
? class_basename($this)
: $this->name;
}

/**
Expand Down
37 changes: 31 additions & 6 deletions src/broadcasting/src/BroadcastManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@
use Hypervel\Queue\Attributes\ReadsQueueAttributes;
use Hypervel\Support\Arr;
use Hypervel\Support\Queue\Concerns\ResolvesQueueRoutes;
use Hypervel\Support\RebindsCallbacksToSelf;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Pusher\Pusher;
use ReflectionException;
use RuntimeException;
use Throwable;
use UnitEnum;

use function Hypervel\Support\enum_value;

/**
* @mixin \Hypervel\Broadcasting\Broadcasters\Broadcaster
Expand All @@ -45,6 +50,7 @@ class BroadcastManager implements BroadcastingFactoryContract
{
use HasPoolProxy;
use ReadsQueueAttributes;
use RebindsCallbacksToSelf;
use ResolvesQueueRoutes;

/**
Expand Down Expand Up @@ -241,17 +247,23 @@ protected function mustBeUniqueAndCannotAcquireLock(mixed $event): bool
/**
* Get a driver instance.
*/
public function connection(?string $driver = null): Broadcaster
public function connection(UnitEnum|string|null $driver = null): Broadcaster
{
return $this->driver($driver);
}

/**
* Get a driver instance.
*/
public function driver(?string $name = null): Broadcaster
public function driver(UnitEnum|string|null $name = null): Broadcaster
{
$name = $name ?: $this->getDefaultDriver();
if ($name instanceof UnitEnum) {
$name = (string) enum_value($name);
}

$name = $name === null || $name === ''
? $this->getDefaultDriver()
: $name;

return $this->drivers[$name] = $this->get($name);
}
Expand Down Expand Up @@ -458,8 +470,10 @@ public function getDefaultDriver(): string
*
* Boot-only. Mutates process-global config; per-request use races across coroutines.
*/
public function setDefaultDriver(string $name): void
public function setDefaultDriver(UnitEnum|string $name): void
{
$name = $name instanceof UnitEnum ? (string) enum_value($name) : $name;

$this->app['config']['broadcasting.default'] = $name;
}

Expand All @@ -470,8 +484,12 @@ public function setDefaultDriver(string $name): void
* resources. Other connections sharing the pool transparently acquire a
* fresh pool on their next operation.
*/
public function purge(?string $name = null): void
public function purge(UnitEnum|string|null $name = null): void
{
if ($name instanceof UnitEnum) {
$name = (string) enum_value($name);
}

$name ??= $this->getDefaultDriver();
$driver = $this->drivers[$name] ?? null;

Expand Down Expand Up @@ -510,7 +528,14 @@ public function purge(?string $name = null): void
*/
public function extend(string $driver, Closure $callback): static
{
$this->customCreators[$driver] = $callback->bindTo($this, $this);
try {
$callback = $this->bindCallbackToSelf($callback)
?? throw new RuntimeException('Unable to bind custom driver callback');
} catch (ReflectionException $e) {
throw new RuntimeException('Unable to bind custom driver callback', previous: $e);
}

$this->customCreators[$driver] = $callback;

return $this;
}
Expand Down
7 changes: 4 additions & 3 deletions src/broadcasting/src/InteractsWithBroadcasting.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ trait InteractsWithBroadcasting
*/
public function broadcastVia(UnitEnum|array|string|null $connection = null): static
{
$connection = is_null($connection) ? null : enum_value($connection);

$this->broadcastConnection = is_null($connection)
? [null]
: Arr::wrap($connection);
: array_map(
fn ($value) => $value instanceof UnitEnum ? (string) enum_value($value) : $value,
Arr::wrap($connection)
);

return $this;
}
Expand Down
4 changes: 3 additions & 1 deletion src/broadcasting/src/PendingBroadcast.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public function __construct(
public function via(UnitEnum|string|null $connection = null): static
{
if (method_exists($this->event, 'broadcastVia')) {
$this->event->broadcastVia(enum_value($connection));
$this->event->broadcastVia(
$connection instanceof UnitEnum ? (string) enum_value($connection) : $connection
);
}

return $this;
Expand Down
12 changes: 8 additions & 4 deletions src/bus/src/ChainedBatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ public function toPendingBatch(): PendingBatch
$batch->name = $this->name;
$batch->options = $this->options;

if ($this->queue) {
if ($this->queue !== null && $this->queue !== '') {
$batch->onQueue($this->queue);
}

if ($this->connection) {
if ($this->connection !== null && $this->connection !== '') {
$batch->onConnection($this->connection);
}

Expand All @@ -114,8 +114,12 @@ protected function attachRemainderOfChainToEndOfBatch(PendingBatch $batch): Pend

$next->chained = $this->chained;

$next->onConnection($next->connection ?: $this->chainConnection);
$next->onQueue($next->queue ?: $this->chainQueue);
$next->onConnection($next->connection === null || $next->connection === ''
? $this->chainConnection
: $next->connection);
$next->onQueue($next->queue === null || $next->queue === ''
? $this->chainQueue
: $next->queue);

$next->chainConnection = $this->chainConnection;
$next->chainQueue = $this->chainQueue;
Expand Down
8 changes: 6 additions & 2 deletions src/bus/src/PendingBatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ public function name(string $name): static
*/
public function onConnection(UnitEnum|string $connection): static
{
$this->options['connection'] = enum_value($connection);
$this->options['connection'] = $connection instanceof UnitEnum
? (string) enum_value($connection)
: $connection;

return $this;
}
Expand All @@ -266,7 +268,9 @@ public function connection(): ?string
*/
public function onQueue(UnitEnum|string|null $queue): static
{
$this->options['queue'] = enum_value($queue);
$this->options['queue'] = $queue instanceof UnitEnum
? (string) enum_value($queue)
: $queue;

return $this;
}
Expand Down
24 changes: 18 additions & 6 deletions src/bus/src/Queueable.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ trait Queueable
*/
public function onConnection(UnitEnum|string|null $connection): static
{
$this->connection = enum_value($connection);
$this->connection = $connection instanceof UnitEnum
? (string) enum_value($connection)
: $connection;

return $this;
}
Expand All @@ -101,7 +103,9 @@ public function onConnection(UnitEnum|string|null $connection): static
*/
public function onQueue(UnitEnum|string|null $queue): static
{
$this->queue = enum_value($queue);
$this->queue = $queue instanceof UnitEnum
? (string) enum_value($queue)
: $queue;

return $this;
}
Expand Down Expand Up @@ -137,7 +141,9 @@ public function withDeduplicator(array|callable|null $deduplicator): static
*/
public function allOnConnection(UnitEnum|string|null $connection): static
{
$resolvedConnection = enum_value($connection);
$resolvedConnection = $connection instanceof UnitEnum
? (string) enum_value($connection)
: $connection;

$this->chainConnection = $resolvedConnection;
$this->connection = $resolvedConnection;
Expand All @@ -150,7 +156,9 @@ public function allOnConnection(UnitEnum|string|null $connection): static
*/
public function allOnQueue(UnitEnum|string|null $queue): static
{
$resolvedQueue = enum_value($queue);
$resolvedQueue = $queue instanceof UnitEnum
? (string) enum_value($queue)
: $queue;

$this->chainQueue = $resolvedQueue;
$this->queue = $resolvedQueue;
Expand Down Expand Up @@ -277,8 +285,12 @@ public function dispatchNextJobInChain(): void
dispatch(tap(unserialize(array_shift($this->chained)), function ($next) {
$next->chained = $this->chained;

$next->onConnection($next->connection ?: $this->chainConnection);
$next->onQueue($next->queue ?: $this->chainQueue);
$next->onConnection($next->connection === null || $next->connection === ''
? $this->chainConnection
: $next->connection);
$next->onQueue($next->queue === null || $next->queue === ''
? $this->chainQueue
: $next->queue);

$next->chainConnection = $this->chainConnection;
$next->chainQueue = $this->chainQueue;
Expand Down
14 changes: 13 additions & 1 deletion src/cache/src/CacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
use Hypervel\Contracts\Events\Dispatcher as DispatcherContract;
use Hypervel\Contracts\Session\Session;
use Hypervel\Support\Arr;
use Hypervel\Support\RebindsCallbacksToSelf;
use Hypervel\Support\Str;
use InvalidArgumentException;
use Mockery;
use Mockery\LegacyMockInterface;
use ReflectionException;
use RuntimeException;
use UnitEnum;

use function Hypervel\Support\enum_value;
Expand All @@ -28,6 +31,8 @@
*/
class CacheManager implements FactoryContract
{
use RebindsCallbacksToSelf;

/**
* The context key prefix for memoized cache repositories.
*/
Expand Down Expand Up @@ -471,7 +476,14 @@ public function purge(UnitEnum|string|null $name = null): void
*/
public function extend(string $driver, Closure $callback): static
{
$this->customCreators[$driver] = $callback->bindTo($this, $this);
try {
$callback = $this->bindCallbackToSelf($callback)
?? throw new RuntimeException('Unable to bind custom driver callback');
} catch (ReflectionException $e) {
throw new RuntimeException('Unable to bind custom driver callback', previous: $e);
}

$this->customCreators[$driver] = $callback;

return $this;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/Events/CacheEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ abstract class CacheEvent
public function __construct(?string $storeName, UnitEnum|string $key, array $tags = [])
{
$this->storeName = $storeName;
$this->key = enum_value($key);
$this->key = $key instanceof UnitEnum ? (string) enum_value($key) : $key;
$this->tags = $tags;
}

Expand Down
4 changes: 3 additions & 1 deletion src/cache/src/Events/CacheHit.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Hypervel\Cache\Events;

use UnitEnum;

class CacheHit extends CacheEvent
{
/**
Expand All @@ -14,7 +16,7 @@ class CacheHit extends CacheEvent
/**
* Create a new event instance.
*/
public function __construct(?string $storeName, string $key, mixed $value, array $tags = [])
public function __construct(?string $storeName, UnitEnum|string $key, mixed $value, array $tags = [])
{
parent::__construct($storeName, $key, $tags);

Expand Down
Loading