Passed
Push — master ( 9338c6...9f1043 )
by Dmytro
07:07
created

Http::curlRequestAsync()   C

Complexity

Conditions 11
Paths 192

Size

Total Lines 64
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 3
Metric Value
cc 11
eloc 39
c 5
b 1
f 3
nc 192
nop 4
dl 0
loc 64
rs 5.6302

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Asymptix\web;
4
5
use Asymptix\web\Session;
6
7
/**
8
 * Http protocol functionality and other connected tools.
9
 *
10
 * @category Asymptix PHP Framework
11
 * @author Dmytro Zarezenko <[email protected]>
12
 * @copyright (c) 2009 - 2016, Dmytro Zarezenko
13
 *
14
 * @git https://github.com/Asymptix/Framework
15
 * @license http://opensource.org/licenses/MIT
16
 */
17
class Http {
18
19
    const POST = "POST";
20
    const GET = "GET";
21
22
    /**
23
     * Redirect to the given url.
24
     *
25
     * @param string $url The URL to redirect to.
26
     * @param array<mixed> $params Associative array of query parameters.
27
     * @param bool $session Whether to append session information.
28
     */
29 View Code Duplication
    public static function http_redirect($url, $params = [], $session = false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
30
        $paramsString = "";
31
        foreach ($params as $key => $value) {
32
            $paramsString.= "&" . $key . "=" . $value;
33
        }
34
        if ($session) {
35
            $paramsString.= "&" . session_name() . "=" . session_id();
36
        }
37
        $paramsString = substr($paramsString, 1);
38
        if ($paramsString) {
39
            $paramsString = "?" . $paramsString;
40
        }
41
        header("Location: " . $url . $paramsString);
42
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method http_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
43
    }
44
45
    /**
46
     * Perform HTTP redirect with saving POST params in session.
47
     *
48
     * @param string $url URL redirect to.
49
     * @param array<mixed> $postData List of post params to save.
50
     */
51
    public static function httpRedirect($url = "", $postData = []) {
0 ignored issues
show
Coding Style introduced by
httpRedirect uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
httpRedirect uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
52
        if (preg_match("#^http[s]?://.+#", $url)) { // absolute url
53
            if (function_exists("http_redirect")) {
54
                http_redirect($url);
55
            } else {
56
                self::http_redirect($url);
57
            }
58
        } else { // same domain (relative url)
59
            if (!empty($postData)) {
60
                if (is_array($postData)) {
61
                    if (!Session::exists('_post') || !is_array($_SESSION['_post'])) {
62
                        Session::set('_post', []);
63
                    }
64
65
                    foreach ($postData as $fieldName => $fieldValue) {
66
                        Session::set("_post[{$fieldName}]", serialize($fieldValue));
67
                    }
68
                } else {
69
                    throw new HttpException("Wrong POST data.");
70
                }
71
            }
72
            if (function_exists("http_redirect")) {
73
                http_redirect("http://" . $_SERVER['SERVER_NAME'] . "/" . $url);
74
            } else {
75
                self::http_redirect("http://" . $_SERVER['SERVER_NAME'] . "/" . $url);
76
            }
77
        }
78
    }
79
80
    /**
81
     * Perform HTTP redirect with saving POST params in session.
82
     *
83
     * @param string $url URL redirect to.
84
     * @param array<mixed> $postData List of post params to save.
85
     */
86
    public static function redirect($url = "", $postData = []) {
87
        self::httpRedirect($url, $postData);
88
    }
89
90
    /**
91
     * Returns clients IP-address.
92
     *
93
     * @return string
94
     */
95
    public static function getIP() {
0 ignored issues
show
Coding Style introduced by
getIP uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
96
        if (!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet
97
            return $_SERVER['HTTP_CLIENT_IP'];
98
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy
99
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
100
        }
101
        return $_SERVER['REMOTE_ADDR'];
102
    }
103
104
    /**
105
     * Returns HTTP referrer.
106
     *
107
     * @return string
108
     */
109
    public static function getReferrer() {
0 ignored issues
show
Coding Style introduced by
getReferrer uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
110
        return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "";
111
    }
112
113
    /**
114
     * Gets the address that the provided URL redirects to,
115
     * or FALSE if there's no redirect.
116
     *
117
     * @param string $url URL.
118
     *
119
     * @return mixed String with redirect URL or FALSE if no redirect.
120
     * @throws HttpException
121
     */
122
    public static function getRedirectUrl($url) {
123
        $urlParts = @parse_url($url);
124
        if (!$urlParts) {
125
            return false;
126
        }
127
        if (!isset($urlParts['host'])) { //can't process relative URLs
128
            return false;
129
        }
130
131
        if (!isset($urlParts['path'])) {
132
            $urlParts['path'] = '/';
133
        }
134
135
        $sock = fsockopen($urlParts['host'], (isset($urlParts['port']) ? (int) $urlParts['port'] : 80), $errno, $errstr, 30);
136
        if (!$sock) {
137
            throw new HttpException("$errstr ($errno)");
138
        }
139
140
        $request = "HEAD " . $urlParts['path'] . (isset($urlParts['query']) ? '?' . $urlParts['query'] : '') . " HTTP/1.1\r\n";
141
        $request.= 'Host: ' . $urlParts['host'] . "\r\n";
142
        $request.= "Connection: Close\r\n\r\n";
143
        fwrite($sock, $request);
144
        $response = '';
145
        while (!feof($sock)) {
146
            $response.= fread($sock, 8192);
147
        }
148
        fclose($sock);
149
150
        if (preg_match('/^Location: (.+?)$/m', $response, $matches)) {
151
            if (substr($matches[1], 0, 1) == "/") {
152
                return $urlParts['scheme'] . "://" . $urlParts['host'] . trim($matches[1]);
153
            }
154
            return trim($matches[1]);
155
        }
156
        return false;
157
    }
158
159
    /**
160
     * Follows and collects all redirects, in order, for the given URL.
161
     *
162
     * @param string $url
163
     * @return array
164
     */
165
    public static function getAllRedirects($url) {
166
        $redirects = [];
167
        while ($newurl = self::getRedirectUrl($url)) {
168
            if (in_array($newurl, $redirects)) {
169
                break;
170
            }
171
            $redirects[] = $newurl;
172
            $url = $newurl;
173
        }
174
175
        return $redirects;
176
    }
177
178
    /**
179
     * Gets the address that the URL ultimately leads to.
180
     * Returns $url itself if it isn't a redirect.
181
     *
182
     * @param string $url
183
     * @return string
184
     */
185
    public static function getFinalUrl($url) {
186
        $redirects = self::getAllRedirects($url);
187
        if (count($redirects) > 0) {
188
            return array_pop($redirects);
189
        }
190
        return $url;
191
    }
192
193
    /**
194
     * Executes CURL async request.
195
     *
196
     * @param string $url URL.
197
     * @param array $params List of request params.
198
     * @param string $type Type of the request (GET, POST, ...).
199
     * @param int $timeout Timeout in seconds.
200
     *
201
     * @return type
202
     */
203
    public static function curlRequestAsync($url, $params, $type = self::POST, $timeout = 30) {
204
        $postParams = [];
205
        foreach ($params as $key => &$val) {
206
            if (is_array($val)) {
207
                $val = implode(',', $val);
208
            }
209
            $postParams[] = $key . '=' . urlencode($val);
210
        }
211
        $postString = implode('&', $postParams);
212
213
        $parts = parse_url($url);
214
215
        $port = isset($parts['port']) ? (int)$parts['port'] : 80;
216
217
        $sock = fsockopen($parts['host'], $port, $errno, $errstr, $timeout);
218
219
        // Data goes in the path for a GET request
220
        if ($type == self::GET) {
221
            $parts['path'].= '?' . $postString;
222
        }
223
224
        $request = "$type " . $parts['path'] . " HTTP/1.1\r\n";
225
        $request.= "Host: " . $parts['host'] . "\r\n";
226
227
        if ($type == self::POST) {
228
            $request.= "Content-Type: application/x-www-form-urlencoded\r\n";
229
            $request.= "Content-Length: " . strlen($postString) . "\r\n";
230
        }
231
        $request.= "Connection: Close\r\n";
232
        $request.= "\r\n";
233
234
        // Data goes in the request body for a POST request
235
        if ($type == self::POST && isset($postString)) {
236
            $request.= $postString;
237
        }
238
239
        fwrite($sock, $request);
240
241
        $response = "";
242
        while (!feof($sock) && $result = fgets($sock)) {
243
            $response.= $result;
244
        }
245
246
        fclose($sock);
247
248
        list($respHeader, $respBody) = preg_split("/\R\R/", $response, 2);
249
250
        $headers = array_map(['self', "pair"], explode("\r\n", $respHeader));
251
        $headerList = [];
252
        foreach ($headers as $value) {
253
            $headerList[$value['key']] = $value['value'];
254
        }
255
256
        return [
257
            'request' => $request,
258
            'response' => [
259
                'header' => $respHeader,
260
                'headerList' => $headerList,
261
                'body' => trim(http_chunked_decode($respBody))
262
            ],
263
            'errno' => $errno,
264
            'errstr' => $errstr
265
        ];
266
    }
267
268
    /**
269
     * Force HTTP status code.
270
     *
271
     * @param int $code Status code.
272
     * @param string $path Include path if needed.
273
     *
274
     * @throws HttpException If invalid HTTP status provided.
275
     */
276
    public static function forceHttpStatus($code, $path = null) {
277
        switch ($code) {
278
            //TODO: implement other statuses
279
            case (404):
280
                header('HTTP/1.0 404 Not Found', true, 404);
281
                break;
282
            default:
283
                throw new HttpException("Invalid HTTP status code '" . $code . "'");
284
        }
285
        if (!is_null($path)) {
286
            include($path);
287
        }
288
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method forceHttpStatus() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
289
    }
290
291
    /**
292
     * Magic methods:
293
     *    force404($path = null) - force HTTP status codes.
294
     *
295
     * @param string $name The name of the method being called.
296
     * @param type $arguments Enumerated array containing the parameters passed
297
     *           to the method.
298
     * @return mixed
299
     *
300
     * @throws HttpException If invalid method name provided.
301
     */
302
    public static function __callStatic($name, $arguments) {
303
        if (substr($name, 0, 5) === "force") {
304
            $code = (int)substr($name, 5);
305
            $path = isset($arguments[0]) ? $arguments[0] : null;
306
307
            return self::forceHttpStatus($code, $path);
308
        }
309
        throw new HttpException("Invalid HTTP class method '" . $name . "'");
310
    }
311
312
}
313
314
/**
315
 * Service exception class.
316
 */
317
class HttpException extends \Exception {}
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
318