Skip to content

Commit 0f1843b

Browse files
committed
Remove Param Sync
Parameter synchronization has been removed, favoring the usage of PSR7 interfaces for exchange of data between routines and routes if necessary, allowing the same controller splitting technique. Respect\Rest now uses Respect\Parameter for its Reflection duties, and the main DispatchContext exposes a valid PSR11 container with its injectable dependencies.
1 parent 74fd050 commit 0f1843b

23 files changed

Lines changed: 125 additions & 674 deletions

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
},
2323
"require": {
2424
"php": ">=8.5",
25+
"psr/container": "^2.0",
2526
"psr/http-factory": "^1.0",
2627
"psr/http-message": "^2.0",
2728
"psr/http-server-handler": "^1.0",
28-
"psr/http-server-middleware": "^1.0"
29+
"psr/http-server-middleware": "^1.0",
30+
"respect/parameter": "^1.0"
2931
},
3032
"require-dev": {
3133
"nyholm/psr7": "^1.8",

docs/README.md

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ $r3->get('/download/*', function(string $file, ResponseInterface $response) {
220220
```
221221

222222
1. Parameters are matched by type, not position. Mix them freely with route parameters.
223-
2. This works with callback routes and class controller methods alike.
223+
2. This works with callback routes, class controller methods, and routine callbacks alike.
224224

225225
## PSR-15 Integration
226226

@@ -291,11 +291,9 @@ $r3->get('/documents/*', function($documentId) {
291291
```
292292

293293
1. This will match the route only if the callback on *when* is matched.
294-
2. The `$documentId` param must have the same name in the action and the
295-
condition (but does not need to appear in the same order).
294+
2. Route parameters are passed positionally, matching the order of the `/*` segments.
296295
3. You can specify more than one parameter per condition callback.
297296
4. You can chain conditions: `when($cb1)->when($cb2)->when($etc)`
298-
5. Conditions will also sync with parameters on bound classes and instance methods.
299297

300298
This makes it possible to validate parameters using any custom routine and
301299
not just data types such as `int` or `string`.
@@ -324,7 +322,7 @@ $r3->get('/artists/*/albums/*', function($artistName, $albumName) {
324322
```
325323

326324
1. This will execute the callback defined with *by* before the route action.
327-
2. Parameters are synced by name, not by order, like with `when`.
325+
2. Route parameters are passed positionally, matching the order of the `/*` segments.
328326
3. You can specify more than one parameter per proxy callback.
329327
4. You can chain proxies: `by($cb1)->by($cb2)->by($etc)`
330328
5. A `return false` from a proxy will stop the execution of any following proxies
@@ -350,7 +348,6 @@ $r3->post('/artists/*/albums/*', function($artistName, $albumName) {
350348
1. `by` proxies will be executed before the route action, `through` proxies
351349
will be executed after.
352350
2. You are free to use them separately or in tandem.
353-
3. `through` can also receive parameters by name.
354351

355352
When processing something after the route has run, it's often desirable to process
356353
its output as well. This can be achieved with a nested closure:
@@ -386,23 +383,6 @@ A simple way of applying routines to every route on the router is:
386383
$r3->always('By', $logRoutine);
387384
```
388385

389-
You can use the param sync to take advantage of this:
390-
```php
391-
$r3->always('When', function($user=null) {
392-
if ($user) {
393-
return strlen($user) > 3;
394-
}
395-
});
396-
397-
$r3->any('/products', function () { /***/ });
398-
$r3->any('/users/*', function ($user) { /***/ });
399-
$r3->any('/users/*/products', function ($user) { /***/ });
400-
$r3->any('/listeners/*', function ($user) { /***/ });
401-
```
402-
403-
Since there are three routes with the `$user` parameter, `when` will
404-
verify them all automatically by name.
405-
406386
## File Extensions
407387

408388
Use the `fileExtension` routine to map URL extensions to response transformations:
@@ -556,7 +536,6 @@ appended to the route. Custom routines have the option of several different inte
556536
which can be implemented:
557537

558538
* `IgnorableFileExtension` - Instructs the router to ignore the file extension in requests.
559-
* `ParamSynced` - Syncs parameters with the route function/method.
560539
* `ProxyableBy` - Instructs the router to run method `by()` before the route.
561540
* `ProxyableThrough` - Instructs the router to run method `through()` after the route.
562541
* `ProxyableWhen` - Instructs the router to run method `when()` to validate the route match.

src/DispatchContext.php

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,25 @@
44

55
namespace Respect\Rest;
66

7+
use Psr\Container\ContainerInterface;
78
use Psr\Http\Message\ResponseFactoryInterface;
89
use Psr\Http\Message\ResponseInterface;
910
use Psr\Http\Message\ServerRequestInterface;
1011
use Psr\Http\Message\StreamFactoryInterface;
11-
use ReflectionFunctionAbstract;
12-
use ReflectionParameter;
12+
use Respect\Parameter\Resolver;
1313
use Respect\Rest\Routes\AbstractRoute;
14-
use Respect\Rest\Routines\ParamSynced;
15-
use Respect\Rest\Routines\Routinable;
1614
use Throwable;
1715

1816
use function is_a;
1917
use function rawurldecode;
2018
use function rtrim;
2119
use function set_error_handler;
20+
use function sprintf;
2221
use function strtolower;
2322
use function strtoupper;
2423

2524
/** Internal routing context wrapping a PSR-7 server request */
26-
final class DispatchContext
25+
final class DispatchContext implements ContainerInterface
2726
{
2827
/** @var array<int, mixed> */
2928
public array $params = [];
@@ -53,6 +52,8 @@ final class DispatchContext
5352
/** @var array<int, AbstractRoute> */
5453
private array $sideRoutes = [];
5554

55+
private Resolver|null $resolver = null;
56+
5657
public function __construct(
5758
public ServerRequestInterface $request,
5859
public ResponseFactoryInterface&StreamFactoryInterface $factory,
@@ -180,33 +181,6 @@ public function response(): ResponseInterface|null
180181
}
181182
}
182183

183-
/** @param array<int, mixed> $params */
184-
public function routineCall(
185-
string $type,
186-
string $method,
187-
Routinable $routine,
188-
array &$params,
189-
AbstractRoute $route,
190-
): mixed {
191-
$reflection = $route->getTargetReflection($method);
192-
193-
$callbackParameters = [];
194-
195-
if (!$routine instanceof ParamSynced) {
196-
$callbackParameters = $params;
197-
} elseif ($reflection !== null) {
198-
foreach ($routine->getParameters() as $parameter) {
199-
$callbackParameters[] = $this->extractRouteParam(
200-
$reflection,
201-
$parameter,
202-
$params,
203-
);
204-
}
205-
}
206-
207-
return $routine->{$type}($this, $callbackParameters);
208-
}
209-
210184
public function forward(AbstractRoute $route): ResponseInterface|null
211185
{
212186
$this->route = $route;
@@ -230,6 +204,30 @@ public function setResponder(Responder $responder): void
230204
$this->responder = $responder;
231205
}
232206

207+
public function resolver(): Resolver
208+
{
209+
return $this->resolver ??= new Resolver($this);
210+
}
211+
212+
public function has(string $id): bool
213+
{
214+
return is_a($id, ServerRequestInterface::class, true)
215+
|| is_a($id, ResponseInterface::class, true);
216+
}
217+
218+
public function get(string $id): mixed
219+
{
220+
if (is_a($id, ServerRequestInterface::class, true)) {
221+
return $this->request;
222+
}
223+
224+
if (is_a($id, ResponseInterface::class, true)) {
225+
return $this->ensureResponseDraft();
226+
}
227+
228+
throw new NotFoundException(sprintf('No entry found for "%s"', $id));
229+
}
230+
233231
/** @return callable|null The previous error handler, or null */
234232
protected function prepareForErrorForwards(AbstractRoute $route): callable|null
235233
{
@@ -310,28 +308,6 @@ protected function forwardToStatusRoute(ResponseInterface $preparedResponse): Re
310308
return null;
311309
}
312310

313-
/** @param array<int, mixed> $params */
314-
protected function extractRouteParam(
315-
ReflectionFunctionAbstract $callback,
316-
ReflectionParameter $routeParam,
317-
array &$params,
318-
): mixed {
319-
foreach ($callback->getParameters() as $callbackParamReflection) {
320-
if (
321-
$callbackParamReflection->getName() === $routeParam->getName()
322-
&& isset($params[$callbackParamReflection->getPosition()])
323-
) {
324-
return $params[$callbackParamReflection->getPosition()];
325-
}
326-
}
327-
328-
if ($routeParam->isDefaultValueAvailable()) {
329-
return $routeParam->getDefaultValue();
330-
}
331-
332-
return null;
333-
}
334-
335311
protected function finalizeResponse(mixed $response): ResponseInterface
336312
{
337313
return $this->responder()->finalize(

src/NotFoundException.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Respect\Rest;
6+
7+
use Exception;
8+
use Psr\Container\NotFoundExceptionInterface;
9+
10+
final class NotFoundException extends Exception implements NotFoundExceptionInterface
11+
{
12+
}

src/ResolvesCallbackArguments.php

Lines changed: 0 additions & 74 deletions
This file was deleted.

src/Routes/AbstractRoute.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use ReflectionClass;
88
use ReflectionFunctionAbstract;
99
use Respect\Rest\DispatchContext;
10-
use Respect\Rest\ResolvesCallbackArguments;
1110
use Respect\Rest\Routines\IgnorableFileExtension;
1211
use Respect\Rest\Routines\Routinable;
1312
use Respect\Rest\Routines\Unique;
@@ -55,8 +54,6 @@
5554
// phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix
5655
abstract class AbstractRoute
5756
{
58-
use ResolvesCallbackArguments;
59-
6057
public const string CATCHALL_IDENTIFIER = '/**';
6158

6259
public const array CORE_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
@@ -135,11 +132,6 @@ public function getTargetMethod(string $method): string
135132
return $method;
136133
}
137134

138-
public function getTargetReflection(string $method): ReflectionFunctionAbstract|null
139-
{
140-
return $this->getReflection($this->getTargetMethod($method));
141-
}
142-
143135
/** @param array<int, mixed> $params */
144136
public function dispatchTarget(string $method, array &$params, DispatchContext $context): mixed
145137
{

src/Routes/Callback.php

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@
44

55
namespace Respect\Rest\Routes;
66

7-
use Closure;
8-
use ReflectionFunction;
97
use ReflectionFunctionAbstract;
10-
use ReflectionMethod;
8+
use Respect\Parameter\Resolver;
119
use Respect\Rest\DispatchContext;
1210

1311
use function array_merge;
14-
use function is_array;
1512

1613
class Callback extends AbstractRoute
1714
{
@@ -31,11 +28,7 @@ public function __construct(
3128

3229
public function getCallbackReflection(): ReflectionFunctionAbstract
3330
{
34-
if (is_array($this->callback)) {
35-
return new ReflectionMethod($this->callback[0], $this->callback[1]);
36-
}
37-
38-
return new ReflectionFunction(Closure::fromCallable($this->callback));
31+
return Resolver::reflectCallable($this->callback);
3932
}
4033

4134
public function getReflection(string $method): ReflectionFunctionAbstract
@@ -51,7 +44,7 @@ public function getReflection(string $method): ReflectionFunctionAbstract
5144
public function runTarget(string $method, array &$params, DispatchContext $context): mixed
5245
{
5346
$reflection = $this->getReflection($method);
54-
$args = $this->resolveCallbackArguments($reflection, array_merge($params, $this->arguments), $context);
47+
$args = $context->resolver()->resolve($reflection, array_merge($params, $this->arguments));
5548

5649
return ($this->callback)(...$args);
5750
}

src/Routes/ControllerRoute.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ protected function invokeTarget(object $target, string $method, array &$params,
9797
{
9898
$reflection = $this->getReflection($method);
9999
if ($reflection !== null) {
100-
$args = $this->resolveCallbackArguments($reflection, $params, $context);
100+
$args = $context->resolver()->resolve($reflection, $params);
101101

102102
return $target->$method(...$args);
103103
}

0 commit comments

Comments
 (0)