@@ -78,4 +78,136 @@ pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
7878# fn main() {}
7979```
8080
81+ ## Lazy Objects (PHP 8.4+)
82+
83+ PHP 8.4 introduced lazy objects, which defer their initialization until their
84+ properties are first accessed. ext-php-rs provides APIs to introspect and create
85+ lazy objects from Rust.
86+
87+ ### Lazy Object Types
88+
89+ There are two types of lazy objects:
90+
91+ - ** Lazy Ghosts** : The ghost object itself becomes the real instance when
92+ initialized. After initialization, the ghost is indistinguishable from a
93+ regular object.
94+
95+ - ** Lazy Proxies** : A proxy wraps a real instance that is created when first
96+ accessed. The proxy and real instance have different identities. After
97+ initialization, the proxy still reports as lazy.
98+
99+ ### Introspection APIs
100+
101+ ``` rust,no_run
102+ # #![cfg_attr(windows, feature(abi_vectorcall))]
103+ # extern crate ext_php_rs;
104+ use ext_php_rs::{prelude::*, types::ZendObject};
105+
106+ #[php_function]
107+ pub fn check_lazy(obj: &ZendObject) -> String {
108+ if obj.is_lazy() {
109+ if obj.is_lazy_ghost() {
110+ if obj.is_lazy_initialized() {
111+ "Initialized lazy ghost".into()
112+ } else {
113+ "Uninitialized lazy ghost".into()
114+ }
115+ } else if obj.is_lazy_proxy() {
116+ if obj.is_lazy_initialized() {
117+ "Initialized lazy proxy".into()
118+ } else {
119+ "Uninitialized lazy proxy".into()
120+ }
121+ } else {
122+ "Unknown lazy type".into()
123+ }
124+ } else {
125+ "Not a lazy object".into()
126+ }
127+ }
128+ # fn main() {}
129+ ```
130+
131+ Available introspection methods:
132+
133+ | Method | Description |
134+ | --------| -------------|
135+ | ` is_lazy() ` | Returns ` true ` if the object is lazy (ghost or proxy) |
136+ | ` is_lazy_ghost() ` | Returns ` true ` if the object is a lazy ghost |
137+ | ` is_lazy_proxy() ` | Returns ` true ` if the object is a lazy proxy |
138+ | ` is_lazy_initialized() ` | Returns ` true ` if the lazy object has been initialized |
139+ | ` lazy_init() ` | Triggers initialization of a lazy object |
140+ | ` lazy_get_instance() ` | For proxies, returns the real instance after initialization |
141+
142+ ### Creating Lazy Objects from Rust
143+
144+ You can create lazy objects from Rust using ` make_lazy_ghost() ` and
145+ ` make_lazy_proxy() ` . These methods require the ` closure ` feature:
146+
147+ ``` toml
148+ [dependencies ]
149+ ext-php-rs = { version = " 0.15" , features = [" closure" ] }
150+ ```
151+
152+ ``` rust,no_run
153+ # #![cfg_attr(windows, feature(abi_vectorcall))]
154+ # extern crate ext_php_rs;
155+ use ext_php_rs::{prelude::*, types::ZendObject, boxed::ZBox};
156+
157+ #[php_function]
158+ pub fn create_lazy_ghost(obj: &mut ZendObject) -> PhpResult<()> {
159+ let init_value = "initialized".to_string();
160+ obj.make_lazy_ghost(Box::new(move || {
161+ // Initialization logic - use captured state
162+ println!("Initializing with: {}", init_value);
163+ }) as Box<dyn Fn()>)?;
164+ Ok(())
165+ }
166+
167+ #[php_function]
168+ pub fn create_lazy_proxy(obj: &mut ZendObject) -> PhpResult<()> {
169+ obj.make_lazy_proxy(Box::new(|| {
170+ // Return the real instance
171+ Some(ZendObject::new_stdclass())
172+ }) as Box<dyn Fn() -> Option<ZBox<ZendObject>>>)?;
173+ Ok(())
174+ }
175+ # fn main() {}
176+ ```
177+
178+ ### Creating Lazy Objects from PHP
179+
180+ For full control over lazy object creation, use PHP's Reflection API:
181+
182+ ``` php
183+ <?php
184+ // Create a lazy ghost
185+ $reflector = new ReflectionClass(MyClass::class);
186+ $ghost = $reflector->newLazyGhost(function ($obj) {
187+ $obj->__construct('initialized');
188+ });
189+
190+ // Create a lazy proxy
191+ $proxy = $reflector->newLazyProxy(function ($obj) {
192+ return new MyClass('initialized');
193+ });
194+ ```
195+
196+ ### Limitations
197+
198+ - ** PHP 8.4+ only** : Lazy objects are a PHP 8.4 feature and not available in
199+ earlier versions.
200+
201+ - ** ` closure ` feature required** : The ` make_lazy_ghost() ` and ` make_lazy_proxy() `
202+ methods require the ` closure ` feature to be enabled.
203+
204+ - ** User-defined classes only** : PHP lazy objects only work with user-defined
205+ PHP classes, not internal classes. Since Rust-defined classes (using
206+ ` #[php_class] ` ) are registered as internal classes, they cannot be made lazy.
207+
208+ - ** Closure parameter access** : Due to Rust trait system limitations, the
209+ ` make_lazy_ghost() ` and ` make_lazy_proxy() ` closures don't receive the object
210+ being initialized as a parameter. Capture any needed initialization state in
211+ the closure itself.
212+
81213[ class object ] : ./class_object.md
0 commit comments