Completed
Push — master ( 5591c4...1f9686 )
by Daniel
37:10 queued 28:40
created

HTTP   F

Complexity

Total Complexity 73

Size/Duplication

Total Lines 561
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 173
dl 0
loc 561
rs 2.56
c 0
b 0
f 0
wmc 73

17 Methods

Rating   Name   Duplication   Size   Complexity  
A filename2url() 0 16 3
A RAW_setGetVar() 0 4 1
A register_etag() 0 7 2
A absoluteURLs() 0 9 2
A getImagesIn() 0 3 1
F setGetVar() 0 55 20
A getLinksIn() 0 3 1
A set_cache_age() 0 5 1
A register_modification_date() 0 4 1
A register_modification_timestamp() 0 4 1
B findByTagAndAttribute() 0 20 7
A get_mime_type() 0 21 4
B urlRewriter() 0 45 6
A gmt_date() 0 3 1
A get_cache_age() 0 3 1
F augmentState() 0 74 16
A add_cache_headers() 0 39 5

How to fix   Complexity   

Complex Class

Complex classes like HTTP often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HTTP, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Control;
4
5
use SilverStripe\Assets\File;
6
use SilverStripe\Control\Middleware\ChangeDetectionMiddleware;
7
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
8
use SilverStripe\Core\Config\Config;
9
use SilverStripe\Core\Config\Configurable;
10
use SilverStripe\Core\Convert;
11
use InvalidArgumentException;
12
use finfo;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Dev\Deprecation;
15
16
/**
17
 * A class with HTTP-related helpers. Like Debug, this is more a bundle of methods than a class.
18
 */
