WebAccessCUrl   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 16
eloc 71
dl 0
loc 123
rs 10
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
C getContent() 0 108 16
1
<?php
2
3
/**
4
 * @copyright Shaarli Community under zlib license https://github.com/shaarli/Shaarli
5
 *
6
 * ZLIB/LIBPNG LICENSE
7
 *
8
 * This software is provided 'as-is', without any express or implied warranty.
9
 * In no event will the authors be held liable for any damages arising from
10
 * the use of this software.
11
 *
12
 * Permission is granted to anyone to use this software for any purpose,
13
 * including commercial applications, and to alter it and redistribute it
14
 * freely, subject to the following restrictions:
15
 *
16
 * 1. The origin of this software must not be misrepresented; you must not
17
 * claim that you wrote the original software. If you use this software
18
 * in a product, an acknowledgment in the product documentation would
19
 * be appreciated but is not required.
20
 *
21
 * 2. Altered source versions must be plainly marked as such, and must
22
 * not be misrepresented as being the original software.
23
 *
24
 * 3. This notice may not be removed or altered from any source distribution.
25
 */
26
27
declare(strict_types=1);
28
29
namespace WebThumbnailer\Application\WebAccess;
30
31
use WebThumbnailer\Application\ConfigManager;
32
33
/**
34
 * Require php-curl
35
 */
36
class WebAccessCUrl implements WebAccess
37
{
38
    /**
39
     * Download content using cURL.
40
     *
41
     * @see https://secure.php.net/manual/en/ref.curl.php
42
     * @see https://secure.php.net/manual/en/functions.anonymous.php
43
     * @see https://secure.php.net/manual/en/function.preg-split.php
44
     * @see https://secure.php.net/manual/en/function.explode.php
45
     * @see http://stackoverflow.com/q/17641073
46
     * @see http://stackoverflow.com/q/9183178
47
     * @see http://stackoverflow.com/q/1462720
48
     *
49
     * @inheritdoc
50
     */
51
    public function getContent(
52
        string $url,
53
        ?int $timeout = null,
54
        ?int $maxBytes = null,
55
        ?callable $dlCallback = null,
56
        ?string &$dlContent = null
57
    ): array {
58
        if (empty($timeout)) {
59
            $timeout = ConfigManager::get('settings.default.timeout', 30);
60
        }
61
62
        if (empty($maxBytes)) {
63
            $maxBytes = ConfigManager::get('settings.default.max_img_dl', 16777216);
64
        }
65
66
        $locale = setlocale(LC_COLLATE, '0');
67
        $cookie = ConfigManager::get('settings.path.cache') . '/cookie.txt';
68
        $userAgent =
69
            'Mozilla/5.0 (X11; Linux x86_64; rv:45.0; WebThumbnailer)'
70
            . ' Gecko/20100101 Firefox/45.0';
71
        $acceptLanguage =
72
            substr($locale ?: 'en', 0, 2) . ',en-US;q=0.7,en;q=0.3';
73
        $maxRedirs = 6;
74
75
        $ch = curl_init($url);
76
        if ($ch === false) {
77
            return [[0 => 'curl_init() error'], false];
78
        }
79
80
        // General cURL settings
81
        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
82
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
83
        curl_setopt($ch, CURLOPT_HEADER, true);
84
        curl_setopt(
85
            $ch,
86
            CURLOPT_HTTPHEADER,
87
            ['Accept-Language: ' . $acceptLanguage]
88
        );
89
        curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs);
90
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
91
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
92
        curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
93
94
        curl_setopt($ch, CURLOPT_COOKIESESSION, true);
95
        curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
96
        curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
97
98
        // Max download size management
99
        curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024 * 16);
100
        curl_setopt($ch, CURLOPT_NOPROGRESS, false);
101
        curl_setopt(
102
            $ch,
103
            CURLOPT_PROGRESSFUNCTION,
104
            function ($arg0, $arg1, $arg2) use ($maxBytes) {
105
                $downloaded = $arg2;
106
                // Non-zero return stops downloading
107
                return ($downloaded > $maxBytes) ? 1 : 0;
108
            }
109
        );
110
111
        if (is_callable($dlCallback)) {
112
            curl_setopt($ch, CURLOPT_WRITEFUNCTION, $dlCallback);
113
            curl_exec($ch);
114
            $response = $dlContent;
115
        } else {
116
            $response = curl_exec($ch);
117
        }
118
119
        $errorNo = curl_errno($ch);
120
        $errorStr = curl_error($ch);
121
        $headSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
122
        curl_close($ch);
123
124
        if (!is_string($response)) {
125
            return [[0 => 'curl_exec() error #' . $errorNo . ': ' . $errorStr], false];
126
        }
127
128
        // Formatting output like the fallback method
129
        $rawHeaders = substr($response, 0, $headSize);
130
131
        // Keep only headers from latest redirection
132
        $rawHeadersArrayRedirs = explode("\r\n\r\n", trim($rawHeaders));
133
        $rawHeadersLastRedir = end($rawHeadersArrayRedirs);
134
135
        $content = substr($response, $headSize);
136
        $headers = [];
137
        foreach (preg_split('~[\r\n]+~', $rawHeadersLastRedir ?: '') ?: [] as $line) {
138
            if (empty($line) or ctype_space($line)) {
139
                continue;
140
            }
141
            $splitLine = explode(': ', $line, 2);
142
            if (count($splitLine) > 1) {
143
                $key = $splitLine[0];
144
                $value = $splitLine[1];
145
                if (array_key_exists($key, $headers)) {
146
                    if (!is_array($headers[$key])) {
147
                        $headers[$key] = array(0 => $headers[$key]);
148
                    }
149
                    $headers[$key][] = $value;
150
                } else {
151
                    $headers[$key] = $value;
152
                }
153
            } else {
154
                $headers[] = $splitLine[0];
155
            }
156
        }
157
158
        return [$headers, $content];
159
    }
160
}
161