Passed
Branch main (0bd566)
by Sammy
11:44 queued 09:43
created

Hopper   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 85
dl 0
loc 263
rs 8.96
c 0
b 0
f 0
wmc 43

26 Methods

Rating   Name   Duplication   Size   Complexity  
A name() 0 3 1
A stay() 0 3 1
A webRoot() 0 3 1
A hop() 0 13 4
A hopBack() 0 7 2
A referer() 0 7 3
A sendFile() 0 29 2
A requests() 0 3 1
A params() 0 3 1
A __debugInfo() 0 7 1
A submitted() 0 3 1
A namedRoutes() 0 3 1
A target() 0 3 1
A hopURL() 0 6 1
A targetController() 0 3 1
A match() 0 18 5
A hyp() 0 9 2
A routeExists() 0 3 1
A filePath() 0 7 2
A extractFrom() 0 20 4
A mapHomeRoute() 0 3 1
A webHost() 0 3 1
A targetMethod() 0 3 1
A __construct() 0 5 1
A submits() 0 3 1
A basePath() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Hopper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Hopper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
* huppel konijntje huppel and wiebel
5
* Hommage to Grace Hopper, programmer & expert in *litteral* duck taping
6
***/
7
8
namespace HexMakina\Hopper;
9
10
class Hopper extends \AltoRouter implements \HexMakina\BlackBox\RouterInterface
11
{
12
    private $match = null;
13
    private $file_root = null;
14
15
    public function __construct($route_home, $web_base, $file_root)
16
    {
17
      $this->mapHomeRoute($route_home);
18
      $this->basePath($web_base);
19
      $this->filePath($file_root);
20
    }
21
22
  //----------------------------------------------------------- INITIALISATION
23
24
    public function mapHomeRoute($route)
25
    {
26
        $this->map(self::REQUEST_GET, '', $route, self::ROUTE_HOME_NAME);
27
    }
28
29
    public function __debugInfo(): array
30
    {
31
        $dbg = get_object_vars($this);
32
        $dbg['routes'] = count($dbg['routes']);
33
        $dbg['namedRoutes'] = count($dbg['namedRoutes']);
34
        unset($dbg['matchTypes']);
35
        return $dbg;
36
    }
37
  // -- MATCHING REQUESTS
38
    public function match($requestUrl = null, $requestMethod = null)
39
    {
40
        $this->match = parent::match($requestUrl, $requestMethod);
41
42
        if ($this->match === false) {
43
            throw new RouterException('ROUTE_MATCH_FALSE');
44
        }
45
46
        $res = explode('::', $this->target());
47
48
        if ($res === false || !isset($res[1]) || isset($res[2])) {
49
            throw new RouterException('INVALID_TARGET_FORMAT');
50
        }
51
52
        $this->match['target_controller'] = $res[0];
53
        $this->match['target_method'] = $res[1];
54
55
        return [$res[0], $res[1]];
56
    }
57
58
    public function params($param_name = null)
59
    {
60
        return $this->extractFrom($this->match['params'] ?? [], $param_name);
61
    }
62
63
    public function submitted($param_name = null)
64
    {
65
        return $this->extractFrom($_POST, $param_name);
66
    }
67
68
69
70
    public function target()
71
    {
72
        return $this->match['target'];
73
    }
74
75
    public function targetController()
76
    {
77
        return $this->match['target_controller'];
78
    }
79
80
    public function targetMethod()
81
    {
82
        return $this->match['target_method'];
83
    }
84
85
    public function name()
86
    {
87
        return $this->match['name'];
88
    }
89
90
  // -- ROUTING TOOLS
91
    public function routeExists($route): bool
92
    {
93
        return isset($this->namedRoutes[$route]);
94
    }
95
96
    public function namedRoutes()
97
    {
98
        return $this->namedRoutes;
99
    }
100
101
  /* Generates HYPertext reference
102
   * @param route_name string  requires
103
   *  - a valid AltoRouter route name
104
   *  - OR a Descendant of Model
105
   * @route_params requires
106
   *  - an assoc_array of url params (strongly AltoRouter-based)
107
   * returns: something to put in a href="", action="" or header('Location:');
108
   */
109
    public function hyp($route, $route_params = [])
110
    {
111
        try {
112
            $url = $this->generate($route, $route_params);
113
        } catch (\Exception $e) {
114
            $url = $this->hyp(self::ROUTE_HOME_NAME);
115
        }
116
117
        return $url;
118
    }
119
120
121
122
  /*
123
   * @params $route is
124
   *    - empty: default is ROUTE_HOME_NAME
125
   *    - an existing route name: make url with optional [$route_params])
126
   *    - a url, go there
127
   * @params $route_params, assoc_data for url creation (i:id, a:format, ..)
128
   */
129
    public function hop($route = null, $route_params = [])
130
    {
131
        $url = null;
132
133
        if (is_null($route)) {
134
            $url = $this->hyp(self::ROUTE_HOME_NAME, $route_params);
135
        } elseif (is_string($route) && $this->routeExists($route)) {
136
            $url = $this->hyp($route, $route_params);
137
        } else {
138
            $url = $route;
139
        }
140
141
        $this->hopURL($url);
142
    }
143
144
    public function stay($url = null)
145
    {
146
        return $url ?? $_SERVER['REQUEST_URI'];
147
    }
148
149
  // hops back to previous page (referer()), or home if no referer
150
    public function hopBack()
151
    {
152
        if (!is_null($back = $this->referer())) {
153
            $this->hopURL($back);
154
        }
155
156
        $this->hop();
157
    }
158
159
    public function hopURL($url)
160
    {
161
        header('Cache-Control: no-cache, must-revalidate');
162
        header('Expires: Mon, 01 Jan 1970 00:00:00 GMT');
163
        header('Location: ' . $url);
164
        exit();
165
    }
166
167
  // returns full URL of the refering URL
168
  // returns null if same as current URL (prevents endless redirection loop)
169
    public function referer()
170
    {
171
        if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] != $this->webHost() . $_SERVER['REQUEST_URI']) {
172
            return $_SERVER['HTTP_REFERER'];
173
        }
