diff --git a/core/Constants.php b/core/Constants.php new file mode 100644 index 000000000..1895f56ab --- /dev/null +++ b/core/Constants.php @@ -0,0 +1,165 @@ + '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 magic quotes setting + */ + if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { + if($_REQUEST) stripslashes_recursively($_REQUEST); + if($_GET) stripslashes_recursively($_GET); + if($_POST) stripslashes_recursively($_POST); + } + + /** + * Fix HTTP_HOST from reverse proxies + */ + if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { + $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; + } +} + +/** + * Define system paths + */ +if(!defined('BASE_PATH')) { + // Assuming that this file is sapphire/core/Core.php we can then determine the base path + define('BASE_PATH', rtrim(dirname(dirname(dirname(__FILE__)))), DIRECTORY_SEPARATOR); +} +if(!defined('BASE_URL')) { + // Determine the base URL by comparing SCRIPT_NAME to SCRIPT_FILENAME and getting the common + // elements + if(substr($_SERVER['SCRIPT_FILENAME'],0,strlen(BASE_PATH)) == BASE_PATH) { + $urlSegmentToRemove = substr($_SERVER['SCRIPT_FILENAME'],strlen(BASE_PATH)); + if(substr($_SERVER['SCRIPT_NAME'],-strlen($urlSegmentToRemove)) == $urlSegmentToRemove) { + $baseURL = substr($_SERVER['SCRIPT_NAME'], 0, -strlen($urlSegmentToRemove)); + define('BASE_URL', rtrim($baseURL, DIRECTORY_SEPARATOR)); + } + } + + // If that didn't work, failover to the old syntax. Hopefully this isn't necessary, and maybe + // if can be phased out? + if(!defined('BASE_URL')) { + $dir = (strpos($_SERVER['SCRIPT_NAME'], 'index.php') !== false) + ? dirname($_SERVER['SCRIPT_NAME']) + : dirname(dirname($_SERVER['SCRIPT_NAME'])); + define('BASE_URL', rtrim($dir, DIRECTORY_SEPARATOR)); + } +} +define('MODULES_DIR', 'modules'); +define('MODULES_PATH', BASE_PATH . '/' . MODULES_DIR); +define('THEMES_DIR', 'themes'); +define('THEMES_PATH', BASE_PATH . '/' . THEMES_DIR); +define('SAPPHIRE_DIR', 'sapphire'); +define('SAPPHIRE_PATH', BASE_PATH . '/' . SAPPHIRE_DIR); +define('CMS_DIR', 'cms'); +define('CMS_PATH', BASE_PATH . '/' . CMS_DIR); +define('THIRDPARTY_DIR', SAPPHIRE_DIR . '/thirdparty'); +define('THIRDPARTY_PATH', BASE_PATH . '/' . THIRDPARTY_DIR); +define('ASSETS_DIR', 'assets'); +define('ASSETS_PATH', BASE_PATH . '/' . ASSETS_DIR); + +/////////////////////////////////////////////////////////////////////////////// +// INCLUDES + +set_include_path(BASE_PATH . '/sapphire' . PATH_SEPARATOR +. BASE_PATH . '/sapphire/parsers' . PATH_SEPARATOR +. BASE_PATH . '/sapphire/thirdparty' . PATH_SEPARATOR +. get_include_path()); + +/** + * Define the temporary folder if it wasn't defined yet + */ +require_once(dirname(__FILE__).'/TempPath.php'); + +if(!defined('TEMP_FOLDER')) { + define('TEMP_FOLDER', getTempFolder()); +} diff --git a/core/Core.php b/core/Core.php index dede6cbb2..5d2d2308b 100755 --- a/core/Core.php +++ b/core/Core.php @@ -3,43 +3,26 @@ * This file is the Sapphire bootstrap. It will get your environment ready to call Director::direct(). * * It takes care of: - * - Including _ss_environment.php - * - Normalisation of $_SERVER values - * - Initialisation of necessary constants (mostly paths) + * - Including Constants.php to include _ss_environment and initialise necessary constants * - Checking of PHP memory limit * - Including all the files needed to get the manifest built * - Building and including the manifest - * - * Initialized constants: - * - BASE_URL: Full URL to the webroot, e.g. "http://my-host.com/my-webroot" (no trailing slash). - * - BASE_PATH: Absolute path to the webroot, e.g. "/var/www/my-webroot" (no trailing slash). - * See Director::baseFolder(). Can be overwritten by Director::setBaseFolder(). - * - TEMP_FOLDER: Absolute path to temporary folder, used for manifest and template caches. Example: "/var/tmp" - * See getTempFolder(). No trailing slash. - * - MODULES_DIR: Not used at the moment - * - MODULES_PATH: Not used at the moment - * - THEMES_DIR: Path relative to webroot, e.g. "themes" - * - THEMES_PATH: Absolute filepath, e.g. "/var/www/my-webroot/themes" - * - CMS_DIR: Path relative to webroot, e.g. "cms" - * - CMS_PATH: Absolute filepath, e.g. "/var/www/my-webroot/cms" - * - SAPPHIRE_DIR: Path relative to webroot, e.g. "sapphire" - * - SAPPHIRE_PATH:Absolute filepath, e.g. "/var/www/my-webroot/sapphire" - * - THIRDPARTY_DIR: Path relative to webroot, e.g. "sapphire/thirdparty" - * - THIRDPARTY_PATH: Absolute filepath, e.g. "/var/www/my-webroot/sapphire/thirdparty" - * + * * @todo This file currently contains a lot of bits and pieces, and its various responsibilities should probably be - * moved into different subsystems. - * @todo A lot of this stuff is very order-independent; for example, the require_once calls have to happen after the defines.' - * This could be decoupled. + * moved into different subsystems. + * @todo A lot of this stuff is very order-dependent. This could be decoupled. + * * @package sapphire * @subpackage core */ -/////////////////////////////////////////////////////////////////////////////// -// ENVIRONMENT CONFIG +/** + * Set up error reporting + */ if(defined('E_DEPRECATED')) error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT)); else error_reporting(E_ALL); + /* * This is for versions of PHP prior to version 5.2 * Creating this here will allow both web requests and cron jobs to inherit it. @@ -55,128 +38,9 @@ if (!function_exists('array_fill_keys')) { } /** - * Include _ss_environment.php files + * Include Constants (if it hasn't already been included) to pull in BASE_PATH, etc */ -$envFiles = array('_ss_environment.php', '../_ss_environment.php', '../../_ss_environment.php', '../../../_ss_environment.php'); -foreach($envFiles as $envFile) { - if(@file_exists($envFile)) { - define('SS_ENVIRONMENT_FILE', $envFile); - include_once($envFile); - break; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// GLOBALS AND DEFINE SETTING - -/** - * 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 - 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))); - - $_SERVER['HTTP_HOST'] = parse_url($url, PHP_URL_HOST); - $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'] = parse_url($url, PHP_URL_PATH); - $_SERVER['REQUEST_PORT'] = parse_url($url, PHP_URL_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 magic quotes setting - */ - if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { - if($_REQUEST) stripslashes_recursively($_REQUEST); - if($_GET) stripslashes_recursively($_GET); - if($_POST) stripslashes_recursively($_POST); - } - - /** - * Fix HTTP_HOST from reverse proxies - */ - if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; - } -} - -/** - * Define system paths - */ -if(!defined('BASE_PATH')) { - // Assuming that this file is sapphire/core/Core.php we can then determine the base path - define('BASE_PATH', rtrim(dirname(dirname(dirname(__FILE__)))), DIRECTORY_SEPARATOR); -} -if(!defined('BASE_URL')) { - // Determine the base URL by comparing SCRIPT_NAME to SCRIPT_FILENAME and getting the common - // elements - if(substr($_SERVER['SCRIPT_FILENAME'],0,strlen(BASE_PATH)) == BASE_PATH) { - $urlSegmentToRemove = substr($_SERVER['SCRIPT_FILENAME'],strlen(BASE_PATH)); - if(substr($_SERVER['SCRIPT_NAME'],-strlen($urlSegmentToRemove)) == $urlSegmentToRemove) { - $baseURL = substr($_SERVER['SCRIPT_NAME'], 0, -strlen($urlSegmentToRemove)); - define('BASE_URL', rtrim($baseURL, DIRECTORY_SEPARATOR)); - } - } - - // If that didn't work, failover to the old syntax. Hopefully this isn't necessary, and maybe - // if can be phased out? - if(!defined('BASE_URL')) { - $dir = (strpos($_SERVER['SCRIPT_NAME'], 'index.php') !== false) - ? dirname($_SERVER['SCRIPT_NAME']) - : dirname(dirname($_SERVER['SCRIPT_NAME'])); - define('BASE_URL', rtrim($dir, DIRECTORY_SEPARATOR)); - } -} -define('MODULES_DIR', 'modules'); -define('MODULES_PATH', BASE_PATH . '/' . MODULES_DIR); -define('THEMES_DIR', 'themes'); -define('THEMES_PATH', BASE_PATH . '/' . THEMES_DIR); -define('SAPPHIRE_DIR', 'sapphire'); -define('SAPPHIRE_PATH', BASE_PATH . '/' . SAPPHIRE_DIR); -define('CMS_DIR', 'cms'); -define('CMS_PATH', BASE_PATH . '/' . CMS_DIR); -define('THIRDPARTY_DIR', SAPPHIRE_DIR . '/thirdparty'); -define('THIRDPARTY_PATH', BASE_PATH . '/' . THIRDPARTY_DIR); -define('ASSETS_DIR', 'assets'); -define('ASSETS_PATH', BASE_PATH . '/' . ASSETS_DIR); - -/** - * Define the temporary folder if it wasn't defined yet - */ -require_once(dirname(__FILE__).'/TempPath.php'); -if(!defined('TEMP_FOLDER')) { - define('TEMP_FOLDER', getTempFolder()); -} +require_once dirname(__FILE__).'/Constants.php'; /** * Priorities definition. These constants are used in calls to _t() as an optional argument @@ -190,14 +54,6 @@ define('PR_LOW',10); */ increase_memory_limit_to('64M'); -/////////////////////////////////////////////////////////////////////////////// -// INCLUDES - -set_include_path(BASE_PATH . '/sapphire' . PATH_SEPARATOR - . BASE_PATH . '/sapphire/parsers' . PATH_SEPARATOR - . BASE_PATH . '/sapphire/thirdparty' . PATH_SEPARATOR - . get_include_path()); - /** * Sapphire class autoloader. Requires the ManifestBuilder to work. * $_CLASS_MANIFEST must have been loaded up by ManifestBuilder for this to successfully load diff --git a/core/startup/ErrorControlChain.php b/core/startup/ErrorControlChain.php index 9cd344ee0..aa2b825fa 100644 --- a/core/startup/ErrorControlChain.php +++ b/core/startup/ErrorControlChain.php @@ -9,8 +9,6 @@ * Normal errors are suppressed even past the end of the chain. Fatal errors are only suppressed until the end * of the chain - the request will then die silently. * - * The exception is if an error occurs and BASE_URL is not yet set - in that case the error is never suppressed. - * * Usage: * * $chain = new ErrorControlChain(); @@ -68,8 +66,8 @@ class ErrorControlChain { return $this->then($callback, null); } - public function handleError() { - if ($this->suppression && defined('BASE_URL')) throw new Exception('Generic Error'); + public function handleError($errno, $errstr) { + if ((error_reporting() & $errno) == $errno && $this->suppression) throw new Exception('Generic Error'); else return false; } @@ -79,7 +77,7 @@ class ErrorControlChain { } public function handleFatalError() { - if ($this->handleFatalErrors && $this->suppression && defined('BASE_URL')) { + if ($this->handleFatalErrors && $this->suppression) { if ($this->lastErrorWasFatal()) { ob_clean(); $this->error = true; @@ -89,7 +87,7 @@ class ErrorControlChain { } public function execute() { - set_error_handler(array($this, 'handleError'), error_reporting()); + set_error_handler(array($this, 'handleError')); register_shutdown_function(array($this, 'handleFatalError')); $this->handleFatalErrors = true; @@ -105,7 +103,7 @@ class ErrorControlChain { call_user_func($step['callback'], $this); } catch (Exception $e) { - if ($this->suppression && defined('BASE_URL')) $this->error = true; + if ($this->suppression) $this->error = true; else throw $e; } } diff --git a/core/startup/ParameterConfirmationToken.php b/core/startup/ParameterConfirmationToken.php index e0ccf9d87..21364f871 100644 --- a/core/startup/ParameterConfirmationToken.php +++ b/core/startup/ParameterConfirmationToken.php @@ -16,17 +16,7 @@ class ParameterConfirmationToken { protected $token = null; protected function pathForToken($token) { - if (defined('BASE_PATH')) { - $basepath = BASE_PATH; - } - else { - $basepath = rtrim(dirname(dirname(dirname(dirname(__FILE__)))), DIRECTORY_SEPARATOR); - } - - require_once(dirname(dirname(__FILE__)).'/TempPath.php'); - $tempfolder = getTempFolder($basepath ? $basepath : DIRECTORY_SEPARATOR); - - return $tempfolder.'/token_'.preg_replace('/[^a-z0-9]+/', '', $token); + return TEMP_FOLDER.'/token_'.preg_replace('/[^a-z0-9]+/', '', $token); } protected function genToken() { diff --git a/main.php b/main.php index 91cc90a6e..82b1df24f 100644 --- a/main.php +++ b/main.php @@ -54,128 +54,127 @@ if(version_compare(phpversion(), 5, '<')) { * @see Director::direct() */ -require_once('core/startup/ErrorControlChain.php'); -require_once('core/startup/ParameterConfirmationToken.php'); +/** + * Include the defines that set BASE_PATH, etc + */ +require_once('core/Constants.php'); -class SilverStripeMain { - static $token; +/** + * Figure out the request URL + */ +global $url; - static function filterFlush($chain) { - self::$token = new ParameterConfirmationToken('flush'); +// IIS will sometimes generate this. +if(!empty($_SERVER['HTTP_X_ORIGINAL_URL'])) { + $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; +} - if (isset($_GET['flush']) && !self::$token->tokenProvided()) { - unset($_GET['flush']); - } - else { - $chain->setSuppression(false); - } +// Apache rewrite rules use this +if (isset($_GET['url'])) { + $url = $_GET['url']; + // IIS includes get variables in url + $i = strpos($url, '?'); + if($i !== false) { + $url = substr($url, 0, $i); } - static function includeCore() { - /** - * Include Sapphire's core code - */ - require_once("core/Core.php"); - - if (function_exists('mb_http_output')) { - mb_http_output('UTF-8'); - mb_internal_encoding('UTF-8'); - } - - Session::start(); - } - - static function parseURL() { - global $url; - - // IIS will sometimes generate this. - if(!empty($_SERVER['HTTP_X_ORIGINAL_URL'])) { - $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; - } - - // Apache rewrite rules use this - if (isset($_GET['url'])) { - $url = $_GET['url']; - // IIS includes get variables in url - $i = strpos($url, '?'); - if($i !== false) { - $url = substr($url, 0, $i); - } - - // Lighttpd uses this - } else { - if(strpos($_SERVER['REQUEST_URI'],'?') !== false) { - list($url, $query) = explode('?', $_SERVER['REQUEST_URI'], 2); - parse_str($query, $_GET); - if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET); - } else { - $url = $_SERVER["REQUEST_URI"]; - } - } - - // Remove base folders from the URL if webroot is hosted in a subfolder - if (substr(strtolower($url), 0, strlen(BASE_URL)) == strtolower(BASE_URL)) $url = substr($url, strlen(BASE_URL)); - } - - static function startupDatabase() { - if (isset($_GET['debug_profile'])) { - Profiler::init(); - Profiler::mark('all_execution'); - Profiler::mark('main.php init'); - } - - // Connect to database - require_once("core/model/DB.php"); - global $databaseConfig; - - if (isset($_GET['debug_profile'])) Profiler::mark('DB::connect'); - if ($databaseConfig) DB::connect($databaseConfig); - if (isset($_GET['debug_profile'])) Profiler::unmark('DB::connect'); - } - - static function flushIfAllowed() { - if (self::$token->parameterProvided() && !self::$token->tokenProvided()) { - // First, check if we're in dev mode, or the database doesn't have any security data - $canFlush = Director::isDev() || !Security::database_is_ready(); - - // Otherwise, we start up the session if needed, then check for admin - if (!$canFlush) { - if(!isset($_SESSION) && (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))) { - Session::start(); - } - - if (Permission::check('ADMIN')) { - $canFlush = true; - } - else { - $loginPage = Director::absoluteURL(Config::inst()->get('Security', 'login_url')); - $loginPage .= "?BackURL=" . urlencode($_SERVER['REQUEST_URI']); - - header('location: '.$loginPage, true, 302); - die; - } - } - - // And if we can flush, reload with an authority token - if ($canFlush) self::$token->reloadWithToken(); - } - } - - static function flushIfErrored() { - if (self::$token->parameterProvided() && !self::$token->tokenProvided()) { - self::$token->reloadWithToken(); - } + // Lighttpd uses this +} else { + if(strpos($_SERVER['REQUEST_URI'],'?') !== false) { + list($url, $query) = explode('?', $_SERVER['REQUEST_URI'], 2); + parse_str($query, $_GET); + if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET); + } else { + $url = $_SERVER["REQUEST_URI"]; } } +// Remove base folders from the URL if webroot is hosted in a subfolder +if (substr(strtolower($url), 0, strlen(BASE_URL)) == strtolower(BASE_URL)) $url = substr($url, strlen(BASE_URL)); + +/** + * Include SilverStripe's core code + */ +require_once('core/startup/ErrorControlChain.php'); +require_once('core/startup/ParameterConfirmationToken.php'); + $chain = new ErrorControlChain(); +$token = new ParameterConfirmationToken('flush'); + +function silverstripe_main($chain) { + global $token; + + if (isset($_GET['flush']) && !$token->tokenProvided()) { + unset($_GET['flush']); + } + else { + $chain->setSuppression(false); + } + + /** + * Include Sapphire's core code + */ + require_once("core/Core.php"); + + if (function_exists('mb_http_output')) { + mb_http_output('UTF-8'); + mb_internal_encoding('UTF-8'); + } + + Session::start(); + + if (isset($_GET['debug_profile'])) { + Profiler::init(); + Profiler::mark('all_execution'); + Profiler::mark('main.php init'); + } + + // Connect to database + require_once("core/model/DB.php"); + global $databaseConfig; + + if (isset($_GET['debug_profile'])) Profiler::mark('DB::connect'); + if ($databaseConfig) DB::connect($databaseConfig); + if (isset($_GET['debug_profile'])) Profiler::unmark('DB::connect'); + + if ($token->parameterProvided() && !$token->tokenProvided()) { + // First, check if we're in dev mode, or the database doesn't have any security data + $canFlush = Director::isDev() || !Security::database_is_ready(); + + // Otherwise, we start up the session if needed, then check for admin + if (!$canFlush) { + if(!isset($_SESSION) && (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))) { + Session::start(); + } + + if (Permission::check('ADMIN')) { + $canFlush = true; + } + else { + $loginPage = Director::absoluteURL('Security/login'); + $loginPage .= "?BackURL=" . urlencode($_SERVER['REQUEST_URI']); + + header('location: '.$loginPage, true, 302); + die; + } + } + + // And if we can flush, reload with an authority token + if ($canFlush) $token->reloadWithToken(); + } +} + +function silverstripe_main_flushOnError() { + global $token; + + if ($token->parameterProvided() && !$token->tokenProvided()) { + $token->reloadWithToken(); + } +} + $chain - ->then(array('SilverStripeMain', 'filterFlush')) - ->then(array('SilverStripeMain', 'includeCore')) - ->thenAlways(array('SilverStripeMain', 'parseURL')) - ->then(array('SilverStripeMain', 'startupDatabase')) - ->then(array('SilverStripeMain', 'flushIfAllowed')) - ->thenIfErrored(array('SilverStripeMain', 'flushIfErrored')) + ->then('silverstripe_main') + ->thenIfErrored('silverstripe_main_flushOnError') ->execute(); // Redirect to the installer if no database is selected