Test Failed
Push — trunk ( e02d87...314b8c )
by SuperNova.WS
07:20
created

HttpUrl::isSigned()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 13
rs 10
c 1
b 0
f 0
1
<?php
2
/**
3
 * Created by Gorlum 13.02.2020 10:46
4
 */
5
6
namespace Core;
7
8
9
class HttpUrl {
10
  const SCHEME_HTTPS = 'https';
11
  const SCHEME_HTTP = 'http';
12
13
  const FIELD_SIGNATURE = 'sign';
14
  const FIELD_TIMESTAMP_MICRO = '_sn_ts_micro';
15
16
  /**
17
   * @var GlobalContainer $gc
18
   */
19
  public $gc;
20
21
  /**
22
   * Scheme - `http` or `https`
23
   *
24
   * @var string $scheme
25
   */
26
  public $scheme = '';
27
  /**
28
   * Host address
29
   *
30
   * @var string $host
31
   */
32
  public $host = '';
33
  /**
34
   * Path from server root WITH starting slash
35
   *
36
   * @var string $path
37
   */
38
  public $path = '';
39
  /**
40
   * [(str)$paramName => [(mixed)$paramValue, ...], ...]
41
   *
42
   * @var string[][]
43
   */
44
  public $params = [];
45
  /**
46
   * Secure string known only to server for internal call exchange
47
   *
48
   * @var string $cypher
49
   */
50
  public $cypher = '';
51
52
  /**
53
   * A bit of syntax sugar
54
   *
55
   * @param GlobalContainer $gc
56
   *
57
   * @return static
58
   */
59
  public static function spawn(GlobalContainer $gc) {
60
    return new static($gc);
61
  }
62
63
  public function __construct(GlobalContainer $gc) {
64
    $this->gc = $gc;
65
  }
66
67
  public function parseUrl($url) {
68
    $parsed = explode('://', $url);
69
70
    if (is_array($parsed) && count($parsed) > 1 && strtolower($parsed[0]) === self::SCHEME_HTTPS) {
71
      $this->scheme = self::SCHEME_HTTPS;
72
    } else {
73
      $this->scheme = self::SCHEME_HTTP;
74
    }
75
76
    if (is_array($parsed)) {
77
      array_shift($parsed);
78
    } else {
79
      $parsed = [];
80
    }
81
82
    $server = explode('/', implode('://', $parsed));
83
    if (is_array($server) && !empty($server)) {
84
      $this->host = $server[0];
85
      array_shift($server);
86
    } else {
87
      $this->host = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $host.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
88
    }
89
90
    return $this->explodeUri(implode('/', $server));
91
  }
92
93
  /**
94
   * Fills URI from current URL
95
   *
96
   * @return $this
97
   */
98
  public function fillCurrent() {
99
    if (
100
      (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on')
101
      ||
102
      (!empty($_SERVER['REQUEST_SCHEME']) && strtolower($_SERVER['REQUEST_SCHEME']) === self::SCHEME_HTTPS)
103
    ) {
104
      $this->scheme = self::SCHEME_HTTPS;
105
    } else {
106
      $this->scheme = self::SCHEME_HTTP;
107
    }
108
109
    $this->host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
110
111
    $this->path   = '';
112
    $this->params = [];
113
114
    return $this->explodeUri($_SERVER['REQUEST_URI']);
115
  }
116
117
  /**
118
   * Return server root - i.e. host with scheme like http://localhost/
119
   * DO NOT adds trailing slash
120
   *
121
   * @return string
122
   */
123
  public function serverRoot() {
124
    return "{$this->scheme}://{$this->host}";
125
  }
126
127
  public function url() {
128
    return $this->serverRoot() . $this->uri();
129
  }
130
131
  public function uri() {
132
    return $this->path . $this->renderGetParams();
133
  }
134
135
  /**
136
   * @param bool $timeStamp
137
   *
138
   * @return string
139
   */
140
  public function urlSigned($timeStamp = true) {
141
    if ($timeStamp) {
142
      $this->params[self::FIELD_TIMESTAMP_MICRO] = SN_TIME_MICRO;
143
    }
144
145
    $this->removeSignature();
146
147
    $signature = $this->gc->crypto->sign($this->url() . $this->cypher);
148
149
    $this->params[self::FIELD_SIGNATURE] = $signature;
150
151
    return $this->url();
152
  }
153
154
  /**
155
   * Checks signature in URL
156
   *
157
   * @return bool
158
   */
159
  public function isSigned() {
160
    $isMatched = false;
161
162
    $signatureExists = $this->isSignaturePresent();
163
    if ($signatureExists) {
164
      $signature = $this->removeSignature();
165
      // Checking signature
166
      $isMatched = $this->gc->crypto->signCheck($this->url() . $this->cypher, $signature);
167
      // Re-adding signature to URL
168
      $this->params[self::FIELD_SIGNATURE] = $signature;
169
    }
170
171
    return $isMatched;
172
  }
173
174
175
  /**
176
   * Explodes provided URI to path and params
177
   *
178
   * @param string $uri URI part of URL - i.e. path from server root with params
179
   *
180
   * @return $this
181
   */
182
  protected function explodeUri($uri) {
183
    if (!is_string($uri) || empty($uri)) {
0 ignored issues
show
introduced by
The condition is_string($uri) is always true.
Loading history...
184
      return $this;
185
    }
186
187
    $exploded   = explode('?', $uri);
188
    $this->path = array_shift($exploded);
189
    if (substr($this->path, 0, 1) !== '/') {
190
      $this->path = '/' . $this->path;
191
    }
192
193
    // Re-imploding left URI parts for a case of malformed request
194
    $this->params = $this->parseGetParams(implode('?', $exploded));
195
196
    return $this;
197
  }
198
199
  /**
200
   * @param string $paramString
201
   *
202
   * @return array
203
   */
204
  protected function parseGetParams($paramString) {
205
    $result = [];
206
207
    if (!is_string($paramString) || empty($paramString)) {
0 ignored issues
show
introduced by
The condition is_string($paramString) is always true.
Loading history...
208
      return $result;
209
    }
210
211
    $params = explode('&', $paramString);
212
    if (!is_array($params) || empty($params)) {
0 ignored issues
show
introduced by
The condition is_array($params) is always true.
Loading history...
213
      return $result;
214
    }
215
216
    foreach ($params as $paramString) {
0 ignored issues
show
introduced by
$paramString is overwriting one of the parameters of this function.
Loading history...
217
      $parsed = explode('=', $paramString);
218
219
      $paramName  = array_shift($parsed);
220
      $paramValue = implode('&', $parsed);
221
222
      // TODO - indexed params support
223
      $result[$paramName] = $paramValue;
224
    }
225
226
    return $result;
227
  }
228
229
  protected function renderGetParams() {
230
    $result = '';
231
    if (empty($this->params) || !is_array($this->params)) {
232
      return $result;
233
    }
234
235
    $query = [];
236
    foreach ($this->params as $paramName => $paramValue) {
237
      $query[] = urlencode($paramName) . '=' . urlencode($paramValue);
0 ignored issues
show
Bug introduced by
$paramValue of type string[] is incompatible with the type string expected by parameter $str of urlencode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

237
      $query[] = urlencode($paramName) . '=' . urlencode(/** @scrutinizer ignore-type */ $paramValue);
Loading history...
238
    }
239
240
    return '?' . implode('&', $query);
241
  }
242
243
  public function removeSignature() {
244
    $result = '';
245
    if ($this->isSignaturePresent()) {
246
      $result = $this->params[self::FIELD_SIGNATURE];
247
      unset($this->params[self::FIELD_SIGNATURE]);
248
    }
249
250
    return $result;
251
  }
252
253
  public function setCypher($cypher) {
254
    $this->cypher = $cypher;
255
256
    return $this;
257
  }
258
259
  /**
260
   * @param array|string $params
261
   *
262
   * @return HttpUrl
263
   */
264
  public function addParams($params) {
265
    if (is_string($params)) {
266
      $params = $this->parseGetParams($params);
267
    }
268
269
    if (is_array($params) && !empty($params)) {
270
      foreach ($params as $paramName => $value) {
271
        $this->params[$paramName] = $value;
272
      }
273
    }
274
275
    return $this;
276
  }
277
278
  public function addPath($path) {
279
    if (substr($this->path, -1) !== '/') {
280
      $this->path .= '/';
281
    }
282
    $this->path .= $path;
283
284
    return $this;
285
  }
286
287
  /**
288
   * @return bool
289
   */
290
  public function isSignaturePresent() {
291
    return array_key_exists(self::FIELD_SIGNATURE, $this->params);
292
  }
293
294
}
295