Passed
Push — master ( 958054...6a5936 )
by Mihail
02:02
created

path_not_found()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
use Koded\Framework\{HTTPBadRequest, HTTPMethodNotAllowed, HTTPNotFound, HTTPNotImplemented};
4
use Koded\Http\{HttpFactory, ServerResponse};
5
use Koded\Http\Client\ClientFactory;
6
use Koded\Http\Interfaces\{HttpStatus, Request};
7
use Psr\Http\Message\ResponseInterface;
8
use function Koded\Http\create_stream;
9
10
11
function path_not_found(string $path): callable
12
{
13 1
    throw new HTTPNotFound(instance: $path);
14
}
15
16
17
function bad_request(...$args): callable
18
{
19
    throw new HTTPBadRequest(...$args);
20
}
21
22
23
function method_not_allowed(array $allowed): callable
24
{
25 1
    throw new HTTPMethodNotAllowed($allowed);
26
}
27
28
29
function no_app_routes(): callable
30
{
31 1
    throw (new HTTPNotImplemented(
32
        title:  'No Routes',
33
        detail: 'No routes are defined in your application',
34
        type:   'https://kodeart.github.io/koded/routing/'
35
    ))
36 1
        ->setMember('framework', 'Koded Framework')
37 1
        ->setMember('version', get_version());
38
}
39
40
/**
41
 * Creates a responder for HTTP HEAD method.
42
 *
43
 * @param string $uri
44
 * @param array  $methods
45
 * @return callable
46
 */
47
function head_response(string $uri, array $methods): callable
48
{
49
    return function() use ($uri, $methods): ResponseInterface {
50
        $methods || $methods = [Request::HEAD, Request::OPTIONS];
0 ignored issues
show
Bug Best Practice introduced by
The expression $methods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
51
        if (false === \in_array(Request::GET, $methods)) {
52
            return (new ServerResponse)->withHeader('Allow', $methods);
53
        }
54
        $get = (new ClientFactory(
55
            \function_exists('curl_init')
56
                ? ClientFactory::CURL
57
                : ClientFactory::PHP
58
        ))
59
            ->get($uri, ['Connection' => 'close'])
60
            ->timeout(5)
61
            ->read()
62
            ->withoutHeader('Set-Cookie')
63
            ->withHeader('Allow', \join(',', $methods));
64
65
        if ($get->getStatusCode() < HttpStatus::BAD_REQUEST) {
66
            return $get;
67
        }
68
        // If GET request fails, it returns the Allow header
69
        // with the failure reason in the "X-Error-*" headers
70
        \error_log($get->getBody()->getContents());
71
        return $get
72
            ->withHeader('X-Error-Status', \join(' ', [$get->getStatusCode(), $get->getReasonPhrase()]))
73
            ->withHeader('X-Error-Message', \str_replace(["\n", "\r", "\t"], ' ', $get->getBody()))
74
            ->withStatus(HttpStatus::OK)
75
            ->withBody(create_stream(''));
76
    };
77
}
78
79
/**
80
 * Creates a responder for HTTP OPTIONS method.
81
 * This method does not return the Origin header, because it may
82
 * be a CORS request which should be handled by the middleware.
83
 *
84
 * @param array $methods Supported HTTP methods for the URI in question
85
 * @return callable The responder for the OPTIONS method
86
 * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
87
 */
88
function create_options_response(array $methods): callable
89
{
90 6
    return fn(): ResponseInterface => (new HttpFactory)
91 6
        ->createResponse(HttpStatus::NO_CONTENT)
92 6
        ->withHeader('Cache-Control', 'no-cache, max-age=0')
93 6
        ->withHeader('Allow', \join(',', $methods))
94 6
        ->withHeader('Content-Type', 'text/plain');
95
}
96
97
/**
98
 * Maps the HTTP methods to responder (public) methods.
99
 *
100
 * @param callable|object|string $resource
101
 * @return array|callable[]|object[]|string[]
102
 */
103
function map_http_methods(callable|object|string $resource): array
104
{
105 24
    $map = [
106
        Request::HEAD => 'head',
107
        Request::OPTIONS => 'options'
108
    ];
109
    foreach ([
110
                 Request::GET,
111
                 Request::POST,
112
                 Request::PUT,
113
                 Request::PATCH,
114
                 Request::DELETE
115
             ] as $method) {
116 24
        if (\method_exists($resource, $method)) {
117 18
            $map = [$method => \strtolower($method)] + $map;
118 24
        } elseif (\is_callable($resource)) {
119 3
            $map = [$method => $resource] + $map;
120
        }
121
    }
122 24
    return $map;
123
}
124