silverstripe-framework/src/Control/HTTP.php

585 lines
21 KiB
PHP
Raw Normal View History

<?php
2013-05-10 11:59:20 +02:00
namespace SilverStripe\Control;
use SilverStripe\Assets\File;
use SilverStripe\Control\Middleware\ChangeDetectionMiddleware;
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Convert;
use InvalidArgumentException;
use finfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Deprecation;
/**
2015-11-13 23:44:49 +01:00
* A class with HTTP-related helpers. Like Debug, this is more a bundle of methods than a class.
*/
2016-11-29 00:31:16 +01:00
class HTTP
{
use Configurable;
/**
* @deprecated 4.2.0:5.0.0 Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead
2016-11-29 00:31:16 +01:00
* @var int
*/
protected static $cache_age = 0;
/**
* @deprecated 4.2.0:5.0.0 Handled by HTTPCacheControlMiddleware
2016-11-29 00:31:16 +01:00
* @var int
*/
protected static $modification_date = null;
/**
* @deprecated 4.2.0:5.0.0 Handled by ChangeDetectionMiddleware
2016-11-29 00:31:16 +01:00
* @var string
*/
protected static $etag = null;
/**
* @config
* @var bool
* @deprecated 4.2.0:5.0.0 'HTTP.cache_ajax_requests config is deprecated.
* Use HTTPCacheControlMiddleware::disableCache() instead'
2016-11-29 00:31:16 +01:00
*/
private static $cache_ajax_requests = false;
2016-11-29 00:31:16 +01:00
2018-06-12 13:50:37 +02:00
/**
* @config
* @var bool
* @deprecated 4.2.0:5.0.0 Use HTTPCacheControlMiddleware.defaultState/.defaultForcingLevel instead
2018-06-12 13:50:37 +02:00
*/
private static $disable_http_cache = false;
2018-06-14 03:01:27 +02:00
/**
* Set to true to disable all deprecated HTTP Cache settings
*
* @var bool
*/
private static $ignoreDeprecatedCaching = false;
2018-06-12 13:50:37 +02:00
/**
* Mapping of extension to mime types
*
* @var array
* @config
*/
private static $MimeTypes = [];
/**
* List of names to add to the Cache-Control header.
*
* @deprecated 4.2.0:5.0.0 Handled by HTTPCacheControlMiddleware instead
2018-06-12 13:50:37 +02:00
* @see HTTPCacheControlMiddleware::__construct()
* @config
* @var array Keys are cache control names, values are boolean flags
*/
private static $cache_control = [];
/**
* Vary string; A comma separated list of var header names
*
* @deprecated 4.2.0:5.0.0 Handled by HTTPCacheControlMiddleware instead
2018-06-12 13:50:37 +02:00
* @config
* @var string|null
*/
private static $vary = null;
2016-11-29 00:31:16 +01:00
/**
* Turns a local system filename into a URL by comparing it to the script filename.
*
* @param string $filename
* @return string
*/
public static function filename2url($filename)
{
2022-04-14 03:12:59 +02:00
$filename = realpath($filename ?? '');
2016-11-29 00:31:16 +01:00
if (!$filename) {
return null;
}
// Filter files outside of the webroot
$base = realpath(BASE_PATH);
2022-04-14 03:12:59 +02:00
$baseLength = strlen($base ?? '');
if (substr($filename ?? '', 0, $baseLength) !== $base) {
2016-11-29 00:31:16 +01:00
return null;
}
2022-04-14 03:12:59 +02:00
$relativePath = ltrim(substr($filename ?? '', $baseLength ?? 0), '/\\');
2016-11-29 00:31:16 +01:00
return Director::absoluteURL($relativePath);
}
/**
* Turn all relative URLs in the content to absolute URLs.
*
* @param string $html
*
* @return string
*/
public static function absoluteURLs($html)
{
2022-04-14 03:12:59 +02:00
$html = str_replace('$CurrentPageURL', Controller::curr()->getRequest()->getURL() ?? '', $html ?? '');
2016-11-29 00:31:16 +01:00
return HTTP::urlRewriter($html, function ($url) {
//no need to rewrite, if uri has a protocol (determined here by existence of reserved URI character ":")
2022-04-14 03:12:59 +02:00
if (preg_match('/^\w+:/', $url ?? '')) {
2016-11-29 00:31:16 +01:00
return $url;
}
2022-11-15 06:20:54 +01:00
return Director::absoluteURL($url);
2016-11-29 00:31:16 +01:00
});
}
/**
* Rewrite all the URLs in the given content, evaluating the given string as PHP code.
*
* Put $URL where you want the URL to appear, however, you can't embed $URL in strings, for example:
* <ul>
* <li><code>'"../../" . $URL'</code></li>
* <li><code>'myRewriter($URL)'</code></li>
* <li><code>'(substr($URL, 0, 1)=="/") ? "../" . substr($URL, 1) : $URL'</code></li>
* </ul>
*
* As of 3.2 $code should be a callable which takes a single parameter and returns the rewritten,
* for example:
* <code>
* function($url) {
* return Director::absoluteURL($url, true);
* }
* </code>
*
* @param string $content The HTML to search for links to rewrite.
* @param callable $code Either a string that can evaluate to an expression to rewrite links
* (depreciated), or a callable that takes a single parameter and returns the rewritten URL.
*
* @return string The content with all links rewritten as per the logic specified in $code.
*/
public static function urlRewriter($content, $code)
{
if (!is_callable($code)) {
throw new InvalidArgumentException(
'HTTP::urlRewriter expects a callable as the second parameter'
);
}
// Replace attributes
2018-06-12 13:50:37 +02:00
$attribs = ["src", "background", "a" => "href", "link" => "href", "base" => "href"];
2016-11-29 00:31:16 +01:00
$regExps = [];
foreach ($attribs as $tag => $attrib) {
if (!is_numeric($tag)) {
$tagPrefix = "$tag ";
} else {
$tagPrefix = "";
}
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/i";
}
// Replace css styles
// @todo - http://www.css3.info/preview/multiple-backgrounds/
2018-06-12 13:50:37 +02:00
$styles = ['background-image', 'background', 'list-style-image', 'list-style', 'content'];
2016-11-29 00:31:16 +01:00
foreach ($styles as $style) {
$regExps[] = "/($style:[^;]*url *\\(\")([^\"]+)(\"\\))/i";
$regExps[] = "/($style:[^;]*url *\\(')([^']+)('\\))/i";
$regExps[] = "/($style:[^;]*url *\\()([^\"\\)')]+)(\\))/i";
}
// Callback for regexp replacement
$callback = function ($matches) use ($code) {
// Decode HTML attribute
$URL = Convert::xml2raw($matches[2]);
$rewritten = $code($URL);
return $matches[1] . Convert::raw2xml($rewritten) . $matches[3];
};
// Execute each expression
foreach ($regExps as $regExp) {
2022-04-14 03:12:59 +02:00
$content = preg_replace_callback($regExp ?? '', $callback, $content ?? '');
2016-11-29 00:31:16 +01:00
}
return $content;
}
/**
* Will try to include a GET parameter for an existing URL, preserving existing parameters and
* fragments. If no URL is given, falls back to $_SERVER['REQUEST_URI']. Uses parse_url() to
* dissect the URL, and http_build_query() to reconstruct it with the additional parameter.
* Converts any '&' (ampersand) URL parameter separators to the more XHTML compliant '&amp;'.
*
* CAUTION: If the URL is determined to be relative, it is prepended with Director::absoluteBaseURL().
* This method will always return an absolute URL because Director::makeRelative() can lead to
* inconsistent results.
*
* @param string $varname
* @param string $varvalue
API Refactor bootstrap, request handling See https://github.com/silverstripe/silverstripe-framework/pull/7037 and https://github.com/silverstripe/silverstripe-framework/issues/6681 Squashed commit of the following: commit 8f65e5653211240650eaa4fa65bb83b45aae6d58 Author: Ingo Schommer <me@chillu.com> Date: Thu Jun 22 22:25:50 2017 +1200 Fixed upgrade guide spelling commit 76f95944fa89b0b540704b8d744329f690f9698c Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 16:38:34 2017 +1200 BUG Fix non-test class manifest including sapphiretest / functionaltest commit 9379834cb4b2e5177a2600049feec05bf111c16b Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:50:47 2017 +1200 BUG Fix nesting bug in Kernel commit 188ce35d82599360c40f0f2de29579c56fb90761 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:14:51 2017 +1200 BUG fix db bootstrapping issues commit 7ed4660e7a63915e8e974deeaba9807bc4d38b0d Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 14:49:07 2017 +1200 BUG Fix issue in DetailedErrorFormatter commit 738f50c497166f81ccbe3f40fbcff895ce71f82f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:49:19 2017 +1200 Upgrading notes on mysite/_config.php commit 6279d28e5e455916f902a2f963c014d8899f7fc7 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:43:28 2017 +1200 Update developer documentation commit 5c90d53a84ef0139c729396949a7857fae60436f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 10:48:44 2017 +1200 Update installer to not use global databaseConfig commit f9b2ba4755371f08bd95f6908ac612fcbb7ca205 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 21:04:39 2017 +1200 Fix behat issues commit 5b59a912b60282b4dad4ef10ed3b97c5d0a761ac Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 17:07:11 2017 +1200 Move HTTPApplication to SilverStripe\Control namespace commit e2c4a18f637bdd3d276619554de60ee8b4d95ced Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 16:29:03 2017 +1200 More documentation Fix up remaining tests Refactor temp DB into TempDatabase class so it’s available outside of unit tests. commit 5d235e64f341d6251bfe9f4833f15cc8593c5034 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 12:13:15 2017 +1200 API HTTPRequestBuilder::createFromEnvironment() now cleans up live globals BUG Fix issue with SSViewer Fix Security / View tests commit d88d4ed4e48291cb65407f222f190064b1f1deeb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:39:43 2017 +1200 API Refactor AppKernel into CoreKernel commit f7946aec3391139ae1b4029c353c327a36552b36 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:00:40 2017 +1200 Docs and minor cleanup commit 12bd31f9366327650b5c0c0f96cd0327d44faf0a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 15:34:34 2017 +1200 API Remove OutputMiddleware API Move environment / global / ini management into Environment class API Move getTempFolder into TempFolder class API Implement HTTPRequestBuilder / CLIRequestBuilder BUG Restore SS_ALLOWED_HOSTS check in original location API CoreKernel now requires $basePath to be passed in API Refactor installer.php to use application to bootstrap API move memstring conversion globals to Convert BUG Fix error in CoreKernel nesting not un-nesting itself properly. commit bba979114624247cf463cf2a8c9e4be9a7c3a772 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 18:07:53 2017 +1200 API Create HTTPMiddleware and standardise middleware for request handling commit 2a10c2397bdc53001013f607b5d38087ce6c0730 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:42:42 2017 +1200 Fixed ORM tests commit d75a8d1d93398af4bd0432df9e4bc6295c15a3fe Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:15:07 2017 +1200 FIx i18n tests commit 06364af3c379c931889c4cc34dd920fee3db204a Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 16:59:34 2017 +1200 Fix controller namespace Move states to sub namespace commit 2a278e2953d2dbb19f78d91c919048e1fc935436 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 12:49:45 2017 +1200 Fix forms namespace commit b65c21241bee019730027071d815dbf7571197a4 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:56:48 2017 +1200 Update API usages commit d1d4375c95a264a6b63cbaefc2c1d12f808bfd82 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:41:44 2017 +1200 API Refactor $flush into HTPPApplication API Enforce health check in Controller::pushCurrent() API Better global backup / restore Updated Director::test() to use new API commit b220534f06732db4fa940d8724c2a85c0ba2495a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 22:05:57 2017 +1200 Move app nesting to a test state helper commit 603704165c08d0c1c81fd5e6bb9506326eeee17b Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 21:46:04 2017 +1200 Restore kernel stack to fix multi-level nesting commit 2f6336a15bf79dc8c2edd44cec1931da2dd51c28 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 17:23:21 2017 +1200 API Implement kernel nesting commit fc7188da7d6ad6785354bab61f08700454c81d91 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:43:13 2017 +1200 Fix core tests commit a0ae7235148fffd71f2f02d1fe7fe45bf3aa39eb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:23:52 2017 +1200 Fix manifest tests commit ca033952513633e182040d3d13e1caa9000ca184 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:00:00 2017 +1200 API Move extension management into test state commit c66d4339777663a8a04661fea32a0cf35b95d20f Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 14:10:59 2017 +1200 API Refactor SapphireTest state management into SapphireTestState API Remove Injector::unregisterAllObjects() API Remove FakeController commit f26ae75c6ecaafa0dec1093264e0187191e6764d Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 18:04:34 2017 +1200 Implement basic CLI application object commit 001d5596621404892de0a5413392379eff990641 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 17:39:38 2017 +1200 Remove references to SapphireTest::is_running_test() Upgrade various code commit de079c041dacd96bc4f4b66421fa2b2cc4c320f8 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 7 18:07:33 2017 +1200 API Implement APP object API Refactor of Session
2017-06-22 12:50:45 +02:00
* @param string|null $currentURL Relative or absolute URL, or HTTPRequest to get url from
2016-11-29 00:31:16 +01:00
* @param string $separator Separator for http_build_query().
* @return string
*/
public static function setGetVar($varname, $varvalue, $currentURL = null, $separator = '&')
2016-11-29 00:31:16 +01:00
{
API Refactor bootstrap, request handling See https://github.com/silverstripe/silverstripe-framework/pull/7037 and https://github.com/silverstripe/silverstripe-framework/issues/6681 Squashed commit of the following: commit 8f65e5653211240650eaa4fa65bb83b45aae6d58 Author: Ingo Schommer <me@chillu.com> Date: Thu Jun 22 22:25:50 2017 +1200 Fixed upgrade guide spelling commit 76f95944fa89b0b540704b8d744329f690f9698c Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 16:38:34 2017 +1200 BUG Fix non-test class manifest including sapphiretest / functionaltest commit 9379834cb4b2e5177a2600049feec05bf111c16b Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:50:47 2017 +1200 BUG Fix nesting bug in Kernel commit 188ce35d82599360c40f0f2de29579c56fb90761 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:14:51 2017 +1200 BUG fix db bootstrapping issues commit 7ed4660e7a63915e8e974deeaba9807bc4d38b0d Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 14:49:07 2017 +1200 BUG Fix issue in DetailedErrorFormatter commit 738f50c497166f81ccbe3f40fbcff895ce71f82f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:49:19 2017 +1200 Upgrading notes on mysite/_config.php commit 6279d28e5e455916f902a2f963c014d8899f7fc7 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:43:28 2017 +1200 Update developer documentation commit 5c90d53a84ef0139c729396949a7857fae60436f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 10:48:44 2017 +1200 Update installer to not use global databaseConfig commit f9b2ba4755371f08bd95f6908ac612fcbb7ca205 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 21:04:39 2017 +1200 Fix behat issues commit 5b59a912b60282b4dad4ef10ed3b97c5d0a761ac Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 17:07:11 2017 +1200 Move HTTPApplication to SilverStripe\Control namespace commit e2c4a18f637bdd3d276619554de60ee8b4d95ced Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 16:29:03 2017 +1200 More documentation Fix up remaining tests Refactor temp DB into TempDatabase class so it’s available outside of unit tests. commit 5d235e64f341d6251bfe9f4833f15cc8593c5034 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 12:13:15 2017 +1200 API HTTPRequestBuilder::createFromEnvironment() now cleans up live globals BUG Fix issue with SSViewer Fix Security / View tests commit d88d4ed4e48291cb65407f222f190064b1f1deeb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:39:43 2017 +1200 API Refactor AppKernel into CoreKernel commit f7946aec3391139ae1b4029c353c327a36552b36 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:00:40 2017 +1200 Docs and minor cleanup commit 12bd31f9366327650b5c0c0f96cd0327d44faf0a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 15:34:34 2017 +1200 API Remove OutputMiddleware API Move environment / global / ini management into Environment class API Move getTempFolder into TempFolder class API Implement HTTPRequestBuilder / CLIRequestBuilder BUG Restore SS_ALLOWED_HOSTS check in original location API CoreKernel now requires $basePath to be passed in API Refactor installer.php to use application to bootstrap API move memstring conversion globals to Convert BUG Fix error in CoreKernel nesting not un-nesting itself properly. commit bba979114624247cf463cf2a8c9e4be9a7c3a772 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 18:07:53 2017 +1200 API Create HTTPMiddleware and standardise middleware for request handling commit 2a10c2397bdc53001013f607b5d38087ce6c0730 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:42:42 2017 +1200 Fixed ORM tests commit d75a8d1d93398af4bd0432df9e4bc6295c15a3fe Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:15:07 2017 +1200 FIx i18n tests commit 06364af3c379c931889c4cc34dd920fee3db204a Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 16:59:34 2017 +1200 Fix controller namespace Move states to sub namespace commit 2a278e2953d2dbb19f78d91c919048e1fc935436 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 12:49:45 2017 +1200 Fix forms namespace commit b65c21241bee019730027071d815dbf7571197a4 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:56:48 2017 +1200 Update API usages commit d1d4375c95a264a6b63cbaefc2c1d12f808bfd82 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:41:44 2017 +1200 API Refactor $flush into HTPPApplication API Enforce health check in Controller::pushCurrent() API Better global backup / restore Updated Director::test() to use new API commit b220534f06732db4fa940d8724c2a85c0ba2495a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 22:05:57 2017 +1200 Move app nesting to a test state helper commit 603704165c08d0c1c81fd5e6bb9506326eeee17b Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 21:46:04 2017 +1200 Restore kernel stack to fix multi-level nesting commit 2f6336a15bf79dc8c2edd44cec1931da2dd51c28 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 17:23:21 2017 +1200 API Implement kernel nesting commit fc7188da7d6ad6785354bab61f08700454c81d91 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:43:13 2017 +1200 Fix core tests commit a0ae7235148fffd71f2f02d1fe7fe45bf3aa39eb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:23:52 2017 +1200 Fix manifest tests commit ca033952513633e182040d3d13e1caa9000ca184 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:00:00 2017 +1200 API Move extension management into test state commit c66d4339777663a8a04661fea32a0cf35b95d20f Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 14:10:59 2017 +1200 API Refactor SapphireTest state management into SapphireTestState API Remove Injector::unregisterAllObjects() API Remove FakeController commit f26ae75c6ecaafa0dec1093264e0187191e6764d Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 18:04:34 2017 +1200 Implement basic CLI application object commit 001d5596621404892de0a5413392379eff990641 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 17:39:38 2017 +1200 Remove references to SapphireTest::is_running_test() Upgrade various code commit de079c041dacd96bc4f4b66421fa2b2cc4c320f8 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 7 18:07:33 2017 +1200 API Implement APP object API Refactor of Session
2017-06-22 12:50:45 +02:00
if (!isset($currentURL)) {
$request = Controller::curr()->getRequest();
$currentURL = $request->getURL(true);
}
$uri = $currentURL;
2016-11-29 00:31:16 +01:00
$isRelative = false;
// We need absolute URLs for parse_url()
if (Director::is_relative_url($uri)) {
$uri = Director::absoluteBaseURL() . $uri;
$isRelative = true;
}
// try to parse uri
2022-04-14 03:12:59 +02:00
$parts = parse_url($uri ?? '');
2016-11-29 00:31:16 +01:00
if (!$parts) {
throw new InvalidArgumentException("Can't parse URL: " . $uri);
}
// Parse params and add new variable
2018-06-12 13:50:37 +02:00
$params = [];
2016-11-29 00:31:16 +01:00
if (isset($parts['query'])) {
2022-04-14 03:12:59 +02:00
parse_str($parts['query'] ?? '', $params);
2016-11-29 00:31:16 +01:00
}
$params[$varname] = $varvalue;
// Generate URI segments and formatting
$scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
2018-06-12 13:50:37 +02:00
$user = (isset($parts['user']) && $parts['user'] != '') ? $parts['user'] : '';
2016-11-29 00:31:16 +01:00
if ($user != '') {
// format in either user:pass@host.com or user@host.com
$user .= (isset($parts['pass']) && $parts['pass'] != '') ? ':' . $parts['pass'] . '@' : '@';
}
$host = (isset($parts['host'])) ? $parts['host'] : '';
2018-01-16 19:39:30 +01:00
$port = (isset($parts['port']) && $parts['port'] != '') ? ':' . $parts['port'] : '';
2016-11-29 00:31:16 +01:00
$path = (isset($parts['path']) && $parts['path'] != '') ? $parts['path'] : '';
// handle URL params which are existing / new
$params = ($params) ? '?' . http_build_query($params, '', $separator) : '';
2016-11-29 00:31:16 +01:00
// keep fragments (anchors) intact.
2018-06-12 13:50:37 +02:00
$fragment = (isset($parts['fragment']) && $parts['fragment'] != '') ? '#' . $parts['fragment'] : '';
2016-11-29 00:31:16 +01:00
// Recompile URI segments
2018-06-12 13:50:37 +02:00
$newUri = $scheme . '://' . $user . $host . $port . $path . $params . $fragment;
2016-11-29 00:31:16 +01:00
if ($isRelative) {
return Director::baseURL() . Director::makeRelative($newUri);
2016-11-29 00:31:16 +01:00
}
return $newUri;
}
/**
* @param string $varname
* @param string $varvalue
* @param null|string $currentURL
*
* @return string
*/
public static function RAW_setGetVar($varname, $varvalue, $currentURL = null)
{
$url = self::setGetVar($varname, $varvalue, $currentURL);
return Convert::xml2raw($url);
}
/**
* Search for all tags with a specific attribute, then return the value of that attribute in a
* flat array.
*
* @param string $content
* @param array $attributes An array of tags to attributes, for example "[a] => 'href', [div] => 'id'"
*
* @return array
*/
public static function findByTagAndAttribute($content, $attributes)
{
2018-06-12 13:50:37 +02:00
$regexes = [];
2016-11-29 00:31:16 +01:00
foreach ($attributes as $tag => $attribute) {
$regexes[] = "/<{$tag} [^>]*$attribute *= *([\"'])(.*?)\\1[^>]*>/i";
$regexes[] = "/<{$tag} [^>]*$attribute *= *([^ \"'>]+)/i";
}
2018-06-12 13:50:37 +02:00
$result = [];
2016-11-29 00:31:16 +01:00
if ($regexes) {
foreach ($regexes as $regex) {
2022-04-14 03:12:59 +02:00
if (preg_match_all($regex ?? '', $content ?? '', $matches)) {
2016-11-29 00:31:16 +01:00
$result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
}
}
}
2022-04-14 03:12:59 +02:00
return count($result ?? []) ? $result : null;
2016-11-29 00:31:16 +01:00
}
/**
* @param string $content
*
* @return array
*/
public static function getLinksIn($content)
{
2018-06-12 13:50:37 +02:00
return self::findByTagAndAttribute($content, ["a" => "href"]);
2016-11-29 00:31:16 +01:00
}
/**
* @param string $content
*
* @return array
*/
public static function getImagesIn($content)
{
2018-06-12 13:50:37 +02:00
return self::findByTagAndAttribute($content, ["img" => "src"]);
2016-11-29 00:31:16 +01:00
}
/**
* Get the MIME type based on a file's extension. If the finfo class exists in PHP, and the file
* exists relative to the project root, then use that extension, otherwise fallback to a list of
* commonly known MIME types.
*
* @param string $filename
* @return string
*/
public static function get_mime_type($filename)
{
// If the finfo module is compiled into PHP, use it.
$path = BASE_PATH . DIRECTORY_SEPARATOR . $filename;
2022-04-14 03:12:59 +02:00
if (class_exists('finfo') && file_exists($path ?? '')) {
2016-11-29 00:31:16 +01:00
$finfo = new finfo(FILEINFO_MIME_TYPE);
return $finfo->file($path);
}
// Fallback to use the list from the HTTP.yml configuration and rely on the file extension
// to get the file mime-type
2022-04-14 03:12:59 +02:00
$ext = strtolower(File::get_file_extension($filename) ?? '');
2016-11-29 00:31:16 +01:00
// Get the mime-types
2017-02-22 04:14:53 +01:00
$mimeTypes = HTTP::config()->uninherited('MimeTypes');
2016-11-29 00:31:16 +01:00
// The mime type doesn't exist
if (!isset($mimeTypes[$ext])) {
return 'application/unknown';
}
return $mimeTypes[$ext];
}
/**
* Set the maximum age of this page in web caches, in seconds.
*
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead
2016-11-29 00:31:16 +01:00
* @param int $age
*/
public static function set_cache_age($age)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use HTTPCacheControlMiddleware::singleton()->setMaxAge($age) instead');
2016-11-29 00:31:16 +01:00
self::$cache_age = $age;
HTTPCacheControlMiddleware::singleton()->setMaxAge($age);
2016-11-29 00:31:16 +01:00
}
/**
* @param string $dateString
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use HTTPCacheControlMiddleware::registerModificationDate() instead
2016-11-29 00:31:16 +01:00
*/
public static function register_modification_date($dateString)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
HTTPCacheControlMiddleware::singleton()->registerModificationDate($dateString);
2016-11-29 00:31:16 +01:00
}
/**
* @param int $timestamp
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use HTTPCacheControlMiddleware::registerModificationDate() instead
2016-11-29 00:31:16 +01:00
*/
public static function register_modification_timestamp($timestamp)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
HTTPCacheControlMiddleware::singleton()->registerModificationDate($timestamp);
2016-11-29 00:31:16 +01:00
}
/**
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use ChangeDetectionMiddleware instead
2016-11-29 00:31:16 +01:00
* @param string $etag
*/
public static function register_etag($etag)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use ChangeDetectionMiddleware instead');
2022-04-14 03:12:59 +02:00
if (strpos($etag ?? '', '"') !== 0) {
$etag = "\"{$etag}\"";
2016-11-29 00:31:16 +01:00
}
self::$etag = $etag;
}
/**
* Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
* Note that setting HTTP::$cache_age will overrule any cache headers set by PHP's
* session_cache_limiter functionality. It is your responsibility to ensure only cacheable data
* is in fact cached, and HTTP::$cache_age isn't set when the HTTP body contains session-specific
* content.
*
* Omitting the $body argument or passing a string is deprecated; in these cases, the headers are
* output directly.
*
* @param HTTPResponse $response
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Headers are added automatically by HTTPCacheControlMiddleware instead.
2016-11-29 00:31:16 +01:00
*/
public static function add_cache_headers($response = null)
2016-11-29 00:31:16 +01:00
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Headers are added automatically by HTTPCacheControlMiddleware instead.');
2016-11-29 00:31:16 +01:00
2018-06-14 03:01:27 +02:00
// Skip if deprecated API is disabled
if (Config::inst()->get(HTTP::class, 'ignoreDeprecatedCaching')) {
return;
}
// Ensure a valid response object is provided
if (!$response instanceof HTTPResponse) {
user_error("HTTP::add_cache_headers() must be passed an HTTPResponse object", E_USER_WARNING);
2016-11-29 00:31:16 +01:00
return;
}
// Warn if already assigned cache-control headers
if ($response->getHeader('Cache-Control')) {
2018-06-12 13:50:37 +02:00
trigger_error(
'Cache-Control header has already been set. '
. 'Please use HTTPCacheControlMiddleware API to set caching options instead.',
E_USER_WARNING
);
return;
}
// Ensure a valid request object exists in the current context
if (!Injector::inst()->has(HTTPRequest::class)) {
user_error("HTTP::add_cache_headers() cannot work without a current HTTPRequest object", E_USER_WARNING);
return;
}
/** @var HTTPRequest $request */
$request = Injector::inst()->get(HTTPRequest::class);
2018-06-14 03:01:27 +02:00
// Run middleware
ChangeDetectionMiddleware::singleton()->process($request, function (HTTPRequest $request) use ($response) {
return HTTPCacheControlMiddleware::singleton()->process($request, function (HTTPRequest $request) use ($response) {
return $response;
});
});
}
/**
* Ensure that all deprecated HTTP cache settings are respected
*
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use HTTPCacheControlMiddleware instead
2022-11-15 06:20:54 +01:00
* Simply delete this method in CMS 5 and the calls to it from HTTPCacheControlMiddleware
*
2018-07-19 02:25:27 +02:00
* @throws \LogicException
2018-06-14 03:01:27 +02:00
* @param HTTPRequest $request
* @param HTTPResponse $response
*/
public static function augmentState(HTTPRequest $request, HTTPResponse $response)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use HTTPCacheControlMiddleware instead');
2018-06-14 03:01:27 +02:00
// Skip if deprecated API is disabled
$config = Config::forClass(HTTP::class);
if ($config->get('ignoreDeprecatedCaching')) {
return;
}
2018-06-12 13:50:37 +02:00
$cacheControlMiddleware = HTTPCacheControlMiddleware::singleton();
2018-06-12 13:50:37 +02:00
// if http caching is disabled by config, disable it - used on dev environments due to frequently changing
// templates and other data. will be overridden by forced publicCache(true) or privateCache(true) calls
2022-11-15 06:20:54 +01:00
Deprecation::withNoReplacement(function () use ($config, $cacheControlMiddleware) {
if ($config->get('disable_http_cache')) {
$cacheControlMiddleware->disableCache();
}
});
2018-06-12 13:50:37 +02:00
// if no caching ajax requests, disable ajax if is ajax request
2022-11-15 06:20:54 +01:00
Deprecation::withNoReplacement(function () use ($config, $cacheControlMiddleware) {
if (!$config->get('cache_ajax_requests') && Director::is_ajax()) {
$cacheControlMiddleware->disableCache();
}
});
2018-06-12 13:50:37 +02:00
// Pass vary to middleware
2022-11-15 06:20:54 +01:00
$configVary = Deprecation::withNoReplacement(function () use ($config) {
return $config->get('vary');
});
if ($configVary) {
$cacheControlMiddleware->addVary($configVary);
2018-06-12 13:50:37 +02:00
}
2018-07-19 02:25:27 +02:00
// Pass cache_control to middleware
2022-11-15 06:20:54 +01:00
$configCacheControl = Deprecation::withNoReplacement(function () use ($config) {
return $config->get('cache_control');
});
2018-07-19 02:25:27 +02:00
if ($configCacheControl) {
$supportedDirectives = ['max-age', 'no-cache', 'no-store', 'must-revalidate'];
2022-04-14 03:12:59 +02:00
if ($foundUnsupported = array_diff(array_keys($configCacheControl ?? []), $supportedDirectives)) {
2018-07-19 02:25:27 +02:00
throw new \LogicException(
'Found unsupported legacy directives in HTTP.cache_control: ' .
implode(', ', $foundUnsupported) .
'. Please use HTTPCacheControlMiddleware API instead'
);
}
if (isset($configCacheControl['max-age'])) {
$cacheControlMiddleware->setMaxAge($configCacheControl['max-age']);
}
if (isset($configCacheControl['no-cache'])) {
$cacheControlMiddleware->setNoCache((bool)$configCacheControl['no-cache']);
}
if (isset($configCacheControl['no-store'])) {
$cacheControlMiddleware->setNoStore((bool)$configCacheControl['no-store']);
}
if (isset($configCacheControl['must-revalidate'])) {
$cacheControlMiddleware->setMustRevalidate((bool)$configCacheControl['must-revalidate']);
}
}
// Set modification date
2018-06-12 13:50:37 +02:00
if (self::$modification_date) {
Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware::registerModificationDate() instead');
$cacheControlMiddleware->registerModificationDate(self::$modification_date);
2018-06-12 13:50:37 +02:00
}
2016-11-29 00:31:16 +01:00
// Ensure deprecated $etag property is assigned
if (self::$etag && !$cacheControlMiddleware->hasDirective('no-store') && !$response->getHeader('ETag')) {
Deprecation::notice('5.0', 'Etag should not be set explicitly');
$response->addHeader('ETag', self::$etag);
2016-11-29 00:31:16 +01:00
}
2018-06-12 13:50:37 +02:00
}
2016-11-29 00:31:16 +01:00
/**
* Return an {@link http://www.faqs.org/rfcs/rfc2822 RFC 2822} date in the GMT timezone (a timestamp
* is always in GMT: the number of seconds since January 1 1970 00:00:00 GMT)
*
* @param int $timestamp
2022-10-13 03:49:15 +02:00
* @deprecated 4.2.0 Use native php function gmdate() instead
2016-11-29 00:31:16 +01:00
* @return string
*/
public static function gmt_date($timestamp)
{
2022-10-13 03:49:15 +02:00
Deprecation::notice('4.2.0', 'Use native php function gmdate() instead');
2016-11-29 00:31:16 +01:00
return gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
}
/**
* Return static variable cache_age in second
*
* @return int
*/
public static function get_cache_age()
{
return self::$cache_age;
}
}