GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Imageoptim::setUsername()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Image\Optimizers;
15
16
// ------------------------------------------------------------------------
17
18
use O2System\Spl\Exceptions\RuntimeException;
19
20
/**
21
 * Class Imageoptim
22
 * @package O2System\Image\Optimizers
23
 */
24
class Imageoptim
25
{
26
    /**
27
     * Imageoptim::$apiUrl
28
     *
29
     * @var string
30
     */
31
    private $apiUrl = 'https://img.gs';
32
33
    /**
34
     * Imageoptim::$apiKey
35
     *
36
     * @var string
37
     */
38
    private $username;
39
40
    // ------------------------------------------------------------------------
41
42
    /**
43
     * Imageoptim::__construct
44
     *
45
     * @param string|null $username
46
     */
47
    public function __construct($username = null)
48
    {
49
        if (isset($username)) {
50
            $this->setUsername($username);
51
        }
52
    }
53
54
    // ------------------------------------------------------------------------
55
56
    /**
57
     * Imageoptim::setApiKey
58
     *
59
     * @param string $username
60
     *
61
     * @return static
62
     */
63
    public function setUsername($username)
64
    {
65
        $this->username = (string)$username;
66
67
        return $this;
68
    }
69
70
    // ------------------------------------------------------------------------
71
72
    /**
73
     * Imageoptim::optimize
74
     *
75
     * @param string $image
76
     * @param string|null
77
     *
78
     * @return string
79
     * @throws \Exception
80
     */
81
    public function optimize($image, $option = null)
82
    {
83
        // optimize: image optimization in the same format
84
        // webp: converts the image to the WebP image format
85
        if ($option === null) {
86
            $option = 'optimize';
87
        }
88
89
        $callApiUrl = $this->apiUrl . '/' . $this->username . '/' . $option;
90
91
        $blob = null;
92
        if (strpos($image, 'http')) {
93
            $blob = $this->fromUrl($image, $callApiUrl);
94
        } elseif (is_file($image)) {
95
            $blob = $this->fromFile($image, $callApiUrl);
96
        }
97
98
        return $blob;
99
    }
100
101
    protected function fromUrl($image, $callApiUrl)
102
    {
103
        $headers = [
104
            'User-Agent: Imageoptim-API',
105
            'Accept: image/*',
106
        ];
107
108
        $handle = curl_init();
109
        curl_setopt_array($handle, [
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $ch of curl_setopt_array() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

109
        curl_setopt_array(/** @scrutinizer ignore-type */ $handle, [
Loading history...
110
            CURLOPT_URL            => $callApiUrl . '/' . $image,
111
            CURLOPT_HTTPHEADER     => $headers,
112
            CURLOPT_BINARYTRANSFER => true,
113
            CURLOPT_RETURNTRANSFER => true,
114
            CURLOPT_HEADER         => true,
115
            CURLOPT_SSL_VERIFYPEER => true,
116
        ]);
117
118
        $response = curl_exec($handle);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

118
        $response = curl_exec(/** @scrutinizer ignore-type */ $handle);
Loading history...
119
        $curlError = curl_error($handle);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $ch of curl_error() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

119
        $curlError = curl_error(/** @scrutinizer ignore-type */ $handle);
Loading history...
120
        $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
0 ignored issues
show
Bug introduced by
It seems like $handle can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

120
        $header_size = curl_getinfo(/** @scrutinizer ignore-type */ $handle, CURLINFO_HEADER_SIZE);
Loading history...
121
        $body = substr($response, $header_size);
122
        // error catching
123
        if ( ! empty($curlError) || empty($body)) {
124
            throw new RuntimeException("Imageoptim-Error: {$curlError}, Output: {$body}");
125
        }
126
127
        return $body;
128
    }
129
130
    protected function fromFile($image, $callApiUrl)
131
    {
132
        $fileData = @file_get_contents($image);
133
        $contentHash = md5($image);
134
        $boundary = "XXX$contentHash";
135
        $nameEscaped = addslashes(basename($image));
136
137
        $options[ 'content' ] = "--$boundary\r\n" .
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
138
            "Content-Disposition: form-data; name=\"file\"; filename=\"{$nameEscaped}\"\r\n" .
139
            "Content-Type: application/octet-stream\r\n" .
140
            "Content-Transfer-Encoding: binary\r\n" .
141
            "\r\n$fileData\r\n--$boundary--";
142
143
        $options[ 'header' ] =
144
            "Accept: image/*,application/im2+json\r\n" .
145
            "User-Agent: ImageOptim-php/1.1 PHP/" . phpversion() .
146
            "Content-Length: " . strlen($options[ 'content' ]) . "\r\n" .
147
            "Content-MD5: $contentHash\r\n" .
148
            "Content-Type: multipart/form-data, boundary=$boundary\r\n";
149
150
        $options[ 'timeout' ] = 30;
151
        $options[ 'ignore_errors' ] = true;
152
        $options[ 'method' ] = 'POST';
153
154
        $stream = @fopen($callApiUrl, 'r', false, stream_context_create(['http' => $options]));
155
        if ( ! $stream) {
0 ignored issues
show
introduced by
$stream is of type false|resource, thus it always evaluated to false.
Loading history...
156
            $error = error_get_last();
157
            throw new RuntimeException("Can't send HTTPS request to: $callApiUrl\n" . ($error ? $error[ 'message' ] : ''));
158
        }
159
        $response = @stream_get_contents($stream);
160
        if ( ! $response) {
161
            $error = error_get_last();
162
            fclose($stream);
163
            throw new RuntimeException("Error reading HTTPS response from: $callApiUrl\n" . ($error ? $error[ 'message' ] : ''));
164
        }
165
        $meta = @stream_get_meta_data($stream);
166
        if ( ! $meta) {
167
            $error = error_get_last();
168
            fclose($stream);
169
            throw new RuntimeException("Error reading HTTPS response from: $callApiUrl\n" . ($error ? $error[ 'message' ] : ''));
170
        }
171
        fclose($stream);
172
        if ( ! $meta || ! isset($meta[ 'wrapper_data' ], $meta[ 'wrapper_data' ][ 0 ])) {
173
            throw new RuntimeException("Unable to read headers from HTTP request to: $callApiUrl");
174
        }
175
        if ( ! empty($meta[ 'timed_out' ])) {
176
            throw new RuntimeException("Request timed out: $callApiUrl", 504);
177
        }
178
        if ( ! preg_match('/HTTP\/[\d.]+ (\d+) (.*)/', $meta[ 'wrapper_data' ][ 0 ], $status)) {
179
            throw new RuntimeException("Unexpected response: " . $meta[ 'wrapper_data' ][ 0 ]);
180
        }
181
        $status = intval($status[ 1 ]);
182
        $errorMessage = $status[ 2 ];
183
        if ($response && preg_grep('/content-type:\s*application\/im2\+json/i', $meta[ 'wrapper_data' ])) {
184
            $json = @json_decode($response);
185
            if ($json) {
186
                if (isset($json->status)) {
187
                    $status = $json->status;
188
                }
189
                if (isset($json->error)) {
190
                    $errorMessage = $json->error;
191
                }
192
                if (isset($json->code) && $json->code === 'IM2ACCOUNT') {
193
                    throw new RuntimeException($errorMessage, $status);
194
                }
195
            }
196
        }
197
        if ($status >= 500) {
198
            throw new RuntimeException($errorMessage, $status);
199
        }
200
        if ($status == 404) {
201
            throw new RuntimeException("Could not find the image: {$image}", $status);
202
        }
203
        if ($status == 403) {
204
            throw new RuntimeException("Origin server denied access to {$image}", $status);
205
        }
206
        if ($status >= 400) {
207
            throw new RuntimeException($errorMessage, $status);
208
        }
209
210
        return $response;
211
    }
212
}