Completed
Push — master ( b871dd...c32ae6 )
by Mikael
07:45 queued 05:18
created

CRemoteImage.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Get a image from a remote server using HTTP GET and If-Modified-Since.
4
 *
5
 */
6
class CRemoteImage
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7
{
8
    /**
9
     * Path to cache files.
10
     */
11
    private $saveFolder = null;
12
13
14
15
    /**
16
     * Use cache or not.
17
     */
18
    private $useCache = true;
19
20
21
22
    /**
23
     * HTTP object to aid in download file.
24
     */
25
    private $http;
26
27
28
29
    /**
30
     * Status of the HTTP request.
31
     */
32
    private $status;
33
34
35
36
    /**
37
     * Defalt age for cached items 60*60*24*7.
38
     */
39
    private $defaultMaxAge = 604800;
40
41
42
43
    /**
44
     * Url of downloaded item.
45
     */
46
    private $url;
47
48
49
50
    /**
51
     * Base name of cache file for downloaded item and name of image.
52
     */
53
    private $fileName;
54
55
56
57
    /**
58
     * Filename for json-file with details of cached item.
59
     */
60
    private $fileJson;
61
62
63
64
    /**
65
     * Cache details loaded from file.
66
     */
67
    private $cache;
68
69
70
71
    /**
72
     * Get status of last HTTP request.
73
     *
74
     * @return int as status
75
     */
76
    public function getStatus()
77
    {
78
        return $this->status;
79
    }
80
81
82
83
    /**
84
     * Get JSON details for cache item.
85
     *
86
     * @return array with json details on cache.
87
     */
88
    public function getDetails()
89
    {
90
        return $this->cache;
91
    }
92
93
94
95
    /**
96
     * Set the path to the cache directory.
97
     *
98
     * @param boolean $use true to use the cache and false to ignore cache.
0 ignored issues
show
There is no parameter named $use. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
99
     *
100
     * @return $this
101
     */
102
    public function setCache($path)
103
    {
104
        $this->saveFolder = $path;
105
        return $this;
106
    }
107
108
109
110
    /**
111
     * Check if cache is writable or throw exception.
112
     *
113
     * @return $this
114
     *
115
     * @throws Exception if cahce folder is not writable.
116
     */
117
    public function isCacheWritable()
118
    {
119
        if (!is_writable($this->saveFolder)) {
120
            throw new Exception("Cache folder is not writable for downloaded files.");
121
        }
122
        return $this;
123
    }
124
125
126
127
    /**
128
     * Decide if the cache should be used or not before trying to download
129
     * a remote file.
130
     *
131
     * @param boolean $use true to use the cache and false to ignore cache.
132
     *
133
     * @return $this
134
     */
135
    public function useCache($use = true)
136
    {
137
        $this->useCache = $use;
138
        return $this;
139
    }
140
141
142
143
    /**
144
     * Set header fields.
145
     *
146
     * @return $this
147
     */
148
    public function setHeaderFields()
149
    {
150
        $this->http->setHeader("User-Agent", "CImage/0.7.2 (PHP/". phpversion() . " cURL)");
151
        $this->http->setHeader("Accept", "image/jpeg,image/png,image/gif");
152
153
        if ($this->useCache) {
154
            $this->http->setHeader("Cache-Control", "max-age=0");
155
        } else {
156
            $this->http->setHeader("Cache-Control", "no-cache");
157
            $this->http->setHeader("Pragma", "no-cache");
158
        }
159
    }
160
161
162
163
    /**
164
     * Save downloaded resource to cache.
165
     *
166
     * @return string as path to saved file or false if not saved.
167
     */
168
    public function save()
169
    {
170
        $this->cache = array();
171
        $date         = $this->http->getDate(time());
172
        $maxAge       = $this->http->getMaxAge($this->defaultMaxAge);
173
        $lastModified = $this->http->getLastModified();
174
        $type         = $this->http->getContentType();
175
176
        $this->cache['Date']           = gmdate("D, d M Y H:i:s T", $date);
177
        $this->cache['Max-Age']        = $maxAge;
178
        $this->cache['Content-Type']   = $type;
179
        $this->cache['Url']            = $this->url;
180
181
        if ($lastModified) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lastModified of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
182
            $this->cache['Last-Modified'] = gmdate("D, d M Y H:i:s T", $lastModified);
183
        }
184
185
        // Save only if body is a valid image
186
        $body = $this->http->getBody();
187
        $img = imagecreatefromstring($body);
188
189
        if ($img !== false) {
190
            file_put_contents($this->fileName, $body);
191
            file_put_contents($this->fileJson, json_encode($this->cache));
192
            return $this->fileName;
193
        }
194
195
        return false;
196
    }
