1132 lines
38 KiB
PHP
1132 lines
38 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\Dev\Install;
|
|
|
|
use Exception;
|
|
use InvalidArgumentException;
|
|
use SilverStripe\Core\TempFolder;
|
|
|
|
/**
|
|
* This class checks requirements
|
|
* Each of the requireXXX functions takes an argument which gives a user description of the test.
|
|
* It's an array of 3 parts:
|
|
* $description[0] - The test catetgory
|
|
* $description[1] - The test title
|
|
* $description[2] - The test error to show, if it goes wrong
|
|
*/
|
|
class InstallRequirements
|
|
{
|
|
protected $errors = [];
|
|
protected $warnings = [];
|
|
protected $tests = [];
|
|
|
|
/**
|
|
* Backup of original ini settings
|
|
* @var array
|
|
*/
|
|
protected $originalIni = [];
|
|
|
|
/**
|
|
* Check the database configuration. These are done one after another
|
|
* starting with checking the database function exists in PHP, and
|
|
* continuing onto more difficult checks like database permissions.
|
|
*
|
|
* @param array $databaseConfig The list of database parameters
|
|
* @return boolean Validity of database configuration details
|
|
*/
|
|
public function checkDatabase($databaseConfig)
|
|
{
|
|
// Check if support is available
|
|
if (!$this->requireDatabaseFunctions(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Database support",
|
|
"Database support in PHP",
|
|
$this->getDatabaseTypeNice($databaseConfig['type'])
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the server is available
|
|
$usePath = !empty($databaseConfig['path']) && empty($databaseConfig['server']);
|
|
if (!$this->requireDatabaseServer(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Database server",
|
|
$usePath
|
|
? "I couldn't write to path '$databaseConfig[path]'"
|
|
: "I couldn't find a database server on '$databaseConfig[server]'",
|
|
$usePath ? $databaseConfig['path'] : $databaseConfig['server']
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the connection credentials allow access to the server / database
|
|
if (!$this->requireDatabaseConnection(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Database access credentials",
|
|
"That username/password doesn't work"
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Check the necessary server version is available
|
|
if (!$this->requireDatabaseVersion(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Database server version requirement",
|
|
'',
|
|
'Version ' . $this->getDatabaseConfigurationHelper($databaseConfig['type'])->getDatabaseVersion($databaseConfig)
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Check that database creation permissions are available
|
|
if (!$this->requireDatabaseOrCreatePermissions(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Can I access/create the database",
|
|
"I can't create new databases and the database '$databaseConfig[database]' doesn't exist"
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Check alter permission (necessary to create tables etc)
|
|
if (!$this->requireDatabaseAlterPermissions(
|
|
$databaseConfig,
|
|
array(
|
|
"Database Configuration",
|
|
"Can I ALTER tables",
|
|
"I don't have permission to ALTER tables"
|
|
)
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Success!
|
|
return true;
|
|
}
|
|
|
|
public function checkAdminConfig($adminConfig)
|
|
{
|
|
if (!$adminConfig['username']) {
|
|
$this->error(array('', 'Please enter a username!'));
|
|
}
|
|
if (!$adminConfig['password']) {
|
|
$this->error(array('', 'Please enter a password!'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the web server is IIS and version greater than the given version.
|
|
*
|
|
* @param int $fromVersion
|
|
* @return bool
|
|
*/
|
|
public function isIIS($fromVersion = 7)
|
|
{
|
|
if (strpos($this->findWebserver(), 'IIS/') === false) {
|
|
return false;
|
|
}
|
|
return substr(strstr($this->findWebserver(), '/'), -3, 1) >= $fromVersion;
|
|
}
|
|
|
|
public function isApache()
|
|
{
|
|
if (strpos($this->findWebserver(), 'Apache') !== false) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the webserver software running on the PHP host.
|
|
* @return string|boolean Server software or boolean FALSE
|
|
*/
|
|
public function findWebserver()
|
|
{
|
|
// Try finding from SERVER_SIGNATURE or SERVER_SOFTWARE
|
|
if (!empty($_SERVER['SERVER_SIGNATURE'])) {
|
|
$webserver = $_SERVER['SERVER_SIGNATURE'];
|
|
} elseif (!empty($_SERVER['SERVER_SOFTWARE'])) {
|
|
$webserver = $_SERVER['SERVER_SOFTWARE'];
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return strip_tags(trim($webserver));
|
|
}
|
|
|
|
/**
|
|
* Check everything except the database
|
|
*/
|
|
public function check()
|
|
{
|
|
$this->errors = [];
|
|
$isApache = $this->isApache();
|
|
$isIIS = $this->isIIS();
|
|
$webserver = $this->findWebserver();
|
|
|
|
$this->requirePHPVersion('5.5.0', '5.5.0', array(
|
|
"PHP Configuration",
|
|
"PHP5 installed",
|
|
null,
|
|
"PHP version " . phpversion()
|
|
));
|
|
|
|
// Check that we can identify the root folder successfully
|
|
$this->requireFile('framework/src/Dev/Install/config-form.html', array(
|
|
"File permissions",
|
|
"Does the webserver know where files are stored?",
|
|
"The webserver isn't letting me identify where files are stored.",
|
|
$this->getBaseDir()
|
|
));
|
|
|
|
$this->requireModule('mysite', array(
|
|
"File permissions",
|
|
"mysite/ directory exists?",
|
|
''
|
|
));
|
|
$this->requireModule('framework', array(
|
|
"File permissions",
|
|
"framework/ directory exists?",
|
|
'',
|
|
));
|
|
|
|
if ($isApache) {
|
|
$this->checkApacheVersion(array(
|
|
"Webserver Configuration",
|
|
"Webserver is not Apache 1.x",
|
|
"SilverStripe requires Apache version 2 or greater",
|
|
$webserver
|
|
));
|
|
$this->requireWriteable('.htaccess', array("File permissions", "Is the .htaccess file writeable?", null));
|
|
} elseif ($isIIS) {
|
|
$this->requireWriteable('web.config', array("File permissions", "Is the web.config file writeable?", null));
|
|
}
|
|
|
|
$this->requireWriteable('mysite/_config.php', array(
|
|
"File permissions",
|
|
"Is the mysite/_config.php file writeable?",
|
|
null
|
|
));
|
|
|
|
$this->requireWriteable('mysite/_config/config.yml', array(
|
|
"File permissions",
|
|
"Is the mysite/_config/config.yml file writeable?",
|
|
null
|
|
));
|
|
|
|
if (!$this->checkModuleExists('cms')) {
|
|
$this->requireWriteable('mysite/code/RootURLController.php', array(
|
|
"File permissions",
|
|
"Is the mysite/code/RootURLController.php file writeable?",
|
|
null
|
|
));
|
|
}
|
|
$this->requireWriteable('assets', array("File permissions", "Is the assets/ directory writeable?", null));
|
|
|
|
try {
|
|
$tempFolder = TempFolder::getTempFolder(BASE_PATH);
|
|
} catch (Exception $e) {
|
|
$tempFolder = false;
|
|
}
|
|
|
|
$this->requireTempFolder(array('File permissions', 'Is a temporary directory available?', null, $tempFolder));
|
|
if ($tempFolder) {
|
|
// in addition to the temp folder being available, check it is writable
|
|
$this->requireWriteable($tempFolder, array(
|
|
"File permissions",
|
|
sprintf("Is the temporary directory writeable?", $tempFolder),
|
|
null
|
|
), true);
|
|
}
|
|
|
|
// Check for web server, unless we're calling the installer from the command-line
|
|
$this->isRunningWebServer(array("Webserver Configuration", "Server software", "Unknown", $webserver));
|
|
|
|
if ($isApache) {
|
|
$this->requireApacheRewriteModule('mod_rewrite', array(
|
|
"Webserver Configuration",
|
|
"URL rewriting support",
|
|
"You need mod_rewrite to use friendly URLs with SilverStripe, but it is not enabled."
|
|
));
|
|
} elseif ($isIIS) {
|
|
$this->requireIISRewriteModule('IIS_UrlRewriteModule', array(
|
|
"Webserver Configuration",
|
|
"URL rewriting support",
|
|
"You need to enable the IIS URL Rewrite Module to use friendly URLs with SilverStripe, "
|
|
. "but it is not installed or enabled. Download it for IIS 7 from http://www.iis.net/expand/URLRewrite"
|
|
));
|
|
} else {
|
|
$this->warning(array(
|
|
"Webserver Configuration",
|
|
"URL rewriting support",
|
|
"I can't tell whether any rewriting module is running. You may need to configure a rewriting rule yourself."
|
|
));
|
|
}
|
|
|
|
$this->requireServerVariables(array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME'), array(
|
|
"Webserver Configuration",
|
|
"Recognised webserver",
|
|
"You seem to be using an unsupported webserver. "
|
|
. "The server variables SCRIPT_NAME, HTTP_HOST, SCRIPT_FILENAME need to be set."
|
|
));
|
|
|
|
$this->requirePostSupport(array(
|
|
"Webserver Configuration",
|
|
"POST Support",
|
|
'I can\'t find $_POST, make sure POST is enabled.'
|
|
));
|
|
|
|
// Check for GD support
|
|
if (!$this->requireFunction("imagecreatetruecolor", array(
|
|
"PHP Configuration",
|
|
"GD2 support",
|
|
"PHP must have GD version 2."
|
|
))
|
|
) {
|
|
$this->requireFunction("imagecreate", array(
|
|
"PHP Configuration",
|
|
"GD2 support",
|
|
"GD support for PHP not included."
|
|
));
|
|
}
|
|
|
|
// Check for XML support
|
|
$this->requireFunction('xml_set_object', array(
|
|
"PHP Configuration",
|
|
"XML support",
|
|
"XML support not included in PHP."
|
|
));
|
|
$this->requireClass('DOMDocument', array(
|
|
"PHP Configuration",
|
|
"DOM/XML support",
|
|
"DOM/XML support not included in PHP."
|
|
));
|
|
$this->requireFunction('simplexml_load_file', array(
|
|
'PHP Configuration',
|
|
'SimpleXML support',
|
|
'SimpleXML support not included in PHP.'
|
|
));
|
|
|
|
// Check for token_get_all
|
|
$this->requireFunction('token_get_all', array(
|
|
"PHP Configuration",
|
|
"Tokenizer support",
|
|
"Tokenizer support not included in PHP."
|
|
));
|
|
|
|
// Check for CType support
|
|
$this->requireFunction('ctype_digit', array(
|
|
'PHP Configuration',
|
|
'CType support',
|
|
'CType support not included in PHP.'
|
|
));
|
|
|
|
// Check for session support
|
|
$this->requireFunction('session_start', array(
|
|
'PHP Configuration',
|
|
'Session support',
|
|
'Session support not included in PHP.'
|
|
));
|
|
|
|
// Check for iconv support
|
|
$this->requireFunction('iconv', array(
|
|
'PHP Configuration',
|
|
'iconv support',
|
|
'iconv support not included in PHP.'
|
|
));
|
|
|
|
// Check for hash support
|
|
$this->requireFunction('hash', array('PHP Configuration', 'hash support', 'hash support not included in PHP.'));
|
|
|
|
// Check for mbstring support
|
|
$this->requireFunction('mb_internal_encoding', array(
|
|
'PHP Configuration',
|
|
'mbstring support',
|
|
'mbstring support not included in PHP.'
|
|
));
|
|
|
|
// Check for Reflection support
|
|
$this->requireClass('ReflectionClass', array(
|
|
'PHP Configuration',
|
|
'Reflection support',
|
|
'Reflection support not included in PHP.'
|
|
));
|
|
|
|
// Check for Standard PHP Library (SPL) support
|
|
$this->requireFunction('spl_classes', array(
|
|
'PHP Configuration',
|
|
'SPL support',
|
|
'Standard PHP Library (SPL) not included in PHP.'
|
|
));
|
|
|
|
$this->requireDateTimezone(array(
|
|
'PHP Configuration',
|
|
'date.timezone setting and validity',
|
|
'date.timezone option in php.ini must be set correctly.',
|
|
$this->getOriginalIni('date.timezone')
|
|
));
|
|
|
|
$this->suggestClass('finfo', array(
|
|
'PHP Configuration',
|
|
'fileinfo support',
|
|
'fileinfo should be enabled in PHP. SilverStripe uses it for MIME type detection of files. '
|
|
. 'SilverStripe will still operate, but email attachments and sending files to browser '
|
|
. '(e.g. export data to CSV) may not work correctly without finfo.'
|
|
));
|
|
|
|
$this->suggestFunction('curl_init', array(
|
|
'PHP Configuration',
|
|
'curl support',
|
|
'curl should be enabled in PHP. SilverStripe uses it for consuming web services'
|
|
. ' via the RestfulService class and many modules rely on it.'
|
|
));
|
|
|
|
$this->suggestClass('tidy', array(
|
|
'PHP Configuration',
|
|
'tidy support',
|
|
'Tidy provides a library of code to clean up your html. '
|
|
. 'SilverStripe will operate fine without tidy but HTMLCleaner will not be effective.'
|
|
));
|
|
|
|
$this->suggestPHPSetting('asp_tags', array(false), array(
|
|
'PHP Configuration',
|
|
'asp_tags option',
|
|
'This should be turned off as it can cause issues with SilverStripe'
|
|
));
|
|
$this->requirePHPSetting('magic_quotes_gpc', array(false), array(
|
|
'PHP Configuration',
|
|
'magic_quotes_gpc option',
|
|
'This should be turned off, as it can cause issues with cookies. '
|
|
. 'More specifically, unserializing data stored in cookies.'
|
|
));
|
|
$this->suggestPHPSetting('display_errors', array(false), array(
|
|
'PHP Configuration',
|
|
'display_errors option',
|
|
'Unless you\'re in a development environment, this should be turned off, '
|
|
. 'as it can expose sensitive data to website users.'
|
|
));
|
|
// on some weirdly configured webservers arg_separator.output is set to &
|
|
// which will results in links like ?param=value&foo=bar which will not be i
|
|
$this->suggestPHPSetting('arg_separator.output', array('&', ''), array(
|
|
'PHP Configuration',
|
|
'arg_separator.output option',
|
|
'This option defines how URL parameters are concatenated. '
|
|
. 'If not set to \'&\' this may cause issues with URL GET parameters'
|
|
));
|
|
|
|
// always_populate_raw_post_data should be set to -1 if PHP < 7.0
|
|
if (version_compare(PHP_VERSION, '7.0.0', '<')) {
|
|
$this->suggestPHPSetting('always_populate_raw_post_data', ['-1'], [
|
|
'PHP Configuration',
|
|
'always_populate_raw_post_data option',
|
|
'It\'s highly recommended to set this to \'-1\' in php 5.x, as $HTTP_RAW_POST_DATA is removed in php 7'
|
|
]);
|
|
}
|
|
|
|
// Check memory allocation
|
|
$this->requireMemory(32 * 1024 * 1024, 64 * 1024 * 1024, array(
|
|
"PHP Configuration",
|
|
"Memory allocation (PHP config option 'memory_limit')",
|
|
"SilverStripe needs a minimum of 32M allocated to PHP, but recommends 64M.",
|
|
$this->getOriginalIni("memory_limit")
|
|
));
|
|
|
|
return $this->errors;
|
|
}
|
|
|
|
/**
|
|
* Get ini setting
|
|
*
|
|
* @param string $settingName
|
|
* @return mixed
|
|
*/
|
|
protected function getOriginalIni($settingName)
|
|
{
|
|
if (isset($this->originalIni[$settingName])) {
|
|
return $this->originalIni[$settingName];
|
|
}
|
|
return ini_get($settingName);
|
|
}
|
|
|
|
public function suggestPHPSetting($settingName, $settingValues, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
// special case for display_errors, check the original value before
|
|
// it was changed at the start of this script.
|
|
$val = $this->getOriginalIni($settingName);
|
|
|
|
if (!in_array($val, $settingValues) && $val != $settingValues) {
|
|
$this->warning($testDetails, "$settingName is set to '$val' in php.ini. $testDetails[2]");
|
|
}
|
|
}
|
|
|
|
public function requirePHPSetting($settingName, $settingValues, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
$val = $this->getOriginalIni($settingName);
|
|
if (!in_array($val, $settingValues) && $val != $settingValues) {
|
|
$this->error($testDetails, "$settingName is set to '$val' in php.ini. $testDetails[2]");
|
|
}
|
|
}
|
|
|
|
public function suggestClass($class, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
if (!class_exists($class)) {
|
|
$this->warning($testDetails);
|
|
}
|
|
}
|
|
|
|
public function suggestFunction($class, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
if (!function_exists($class)) {
|
|
$this->warning($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireDateTimezone($testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$val = $this->getOriginalIni('date.timezone');
|
|
$result = $val && in_array($val, timezone_identifiers_list());
|
|
if (!$result) {
|
|
$this->error($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireMemory($min, $recommended, $testDetails)
|
|
{
|
|
$_SESSION['forcemem'] = false;
|
|
|
|
$mem = $this->getPHPMemory();
|
|
$memLimit = $this->getOriginalIni("memory_limit");
|
|
if ($mem < (64 * 1024 * 1024)) {
|
|
ini_set('memory_limit', '64M');
|
|
$mem = $this->getPHPMemory();
|
|
$testDetails[3] = $memLimit;
|
|
}
|
|
|
|
$this->testing($testDetails);
|
|
|
|
if ($mem < $min && $mem > 0) {
|
|
$message = $testDetails[2] . " You only have " . $memLimit . " allocated";
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
} elseif ($mem < $recommended && $mem > 0) {
|
|
$message = $testDetails[2] . " You only have " . $memLimit . " allocated";
|
|
$this->warning($testDetails, $message);
|
|
return false;
|
|
} elseif ($mem == 0) {
|
|
$message = $testDetails[2] . " We can't determine how much memory you have allocated. "
|
|
. "Install only if you're sure you've allocated at least 20 MB.";
|
|
$this->warning($testDetails, $message);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function getPHPMemory()
|
|
{
|
|
$memString = $this->getOriginalIni("memory_limit");
|
|
|
|
switch (strtolower(substr($memString, -1))) {
|
|
case "k":
|
|
return round(substr($memString, 0, -1) * 1024);
|
|
|
|
case "m":
|
|
return round(substr($memString, 0, -1) * 1024 * 1024);
|
|
|
|
case "g":
|
|
return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
|
|
|
|
default:
|
|
return round($memString);
|
|
}
|
|
}
|
|
|
|
|
|
public function listErrors()
|
|
{
|
|
if ($this->errors) {
|
|
echo "<p>The following problems are preventing me from installing SilverStripe CMS:</p>\n\n";
|
|
foreach ($this->errors as $error) {
|
|
echo "<li>" . htmlentities(implode(", ", $error), ENT_COMPAT, 'UTF-8') . "</li>\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
public function showTable($section = null)
|
|
{
|
|
if ($section) {
|
|
$tests = $this->tests[$section];
|
|
$id = strtolower(str_replace(' ', '_', $section));
|
|
echo "<table id=\"{$id}_results\" class=\"testResults\" width=\"100%\">";
|
|
foreach ($tests as $test => $result) {
|
|
echo "<tr class=\"$result[0]\"><td>$test</td><td>"
|
|
. nl2br(htmlentities($result[1], ENT_COMPAT, 'UTF-8')) . "</td></tr>";
|
|
}
|
|
echo "</table>";
|
|
} else {
|
|
foreach ($this->tests as $section => $tests) {
|
|
$failedRequirements = 0;
|
|
$warningRequirements = 0;
|
|
|
|
$output = "";
|
|
|
|
foreach ($tests as $test => $result) {
|
|
if (isset($result['0'])) {
|
|
switch ($result['0']) {
|
|
case 'error':
|
|
$failedRequirements++;
|
|
break;
|
|
case 'warning':
|
|
$warningRequirements++;
|
|
break;
|
|
}
|
|
}
|
|
$output .= "<tr class=\"$result[0]\"><td>$test</td><td>"
|
|
. nl2br(htmlentities($result[1], ENT_COMPAT, 'UTF-8')) . "</td></tr>";
|
|
}
|
|
$className = "good";
|
|
$text = "All Requirements Pass";
|
|
$pluralWarnings = ($warningRequirements == 1) ? 'Warning' : 'Warnings';
|
|
|
|
if ($failedRequirements > 0) {
|
|
$className = "error";
|
|
$pluralWarnings = ($warningRequirements == 1) ? 'Warning' : 'Warnings';
|
|
|
|
$text = $failedRequirements . ' Failed and ' . $warningRequirements . ' ' . $pluralWarnings;
|
|
} elseif ($warningRequirements > 0) {
|
|
$className = "warning";
|
|
$text = "All Requirements Pass but " . $warningRequirements . ' ' . $pluralWarnings;
|
|
}
|
|
|
|
echo "<h5 class='requirement $className'>$section <a href='#'>Show All Requirements</a> <span>$text</span></h5>";
|
|
echo "<table class=\"testResults\">";
|
|
echo $output;
|
|
echo "</table>";
|
|
}
|
|
}
|
|
}
|
|
|
|
public function requireFunction($funcName, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
if (!function_exists($funcName)) {
|
|
$this->error($testDetails);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function requireClass($className, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
if (!class_exists($className)) {
|
|
$this->error($testDetails);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Require that the given class doesn't exist
|
|
*
|
|
* @param array $classNames
|
|
* @param array $testDetails
|
|
* @return bool
|
|
*/
|
|
public function requireNoClasses($classNames, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$badClasses = array();
|
|
foreach ($classNames as $className) {
|
|
if (class_exists($className)) {
|
|
$badClasses[] = $className;
|
|
}
|
|
}
|
|
if ($badClasses) {
|
|
$message = $testDetails[2] . ". The following classes are at fault: " . implode(', ', $badClasses);
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function checkApacheVersion($testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
$is1pointx = preg_match('#Apache[/ ]1\.#', $testDetails[3]);
|
|
if ($is1pointx) {
|
|
$this->error($testDetails);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function requirePHPVersion($recommendedVersion, $requiredVersion, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
$installedVersion = phpversion();
|
|
|
|
if (version_compare($installedVersion, $requiredVersion, '<')) {
|
|
$message = "SilverStripe requires PHP version $requiredVersion or later.\n
|
|
PHP version $installedVersion is currently installed.\n
|
|
While SilverStripe requires at least PHP version $requiredVersion, upgrading to $recommendedVersion or later is recommended.\n
|
|
If you are installing SilverStripe on a shared web server, please ask your web hosting provider to upgrade PHP for you.";
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
}
|
|
|
|
if (version_compare($installedVersion, $recommendedVersion, '<')) {
|
|
$message = "PHP version $installedVersion is currently installed.\n
|
|
Upgrading to at least PHP version $recommendedVersion is recommended.\n
|
|
SilverStripe should run, but you may run into issues. Future releases may require a later version of PHP.\n";
|
|
$this->warning($testDetails, $message);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check that a module exists
|
|
*
|
|
* @param string $dirname
|
|
* @return bool
|
|
*/
|
|
public function checkModuleExists($dirname)
|
|
{
|
|
$path = $this->getBaseDir() . $dirname;
|
|
return file_exists($path) && ($dirname == 'mysite' || file_exists($path . '/_config.php'));
|
|
}
|
|
|
|
/**
|
|
* The same as {@link requireFile()} but does additional checks
|
|
* to ensure the module directory is intact.
|
|
*
|
|
* @param string $dirname
|
|
* @param array $testDetails
|
|
*/
|
|
public function requireModule($dirname, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$path = $this->getBaseDir() . $dirname;
|
|
if (!file_exists($path)) {
|
|
$testDetails[2] .= " Directory '$path' not found. Please make sure you have uploaded the SilverStripe files to your webserver correctly.";
|
|
$this->error($testDetails);
|
|
} elseif (!file_exists($path . '/_config.php') && $dirname != 'mysite') {
|
|
$testDetails[2] .= " Directory '$path' exists, but is missing files. Please make sure you have uploaded "
|
|
. "the SilverStripe files to your webserver correctly.";
|
|
$this->error($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireFile($filename, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$filename = $this->getBaseDir() . $filename;
|
|
if (!file_exists($filename)) {
|
|
$testDetails[2] .= " (file '$filename' not found)";
|
|
$this->error($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireWriteable($filename, $testDetails, $absolute = false)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
if ($absolute) {
|
|
$filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
|
|
} else {
|
|
$filename = $this->getBaseDir() . str_replace('/', DIRECTORY_SEPARATOR, $filename);
|
|
}
|
|
|
|
if (file_exists($filename)) {
|
|
$isWriteable = is_writeable($filename);
|
|
} else {
|
|
$isWriteable = is_writeable(dirname($filename));
|
|
}
|
|
|
|
if (!$isWriteable) {
|
|
if (function_exists('posix_getgroups')) {
|
|
$userID = posix_geteuid();
|
|
$user = posix_getpwuid($userID);
|
|
|
|
$currentOwnerID = fileowner(file_exists($filename) ? $filename : dirname($filename));
|
|
$currentOwner = posix_getpwuid($currentOwnerID);
|
|
|
|
$testDetails[2] .= "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe "
|
|
. "file is currently owned by '$currentOwner[name]'. ";
|
|
|
|
if ($user['name'] == $currentOwner['name']) {
|
|
$testDetails[2] .= "We recommend that you make the file writeable.";
|
|
} else {
|
|
$groups = posix_getgroups();
|
|
$groupList = array();
|
|
foreach ($groups as $group) {
|
|
$groupInfo = posix_getgrgid($group);
|
|
if (in_array($currentOwner['name'], $groupInfo['members'])) {
|
|
$groupList[] = $groupInfo['name'];
|
|
}
|
|
}
|
|
if ($groupList) {
|
|
$testDetails[2] .= " We recommend that you make the file group-writeable "
|
|
. "and change the group to one of these groups:\n - " . implode("\n - ", $groupList)
|
|
. "\n\nFor example:\nchmod g+w $filename\nchgrp " . $groupList[0] . " $filename";
|
|
} else {
|
|
$testDetails[2] .= " There is no user-group that contains both the web-server user and the "
|
|
. "owner of this file. Change the ownership of the file, create a new group, or "
|
|
. "temporarily make the file writeable by everyone during the install process.";
|
|
}
|
|
}
|
|
} else {
|
|
$testDetails[2] .= "The webserver user needs to be able to write to this file:\n$filename";
|
|
}
|
|
|
|
$this->error($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireTempFolder($testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
try {
|
|
$tempFolder = TempFolder::getTempFolder(BASE_PATH);
|
|
} catch (Exception $e) {
|
|
$tempFolder = false;
|
|
}
|
|
|
|
if (!$tempFolder) {
|
|
$testDetails[2] = "Permission problem gaining access to a temp directory. " .
|
|
"Please create a folder named silverstripe-cache in the base directory " .
|
|
"of the installation and ensure it has the adequate permissions.";
|
|
$this->error($testDetails);
|
|
}
|
|
}
|
|
|
|
public function requireApacheModule($moduleName, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
if (!in_array($moduleName, apache_get_modules())) {
|
|
$this->error($testDetails);
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public function testApacheRewriteExists($moduleName = 'mod_rewrite')
|
|
{
|
|
if (function_exists('apache_get_modules') && in_array($moduleName, apache_get_modules())) {
|
|
return true;
|
|
}
|
|
if (isset($_SERVER['HTTP_MOD_REWRITE']) && $_SERVER['HTTP_MOD_REWRITE'] == 'On') {
|
|
return true;
|
|
}
|
|
if (isset($_SERVER['REDIRECT_HTTP_MOD_REWRITE']) && $_SERVER['REDIRECT_HTTP_MOD_REWRITE'] == 'On') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function testIISRewriteModuleExists($moduleName = 'IIS_UrlRewriteModule')
|
|
{
|
|
if (isset($_SERVER[$moduleName]) && $_SERVER[$moduleName]) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireApacheRewriteModule($moduleName, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
if ($this->testApacheRewriteExists()) {
|
|
return true;
|
|
} else {
|
|
$this->warning($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the web server has any rewriting capability.
|
|
* @return boolean
|
|
*/
|
|
public function hasRewritingCapability()
|
|
{
|
|
return ($this->testApacheRewriteExists() || $this->testIISRewriteModuleExists());
|
|
}
|
|
|
|
public function requireIISRewriteModule($moduleName, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
if ($this->testIISRewriteModuleExists()) {
|
|
return true;
|
|
} else {
|
|
$this->warning($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function getDatabaseTypeNice($databaseClass)
|
|
{
|
|
return substr($databaseClass, 0, -8);
|
|
}
|
|
|
|
/**
|
|
* Get an instance of a helper class for the specific database.
|
|
*
|
|
* @param string $databaseClass e.g. MySQLDatabase or MSSQLDatabase
|
|
* @return DatabaseConfigurationHelper
|
|
*/
|
|
public function getDatabaseConfigurationHelper($databaseClass)
|
|
{
|
|
return DatabaseAdapterRegistry::getDatabaseConfigurationHelper($databaseClass);
|
|
}
|
|
|
|
public function requireDatabaseFunctions($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
if (!$helper) {
|
|
$this->error($testDetails, "Couldn't load database helper code for " . $databaseConfig['type']);
|
|
return false;
|
|
}
|
|
$result = $helper->requireDatabaseFunctions($databaseConfig);
|
|
if ($result) {
|
|
return true;
|
|
} else {
|
|
$this->error($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireDatabaseConnection($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
$result = $helper->requireDatabaseConnection($databaseConfig);
|
|
if ($result['success']) {
|
|
return true;
|
|
} else {
|
|
$testDetails[2] .= ": " . $result['error'];
|
|
$this->error($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireDatabaseVersion($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
if (method_exists($helper, 'requireDatabaseVersion')) {
|
|
$result = $helper->requireDatabaseVersion($databaseConfig);
|
|
if ($result['success']) {
|
|
return true;
|
|
} else {
|
|
$testDetails[2] .= $result['error'];
|
|
$this->warning($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
// Skipped test because this database has no required version
|
|
return true;
|
|
}
|
|
|
|
public function requireDatabaseServer($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
$result = $helper->requireDatabaseServer($databaseConfig);
|
|
if ($result['success']) {
|
|
return true;
|
|
} else {
|
|
$message = $testDetails[2] . ": " . $result['error'];
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireDatabaseOrCreatePermissions($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
$result = $helper->requireDatabaseOrCreatePermissions($databaseConfig);
|
|
if ($result['success']) {
|
|
if ($result['alreadyExists']) {
|
|
$testDetails[3] = "Database $databaseConfig[database]";
|
|
} else {
|
|
$testDetails[3] = "Able to create a new database";
|
|
}
|
|
$this->testing($testDetails);
|
|
return true;
|
|
} else {
|
|
if (empty($result['cannotCreate'])) {
|
|
$message = $testDetails[2] . ". Please create the database manually.";
|
|
} else {
|
|
$message = $testDetails[2] . " (user '$databaseConfig[username]' doesn't have CREATE DATABASE permissions.)";
|
|
}
|
|
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireDatabaseAlterPermissions($databaseConfig, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
|
$result = $helper->requireDatabaseAlterPermissions($databaseConfig);
|
|
if ($result['success']) {
|
|
return true;
|
|
} else {
|
|
$message = "Silverstripe cannot alter tables. This won't prevent installation, however it may "
|
|
. "cause issues if you try to run a /dev/build once installed.";
|
|
$this->warning($testDetails, $message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function requireServerVariables($varNames, $testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
$missing = array();
|
|
|
|
foreach ($varNames as $varName) {
|
|
if (!isset($_SERVER[$varName]) || !$_SERVER[$varName]) {
|
|
$missing[] = '$_SERVER[' . $varName . ']';
|
|
}
|
|
}
|
|
|
|
if (!$missing) {
|
|
return true;
|
|
}
|
|
|
|
$message = $testDetails[2] . " (the following PHP variables are missing: " . implode(", ", $missing) . ")";
|
|
$this->error($testDetails, $message);
|
|
return false;
|
|
}
|
|
|
|
|
|
public function requirePostSupport($testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
|
|
if (!isset($_POST)) {
|
|
$this->error($testDetails);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function isRunningWebServer($testDetails)
|
|
{
|
|
$this->testing($testDetails);
|
|
if ($testDetails[3]) {
|
|
return true;
|
|
} else {
|
|
$this->warning($testDetails);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Must be PHP4 compatible
|
|
var $baseDir;
|
|
|
|
public function getBaseDir()
|
|
{
|
|
return BASE_PATH . '/';
|
|
}
|
|
|
|
public function testing($testDetails)
|
|
{
|
|
if (!$testDetails) {
|
|
return;
|
|
}
|
|
|
|
$section = $testDetails[0];
|
|
$test = $testDetails[1];
|
|
|
|
$message = "OK";
|
|
if (isset($testDetails[3])) {
|
|
$message .= " ($testDetails[3])";
|
|
}
|
|
|
|
$this->tests[$section][$test] = array("good", $message);
|
|
}
|
|
|
|
public function error($testDetails, $message = null)
|
|
{
|
|
if (!is_array($testDetails)) {
|
|
throw new InvalidArgumentException("Invalid error");
|
|
}
|
|
$section = $testDetails[0];
|
|
$test = $testDetails[1];
|
|
if (!$message && isset($testDetails[2])) {
|
|
$message = $testDetails[2];
|
|
}
|
|
|
|
$this->tests[$section][$test] = array("error", $message);
|
|
$this->errors[] = $testDetails;
|
|
}
|
|
|
|
public function warning($testDetails, $message = null)
|
|
{
|
|
if (!is_array($testDetails)) {
|
|
throw new InvalidArgumentException("Invalid warning");
|
|
}
|
|
$section = $testDetails[0];
|
|
$test = $testDetails[1];
|
|
if (!$message && isset($testDetails[2])) {
|
|
$message = $testDetails[2];
|
|
}
|
|
|
|
$this->tests[$section][$test] = array("warning", $message);
|
|
$this->warnings[] = $testDetails;
|
|
}
|
|
|
|
public function hasErrors()
|
|
{
|
|
return sizeof($this->errors);
|
|
}
|
|
|
|
public function hasWarnings()
|
|
{
|
|
return sizeof($this->warnings);
|
|
}
|
|
}
|