Skip to content

Commit c66a28f

Browse files
authored
feat: Add preliminary support for enums (#201)
1 parent 1598925 commit c66a28f

File tree

17 files changed

+781
-13
lines changed

17 files changed

+781
-13
lines changed

Cargo.lock

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phper-build/src/lib.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,36 @@ pub fn register_all() {
2424
pub fn register_configures() {
2525
// versions
2626
println!(
27-
"cargo:rustc-cfg=phper_major_version=\"{}\"",
27+
"cargo::rustc-cfg=phper_major_version=\"{}\"",
2828
PHP_MAJOR_VERSION
2929
);
3030
println!(
31-
"cargo:rustc-cfg=phper_minor_version=\"{}\"",
31+
"cargo::rustc-cfg=phper_minor_version=\"{}\"",
3232
PHP_MINOR_VERSION
3333
);
3434
println!(
35-
"cargo:rustc-cfg=phper_release_version=\"{}\"",
35+
"cargo::rustc-cfg=phper_release_version=\"{}\"",
3636
PHP_RELEASE_VERSION
3737
);
3838

3939
if PHP_DEBUG != 0 {
40-
println!("cargo:rustc-cfg=phper_debug");
40+
println!("cargo::rustc-cfg=phper_debug");
4141
}
4242

4343
if USING_ZTS != 0 {
44-
println!("cargo:rustc-cfg=phper_zts");
44+
println!("cargo::rustc-cfg=phper_zts");
45+
}
46+
47+
if PHP_VERSION_ID >= 80100 {
48+
println!("cargo::rustc-cfg=phper_enum_supported");
4549
}
4650
}
4751

4852
/// Register link arguments for os-specified situation.
4953
pub fn register_link_args() {
5054
#[cfg(target_os = "macos")]
5155
{
52-
println!("cargo:rustc-link-arg=-undefined");
53-
println!("cargo:rustc-link-arg=dynamic_lookup");
56+
println!("cargo::rustc-link-arg=-undefined");
57+
println!("cargo::rustc-link-arg=dynamic_lookup");
5458
}
5559
}

phper-doc/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,11 @@ phper = { workspace = true }
2525
[dev-dependencies]
2626
thiserror = "2.0.11"
2727
reqwest = { version = "0.12.12", features = ["blocking", "cookies"] }
28+
29+
[build-dependencies]
30+
phper-build = { workspace = true }
31+
32+
[lints.rust]
33+
unexpected_cfgs = { level = "warn", check-cfg = [
34+
'cfg(phper_enum_supported)',
35+
] }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
# Register Enums
2+
3+
> PHP 8.1 and above introduced the Enum functionality. `PHPER` allowing you to create PHP enums using Rust code.
4+
5+
In `PHPER`, you can use the [`add_enum`](phper::modules::Module::add_enum) method to register enums.
6+
According to PHP's enum specification, `PHPER` supports three types of enums:
7+
8+
1. Pure enums (without values)
9+
2. Integer-backed enums
10+
3. String-backed enums
11+
12+
## Creating Pure Enums
13+
14+
Pure enums are the simplest type of enum, having only member names without associated values. Use `EnumEntity<()>` to create a pure enum (or simply use `EnumEntity::new()` since `()` is the default type parameter).
15+
16+
```rust,no_run
17+
use phper::{modules::Module, php_get_module, enums::EnumEntity, classes::Visibility};
18+
19+
#[php_get_module]
20+
pub fn get_module() -> Module {
21+
let mut module = Module::new(
22+
env!("CARGO_CRATE_NAME"),
23+
env!("CARGO_PKG_VERSION"),
24+
env!("CARGO_PKG_AUTHORS"),
25+
);
26+
27+
// Create a pure enum
28+
let mut status = EnumEntity::new("Status");
29+
30+
// Add enum cases (without values)
31+
status.add_case("PENDING", ());
32+
status.add_case("ACTIVE", ());
33+
status.add_case("INACTIVE", ());
34+
35+
// Register the enum to the module
36+
module.add_enum(status);
37+
38+
module
39+
}
40+
```
41+
42+
This is equivalent to the following PHP code:
43+
44+
```php
45+
enum Status {
46+
case PENDING;
47+
case ACTIVE;
48+
case INACTIVE;
49+
}
50+
```
51+
52+
## Creating Integer-Backed Enums
53+
54+
Integer-backed enums associate each enum member with an integer value. Use `EnumEntity<i64>` to create an integer-backed enum.
55+
56+
```rust,no_run
57+
use phper::{modules::Module, php_get_module, enums::EnumEntity};
58+
59+
#[php_get_module]
60+
pub fn get_module() -> Module {
61+
let mut module = Module::new(
62+
env!("CARGO_CRATE_NAME"),
63+
env!("CARGO_PKG_VERSION"),
64+
env!("CARGO_PKG_AUTHORS"),
65+
);
66+
67+
// Create an integer-backed enum
68+
let mut level = EnumEntity::<i64>::new("Level");
69+
70+
// Add enum cases with their associated integer values
71+
level.add_case("LOW", 1);
72+
level.add_case("MEDIUM", 5);
73+
level.add_case("HIGH", 10);
74+
75+
// Register the enum to the module
76+
module.add_enum(level);
77+
78+
module
79+
}
80+
```
81+
82+
This is equivalent to the following PHP code:
83+
84+
```php
85+
enum Level: int {
86+
case LOW = 1;
87+
case MEDIUM = 5;
88+
case HIGH = 10;
89+
}
90+
```
91+
92+
## Creating String-Backed Enums
93+
94+
String-backed enums associate each enum member with a string value. Use `EnumEntity<String>` to create a string-backed enum.
95+
96+
```rust,no_run
97+
use phper::{modules::Module, php_get_module, enums::EnumEntity};
98+
99+
#[php_get_module]
100+
pub fn get_module() -> Module {
101+
let mut module = Module::new(
102+
env!("CARGO_CRATE_NAME"),
103+
env!("CARGO_PKG_VERSION"),
104+
env!("CARGO_PKG_AUTHORS"),
105+
);
106+
107+
// Create a string-backed enum
108+
let mut color = EnumEntity::<String>::new("Color");
109+
110+
// Add enum cases with their associated string values
111+
color.add_case("RED", "FF0000".to_string());
112+
color.add_case("GREEN", "00FF00".to_string());
113+
color.add_case("BLUE", "0000FF".to_string());
114+
115+
// Register the enum to the module
116+
module.add_enum(color);
117+
118+
module
119+
}
120+
```
121+
122+
This is equivalent to the following PHP code:
123+
124+
```php
125+
enum Color: string {
126+
case RED = "FF0000";
127+
case GREEN = "00FF00";
128+
case BLUE = "0000FF";
129+
}
130+
```
131+
132+
## Adding Constants
133+
134+
Enums can contain constants. Use the `add_constant` method to add constants to an enum.
135+
136+
```rust,no_run
137+
use phper::{modules::Module, php_get_module, enums::EnumEntity};
138+
139+
let mut status = EnumEntity::new("Status");
140+
141+
// Add enum cases
142+
status.add_case("PENDING", ());
143+
status.add_case("ACTIVE", ());
144+
145+
// Add constants
146+
status.add_constant("VERSION", "1.0.0");
147+
status.add_constant("MAX_ATTEMPTS", 3);
148+
```
149+
150+
This is equivalent to the following PHP code:
151+
152+
```php
153+
enum Status {
154+
case PENDING;
155+
case ACTIVE;
156+
157+
public const VERSION = "1.0.0";
158+
public const MAX_ATTEMPTS = 3;
159+
}
160+
```
161+
162+
## Adding Static Methods
163+
164+
You can add static methods to enums. Use the `add_static_method` method to add a static method to an enum.
165+
166+
```rust,no_run
167+
use phper::{modules::Module, php_get_module, enums::EnumEntity, classes::Visibility};
168+
use std::convert::Infallible;
169+
170+
let mut status = EnumEntity::new("Status");
171+
172+
// Add enum cases
173+
status.add_case("PENDING", ());
174+
status.add_case("ACTIVE", ());
175+
176+
// Add static method
177+
status.add_static_method("getDescription", Visibility::Public, |_| {
178+
Ok::<_, Infallible>("Status enumeration for tracking item states")
179+
});
180+
```
181+
182+
This is equivalent to the following PHP code:
183+
184+
```php
185+
enum Status {
186+
case PENDING;
187+
case ACTIVE;
188+
189+
public static function getDescription(): string {
190+
return "Status enumeration for tracking item states";
191+
}
192+
}
193+
```
194+
195+
## Implementing Interfaces
196+
197+
You can make enums implement interfaces. Use the `implements` method to make an enum implement a specific interface.
198+
199+
```rust,no_run
200+
use phper::{modules::Module, php_get_module, enums::EnumEntity, classes::Interface};
201+
202+
let mut color = EnumEntity::<String>::new("Color");
203+
204+
// Add enum cases
205+
color.add_case("RED", "FF0000".to_string());
206+
color.add_case("GREEN", "00FF00".to_string());
207+
208+
// Implement interface
209+
color.implements(Interface::from_name("JsonSerializable"));
210+
211+
// Note: You need to add necessary methods to satisfy interface requirements
212+
// For example, JsonSerializable interface requires the implementation of jsonSerialize method
213+
```
214+
215+
## Using Built-in Enum Methods
216+
217+
PHP enums come with some built-in methods. Pure enums (`UnitEnum`) have the `cases()` method, while backed enums (`BackedEnum`) additionally have the `from()` and `tryFrom()` methods.
218+
219+
```php
220+
// Examples of using built-in methods in PHP
221+
$allCases = Color::cases(); // Returns an array of all enum cases
222+
223+
// Only available for backed enums
224+
$colorFromValue = Color::from("FF0000"); // Returns RED
225+
$colorOrNull = Color::tryFrom("INVALID"); // Returns null (when the value doesn't exist)
226+
```
227+
228+
## Complete Example
229+
230+
Here's a comprehensive example using both pure and backed enums:
231+
232+
```rust,no_run
233+
use phper::{
234+
modules::Module,
235+
php_get_module,
236+
enums::EnumEntity,
237+
classes::Visibility
238+
};
239+
use std::convert::Infallible;
240+
241+
#[php_get_module]
242+
pub fn get_module() -> Module {
243+
let mut module = Module::new(
244+
env!("CARGO_CRATE_NAME"),
245+
env!("CARGO_PKG_VERSION"),
246+
env!("CARGO_PKG_AUTHORS"),
247+
);
248+
249+
// Pure enum
250+
let mut status = EnumEntity::new("Status");
251+
status.add_case("PENDING", ());
252+
status.add_case("ACTIVE", ());
253+
status.add_case("INACTIVE", ());
254+
status.add_constant("VERSION", "1.0.0");
255+
status.add_static_method("getDescription", Visibility::Public, |_| {
256+
Ok::<_, Infallible>("Status enumeration")
257+
});
258+
259+
// Integer-backed enum
260+
let mut level = EnumEntity::<i64>::new("Level");
261+
level.add_case("LOW", 1);
262+
level.add_case("MEDIUM", 5);
263+
level.add_case("HIGH", 10);
264+
265+
// Register enums to the module
266+
module.add_enum(status);
267+
module.add_enum(level);
268+
269+
module
270+
}
271+
```
272+
273+
> **Note**: PHP enums require PHP 8.1 or higher. Make sure your extension sets the correct PHP version requirements.

phper-doc/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ pub mod _06_module {
6969

7070
#[doc = include_str!("../doc/_06_module/_07_register_interface/index.md")]
7171
pub mod _07_register_interface {}
72+
73+
#[cfg(phper_enum_supported)]
74+
#[doc = include_str!("../doc/_06_module/_08_register_enum/index.md")]
75+
pub mod _08_register_enum {}
7276
}
7377

7478
/// TODO

phper-sys/php_wrapper.c

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#include <zend_observer.h>
2323
#endif
2424

25+
#if PHP_VERSION_ID >= 80100
26+
#include <zend_enum.h>
27+
#endif
28+
2529
typedef ZEND_INI_MH(phper_zend_ini_mh);
2630

2731
typedef zend_class_entry *

0 commit comments

Comments
 (0)