diff --git a/Dev/Install/DatabaseAdapterRegistry.php b/Dev/Install/DatabaseAdapterRegistry.php
index 5420cf5d0..695bc4eaf 100644
--- a/Dev/Install/DatabaseAdapterRegistry.php
+++ b/Dev/Install/DatabaseAdapterRegistry.php
@@ -2,6 +2,8 @@
namespace SilverStripe\Dev\Install;
+use InvalidArgumentException;
+
/**
* This class keeps track of the available database adapters
* and provides a meaning of registering community built
@@ -51,25 +53,46 @@ class DatabaseAdapterRegistry {
/**
* Add new adapter to the registry
*
- * @param array $config Associative array of configuration details
+ * @param array $config Associative array of configuration details. This must include:
+ * - title
+ * - class
+ * - helperClass
+ * - supported
+ * This SHOULD include:
+ * - fields
+ * - helperPath (if helperClass can't be autoloaded via psr-4/-0)
+ * - missingExtensionText
+ * - module OR missingModuleText
*/
public static function register($config) {
- $missingExtensionText = isset($config['missingExtensionText'])
- ? $config['missingExtensionText']
- : 'The PHP extension is missing, please enable or install it.';
+ // Validate config
+ $missing = array_diff(['title', 'class', 'helperClass', 'supported'], array_keys($config));
+ if($missing) {
+ throw new InvalidArgumentException(
+ "Missing database helper config keys: '" . implode("', '", $missing) . "'"
+ );
+ }
- $path = explode('/', $config['helperPath']);
- $moduleName = array_shift($path);
- $missingModuleText = isset($config['missingModuleText'])
- ? $config['missingModuleText']
- : 'The SilverStripe module, '.$moduleName.', is missing or incomplete.'
- . ' Please download it.';
+ // Guess missing module text if not given
+ if (empty($config['missingModuleText'])) {
+ if (empty($config['module'])) {
+ $moduleText = 'Module for database connector ' . $config['title'] . 'is missing.';
+ } else {
+ $moduleText = "The SilverStripe module '" . $config['module'] . "' is missing.";
+ }
+ $config['missingModuleText'] = $moduleText
+ . ' Please install it via composer or from http://addons.silverstripe.org/.';
+ }
- $config['missingModuleText'] = $missingModuleText;
- $config['missingExtensionText'] = $missingExtensionText;
+ // Set missing text
+ if (empty($config['missingExtensionText'])) {
+ $config['missingExtensionText'] = 'The PHP extension is missing, please enable or install it.';
+ }
// set default fields if none are defined already
- if(!isset($config['fields'])) $config['fields'] = self::$default_fields;
+ if(!isset($config['fields'])) {
+ $config['fields'] = self::$default_fields;
+ }
self::$adapters[$config['class']] = $config;
}
@@ -137,4 +160,26 @@ class DatabaseAdapterRegistry {
return self::$default_fields;
}
+ /**
+ * Build configuration helper for a given class
+ *
+ * @param string $databaseClass Name of class
+ * @return DatabaseConfigurationHelper|null Instance of helper, or null if cannot be loaded
+ */
+ public static function getDatabaseConfigurationHelper($databaseClass) {
+ $adapters = static::get_adapters();
+ if(empty($adapters[$databaseClass]) || empty($adapters[$databaseClass]['helperClass'])) {
+ return null;
+ }
+
+ // Load if path given
+ if (isset($adapters[$databaseClass]['helperPath'])) {
+ include_once $adapters[$databaseClass]['helperPath'];
+ }
+
+ // Construct
+ $class = $adapters[$databaseClass]['helperClass'];
+ return (class_exists($class)) ? new $class() : null;
+ }
+
}
diff --git a/Dev/Install/MySQLDatabaseConfigurationHelper.php b/Dev/Install/MySQLDatabaseConfigurationHelper.php
index d3bb15940..8d1bb563d 100644
--- a/Dev/Install/MySQLDatabaseConfigurationHelper.php
+++ b/Dev/Install/MySQLDatabaseConfigurationHelper.php
@@ -18,6 +18,7 @@ class MySQLDatabaseConfigurationHelper implements DatabaseConfigurationHelper {
/**
* Create a connection of the appropriate type
*
+ * @skipUpgrade
* @param array $databaseConfig
* @param string $error Error message passed by value
* @return mixed|null Either the connection object, or null if error
@@ -26,7 +27,7 @@ class MySQLDatabaseConfigurationHelper implements DatabaseConfigurationHelper {
$error = null;
try {
switch($databaseConfig['type']) {
- case 'SilverStripe\\ORM\\Connect\\MySQLDatabase':
+ case 'MySQLDatabase':
$conn = @new MySQLi($databaseConfig['server'], $databaseConfig['username'],
$databaseConfig['password']);
if($conn && empty($conn->connect_errno)) {
@@ -50,7 +51,7 @@ class MySQLDatabaseConfigurationHelper implements DatabaseConfigurationHelper {
return null;
}
default:
- $error = 'Invalid connection type';
+ $error = 'Invalid connection type: ' . $databaseConfig['type'];
return null;
}
} catch(Exception $ex) {
diff --git a/Dev/Install/config-form.html b/Dev/Install/config-form.html
index b0bc4f574..318917aa6 100644
--- a/Dev/Install/config-form.html
+++ b/Dev/Install/config-form.html
@@ -6,7 +6,7 @@
SilverStripe CMS / Framework Installation
-
+
diff --git a/Dev/Install/install.php5 b/Dev/Install/install.php5
index ae935b691..e7431035a 100755
--- a/Dev/Install/install.php5
+++ b/Dev/Install/install.php5
@@ -8,7 +8,13 @@
** **
************************************************************************************
************************************************************************************/
+use SilverStripe\Control\Controller;
+use SilverStripe\Core\Startup\ParameterConfirmationToken;
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
+use SilverStripe\Dev\Install\DatabaseConfigurationHelper;
+use SilverStripe\ORM\DatabaseAdmin;
+use SilverStripe\ORM\DB;
+use SilverStripe\Security\Security;
/**
* SilverStripe CMS Installer
@@ -116,14 +122,10 @@ $locales = array(
DatabaseAdapterRegistry::autodiscover();
// Determine which external database modules are USABLE
-foreach(DatabaseAdapterRegistry::get_adapters() as $class => $details) {
- $databaseClasses[$class] = $details;
- if(file_exists($details['helperPath'])) {
- $databaseClasses[$class]['hasModule'] = true;
- include_once($details['helperPath']);
- } else {
- $databaseClasses[$class]['hasModule'] = false;
- }
+$databaseClasses = DatabaseAdapterRegistry::get_adapters();
+foreach($databaseClasses as $class => $details) {
+ $helper = DatabaseAdapterRegistry::getDatabaseConfigurationHelper($class);
+ $databaseClasses[$class]['hasModule'] = !empty($helper);
}
// Load database config
@@ -182,7 +184,9 @@ if(file_exists('mysite/_config.php')) {
// Find the $database variable in the relevant config file without having to execute the config file
if(preg_match("/\\\$database\s*=\s*[^\n\r]+[\n\r]/", file_get_contents("mysite/_config.php"), $parts)) {
eval($parts[0]);
- if($database) $alreadyInstalled = true;
+ if(!empty($database)) {
+ $alreadyInstalled = true;
+ }
// Assume that if $databaseConfig is defined in mysite/_config.php, then a non-environment-based installation has
// already gone ahead
} else if(preg_match("/\\\$databaseConfig\s*=\s*[^\n\r]+[\n\r]/", file_get_contents("mysite/_config.php"), $parts)) {
@@ -290,7 +294,9 @@ class InstallRequirements {
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
+ ? "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;
@@ -351,7 +357,9 @@ class InstallRequirements {
/**
* Check if the web server is IIS and version greater than the given version.
- * @return boolean
+ *
+ * @param int $fromVersion
+ * @return bool
*/
public function isIIS($fromVersion = 7) {
if(strpos($this->findWebserver(), 'IIS/') === false) {
@@ -656,8 +664,7 @@ class InstallRequirements {
}
if(!in_array($val, $settingValues) && $val != $settingValues) {
- $testDetails[2] = "$settingName is set to '$val' in php.ini. $testDetails[2]";
- $this->warning($testDetails);
+ $this->warning($testDetails, "$settingName is set to '$val' in php.ini. $testDetails[2]");
}
}
@@ -666,8 +673,7 @@ class InstallRequirements {
$val = ini_get($settingName);
if(!in_array($val, $settingValues) && $val != $settingValues) {
- $testDetails[2] = "$settingName is set to '$val' in php.ini. $testDetails[2]";
- $this->error($testDetails);
+ $this->error($testDetails, "$settingName is set to '$val' in php.ini. $testDetails[2]");
}
}
@@ -709,16 +715,20 @@ class InstallRequirements {
$this->testing($testDetails);
if($mem < $min && $mem > 0) {
- $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
- $this->error($testDetails);
+ $message = $testDetails[2] . " You only have " . ini_get("memory_limit") . " allocated";
+ $this->error($testDetails, $message);
+ return false;
} else if($mem < $recommended && $mem > 0) {
- $testDetails[2] .= " You only have " . ini_get("memory_limit") . " allocated";
- $this->warning($testDetails);
+ $message = $testDetails[2] . " You only have " . ini_get("memory_limit") . " allocated";
+ $this->warning($testDetails, $message);
+ return false;
} elseif($mem == 0) {
- $testDetails[2] .= " We can't determine how much memory you have allocated. "
+ $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);
+ $this->warning($testDetails, $message);
+ return false;
}
+ return true;
}
public function getPHPMemory() {
@@ -807,35 +817,41 @@ class InstallRequirements {
if(!function_exists($funcName)) {
$this->error($testDetails);
- } else {
- return true;
+ return false;
}
+ return true;
}
public function requireClass($className, $testDetails) {
$this->testing($testDetails);
if(!class_exists($className)) {
$this->error($testDetails);
- } else {
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(class_exists($className)) {
+ $badClasses[] = $className;
+ }
}
if($badClasses) {
- $testDetails[2] .= ". The following classes are at fault: " . implode(', ', $badClasses);
- $this->error($testDetails);
- } else {
- return true;
+ $message = $testDetails[2] . ". The following classes are at fault: " . implode(', ', $badClasses);
+ $this->error($testDetails, $message);
+ return false;
}
+ return true;
}
public function checkApacheVersion($testDetails) {
@@ -855,20 +871,20 @@ class InstallRequirements {
$installedVersion = phpversion();
if(version_compare($installedVersion, $requiredVersion, '<')) {
- $testDetails[2] = "SilverStripe requires PHP version $requiredVersion or later.\n
+ $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);
- return;
+ $this->error($testDetails, $message);
+ return false;
}
if(version_compare($installedVersion, $recommendedVersion, '<')) {
- $testDetails[2] = "PHP version $installedVersion is currently installed.\n
+ $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);
- return;
+ $this->warning($testDetails, $message);
+ return false;
}
return true;
@@ -876,6 +892,9 @@ class InstallRequirements {
/**
* Check that a module exists
+ *
+ * @param string $dirname
+ * @return bool
*/
public function checkModuleExists($dirname) {
$path = $this->getBaseDir() . $dirname;
@@ -885,6 +904,9 @@ class InstallRequirements {
/**
* 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);
@@ -1041,22 +1063,19 @@ class InstallRequirements {
/**
* 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) {
- $adapters = DatabaseAdapterRegistry::get_adapters();
- if(isset($adapters[$databaseClass])) {
- $helperPath = $adapters[$databaseClass]['helperPath'];
- $class = str_replace('.php', '', basename($helperPath));
- }
- return (class_exists($class)) ? new $class() : false;
+ return DatabaseAdapterRegistry::getDatabaseConfigurationHelper($databaseClass);
}
public function requireDatabaseFunctions($databaseConfig, $testDetails) {
$this->testing($testDetails);
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
if (!$helper) {
- $this->error("Couldn't load database helper code for ". $databaseConfig['type']);
+ $this->error($testDetails, "Couldn't load database helper code for ". $databaseConfig['type']);
return false;
}
$result = $helper->requireDatabaseFunctions($databaseConfig);
@@ -1105,8 +1124,8 @@ class InstallRequirements {
if($result['success']) {
return true;
} else {
- $testDetails[2] .= ": " . $result['error'];
- $this->error($testDetails);
+ $message = $testDetails[2] . ": " . $result['error'];
+ $this->error($testDetails, $message);
return false;
}
}
@@ -1116,18 +1135,21 @@ class InstallRequirements {
$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";
+ 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'])) {
- $testDetails[2] .= ". Please create the database manually.";
+ $message = $testDetails[2] . ". Please create the database manually.";
} else {
- $testDetails[2] .= " (user '$databaseConfig[username]' doesn't have CREATE DATABASE permissions.)";
+ $message = $testDetails[2] . " (user '$databaseConfig[username]' doesn't have CREATE DATABASE permissions.)";
}
- $this->error($testDetails);
+ $this->error($testDetails, $message);
return false;
}
}
@@ -1139,10 +1161,10 @@ class InstallRequirements {
if ($result['success']) {
return true;
} else {
- $testDetails[2] = "Silverstripe cannot alter tables. This won't prevent installation, however it may "
+ $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);
- return;
+ $this->warning($testDetails, $message);
+ return false;
}
}
@@ -1158,10 +1180,11 @@ class InstallRequirements {
if(!$missing) {
return true;
- } else {
- $testDetails[2] .= " (the following PHP variables are missing: " . implode(", ", $missing) . ")";
- $this->error($testDetails);
}
+
+ $message = $testDetails[2] . " (the following PHP variables are missing: " . implode(", ", $missing) . ")";
+ $this->error($testDetails, $message);
+ return false;
}
@@ -1209,19 +1232,31 @@ class InstallRequirements {
$this->tests[$section][$test] = array("good", $message);
}
- public function error($testDetails) {
+ 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", isset($testDetails[2]) ? $testDetails[2] : null);
+ $this->tests[$section][$test] = array("error", $message);
$this->errors[] = $testDetails;
}
- public function warning($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", isset($testDetails[2]) ? $testDetails[2] : null);
+ $this->tests[$section][$test] = array("warning", $message);
$this->warnings[] = $testDetails;
}
@@ -1345,9 +1380,6 @@ global \$database;
require_once('conf/ConfigureFromEnv.php');
-// Set the site locale
-i18n::set_locale('$locale');
-
PHP
);
@@ -1372,9 +1404,6 @@ global \$databaseConfig;
{$databaseConfigContent}
);
-// Set the site locale
-i18n::set_locale('$locale');
-
PHP
);
}
@@ -1390,8 +1419,12 @@ After:
# YAML configuration for SilverStripe
# See http://doc.silverstripe.org/framework/en/topics/configuration
# Caution: Indentation through two spaces, not tabs
-SSViewer:
- theme: '$theme'
+SilverStripe\\View\\SSViewer:
+ themes:
+ - '$theme'
+ - '\$default'
+SilverStripe\\i18n\\i18n:
+ default_locale: '$locale'
YML
);
@@ -1399,6 +1432,8 @@ YML
$this->writeToFile("mysite/code/RootURLController.php", <<init();
+ $dbAdmin->doInit();
$dbAdmin->doBuild(true);
@@ -1500,9 +1535,9 @@ HTML;
if((@$fh = fopen($base . $filename, 'wb')) && fwrite($fh, $content) && fclose($fh)) {
return true;
- } else {
- $this->error("Couldn't write to file $base$filename");
}
+ $this->error("Couldn't write to file $base$filename");
+ return false;
}
public function createHtaccess() {
@@ -1684,6 +1719,8 @@ HTML;
/**
* Show an installation status message.
* The output differs depending on whether this is CLI or web based
+ *
+ * @param string $msg
*/
public function statusMessage($msg) {
echo "$msg\n";
diff --git a/_register_database.php b/_register_database.php
index a6e12b3e2..0a0fde62c 100644
--- a/_register_database.php
+++ b/_register_database.php
@@ -2,16 +2,17 @@
// Register the SilverStripe provided databases
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
-
-$frameworkPath = defined('FRAMEWORK_PATH') ? FRAMEWORK_PATH : FRAMEWORK_NAME;
+use SilverStripe\Dev\Install\MySQLDatabaseConfigurationHelper;
// Use MySQLi as default
DatabaseAdapterRegistry::register(
array(
/** @skipUpgrade */
'class' => 'MySQLDatabase',
+ 'module' => 'framework',
'title' => 'MySQL 5.0+ (using MySQLi)',
- 'helperPath' => $frameworkPath . '/dev/install/MySQLDatabaseConfigurationHelper.php',
+ 'helperPath' => __DIR__ . '/dev/install/MySQLDatabaseConfigurationHelper.php',
+ 'helperClass' => MySQLDatabaseConfigurationHelper::class,
'supported' => class_exists('MySQLi'),
'missingExtensionText' =>
'The MySQLi
@@ -24,8 +25,10 @@ DatabaseAdapterRegistry::register(
array(
/** @skipUpgrade */
'class' => 'MySQLPDODatabase',
+ 'module' => 'framework',
'title' => 'MySQL 5.0+ (using PDO)',
- 'helperPath' => $frameworkPath . '/dev/install/MySQLDatabaseConfigurationHelper.php',
+ 'helperPath' => __DIR__ . '/dev/install/MySQLDatabaseConfigurationHelper.php',
+ 'helperClass' => MySQLDatabaseConfigurationHelper::class,
'supported' => (class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers())),
'missingExtensionText' =>
'Either the PDO Extension or