19
class HTTP
20
{
21
    use Configurable;
22
23
    /**
24
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead
25
     * @var int
26
     */
27
    protected static $cache_age = 0;
28
29
    /**
30
     * @deprecated 4.2..5.0 Handled by HTTPCacheControlMiddleware
31
     * @var int
32
     */
33
    protected static $modification_date = null;
34
35
    /**
36
     * @deprecated 4.2..5.0 Handled by ChangeDetectionMiddleware
37
     * @var string
38
     */
39
    protected static $etag = null;
40
41
    /**
42
     * @config
43
     * @var bool
44
     * @deprecated 4.2..5.0 'HTTP.cache_ajax_requests config is deprecated.
45
     * Use HTTPCacheControlMiddleware::disableCache() instead'
46
     */
47
    private static $cache_ajax_requests = false;
0 ignored issues
show
introduced by
The private property $cache_ajax_requests is not used, and could be removed.
Loading history...
48
49
    /**
50
     * @config
51
     * @var bool
52
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware.defaultState/.defaultForcingLevel instead
53
     */
54
    private static $disable_http_cache = false;
0 ignored issues
show
introduced by
The private property $disable_http_cache is not used, and could be removed.
Loading history...
55
56
    /**
57
     * Set to true to disable all deprecated HTTP Cache settings
58
     *
59
     * @var bool
60
     */
61
    private static $ignoreDeprecatedCaching = false;
0 ignored issues
show
introduced by
The private property $ignoreDeprecatedCaching is not used, and could be removed.
Loading history...
62
63
    /**
64
     * Mapping of extension to mime types
65
     *
66
     * @var array
67
     * @config
68
     */
69
    private static $MimeTypes = [];
0 ignored issues
show
introduced by
The private property $MimeTypes is not used, and could be removed.
Loading history...
70
71
    /**
72
     * List of names to add to the Cache-Control header.
73
     *
74
     * @deprecated 4.2..5.0 Handled by HTTPCacheControlMiddleware instead
75
     * @see HTTPCacheControlMiddleware::__construct()
76
     * @config
77
     * @var array Keys are cache control names, values are boolean flags
78
     */
79
    private static $cache_control = [];
0 ignored issues
show
introduced by
The private property $cache_control is not used, and could be removed.
Loading history...
80
81
    /**
82
     * Vary string; A comma separated list of var header names
83
     *
84
     * @deprecated 4.2..5.0 Handled by HTTPCacheControlMiddleware instead
85
     * @config
86
     * @var string|null
87
     */
88
    private static $vary = null;
0 ignored issues
show
introduced by
The private property $vary is not used, and could be removed.
Loading history...
89
90
    /**
91
     * Turns a local system filename into a URL by comparing it to the script filename.
92
     *
93
     * @param string $filename
94
     * @return string
95
     */
96
    public static function filename2url($filename)
97
    {
98
        $filename = realpath($filename);
99
        if (!$filename) {
100
            return null;
101
        }
102
103
        // Filter files outside of the webroot
104
        $base = realpath(BASE_PATH);
105
        $baseLength = strlen($base);
106
        if (substr($filename, 0, $baseLength) !== $base) {
107
            return null;
108
        }
109
110
        $relativePath = ltrim(substr($filename, $baseLength), '/\\');
111
        return Director::absoluteURL($relativePath);
112
    }
113
114
    /**
115
     * Turn all relative URLs in the content to absolute URLs.
116
     *
117
     * @param string $html
118
     *
119
     * @return string
120
     */
121
    public static function absoluteURLs($html)
122
    {
123
        $html = str_replace('$CurrentPageURL', Controller::curr()->getRequest()->getURL(), $html);
124
        return HTTP::urlRewriter($html, function ($url) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
125
            //no need to rewrite, if uri has a protocol (determined here by existence of reserved URI character ":")
126
            if (preg_match('/^\w+:/', $url)) {
127
                return $url;
128
            }
129
            return Director::absoluteURL($url, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $relativeParent of SilverStripe\Control\Director::absoluteURL(). ( Ignorable by Annotation )

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

129
            return Director::absoluteURL($url, /** @scrutinizer ignore-type */ true);
Loading history...
130
        });
131
    }
132
133
    /**
134
     * Rewrite all the URLs in the given content, evaluating the given string as PHP code.
135
     *
136
     * Put $URL where you want the URL to appear, however, you can't embed $URL in strings, for example:
137
     * <ul>
138
     * <li><code>'"../../" . $URL'</code></li>
139
     * <li><code>'myRewriter($URL)'</code></li>
140
     * <li><code>'(substr($URL, 0, 1)=="/") ? "../" . substr($URL, 1) : $URL'</code></li>
141
     * </ul>
142
     *
143
     * As of 3.2 $code should be a callable which takes a single parameter and returns the rewritten,
144
     * for example:
145
     * <code>
146
     * function($url) {
147
     *      return Director::absoluteURL($url, true);
148
     * }
149
     * </code>
150
     *
151
     * @param string $content The HTML to search for links to rewrite.
152
     * @param callable $code Either a string that can evaluate to an expression to rewrite links
153
     * (depreciated), or a callable that takes a single parameter and returns the rewritten URL.
154
     *
155
     * @return string The content with all links rewritten as per the logic specified in $code.
156
     */
157
    public static function urlRewriter($content, $code)
158
    {
159
        if (!is_callable($code)) {
160
            throw new InvalidArgumentException(
161
                'HTTP::urlRewriter expects a callable as the second parameter'
162
            );
163
        }
164
165
        // Replace attributes
166
        $attribs = ["src", "background", "a" => "href", "link" => "href", "base" => "href"];
167
        $regExps = [];
168
        foreach ($attribs as $tag => $attrib) {
169
            if (!is_numeric($tag)) {
170
                $tagPrefix = "$tag ";
171
            } else {
172
                $tagPrefix = "";
173
            }
174
175
            $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/i";
176
            $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/i";
177
            $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/i";
178
        }
179
        // Replace css styles
180
        // @todo - http://www.css3.info/preview/multiple-backgrounds/
181
        $styles = ['background-image', 'background', 'list-style-image', 'list-style', 'content'];
182
        foreach ($styles as $style) {
183
            $regExps[] = "/($style:[^;]*url *\\(\")([^\"]+)(\"\\))/i";
184
            $regExps[] = "/($style:[^;]*url *\\(')([^']+)('\\))/i";
185
            $regExps[] = "/($style:[^;]*url *\\()([^\"\\)')]+)(\\))/i";
186
        }
187
188
        // Callback for regexp replacement
189
        $callback = function ($matches) use ($code) {
190
            // Decode HTML attribute
191
            $URL = Convert::xml2raw($matches[2]);
192
            $rewritten = $code($URL);
193
            return $matches[1] . Convert::raw2xml($rewritten) . $matches[3];
0 ignored issues
show
Bug introduced by
Are you sure SilverStripe\Core\Convert::raw2xml($rewritten) of type string|array can be used in concatenation? ( Ignorable by Annotation )

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

193
            return $matches[1] . /** @scrutinizer ignore-type */ Convert::raw2xml($rewritten) . $matches[3];
Loading history...
194
        };
195
196
        // Execute each expression
197
        foreach ($regExps as $regExp) {
198
            $content = preg_replace_callback($regExp, $callback, $content);
199
        }
200
201
        return $content;
202
    }
203
204
    /**
205
     * Will try to include a GET parameter for an existing URL, preserving existing parameters and
206
     * fragments. If no URL is given, falls back to $_SERVER['REQUEST_URI']. Uses parse_url() to
207
     * dissect the URL, and http_build_query() to reconstruct it with the additional parameter.
208
     * Converts any '&' (ampersand) URL parameter separators to the more XHTML compliant '&amp;'.
209
     *
210
     * CAUTION: If the URL is determined to be relative, it is prepended with Director::absoluteBaseURL().
211
     * This method will always return an absolute URL because Director::makeRelative() can lead to
212
     * inconsistent results.
213
     *
214
     * @param string $varname
215
     * @param string $varvalue
216
     * @param string|null $currentURL Relative or absolute URL, or HTTPRequest to get url from
217
     * @param string $separator Separator for http_build_query().
218
     * @return string
219
     */
220
    public static function setGetVar($varname, $varvalue, $currentURL = null, $separator = '&')
221
    {
222
        if (!isset($currentURL)) {
223
            $request = Controller::curr()->getRequest();
224
            $currentURL = $request->getURL(true);
225
        }
226
        $uri = $currentURL;
227
228
        $isRelative = false;
229
        // We need absolute URLs for parse_url()
230
        if (Director::is_relative_url($uri)) {
231
            $uri = Director::absoluteBaseURL() . $uri;
232
            $isRelative = true;
233
        }
234
235
        // try to parse uri
236
        $parts = parse_url($uri);
237
        if (!$parts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
238
            throw new InvalidArgumentException("Can't parse URL: " . $uri);
239
        }
240
241
        // Parse params and add new variable
242
        $params = [];
243
        if (isset($parts['query'])) {
244
            parse_str($parts['query'], $params);
245
        }
246
        $params[$varname] = $varvalue;
247
248
        // Generate URI segments and formatting
249
        $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
250
        $user = (isset($parts['user']) && $parts['user'] != '') ? $parts['user'] : '';
251
252
        if ($user != '') {
253
            // format in either user:[email protected] or [email protected]
254
            $user .= (isset($parts['pass']) && $parts['pass'] != '') ? ':' . $parts['pass'] . '@' : '@';
255
        }
256
257
        $host = (isset($parts['host'])) ? $parts['host'] : '';
258
        $port = (isset($parts['port']) && $parts['port'] != '') ? ':' . $parts['port'] : '';
259
        $path = (isset($parts['path']) && $parts['path'] != '') ? $parts['path'] : '';
260
261
        // handle URL params which are existing / new
262
        $params = ($params) ? '?' . http_build_query($params, null, $separator) : '';
263
264
        // keep fragments (anchors) intact.
265
        $fragment = (isset($parts['fragment']) && $parts['fragment'] != '') ? '#' . $parts['fragment'] : '';
266
267
        // Recompile URI segments
268
        $newUri = $scheme . '://' . $user . $host . $port . $path . $params . $fragment;
269
270
        if ($isRelative) {
271
            return Director::makeRelative($newUri);
272
        }
273
274
        return $newUri;
275
    }
276
277
    /**
278
     * @param string $varname
279
     * @param string $varvalue
280
     * @param null|string $currentURL
281
     *
282
     * @return string
283
     */
284
    public static function RAW_setGetVar($varname, $varvalue, $currentURL = null)
285
    {
286
        $url = self::setGetVar($varname, $varvalue, $currentURL);
287
        return Convert::xml2raw($url);
288
    }
289
290
    /**
291
     * Search for all tags with a specific attribute, then return the value of that attribute in a
292
     * flat array.
293
     *
294
     * @param string $content
295
     * @param array $attributes An array of tags to attributes, for example "[a] => 'href', [div] => 'id'"
296
     *
297
     * @return array
298
     */
299
    public static function findByTagAndAttribute($content, $attributes)
300
    {
301
        $regexes = [];
302
303
        foreach ($attributes as $tag => $attribute) {
304
            $regexes[] = "/<{$tag} [^>]*$attribute *= *([\"'])(.*?)\\1[^>]*>/i";
305
            $regexes[] = "/<{$tag} [^>]*$attribute *= *([^ \"'>]+)/i";
306
        }
307
308
        $result = [];
309
310
        if ($regexes) {
311
            foreach ($regexes as $regex) {
312
                if (preg_match_all($regex, $content, $matches)) {
313
                    $result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
314
                }
315
            }
316
        }
317
318
        return count($result) ? $result : null;
319
    }
320
321
    /**
322
     * @param string $content
323
     *
324
     * @return array
325
     */
326
    public static function getLinksIn($content)
327
    {
328
        return self::findByTagAndAttribute($content, ["a" => "href"]);
329
    }
330
331
    /**
332
     * @param string $content
333
     *
334
     * @return array
335
     */
336
    public static function getImagesIn($content)
337
    {
338
        return self::findByTagAndAttribute($content, ["img" => "src"]);
339
    }
340
341
    /**
342
     * Get the MIME type based on a file's extension. If the finfo class exists in PHP, and the file
343
     * exists relative to the project root, then use that extension, otherwise fallback to a list of
344
     * commonly known MIME types.
345
     *
346
     * @param string $filename
347
     * @return string
348
     */
349
    public static function get_mime_type($filename)
350
    {
351
        // If the finfo module is compiled into PHP, use it.
352
        $path = BASE_PATH . DIRECTORY_SEPARATOR . $filename;
353
        if (class_exists('finfo') && file_exists($path)) {
354
            $finfo = new finfo(FILEINFO_MIME_TYPE);
355
            return $finfo->file($path);
356
        }
357
358
        // Fallback to use the list from the HTTP.yml configuration and rely on the file extension
359
        // to get the file mime-type
360
        $ext = strtolower(File::get_file_extension($filename));
361
        // Get the mime-types
362
        $mimeTypes = HTTP::config()->uninherited('MimeTypes');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
363
364
        // The mime type doesn't exist
365
        if (!isset($mimeTypes[$ext])) {
366
            return 'application/unknown';
367
        }
368
369
        return $mimeTypes[$ext];
370
    }
371
372
    /**
373
     * Set the maximum age of this page in web caches, in seconds.
374
     *
375
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead
376
     * @param int $age
377
     */
378
    public static function set_cache_age($age)
379
    {
380
        Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead');
381
        self::$cache_age = $age;
382
        HTTPCacheControlMiddleware::singleton()->setMaxAge($age);
383
    }
384
385
    /**
386
     * @param string $dateString
387
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware::registerModificationDate() instead
388
     */
389
    public static function register_modification_date($dateString)
390
    {
391
        Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
392
        HTTPCacheControlMiddleware::singleton()->registerModificationDate($dateString);
393
    }
394
395
    /**
396
     * @param int $timestamp
397
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware::registerModificationDate() instead
398
     */
399
    public static function register_modification_timestamp($timestamp)
400
    {
401
        Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
402
        HTTPCacheControlMiddleware::singleton()->registerModificationDate($timestamp);
403
    }
404
405
    /**
406
     * @deprecated 4.2..5.0 Use ChangeDetectionMiddleware instead
407
     * @param string $etag
408
     */
409
    public static function register_etag($etag)
410
    {
411
        Deprecation::notice('5.0', 'Use ChangeDetectionMiddleware instead');
412
        if (strpos($etag, '"') !== 0) {
413
            $etag =  "\"{$etag}\"";
414
        }
415
        self::$etag = $etag;
416
    }
417
418
    /**
419
     * Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
420
     * Note that setting HTTP::$cache_age will overrule any cache headers set by PHP's
421
     * session_cache_limiter functionality. It is your responsibility to ensure only cacheable data
422
     * is in fact cached, and HTTP::$cache_age isn't set when the HTTP body contains session-specific
423
     * content.
424
     *
425
     * Omitting the $body argument or passing a string is deprecated; in these cases, the headers are
426
     * output directly.
427
     *
428
     * @param HTTPResponse $response
429
     * @deprecated 4.2..5.0 Headers are added automatically by HTTPCacheControlMiddleware instead.
430
     */
431
    public static function add_cache_headers($response = null)
432
    {
433
        Deprecation::notice('5.0', 'Headers are added automatically by HTTPCacheControlMiddleware instead.');
434
435
        // Skip if deprecated API is disabled
436
        if (Config::inst()->get(HTTP::class, 'ignoreDeprecatedCaching')) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
437
            return;
438
        }
439
440
        // Ensure a valid response object is provided
441
        if (!$response instanceof HTTPResponse) {
442
            user_error("HTTP::add_cache_headers() must be passed an HTTPResponse object", E_USER_WARNING);
443
            return;
444
        }
445
446
        // Warn if already assigned cache-control headers
447
        if ($response->getHeader('Cache-Control')) {
448
            trigger_error(
449
                'Cache-Control header has already been set. '
450
                . 'Please use HTTPCacheControlMiddleware API to set caching options instead.',
451
                E_USER_WARNING
452
            );
453
            return;
454
        }
455
456
        // Ensure a valid request object exists in the current context
457
        if (!Injector::inst()->has(HTTPRequest::class)) {
458
            user_error("HTTP::add_cache_headers() cannot work without a current HTTPRequest object", E_USER_WARNING);
459
            return;
460
        }
461
462
        /** @var HTTPRequest $request */
463
        $request = Injector::inst()->get(HTTPRequest::class);
464
465
        // Run middleware
466
        ChangeDetectionMiddleware::singleton()->process($request, function (HTTPRequest $request) use ($response) {
467
            return HTTPCacheControlMiddleware::singleton()
468
                ->process($request, function (HTTPRequest $request) use ($response) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

468
                ->process($request, function (/** @scrutinizer ignore-unused */ HTTPRequest $request) use ($response) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
469
                    return $response;
470
                });
471
        });
472
    }
