• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ICanBoogie / Routing / 4086305047

pending completion
4086305047

push

github

Olivier Laviale
Tidy

2 of 2 new or added lines in 1 file covered. (100.0%)

398 of 455 relevant lines covered (87.47%)

6.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/ControllerAbstract.php
1
<?php
2

3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace ICanBoogie\Routing;
13

14
use ICanBoogie\HTTP\RedirectResponse;
15
use ICanBoogie\HTTP\Request;
16
use ICanBoogie\HTTP\Responder;
17
use ICanBoogie\HTTP\Response;
18
use ICanBoogie\HTTP\ResponseStatus;
19
use ICanBoogie\Prototyped;
20
use ICanBoogie\Routing\Controller\ActionEvent;
21
use ICanBoogie\Routing\Controller\BeforeActionEvent;
22
use InvalidArgumentException;
23
use JsonException;
24
use Throwable;
25

26
use function get_class;
27
use function ICanBoogie\emit;
28
use function is_array;
29
use function is_callable;
30
use function is_object;
31
use function json_encode;
32
use function trigger_error;
33

34
use const E_USER_WARNING;
35
use const JSON_THROW_ON_ERROR;
36

37
/**
38
 * A route controller.
39
 *
40
 * # Accessing the application's properties
41
 *
42
 * The class tries to retrieve undefined properties from the application, so the following code
43
 * yields the same results:
44
 *
45
 * ```php
46
 * <?php
47
 *
48
 * $this->app->models
49
 * # or
50
 * $this->models
51
 * ```
52
 *
53
 * But because `request` is defined by the controller the following code might not yield the same
54
 * results:
55
 *
56
 * ```php
57
 * <?php
58
 *
59
 * $this->app->request
60
 * # or
61
 * $this->request
62
 * ```
63
 *
64
 * @property-read Request $request The request being dispatched.
65
 * @property-read Route $route The route being dispatched.
66
 * @property Response $response
67
 */
68
abstract class ControllerAbstract extends Prototyped implements Responder
69
{
70
    private Request $request;
71

72
    protected function get_request(): Request
73
    {
74
        return $this->request;
×
75
    }
76

77
    protected function get_route(): Route
78
    {
79
        return $this->request->context->get(Route::class);
×
80
    }
81

82
    protected function lazy_get_response(): Response
83
    {
84
        return new Response(null, ResponseStatus::STATUS_OK, [
×
85

86
            'Content-Type' => 'text/html; charset=utf-8'
×
87

88
        ]);
×
89
    }
90

91
    /**
92
     * Controls the route and returns a response.
93
     *
94
     * The response is obtained by invoking `action()`. When the result is a {@link Response}
95
     * instance it is returned as is, when the `$response` property has been initialized the result
96
     * is used as its body and the response is returned, otherwise the result is returned as is.
97
     *
98
     * The `ICanBoogie\Routing\Controller::action:before` event of class
99
     * {@link Controller\BeforeActionEvent} is fired before invoking `action()`, the
100
     * `ICanBoogie\Routing\Controller::action:before` event of class
101
     * {@link Controller\ActionEvent} is fired after.
102
     *
103
     * @throws Throwable
104
     */
105
    final public function respond(Request $request): Response
106
    {
107
        $this->request = $request;
×
108

109
        $result = null;
×
110

111
        emit(new BeforeActionEvent($this, $result));
×
112

113
        if (!$result) {
×
114
            $result = $this->action($request);
×
115
        }
116

117
        emit(new ActionEvent($this, $result));
×
118

119
        if ($result instanceof Response) {
×
120
            return $result;
×
121
        }
122

123
        if (isset($this->response)) {
×
124
            if ($result !== null) {
×
125
                $this->response->body = $result;
×
126
            }
127

128
            return $this->response;
×
129
        }
130

131
        return new Response($result);
×
132
    }
133

134
    /**
135
     * Performs the proper action for the request.
136
     *
137
     * @return Response|mixed
138
     */
139
    abstract protected function action(Request $request): mixed;
140

141
    /**
142
     * Redirects the request.
143
     *
144
     * @param Route|string $url The URL to redirect the request to.
145
     * @param int $status Status code (defaults to {@link ResponseStatus::STATUS_FOUND}, 302).
146
     * @param array $headers Additional headers.
147
     *
148
     * @return RedirectResponse
149
     */
150
    public function redirect(
151
        Route|string $url,
152
        int $status = ResponseStatus::STATUS_FOUND,
×
153
        array $headers = []
×
154
    ): RedirectResponse {
155
        trigger_error("We need a URL generator here", E_USER_WARNING);
×
156

157
        if ($url instanceof Route) {
×
158
            $url = $url->url;
×
159
        }
160

161
        return new RedirectResponse($url, $status, $headers);
×
162
    }
163

164
    /**
165
     * Forwards the request.
166
     *
167
     * @return mixed
168
     * @throws JsonException
169
     */
170
    public function forward_to(object|array|string $destination) //TODO-202105: Only support Route?
171
    {
172
        if ($destination instanceof Route) {
×
173
            return $this->forward_to_route($destination);
×
174
        }
175

176
        if (is_object($destination)) {
×
177
            $destination = "instance of " . get_class($destination);
×
178
        } elseif (is_array($destination)) {
×
179
            $destination = (string) json_encode($destination, JSON_THROW_ON_ERROR);
×
180
        }
181

182
        throw new InvalidArgumentException("Don't know how to forward to: $destination.");
×
183
    }
184

185
    /**
186
     * Forwards dispatching to another router.
187
     *
188
     * @return Response|mixed
189
     */
190
    protected function forward_to_route(Route $route) //TODO-202105: Return only Response
191
    {
192
        $route->pattern->matches($this->request->uri, $captured);
×
193

194
        $request = $this->request->with([
×
195

196
            'path_params' => $captured
×
197

198
        ]);
×
199

200
        $request->context->route = $route;
×
201

202
        $controller = $route->controller;
×
203

204
        if (!is_callable($controller)) {
×
205
            $controller = new $controller();
×
206
        }
207

208
        return $controller($request);
×
209
    }
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc