Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
Manager
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
11 / 11
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 addHandlers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandlers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasHandlers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 process
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 handle
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 terminate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Middleware;
16
17use Pop\AbstractManager;
18
19/**
20 * Middleware manager class
21 *
22 * @category   Pop
23 * @package    Pop\Middleware
24 * @author     Nick Sagona, III <nick@popphp.org>
25 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
26 * @license    https://www.popphp.org/license     New BSD License
27 * @version    5.0.0
28 */
29class Manager extends AbstractManager
30{
31
32    /**
33     * Constructor
34     *
35     * Instantiate the middleware manager object.
36     *
37     * @param ?array $handlers
38     */
39    public function __construct(?array $handlers = null)
40    {
41        if (!empty($handlers)) {
42            parent::addItems($handlers);
43        }
44    }
45
46    /**
47     * Add handlers
48     *
49     * @param  array $handlers
50     * @return static
51     */
52    public function addHandlers(array $handlers): static
53    {
54        return parent::addItems($handlers);
55    }
56
57    /**
58     * Add a handler
59     *
60     * @param  mixed $handler
61     * @param  mixed $name
62     * @return static
63     */
64    public function addHandler(mixed $handler, mixed $name = null): static
65    {
66        return parent::addItem($handler, $name);
67    }
68
69    /**
70     * Remove a handler
71     *
72     * @param  mixed $name
73     * @return static
74     */
75    public function removeHandler(mixed $name): static
76    {
77        return parent::removeItem($name);
78    }
79
80    /**
81     * Get a handler
82     *
83     * @param  mixed $name
84     * @return mixed
85     */
86    public function getHandler(mixed $name): mixed
87    {
88        return parent::getItem($name);
89    }
90
91    /**
92     * Get handlers
93     *
94     * @return array
95     */
96    public function getHandlers(): array
97    {
98        return parent::getItems();
99    }
100
101    /**
102     * Determine whether the manager has a handler
103     *
104     * @param  string $name
105     * @return bool
106     */
107    public function hasHandler(string $name): bool
108    {
109        return parent::hasItem($name);
110    }
111
112    /**
113     * Determine whether the manager has handlers
114     *
115     * @return bool
116     */
117    public function hasHandlers(): bool
118    {
119        return parent::hasItems();
120    }
121
122    /**
123     * Process all middleware
124     *
125     * @param  mixed    $request
126     * @param  \Closure $dispatch
127     * @param  mixed    $dispatchParams
128     * @return static
129     */
130    public function process(mixed $request, \Closure $dispatch, mixed $dispatchParams = null): static
131    {
132        $response = $this->handle($request, $this->items, $dispatch, $dispatchParams);
133
134        self::terminate($this->items, $request, $response);
135
136        return $this;
137    }
138
139    /**
140     * Recursive method to execute all middleware handlers
141     *
142     * The remaining handler queue is threaded through as a plain local
143     * parameter (captured by value in the continuation closure below)
144     * rather than shared instance/class state - so a middleware that itself
145     * triggers a nested process() call (its own, or on a different Manager
146     * entirely) can never corrupt this call's remaining queue, since there
147     * is no shared mutable slot for it to clobber.
148     *
149     * @param  mixed    $request
150     * @param  array    $handlers
151     * @param  \Closure $dispatch
152     * @param  mixed    $dispatchParams
153     * @return mixed
154     */
155    protected function handle(mixed $request, array $handlers, \Closure $dispatch, mixed $dispatchParams = null): mixed
156    {
157        $next = array_shift($handlers);
158
159        if ($next === null) {
160            return (null !== $dispatchParams) ? call_user_func_array($dispatch, $dispatchParams) : $dispatch();
161        } else if (is_string($next) && class_exists($next)) {
162            $next = new $next();
163        }
164
165        return $next->handle($request, function ($req) use ($handlers, $dispatch, $dispatchParams) {
166            return $this->handle($req, $handlers, $dispatch, $dispatchParams);
167        });
168    }
169
170    /**
171     * Execute all middleware handlers terminate methods
172     *
173     * @param  array $handlers
174     * @param  mixed $request
175     * @param  mixed $response
176     * @return void
177     */
178    public static function terminate(array $handlers, mixed $request = null, mixed $response = null): void
179    {
180        foreach ($handlers as $handler) {
181            if (is_string($handler) && class_exists($handler)) {
182                $handler = new $handler();
183            }
184            if ($handler instanceof TerminableInterface) {
185                $handler->terminate($request, $response);
186            }
187        }
188    }
189
190}