Skip to content

Commit 1f88d5c

Browse files
authored
test and document co-dependent classes (#194)
1 parent 944a88a commit 1f88d5c

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class Foo {}
3838

3939
## Extends & implements
4040

41-
To allow a class to extend another, you can use `ClassEntity.extends(StateClass<T>)` for classes implemented
42-
in your module. A StateClass can either be obtained by registering your own class against the module, or
41+
To allow a class to extend another, you can use `ClassEntity.extends(StateClass<T>)`.
42+
A StateClass can either be obtained by registering your own class against the module, or
4343
by looking up the class by name (for example, for PHP built-in classes like `Exception`).
4444

4545
If you want class `Foo` extends `Bar`, and implements interface `Stringable`:
@@ -195,3 +195,51 @@ class MyHashMap {
195195

196196
}
197197
```
198+
199+
## Circular class dependencies
200+
If you wish to register classes which depend on each other, you can retrieve the bound class (`StateClass<T>`)
201+
from a `ClassEntity`, and use it in functions and methods:
202+
203+
```rust,no_run
204+
use phper::{
205+
classes::{ClassEntity, StateClass, Visibility},
206+
modules::Module,
207+
};
208+
209+
let mut module = Module::new(
210+
env!("CARGO_CRATE_NAME"),
211+
env!("CARGO_PKG_VERSION"),
212+
env!("CARGO_PKG_AUTHORS"),
213+
);
214+
215+
let mut a_cls = ClassEntity::new("A");
216+
let mut b_cls = ClassEntity::new("B");
217+
let a_bound_class = a_cls.bound_class();
218+
let b_bound_class = b_cls.bound_class();
219+
220+
a_cls.add_static_method("createB", Visibility::Public, move |_| {
221+
let object = b_bound_class.init_object()?;
222+
Ok::<_, phper::Error>(object)
223+
});
224+
b_cls.add_static_method("createA", Visibility::Public, move |_| {
225+
let object = a_bound_class.init_object()?;
226+
Ok::<_, phper::Error>(object)
227+
});
228+
229+
module.add_class(a_cls);
230+
module.add_class(b_cls);
231+
```
232+
233+
This is equivalent to the following PHP code:
234+
```php
235+
class A {
236+
public static function createB(): B {
237+
return new B();
238+
}
239+
}
240+
class B {
241+
public static function createA(): A {
242+
return new A();
243+
}
244+
}
245+
```

tests/integration/src/classes.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn integrate(module: &mut Module) {
2525
integrate_static_props(module);
2626
integrate_i_constants(module);
2727
integrate_bar_extends_foo(module, foo_class);
28+
integrate_dependent_classes(module);
2829
#[cfg(phper_major_version = "8")]
2930
integrate_stringable(module);
3031
}
@@ -229,6 +230,35 @@ fn integrate_bar_extends_foo(module: &mut Module, foo_class: StateClass<Foo>) {
229230
module.add_class(cls);
230231
}
231232

233+
fn integrate_dependent_classes(module: &mut Module) {
234+
const A_CLS: &str = r"IntegrationTest\Dependency\A";
235+
const B_CLS: &str = r"IntegrationTest\Dependency\B";
236+
// Build both class entities
237+
let mut a_cls = ClassEntity::new(A_CLS);
238+
let mut b_cls = ClassEntity::new(B_CLS);
239+
240+
let a_bound_class = a_cls.bound_class();
241+
let b_bound_class = b_cls.bound_class();
242+
243+
a_cls
244+
.add_static_method("createB", Visibility::Public, move |_| {
245+
let object = b_bound_class.init_object()?;
246+
Ok::<_, phper::Error>(object)
247+
})
248+
.return_type(ReturnType::new(ReturnTypeHint::ClassEntry(B_CLS.into())));
249+
250+
b_cls
251+
.add_static_method("createA", Visibility::Public, move |_| {
252+
let object = a_bound_class.init_object()?;
253+
Ok::<_, phper::Error>(object)
254+
})
255+
.return_type(ReturnType::new(ReturnTypeHint::ClassEntry(A_CLS.into())));
256+
257+
// Register both classes
258+
module.add_class(a_cls);
259+
module.add_class(b_cls);
260+
}
261+
232262
#[cfg(phper_major_version = "8")]
233263
fn integrate_stringable(module: &mut Module) {
234264
use phper::{functions::ReturnType, types::ReturnTypeHint};

tests/integration/tests/php/classes.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,9 @@ class Foo2 extends IntegrationTest\Foo {}
105105
$bar = new \IntegrationTest\BarExtendsFoo; //Bar should extend Foo
106106
$reflection = new ReflectionClass($bar);
107107
assert_true($reflection->isSubclassOf(IntegrationTest\Foo::class));
108+
109+
// Test co-dependent classes
110+
$b = IntegrationTest\Dependency\A::createB();
111+
assert_true($b instanceof IntegrationTest\Dependency\B);
112+
$a = IntegrationTest\Dependency\B::createA();
113+
assert_true($a instanceof IntegrationTest\Dependency\A);

0 commit comments

Comments
 (0)