#[php_impl]Expand description
§#[php_impl] Attribute
You can export an entire impl block to PHP. This exports all methods as
well as constants to PHP on the class that it is implemented on. This
requires the #[php_class] macro to already be used on the underlying
struct. Trait implementations cannot be exported to PHP. Only one impl
block can be exported per class.
If you do not want a function exported to PHP, you should place it in a
separate impl block.
If you want to use async Rust, use #[php_async_impl], instead: see here
» for more info.
§Options
By default all constants are renamed to UPPER_CASE and all methods are
renamed to camelCase. This can be changed by passing the
change_method_case and change_constant_case as #[php] attributes on
the impl block. The options are:
#[php(change_method_case = "snake_case")]- Renames the method to snake case.#[php(change_constant_case = "snake_case")]- Renames the constant to snake case.
See the name and change_case section
for a list of all available cases.
§Methods
Methods basically follow the same rules as functions, so read about the
php_function macro first. The primary difference between functions and
methods is they are bounded by their class object.
Both instance and static methods benefit from the same zero-allocation fast path as standalone functions. See the Performance section for details.
Class methods can take a &self or &mut self parameter. They cannot take
a consuming self parameter. Static methods can omit this self parameter.
To access the underlying Zend object, you can take a reference to a
ZendClassObject<T> in place of the self parameter, where the parameter
must be named self_. This can also be used to return a reference to
$this.
The rest of the options are passed as separate attributes:
#[php(defaults(i = 5, b = "\"hello\""))]- Sets the default value for parameter(s). Each key must name a parameter.#[php(optional = i)]- Sets the first optional parameter. Note that this also sets the remaining parameters as optional, so each of them must be anOption<T>or have a default. Otherwise the build fails.#[php(vis = "public")],#[php(vis = "protected")]and#[php(vis = "private")]- Sets the visibility of the method.#[php(name = "method_name")]- Renames the PHP method to a different identifier, without renaming the Rust method name.#[php(final)]- Makes the method final (cannot be overridden in subclasses).#[php(abstract)]- Makes the method abstract (must be implemented by subclasses). Can only be used in abstract classes.
The #[php(defaults)] and #[php(optional)] attributes operate the same as
the equivalent function attribute parameters. A self_ parameter is the PHP
object, not a PHP parameter, so defaults and optional cannot name it. A
getter or setter method accepts neither option.
§Static Methods
Methods that do not take a &self or &mut self parameter are
automatically exported as static methods. These can be called on the class
itself without creating an instance.
#[php_impl]
impl MyClass {
// Static method - no self parameter
pub fn create_default() -> Self {
Self { /* ... */ }
}
// Instance method - takes &self
pub fn get_value(&self) -> i32 {
self.value
}
}From PHP:
$obj = MyClass::createDefault(); // Static call
$val = $obj->getValue(); // Instance call§Final Methods
Methods marked with #[php(final)] cannot be overridden in subclasses. This
is useful when you want to prevent modification of critical functionality.
use ext_php_rs::prelude::*;
#[php_class]
pub struct SecureClass;
#[php_impl]
impl SecureClass {
#[php(final)]
pub fn secure_method(&self) -> &str {
"This cannot be overridden"
}
// Final static methods are also supported
#[php(final)]
pub fn secure_static() -> i32 {
42
}
}§Abstract Methods
Methods marked with #[php(abstract)] must be implemented by subclasses.
Abstract methods can only be defined in abstract classes (classes with
ClassFlags::Abstract).
use ext_php_rs::prelude::*;
use ext_php_rs::flags::ClassFlags;
#[php_class]
#[php(flags = ClassFlags::Abstract)]
pub struct AbstractShape;
#[php_impl]
impl AbstractShape {
// Protected constructor for subclasses
#[php(vis = "protected")]
pub fn __construct() -> Self {
Self
}
// Abstract method - subclasses must implement this.
// The body is never called; use unimplemented!() as a placeholder.
#[php(abstract)]
pub fn area(&self) -> f64 {
unimplemented!()
}
// Concrete method in abstract class
pub fn describe(&self) -> String {
format!("A shape with area {}", self.area())
}
}Note: Abstract method bodies are never called - they exist only because
Rust syntax requires a body for methods in impl blocks. Use
unimplemented!() as a clear placeholder.
Note: If you try to use #[php(abstract)] on a method in a non-abstract
class, you will get a compile-time error.
Note: PHP does not support abstract static methods. If you need static behavior that can be customized by subclasses, use a regular instance method or the late static binding pattern in PHP.
§Constructors
By default, if a class does not have a constructor, it is not constructable from PHP. It can only be returned from a Rust function to PHP.
Constructors are Rust methods which can take any amount of parameters and
returns either Self or Result<Self, E>, where E: Into<PhpException>.
When the error variant of Result is encountered, it is thrown as an
exception and the class is not constructed.
Constructors are designated by either naming the method __construct or by
annotating a method with the #[php(constructor)] attribute. Note that when
using the attribute, the function is not exported to PHP like a regular
method.
Constructors cannot use the visibility or rename attributes listed above.
§Constants
Constants are defined as regular Rust impl constants. Any type that
implements IntoZval can be used as a constant. A constant accepts the
name, change_case and vis options. The default visibility is public.
#[php_class]
pub struct Limits;
#[php_impl]
impl Limits {
const MAX_USERS: i64 = 100;
#[php(vis = "protected")]
const MAX_RETRIES: i64 = 3;
#[php(vis = "private", name = "SEED")]
const RANDOM_SEED: i64 = 42;
}PHP sees public const MAX_USERS, protected const MAX_RETRIES and
private const SEED. Reflection reports the same visibility, and the
generated stubs print it.
§Property getters and setters
A class can expose a property whose value comes from Rust methods. Mark the
method that reads the value with #[php(getter)] and the method that writes
it with #[php(setter)]. The property name is the method name without its
get_ or set_ prefix, converted to camelCase. This is the same rule as
for #[php(prop)] fields: get_first_name gives the property firstName.
Properties are declared in the order in which their first
getter or setter appears in the impl block, after the #[php(prop)] field
properties of the struct.
If you want a different property name, pass a name or change_case option
to the #[php] attribute: #[php(getter, name = "first_name")] or
#[php(getter, change_case = "snake_case")].
A property does not need both methods. A property with only a getter is read-only. A property with only a setter is write-only.
One method cannot be both a getter and a setter. A property cannot have two getters or two setters. The getter and the setter of one property must have the same visibility. Each of these cases is a compile error.
A method property cannot have the same name as a #[php(prop)] field of the
struct. The two macros are expanded separately, so this is not a compile
error. The extension fails to load with this error:
property `Book::$title` is declared twiceThe PHP type of the property comes from the return type of the getter. If
the property has no getter, the type comes from the argument of the setter.
The documentation of the property comes from the getter, or from the setter
when the getter has none. Like field properties, the type must implement
IntoZval and FromZval.
§Example
Continuing on from our Human example in the structs section, we will
define a constructor, as well as getters for the properties. We will also
define a constant for the maximum age of a Human.
use ext_php_rs::{prelude::*, types::ZendClassObject};
#[php_class]
#[derive(Debug, Default)]
pub struct Human {
name: String,
age: i32,
#[php(prop)]
address: String,
}
#[php_impl]
impl Human {
const MAX_AGE: i32 = 100;
// No `#[constructor]` attribute required here - the name is `__construct`.
pub fn __construct(name: String, age: i32) -> Self {
Self {
name,
age,
address: String::new()
}
}
#[php(getter)]
pub fn get_name(&self) -> String {
self.name.to_string()
}
#[php(setter)]
pub fn set_name(&mut self, name: String) {
self.name = name;
}
#[php(getter)]
pub fn get_age(&self) -> i32 {
self.age
}
pub fn introduce(&self) {
println!("My name is {} and I am {} years old. I live at {}.", self.name, self.age, self.address);
}
pub fn get_raw_obj(self_: &mut ZendClassObject<Human>) -> &mut ZendClassObject<Human> {
dbg!(self_)
}
pub fn get_max_age() -> i32 {
Self::MAX_AGE
}
}
#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
module.class::<Human>()
}Using our newly created class in PHP:
<?php
$me = new Human('David', 20);
$me->introduce(); // My name is David and I am 20 years old.
var_dump(Human::get_max_age()); // int(100)
var_dump(Human::MAX_AGE); // int(100)