Make CarbonImmutable the framework default#449
Conversation
Introduce the canonical immutable counterpart to Hypervel Support Carbon while retaining the existing mutable class as an explicit opt-out. Move the shared conditionable, dumpable, identifier, plus, and minus behavior into one trait. Override mutability conversions so both directions preserve the Hypervel class pair, subclasses, settings, timezone, and immutable identity semantics. Cover the complete helper surface, serialization, conversions, late-static behavior, and immutable modifier behavior on both classes.
Resolve class-level @method metadata against the class that owns the docblock instead of borrowing constructor context or falling back to unresolved text. Make generation fail fast when parsing or type resolution fails, and render imported global classes in the same stable form as PHP CS Fixer without shortening namespaced collisions. Replace the obsolete fallback test with regressions for constructor-less owners, inherited constructors, global imports, namespaced imports, and colliding basenames.
Change DateFactory, the Date facade, global helpers, and Stringable date conversion to produce Hypervel CarbonImmutable by default while retaining CarbonInterface as the configurable public contract. Restrict class handlers to instantiable CarbonInterface implementations, normalize callable handlers once, remove the dead generic constructor path, and keep Carbon Factory handlers and callable transformations available. Regenerate facade metadata from the corrected owner and cover immutable defaults, mutable opt-out, custom Carbon subclasses, factories, callables, macros, locale behavior, invalid handlers, and helper return types.
Route DataObject date hydration through the configured Date factory, then convert the result according to the property declaration across native, Carbon interface, Hypervel Carbon, and base Carbon mutable and immutable targets. Preserve configured subclasses only at interface boundaries, honor exact concrete targets without losing Carbon settings, accept timestamp zero, and let nullability fail through the constructor contract. Unify date serialization on DateTimeInterface and add a full input, target, factory-mode, subclass, nullable, nested, serialization, and failure matrix.
Configure the existing PSR clock binding to construct Hypervel CarbonImmutable independently from application Date factory opt-outs. Keep one authoritative Carbon static-state reset in the PHPUnit subscriber and restore the shared test clock, macros, serializer, string format, and strict-mode state between tests. Verify exact clock output, frozen-time agreement, mutable factory isolation, and complete cleanup through both Hypervel Carbon variants.
Create frozen dates, wormhole targets, and Sleep deadlines through the configured Date factory while continuing to use Carbon shared static test-clock state. Capture immutable modifier results for every time-travel operation and preserve callback return values and exception-safe restoration. Cover immutable defaults, mutable opt-out, numeric deadlines, synchronization, each supported travel unit, and restoration after callback failures.
Store framework-owned HTTP request and console command start times as exact immutable values and capture timezone conversions instead of relying on mutable side effects. Write the converted HTTP start value back to coroutine context so duration handlers and lifecycle accessors observe the same instance, then clear lifecycle state after completion or failure. Exercise configured timezones, handler arguments, thresholds, coroutine-visible values, exception cleanup, and post-termination null state.
Use Hypervel CarbonImmutable for maintenance retry timestamps, bypass-cookie expiry, and worker-cached maintenance metadata. Keep these framework-owned values independent from application Date factory configuration while preserving their existing wire formats and expiry behavior. Update unit and integration coverage to assert the canonical Hypervel immutable class at each boundary.
Remove the mutable Carbon intermediate from request date casts and create standard, formatted, and fallback dates directly through DateFactory. Let Carbon createFromFormat own modifier grammar, catch only its exact invalid-format exception, and retain generic parsing as the mismatch fallback. Cover immutable output and valid PHP format modifiers, escaped literals, and trailing-data formats that Carbon preflight helpers do not model completely.
Use immutable framework dates for schedule listing, frequency calculations, and command logging while retaining CarbonInterface for the configurable scheduler start time. Compute the repeat-loop end boundary from a copy, stop before work that crosses the minute, and leave the original start instant unchanged for single-server mutex decisions under both mutable and immutable Date modes. Update scheduler unit and integration coverage for boundaries, frequencies, context propagation, grouped schedules, sub-minute execution, and mutable opt-out behavior.
Create Eloquent date casts, date query boundaries, and factory timestamps through the immutable Hypervel date model while preserving DateFactory configurability for ordinary date and datetime casts. Return the canonical Hypervel immutable class for explicit immutable casts, capture modifier results, retain intentional mutable serialization conversions, and parse formatted values through Carbon rather than incomplete format preflight helpers. Migrate database unit and integration expectations across model casts, timestamps, soft deletes, relations, factories, query builders, locks, cache storage, and all supported database drivers.
Widen the internal date parsing contracts from native mutable DateTime to DateTimeInterface so validator fallback parsing can return the configured immutable Carbon implementation. Retain native DateTime format results while allowing DateFactory parsing for after, before, and sibling date-format comparisons. Add regressions for immutable fallback results and update date-rule fixtures to capture immutable modifier values.
Return DateTimeInterface from queued mailable and notification retryUntil wrappers so application retry deadlines may use Hypervel CarbonImmutable without native return-type failures. Preserve nullable property and method-based retry contracts and keep locale-sensitive mail and notification fixtures aligned with immutable test dates. Cover both queued object types with immutable retry deadlines while retaining existing queue serialization behavior.
Use Hypervel CarbonImmutable for verification-link expiry and password-token repository deadlines so framework-owned authentication timestamps cannot be mutated after construction. Preserve existing expiry calculations, storage formats, and comparison behavior while capturing all modifier results explicitly. Update database and cache token repository coverage, verification-related middleware fixtures, and exact date expectations.
Store cache item and lock expiry values as exact Hypervel CarbonImmutable instances across array, worker-array, repository, session, stack, and Swoole stores. Keep public DateInterval and timestamp behavior unchanged while preventing shared worker records from being shifted by later modifier calls. Update cache unit and integration suites for immutable record shapes, TTL handling, locks, tags, memoization, sessions, database stores, Redis, and Swoole timers.
Use Hypervel CarbonImmutable for batch creation, cancellation, completion, pruning, debounce locks, batchable state, and batch repository fakes. Preserve serialized repository data and public batch behavior while ensuring framework-held timestamps use one canonical immutable class. Update batch, cancellation, debounce, retry-command, and fake-repository coverage to assert exact Hypervel immutable values.
Use Hypervel CarbonImmutable for queue availability, retry, pruning, worker timeout, and command timestamps while retaining DateTimeInterface at user-configurable scheduling boundaries. Capture deadline modifiers explicitly and keep queue payload timestamps, database storage, pause and resume behavior, and driver protocols unchanged. Migrate queue unit and integration coverage across database, Redis, SQS, Beanstalkd, deferred execution, rate limiting, throttling, chaining, failed jobs, work commands, and worker behavior.
Replace base CarbonImmutable values with Hypervel CarbonImmutable throughout Horizon job retries, trimming, monitoring, supervisors, process pools, metrics, and Redis repositories. Keep Redis payloads and Horizon public behavior stable while ensuring repository hydration and worker-held timestamps retain the Hypervel helper surface and immutable semantics. Update feature coverage for retrieval, metrics, wait monitoring, process repositories, queue processing, supervisors, trimming, and worker processes.
Use Hypervel CarbonImmutable for database and file session expiration and for session middleware lifetime calculations. Preserve existing garbage collection, cookie lifetime, storage, and route-lock behavior while capturing every modifier result that must persist. Update array, file, and database session handler coverage to use immutable clocks and exact expiry expectations.
Construct every registered request message and eligible concrete response message during the existing pre-fork router compilation phase. This moves process-global protobuf descriptor registration out of concurrent request coroutines, avoids first-use registration races, and removes descriptor initialization from the request path. Keep response warming limited to statically named message classes that can be constructed without required arguments. Also canonicalize gRPC call timestamps on Hypervel CarbonImmutable and add cold-process regressions for request and response descriptor warming alongside coroutine client isolation coverage.
Use Hypervel CarbonImmutable for the optional LazyCollection clock path and capture advancing timestamps explicitly during time-based iteration. Keep native microtime behavior and lazy evaluation unchanged while making Carbon-backed intervals safe under immutable semantics. Update lazy collection coverage for clock progression, throttling, and exact immutable values.
Use Hypervel CarbonImmutable for response cache expiry and temporary signed URL deadlines while preserving the existing HTTP and URL signature formats. Capture date modifiers explicitly and keep user-supplied DateTimeInterface boundaries unchanged. Align HTTP client, Redis throttling, and URL signing fixtures with the framework immutable default.
Use Hypervel CarbonImmutable for Telescope pruning and exception time windows, and express entry timestamp inputs through the existing CarbonInterface contract. Preserve query watcher payloads, stored timestamps, and pruning behavior while removing mutable-only metadata. Update watcher fixtures to capture immutable clock changes explicitly.
Describe Passkey and Sanctum model date attributes as CarbonInterface rather than concrete mutable Carbon classes so their metadata matches configurable Eloquent cast output. Keep nullable attribute semantics and model behavior unchanged while allowing the immutable framework default and explicit mutable opt-out. Align Sanctum expiry-pruning coverage with immutable modifier semantics.
Use Hypervel CarbonImmutable when TestResponse converts cookie timestamps and compares them with the current time. Keep assertion messages and session-cookie handling unchanged while ensuring test helpers follow the same date semantics as the framework they exercise.
Point the WithImmutableDates attribute and Testbench workbench casts at Hypervel CarbonImmutable, and treat the attribute as an explicit forcing mechanism for applications that otherwise opt into mutable dates. Update Testbench default configuration, timezone, migration, and attribute contracts to assert exact immutable output while preserving package-mode database behavior. Keep all runtime skeleton cleanup and environment isolation aligned with the shared Testbench base classes.
Freeze JWT tests through Hypervel CarbonImmutable and obtain configured current timestamps through the Date facade. Keep claim encoding, blacklist TTL, provider, and temporal validation behavior unchanged while making every fixture match the framework default. Add missing native test return types in the touched validation coverage.
Update array, Fluent, validated input, and InteractsWithData coverage to use and assert exact Hypervel CarbonImmutable results from default date parsing. Retain explicit coverage that InteractsWithData follows the mutable DateFactory opt-out, keeping configurable public boundaries honest. Capture stronger exact-class assertions without weakening existing value, timezone, enum, or formatting checks.
Move remaining filesystem, exception throttling, nested set, pagination, permission, and translation fixtures to Hypervel CarbonImmutable. Type native integration boundaries as DateTimeInterface, capture immutable clock changes, and assert canonical Eloquent date classes where the framework owns the result. Preserve each package contract and existing behavioral assertions while removing mutable-default assumptions from the test suite.
Describe the immutable default across helpers, Eloquent, requests, data objects, collections, strings, mail, notifications, queues, mocking, and Testbench. Show modifier-result assignment, CarbonInterface boundaries, exact concrete DataObject targets, explicit mutable opt-out, and the role of WithImmutableDates when an application changes its factory. Document gRPC descriptor prewarming for route-discoverable messages and the boot-time responsibility for client, iterable, union, or otherwise undiscoverable protobuf message classes.
Record Hypervel CarbonImmutable as a 0.4 framework default and explain the value-semantics and worker-safety motivation. Provide migration guidance for concrete mutable type declarations, retained modifier calls, custom CarbonInterface handlers, callable handlers, and the boot-time mutable opt-out. Keep release notes concise while giving upgrading applications the exact code changes required.
Add immutable dates to Hypervel code and porting conventions, including construction boundaries, interface typing, modifier assignment, and the explicit mutable Carbon exception. Record the Laravel difference, update historical plans whose examples describe current framework types, and remove the completed framework todo. Include the framework-wide audit and implementation record covering architecture, performance research, migration ledgers, edge cases, testing strategy, and deliberate non-changes.
Restore the original mutable Carbon examples and guidance in the array-cache and passkeys plans so those documents remain accurate records of their implementation context. Add concise notes near the top of each plan that identify the later immutable-date change, state the current behavior, and link to the framework-wide CarbonImmutable plan. This keeps past debugging context intact without allowing historical snippets to masquerade as current framework guidance.
|
Important Review skippedToo many files! This PR contains 277 files, which is 177 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (279)
You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR makes
Confidence Score: 5/5Safe to merge. The immutability change is a deliberate breaking change with a documented migration path, and the implementation is consistent, well-tested, and audited across 279 files. The core CarbonImmutable class, DateHelpers trait, DateFactory validation, and DataObject target matrix are all logically correct. Conversion semantics (crossing the mutability boundary before calling instance() to force construction of the exact target class) are tested across all four Carbon concrete types. The DateTimeInterface instanceof check in toArray correctly unifies serialization for both mutable and immutable values where the old exact-class dispatch missed CarbonImmutable. The GrpcRouter protobuf pre-warming includes a required-argument guard before calling newInstance(). The Date::createFromFormat + InvalidFormatException catch pattern replaces the incomplete Carbon::hasFormat preflight correctly in both HasAttributes and HasCasts. No behavioral regressions are visible in the framework-owned paths, and the full test suite is reported passing. No files require special attention; the changes are internally consistent. Important Files Changed
Reviews (2): Last reviewed commit: "build: raise the Carbon minimum to 3.13...." | Re-trigger Greptile |
|
@greptileai I checked all three observations against the current branch, PHP 8.4 reflection, and the installed Carbon 3.13.1 source.
The review identified useful maintenance boundaries, but no code change is warranted. |
|
Acknowledged on all three. The explanations are correct:
No changes warranted. Thanks for the thorough verification. |
Require the audited Carbon release from the monorepo and every split package that declares a direct Carbon dependency. The immutable-date implementation is built and tested against Carbon 3.13.1. The previous 3.8.4 floor was not covered by a lowest-dependency suite, so retaining it would claim support for behavior this change did not verify. Record the dependency baseline and its completion check in the implementation plan.
|
@greptileai Correction to my earlier summary: the review was right about the declared Carbon version. The local install was 3.13.1, but the root and split-package manifests still allowed That mismatch is fixed in 76e3ff5. The monorepo and every split package with a direct Carbon dependency now require |
|
The fix is clean and complete. Every Thanks for the clear correction. The original comment stands resolved — the version floor now matches the audited implementation baseline. |
Why
Hypervel inherited Laravel's mutable Carbon default, but it does not inherit Laravel's request-per-process lifecycle.
A Hypervel worker retains services, metadata, caches, and other values across many requests. Mutable dates held by those objects can be changed through an alias, and ordinary modifier calls depend on side effects that are easy to miss when a value crosses a framework boundary. The framework already used immutable dates in several subsystems and exposed an immutable PSR clock, so the effective model was inconsistent.
This change makes immutable dates the default value model across the framework:
The API remains Carbon. The difference is that a modifier returns the value that must be retained.
Design
Hypervel\Support\CarbonImmutableis now the canonical framework date class. It is the immutable counterpart toHypervel\Support\Carbon, not a wrapper around it.Both classes share Hypervel's existing additions through a small
DateHelperstrait:createFromId()for time-based UUIDs and ULIDs;plus()andminus().Conversions are overridden at the class boundary so mutable and immutable values stay within the Hypervel class pair. They preserve Carbon settings, timezone, microseconds, subclass behavior, and the identity semantics of immutable-to-immutable conversion.
The ownership rule is:
Date,now(), ortoday()and is typed asCarbonInterface.Hypervel\Support\CarbonImmutable.DateTimeInterface.Hypervel\Support\Carbonremains the explicit mutable implementation.This keeps configuration where it is useful without allowing application configuration to change the mutability of framework-owned state.
The monorepo and every split package that directly depends on Carbon now require
nesbot/carbon:^3.13.1. This matches the version used for the framework-wide audit instead of claiming support for an older, untested Carbon patch.Date factory
DateFactorynow defaults toHypervel\Support\CarbonImmutable.Class handlers must be concrete, instantiable
CarbonInterfaceimplementations. This matches the return contract already exposed by helpers and the Date facade. Callable and CarbonFactoryhandlers remain available for deliberate transformations.Applications that need mutable factory output can opt out during boot:
The factory no longer accepts
DateTime::classor arbitrary classes with a compatible-looking method. Those handlers could not satisfy the framework's typed date contract.The generated Date facade, global helpers, Stringable conversion, public metadata, and test-time APIs now describe the configurable interface while asserting the exact immutable default.
Framework integration
The change audits date construction and mutation across the entire components repository.
Eloquent
dateanddatetimecasts now follow the configured Date factory and return the Hypervel immutable class by default. Explicit immutable casts always returnHypervel\Support\CarbonImmutable, including when an application opts the general factory into mutable dates.Request casts and Eloquent formatted-date parsing now let Carbon's creator own format validation. This handles PHP format modifiers that Carbon's preflight helpers do not model completely, performs one parse on the normal path, and falls back to general parsing only after an actual format mismatch.
Data objects support all meaningful declared targets:
DateTimeInterface;CarbonInterface;Interface targets follow Date factory configuration. Concrete targets remain exact. Serialization is unified through
DateTimeInterface.Foundation lifecycle timestamps capture immutable timezone conversions and retain the converted value in their owning property or coroutine context. Scheduler minute boundaries no longer mutate the start time used by mutex decisions. Validation, queued mail, and queued notifications accept
DateTimeInterfacewherever immutable values are valid.Authentication, cache, bus, queue, Horizon, sessions, routing, Telescope, collections, testing, and the remaining framework date consumers now use immutable held values and retain modifier results explicitly.
The existing PSR clock binding remains the only clock service. It now constructs the canonical Hypervel immutable class and remains independent from an application's mutable Date factory opt-out.
Carbon's process-global test state has one cleanup owner. Test clocks, macros, serialization callbacks, string formats, and strict mode are restored between tests without maintaining parallel mutable and immutable reset paths.
Protobuf initialization
Verification exposed a separate worker-lifecycle issue in generated protobuf metadata.
Generated descriptor registration is process-global and is not safe when multiple coroutines initialize the same message class concurrently for the first time.
GrpcRouter::compileAndWarm()now constructs every registered request message and every statically named concrete response message that can be created without required arguments before workers fork.This fixes the registration race and moves descriptor initialization off the request path. Response types hidden behind unions, iterables, or untyped handlers, and application client messages, cannot be discovered by the server router; the gRPC documentation explains that those message classes should be initialized during application boot.
Performance
The implementation does not add a clock abstraction, converter registry, runtime lock, per-instance wrapper, or raw timestamp layer.
Immutable modifier allocation is the mechanism that changes. Construction and factory dispatch remain effectively neutral, and profiling showed that the request-level effect is negligible for normal framework paths. Framework subsystems that already used immutable dates retain the same allocation model.
Internal fixed-type paths construct
CarbonImmutabledirectly. Configurable paths continue through the existing Date facade. Formatted-date parsing removes preflight work from the matching path. Protobuf descriptor work moves from concurrent request execution to pre-fork boot.The result keeps Carbon's API and predictable performance characteristics without introducing a second time system.
Migration
This intentionally changes the framework default.
Applications should:
CarbonInterfaceorCarbonImmutable;CarbonInterface;Date::use(Carbon::class)during boot only when mutable factory output is deliberate.The upgrade guide, release notes, framework documentation, Laravel differences, contributor conventions, and implementation history have been updated together.
Verification
The complete formatter, static analysis, parallel component suite, Testbench contract suite, and dogfood suite pass.
Focused coverage exercises:
The generated Date facade converges with formatting and passes its dedicated lint check. The final repository-wide structural sweep found no stale mutable-default contracts or discarded modifier results in the migrated paths.