Skip to content

Commit 08acdd3

Browse files
committed
Move current implementation into seperate class
1 parent 06f0b06 commit 08acdd3

File tree

5 files changed

+228
-67
lines changed

5 files changed

+228
-67
lines changed

lib/Dispatcher.php

Lines changed: 20 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33

44
namespace AdvancedJsonRpc;
55

6+
use AdvancedJsonRpc\Reflection\NativeReflection;
67
use JsonMapper;
78
use JsonMapper_Exception;
8-
use phpDocumentor\Reflection\DocBlockFactory;
9-
use phpDocumentor\Reflection\Types;
10-
use ReflectionException;
11-
use ReflectionMethod;
12-
use ReflectionNamedType;
139

1410
class Dispatcher
1511
{
@@ -24,21 +20,14 @@ class Dispatcher
2420
private $delimiter;
2521

2622
/**
27-
* method => ReflectionMethod[]
28-
*
29-
* @var ReflectionMethod
30-
*/
31-
private $methods;
32-
33-
/**
34-
* @var \phpDocumentor\Reflection\DocBlockFactory
23+
* @var Reflection\NativeReflection
3524
*/
36-
private $docBlockFactory;
25+
private $reflection;
3726

3827
/**
39-
* @var \phpDocumentor\Reflection\Types\ContextFactory
28+
* @var JsonMapper
4029
*/
41-
private $contextFactory;
30+
private $mapper;
4231

4332
/**
4433
* @param object $target The target object that should receive the method calls
@@ -48,9 +37,8 @@ public function __construct($target, $delimiter = '->')
4837
{
4938
$this->target = $target;
5039
$this->delimiter = $delimiter;
51-
$this->docBlockFactory = DocBlockFactory::createInstance();
52-
$this->contextFactory = new Types\ContextFactory();
5340
$this->mapper = new JsonMapper();
41+
$this->reflection = new NativeReflection();
5442
}
5543

5644
/**
@@ -81,33 +69,19 @@ public function dispatch($msg)
8169
}
8270
$obj = $obj->$part;
8371
}
84-
if (!isset($this->methods[$msg->method])) {
85-
try {
86-
$method = new ReflectionMethod($obj, $fn);
87-
$this->methods[$msg->method] = $method;
88-
} catch (ReflectionException $e) {
89-
throw new Error($e->getMessage(), ErrorCode::METHOD_NOT_FOUND, null, $e);
90-
}
91-
}
92-
$method = $this->methods[$msg->method];
93-
$parameters = $method->getParameters();
94-
if ($method->getDocComment()) {
95-
$docBlock = $this->docBlockFactory->create(
96-
$method->getDocComment(),
97-
$this->contextFactory->createFromReflector($method->getDeclaringClass())
98-
);
99-
$paramTags = $docBlock->getTagsByName('param');
100-
}
72+
73+
$method = $this->reflection->getMethodDetails($msg->method, $obj, $fn);
74+
10175
$args = [];
10276
if (isset($msg->params)) {
10377
// Find out the position
10478
if (is_array($msg->params)) {
10579
$args = $msg->params;
10680
} else if (is_object($msg->params)) {
107-
foreach ($parameters as $pos => $parameter) {
81+
foreach ($method->getParameters() as $pos => $parameter) {
10882
$value = null;
10983
foreach(get_object_vars($msg->params) as $key => $val) {
110-
if ($parameter->name === $key) {
84+
if ($parameter->getName() === $key) {
11185
$value = $val;
11286
break;
11387
}
@@ -117,46 +91,25 @@ public function dispatch($msg)
11791
} else {
11892
throw new Error('Params must be structured or omitted', ErrorCode::INVALID_REQUEST);
11993
}
94+
12095
foreach ($args as $position => $value) {
12196
try {
12297
// If the type is structured (array or object), map it with JsonMapper
12398
if (is_object($value)) {
12499
// Does the parameter have a type hint?
125-
$param = $parameters[$position];
100+
$param = $method->getParameters()[$position];
126101
if ($param->hasType()) {
127-
$paramType = $param->getType();
128-
if ($paramType instanceof ReflectionNamedType) {
129-
// We have object data to map and want the class name.
130-
// This should not include the `?` if the type was nullable.
131-
$class = $paramType->getName();
132-
} else {
133-
// Fallback for php 7.0, which is still supported (and doesn't have nullable).
134-
$class = (string)$paramType;
135-
}
102+
$class = $param->getType()->getName();
136103
$value = $this->mapper->map($value, new $class());
137104
}
138-
} else if (is_array($value) && isset($docBlock)) {
139-
// Get the array type from the DocBlock
140-
$type = $paramTags[$position]->getType();
141-
// For union types, use the first one that is a class array (often it is SomeClass[]|null)
142-
if ($type instanceof Types\Compound) {
143-
for ($i = 0; $t = $type->get($i); $i++) {
144-
if (
145-
$t instanceof Types\Array_
146-
&& $t->getValueType() instanceof Types\Object_
147-
&& (string)$t->getValueType() !== 'object'
148-
) {
149-
$class = (string)$t->getValueType()->getFqsen();
150-
$value = $this->mapper->mapArray($value, [], $class);
151-
break;
152-
}
153-
}
154-
} else if ($type instanceof Types\Array_) {
155-
$class = (string)$type->getValueType()->getFqsen();
156-
$value = $this->mapper->mapArray($value, [], $class);
157-
} else {
105+
} else if (is_array($value) && $method->getDocComment() !== '') {
106+
if (!array_key_exists($position, $method->getParamTags())) {
158107
throw new Error('Type is not matching @param tag', ErrorCode::INVALID_PARAMS);
159108
}
109+
110+
// Get the array type from the DocBlock
111+
$class = $method->getParamTags()[$position]->getType()->getName();
112+
$value = $this->mapper->mapArray($value, [], $class);
160113
}
161114
} catch (JsonMapper_Exception $e) {
162115
throw new Error($e->getMessage(), ErrorCode::INVALID_PARAMS, null, $e);

lib/Reflection/Dto/Method.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AdvancedJsonRpc\Reflection\Dto;
6+
7+
class Method
8+
{
9+
/** @var string */
10+
private $declaringClass;
11+
/** @var string|null */
12+
private $docComment;
13+
/** @var Parameter[] */
14+
private $parameters;
15+
/** @var Parameter[] */
16+
private $paramTags;
17+
18+
/**
19+
* @param string|null $docComment
20+
*/
21+
public function __construct(string $declaringClass, $docComment, array $parameters, array $paramTags)
22+
{
23+
$this->declaringClass = $declaringClass;
24+
$this->docComment = $docComment;
25+
$this->parameters = $parameters;
26+
$this->paramTags = $paramTags;
27+
}
28+
29+
public function getDeclaringClass(): string
30+
{
31+
return $this->declaringClass;
32+
}
33+
34+
public function getDocComment(): string
35+
{
36+
return $this->docComment;
37+
}
38+
39+
public function getParameters(): array
40+
{
41+
return $this->parameters;
42+
}
43+
44+
public function getParamTags(): array
45+
{
46+
return $this->paramTags;
47+
}
48+
}

