Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f348b89
fix(redis): reconnect pooled clients after primary endpoint failover
binaryfire Sep 6, 2026
d95eb55
fix(redis): distinguish failed callbacks from abandoned transactions
binaryfire Sep 6, 2026
40ecbf2
fix(http): complete JSON:API request and generator updates
binaryfire Sep 6, 2026
6a8e473
fix(http): release pending requests without cyclic collection
binaryfire Sep 6, 2026
2460f4e
fix(foundation): memoize event cache state and order core aliases
binaryfire Sep 6, 2026
4a1bf9b
test(cache): complete Redis lock compression coverage
binaryfire Sep 6, 2026
f128809
fix(cache): apply native compression to Lua cache writes
binaryfire Sep 6, 2026
814167e
test(cache): complete upstream Redis counter regression coverage
binaryfire Sep 6, 2026
084fe95
fix(validation): complete upstream placeholder replacement delegation
binaryfire Sep 6, 2026
3b6c49a
feat(validation): port array_keys and preserve literal field identity
binaryfire Sep 6, 2026
782af27
test(console): complete upstream command resolution assertions
binaryfire Sep 6, 2026
e4863b7
docs(http): configure exception truncation during provider registration
binaryfire Sep 6, 2026
9cd6855
fix(routing): preserve middleware state when listing routes
binaryfire Sep 6, 2026
a967532
fix(console): initialize middleware before inspecting routes
binaryfire Sep 6, 2026
74828e4
Complete string helper type parity and correct matching contracts
binaryfire Sep 6, 2026
57bfafd
Clarify when the Blade hasStack directive renders
binaryfire Sep 6, 2026
7a1ee22
Fix Unicode case-insensitive string replacement and removal
binaryfire Sep 6, 2026
94e16b7
Preserve fractional durations and add date overflow control
binaryfire Sep 6, 2026
98b8eb5
Freeze the array-store increment test clock
binaryfire Sep 6, 2026
6481329
Port PHP 8.5-compatible path, mail and word-count tests
binaryfire Sep 7, 2026
5434bd1
Document fluent request input and verify nonempty defaults
binaryfire Sep 7, 2026
daa183c
Port mail address validation and correct supported recipient types
binaryfire Sep 7, 2026
c999b57
Port global queue pause and resume controls
binaryfire Sep 7, 2026
90f08ff
Report paused and resumed queues from workers
binaryfire Sep 7, 2026
2d048a4
Align Redis option errors and port native backoff coverage
binaryfire Sep 7, 2026
2e3b071
Complete sliding-window parameter type parity
binaryfire Sep 7, 2026
cfb365f
Port password-reset notification integration coverage
binaryfire Sep 7, 2026
7516443
Fix validation wildcard message lookup for literal and numeric keys
binaryfire Sep 7, 2026
859ea23
Preserve literal validation parameters across rule serialization
binaryfire Sep 7, 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
13 changes: 3 additions & 10 deletions src/cache/src/Redis/Support/Serialization.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function serialize(RedisConnection $connection, mixed $value): mixed
* serialize Lua ARGV parameters.
*
* This method handles three scenarios:
* 1. Serializer configured (igbinary/json/php): Use pack() which calls _serialize()
* 1. Serializer configured (igbinary/json/php): Pack with native serialization and compression
* 2. No serializer, but compression enabled: PHP serialize, then compress
* 3. No serializer, no compression: Just PHP serialize
*
Expand All @@ -69,24 +69,17 @@ public function serialize(RedisConnection $connection, mixed $value): mixed
*/
public function serializeForLua(RedisConnection $connection, mixed $value): string
{
// Case 1: Serializer configured (e.g. igbinary/json)
// pack() calls _serialize() which handles serialization and compression
if ($connection->serialized()) {
return $connection->pack([$value])[0];
}

// No serializer - must PHP-serialize first
$serialized = $this->phpSerialize($value);

// Case 2: Check if compression is enabled (even without serializer)
if ($connection->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE) {
// _serialize() applies compression even with SERIALIZER_NONE
// Cast to string in case serialize() returned a numeric value
return $connection->_serialize(is_numeric($serialized) ? (string) $serialized : $serialized);
// Lua arguments need native packing for compression. Preserve numeric types for pack_ignore_numbers.
return $connection->pack([$serialized])[0];
}

// Case 3: No serializer, no compression
// Cast to string in case serialize() returned a numeric value
return is_numeric($serialized) ? (string) $serialized : $serialized;
}

