Skip to content

Commit ee4fd40

Browse files
committed
extends StateClass
1 parent 40c3503 commit ee4fd40

File tree

6 files changed

+63
-21
lines changed

6 files changed

+63
-21
lines changed

examples/http-client/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const EXCEPTION_CLASS_NAME: &str = "HttpClient\\HttpClientException";
1919
pub fn make_exception_class() -> ClassEntity<()> {
2020
let mut class = ClassEntity::new(EXCEPTION_CLASS_NAME);
2121
// The `extends` is same as the PHP class `extends`.
22-
class.extends("Exception");
22+
class.extends_name("Exception");
2323
class
2424
}
2525

examples/http-server/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ impl From<HttpServerError> for phper::Error {
4343
pub fn make_exception_class() -> ClassEntity<()> {
4444
let mut class = ClassEntity::new(EXCEPTION_CLASS_NAME);
4545
// As an Exception class, inheriting from the base Exception class is important.
46-
class.extends("Exception");
46+
class.extends_name("Exception");
4747
class
4848
}

phper-doc/doc/_02_quick_start/_02_write_a_simple_http_client/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Now let's begin to finish the logic.
101101
pub fn make_exception_class() -> ClassEntity<()> {
102102
let mut class = ClassEntity::new(EXCEPTION_CLASS_NAME);
103103
// The `extends` is same as the PHP class `extends`.
104-
class.extends("Exception");
104+
class.extends_name("Exception");
105105
class
106106
}
107107

@@ -155,7 +155,7 @@ Now let's begin to finish the logic.
155155
# pub fn make_exception_class() -> ClassEntity<()> {
156156
# let mut class = ClassEntity::new(EXCEPTION_CLASS_NAME);
157157
# // The `extends` is same as the PHP class `extends`.
158-
# class.extends("Exception");
158+
# class.extends_name("Exception");
159159
# class
160160
# }
161161
#

phper-doc/doc/_06_module/_06_register_class/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,17 @@ class Foo {}
3838

3939
## Extends & implements
4040

41+
To allow a class to extend another, you can either use `ClassEntity.extends(StateClass<T>)` for classes implemented
42+
in your module, or `ClassEntity.extends_name(String)` for built-ins (although, `extends_name` should actually work
43+
for any class).
44+
4145
If you want the class `Foo` extends `Bar`, and implements interface `Stringable`:
4246

4347
```rust,no_run
4448
use phper::classes::{ClassEntity, ClassEntry, Interface};
4549
4650
let mut foo = ClassEntity::new("Foo");
47-
foo.extends("Bar");
51+
foo.extends_name("Bar");
4852
foo.implements(Interface::from_name("Stringable"));
4953
```
5054

phper/src/classes.rs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ pub struct ClassEntity<T: 'static> {
427427
state_constructor: Rc<StateConstructor>,
428428
method_entities: Vec<MethodEntity>,
429429
property_entities: Vec<PropertyEntity>,
430-
parent: Option<String>,
430+
parent: Option<Box<dyn Fn() -> &'static ClassEntry>>,
431431
interfaces: Vec<Interface>,
432432
constants: Vec<ConstantEntity>,
433433
bind_class: StateClass<T>,
@@ -556,13 +556,54 @@ impl<T: 'static> ClassEntity<T> {
556556
/// # Examples
557557
///
558558
/// ```no_run
559+
/// use phper::{
560+
/// classes::{ClassEntity, ClassEntry},
561+
/// modules::Module,
562+
/// php_get_module,
563+
/// };
564+
///
565+
/// #[php_get_module]
566+
/// pub fn get_module() -> Module {
567+
/// let mut module = Module::new(
568+
/// env!("CARGO_CRATE_NAME"),
569+
/// env!("CARGO_PKG_VERSION"),
570+
/// env!("CARGO_PKG_AUTHORS"),
571+
/// );
572+
///
573+
/// let foo = module.add_class(ClassEntity::new("Foo"));
574+
/// let mut bar = ClassEntity::new("Bar");
575+
/// bar.extends(&foo);
576+
///
577+
/// module
578+
/// }
579+
/// ```
580+
pub fn extends<U: 'static>(&mut self, parent: &StateClass<U>) {
581+
let parent = parent.clone();
582+
self.parent = Some(Box::new(move || {
583+
let entry: &'static ClassEntry =
584+
unsafe { std::mem::transmute(parent.as_class_entry()) };
585+
entry
586+
}));
587+
}
588+
589+
/// Register class to `extends` the parent class, by name. Similar to `extends`, this is
590+
/// useful for built-ins.
591+
///
592+
/// # Examples
593+
///
594+
/// ```no_run
559595
/// use phper::classes::{ClassEntity, ClassEntry};
560596
///
561597
/// let mut class = ClassEntity::new("MyException");
562-
/// class.extends("Exception");
598+
/// class.extends_name("Exception");
563599
/// ```
564-
pub fn extends(&mut self, parent_name: impl Into<String>) {
565-
self.parent = Some(parent_name.into());
600+
pub fn extends_name(&mut self, name: impl Into<String>) {
601+
let name = name.into();
602+
self.parent = Some(Box::new(move || {
603+
ClassEntry::from_globals(&name).unwrap_or_else(|_| {
604+
panic!("Unable to resolve parent class: {}", name);
605+
})
606+
}));
566607
}
567608

568609
/// Register class to `implements` the interface, due to the class can
@@ -631,11 +672,8 @@ impl<T: 'static> ClassEntity<T> {
631672
#[allow(clippy::useless_conversion)]
632673
pub(crate) unsafe fn init(&self) -> *mut zend_class_entry {
633674
unsafe {
634-
let parent: *mut zend_class_entry = if let Some(ref name) = self.parent {
635-
let entry = ClassEntry::from_globals(name).unwrap_or_else(|err| {
636-
panic!("Unable to resolve parent class: {}: {}", name,err);
637-
});
638-
entry.as_ptr() as *mut _
675+
let parent: *mut zend_class_entry = if let Some(ref parent_fn) = self.parent {
676+
parent_fn().as_ptr() as *mut _
639677
} else {
640678
null_mut()
641679
};

tests/integration/src/classes.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use phper::{
1212
alloc::RefClone,
1313
classes::{
14-
ClassEntity, ClassEntry, Interface, InterfaceEntity, Visibility,
14+
ClassEntity, ClassEntry, Interface, InterfaceEntity, StateClass, Visibility,
1515
},
1616
functions::{Argument, ReturnType},
1717
modules::Module,
@@ -22,11 +22,11 @@ use std::{collections::HashMap, convert::Infallible};
2222

2323
pub fn integrate(module: &mut Module) {
2424
integrate_a(module);
25-
integrate_foo(module);
25+
let foo = integrate_foo(module);
2626
integrate_i_bar(module);
2727
integrate_static_props(module);
2828
integrate_i_constants(module);
29-
integrate_bar_extends_foo(module);
29+
integrate_bar_extends_foo(module, &foo);
3030
#[cfg(phper_major_version = "8")]
3131
integrate_stringable(module);
3232
}
@@ -78,7 +78,7 @@ struct Foo {
7878
array: HashMap<i64, ZVal>,
7979
}
8080

81-
fn integrate_foo(module: &mut Module) {
81+
fn integrate_foo(module: &mut Module) -> StateClass<Foo> {
8282
let mut class = ClassEntity::new_with_state_constructor("IntegrationTest\\Foo", || Foo {
8383
position: 0,
8484
array: Default::default(),
@@ -169,7 +169,7 @@ fn integrate_foo(module: &mut Module) {
169169
.argument(Argument::new("offset").with_type_hint(ArgumentTypeHint::Mixed))
170170
.return_type(ReturnType::new(ReturnTypeHint::Void));
171171

172-
module.add_class(class);
172+
module.add_class(class)
173173
}
174174

175175
fn integrate_i_bar(module: &mut Module) {
@@ -224,9 +224,9 @@ fn integrate_static_props(module: &mut Module) {
224224
module.add_class(class);
225225
}
226226

227-
fn integrate_bar_extends_foo(module: &mut Module) {
227+
fn integrate_bar_extends_foo(module: &mut Module, foo: &StateClass<Foo>) {
228228
let mut cls = ClassEntity::new(r"IntegrationTest\BarExtendsFoo");
229-
cls.extends(r"IntegrationTest\Foo");
229+
cls.extends(foo);
230230
cls.add_method("test", Visibility::Public, |_,_| {
231231
phper::ok(())
232232
});

0 commit comments

Comments
 (0)