Issues (994)

src/img/cache.php (9 issues)

1
<?php
2
3
namespace img;
4
5
/**
6
 * Image Cache Engine
7
 */
8
class cache
9
{
10
  public static $saved;
11
  public static $url;
12
  public static $api;
13
  public static $cache;
14
  public static $cache_dir = ROOT . '/tmp/img/';
15
16
  public function __construct()
17
  {
18
    self::$api = new \Extender\request('https://unsplash.it');
19
  }
20
21
  function clean()
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
22
  {
23
    $folder = self::$cache_dir;
0 ignored issues
show
The assignment to $folder is dead and can be removed.
Loading history...
24
  }
25
26
  public static function imageCache(string $url, bool $rewrite = false)
27
  {
28
    resolve_file(self::$cache_dir . '/.htaccess', 'deny from all');
29
    if (!self::$api) {
30
      self::$api = new \Extender\request('https://unsplash.it');
31
    }
32
    self::$url = $url;
33
    self::$api->setCookieFile(self::$cache_dir . '/cookies.txt');
34
    self::$api->setCookieJar(self::$cache_dir . '/cookies.txt');
35
    self::$api->setReferrer($url);
36
    self::$api->setUserAgent($_SERVER['HTTP_USER_AGENT']);
37
    $saved = resolve_file(self::$cache_dir . '/saved.json', '{}');
38
    self::$saved = $saved;
39
    $res = read_file($saved, []);
40
    if (is_string($res) && is_json($res)) {
41
      $res = json_decode($res, true);
42
    }
43
44
    self::$api->setUrl($url);
45
    $url = self::$api->url;
46
    $cache = self::$cache_dir . '/' . md5($url);
47
    self::$cache = $cache;
48
    if (file_exists($cache) && !headers_sent()) {
49
      header_remove('x-powered-by');
50
      header_remove('pragma');
51
      self::send_cache_header($cache);
52
      $lastModified = gmdate('D, d M Y H:i:s', filemtime($cache)) . ' GMT';
53
      header('Cache-Control: private');
54
      /*
55
       * Send fallback headers
56
       */
57
      //header('Content-type: image/jpeg');
58
      header('ETag: ' . md5_file($cache));
59
      header("Last-Modified: $lastModified");
60
      // 1 day expires
61
      header('Expires: ' . gmdate('D, d M Y H:i:s', ((60 * 60 * 24 * 1) + strtotime($lastModified))));
62
    }
63
64
    if (!isset($res[$url]) || (is_bool($rewrite) && $rewrite)) {
65
      self::$api->set_method('get');
66
      self::$api->exec();
67
      for ($i = 0; $i < 2; ++$i) {
68
        if (isset(self::$api->responseHeaders['Location'])) {
69
          self::$api->get(self::$api->responseHeaders['Location']);
70
        } else {
71
          break;
72
        }
73
      }
74
      $res[$url] = self::$api->url;
75
      write_file($saved, $res, true);
76
      write_file($cache, self::$api->response, true);
77
      header('Content-Type: ' . self::$api->responseHeaders['Content-Type'], true);
78
      echo self::$api->response;
79
    } elseif (file_exists($cache)) {
80
      header('Content-Type: ' . mime_content_type($cache), true);
81
      $read = read_file($cache);
82
      if (!empty($read)) {
83
        echo $read;
84
      } else {
85
        return self::{__FUNCTION__}($url, true);
86
      }
87
    } else {
88
      self::$api->set_url($res[$url])->set_method('get')->exec();
89
      self::writeCache();
90
    }
91
  }
92
93
  /**
94
   * Transform url image to cache
95
   * * `schema data cache`
96
   * ```json
97
   * { "md5": "realURL" }
98
   * ```.
99
   *
100
   * @author Dimas Lanjaka <[email protected]>
101
   */
102
  public static function url2cache(string $url)
103
  {
104
    if (!self::$api) {
105
      self::$api = new \Extender\request('https://unsplash.it');
106
    }
107
    /**
108
     * @var string Anonymize url into md5
109
     *
110
     * @todo Fix unecessary characters from url
111
     */
112
    $md5 = md5($url);
113
    /**
114
     * @var string Save cache name generator.
115
     *
116
     * @return string domain or default
117
     */
118
    $savedname = function () {
119
      global $url;
120
      $savedname = \MVC\helper::url2host($url);
121
      if (!$savedname) {
122
        $savedname = 'default';
123
      }
124
125
      return $savedname;
126
    };
127
    /**
128
     * @var string Save location schema image
129
     */
130
    $saved = __DIR__ . "/data/$savedname.json";
131
    self::$saved = $saved;
132
    $res = read_file($saved, []);
133
    if (is_string($res)) {
134
      $res = json_decode($res, true);
135
    }
136
    if (isset($res[$md5])) {
137
      self::$url = $res[$md5];
138
    } elseif (\MVC\helper::is_url($url)) {
139
      $res[$md5] = self::$url = $url;
140
      $saved = __DIR__ . "/data/$savedname.json";
141
    }
142
143
    self::$api->setUrl(self::$url);
144
    $url = self::$api->url;
145
    $cache = ROOT . '/tmp/img/' . md5($url);
146
    self::$cache = $cache;
147
148
    if (!file_exists($cache)) {
149
      self::$api->set_method('get');
150
      self::$api->exec();
151
      for ($i = 0; $i < 10; ++$i) {
152
        if (isset(self::$api->responseHeaders['Location'])) {
153
          self::$api->get(self::$api->responseHeaders['Location']);
154
        } else {
155
          break;
156
        }
157
      }
158
159
      write_file($saved, $res, true);
160
      write_file($cache, self::$api->response, true);
161
    }
162
163
    return '/img/cache?hash=' . md5($url);
164
  }
165
166
  /**
167
   * Write Cache.
168
   *
169
   * @return void
170
   */
171
  public static function writeCache()
172
  {
173
    $res[self::$url] = self::$api->url;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$res was never initialized. Although not strictly required by PHP, it is generally a good practice to add $res = array(); before regardless.
Loading history...
174
    write_file(self::$saved, $res, true);
175
    write_file(self::$cache, self::$api->response, true);
176
    header('Content-Type: ' . self::$api->responseHeaders['Content-Type'], true);
177
    echo self::$api->response;
178
  }
179
180
  /**
181
   * Send Cache Header for static content.
182
   *
183
   * @param [type] $cache_file_name
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
184
   * @param bool   $check_request
185
   *
186
   * @return void
187
   */
188
  public static function send_cache_header($cache_file_name, $check_request = false)
189
  {
190
    if (headers_sent()) {
191
      return;
192
    }
193
    $mtime = @filemtime($cache_file_name);
194
195
    if ($mtime > 0) {
196
      $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
0 ignored issues
show
It seems like $mtime can also be of type false; however, parameter $timestamp of gmdate() does only seem to accept integer|null, 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

196
      $gmt_mtime = gmdate('D, d M Y H:i:s', /** @scrutinizer ignore-type */ $mtime) . ' GMT';
Loading history...
197
      $etag = sprintf('%08x-%08x', crc32($cache_file_name), $mtime);
0 ignored issues
show
It seems like $mtime can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

197
      $etag = sprintf('%08x-%08x', crc32($cache_file_name), /** @scrutinizer ignore-type */ $mtime);
Loading history...
198
199
      header('ETag: "' . $etag . '"', true);
200
      header('Last-Modified: ' . $gmt_mtime, true);
201
      header('Cache-Control: private', true);
202
      // we don't send an "Expires:" header to make clients/browsers use if-modified-since and/or if-none-match
203
204
      if ($check_request) {
205
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && !empty($_SERVER['HTTP_IF_NONE_MATCH'])) {
206
          $tmp = explode(';', $_SERVER['HTTP_IF_NONE_MATCH']); // IE fix!
207
          if (!empty($tmp[0]) && strtotime($tmp[0]) == strtotime($gmt_mtime)) {
208
            header('HTTP/1.1 304 Not Modified');
209
210
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
211
          }
212
        }
213
214
        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
215
          if (str_replace(['\"', '"'], '', $_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
216
            header('HTTP/1.1 304 Not Modified');
217
218
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
219
          }
220
        }
221
      }
222
    }
223
224
    return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
225
  }
226
}
227