@@ -39,6 +39,10 @@ extern crate proc_macro;
3939/// struct name is kept the same. If no name is given, the name of the struct
4040/// is used. Useful for namespacing classes.
4141/// - `change_case` - Changes the case of the class name when exported to PHP.
42+ /// - `readonly` - Marks the class as readonly (PHP 8.2+). All properties in a
43+ /// readonly class are implicitly readonly.
44+ /// - `flags` - Sets class flags using `ClassFlags`, e.g. `#[php(flags =
45+ /// ClassFlags::Final)]` for a final class.
4246/// - `#[php(extends(ce = ce_fn, stub = "ParentClass"))]` - Sets the parent
4347/// class of the class. Can only be used once. `ce_fn` must be a function with
4448/// the signature `fn() -> &'static ClassEntry`.
@@ -274,6 +278,118 @@ extern crate proc_macro;
274278/// echo Counter::$count; // 2
275279/// echo Counter::getCount(); // 2
276280/// ```
281+ ///
282+ /// ## Readonly Classes (PHP 8.2+)
283+ ///
284+ /// PHP 8.2 introduced [readonly classes](https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.readonly),
285+ /// where all properties are implicitly readonly. You can create a readonly
286+ /// class using the `#[php(readonly)]` attribute:
287+ ///
288+ /// ```rust,ignore
289+ /// # #![cfg_attr(windows, feature(abi_vectorcall))]
290+ /// # extern crate ext_php_rs;
291+ /// use ext_php_rs::prelude::*;
292+ ///
293+ /// #[php_class]
294+ /// #[php(readonly)]
295+ /// pub struct ImmutablePoint {
296+ /// x: f64,
297+ /// y: f64,
298+ /// }
299+ ///
300+ /// #[php_impl]
301+ /// impl ImmutablePoint {
302+ /// pub fn __construct(x: f64, y: f64) -> Self {
303+ /// Self { x, y }
304+ /// }
305+ ///
306+ /// pub fn get_x(&self) -> f64 {
307+ /// self.x
308+ /// }
309+ ///
310+ /// pub fn get_y(&self) -> f64 {
311+ /// self.y
312+ /// }
313+ ///
314+ /// pub fn distance_from_origin(&self) -> f64 {
315+ /// (self.x * self.x + self.y * self.y).sqrt()
316+ /// }
317+ /// }
318+ ///
319+ /// #[php_module]
320+ /// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
321+ /// module.class::<ImmutablePoint>()
322+ /// }
323+ /// # fn main() {}
324+ /// ```
325+ ///
326+ /// From PHP:
327+ ///
328+ /// ```php
329+ /// $point = new ImmutablePoint(3.0, 4.0);
330+ /// echo $point->getX(); // 3.0
331+ /// echo $point->getY(); // 4.0
332+ /// echo $point->distanceFromOrigin(); // 5.0
333+ ///
334+ /// // On PHP 8.2+, you can verify the class is readonly:
335+ /// $reflection = new ReflectionClass(ImmutablePoint::class);
336+ /// var_dump($reflection->isReadOnly()); // true
337+ /// ```
338+ ///
339+ /// The `readonly` attribute is compatible with other class attributes:
340+ ///
341+ /// ```rust,ignore
342+ /// # #![cfg_attr(windows, feature(abi_vectorcall))]
343+ /// # extern crate ext_php_rs;
344+ /// use ext_php_rs::prelude::*;
345+ /// use ext_php_rs::flags::ClassFlags;
346+ ///
347+ /// // Readonly + Final class
348+ /// #[php_class]
349+ /// #[php(readonly)]
350+ /// #[php(flags = ClassFlags::Final)]
351+ /// pub struct FinalImmutableData {
352+ /// value: String,
353+ /// }
354+ /// # fn main() {}
355+ /// ```
356+ ///
357+ /// **Note:** The `readonly` attribute requires PHP 8.2 or later. Using it when
358+ /// compiling against an earlier PHP version will result in a compile error.
359+ ///
360+ /// ### Conditional Compilation for Multi-Version Support
361+ ///
362+ /// If your extension needs to support both PHP 8.1 and PHP 8.2+, you can use
363+ /// conditional compilation to only enable readonly on supported versions.
364+ ///
365+ /// First, add `ext-php-rs` as a build dependency in your `Cargo.toml`:
366+ ///
367+ /// ```toml
368+ /// [build-dependencies]
369+ /// ext-php-rs = "0.15"
370+ /// ```
371+ ///
372+ /// Then create a `build.rs` that registers the PHP version cfg flags:
373+ ///
374+ /// ```rust,ignore
375+ /// fn main() {
376+ /// ext_php_rs::build::register_php_cfg_flags();
377+ /// }
378+ /// ```
379+ ///
380+ /// Now you can use `#[cfg(php82)]` to conditionally apply the readonly
381+ /// attribute:
382+ ///
383+ /// ```rust,ignore
384+ /// #[php_class]
385+ /// #[cfg_attr(php82, php(readonly))]
386+ /// pub struct MaybeReadonlyClass {
387+ /// value: String,
388+ /// }
389+ /// ```
390+ ///
391+ /// This is **optional** - if your extension only targets PHP 8.2+, you can use
392+ /// `#[php(readonly)]` directly without any build script setup.
277393// END DOCS FROM classes.md
278394#[ proc_macro_attribute]
279395pub fn php_class ( args : TokenStream , input : TokenStream ) -> TokenStream {
0 commit comments