From ff75c301feb167d50da9dbcea28f29a01208a5cb Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 28 Apr 2026 14:56:29 +0800 Subject: [PATCH 1/7] chore: align coding style with newest php-cs-fixer --- .../src/Support/Providers/RouteServiceProvider.php | 3 +-- src/horizon/src/EventMap.php | 6 ++---- src/sentry/config/sentry.php | 3 +-- src/telescope/config/telescope.php | 6 ++---- src/telescope/src/TelescopeApplicationServiceProvider.php | 3 +-- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/foundation/src/Support/Providers/RouteServiceProvider.php b/src/foundation/src/Support/Providers/RouteServiceProvider.php index 48c51cbf9..6ce60b15e 100644 --- a/src/foundation/src/Support/Providers/RouteServiceProvider.php +++ b/src/foundation/src/Support/Providers/RouteServiceProvider.php @@ -12,8 +12,7 @@ class RouteServiceProvider extends ServiceProvider /** * The route files for the application. */ - protected array $routes = [ - ]; + protected array $routes = []; public function boot(): void { diff --git a/src/horizon/src/EventMap.php b/src/horizon/src/EventMap.php index 02798ae92..353f7cccc 100644 --- a/src/horizon/src/EventMap.php +++ b/src/horizon/src/EventMap.php @@ -61,11 +61,9 @@ trait EventMap Listeners\MonitorWaitTimes::class, ], - Events\WorkerProcessRestarting::class => [ - ], + Events\WorkerProcessRestarting::class => [], - Events\SupervisorProcessRestarting::class => [ - ], + Events\SupervisorProcessRestarting::class => [], Events\LongWaitDetected::class => [ Listeners\SendNotification::class, diff --git a/src/sentry/config/sentry.php b/src/sentry/config/sentry.php index b7756114e..a7f2bf7cd 100644 --- a/src/sentry/config/sentry.php +++ b/src/sentry/config/sentry.php @@ -95,8 +95,7 @@ ValidationException::class, ], - 'ignore_transactions' => [ - ], + 'ignore_transactions' => [], 'ignore_commands' => [ 'crontab:run', diff --git a/src/telescope/config/telescope.php b/src/telescope/config/telescope.php index 18902050c..9a3530dce 100644 --- a/src/telescope/config/telescope.php +++ b/src/telescope/config/telescope.php @@ -127,11 +127,9 @@ // 'api/*' ], - 'ignore_paths' => [ - ], + 'ignore_paths' => [], - 'ignore_commands' => [ - ], + 'ignore_commands' => [], /* |-------------------------------------------------------------------------- diff --git a/src/telescope/src/TelescopeApplicationServiceProvider.php b/src/telescope/src/TelescopeApplicationServiceProvider.php index 614af2d98..a65da14ea 100644 --- a/src/telescope/src/TelescopeApplicationServiceProvider.php +++ b/src/telescope/src/TelescopeApplicationServiceProvider.php @@ -39,8 +39,7 @@ protected function authorization(): void protected function gate(): void { Gate::define('viewTelescope', function ($user) { - return in_array($user->email, [ - ]); + return in_array($user->email, []); }); } From 57be66ba16851a470829574c1572680b90182562 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 28 Apr 2026 15:07:06 +0800 Subject: [PATCH 2/7] improve: improve code via phpstan --- src/bus/src/PendingChain.php | 2 +- src/horizon/src/ProcessPool.php | 2 -- src/http-client/src/PendingRequest.php | 4 ++-- src/http-client/src/Request.php | 2 +- src/queue/src/Queue.php | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/bus/src/PendingChain.php b/src/bus/src/PendingChain.php index 8b86c5e30..f51355283 100644 --- a/src/bus/src/PendingChain.php +++ b/src/bus/src/PendingChain.php @@ -100,7 +100,7 @@ public function catch(callable $callback): static */ public function catchCallbacks(): array { - return $this->catchCallbacks ?? []; + return $this->catchCallbacks ?: []; } /** diff --git a/src/horizon/src/ProcessPool.php b/src/horizon/src/ProcessPool.php index ff67fd57a..dd8bed92b 100644 --- a/src/horizon/src/ProcessPool.php +++ b/src/horizon/src/ProcessPool.php @@ -126,8 +126,6 @@ public function markForTermination(WorkerProcess $process): void protected function removeProcesses(int $count): void { array_splice($this->processes, 0, $count); - - $this->processes = array_values($this->processes); } /** diff --git a/src/http-client/src/PendingRequest.php b/src/http-client/src/PendingRequest.php index 863813d4e..a491eb028 100644 --- a/src/http-client/src/PendingRequest.php +++ b/src/http-client/src/PendingRequest.php @@ -728,7 +728,7 @@ public function send(string $method, string $url, array $options = []): PromiseI $shouldRetry = null; - return retry($this->tries ?? 1, function ($attempt) use ($method, $url, $options, &$shouldRetry) { + return retry($this->tries ?: 1, function ($attempt) use ($method, $url, $options, &$shouldRetry) { try { return tap( $this->newResponse($this->sendRequest($method, $url, $options)), @@ -780,7 +780,7 @@ function (Response $response) use ($attempt, &$shouldRetry) { throw $exception; } - }, $this->retryDelay ?? 100, function ($exception) use (&$shouldRetry) { + }, $this->retryDelay ?: 100, function ($exception) use (&$shouldRetry) { // @phpstan-ignore-next-line nullCoalesce.variable $result = $shouldRetry ?? ($this->retryWhenCallback ? call_user_func( $this->retryWhenCallback, diff --git a/src/http-client/src/Request.php b/src/http-client/src/Request.php index 8bc3f0de8..3ea74e406 100644 --- a/src/http-client/src/Request.php +++ b/src/http-client/src/Request.php @@ -133,7 +133,7 @@ public function data(): array return $this->json(); } - return $this->data ?? []; + return $this->data ?: []; } /** diff --git a/src/queue/src/Queue.php b/src/queue/src/Queue.php index 365e4c033..b41d69e6d 100644 --- a/src/queue/src/Queue.php +++ b/src/queue/src/Queue.php @@ -315,7 +315,7 @@ protected function shouldDispatchAfterCommit(object|string $job): bool return $job->afterCommit; } - return $this->dispatchAfterCommit ?? false; + return $this->dispatchAfterCommit; } /** From 703949d17363a4f03e8717e55c62ecdf7a3dc45d Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 28 Apr 2026 15:11:24 +0800 Subject: [PATCH 3/7] fix: fix FileinfoMimeTypeGuesser in coroutines --- src/support/src/FileinfoMimeTypeGuesser.php | 11 ++-- tests/Support/FileinfoMimeTypeGuesserTest.php | 49 ++++++++++++++++++ tests/Support/fixtures/test.gif | Bin 0 -> 35 bytes 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/Support/FileinfoMimeTypeGuesserTest.php create mode 100644 tests/Support/fixtures/test.gif diff --git a/src/support/src/FileinfoMimeTypeGuesser.php b/src/support/src/FileinfoMimeTypeGuesser.php index fa04a2b33..6835e52e9 100644 --- a/src/support/src/FileinfoMimeTypeGuesser.php +++ b/src/support/src/FileinfoMimeTypeGuesser.php @@ -15,6 +15,7 @@ use Exception; use finfo; +use Hypervel\Context\Context; use InvalidArgumentException; use LogicException; use RuntimeException; @@ -24,10 +25,7 @@ */ class FileinfoMimeTypeGuesser { - /** - * @var array - */ - private static $finfoCache = []; + private const FINFO_CACHE_KEY = '__support.finfo_mime_type_guesser.'; /** * @param null|string $magicFile A magic file to use with the finfo instance @@ -55,7 +53,10 @@ public function guessMimeType(string $path): ?string } try { - $finfo = self::$finfoCache[$this->magicFile] ??= new finfo(FILEINFO_MIME_TYPE, $this->magicFile); + $finfo = Context::getOrSet( + self::FINFO_CACHE_KEY . ($this->magicFile ?? ''), + fn () => new finfo(FILEINFO_MIME_TYPE, $this->magicFile) + ); } catch (Exception $e) { throw new RuntimeException($e->getMessage()); } diff --git a/tests/Support/FileinfoMimeTypeGuesserTest.php b/tests/Support/FileinfoMimeTypeGuesserTest.php new file mode 100644 index 000000000..5a59b72dd --- /dev/null +++ b/tests/Support/FileinfoMimeTypeGuesserTest.php @@ -0,0 +1,49 @@ +expectException(InvalidArgumentException::class); + + (new FileinfoMimeTypeGuesser()) + ->guessMimeType(__DIR__ . '/unknown'); + } + + public function testGuessMimeType(): void + { + $mimeType = (new FileinfoMimeTypeGuesser()) + ->guessMimeType(__DIR__ . '/fixtures/test.gif'); + + $this->assertEquals('image/gif', $mimeType); + } + + public function testGuessMimeTypeInCoroutines(): void + { + $guesser = (new FileinfoMimeTypeGuesser()); + for ($i = 0; $i < 5; ++$i) { + Coroutine::create(function () use ($guesser) { + $mimeType = $guesser->guessMimeType(__DIR__ . '/fixtures/test.gif'); + $this->assertEquals('image/gif', $mimeType); + }); + } + } +} diff --git a/tests/Support/fixtures/test.gif b/tests/Support/fixtures/test.gif new file mode 100644 index 0000000000000000000000000000000000000000..b506624ab23a8fb9bea614a56af81e5723576685 GIT binary patch literal 35 kcmZ?wbh9u|WMp7uXkcUjg8%>j>wsvG2m=$74 Date: Tue, 28 Apr 2026 07:37:50 +0000 Subject: [PATCH 4/7] chore: restore Queue.php to 0.4 version Co-authored-by: albertcht <9117929+albertcht@users.noreply.github.com> --- src/queue/src/Queue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queue/src/Queue.php b/src/queue/src/Queue.php index 8136197da..ec60f8607 100644 --- a/src/queue/src/Queue.php +++ b/src/queue/src/Queue.php @@ -356,7 +356,7 @@ protected function shouldDispatchAfterCommit(object|string $job): bool return $job->afterCommit; } - return $this->dispatchAfterCommit; + return $this->dispatchAfterCommit ?? false; } /** From 20474c0d8548d74c01673ad872a592a628a3a88f Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 1 May 2026 11:58:37 +0000 Subject: [PATCH 5/7] fix(support): use CoroutineContext for finfo cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix imported `Hypervel\Context\Context`, which does not exist in this codebase — `hypervel/context` only exposes `CoroutineContext`, `RequestContext`, `ResponseContext`, `ParentCoroutineContext`, `ReplicableContext`. Calling `guessMimeType()` threw a class-not-found error. Switch to `CoroutineContext::getOrSet()` to match how every other ported package uses per-coroutine caching (JwtGuard, Sentry\Hub, UrlGenerator, Log\Context\Repository). Rename the constant to `FINFO_CONTEXT_KEY_PREFIX` per CLAUDE.md naming convention (existing examples: `CACHED_SCHEME_CONTEXT_KEY`, `CACHED_ROOT_CONTEXT_KEY`) and promote it to `public` so the regression test can read the cached `finfo` back out of context. --- src/support/src/FileinfoMimeTypeGuesser.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/support/src/FileinfoMimeTypeGuesser.php b/src/support/src/FileinfoMimeTypeGuesser.php index 6835e52e9..2debe0d96 100644 --- a/src/support/src/FileinfoMimeTypeGuesser.php +++ b/src/support/src/FileinfoMimeTypeGuesser.php @@ -15,7 +15,7 @@ use Exception; use finfo; -use Hypervel\Context\Context; +use Hypervel\Context\CoroutineContext; use InvalidArgumentException; use LogicException; use RuntimeException; @@ -25,7 +25,7 @@ */ class FileinfoMimeTypeGuesser { - private const FINFO_CACHE_KEY = '__support.finfo_mime_type_guesser.'; + public const FINFO_CONTEXT_KEY_PREFIX = '__support.finfo_mime_type_guesser.'; /** * @param null|string $magicFile A magic file to use with the finfo instance @@ -53,8 +53,8 @@ public function guessMimeType(string $path): ?string } try { - $finfo = Context::getOrSet( - self::FINFO_CACHE_KEY . ($this->magicFile ?? ''), + $finfo = CoroutineContext::getOrSet( + self::FINFO_CONTEXT_KEY_PREFIX . ($this->magicFile ?? ''), fn () => new finfo(FILEINFO_MIME_TYPE, $this->magicFile) ); } catch (Exception $e) { From de5ca7d9beb1cddcaf4873b996c866fd64a1965a Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 1 May 2026 11:58:40 +0000 Subject: [PATCH 6/7] test(support): move finfo fixture to Fixtures/ (capital F) --- tests/Support/{fixtures => Fixtures}/test.gif | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/Support/{fixtures => Fixtures}/test.gif (100%) diff --git a/tests/Support/fixtures/test.gif b/tests/Support/Fixtures/test.gif similarity index 100% rename from tests/Support/fixtures/test.gif rename to tests/Support/Fixtures/test.gif From dcfa550221463acf4ca66a2ee89916c928f74b07 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 1 May 2026 11:58:49 +0000 Subject: [PATCH 7/7] test(support): rewrite FileinfoMimeTypeGuesserTest per porting conventions - Extend `Hypervel\Tests\TestCase` instead of raw `PHPUnit\Framework\TestCase` - Drop the `RunTestsInCoroutine` trait (already on the base class) - Drop `@internal` / `@coversNothing` docblock and `: void` return types - Update fixture path to `Fixtures/` (capital F) Replace the bare `Coroutine::create()` regression test with `parallel()`, which awaits the children and propagates assertion failures back to PHPUnit. The previous form spawned 5 coroutines without a `WaitGroup`, so failures inside them never failed the test. Strengthen the test to actually exercise the per-coroutine isolation the fix provides: each coroutine pulls its cached `finfo` back out of `CoroutineContext` via `FINFO_CONTEXT_KEY_PREFIX` and the test asserts all 5 instances are distinct objects. Reverting to the shared static cache would now fail this test. --- tests/Support/FileinfoMimeTypeGuesserTest.php | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/tests/Support/FileinfoMimeTypeGuesserTest.php b/tests/Support/FileinfoMimeTypeGuesserTest.php index 5a59b72dd..2705661e0 100644 --- a/tests/Support/FileinfoMimeTypeGuesserTest.php +++ b/tests/Support/FileinfoMimeTypeGuesserTest.php @@ -4,46 +4,54 @@ namespace Hypervel\Tests\Support; -use Hypervel\Coroutine\Coroutine; -use Hypervel\Foundation\Testing\Concerns\RunTestsInCoroutine; +use finfo; +use Hypervel\Context\CoroutineContext; use Hypervel\Support\FileinfoMimeTypeGuesser; +use Hypervel\Tests\TestCase; use InvalidArgumentException; use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use PHPUnit\Framework\TestCase; -/** - * @internal - * @coversNothing - */ +use function Hypervel\Coroutine\parallel; + #[RequiresPhpExtension('fileinfo')] class FileinfoMimeTypeGuesserTest extends TestCase { - use RunTestsInCoroutine; - - public function testGuessMimeTypeWithInvalidFile(): void + public function testGuessMimeTypeWithInvalidFile() { $this->expectException(InvalidArgumentException::class); - (new FileinfoMimeTypeGuesser()) + (new FileinfoMimeTypeGuesser) ->guessMimeType(__DIR__ . '/unknown'); } - public function testGuessMimeType(): void + public function testGuessMimeType() { - $mimeType = (new FileinfoMimeTypeGuesser()) - ->guessMimeType(__DIR__ . '/fixtures/test.gif'); + $mimeType = (new FileinfoMimeTypeGuesser) + ->guessMimeType(__DIR__ . '/Fixtures/test.gif'); $this->assertEquals('image/gif', $mimeType); } - public function testGuessMimeTypeInCoroutines(): void + public function testGuessMimeTypeIsCoroutineScoped() { - $guesser = (new FileinfoMimeTypeGuesser()); - for ($i = 0; $i < 5; ++$i) { - Coroutine::create(function () use ($guesser) { - $mimeType = $guesser->guessMimeType(__DIR__ . '/fixtures/test.gif'); - $this->assertEquals('image/gif', $mimeType); - }); + $guesser = new FileinfoMimeTypeGuesser; + $key = FileinfoMimeTypeGuesser::FINFO_CONTEXT_KEY_PREFIX; + + $results = parallel(array_fill(0, 5, function () use ($guesser, $key) { + return [ + 'mimeType' => $guesser->guessMimeType(__DIR__ . '/Fixtures/test.gif'), + 'finfo' => CoroutineContext::get($key), + ]; + })); + + $this->assertCount(5, $results); + foreach ($results as $result) { + $this->assertSame('image/gif', $result['mimeType']); } + + $finfoInstances = array_column($results, 'finfo'); + + $this->assertContainsOnlyInstancesOf(finfo::class, $finfoInstances); + $this->assertCount(5, array_unique(array_map(spl_object_id(...), $finfoInstances))); } }