Skip to content

Commit 41febbe

Browse files
authored
Merge pull request #24 from xp-forge/refactor/reflection
Migrate to new reflection library
2 parents 39bc616 + 6c6377c commit 41febbe

7 files changed

Lines changed: 57 additions & 46 deletions

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"keywords": ["module", "xp"],
88
"require" : {
99
"xp-framework/core": "^11.0 | ^10.0 | ^9.0 | ^8.0 | ^7.0",
10+
"xp-framework/reflection": "^2.0",
1011
"xp-forge/web": "^3.0 | ^2.0 | ^1.0",
1112
"xp-forge/marshalling": "^1.0 | ^0.3 | ^0.2",
1213
"xp-forge/json": "^5.0 | ^4.0 | ^3.1",

src/main/php/web/rest/Delegate.class.php

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php namespace web\rest;
22

33
use io\streams\{InputStream, Streams};
4-
use lang\IllegalArgumentException;
5-
use lang\reflect\TargetInvocationException;
4+
use lang\reflection\{Method, TargetException};
5+
use lang\{IllegalArgumentException, Reflection, Type};
66
use web\Request;
77

88
class Delegate {
@@ -31,67 +31,66 @@ static function __static() {
3131
* Creates a new delegate
3232
*
3333
* @param object $instance
34-
* @param lang.reflect.Method $method
34+
* @param string|lang.reflection.Method $method
3535
* @param string $source Default source
3636
*/
3737
public function __construct($instance, $method, $source) {
3838
$this->instance= $instance;
39-
$this->method= $method;
40-
foreach ($method->getParameters() as $param) {
39+
$this->method= $method instanceof Method ? $method : Reflection::type($instance)->method($method);
40+
foreach ($this->method->parameters() as $param) {
4141

4242
// Source explicitely set by annotation
43-
foreach ($param->getAnnotations() as $source => $name) {
44-
if (isset(self::$SOURCES[$source])) {
45-
$this->param($param, $name ?? $param->getName(), $source);
43+
foreach ($param->annotations() as $annotation) {
44+
if ($accessor= self::$SOURCES[$annotation->name()] ?? null) {
45+
$this->param($param, $name ?? $param->name(), $accessor);
4646
continue 2;
4747
}
4848
}
4949

5050
// Source derived from parameter type
51-
$type= $param->getType();
52-
if ('var' === $type->getName()) {
51+
$type= $param->constraint()->type();
52+
if (Type::$VAR === $type) {
5353
// NOOP
5454
} else if ($type->isAssignableFrom(InputStream::class)) {
5555
$source= 'stream';
5656
} else if ($type->isAssignableFrom(Request::class)) {
5757
$source= 'request';
5858
}
59-
$this->param($param, $param->getName(), $source);
59+
$this->param($param, $param->name(), self::$SOURCES[$source]);
6060
}
6161
}
6262

6363
/**
64-
* Adds parameter request reader for a given parameter
64+
* Adds parameter request accessor for a given parameter
6565
*
66-
* @param lang.reflect.Parameter $param
66+
* @param lang.reflection.Parameter $param
6767
* @param string $name
68-
* @param function(web.Request, web.rest.format.EntityFormat, string): var $source
68+
* @param function(web.Request, web.rest.format.EntityFormat, string): var $accessor
6969
* @return void
70+
* @throws lang.IllegalArgumentException
7071
*/
71-
private function param($param, $name, $source) {
72-
$extract= self::$SOURCES[$source];
73-
74-
if ($param->isOptional()) {
75-
$default= $param->getDefaultValue();
76-
$read= function($req, $format) use($extract, $name, $default) {
77-
return $extract($req, $format, $name) ?? $default;
72+
private function param($param, $name, $accessor) {
73+
if ($param->optional()) {
74+
$default= $param->default();
75+
$read= function($req, $format) use($accessor, $name, $default) {
76+
return $accessor($req, $format, $name) ?? $default;
7877
};
7978
} else {
80-
$read= function($req, $format) use($extract, $name) {
81-
if (null === ($value= $extract($req, $format, $name))) {
79+
$read= function($req, $format) use($accessor, $name) {
80+
if (null === ($value= $accessor($req, $format, $name))) {
8281
throw new IllegalArgumentException('Missing argument '.$name);
8382
}
8483
return $value;
8584
};
8685
}
87-
$this->params[$name]= ['type' => $param->getType(), 'read' => $read];
86+
$this->params[$name]= ['type' => $param->constraint()->type(), 'read' => $read];
8887
}
8988

9089
/** @return string */
91-
public function name() { return nameof($this->instance).'::'.$this->method->getName(); }
90+
public function name() { return nameof($this->instance).'::'.$this->method->name(); }
9291

93-
/** @return [:var] */
94-
public function annotations() { return $this->method->getAnnotations(); }
92+
/** @return lang.reflection.Annotations */
93+
public function annotations() { return $this->method->annotations(); }
9594

9695
/** @return [:var] */
9796
public function params() { return $this->params; }
@@ -106,7 +105,7 @@ public function params() { return $this->params; }
106105
public function invoke($args) {
107106
try {
108107
return $this->method->invoke($this->instance, $args);
109-
} catch (TargetInvocationException $e) {
108+
} catch (TargetException $e) {
110109
throw $e->getCause();
111110
}
112111
}

src/main/php/web/rest/Delegates.class.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php namespace web\rest;
22

3-
use lang\IllegalArgumentException;
3+
use lang\{IllegalArgumentException, Reflection};
44

55
/**
66
* Matches request and routes to correct delegate
@@ -31,16 +31,21 @@ public function with($instance, $base= '/') {
3131
}
3232

3333
$base= rtrim($base, '/');
34-
foreach (typeof($instance)->getMethods() as $method) {
35-
foreach (array_intersect_key($method->getAnnotations(), self::$METHODS) as $verb => $segment) {
34+
foreach (Reflection::type($instance)->methods() as $method) {
35+
36+
foreach ($method->annotations() as $annotation) {
37+
$verb= $annotation->name();
38+
if (null === ($source= self::$METHODS[$verb] ?? null)) continue;
39+
40+
$segment= $annotation->argument(0);
3641
if (null === $segment) {
3742
$pattern= $base.'(/.+)?';
3843
} else if ('/' === $segment || '' === $segment) {
3944
$pattern= $base.'/?';
4045
} else {
4146
$pattern= $base.preg_replace(['/\{([^:}]+):([^}]+)\}/', '/\{([^}]+)\}/'], ['(?<$1>$2)', '(?<$1>[^/]+)'], $segment);
4247
}
43-
$this->patterns['#^'.$verb.$pattern.'$#']= new Delegate($instance, $method, self::$METHODS[$verb]);
48+
$this->patterns['#^'.$verb.$pattern.'$#']= new Delegate($instance, $method, $source);
4449
}
4550
}
4651
return $this;
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
<?php namespace web\rest;
22

3+
use lang\Reflection;
4+
35
/**
46
* Creates routing based on a given instance
57
*/
68
class MethodsIn extends Delegates {
79

810
/** @param object $instance */
911
public function __construct($instance) {
10-
$class= typeof($instance);
11-
$this->with($instance, $class->hasAnnotation('resource') ? $class->getAnnotation('resource') ?? '' : '/');
12+
$class= Reflection::type($instance);
13+
if ($annotation= $class->annotation(Resource::class)) {
14+
$this->with($instance, (string)$annotation->argument(0));
15+
} else {
16+
$this->with($instance, '/');
17+
}
1218
uksort($this->patterns, function($a, $b) { return strlen($b) - strlen($a); });
1319
}
1420
}

src/main/php/web/rest/ResourcesIn.class.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
<?php namespace web\rest;
22

3-
use lang\reflect\Package;
3+
use lang\reflection\Package;
44

55
/**
66
* Creates routing based on resource classes in a given package
77
*
8-
* @test xp://web.rest.unittest.ResourcesInTest
8+
* @test web.rest.unittest.ResourcesInTest
99
*/
1010
class ResourcesIn extends Delegates {
1111

1212
/**
1313
* Creates this delegates instance
1414
*
15-
* @param lang.reflect.Package|string $package
15+
* @param lang.reflection.Package|string $package
1616
* @param function(lang.XPClass): object $new Optional function to create instances
1717
*/
1818
public function __construct($package, $new= null) {
19-
$p= $package instanceof Package ? $package : Package::forName($package);
20-
foreach ($p->getClasses() as $class) {
21-
if ($class->hasAnnotation('resource')) {
22-
$this->with($new ? $new($class) : $class->newInstance(), $class->getAnnotation('resource') ?? '');
19+
$p= $package instanceof Package ? $package : new Package($package);
20+
foreach ($p->types() as $type) {
21+
if ($resource= $type->annotation(Resource::class)) {
22+
$this->with($new ? $new($type->class()) : $type->newInstance(), (string)$resource->argument(0));
2323
}
2424
}
2525
uksort($this->patterns, function($a, $b) { return strlen($b) - strlen($a); });

src/test/php/web/rest/unittest/InvocationsTest.class.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use lang\{ElementNotFoundException, IllegalStateException};
44
use test\{Assert, Test};
5-
use web\rest\unittest\api\Users;
5+
use web\rest\unittest\api\{Users, Cached};
66
use web\rest\{Interceptor, Response, RestApi};
77

88
class InvocationsTest extends RunTest {
@@ -49,12 +49,12 @@ public function intercepting_catching_exceptions() {
4949
#[Test]
5050
public function intercepting_can_access_annotations() {
5151
$invocations= function($invocation, $args) use(&$cached) {
52-
$cached= $invocation->target()->annotations()['cached'];
52+
$cached= $invocation->target()->annotations()->type(Cached::class);
5353
return $invocation->proceed($args);
5454
};
5555

5656
$this->run((new RestApi(new Users()))->intercepting($invocations), 'GET', '/users/1549/avatar');
57-
Assert::equals(['ttl' => 3600], $cached);
57+
Assert::equals(['ttl' => 3600], $cached->arguments());
5858
}
5959

6060
#[Test]

src/test/php/web/rest/unittest/ResourcesInTest.class.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php namespace web\rest\unittest;
22

3-
use lang\reflect\Package;
3+
use lang\reflection\Package;
44
use test\{Assert, Test};
55
use web\rest\ResourcesIn;
66

@@ -14,7 +14,7 @@ public function using_package_name() {
1414

1515
#[Test]
1616
public function using_package_instance() {
17-
$r= new ResourcesIn(Package::forName('web.rest.unittest.api'));
17+
$r= new ResourcesIn(new Package('web.rest.unittest.api'));
1818
Assert::notEquals(null, $r->target('get', '/monitoring/status'));
1919
}
2020

0 commit comments

Comments
 (0)