Expand Down
4 changes: 3 additions & 1 deletion src/collections/src/LazyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,8 @@ public function shuffle(): static
/**
* Create chunks representing a "sliding window" view of the items in the collection.
*
* @param positive-int $size
* @param positive-int $step
* @return static<int, static>
*
* @throws InvalidArgumentException
Expand All @@ -1137,7 +1139,7 @@ public function sliding(int $size = 2, int $step = 1): static
while ($iterator->valid()) {
$chunk[$iterator->key()] = $iterator->current();

if (count($chunk) == $size) {
if (count($chunk) === $size) {
yield $this->newInstance($chunk)->tap(function () use (&$chunk, $step) {
$chunk = array_slice($chunk, $step, null, true);
});
Expand Down
23 changes: 18 additions & 5 deletions src/data/src/Support/Validation/RuleDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Hypervel\Data\Attributes\Validation\StringValidationAttribute;
use Hypervel\Data\Support\Validation\References\ExternalReference;
use Hypervel\Data\Support\Validation\References\FieldReference;
use Hypervel\Support\Arr;

class RuleDenormalizer
{
Expand Down Expand Up @@ -73,6 +74,7 @@ protected function normalizeStringValidationAttribute(
ValidationPath $path,
): array {
$parameters = [];
$quoteParameters = ! in_array($rule->keyword(), ['regex', 'not_regex'], true);

foreach ($rule->parameters() as $key => $value) {
$parameter = $this->normalizeRuleParameter($value, $path);
Expand All @@ -81,7 +83,16 @@ protected function normalizeStringValidationAttribute(
continue;
}

$parameters[] = is_string($key) ? "{$key}={$parameter}" : $parameter;
foreach (Arr::wrap($parameter) as $index => $field) {
if (is_string($key) && $index === 0) {
$field = "{$key}={$field}";
}

// Quote after adding the name so the entire parameter remains one CSV field.
$parameters[] = $quoteParameters && strpbrk($field, ',"') !== false
? '"' . str_replace('"', '""', $field) . '"'
: $field;
}
}

if ($parameters === []) {
Expand All @@ -92,12 +103,14 @@ protected function normalizeStringValidationAttribute(
}

/**
* Convert one rule parameter into Validator string form.
* Normalize one rule parameter while preserving its field boundaries.
*
* @return null|list<string>|string
*/
protected function normalizeRuleParameter(
mixed $parameter,
ValidationPath $path,
): ?string {
): array|string|null {
if ($parameter === null) {
return null;
}
Expand All @@ -117,11 +130,11 @@ protected function normalizeRuleParameter(
if (is_array($parameter)) {
// ValidatesAttributes::convertValuesToNull() decodes list values from this literal token.
$subParameters = array_map(
fn (mixed $subParameter): string => $this->normalizeRuleParameter($subParameter, $path) ?? 'null',
fn (mixed $subParameter): array|string => $this->normalizeRuleParameter($subParameter, $path) ?? 'null',
$parameter
);

return implode(',', $subParameters);
return Arr::flatten($subParameters);
}

if ($parameter instanceof DateTimeInterface) {
Expand Down
2 changes: 1 addition & 1 deletion src/docs/blade.md
Original file line number Diff line number Diff line change
Expand Up @@ -1895,7 +1895,7 @@ If you would like to prepend content onto the beginning of a stack, you should u
@endprepend
```

The `@hasStack` directive may be used to determine if a stack is empty:
The `@hasStack` directive may be used to render markup when a stack has content:

```blade
@hasStack('list')
Expand Down
2 changes: 1 addition & 1 deletion src/docs/cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ Cache::decrement('key');
Cache::decrement('key', $amount);
```

When using a Redis cache store with PhpRedis serialization, atomic counters require either `Redis::SERIALIZER_NONE` or the PhpRedis 6.2+ `pack_ignore_numbers` option. See the [PhpRedis serialization documentation](/docs/{{version}}/redis#phpredis-serialization) for configuration details.
When using a Redis cache store, atomic counters require disabling both PhpRedis serialization and compression or enabling the PhpRedis 6.2+ `pack_ignore_numbers` option. See the [PhpRedis serialization documentation](/docs/{{version}}/redis#phpredis-serialization) for configuration details.

<a name="retrieve-store"></a>
#### Retrieve and Store
Expand Down
21 changes: 15 additions & 6 deletions src/docs/eloquent-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -943,24 +943,25 @@ The generated class will extend `Hypervel\Http\Resources\JsonApi\JsonApiResource
```php
<?php

declare(strict_types=1);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

namespace App\Http\Resources;

use Hypervel\Http\Request;
use Hypervel\Http\Resources\JsonApi\JsonApiResource;

class PostResource extends JsonApiResource
{
/**
* The resource's attributes.
*/
public $attributes = [
public array $attributes = [
// ...
];

/**
* The resource's relationships.
*/
public $relationships = [
public array $relationships = [
// ...
];
}
Expand Down Expand Up @@ -1040,7 +1041,7 @@ There are two ways to define which attributes are included in your JSON:API reso
The simplest approach is to define an `$attributes` property on your resource. You may list attribute names as values, which will be read directly from the underlying model:

```php
public $attributes = [
public array $attributes = [
'title',
'body',
'created_at',
Expand All @@ -1052,6 +1053,8 @@ If an attribute is expensive to calculate, you may return it from `toAttributes`
Or, for full control over the resource's attributes, you may override the `toAttributes` method on the resource:

```php
use Hypervel\Http\Request;

/**
* Get the resource's attributes.
*
Expand Down Expand Up @@ -1079,7 +1082,7 @@ JSON:API resources support defining relationships that follow the JSON:API speci
You may define your resource's includable relationships via the `$relationships` property on your resource:

```php
public $relationships = [
public array $relationships = [
'author',
'comments',
];
Expand All @@ -1090,7 +1093,7 @@ When listing a relationship name as a value, Hypervel will resolve the correspon
```php
use App\Http\Resources\UserResource;

public $relationships = [
public array $relationships = [
'author' => UserResource::class,
'comments',
];
Expand All @@ -1099,6 +1102,8 @@ public $relationships = [
Alternatively, you may override the `toRelationships` method on the resource:

```php
use Hypervel\Http\Request;

/**
* Get the resource's relationships.
*/
Expand Down Expand Up @@ -1199,6 +1204,8 @@ By default, the resource's `type` is derived from the resource class name. For e
If you need to customize these values, you may override the `toType` and `toId` methods on your resource:

```php
use Hypervel\Http\Request;

/**
* Get the resource's type.
*/
Expand Down Expand Up @@ -1256,6 +1263,8 @@ return $post->load('author', 'comments')
You may add links and meta information to your JSON:API resource objects by overriding the `toLinks` and `toMeta` methods on the resource:

```php
use Hypervel\Http\Request;

/**
* Get the resource's links.
*/
Expand Down
10 changes: 9 additions & 1 deletion src/docs/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -3461,6 +3461,12 @@ return now()->minus(hours: 8);
return now()->minus(weeks: 4);
```

When adding or subtracting months or years, you may pass `overflow: false` to keep the resulting date within the target month:

```php
CarbonImmutable::parse('2026-01-31')->plus(months: 1, overflow: false); // 2026-02-28
```

Since the default date is immutable, assign the result of a modifier when you want to retain the changed value:

```php
Expand All @@ -3482,7 +3488,7 @@ For a thorough discussion of Carbon and its features, please consult the [offici
<a name="interval-functions"></a>
#### Interval Functions

Hypervel also offers `milliseconds`, `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, and `years` functions that return `CarbonInterval` instances, which extend PHP's [DateInterval](https://www.php.net/manual/en/class.dateinterval.php) class. These functions may be used anywhere that Hypervel accepts a `DateInterval` instance:
Hypervel also offers `microseconds`, `milliseconds`, `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, and `years` functions that return `CarbonInterval` instances, which extend PHP's [DateInterval](https://www.php.net/manual/en/class.dateinterval.php) class. These functions may be used anywhere that Hypervel accepts a `DateInterval` instance:

```php
use Hypervel\Support\Facades\Cache;
Expand All @@ -3492,6 +3498,8 @@ use function Hypervel\Support\{minutes};
Cache::put('metrics', $metrics, minutes(10));
```

The functions from `microseconds` through `days` also accept fractional amounts, such as `seconds(1.5)`.

<a name="deferred-functions"></a>
### Deferred Functions

Expand Down
10 changes: 5 additions & 5 deletions src/docs/http-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -485,17 +485,17 @@ return Http::post(/* ... */)->throw(function (Response $response, RequestExcepti
})->json();
```

By default, `RequestException` messages are truncated to 120 characters when logged or reported. To customize or disable this behavior, you may utilize the `truncateRequestExceptionsAt` and `dontTruncateRequestExceptions` methods when configuring your application's exception handling behavior in your `bootstrap/app.php` file:
By default, `RequestException` messages are truncated to 120 characters when logged or reported. To customize or disable this behavior, you may utilize the `truncateAt` and `dontTruncate` methods when configuring your application's registered behavior in your `bootstrap/app.php` file:

```php
use Hypervel\Foundation\Configuration\Exceptions;
use Hypervel\Http\Client\RequestException;

->withExceptions(function (Exceptions $exceptions): void {
->registered(function (): void {
// Truncate request exception messages to 240 characters...
$exceptions->truncateRequestExceptionsAt(240);
RequestException::truncateAt(240);

// Disable request exception message truncation...
$exceptions->dontTruncateRequestExceptions();
RequestException::dontTruncate();
})
```

Expand Down
10 changes: 10 additions & 0 deletions src/docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ php artisan schema:dump --database=testing --prune

You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure.

To prevent schema dumps in production, call `DumpCommand::prohibit` from the `boot` method of your application's `AppServiceProvider`:

```php
use Hypervel\Database\Console\DumpCommand;

DumpCommand::prohibit($this->app->isProduction());
```

When prohibited, `schema:dump` exits without dumping the schema or pruning migrations.

> [!WARNING]
> Migration squashing is only available for the MariaDB, MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client.

Expand Down
8 changes: 7 additions & 1 deletion src/docs/porting-from-laravel.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [HTTP Client and Concurrency](#http-client-and-concurrency)
- [Scout](#scout)
- [JSON Schema](#json-schema)
- [Validation](#validation)
- [Data Objects](#data-objects)
- [Rate Limiting](#rate-limiting)
- [Pagination](#pagination)
Expand Down Expand Up @@ -500,6 +501,11 @@ Hypervel compiles integer and float values passed to Scout's Algolia `where`, `w

When porting schemas that place sibling assertions beside a local `$ref` or use nullable composition, make overlapping assertions identical. Hypervel rejects conflicts instead of silently replacing referenced constraints. See the [JSON Schema documentation](/docs/{{version}}/json-schema#reconstructing-schemas).

<a name="validation"></a>
### Validation

Handwritten validation parameters use standard CSV quoting. Replace backslash-escaped quotes inside quoted parameters with doubled quotes; backslashes are literal. Fluent rule builders handle quoting for you. See [rule parameters](/docs/{{version}}/validation#rule-parameters).

<a name="data-objects"></a>
### Data Objects

Expand Down Expand Up @@ -575,7 +581,7 @@ Hypervel's `migrate:fresh` command discovers the connection declared by each mig

Hypervel's Redis integration uses the PhpRedis extension exclusively. Its default `config/database.php` file does not contain a `client` option or `REDIS_CLIENT` environment variable. Remove those Laravel settings when porting configuration. A copied `client` option with any value other than `phpredis` is rejected; Predis is not supported.

Laravel's top-level `database.redis.clusters` configuration is also rejected. Each Hypervel Redis connection selects its standalone, Sentinel, or Cluster topology within the named connection, so begin with the matching Hypervel example instead of adapting Laravel's connection shape. Optional advanced members use their documented defaults when omitted. Hypervel does not support Laravel's `retry_interval` setting; configure retries with `max_retries`, `backoff_algorithm`, `backoff_base`, and `backoff_cap`. Configure Redis Cluster by adding a `cluster` array to a named Redis connection. See the [Redis configuration](/docs/{{version}}/redis#configuration) and [cluster documentation](/docs/{{version}}/redis#clusters).
Laravel's top-level `database.redis.clusters` configuration is also rejected. Each Hypervel Redis connection selects its standalone, Sentinel, or Cluster topology within the named connection, so begin with the matching Hypervel example instead of adapting Laravel's connection shape. Optional advanced members use their documented defaults when omitted. Hypervel does not support Laravel's `retry_interval` or `command_retries` settings and does not replay failed commands; configure PhpRedis connection retries with `max_retries`, `backoff_algorithm`, `backoff_base`, and `backoff_cap`. Configure Redis Cluster by adding a `cluster` array to a named Redis connection. See the [Redis configuration](/docs/{{version}}/redis#configuration) and [cluster documentation](/docs/{{version}}/redis#clusters).

<a name="cache"></a>
### Cache
Expand Down
16 changes: 15 additions & 1 deletion src/docs/queues.md
Original file line number Diff line number Diff line change
Expand Up @@ -2855,13 +2855,27 @@ php artisan queue:pause database:default

In this example, `database` is the queue connection name and `default` is the queue name. Once a queue is paused, any workers processing jobs from that queue will continue to finish their current job, but will not pick up any new jobs until the queue is resumed.

To pause job processing for every queue on every connection, use the `--all` option:

```shell
php artisan queue:pause --all
```

To resume processing jobs on a paused queue, use the `queue:resume` command:

```shell
php artisan queue:resume database:default
```

After resuming a queue, workers will begin processing new jobs from that queue immediately. The `queue:continue` command is available as an alias for `queue:resume`. Note that pausing a queue does not stop the worker process itself - it only prevents the worker from processing new jobs from the specified queue.
To resume job processing for every queue on every connection, use the `--all` option with the `queue:resume` command:

```shell
php artisan queue:resume --all
```

After resuming a queue, workers will begin processing new jobs from that queue immediately. Resuming all queues does not resume queues that were paused individually. The `queue:continue` command is available as an alias for `queue:resume`. Note that pausing a queue does not stop the worker process itself - it only prevents the worker from processing new jobs from the specified queue.

Queue workers report paused and resumed queues in their console output.

<a name="worker-restart-and-pause-signals"></a>
#### Worker Restart and Pause Signals
Expand Down
2 changes: 1 addition & 1 deletion src/docs/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ The PhpRedis extension may also be configured to use a variety of serializers an

Currently supported serializers include: `Redis::SERIALIZER_NONE` (default), `Redis::SERIALIZER_PHP`, `Redis::SERIALIZER_JSON`, `Redis::SERIALIZER_IGBINARY`, and `Redis::SERIALIZER_MSGPACK`.

Redis atomic counters must remain unencoded. Connections used for atomic increments or decrements should either use `Redis::SERIALIZER_NONE` or enable `pack_ignore_numbers` with PhpRedis 6.2 or later. This applies to standalone and Cluster connections.
Redis atomic counters must remain unencoded. Connections used to store atomic counters must disable both serialization and compression, or enable `pack_ignore_numbers` with PhpRedis 6.2 or later. This applies to standalone and Cluster connections.

When relying on Cache's [serializable class allowlist](/docs/{{version}}/cache#serializable-cached-objects), configure the connection used by the Redis cache store with `Redis::SERIALIZER_NONE`. Options in the shared `options` array also apply to that connection.

Expand Down
23 changes: 23 additions & 0 deletions src/docs/requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,29 @@ Input values containing arrays may be retrieved using the `array` method. This m
$versions = $request->array('versions');
```

<a name="retrieving-fluent-input-values"></a>
#### Retrieving Fluent Input Values

The `fluent` method retrieves input as a `Hypervel\Support\Fluent` instance, allowing you to access its values as properties:

```php
$user = $request->fluent('user');

$name = $user->name;
```

If the input is missing or `null`, an empty instance is returned. You may pass an array of default values as the second argument:

```php
$user = $request->fluent('user', ['name' => 'Guest']);
```

You may also pass an array of keys to build the instance from only those input values:

```php
$user = $request->fluent(['name', 'role']);
```

<a name="retrieving-date-input-values"></a>
#### Retrieving Date Input Values

Expand Down
Loading
Loading