174
175
        return null;
176
    }
177
178
    public function sendFile($file_path)
179
    {
180
        if (!file_exists($file_path)) {
181
            throw new RouterException('SENDING_NON_EXISTING_FILE');
182
        }
183
184
        $file_name = basename($file_path);
185
186
      //Get file type and set it as Content Type
187
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
188
189
        header('Content-Type: ' . finfo_file($finfo, $file_path));
190
191
        finfo_close($finfo);
192
193
      //Use Content-Disposition: attachment to specify the filename
194
        header('Content-Disposition: attachment; filename=' . $file_name);
195
196
      //No cache
197
        header('Expires: 0');
198
        header('Cache-Control: must-revalidate');
199
        header('Pragma: public');
200
201
      //Define file size
202
        header('Content-Length: ' . filesize($file_path));
203
204
        ob_clean();
205
        flush();
206
        readfile($file_path);
207
        // die; // might be useless after all
208
    }
209
210
  // -- PROCESSING REQUESTS
211
    public function requests(): bool
212
    {
213
        return $_SERVER['REQUEST_METHOD'] === self::REQUEST_GET;
214
    }
215
216
    public function submits(): bool
217
    {
218
        return $_SERVER['REQUEST_METHOD'] === self::REQUEST_POST;
219
    }
220
221
    public function webHost(): string
222
    {
223
        return $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'];
224
    }
225
226
    public function webRoot(): string
227
    {
228
        return $this->webHost() . $this->basePath();
229
    }
230
231
    // return web base
232
    public function basePath($setter = null): string
233
    {
234
        if (!is_null($setter)) {
235
            $this->basePath = $setter;
236
        }
237
238
        return $this->basePath ?? '';
239
    }
240
241
    // returns root filepath for project
242
    // default out of vendor/hexmakina/Hopper
243
    public function filePath($setter = null): string
244
    {
245
        if (!is_null($setter)) {
246
            $this->file_root = realpath($setter) . '/';
247
        }
248
249
        return $this->file_root ?? __DIR__ . '/../../';
250
    }
251
252
253
    private function extractFrom($dat_ass, $key = null)
254
    {
255
256
      // $key is null, returns $dat_ass or empty array
257
        if (is_null($key)) {
258
            return $dat_ass ?? [];
259
        }
260
261
      // $dat_ass[$key] not set, returns null
262
        if (!isset($dat_ass[$key])) {
263
            return null;
264
        }
265
266
      // $dat_ass[$key] is a string, returns decoded value
267
        if (is_string($dat_ass[$key])) {
268
            return urldecode($dat_ass[$key]);
269
        }
270
271
      // $dat_ass[$key] is not a string, return match[$key]
272
        return $dat_ass[$key];
273
    }
274
}
275