-
-
Notifications
You must be signed in to change notification settings - Fork 15
feat: framework refactor + decouple from Hyperf #349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
binaryfire
wants to merge
609
commits into
hypervel:0.4
Choose a base branch
from
binaryfire:feature/hyperf-decouple
base: 0.4
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- Fix helpers.php: add missing return null in optional(), use while loop in retry() - Fix CacheCommandMutex: forceRelease() returns void, return true after - Fix ProcessInspector: remove incorrect @var annotation - Fix Onceable: add missing return null in tryFromTrace() - Fix ModelWatcher: use ModelEvent instead of non-existent Event class - Fix Response/CoreMiddleware/Logger: use toJson()/toArray() for Hypervel contracts - Update ResponseTest to use Hypervel contracts with proper toJson() implementation
- Worker: Fix broken tap pattern, use until() like Laravel, update EventDispatcher type - FailedJobProviders: Replace dynamic whereConnection/whereQueue with explicit where() - HasFactory: Add phpstan ignore for optional $factory property check - AutoScaler: Add type annotation to fix sum() type inference - Horizon RedisQueue: Add type assertion for RedisJob in pop() callback - Telescope migration: Fix getConnection return type to ?string - Tests: Update event namespaces and Connection mock for database port
Worker now requires Hypervel\Event\Contracts\Dispatcher (for until() method) instead of Psr\EventDispatcher\EventDispatcherInterface.
The test suite was hanging at ~25% due to exponential growth of $modifiedItems in ReloadDotenvAndConfig. Root cause: - Repository::$afterSettingCallback was static, so test-created Repository instances polluted the shared $modifiedItems array - array_merge() appends integer keys instead of overwriting them, causing exponential growth when tests used numeric config keys Fix: - Make $afterSettingCallback instance-scoped so only the container's Repository triggers the callback - Use array_replace() instead of array_merge() to handle integer keys correctly
- Update Contains and DoesntContain rules to use Hypervel\Contracts\Support\Arrayable instead of Hyperf\Contract\Arrayable - Add null guard in ValidatesAttributes::parseTable() before Str::startsWith() call since getConnectionName() can return null and the newly ported Str has strict types
Move Hypervel\Broadcasting\Contracts\* to Hypervel\Contracts\Broadcasting\* for Laravel directory structure parity.
Move Hypervel\Queue\Contracts\* to Hypervel\Contracts\Queue\* for Laravel directory structure parity. Update phpstan ignore pattern for new namespace.
Relocate all Auth contracts from src/auth/src/Contracts/ to src/contracts/src/Auth/ to match Laravel's directory structure. - Authenticatable, Factory, Guard, StatefulGuard, UserProvider moved to Hypervel\Contracts\Auth - Authorizable, Gate moved to Hypervel\Contracts\Auth\Access - Updated all 68 files referencing the old namespaces
Relocate all Bus contracts from src/bus/src/Contracts/ to src/contracts/src/Bus/ to match Laravel's directory structure. - Dispatcher, QueueingDispatcher (Laravel matches) - BatchRepository, PrunableBatchRepository (Hypervel-specific) - Updated all 42 files referencing the old namespaces
Relocate all Cache contracts from src/cache/src/Contracts/ to src/contracts/src/Cache/ to match Laravel's illuminate/contracts directory structure. Updated namespace from Hypervel\Cache\Contracts to Hypervel\Contracts\Cache across all src and test files.
Relocate Cookie contract from src/cookie/src/Contracts/ to src/contracts/src/Cookie/ to match Laravel's illuminate/contracts directory structure. Updated namespace from Hypervel\Cookie\Contracts to Hypervel\Contracts\Cookie across all src and test files.
Move contract-documented exceptions that may be thrown/caught across packages: - Encryption: DecryptException, EncryptException - Filesystem: LockTimeoutException - Cache: LockTimeoutException
Cross-cutting exception used by router package's ThrottleRequests middleware.
Relocate contracts from Hypervel\Router\Contracts to Hypervel\Contracts\Router for centralized contract management.
Relocate contracts from Hypervel\Session\Contracts to Hypervel\Contracts\Session for centralized contract management.
Relocate contracts from Hypervel\Translation\Contracts to Hypervel\Contracts\Translation for centralized contract management.
Relocate contracts from Hypervel\Validation\Contracts to Hypervel\Contracts\Validation for centralized contract management.
Relocate contracts from Hypervel\Hashing\Contracts to Hypervel\Contracts\Hashing for centralized contract management.
- Application -> Contracts\Foundation\Application - ExceptionRenderer -> Contracts\Foundation\ExceptionRenderer - ExceptionHandler -> Contracts\Debug\ExceptionHandler - ShouldntReport -> Contracts\Debug\ShouldntReport
- Add DEPTH_KEY constant for DI depth tracking - Add UnitEnum|string support for all key methods using enum_value() - Add setMany() for setting multiple key-value pairs - Add copyFromNonCoroutine() for copying from non-coroutine context - Add destroyAll() for destroying all context data except DEPTH_KEY - Skip __call method (dead code - never used, all calls are static)
Convenience wrapper to access parent coroutine's context. Uses Hyperf\Coroutine\Coroutine for inCoroutine/parentId methods.
- Delete tests/Core/ContextTest.php and ContextEnumTest.php (moved to tests/Context/) - Delete src/core/src/Context/ directory (replaced by src/context/)
The value() function is defined in the global namespace in collections/helpers.php, not in Hypervel\Support namespace.
The previous test expected a TypeError for int-backed enums, but that was a side-effect of the Hyperf parent class signature (string $id). Now that Context is standalone, int-backed enums work correctly as array keys. Updated test to verify the correct behavior.
ApplicationContext::getContainer() returns Hypervel\Contracts\Container\Container which guarantees call() exists, making the method_exists() check always true.
This allows Hypervel code using Hypervel\Context\Context to share the static $nonCoContext storage with Hyperf vendor code that still uses Hyperf\Context\Context directly. TODO: Remove extends and uncomment $nonCoContext once all Hyperf dependencies are removed.
Same pattern as Context class - extend parent to share static $container with Hyperf vendor code until all Hyperf dependencies are removed.
- ApplicationContext now extends Hyperf\Context\ApplicationContext to share static container storage - Update tests that mock ContainerInterface to use Hypervel\Contracts\Container\Container instead - Update tests using Hyperf\Di\Container to use Hypervel\Container\Container instead
- MailMailableTest: Use Testbench, bind mocks via $this->app->set() - MailMailerTest: Use Testbench, simplify mockContainer - MailManagerTest: Use Testbench with setUp to mock ViewFactory - MailFailoverTransportTest: Partial update (in progress)
Update tests that were using Hyperf\Di\Container or mocking Hyperf\Contract\ContainerInterface to use Hypervel\Container\Container and Hypervel\Contracts\Container\Container instead. This is required because ApplicationContext::getContainer() now returns Hypervel\Contracts\Container\Container, not Psr\Container\ContainerInterface. - Mail tests: Convert to Testbench TestCase for proper container bootstrap - Router tests: Use Hypervel container and contract - Notifications tests: Use Hypervel container
Update tests that were using Hyperf\Di\Container or mocking Psr\Container\ContainerInterface to use Hypervel\Container\Container and Hypervel\Contracts\Container\Container instead.
- Update Queue tests to use Hypervel container - Delete duplicate Context tests from tests/Core (moved to tests/Context) - Fix phpstan error in Context.php (commented-out @var tag) - Run php-cs-fixer on entire codebase
- Delete old Context files from src/core/src/Context (now in src/context) - Move context helpers.php from core to context package - Update composer.json files for helpers.php location - Fix types assertion for looser Collection return type
- Port EloquentModelCustomEventsTest - Port EloquentModelDateCastingTest - Port EloquentModelDecimalCastingTest - Port EloquentModelEncryptedCastingTest - Port EloquentModelEncryptedDirtyTest - Add cipher, key, and previous_keys to workbench app.php config
…to Argon hashers Tests: - Port EloquentModelEnumCastingTest - Port EloquentModelHashedCastingTest - Port Enums.php fixture Source fixes: - Add verifyConfiguration(), isUsingCorrectAlgorithm(), isUsingValidOptions() to ArgonHasher - Add isUsingCorrectAlgorithm() override to Argon2IdHasher for 'argon2id' algorithm check - These methods were missing, causing hashed cast verification to always pass for Argon hashes
Tests ported: - EloquentModelImmutableDateCastingTest - EloquentModelJsonCastingTest - EloquentModelLoadCountTest - EloquentModelLoadMaxTest - EloquentModelLoadMinTest - EloquentModelLoadMissingTest - EloquentModelLoadSumTest - EloquentModelRefreshTest Source fixes: - Change newPivot() return type from Pivot to Model/self in: - Model.php - InteractsWithPivotTable.php (also newExistingPivot) - MorphToMany.php - This allows AsPivot trait to be used on models that don't extend Pivot
The whereNotNull() method passes null to where() when called without arguments (to filter items that are not null values). This failed because the $key type hint was too strict (callable|string). data_get($item, null) correctly returns the item itself, making null a valid key. Updated where() and operatorForWhere() in EnumeratesValues trait and the Enumerable interface to accept callable|string|null. Also ports EloquentModelRelationAutoloadTest.
Converted 11 Laravel integration tests to Hypervel: - EloquentModelScopeTest - EloquentModelStringCastingTest - EloquentModelTest - EloquentModelWithoutEventsTest - EloquentMorphConstrainTest - EloquentMorphCountEagerLoadingTest - EloquentMorphCountLazyEagerLoadingTest - EloquentMorphEagerLoadingTest - EloquentMorphLazyEagerLoadingTest - EloquentMorphManyTest - EloquentMorphOneIsTest Standard conversions: namespace changes, strict types, property type hints, and method return types.
Ported 6 Laravel integration tests: - EloquentMorphToGlobalScopesTest - EloquentMorphToIsTest - EloquentMorphToLazyEagerLoadingTest - EloquentMorphToSelectTest - EloquentMorphToTouchesTest - EloquentMultiDimensionalArrayEagerLoadingTest Removed `: static` return type from flatMap() in Enumerable interface and EnumeratesValues trait. Eloquent\Collection::collapse() returns base collection, which violates the `: static` contract. Laravel has no return type on flatMap(), so this aligns with upstream.
Ported 4 Laravel integration tests: - EloquentNamedScopeAttributeTest - EloquentPaginateTest - EloquentPivotEventsTest - EloquentPivotSerializationTest Fixed getQueryForModelRestoration() type hint in queue package to allow string IDs. Pivot models use composite string keys which were rejected by the strict array|int type. Changed to array|int|string to match Laravel's untyped signature.
Standard conversion: namespace changes, strict types, property type hints, and method return types.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Hi @albertcht. This isn't ready yet but I'm opening it as a draft so we can begin discussions and code reviews. The goal of this PR is to refactor Hypervel to be a fully standalone framework that is as close to 1:1 parity with Laravel as possible.
Why one large PR
Sorry about the size of this PR. I tried spreading things across multiple branches but it made my work a lot more difficult. This is effectively a framework refactor - the database package is tightly coupled to many other packages (collections, pagination, pool) as well as several support classes, so all these things need to be updated together. Splitting it across branches would mean each branch needs multiple temporary workarounds + would have failing tests until merged together, making review and CI impractical.
A single large, reviewable PR is less risky than a stack of dependent branches that can't pass CI independently.
Reasons for the refactor
1. Outdated Hyperf packages
It's been difficult to migrate existing Laravel projects to Hypervel because Hyperf's database packages are quite outdated. There are almost 100 missing methods, missing traits, it doesn't support nested transactions, there are old Laravel bugs which haven't been fixed (eg. JSON indices aren't handled correctly), coroutine safety issues (eg. model
unguard(),withoutTouching()). Other packages like pagination, collections and support are outdated too.Stringablewas missing a bunch of methods and traits, for example. There are just too many to PR to Hyperf at this point.2. Faster framework development
We need to be able to move quickly and waiting for Hyperf maintainers to merge things adds a lot of friction to framework development. Decoupling means we don't need to work around things like PHP 8.4 compatibility while waiting for it to be added upstream. Hyperf's testing package uses PHPUnit 10 so we can't update to PHPUnit 13 (and Pest 4 in the skeleton) when it releases in a couple of weeks. v13 has the fix that allows
RunTestsInCoroutineto work with newer PHPUnit versions. There are lots of examples like this.3. Parity with Laravel
We need to avoid the same drift from Laravel that's happened with Hyperf since 2019. If we're not proactive with regularly merging Laravel updates every week we'll end up in the same situation. Having a 1:1 directory and code structure to Laravel whenever possible will make this much easier. Especially when using AI tools.
Most importantly, we need to make it easier for Laravel developers to use and contribute to the framework. That means following the same APIs and directory structures and only modifying code when there's a good reason to (coroutine safety, performance, type modernisation etc).
Right now the Hypervel codebase is confusing for both Laravel developers and AI tools:
hypervel/contractspackage, the Hyperf database code is split across 3 packages, the Hyperf pagination package ishyperf/paginatorand nothyperf/pagination)static::registerCallback('creating')vsstatic::creating())ConfigProviderand LaravelServiceProviderpatterns across different packages is confusing for anyone who doesn't know HyperfThis makes it difficult for Laravel developers to port over apps and to contribute to the framework.
4. AI
The above issues mean that AI needs a lot of guidance to understand the Hypervel codebase and generate Hypervel boilerplate. A few examples:
hypervel/contractsfor contracts) and then have to spend a lot of time grepping for things to find them.And so on... This greatly limits the effectiveness of building Hypervel apps with AI. Unfortunately MCP docs servers and CLAUDE.md rules don't solve all these problems - LLMs aren't great at following instructions well and the sheer volume of Laravel data they've trained on means they always default to Laravel-style code. The only solution is 1:1 parity. Small improvements such as adding native type hints are fine - models can solve that kind of thing quickly from exception messages.
What changed so far
New packages
illuminate/databaseportilluminate/collectionsportilluminate/paginationportilluminate/contracts)hyperf/pool)Macroableto a separate package for Laravel parityRemoved Hyperf dependencies so far
Database package
The big task was porting the database package, making it coroutine safe, implementing performance improvements like static caching and modernising the types.
whereLike,whereNot,groupLimit,rawValue,soleValue, JSON operations, etc.Collections package
Contracts package
Support package
hyperf/tappable,hyperf/stringable,hyperf/macroable,hyperf/codecdependenciesStr,Envand helper classes from LaravelHypervel\Contextwrappers (will be portinghyperf/contextsoon)Number::useCurrency()wasn't actually setting the currency)Coroutine safety
withoutEvents(),withoutBroadcasting(),withoutTouching()now use Context instead of static propertiesUnsetContextInTaskWorkerListenerto clear database context in task workersConnection::resetForPool()to prevent state leaks between coroutinesDatabaseTransactionsManagercoroutine-safeBenefits
Testing status so far
What's left (WIP)
The refactor process
Hyperf's Swoole packages like
pool,coroutine,contextandhttp-serverhaven't changed in many years so porting these is straightforward. A lot of the code can be simplified since we don't need SWOW support. And we can still support the ecosystem by contributing any improvements we make back to Hyperf in separate PRs.Eventually I'll refactor the bigger pieces like the container (contextual binding would be nice!) and the config system (completely drop
ConfigProviderand move entirely to service providers). But those will be future PRs. For now the main refactors are the database layer, collections and support classes + the simple Hyperf packages. I'll just port the container and config packages as-is for now.Let me know if you have any feedback, questions or suggestions. I'm happy to make any changes you want. I suggest we just work through this gradually, as an ongoing task over the next month or so. I'll continue working in this branch and ping you each time I add something new.