From 7b2807a839839c8e05564d5aaf7f37256e9fc754 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Sat, 26 Jan 2019 22:30:58 +0100 Subject: [PATCH] Extract DatabaseConfig class with validation --- .../Install/Console/DataProviderInterface.php | 3 +- .../src/Install/Console/FileDataProvider.php | 24 +++-- .../src/Install/Console/InstallCommand.php | 33 +------ .../src/Install/Console/UserDataProvider.php | 26 +++-- .../Install/Controller/InstallController.php | 42 ++++---- framework/core/src/Install/DatabaseConfig.php | 98 +++++++++++++++++++ framework/core/src/Install/Installation.php | 6 +- .../src/Install/Steps/ConnectToDatabase.php | 14 +-- .../core/src/Install/Steps/StoreConfig.php | 21 +--- 9 files changed, 161 insertions(+), 106 deletions(-) create mode 100644 framework/core/src/Install/DatabaseConfig.php diff --git a/framework/core/src/Install/Console/DataProviderInterface.php b/framework/core/src/Install/Console/DataProviderInterface.php index 4913e2a28..0c4175a93 100644 --- a/framework/core/src/Install/Console/DataProviderInterface.php +++ b/framework/core/src/Install/Console/DataProviderInterface.php @@ -12,10 +12,11 @@ namespace Flarum\Install\Console; use Flarum\Install\AdminUser; +use Flarum\Install\DatabaseConfig; interface DataProviderInterface { - public function getDatabaseConfiguration(); + public function getDatabaseConfiguration(): DatabaseConfig; public function getBaseUrl(); diff --git a/framework/core/src/Install/Console/FileDataProvider.php b/framework/core/src/Install/Console/FileDataProvider.php index ec304c593..5ce2e1e26 100644 --- a/framework/core/src/Install/Console/FileDataProvider.php +++ b/framework/core/src/Install/Console/FileDataProvider.php @@ -13,6 +13,7 @@ namespace Flarum\Install\Console; use Exception; use Flarum\Install\AdminUser; +use Flarum\Install\DatabaseConfig; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Yaml\Yaml; @@ -52,20 +53,17 @@ class FileDataProvider implements DataProviderInterface } } - public function getDatabaseConfiguration() + public function getDatabaseConfiguration(): DatabaseConfig { - return $this->databaseConfiguration + [ - 'driver' => 'mysql', - 'host' => 'localhost', - 'database' => 'flarum', - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', - 'port' => '3306', - 'strict' => false, - ]; + return new DatabaseConfig( + $this->databaseConfiguration['driver'] ?? 'mysql', + $this->databaseConfiguration['host'] ?? 'localhost', + $this->databaseConfiguration['port'] ?? 3306, + $this->databaseConfiguration['database'] ?? 'flarum', + $this->databaseConfiguration['username'] ?? 'root', + $this->databaseConfiguration['password'] ?? '', + $this->databaseConfiguration['prefix'] ?? '' + ); } public function getBaseUrl() diff --git a/framework/core/src/Install/Console/InstallCommand.php b/framework/core/src/Install/Console/InstallCommand.php index 07995a87a..b476fb45d 100644 --- a/framework/core/src/Install/Console/InstallCommand.php +++ b/framework/core/src/Install/Console/InstallCommand.php @@ -11,12 +11,10 @@ namespace Flarum\Install\Console; -use Exception; use Flarum\Console\AbstractCommand; use Flarum\Install\Installation; use Flarum\Install\Pipeline; use Flarum\Install\Step; -use Illuminate\Contracts\Validation\Factory; use Symfony\Component\Console\Input\InputOption; class InstallCommand extends AbstractCommand @@ -26,11 +24,6 @@ class InstallCommand extends AbstractCommand */ protected $installation; - /** - * @var Factory - */ - protected $validator; - /** * @var DataProviderInterface */ @@ -38,12 +31,10 @@ class InstallCommand extends AbstractCommand /** * @param Installation $installation - * @param Factory $validator */ - public function __construct(Installation $installation, Factory $validator) + public function __construct(Installation $installation) { $this->installation = $installation; - $this->validator = $validator; parent::__construct(); } @@ -98,32 +89,12 @@ class InstallCommand extends AbstractCommand protected function install() { - $dbConfig = $this->dataSource->getDatabaseConfiguration(); - - $validation = $this->validator->make( - $dbConfig, - [ - 'driver' => 'required|in:mysql', - 'host' => 'required', - 'database' => 'required|string', - 'username' => 'required|string', - 'prefix' => 'nullable|alpha_dash|max:10', - 'port' => 'nullable|integer|min:1|max:65535', - ] - ); - - if ($validation->fails()) { - throw new Exception(implode("\n", - call_user_func_array('array_merge', - $validation->getMessageBag()->toArray()))); - } - $this->runPipeline( $this->installation ->configPath($this->input->getOption('config')) ->debugMode($this->dataSource->isDebugMode()) ->baseUrl($this->dataSource->getBaseUrl()) - ->databaseConfig($dbConfig) + ->databaseConfig($this->dataSource->getDatabaseConfiguration()) ->adminUser($this->dataSource->getAdminUser()) ->settings($this->dataSource->getSettings()) ->build() diff --git a/framework/core/src/Install/Console/UserDataProvider.php b/framework/core/src/Install/Console/UserDataProvider.php index d125470d6..ef8bd1df0 100644 --- a/framework/core/src/Install/Console/UserDataProvider.php +++ b/framework/core/src/Install/Console/UserDataProvider.php @@ -12,6 +12,7 @@ namespace Flarum\Install\Console; use Flarum\Install\AdminUser; +use Flarum\Install\DatabaseConfig; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -34,27 +35,24 @@ class UserDataProvider implements DataProviderInterface $this->questionHelper = $questionHelper; } - public function getDatabaseConfiguration() + public function getDatabaseConfiguration(): DatabaseConfig { $host = $this->ask('Database host:'); - $port = '3306'; + $port = 3306; if (str_contains($host, ':')) { list($host, $port) = explode(':', $host, 2); } - return [ - 'driver' => 'mysql', - 'host' => $host, - 'port' => $port, - 'database' => $this->ask('Database name:'), - 'username' => $this->ask('Database user:'), - 'password' => $this->secret('Database password:'), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => $this->ask('Prefix:'), - 'strict' => false, - ]; + return new DatabaseConfig( + 'mysql', + $host, + intval($port), + $this->ask('Database name:'), + $this->ask('Database user:'), + $this->secret('Database password:'), + $this->ask('Prefix:') + ); } public function getBaseUrl() diff --git a/framework/core/src/Install/Controller/InstallController.php b/framework/core/src/Install/Controller/InstallController.php index 7d4acaa9d..cbefc3849 100644 --- a/framework/core/src/Install/Controller/InstallController.php +++ b/framework/core/src/Install/Controller/InstallController.php @@ -13,6 +13,7 @@ namespace Flarum\Install\Controller; use Flarum\Http\SessionAuthenticator; use Flarum\Install\AdminUser; +use Flarum\Install\DatabaseConfig; use Flarum\Install\Installation; use Flarum\Install\StepFailed; use Flarum\Install\ValidationFailed; @@ -51,31 +52,12 @@ class InstallController implements RequestHandlerInterface public function handle(Request $request): ResponseInterface { $input = $request->getParsedBody(); - - $host = array_get($input, 'mysqlHost'); - $port = '3306'; - - if (str_contains($host, ':')) { - list($host, $port) = explode(':', $host, 2); - } - $baseUrl = rtrim((string) $request->getUri(), '/'); try { $pipeline = $this->installation ->baseUrl($baseUrl) - ->databaseConfig([ - 'driver' => 'mysql', - 'host' => $host, - 'port' => $port, - 'database' => array_get($input, 'mysqlDatabase'), - 'username' => array_get($input, 'mysqlUsername'), - 'password' => array_get($input, 'mysqlPassword'), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => array_get($input, 'tablePrefix'), - 'strict' => false, - ]) + ->databaseConfig($this->makeDatabaseConfig($input)) ->adminUser($this->makeAdminUser($input)) ->settings([ 'forum_title' => array_get($input, 'forumTitle'), @@ -99,6 +81,26 @@ class InstallController implements RequestHandlerInterface return new Response\EmptyResponse; } + private function makeDatabaseConfig(array $input): DatabaseConfig + { + $host = array_get($input, 'mysqlHost'); + $port = 3306; + + if (str_contains($host, ':')) { + list($host, $port) = explode(':', $host, 2); + } + + return new DatabaseConfig( + 'mysql', + $host, + intval($port), + array_get($input, 'mysqlDatabase'), + array_get($input, 'mysqlUsername'), + array_get($input, 'mysqlPassword'), + array_get($input, 'tablePrefix') + ); + } + /** * @param array $input * @return AdminUser diff --git a/framework/core/src/Install/DatabaseConfig.php b/framework/core/src/Install/DatabaseConfig.php new file mode 100644 index 000000000..b4e666429 --- /dev/null +++ b/framework/core/src/Install/DatabaseConfig.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Install; + +class DatabaseConfig +{ + private $driver; + private $host; + private $port; + private $database; + private $username; + private $password; + private $prefix; + + public function __construct($driver, $host, $port, $database, $username, $password, $prefix) + { + $this->driver = $driver; + $this->host = $host; + $this->port = $port; + $this->database = $database; + $this->username = $username; + $this->password = $password; + $this->prefix = $prefix; + + $this->validate(); + } + + public function getConfig(): array + { + return [ + 'driver' => $this->driver, + 'host' => $this->host, + 'port' => $this->port, + 'database' => $this->database, + 'username' => $this->username, + 'password' => $this->password, + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => $this->prefix, + 'strict' => false, + 'engine' => 'InnoDB', + ]; + } + + private function validate() + { + if (empty($this->driver)) { + throw new ValidationFailed('Please specify a database driver.'); + } + + if ($this->driver !== 'mysql') { + throw new ValidationFailed('Currently, only MySQL/MariaDB is supported.'); + } + + if (empty($this->host)) { + throw new ValidationFailed('Please specify the hostname of your database server.'); + } + + if (! is_int($this->port) || $this->port < 1 || $this->port > 65535) { + throw new ValidationFailed('Please provide a valid port number between 1 and 65535.'); + } + + if (empty($this->database)) { + throw new ValidationFailed('Please specify the database name.'); + } + + if (! is_string($this->database)) { + throw new ValidationFailed('The database name must be a non-empty string.'); + } + + if (empty($this->username)) { + throw new ValidationFailed('Please specify the username for accessing the database.'); + } + + if (! is_string($this->database)) { + throw new ValidationFailed('The username must be a non-empty string.'); + } + + if (! empty($this->prefix)) { + if (! preg_match('/^[\pL\pM\pN_-]+$/u', $this->prefix)) { + throw new ValidationFailed('The prefix may only contain characters, dashes and underscores.'); + } + + if (strlen($this->prefix) > 10) { + throw new ValidationFailed('The prefix should be no longer than 10 characters.'); + } + } + } +} diff --git a/framework/core/src/Install/Installation.php b/framework/core/src/Install/Installation.php index 744a76cfa..074d5d135 100644 --- a/framework/core/src/Install/Installation.php +++ b/framework/core/src/Install/Installation.php @@ -19,10 +19,12 @@ class Installation private $configPath; private $debug = false; - private $dbConfig = []; private $baseUrl; private $customSettings = []; + /** @var DatabaseConfig */ + private $dbConfig; + /** @var AdminUser */ private $adminUser; @@ -54,7 +56,7 @@ class Installation return $this; } - public function databaseConfig(array $dbConfig) + public function databaseConfig(DatabaseConfig $dbConfig) { $this->dbConfig = $dbConfig; diff --git a/framework/core/src/Install/Steps/ConnectToDatabase.php b/framework/core/src/Install/Steps/ConnectToDatabase.php index 1f8357b25..027c378ca 100644 --- a/framework/core/src/Install/Steps/ConnectToDatabase.php +++ b/framework/core/src/Install/Steps/ConnectToDatabase.php @@ -11,6 +11,7 @@ namespace Flarum\Install\Steps; +use Flarum\Install\DatabaseConfig; use Flarum\Install\Step; use Illuminate\Database\Connectors\MySqlConnector; use Illuminate\Database\MySqlConnection; @@ -22,11 +23,9 @@ class ConnectToDatabase implements Step private $dbConfig; private $store; - public function __construct($dbConfig, callable $store) + public function __construct(DatabaseConfig $dbConfig, callable $store) { $this->dbConfig = $dbConfig; - $this->dbConfig['engine'] = 'InnoDB'; - $this->store = $store; } @@ -37,7 +36,8 @@ class ConnectToDatabase implements Step public function run() { - $pdo = (new MySqlConnector)->connect($this->dbConfig); + $config = $this->dbConfig->getConfig(); + $pdo = (new MySqlConnector)->connect($config); $version = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION); @@ -48,9 +48,9 @@ class ConnectToDatabase implements Step ($this->store)( new MySqlConnection( $pdo, - $this->dbConfig['database'], - $this->dbConfig['prefix'], - $this->dbConfig + $config['database'], + $config['prefix'], + $config ) ); } diff --git a/framework/core/src/Install/Steps/StoreConfig.php b/framework/core/src/Install/Steps/StoreConfig.php index fca897ff4..62d4df77e 100644 --- a/framework/core/src/Install/Steps/StoreConfig.php +++ b/framework/core/src/Install/Steps/StoreConfig.php @@ -11,6 +11,7 @@ namespace Flarum\Install\Steps; +use Flarum\Install\DatabaseConfig; use Flarum\Install\ReversibleStep; use Flarum\Install\Step; @@ -24,7 +25,7 @@ class StoreConfig implements Step, ReversibleStep private $configFile; - public function __construct($debugMode, $dbConfig, $baseUrl, $configFile) + public function __construct($debugMode, DatabaseConfig $dbConfig, $baseUrl, $configFile) { $this->debugMode = $debugMode; $this->dbConfig = $dbConfig; @@ -55,28 +56,12 @@ class StoreConfig implements Step, ReversibleStep { return [ 'debug' => $this->debugMode, - 'database' => $this->getDatabaseConfig(), + 'database' => $this->dbConfig->getConfig(), 'url' => $this->baseUrl, 'paths' => $this->getPathsConfig(), ]; } - private function getDatabaseConfig() - { - return [ - 'driver' => $this->dbConfig['driver'], - 'host' => $this->dbConfig['host'], - 'database' => $this->dbConfig['database'], - 'username' => $this->dbConfig['username'], - 'password' => $this->dbConfig['password'], - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => $this->dbConfig['prefix'], - 'port' => $this->dbConfig['port'], - 'strict' => false, - ]; - } - private function getPathsConfig() { return [