diego-ninja / granite
A lightweight zero-dependency PHP library for building immutable, serializable objects with validation capabilities.
Requires
- php: ^8.3
Requires (Dev)
- laravel/pint: ^1.24
- nesbot/carbon: ^3.10
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^11.0
- ramsey/uuid: ^4.7
- symfony/uid: ^7.1
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v2.0.0
- v1.7.0
- v1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.1
- v1.4.0
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.2
- v1.2.0
- v1.1.0
- v1.0.0
- dev-codex/fix-all-audit-findings
- dev-feature/performance-optimizations
- dev-feature/uuid_hydration
- dev-hotfix/coderabbit_review
- dev-coderabbitai/docstrings/00ed8aa
- dev-feature/peeble_immutable_dto
- dev-feature/hydration_from_plain_object
- dev-feature/comparation_capabilities
- dev-feat/support-default-enum-for-unexpected-values
- dev-feat/deserialization/support-default-case
- dev-feature/maybe_option_monads
This package is auto-updated.
Last update: 2026-09-07 09:21:20 UTC
README
A powerful, zero-dependency PHP library for building immutable, serializable objects with validation and mapping capabilities. Perfect for DTOs, Value Objects, API responses, and domain modeling.
๐ชถ Pebble - Lightweight Immutable Snapshots
Pebble is a lightweight alternative to Granite for defensive, immutable snapshots without validation overhead โ ideal for caching, Eloquent snapshots, and fast comparisons. Nested arrays, userland objects, and dates are copied so later source mutations cannot change the snapshot.
$snapshot = Pebble::from($eloquentModel); $snapshot->name; // Magic __get $snapshot['email']; // ArrayAccess $snapshot->equals($other); // O(1) fingerprint comparison
๐ Read Pebble Documentation
โจ Features
- Immutable Objects โ Read-only DTOs and Value Objects, thread-safe by design
- Flexible
from()Method โ Create from arrays, JSON, named parameters, other Granite objects, or mix them - Comprehensive Validation โ 30+ built-in rules including Carbon date validation (Age, Future, Past, BusinessDay...)
- ObjectMapper โ Convention-based property mapping between objects with custom transformations
- Smart Serialization โ Custom property names, naming conventions, hidden fields, Carbon date formats
- Object Comparison โ Deep equality with
equals(), detailed diffs withdiffers() - Performance Optimized โ Conservative fast path for simple DTOs, deep immutable WeakMap caching, direct property access
๐ Quick Start
Installation
composer require diego-ninja/granite
Basic Usage
use Ninja\Granite\Granite; use Ninja\Granite\Validation\Attributes\Required; use Ninja\Granite\Validation\Attributes\Email; use Ninja\Granite\Validation\Attributes\Min; use Ninja\Granite\Serialization\Attributes\Hidden; final readonly class User extends Granite { public function __construct( #[Required] #[Min(2)] public string $name, #[Required] #[Email] public string $email, #[Hidden] public ?string $password = null, ) {} } // Create from array $user = User::from(['name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'secret']); // Create from named parameters $user = User::from(name: 'John Doe', email: 'john@example.com'); // Immutable updates $updated = $user->with(['name' => 'Jane Doe']); // Serialization (password hidden automatically) $json = $user->json(); // {"name":"John Doe","email":"john@example.com"} $array = $user->array();
๐ Documentation
Core Concepts
- Enhanced from() Method โ Multiple invocation patterns for flexible object creation
- Validation โ Comprehensive validation system with 30+ built-in rules including Carbon
- Serialization โ Control how objects are converted to/from arrays and JSON with Carbon support
- Object Comparison โ Deep equality checks and difference detection
- ObjectMapper โ Powerful object-to-object mapping with conventions
- Pebble โ Lightweight immutable snapshots with fingerprinting
- Advanced Usage โ Patterns for complex applications
- API Reference โ Complete API documentation
Guides
- Migration Guide โ Migrate from arrays, stdClass, Doctrine, Laravel
- Troubleshooting โ Common issues and solutions
๐ Performance & Benchmarks
Granite uses a multi-layer fast path system that detects simple DTOs at class-load time and bypasses the full hydration pipeline (reflection, metadata, type conversion) entirely. For objects that qualify, the overhead vs plain PHP constructors is minimal.
Benchmark Results
Measured on PHP 8.5.10 with OPcache CLI enabled, using the median of 7 repetitions with 100,000 operations each:
| Scenario | ยตs/op | Improvement |
|---|---|---|
| Plain PHP constructor | 0.147 | control |
Granite::from(array) |
0.284 | โ |
Granite::from(JSON) |
1.019 | 6.11x faster |
Granite::from(object) |
1.000 | 5.59x faster |
Granite::from(Granite) |
0.898 | 7.50x faster |
| Array-property DTO hydration | 0.201 | 11.69x faster |
| Validated DTO hydration | 4.691 | 1.96x faster |
Cached array() |
0.040 | 22.58x faster |
Cached nested array() |
0.040 | 31.56x faster |
| General array serialization | 1.175 | 1.39x faster |
| ObjectMapper to plain object | 1.869 | 1.11x faster |
| ObjectMapper to Granite | 1.550 | 1.14x faster |
Improvements compare the same benchmark against the pre-optimization baseline. Full samples and methodology are recorded in the performance optimization plan.
How it works
Granite's performance comes from three layers of optimization:
-
Capability-specific fast paths (
ClassProfile) โ Hydration, serialization, and comparison are evaluated independently. Exact array properties can use constructor hydration while still using recursive general serialization. -
WeakMap caching โ
array()andjson()results are read before repeating graph analysis and are cached only for deeply immutable graphs. Runtime serialization configuration invalidates cached values. -
Compiled metadata โ Reflection objects, validation attributes, validators, class date providers, and property transformers are reused across operations.
Classes that need conversion or custom naming use the general pipeline, with cached metadata and early exits for scalar values.
Running the Benchmarks
php benchmarks/GraniteBench.php composer bench:objects composer bench:objects -- --json --iterations=100000 --repetitions=7
โ ๏ธ Deprecation Notice
GraniteDTO was already deprecated before v2.0.0, and GraniteVO is deprecated as of v2.0.0, in favor of the unified Granite base class. Both still extend Granite for backward compatibility but will be removed in v3.0.0.
// โ Deprecated โ use Granite instead final readonly class User extends GraniteVO { } // โ final readonly class User extends Granite { }
๐ง Requirements
- PHP 8.3+ โ Takes advantage of modern PHP features
- No dependencies โ Zero external dependencies for maximum compatibility
๐ฆ Installation
composer require diego-ninja/granite
๐ค Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
๐ License
This package is open-sourced software licensed under the MIT license.
๐ Credits
This project is developed and maintained by ๐ฅท Diego Rin in his free time.
If you find this project useful, please consider:
- โญ Starring the repository
- ๐ Reporting bugs and issues
- ๐ก Suggesting new features
- ๐ง Contributing code improvements
Made with โค๏ธ for the PHP community