473
474
    /**
475
     * Ensure that all deprecated HTTP cache settings are respected
476
     *
477
     * @deprecated 4.2..5.0 Use HTTPCacheControlMiddleware instead
478
     * @throws \LogicException
479
     * @param HTTPRequest $request
480
     * @param HTTPResponse $response
481
     */
482
    public static function augmentState(HTTPRequest $request, HTTPResponse $response)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

482
    public static function augmentState(/** @scrutinizer ignore-unused */ HTTPRequest $request, HTTPResponse $response)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
483
    {
484
        // Skip if deprecated API is disabled
485
        $config = Config::forClass(HTTP::class);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
486
        if ($config->get('ignoreDeprecatedCaching')) {
487
            return;
488
        }
489
490
        $cacheControlMiddleware = HTTPCacheControlMiddleware::singleton();
491
492
        // if http caching is disabled by config, disable it - used on dev environments due to frequently changing
493
        // templates and other data. will be overridden by forced publicCache(true) or privateCache(true) calls
494
        if ($config->get('disable_http_cache')) {
495
            Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware.defaultState/.defaultForcingLevel instead');
496
            $cacheControlMiddleware->disableCache();
497
        }
498
499
        // if no caching ajax requests, disable ajax if is ajax request
500
        if (!$config->get('cache_ajax_requests') && Director::is_ajax()) {
501
            Deprecation::notice(
502
                '5.0',
503
                'HTTP.cache_ajax_requests config is deprecated. Use HTTPCacheControlMiddleware::disableCache() instead'
504
            );
505
            $cacheControlMiddleware->disableCache();
506
        }
507
508
        // Pass vary to middleware
509
        $configVary = $config->get('vary');
510
        if ($configVary) {
511
            Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware.defaultVary instead');
512
            $cacheControlMiddleware->addVary($configVary);
513
        }
514
515
        // Pass cache_control to middleware
516
        $configCacheControl = $config->get('cache_control');
517
        if ($configCacheControl) {
518
            Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware API instead');
519
520
            $supportedDirectives = ['max-age', 'no-cache', 'no-store', 'must-revalidate'];
521
            if ($foundUnsupported = array_diff(array_keys($configCacheControl), $supportedDirectives)) {
522
                throw new \LogicException(
523
                    'Found unsupported legacy directives in HTTP.cache_control: ' .
524
                    implode(', ', $foundUnsupported) .
525
                    '. Please use HTTPCacheControlMiddleware API instead'
526
                );
527
            }
528
529
            if (isset($configCacheControl['max-age'])) {
530
                $cacheControlMiddleware->setMaxAge($configCacheControl['max-age']);
531
            }
532
533
            if (isset($configCacheControl['no-cache'])) {
534
                $cacheControlMiddleware->setNoCache((bool)$configCacheControl['no-cache']);
535
            }
536
537
            if (isset($configCacheControl['no-store'])) {
538
                $cacheControlMiddleware->setNoStore((bool)$configCacheControl['no-store']);
539
            }
540
541
            if (isset($configCacheControl['must-revalidate'])) {
542
                $cacheControlMiddleware->setMustRevalidate((bool)$configCacheControl['must-revalidate']);
543
            }
544
        }
545
546
        // Set modification date
547
        if (self::$modification_date) {
548
            Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
549
            $cacheControlMiddleware->registerModificationDate(self::$modification_date);
550
        }
551
552
        // Ensure deprecated $etag property is assigned
553
        if (self::$etag && !$cacheControlMiddleware->hasDirective('no-store') && !$response->getHeader('ETag')) {
554
            Deprecation::notice('5.0', 'Etag should not be set explicitly');
555
            $response->addHeader('ETag', self::$etag);
556
        }
557
    }
558
559
    /**
560
     * Return an {@link http://www.faqs.org/rfcs/rfc2822 RFC 2822} date in the GMT timezone (a timestamp
561
     * is always in GMT: the number of seconds since January 1 1970 00:00:00 GMT)
562
     *
563
     * @param int $timestamp
564
     * @deprecated 4.2..5.0 Inline if you need this
565
     * @return string
566
     */
567
    public static function gmt_date($timestamp)
568
    {
569
        return gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
570
    }
571
572
    /**
573
     * Return static variable cache_age in second
574
     *
575
     * @return int
576
     */
577
    public static function get_cache_age()
578
    {
579
        return self::$cache_age;
580
    }
581
}
582