Skip to content

Commit f8811e1

Browse files
committed
allowed for middlewares to be created with container
1 parent ad58c09 commit f8811e1

6 files changed

Lines changed: 132 additions & 51 deletions

File tree

flight/Engine.php

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -382,64 +382,82 @@ public function path(string $dir): void
382382
* Processes each routes middleware.
383383
*
384384
* @param Route $route The route to process the middleware for.
385-
* @param string $event_name If this is the before or after method.
385+
* @param string $eventName If this is the before or after method.
386386
*/
387-
protected function processMiddleware(Route $route, string $event_name): bool
387+
protected function processMiddleware(Route $route, string $eventName): bool
388388
{
389-
$at_least_one_middleware_failed = false;
389+
$atLeastOneMiddlewareFailed = false;
390390

391-
$middlewares = $event_name === Dispatcher::FILTER_BEFORE ? $route->middleware : array_reverse($route->middleware);
391+
// Process things normally for before, and then in reverse order for after.
392+
$middlewares = $eventName === Dispatcher::FILTER_BEFORE
393+
? $route->middleware
394+
: array_reverse($route->middleware);
392395
$params = $route->params;
393396

394397
foreach ($middlewares as $middleware) {
395-
$middleware_object = false;
396-
397-
if ($event_name === Dispatcher::FILTER_BEFORE) {
398-
// can be a callable or a class
399-
$middleware_object = (is_callable($middleware) === true
400-
? $middleware
401-
: (method_exists($middleware, Dispatcher::FILTER_BEFORE) === true
402-
? [$middleware, Dispatcher::FILTER_BEFORE]
403-
: false
404-
)
405-
);
406-
} elseif ($event_name === Dispatcher::FILTER_AFTER) {
407-
// must be an object. No functions allowed here
408-
if (
409-
is_object($middleware) === true
410-
&& !($middleware instanceof Closure)
411-
&& method_exists($middleware, Dispatcher::FILTER_AFTER) === true
412-
) {
413-
$middleware_object = [$middleware, Dispatcher::FILTER_AFTER];
398+
399+
// Assume that nothing is going to be executed for the middleware.
400+
$middlewareObject = false;
401+
402+
// Closure functions can only run on the before event
403+
if ($eventName === Dispatcher::FILTER_BEFORE && is_object($middleware) === true && ($middleware instanceof Closure)) {
404+
$middlewareObject = $middleware;
405+
406+
// If the object has already been created, we can just use it if the event name exists.
407+
} elseif (is_object($middleware) === true) {
408+
$middlewareObject = method_exists($middleware, $eventName) === true ? [ $middleware, $eventName ] : false;
409+
410+
// If the middleware is a string, we need to create the object and then call the event.
411+
} elseif (is_string($middleware) === true && method_exists($middleware, $eventName) === true) {
412+
$resolvedClass = null;
413+
414+
// if there's a container assigned, we should use it to create the object
415+
if ($this->dispatcher->mustUseContainer($middleware) === true) {
416+
$resolvedClass = $this->dispatcher->resolveContainerClass($middleware, $params);
417+
// otherwise just assume it's a plain jane class, so inject the engine
418+
// just like in Dispatcher::invokeCallable()
419+
} elseif (class_exists($middleware) === true) {
420+
$resolvedClass = new $middleware($this);
421+
}
422+
423+
// If something was resolved, create an array callable that will be passed in later.
424+
if ($resolvedClass !== null) {
425+
$middlewareObject = [ $resolvedClass, $eventName ];
414426
}
415427
}
416428

417-
if ($middleware_object === false) {
429+
// If nothing was resolved, go to the next thing
430+
if ($middlewareObject === false) {
418431
continue;
419432
}
420433

421-
$use_v3_output_buffering =
434+
// This is the way that v3 handles output buffering (which captures output correctly)
435+
$useV3OutputBuffering =
422436
$this->response()->v2_output_buffering === false &&
423437
$route->is_streamed === false;
424438

425-
if ($use_v3_output_buffering === true) {
439+
if ($useV3OutputBuffering === true) {
426440
ob_start();
427441
}
428442

429-
// It's assumed if you don't declare before, that it will be assumed as the before method
430-
$middleware_result = $middleware_object($params);
443+
// Here is the array callable $middlewareObject that we created earlier.
444+
// It looks bizarre but it's really calling [ $class, $method ]($params)
445+
// Which loosely translates to $class->$method($params)
446+
$middlewareResult = $middlewareObject($params);
431447

432-
if ($use_v3_output_buffering === true) {
448+
if ($useV3OutputBuffering === true) {
433449
$this->response()->write(ob_get_clean());
434450
}
435451

436-
if ($middleware_result === false) {
437-
$at_least_one_middleware_failed = true;
452+
// If you return false in your middleware, it will halt the request
453+
// and throw a 403 forbidden error by default.
454+
if ($middlewareResult === false) {
455+
$atLeastOneMiddlewareFailed = true;
438456
break;
439457
}
440458
}
441459

442-
return $at_least_one_middleware_failed;
460+
return $atLeastOneMiddlewareFailed;
443461
}
444462

445463
////////////////////////
@@ -475,7 +493,7 @@ public function _start(): void
475493
}
476494

477495
// Route the request
478-
$failed_middleware_check = false;
496+
$failedMiddlewareCheck = false;
479497

480498
while ($route = $router->route($request)) {
481499
$params = array_values($route->params);
@@ -506,18 +524,18 @@ public function _start(): void
506524

507525
// Run any before middlewares
508526
if (count($route->middleware) > 0) {
509-
$at_least_one_middleware_failed = $this->processMiddleware($route, 'before');
510-
if ($at_least_one_middleware_failed === true) {
511-
$failed_middleware_check = true;
527+
$atLeastOneMiddlewareFailed = $this->processMiddleware($route, 'before');
528+
if ($atLeastOneMiddlewareFailed === true) {
529+
$failedMiddlewareCheck = true;
512530
break;
513531
}
514532
}
515533

516-
$use_v3_output_buffering =
534+
$useV3OutputBuffering =
517535
$this->response()->v2_output_buffering === false &&
518536
$route->is_streamed === false;
519537

520-
if ($use_v3_output_buffering === true) {
538+
if ($useV3OutputBuffering === true) {
521539
ob_start();
522540
}
523541

@@ -527,17 +545,17 @@ public function _start(): void
527545
$params
528546
);
529547

530-
if ($use_v3_output_buffering === true) {
548+
if ($useV3OutputBuffering === true) {
531549
$response->write(ob_get_clean());
532550
}
533551

534552
// Run any before middlewares
535553
if (count($route->middleware) > 0) {
536554
// process the middleware in reverse order now
537-
$at_least_one_middleware_failed = $this->processMiddleware($route, 'after');
555+
$atLeastOneMiddlewareFailed = $this->processMiddleware($route, 'after');
538556

539-
if ($at_least_one_middleware_failed === true) {
540-
$failed_middleware_check = true;
557+
if ($atLeastOneMiddlewareFailed === true) {
558+
$failedMiddlewareCheck = true;
541559
break;
542560
}
543561
}
@@ -558,7 +576,7 @@ public function _start(): void
558576
$response->clearBody();
559577
}
560578

561-
if ($failed_middleware_check === true) {
579+
if ($failedMiddlewareCheck === true) {
562580
$this->halt(403, 'Forbidden', empty(getenv('PHPUNIT_TEST')));
563581
} elseif ($dispatched === false) {
564582
$this->notFound();

flight/core/Dispatcher.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,7 @@ public function invokeCallable($func, array &$params = [])
356356

357357
[$class, $method] = $func;
358358

359-
$mustUseTheContainer = $this->containerHandler !== null && (
360-
(is_object($class) === true && strpos(get_class($class), 'flight\\') === false)
361-
|| is_string($class)
362-
);
359+
$mustUseTheContainer = $this->mustUseContainer($class);
363360

364361
if ($mustUseTheContainer === true) {
365362
$resolvedClass = $this->resolveContainerClass($class, $params);
@@ -437,7 +434,7 @@ protected function verifyValidClassCallable($class, $method, $resolvedClass): vo
437434
*
438435
* @return ?object Class object.
439436
*/
440-
protected function resolveContainerClass(string $class, array &$params)
437+
public function resolveContainerClass(string $class, array &$params)
441438
{
442439
// PSR-11
443440
if (
@@ -468,6 +465,21 @@ protected function resolveContainerClass(string $class, array &$params)
468465
return null;
469466
}
470467

468+
/**
469+
* Checks to see if a container should be used or not.
470+
*
471+
* @param string|object $class the class to verify
472+
*
473+
* @return boolean
474+
*/
475+
public function mustUseContainer($class): bool
476+
{
477+
return $this->containerHandler !== null && (
478+
(is_object($class) === true && strpos(get_class($class), 'flight\\') === false)
479+
|| is_string($class)
480+
);
481+
}
482+
471483
/** Because this could throw an exception in the middle of an output buffer, */
472484
protected function fixOutputBuffering(): void
473485
{

flight/net/Route.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class Route
6363
/**
6464
* The middleware to be applied to the route
6565
*
66-
* @var array<int, callable|object>
66+
* @var array<int, callable|object|string>
6767
*/
6868
public array $middleware = [];
6969

@@ -226,7 +226,7 @@ public function setAlias(string $alias): self
226226
/**
227227
* Sets the route middleware
228228
*
229-
* @param array<int, callable>|callable $middleware
229+
* @param array<int, callable|string>|callable|string $middleware
230230
*/
231231
public function addMiddleware($middleware): self
232232
{

phpunit.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
stopOnFailure="true"
1212
verbose="true"
1313
colors="true">
14-
<coverage processUncoveredFiles="true">
14+
<coverage processUncoveredFiles="false">
1515
<include>
1616
<directory suffix=".php">flight/</directory>
1717
</include>
18+
<exclude>
19+
<file>flight/autoload.php</file>
20+
</exclude>
1821
</coverage>
1922
<testsuites>
2023
<testsuite name="default">

tests/EngineTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,49 @@ public function after($params)
572572
$this->expectOutputString('OK123after123');
573573
}
574574

575+
public function testMiddlewareClassStringNoContainer()
576+
{
577+
$middleware = new class {
578+
public function after($params)
579+
{
580+
echo 'after' . $params['id'];
581+
}
582+
};
583+
$engine = new Engine();
584+
585+
$engine->route('/path1/@id', function ($id) {
586+
echo 'OK' . $id;
587+
})
588+
->addMiddleware(get_class($middleware));
589+
$engine->request()->url = '/path1/123';
590+
$engine->start();
591+
$this->expectOutputString('OK123after123');
592+
}
593+
594+
public function testMiddlewareClassStringWithContainer()
595+
{
596+
597+
$engine = new Engine();
598+
$dice = new \Dice\Dice();
599+
$dice = $dice->addRule('*', [
600+
'substitutions' => [
601+
Engine::class => $engine
602+
]
603+
]);
604+
$engine->registerContainerHandler(function ($class, $params) use ($dice) {
605+
return $dice->create($class, $params);
606+
});
607+
608+
609+
$engine->route('/path1/@id', function ($id) {
610+
echo 'OK' . $id;
611+
})
612+
->addMiddleware(ContainerDefault::class);
613+
$engine->request()->url = '/path1/123';
614+
$engine->start();
615+
$this->expectOutputString('I returned before the route was called with the following parameters: {"id":"123"}OK123');
616+
}
617+
575618
public function testMiddlewareClassAfterFailedCheck()
576619
{
577620
$middleware = new class {

tests/classes/ContainerDefault.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ public function __construct(Engine $engine)
1515
$this->app = $engine;
1616
}
1717

18+
public function before(array $params)
19+
{
20+
echo 'I returned before the route was called with the following parameters: ' . json_encode($params);
21+
}
22+
1823
public function testTheContainer()
1924
{
2025
return $this->app->get('test_me_out');

0 commit comments

Comments
 (0)