197
198
199
200
    /**
201
     * Got a 304 and updates cache with new age.
202
     *
203
     * @return string as path to cached file.
204
     */
205
    public function updateCacheDetails()
206
    {
207
        $date         = $this->http->getDate(time());
208
        $maxAge       = $this->http->getMaxAge($this->defaultMaxAge);
209
        $lastModified = $this->http->getLastModified();
210
211
        $this->cache['Date']    = gmdate("D, d M Y H:i:s T", $date);
212
        $this->cache['Max-Age'] = $maxAge;
213
214
        if ($lastModified) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lastModified of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
215
            $this->cache['Last-Modified'] = gmdate("D, d M Y H:i:s T", $lastModified);
216
        }
217
218
        file_put_contents($this->fileJson, json_encode($this->cache));
219
        return $this->fileName;
220
    }
221
222
223
224
    /**
225
     * Download a remote file and keep a cache of downloaded files.
226
     *
227
     * @param string $url a remote url.
228
     *
229
     * @throws Exception when status code does not match 200 or 304.
230
     *
231
     * @return string as path to downloaded file or false if failed.
232
     */
233
    public function download($url)
234
    {
235
        $this->http = new CHttpGet();
236
        $this->url = $url;
237
238
        // First check if the cache is valid and can be used
239
        $this->loadCacheDetails();
240
241
        if ($this->useCache) {
242
            $src = $this->getCachedSource();
243
            if ($src) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $src of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
244
                $this->status = 1;
245
                return $src;
246
            }
247
        }
248
249
        // Do a HTTP request to download item
250
        $this->setHeaderFields();
251
        $this->http->setUrl($this->url);
252
        $this->http->doGet();
253
254
        $this->status = $this->http->getStatus();
255
        if ($this->status === 200) {
256
            $this->isCacheWritable();
257
            return $this->save();
258
        } elseif ($this->status === 304) {
259
            $this->isCacheWritable();
260
            return $this->updateCacheDetails();
261
        }
262
263
        throw new Exception("Unknown statuscode when downloading remote image: " . $this->status);
264
    }
265
266
267
268
    /**
269
     * Get the path to the cached image file if the cache is valid.
270
     *
271
     * @return $this
272
     */
273
    public function loadCacheDetails()
274
    {
275
        $cacheFile = md5($this->url);
276
        $this->fileName = $this->saveFolder . $cacheFile;
277
        $this->fileJson = $this->fileName . ".json";
278
        if (is_readable($this->fileJson)) {
279
            $this->cache = json_decode(file_get_contents($this->fileJson), true);
280
        }
281
    }
282
283
284
285
    /**
286
     * Get the path to the cached image file if the cache is valid.
287
     *
288
     * @return string as the path ot the image file or false if no cache.
289
     */
290
    public function getCachedSource()
291
    {
292
        $imageExists = is_readable($this->fileName);
293
294
        // Is cache valid?
295
        $date   = strtotime($this->cache['Date']);
296
        $maxAge = $this->cache['Max-Age'];
297
        $now    = time();
298
        
299
        if ($imageExists && $date + $maxAge > $now) {
300
            return $this->fileName;
301
        }
302
303
        // Prepare for a 304 if available
304
        if ($imageExists && isset($this->cache['Last-Modified'])) {
305
            $this->http->setHeader("If-Modified-Since", $this->cache['Last-Modified']);
306
        }
307
308
        return false;
309
    }
310
}
311