mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW Director::host() to determine host name of site
This commit is contained in:
parent
6e096f6172
commit
8c8231c03e
@ -20,6 +20,21 @@ if(PHP_SAPI != "cli" && PHP_SAPI != "cgi" && PHP_SAPI != "cgi-fcgi") {
|
||||
die();
|
||||
}
|
||||
|
||||
// We update the $_SERVER variable to contain data consistent with the rest of the application.
|
||||
$_SERVER = array_merge(array(
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'HTTP_ACCEPT' => 'text/plain;q=0.5',
|
||||
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
|
||||
'HTTP_ACCEPT_ENCODING' => '',
|
||||
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
|
||||
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
|
||||
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
|
||||
'SERVER_ADDR' => '127.0.0.1',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_USER_AGENT' => 'CLI',
|
||||
), $_SERVER);
|
||||
|
||||
/**
|
||||
* Identify the cli-script.php file and change to its container directory, so that require_once() works
|
||||
*/
|
||||
|
@ -87,3 +87,4 @@ SilverStripe core environment variables are listed here, though you're free to d
|
||||
| `SS_ALLOWED_HOSTS` | A comma deliminated list of hostnames the site is allowed to respond to |
|
||||
| `SS_MANIFESTCACHE` | The manifest cache to use (defaults to file based caching) |
|
||||
| `SS_IGNORE_DOT_ENV` | If set the .env file will be ignored. This is good for live to mitigate any performance implications of loading the .env file |
|
||||
| `SS_HOST` | The hostname to use when it isn't determinable by other means (eg: for CLI commands) |
|
||||
|
@ -44,25 +44,21 @@ This currently only works on UNIX like systems, not on Windows.
|
||||
|
||||
Sometimes SilverStripe needs to know the URL of your site. For example, when sending an email or generating static
|
||||
files. When you're visiting the site in a web browser this is easy to work out, but when executing scripts on the
|
||||
command line, it has no way of knowing. To work this out, add lines to your
|
||||
[_ss_environment.php](/getting_started/environment_management) file.
|
||||
command line, it has no way of knowing. To work this out, there are several ways to resolve this. You can set alternate
|
||||
base URLs, hosts, and protocol.
|
||||
|
||||
:::php
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
eg:
|
||||
|
||||
$_FILE_TO_URL_MAPPING['/Users/sminnee/Sites'] = 'http://localhost';
|
||||
```yml
|
||||
SilverStripe\Control\Director:
|
||||
alternate_base_url: 'https://example.com/'
|
||||
```
|
||||
|
||||
The above statement tells SilverStripe that anything executed under the `/Users/sminnee/Sites` directory will have the
|
||||
base URL `http://localhost`. The site `/Users/sminnee/Sites/my_silverstripe_project` will translate to the URL
|
||||
`http://localhost/my_silverstripe_project`.
|
||||
Alternatively you can use the `SS_HOST` environment variable to set a fallback hostname:
|
||||
|
||||
You can add multiple file to url mapping definitions. The most specific mapping will be used.
|
||||
|
||||
:::php
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
|
||||
$_FILE_TO_URL_MAPPING['/Users/sminnee/Sites'] = 'http://localhost';
|
||||
$_FILE_TO_URL_MAPPING['/Users/sminnee/Sites/my_silverstripe_project'] = 'http://project.localhost';
|
||||
```
|
||||
SS_HOST="localhost"
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
|
@ -1352,3 +1352,4 @@ logic early in the bootstrap, this is best placed in the `_config.php` files.
|
||||
* Removed support for _ss_environment.php in favour of .env and first class environment variables
|
||||
* Environment variables now can be set in `.env` file placed in webroot or one level above
|
||||
* Environment variables will be read from the environment as well
|
||||
* `$_FILE_TO_URL_MAPPING` has been removed and replaced with using `Director.alternate_host` or `SS_HOST` env var
|
||||
|
@ -58,8 +58,7 @@ Example `.env`:
|
||||
SS_DEFAULT_ADMIN_PASSWORD="password"
|
||||
|
||||
# Basic CLI hostname
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
$_FILE_TO_URL_MAPPING[__DIR__] = "http://localhost";
|
||||
SS_HOST="localhost";
|
||||
|
||||
|
||||
You will also need to be assigned the following permissions. Contact one of the SS staff from
|
||||
|
3
main.php
3
main.php
@ -188,8 +188,9 @@ if(!isset($databaseConfig) || !isset($databaseConfig['database']) || !$databaseC
|
||||
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
|
||||
die('SilverStripe Framework requires a $databaseConfig defined.');
|
||||
}
|
||||
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
|
||||
$s = (isset($_SERVER['SSL']) || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) ? 's' : '';
|
||||
$installURL = "http$s://" . $_SERVER['HTTP_HOST'] . BASE_URL . '/install.php';
|
||||
$installURL = "http$s://" . $host . BASE_URL . '/install.php';
|
||||
|
||||
// The above dirname() will equate to "\" on Windows when installing directly from http://localhost (not using
|
||||
// a sub-directory), this really messes things up in some browsers. Let's get rid of the backslashes
|
||||
|
@ -101,6 +101,13 @@ class Director implements TemplateGlobalProvider
|
||||
*/
|
||||
private static $alternate_protocol;
|
||||
|
||||
/**
|
||||
* @config
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $alternate_host;
|
||||
|
||||
/**
|
||||
* @config
|
||||
*
|
||||
@ -139,6 +146,15 @@ class Director implements TemplateGlobalProvider
|
||||
*/
|
||||
public static function direct($url, DataModel $model)
|
||||
{
|
||||
// check allowed hosts
|
||||
if (getenv('SS_ALLOWED_HOSTS') && !Director::is_cli()) {
|
||||
$all_allowed_hosts = explode(',', getenv('SS_ALLOWED_HOSTS'));
|
||||
if (!in_array(static::host(), $all_allowed_hosts)) {
|
||||
throw new HTTPResponse_Exception('Invalid Host', 400);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate $_FILES array before merging it with $_POST
|
||||
foreach ($_FILES as $k => $v) {
|
||||
if (is_array($v['tmp_name'])) {
|
||||
@ -551,6 +567,62 @@ class Director implements TemplateGlobalProvider
|
||||
return Controller::join_links($parent, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to determine the current hostname used to access the site.
|
||||
* The following are used to determine the host (in order)
|
||||
* - Director.alternate_host
|
||||
* - Director.alternate_base_url (if it contains a domain name)
|
||||
* - Trusted proxy headers
|
||||
* - HTTP Host header
|
||||
* - SS_HOST env var
|
||||
* - SERVER_NAME
|
||||
* - gethostname()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function host()
|
||||
{
|
||||
$headerOverride = false;
|
||||
if (TRUSTED_PROXY) {
|
||||
$headers = (getenv('SS_TRUSTED_PROXY_HOST_HEADER')) ? array(getenv('SS_TRUSTED_PROXY_HOST_HEADER')) : null;
|
||||
if (!$headers) {
|
||||
// Backwards compatible defaults
|
||||
$headers = array('HTTP_X_FORWARDED_HOST');
|
||||
}
|
||||
foreach ($headers as $header) {
|
||||
if (!empty($_SERVER[$header])) {
|
||||
// Get the first host, in case there's multiple separated through commas
|
||||
$headerOverride = strtok($_SERVER[$header], ',');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($host = static::config()->get('alternate_host')) {
|
||||
return $host;
|
||||
}
|
||||
|
||||
if ($baseURL = static::config()->get('alternate_base_url')) {
|
||||
if (preg_match('/^(http[^:]*:\/\/[^\/]+)(\/|$)/', $baseURL, $matches)) {
|
||||
return parse_url($baseURL, PHP_URL_HOST);
|
||||
}
|
||||
}
|
||||
|
||||
if ($headerOverride) {
|
||||
return $headerOverride;
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
return $_SERVER['HTTP_HOST'];
|
||||
}
|
||||
|
||||
if ($host = getenv('SS_HOST')) {
|
||||
return $host;
|
||||
}
|
||||
|
||||
return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : gethostname();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain part of the URL 'http://www.mysite.com'. Returns FALSE is this environment
|
||||
* variable isn't set.
|
||||
@ -559,31 +631,7 @@ class Director implements TemplateGlobalProvider
|
||||
*/
|
||||
public static function protocolAndHost()
|
||||
{
|
||||
$alternate = Config::inst()->get('SilverStripe\\Control\\Director', 'alternate_base_url');
|
||||
if ($alternate) {
|
||||
if (preg_match('/^(http[^:]*:\/\/[^\/]+)(\/|$)/', $alternate, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
return Director::protocol() . $_SERVER['HTTP_HOST'];
|
||||
} else {
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
if (Director::is_cli() && isset($_FILE_TO_URL_MAPPING)) {
|
||||
$errorSuggestion = ' You probably want to define ' .
|
||||
'an entry in $_FILE_TO_URL_MAPPING that covers "' . Director::baseFolder() . '"';
|
||||
} elseif (Director::is_cli()) {
|
||||
$errorSuggestion = ' You probably want to define $_FILE_TO_URL_MAPPING in ' .
|
||||
'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.com wiki';
|
||||
} else {
|
||||
$errorSuggestion = "";
|
||||
}
|
||||
|
||||
user_error("Director::protocolAndHost() lacks sufficient information - HTTP_HOST not set."
|
||||
. $errorSuggestion, E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
return static::protocol() . static::host();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -950,7 +998,7 @@ class Director implements TemplateGlobalProvider
|
||||
$login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
|
||||
}
|
||||
|
||||
return Director::protocol() . $login . $_SERVER['HTTP_HOST'] . Director::baseURL();
|
||||
return Director::protocol() . $login . static::host() . Director::baseURL();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1052,7 +1100,7 @@ class Director implements TemplateGlobalProvider
|
||||
*/
|
||||
public static function forceWWW()
|
||||
{
|
||||
if (!Director::isDev() && !Director::isTest() && strpos($_SERVER['HTTP_HOST'], 'www') !== 0) {
|
||||
if (!Director::isDev() && !Director::isTest() && strpos(static::host(), 'www') !== 0) {
|
||||
$destURL = str_replace(
|
||||
Director::protocol(),
|
||||
Director::protocol() . 'www.',
|
||||
@ -1191,11 +1239,7 @@ class Director implements TemplateGlobalProvider
|
||||
|
||||
// Check if we are running on one of the test servers
|
||||
$devServers = (array)Config::inst()->get('SilverStripe\\Control\\Director', 'dev_servers');
|
||||
if (isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], $devServers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return in_array(static::host(), $devServers);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1223,11 +1267,7 @@ class Director implements TemplateGlobalProvider
|
||||
|
||||
// Check if we are running on one of the test servers
|
||||
$testServers = (array)Config::inst()->get('SilverStripe\\Control\\Director', 'test_servers');
|
||||
if (isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], $testServers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return in_array(static::host(), $testServers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,80 +61,6 @@ if (!defined('TRUSTED_PROXY')) {
|
||||
define('TRUSTED_PROXY', $trusted);
|
||||
}
|
||||
|
||||
/**
|
||||
* A blank HTTP_HOST value is used to detect command-line execution.
|
||||
* We update the $_SERVER variable to contain data consistent with the rest of the application.
|
||||
*/
|
||||
if (!isset($_SERVER['HTTP_HOST'])) {
|
||||
// HTTP_HOST, REQUEST_PORT, SCRIPT_NAME, and PHP_SELF
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
if (isset($_FILE_TO_URL_MAPPING)) {
|
||||
$fullPath = $testPath = realpath($_SERVER['SCRIPT_FILENAME']);
|
||||
while ($testPath && $testPath != '/' && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
|
||||
if (isset($_FILE_TO_URL_MAPPING[$testPath])) {
|
||||
$url = $_FILE_TO_URL_MAPPING[$testPath]
|
||||
. str_replace(DIRECTORY_SEPARATOR, '/', substr($fullPath, strlen($testPath)));
|
||||
|
||||
$components = parse_url($url);
|
||||
$_SERVER['HTTP_HOST'] = $components['host'];
|
||||
if (!empty($components['port'])) {
|
||||
$_SERVER['HTTP_HOST'] .= ':' . $components['port'];
|
||||
}
|
||||
$_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'] = $components['path'];
|
||||
if (!empty($components['port'])) {
|
||||
$_SERVER['REQUEST_PORT'] = $components['port'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
$testPath = dirname($testPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Everything else
|
||||
$serverDefaults = array(
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'HTTP_ACCEPT' => 'text/plain;q=0.5',
|
||||
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
|
||||
'HTTP_ACCEPT_ENCODING' => '',
|
||||
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
|
||||
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
|
||||
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
|
||||
'SERVER_ADDR' => '127.0.0.1',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_USER_AGENT' => 'CLI',
|
||||
);
|
||||
|
||||
$_SERVER = array_merge($serverDefaults, $_SERVER);
|
||||
|
||||
/**
|
||||
* If we have an HTTP_HOST value, then we're being called from the webserver and there are some things that
|
||||
* need checking
|
||||
*/
|
||||
} else {
|
||||
|
||||
/**
|
||||
* Fix HTTP_HOST from reverse proxies
|
||||
*/
|
||||
$trustedProxyHeader = (defined('SS_TRUSTED_PROXY_HOST_HEADER'))
|
||||
? SS_TRUSTED_PROXY_HOST_HEADER
|
||||
: 'HTTP_X_FORWARDED_HOST';
|
||||
|
||||
if (TRUSTED_PROXY && !empty($_SERVER[$trustedProxyHeader])) {
|
||||
// Get the first host, in case there's multiple separated through commas
|
||||
$_SERVER['HTTP_HOST'] = strtok($_SERVER[$trustedProxyHeader], ',');
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by configured allowed hosts
|
||||
if (defined('SS_ALLOWED_HOSTS') && php_sapi_name() !== "cli") {
|
||||
$all_allowed_hosts = explode(',', SS_ALLOWED_HOSTS);
|
||||
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $all_allowed_hosts)) {
|
||||
header('HTTP/1.1 400 Invalid Host', true, 400);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define system paths
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Security\RandomGenerator;
|
||||
|
||||
/**
|
||||
@ -203,7 +204,7 @@ class ParameterConfirmationToken
|
||||
|
||||
$parts = array_filter(array(
|
||||
// What's our host
|
||||
$_SERVER['HTTP_HOST'],
|
||||
Director::host(),
|
||||
// SilverStripe base
|
||||
self::$alternateBaseURL !== null ? self::$alternateBaseURL : BASE_URL,
|
||||
// And URL including base script (eg: if it's index.php/page/url/)
|
||||
|
@ -64,31 +64,6 @@ class DevelopmentAdmin extends Controller
|
||||
return;
|
||||
}
|
||||
|
||||
// check for valid url mapping
|
||||
// lacking this information can cause really nasty bugs,
|
||||
// e.g. when running Director::test() from a FunctionalTest instance
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
if (Director::is_cli()) {
|
||||
if (isset($_FILE_TO_URL_MAPPING)) {
|
||||
$testPath = BASE_PATH;
|
||||
$matched = false;
|
||||
while ($testPath && $testPath != "/" && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
|
||||
if (isset($_FILE_TO_URL_MAPPING[$testPath])) {
|
||||
$matched = true;
|
||||
break;
|
||||
}
|
||||
$testPath = dirname($testPath);
|
||||
}
|
||||
if (!$matched) {
|
||||
echo 'Warning: You probably want to define '.
|
||||
'an entry in $_FILE_TO_URL_MAPPING that covers "' . Director::baseFolder() . '"' . "\n";
|
||||
}
|
||||
} else {
|
||||
echo 'Warning: You probably want to define $_FILE_TO_URL_MAPPING in '.
|
||||
'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.org wiki'."\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compat: Default to "draft" stage, which is important
|
||||
// for tasks like dev/build which call DataObject->requireDefaultRecords(),
|
||||
// but also for other administrative tasks which have assumptions about the default stage.
|
||||
|
@ -6,6 +6,22 @@ if (!$_SERVER) {
|
||||
$_SERVER = array();
|
||||
}
|
||||
|
||||
// We update the $_SERVER variable to contain data consistent with the rest of the application.
|
||||
$_SERVER = array_merge(array(
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'HTTP_ACCEPT' => 'text/plain;q=0.5',
|
||||
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
|
||||
'HTTP_ACCEPT_ENCODING' => '',
|
||||
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
|
||||
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
|
||||
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
|
||||
'SERVER_NAME' => 'localhost',
|
||||
'SERVER_ADDR' => '127.0.0.1',
|
||||
'REMOTE_ADDR' => '127.0.0.1',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'HTTP_USER_AGENT' => 'CLI',
|
||||
), $_SERVER);
|
||||
|
||||
$frameworkPath = dirname(dirname(__FILE__));
|
||||
$frameworkDir = basename($frameworkPath);
|
||||
|
||||
|
@ -42,7 +42,7 @@ class DevAdminControllerTest extends FunctionalTest
|
||||
public function testGoodRegisteredControllerOutput()
|
||||
{
|
||||
// Check for the controller running from the registered url above
|
||||
// (we use contains rather than equals because sometimes you get Warning: You probably want to define an entry in $_FILE_TO_URL_MAPPING)
|
||||
// (we use contains rather than equals because sometimes you get a warning)
|
||||
$this->assertContains(Controller1::OK_MSG, $this->getCapture('/dev/x1'));
|
||||
$this->assertContains(Controller1::OK_MSG, $this->getCapture('/dev/x1/y1'));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user