lib/Reflection/Dto/Parameter.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AdvancedJsonRpc\Reflection\Dto;
6+
7+
8+
class Parameter
9+
{
10+
/** @var string */
11+
private $name;
12+
/** @var Type|null */
13+
private $type;
14+
15+
public function __construct(string $name, Type $type = null)
16+
{
17+
$this->name = $name;
18+
$this->type = $type;
19+
}
20+
21+
public function getName(): string
22+
{
23+
return $this->name;
24+
}
25+
26+
public function getType(): Type
27+
{
28+
return $this->type;
29+
}
30+
31+
public function hasType(): bool
32+
{
33+
return isset($this->type);
34+
}
35+
}

lib/Reflection/Dto/Type.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AdvancedJsonRpc\Reflection\Dto;
6+
7+
class Type
8+
{
9+
/** @var string */
10+
private $name;
11+
12+
public function __construct(string $name)
13+
{
14+
$this->name = $name;
15+
}
16+
17+
public function getName(): string
18+
{
19+
return $this->name;
20+
}
21+
}

lib/Reflection/NativeReflection.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AdvancedJsonRpc\Reflection;
6+
7+
8+
use AdvancedJsonRpc\Error;
9+
use AdvancedJsonRpc\ErrorCode;
10+
use AdvancedJsonRpc\Reflection\Dto\Method;
11+
use AdvancedJsonRpc\Reflection\Dto\Parameter;
12+
use AdvancedJsonRpc\Reflection\Dto\Type;
13+
use phpDocumentor\Reflection\DocBlock\Tag;
14+
use phpDocumentor\Reflection\DocBlockFactory;
15+
use phpDocumentor\Reflection\Types;
16+
use ReflectionException;
17+
use ReflectionMethod;
18+
use ReflectionNamedType;
19+
20+
class NativeReflection
21+
{
22+
/** @var DocBlockFactory */
23+
private $docBlockFactory;
24+
/** @var \phpDocumentor\Reflection\Types\ContextFactory */
25+
private $contextFactory;
26+
/** @var Method[] */
27+
private $methods = [];
28+
29+
public function __construct()
30+
{
31+
$this->docBlockFactory = DocBlockFactory::createInstance();
32+
$this->contextFactory = new Types\ContextFactory();
33+
}
34+
35+
public function getMethodDetails($rpcMethod, $target, $nativeMethod): Method
36+
{
37+
if (array_key_exists($rpcMethod, $this->methods)) {
38+
return $this->methods[$rpcMethod];
39+
}
40+
41+
try {
42+
$nativeMethod = new ReflectionMethod($target, $nativeMethod);
43+
} catch (ReflectionException $e) {
44+
throw new Error($e->getMessage(), ErrorCode::METHOD_NOT_FOUND, null, $e);
45+
}
46+
47+
$paramTags = [];
48+
if ($nativeMethod->getDocComment()) {
49+
$docBlock = $this->docBlockFactory->create(
50+
$nativeMethod->getDocComment(),
51+
$this->contextFactory->createFromReflector($nativeMethod->getDeclaringClass())
52+
);
53+
$paramTags = $docBlock->getTagsByName('param');
54+
}
55+
56+
$method = new Method(
57+
$nativeMethod->getDeclaringClass()->getName(),
58+
$nativeMethod->getDocComment() ?: null,
59+
array_map(function($p) { return $this->mapNativeReflectionParameterToParameter($p); }, $nativeMethod->getParameters()),
60+
array_map(function($p) { return $this->mapDocBlockTagToParameter($p); }, $paramTags)
61+
);
62+
63+
$this->methods[$rpcMethod] = $method;
64+
65+
return $method;
66+
}
67+
68+
private function mapNativeReflectionParameterToParameter(\ReflectionParameter $native): Parameter
69+
{
70+
$types = $this->mapNativeReflectionTypeToType($native->getType());
71+
return new Parameter($native->getName(), $types);
72+
}
73+
74+
private function mapDocBlockTagToParameter(Tag $tag): Parameter
75+
{
76+
$type = $tag->getType();
77+
// For union types, use the first one that is a class array (often it is SomeClass[]|null)
78+
if ($type instanceof Types\Compound) {
79+
for ($i = 0; $t = $type->get($i); $i++) {
80+
if (
81+
$t instanceof Types\Array_
82+
&& $t->getValueType() instanceof Types\Object_
83+
&& (string)$t->getValueType() !== 'object'
84+
) {
85+
return new Parameter($tag->getName(), new Type((string)$t->getValueType()->getFqsen()));
86+
}
87+
}
88+
} else if ($type instanceof Types\Array_) {
89+
return new Parameter($tag->getName(), new Type((string)$type->getValueType()->getFqsen()));
90+
}
91+
}
92+
93+
private function mapNativeReflectionTypeToType(\ReflectionType $native = null): Type
94+
{
95+
if ($native instanceof ReflectionNamedType) {
96+
// We have object data to map and want the class name.
97+
// This should not include the `?` if the type was nullable.
98+
return new Type($native->getName());
99+
} else {
100+
// Fallback for php 7.0, which is still supported (and doesn't have nullable).
101+
return new Type((string) $native);
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)