GenerateFunction::generateDefault()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\SwitchRoute\Generator;
6
7
use Jasny\SwitchRoute\Endpoint;
8
use Jasny\SwitchRoute\InvalidRouteException;
9
use Jasny\SwitchRoute\Invoker;
10
use Jasny\SwitchRoute\InvokerInterface;
11
use ReflectionException;
12
13
/**
14
 * Generate a function that invokes an action based on the route.
15
 */
16
class GenerateFunction extends AbstractGenerate
17
{
18
    protected InvokerInterface $invoker;
19
20
    /**
21
     * GenerateScript constructor.
22
     */
23 8
    public function __construct(InvokerInterface $invoker = null)
24
    {
25 8
        $this->invoker = $invoker ?? new Invoker();
26
    }
27
28
    /**
29
     * Invoke code generation.
30
     *
31
     * @param string $name       Function name
32
     * @param array  $routes     Ignored
33
     * @param array  $structure
34
     * @return string
35
     */
36 6
    public function __invoke(string $name, array $routes, array $structure): string
37
    {
38 6
        $default = $structure["\e"] ?? null;
39 6
        unset($structure["\e"]);
40
41 6
        $switchCode = self::indent($this->generateSwitch($structure));
42 4
        $defaultCode = self::indent($this->generateDefault($default));
43
44 4
        [$namespace, $function] = $this->generateNs($name);
45
46 4
        return <<<CODE
47 4
<?php
48
49
declare(strict_types=1);
50 4
{$namespace}
51
/**
52
 * This function is generated by SwitchRoute.
53
 * Do not modify it manually. Any changes will be overwritten.
54
 */
55 4
function {$function}(string \$method, string \$path, ?callable \$instantiate = null)
56
{
57
    \$segments = \$path === "/" ? [] : explode("/", trim(\$path, "/"));
58
    \$allowedMethods = [];
59
60 4
$switchCode
61
62 4
$defaultCode
63
}
64 4
CODE;
65
    }
66
67
    /**
68
     * Generate code for an endpoint
69
     */
70 7
    protected function generateEndpoint(Endpoint $endpoint): string
71
    {
72 7
        $exportValue = function ($var): string {
73 6
            return var_export($var, true);
74 7
        };
75
76 7
        return join("\n", [
77 7
            "\$allowedMethods = [" . join(', ', array_map($exportValue, $endpoint->getAllowedMethods())) . "];",
78 7
            parent::generateEndpoint($endpoint)
79 7
        ]);
80
    }
81
82
    /**
83
     * Generate routing code for an endpoint.
84
     *
85
     * @throws InvalidRouteException
86
     */
87 7
    protected function generateRoute(string $key, array $route, array $vars, ?callable $genArg = null): string
88
    {
89 7
        if (!isset($route['include']) && !isset($route['controller']) && !isset($route['action'])) {
90 1
            throw new InvalidRouteException("Route for '$key' should specify 'include', 'controller', " .
91 1
                "or 'action'");
92
        }
93
94 6
        if (isset($route['include'])) {
95 1
            return "return require '" . addslashes($route['include']) . "';";
96
        }
97
98
        try {
99 6
            $genArg = $genArg ?? function (?string $name, ?string $type = null, $default = null) use ($vars): string {
100 2
                return $this->genArg($vars, $name, $type, $default);
101 6
            };
102 6
            $new = '(isset($instantiate) ? ($instantiate)(\'%1$s\') : new \\%1$s)';
103
104 6
            $invocation = $this->invoker->generateInvocation($route, $genArg, $new);
105 2
        } catch (ReflectionException $exception) {
106 2
            throw new InvalidRouteException("Invalid route for '$key'. " . $exception->getMessage(), 0, $exception);
107
        }
108
109 4
        return "return $invocation;";
110
    }
111
112
    /**
113
     * Generate code for when no route matches.
114
     *
115
     * @throws InvalidRouteException
116
     */
117 5
    protected function generateDefault(?Endpoint $endpoint): string
118
    {
119 5
        if ($endpoint === null) {
120 4
            return $this->invoker->generateDefault();
121
        }
122
123 1
        $genArg = function ($name, $type = null, $default = null) {
124 1
            return "\${$name} ?? " . var_export($default, true);
125 1
        };
126
127 1
        return $this->generateRoute('default', $endpoint->getRoutes()[''], [], $genArg);
128
    }
129
130
    /**
131
     * Generate code for argument using path vars.
132
     */
133 3
    protected function genArg(array $vars, ?string $name, ?string $type = null, mixed $default = null): string
134
    {
135 3
        if ($name === null) {
136 2
            $fnMap = function ($name, $pos) {
137 2
                return sprintf('"%s" => $segments[%d]', addslashes($name), $pos);
138 2
            };
139
140 2
            return '[' . join(', ', array_map($fnMap, array_keys($vars), $vars)) . ']';
141
        }
142
143 2
        return isset($vars[$name]) ? "\$segments[{$vars[$name]}]" : var_export($default, true);
144
    }
145
}
146