From 2c498f6044df41a5cb106abfd07c24e60cf6c2f7 Mon Sep 17 00:00:00 2001 From: yarik Date: Thu, 23 Mar 2017 00:13:18 +0200 Subject: [PATCH] Initializer completed --- .gitignore | 2 +- Initializer.php | 1037 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common/config/.gitignore | 1 + console/controllers/CreateController.php | 24 ++++++++++++++++++------ environments/dbmysql.php | 9 +++++++++ environments/dbpostgresql.php | 15 +++++++++++++++ environments/dev/common/config/main-local.php | 8 +------- environments/index.php | 14 ++++++++------ environments/prod/common/config/main-local.php | 8 +------- init | 313 ++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 10 files changed, 1107 insertions(+), 324 deletions(-) create mode 100644 Initializer.php create mode 100644 environments/dbmysql.php create mode 100644 environments/dbpostgresql.php diff --git a/.gitignore b/.gitignore index 9158548..569df28 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ phpunit.phar # vagrant runtime /.vagrant /artweb/ -/storage +/storage \ No newline at end of file diff --git a/Initializer.php b/Initializer.php new file mode 100644 index 0000000..5e3ffeb --- /dev/null +++ b/Initializer.php @@ -0,0 +1,1037 @@ +defineEnvironment(); + $instance->startInitialization(); + if ($instance->askInitDb()) { + $instance->defineDb(); + $instance->migrate(); + $instance->createUser(); + $instance->congratulate(); + } + } + + /** + * Get instance of Initializer + * + * @return mixed + */ + public static function getInstance(): Initializer + { + if (empty( self::$instance )) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * Initializer private constructor. You can get Singleton with getInstance() + * + * Availiable options: + * * --env - One of available environments + * * --overwrite - Do overwrite all files without confirmation + * * --dbinit - Initialize database after app initialization + * * --dbtype - Database type. Available: postgresql, mysql + * * --host - Database host, default to 127.0.0.1 + * * --port - Database port used by postgresql, default to 5432 + * * --schema - Database schema for postresql, default to public + * * --dbname - Database name, required + * * --username - Database username, required + * * --password - Database password, required + * * --migrationPath - Migration path, default to vendor/artweb/artbox-core/migrations + * * --migrate - Whether to migrate, default to apply, set no to skip migration + * * --user_username - Username for user creation + * * --user_email - Email for user creation + * * --user_password - Password for user creation + * * --user - Whether to create user, default to yes, set no to skip creation + * * --defaultuser - Whether to use default user creation + * * --o - Webpage to open after intallaction process + * + * @see Initializer::getInstance() + */ + private function __construct() + { + echo $this->formatMessage( + "\tArtbox Application Initialization Tool v" . self::VERSION, + [ 'bold' ] + ) . "\n\n"; + } + + /** + * Define environment for an application + * + * @return string + */ + private function defineEnvironment() + { + $envName = $this->getParamValue('env'); + $envs = $this->getEnvironmentNames(); + if (empty( $envName ) || $envName === '1') { + do { + $envName = $this->askEnvironment($envs); + echo "\n Initialize the application under '{$envName}' environment? [yes|no] "; + $answer = trim(fgets(STDIN)); + } while (strncasecmp($answer, 'y', 1)); + } else { + if (!in_array($envName, $envs)) { + $this->printError("Wrong environment name, available list: " . implode(', ', $envs)); + exit( 1 ); + } + } + $this->environment = $this->envs[ $envName ]; + return $this->environment; + } + + /** + * Start actual application initialization with files overwriting + */ + private function startInitialization() + { + echo "\n Start initialization\n"; + echo " =====================\n"; + + $this->rewriteFiles(); + $this->callback(); + echo "\n App initialization completed\n"; + echo " ==============================\n"; + } + + /** + * Ask whether to init databse. If dbinit param is set, this step will be skipped. + * + * @return bool + */ + private function askInitDb(): bool + { + echo " Start database initialization\n"; + $params = $this->getParams(); + if (!isset( $params[ 'dbinit' ] )) { + do { + echo $this->formatMessage("\n Init the database? [yes|no] ", [ 'bold' ]); + $answer = trim(fgets(STDIN)); + } while (( strncasecmp($answer, 'y', 1) !== 0 ) && ( strncasecmp($answer, 'n', 1) !== 0 )); + if (strncasecmp($answer, 'n', 1) == 0) { + $this->noDatabaseMessage(); + } + } + return true; + } + + /** + * Define which database driver to use. Available options: postgresql, mysql + */ + private function defineDb() + { + $params = $this->getParams(); + if (isset( $params[ 'dbinit' ] )) { + $this->validateConnection(); + } else { + $answer = ''; + do { + if (!empty( $answer )) { + $this->printError("Incorrect database type. Try again or write 'quit' to exit"); + } + echo "\n Which database do you want to use? [postgresql|mysql] "; + $answer = trim(fgets(STDIN)); + + } while (( strncasecmp($answer, 'q', 1) !== 0 ) && ( !in_array( + $answer, + [ + 'postgresql', + 'mysql', + 'p', + 'm', + ] + ) )); + if (strncasecmp($answer, 'q', 1) === 0) { + $this->noDatabaseMessage(); + } + if (strncasecmp($answer, 'p', 1) === 0) { + $answer = 'postgresql'; + } else { + $answer = 'mysql'; + } + if ($answer == 'postgresql') { + $this->initPostgres(); + } else { + $this->initMysql(); + } + } + $this->initDb(); + } + + /** + * Get parsed params + * + * @see Initializer::parseParams() + * + * @return array + */ + private function getParams(): array + { + if (empty( $this->params )) { + $this->parseParams(); + } + return $this->params; + } + + /** + * Get params from input string + * For example + * + * **php init test --param1=value1 --param2=value2** + * + * will get + * + * ** + * [ + * test, + * value1, + * value2 + * ] + * + * + * @return array + */ + private function parseParams(): array + { + $rawParams = []; + if (isset( $_SERVER[ 'argv' ] )) { + $rawParams = $_SERVER[ 'argv' ]; + array_shift($rawParams); + } + + $params = []; + foreach ($rawParams as $param) { + if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) { + $name = $matches[ 1 ]; + $params[ $name ] = isset( $matches[ 3 ] ) ? $matches[ 3 ] : true; + } else { + $params[] = $param; + } + } + $this->params = $params; + return $this->params; + } + + /** + * Get value of input param. Empty string if not exist. + * + * @param string $name + * + * @return string + */ + private function getParamValue(string $name): string + { + $params = $this->getParams(); + if (isset( $params[ $name ] ) && !empty( $params[ $name ] )) { + return $params[ $name ]; + } else { + return ''; + } + } + + /** + * Get project root directory according to file system + * + * @return mixed + */ + private function getRoot(): string + { + if (empty( $this->root )) { + $this->root = str_replace('\\', '/', __DIR__); + } + return $this->root; + } + + /** + * Get available environments from environments/index.php file + * + * Follow environments/index.php manifest to create your own environments + * + * @return array + */ + private function getEnvironments(): array + { + $path = "{$this->getRoot()}/environments/index.php"; + if (empty( $this->envs )) { + /** @noinspection PhpIncludeInspection */ + $this->envs = require( $path ); + } + if (empty( $this->envs )) { + die( "There are no available environments in $path" ); + } + return $this->envs; + } + + /** + * Get environment names + * + * @return array + */ + private function getEnvironmentNames(): array + { + return array_keys($this->getEnvironments()); + } + + /** + * Show user variants to choose environment from + * + * @param array $envs + * + * @return string + */ + private function askEnvironment(array $envs): string + { + echo "Which environment do you want the application to be initialized in?\n\n"; + foreach ($envs as $i => $name) { + echo " [$i] $name\n"; + } + $answer = $this->offerEnvironment(count($envs)); + while (!( ( ctype_digit($answer) && in_array($answer, range(0, count($envs) - 1)) ) || $answer === 'q' )) { + $answer = $this->offerEnvironment(count($envs)); + } + if ($answer === 'q') { + echo "\n Quit initialization.\n"; + exit( 0 ); + } else { + if (isset( $envs[ $answer ] )) { + $envName = $envs[ $answer ]; + } else { + die( "Error while trying to get environment name. Try another one." ); + } + } + return $envName; + } + + /** + * Offer user to choose environment number + * + * @param int $count + * + * @return string + */ + private function offerEnvironment(int $count): string + { + echo "\n Your choice [0-" . ( $count - 1 ) . ', or "q" to quit] '; + return trim(fgets(STDIN)); + } + + /** + * Rewrite files by files in environments/$environment['path'] + */ + private function rewriteFiles() + { + $environment = $this->environment; + $root = $this->getRoot(); + if (isset( $environment[ 'path' ] ) && !empty( $environment[ 'path' ] )) { + $path = $environment[ 'path' ]; + } else { + $this->printError('Environment configuration failed. Please set path value.'); + exit( 1 ); + } + $files = $this->getFileList("$root/environments/$path"); + if (isset( $environment[ 'skipFiles' ] )) { + $skipFiles = $environment[ 'skipFiles' ]; + array_walk( + $skipFiles, + function (&$value) use ($root) { + $value = "$root/$value"; + } + ); + $files = array_diff( + $files, + array_intersect_key( + $environment[ 'skipFiles' ], + array_filter($skipFiles, 'file_exists') + ) + ); + } + $all = false; + foreach ($files as $file) { + if (!$this->copyFile("environments/{$path}/$file", $file, $all)) { + break; + } + } + } + + /** + * Recursively get all files from $root directory. + * + * This will ignore .git, .svn directories. + * + * @param string $root + * @param string $basePath + * + * @return array + */ + private function getFileList(string $root, string $basePath = ''): array + { + $files = []; + $handle = opendir($root); + while (( $path = readdir($handle) ) !== false) { + if ($path === '.git' || $path === '.svn' || $path === '.' || $path === '..') { + continue; + } + $fullPath = "$root/$path"; + $relativePath = $basePath === '' ? $path : "$basePath/$path"; + if (is_dir($fullPath)) { + $files = array_merge($files, $this->getFileList($fullPath, $relativePath)); + } else { + $files[] = $relativePath; + } + } + closedir($handle); + return $files; + } + + /** + * Run callbacks for application initialization + */ + private function callback() + { + $environment = $this->environment; + foreach (self::CALLBACKS as $callback) { + if (!empty( $environment[ $callback ] )) { + $this->$callback($environment[ $callback ]); + } + } + } + + /** + * Set directories in $environment['setWrotable'] to chmod 0777 + * + * @param $paths + */ + private function setWritable(array $paths) + { + $root = $this->getRoot(); + foreach ($paths as $writable) { + $fullPath = "$root/$writable"; + if (is_dir($fullPath)) { + if (@chmod($fullPath, 0777)) { + echo $this->formatMessage( + " ***** Set writable $writable (chmod 0777)", + [ 'fg-yellow' ] + ) . "\n"; + } else { + $this->printError("Operation chmod not permitted for directory $writable."); + } + } else { + if (!@mkdir($fullPath, 0777, true)) { + $this->printError("Directory $writable does not exist and cannot be created."); + } + } + } + } + + /** + * Set files in $environment['setExecutable'] to chmod 0755 + * + * @param $paths + */ + private function setExecutable(array $paths) + { + $root = $this->getRoot(); + foreach ($paths as $executable) { + $fullPath = "$root/$executable"; + if (file_exists($fullPath)) { + if (@chmod($fullPath, 0755)) { + echo $this->formatMessage(" ***** Set executable $executable (chmod 0755)") . "\n"; + } else { + $this->printError("Operation chmod not permitted for $executable."); + } + } else { + $this->printError("$executable does not exist."); + } + } + } + + /** + * Set cookie validation keys to files in $environment['setCookieValidationKey']. + * + * @param $paths + */ + private function setCookieValidationKey(array $paths) + { + $root = $this->getRoot(); + foreach ($paths as $file) { + echo $this->formatMessage( + " ***** Generate cookie validation key in $file", + [ + 'bold', + 'fg-magenta', + ] + ) . "\n"; + $file = $root . '/' . $file; + $length = 32; + $bytes = openssl_random_pseudo_bytes($length); + $key = strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.'); + $this->setParam('cookieValidationKey', $key, $file); + } + } + + private function createSymlink(array $links) + { + $root = $this->getRoot(); + foreach ($links as $link => $target) { + //first removing folders to avoid errors if the folder already exists + @rmdir($root . "/" . $link); + //next removing existing symlink in order to update the target + if (is_link($root . "/" . $link)) { + @unlink($root . "/" . $link); + } + if (@symlink($root . "/" . $target, $root . "/" . $link)) { + echo $this->formatMessage(" ***** Symlink $root/$target $root/$link", [ 'fg-blue' ]) . "\n"; + } else { + $this->printError("Cannot create symlink $root/$target $root/$link."); + } + } + } + + /** + * Copy file from environment directory to project directory + * + * @param string $source Environment source file path + * @param string $target Project file path target + * @param bool $all Rewrite all flag + * + * @return bool + */ + private function copyFile(string $source, string $target, bool &$all) + { + $root = $this->getRoot(); + $params = $this->getParams(); + if (!is_file($root . '/' . $source)) { + echo $this->formatMessage(" ----- skip $target ($source not exist)", [ 'fg-cyan' ]) . "\n"; + return true; + } + if (is_file($root . '/' . $target)) { + if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { + echo $this->formatMessage(" ----- unchanged $target", [ 'fg-cyan' ]) . "\n"; + return true; + } + if ($all) { + echo $this->formatMessage(" ----- overwrite $target", [ 'fg-blue' ]) . "\n"; + } else { + echo $this->formatMessage(" -- exist $target", [ 'fg-magenta' ]) . "\n"; + echo " overwrite? [Yes|No|All|Quit] "; + + $answer = !empty( $params[ 'overwrite' ] ) ? $params[ 'overwrite' ] : trim(fgets(STDIN)); + echo "\n"; + if (!strncasecmp($answer, 'q', 1)) { + return false; + } else { + if (!strncasecmp($answer, 'y', 1)) { + echo $this->formatMessage(" ----- overwrite $target", [ 'fg-blue' ]) . "\n"; + } else { + if (!strncasecmp($answer, 'a', 1)) { + echo $this->formatMessage(" ----- overwrite $target", [ 'fg-blue' ]) . "\n"; + $all = true; + } else { + echo $this->formatMessage( + " ----- skip $target ($source not exist)", + [ 'fg-cyan' ] + ) . "\n"; + return true; + } + } + } + } + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; + } + echo "\n" . $this->formatMessage(" ----- generate $target", [ 'fg-green' ]) . "\n"; + @mkdir(dirname($root . '/' . $target), 0777, true); + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; + } + + /** + * Set param in file to particular value. + * + * For example: setParam('param', 'value', 'config.php') will replace content in config.php + * + * from + * + * 'param' => '' + * + * to + * + * 'param' => 'value' + * + * @param string $param + * @param string $value + * @param string $file + * @param bool $noQuote + * @param bool $force + * @param bool $alternate + */ + private function setParam( + string $param, + string $value, + string $file, + bool $noQuote = false, + bool $force = false, + bool $alternate = false + ) { + if ($alternate) { + $regexp = '/(("|\')db("|\')\s*=>\s*)(.*|.*)/'; + } else { + if ($force) { + $regexp = '/(("|\')' . $param . '("|\')\s*=>\s*)(".*"|\'.*\')/'; + } else { + $regexp = '/(("|\')' . $param . '("|\')\s*=>\s*)(""|\'\')/'; + } + } + if ($noQuote) { + $content = preg_replace( + $regexp, + "\\1$value", + file_get_contents($file) + ); + } else { + $content = preg_replace( + $regexp, + "\\1'$value'", + file_get_contents($file) + ); + } + file_put_contents($file, $content); + } + + /** + * Init postgres connection + * + * @return array + */ + private function initPostgres(): array + { + $db = [ + 'dbtype' => 'postgresql', + ]; + echo "\n Enter your database host: "; + $host = trim(fgets(STDIN)); + echo "\n Enter your database port: "; + $port = trim(fgets(STDIN)); + echo "\n Enter your database schema: "; + $db[ 'schema' ] = trim(fgets(STDIN)); + echo "\n Enter your database name: "; + $name = trim(fgets(STDIN)); + echo "\n Enter your database username: "; + $db[ 'username' ] = trim(fgets(STDIN)); + echo "\n Enter your database password: "; + $db[ 'password' ] = trim(fgets(STDIN)); + $db[ 'dsn' ] = "pgsql:host={$host};port={$port};dbname={$name}"; + $this->db = $db; + return $db; + } + + /** + * Init mysql connection + * + * @return array + */ + private function initMysql(): array + { + $db = [ + 'dbtype' => 'mysql', + ]; + echo "\n Enter your database host: "; + $host = trim(fgets(STDIN)); + echo "\n Enter your database name: "; + $name = trim(fgets(STDIN)); + echo "\n Enter your database username: "; + $db[ 'username' ] = trim(fgets(STDIN)); + echo "\n Enter your database password: "; + $db[ 'password' ] = trim(fgets(STDIN)); + $db[ 'dsn' ] = "mysql:host={$host};dbname={$name}"; + $this->db = $db; + return $db; + } + + /** + * Validate non-interactive db data and fill it for further actions. + * + * @return array + */ + private function validateConnection(): array + { + $db = []; + $dbType = strtolower($this->getParamValue('dbtype')); + if ($dbType !== 'postgresql' && $dbType !== 'mysql') { + die( "Supported DB types are postgresql and mysql" ); + } + if ($dbType === 'postgresql') { + $host = $this->getParamValue('host') ? : '127.0.0.1'; + $port = $this->getParamValue('port') ? : '5432'; + $db[ 'schema' ] = $this->getParamValue('schema') ? : 'public'; + $name = $this->getParamValue('dbname'); + if (empty( $name )) { + die( "Database name must be set" ); + } + $db[ 'dsn' ] = "pgsql:host={$host};port={$port};dbname={$name}"; + if (empty( $username = $this->getParamValue('username') )) { + die( "Database username must be set" ); + } + $db[ 'username' ] = $username; + if (empty( $password = $this->getParamValue('password') )) { + die( "Database password must be set" ); + } + $db[ 'password' ] = $password; + } else { + $host = $this->getParamValue('host') ? : '127.0.0.1'; + $name = $this->getParamValue('dbname'); + if (empty( $name )) { + die( "Database name must be set" ); + } + $db[ 'dsn' ] = "mysql:host={$host};dbname={$name}"; + if (empty( $username = $this->getParamValue('username') )) { + die( "Database username must be set" ); + } + $db[ 'username' ] = $username; + if (empty( $password = $this->getParamValue('password') )) { + die( "Database password must be set" ); + } + $db[ 'password' ] = $password; + } + $db[ 'dbtype' ] = $dbType; + $this->db = $db; + return $db; + } + + /** + * Copy db connection file accroding to choosen driver + */ + private function initDb() + { + if (!empty( $configPath = $this->environment[ 'setDbConnection' ] )) { + if (preg_match('/(.*)\/.*/', $configPath, $matches)) { + $path = $matches[ 1 ]; + } else { + $this->printError("Unknown error while trying to init database"); + exit( 1 ); + } + $filename = "db{$this->db['dbtype']}.php"; + $all = !empty( $this->getParamValue('overwrite') ) ? false : true; + $fullpath = "{$path}/{$filename}"; + if ($this->copyFile("environments/{$filename}", $fullpath, $all)) { + $this->rewriteDb($fullpath, $configPath); + } + } + } + + /** + * Rewrite params in db config and local config files + * + * @param string $path + * @param string $config + */ + private function rewriteDb(string $path, string $config) + { + $db = $this->db; + $this->setParam('dsn', $db[ 'dsn' ], $path); + $this->setParam('username', $db[ 'username' ], $path); + $this->setParam('password', $db[ 'password' ], $path); + if ($this->db[ 'dbtype' ] == 'postgresql') { + $this->setParam('defaultSchema', $db[ 'schema' ], $path); + } + $filename = "db{$this->db['dbtype']}.php"; + $this->setParam('db', "require('{$filename}')", $config, true, true); + $driver = ucfirst($db[ 'dbtype' ]); + echo $this->formatMessage( + "Database access file $path was created and filled with configurations for database driver {$driver}", + [ 'fg-green' ] + ) . "\n"; + echo $this->formatMessage( + "Database access file $path was linked to local config file $config", + [ 'fg-green' ] + ) . "\n"; + echo $this->formatMessage("Database Access: ", [ 'bg-yellow' ]) . $this->formatMessage( + $this->db[ 'dsn' ], + [ + 'bg-yellow', + 'fg-blue', + ] + ) . $this->formatMessage(', username: ', [ 'bg-yellow' ]) . $this->formatMessage( + $this->db[ 'username' ], + [ + 'bg-yellow', + 'fg-blue', + ] + ) . "\n"; + echo " Database initialization completed\n"; + echo " =================================\n"; + } + + /** + * Prints error message. + * + * @param string $message message + */ + private function printError(string $message) + { + echo "\n " . $this->formatMessage( + "Error. $message", + [ + 'fg-red', + 'bold', + ] + ) . " \n"; + } + + /** + * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream. + * - windows without ansicon + * - not tty consoles + * + * @return boolean true if the stream supports ANSI colors, otherwise false. + */ + private function ansiColorsSupported(): bool + { + return DIRECTORY_SEPARATOR === '\\' ? getenv('ANSICON') !== false || getenv( + 'ConEmuANSI' + ) === 'ON' : function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + + /** + * Get ANSI code of style. + * + * @param string $name style name + * + * @return integer ANSI code of style. + */ + private function getStyleCode(string $name): int + { + $styles = [ + 'bold' => 1, + 'fg-black' => 30, + 'fg-red' => 31, + 'fg-green' => 32, + 'fg-yellow' => 33, + 'fg-blue' => 34, + 'fg-magenta' => 35, + 'fg-cyan' => 36, + 'fg-white' => 37, + 'bg-black' => 40, + 'bg-red' => 41, + 'bg-green' => 42, + 'bg-yellow' => 43, + 'bg-blue' => 44, + 'bg-magenta' => 45, + 'bg-cyan' => 46, + 'bg-white' => 47, + ]; + return $styles[ $name ]; + } + + /** + * Formats message using styles if STDOUT supports it. + * + * @param string $message message + * @param string[] $styles styles + * + * @return string formatted message. + */ + public function formatMessage(string $message, array $styles = []): string + { + if (empty( $styles ) || !$this->ansiColorsSupported()) { + return $message; + } + + return sprintf("\x1b[%sm", implode(';', array_map('getStyleCode', $styles))) . $message . "\x1b[0m"; + } + + /** + * Inform that an application initialized without database. Exit code 0, means success. + */ + private function noDatabaseMessage() + { + echo $this->formatMessage( + "\n Attention: ", + [ + 'bold', + 'fg-red', + 'bg-yellow', + ] + ); + echo $this->formatMessage( + "Application initialized without database. Set it manually in {$this->getRoot()}/common/config/main-local.php and run migration", + [ + 'bold', + 'bg-yellow', + ] + ) . "\n"; + exit( 0 ); + } + + /** + * Perform database migration if input param migrate doesn't set to 'no' + */ + private function migrate() + { + $migrate = $this->getParamValue('migrate'); + if ($migrate == 'no') { + echo $this->formatMessage( + "Migration skipped by user. In order to application work correct you will need to run it manually", + [ + 'bg-yellow', + 'bold', + ] + ) . "\n"; + exit( 0 ); + } elseif ($migrate != 'yes') { + do { + echo " Do you want to perform database migration? [yes|no]"; + $answer = trim(fgets(STDIN)); + } while (strncasecmp($answer, 'y', 1) !== 0 && strncasecmp($answer, 'n', 1) !== 0); + if (strncasecmp($answer, 'n', 1) === 0) { + echo $this->formatMessage( + "Migration skipped by user. In order to application work correct you will need to run it manually", + [ + 'bg-yellow', + 'bold', + ] + ) . "\n"; + exit( 0 ); + } + } + echo $this->formatMessage("Migration begins...", [ 'fg-yellow' ]) . "\n"; + $migrationPath = $this->getParamValue('migrationPath'); + if (empty( $migrationPath )) { + $migrationPath = 'vendor/artweb/artbox-core/migrations'; + } + $result = exec("php yii migrate --migrationPath=$migrationPath --interactive=0", $output, $return); + if ($return !== 0) { + $this->printError("Migration cannot be applied. Run it manually to check the reason"); + exit( 1 ); + } + $this->writeLine($result); + foreach ($output as $value) { + $this->writeLine($value); + } + echo $this->formatMessage("Migration ended successfully", [ 'fg-yellow' ]) . "\n"; + } + + /** + * Write line of code followed by new line symbol + * + * @param string $string + */ + private function writeLine(string $string) + { + echo $string . "\n"; + } + + /** + * Perform user creation if input param user doesn't set to 'no' + */ + private function createUser() + { + $params = $this->getParams(); + if (!isset( $params[ 'defaultuser' ] )) { + if ($this->getParamValue('user') == 'no') { + echo $this->formatMessage( + "User creation skipped by user. Run command 'php yii user/create' manually to create user.", + [ + 'bg-yellow', + 'bold', + ] + ) . "\n"; + exit( 0 ); + } + do { + echo " Do you want to create user? [yes|no]"; + $answer = trim(fgets(STDIN)); + } while (strncasecmp($answer, 'y', 1) !== 0 && strncasecmp($answer, 'n', 1) !== 0); + if (strncasecmp($answer, 'n', 1) === 0) { + echo $this->formatMessage( + "User creation skipped by user. Run command 'php yii user/create' manually to create user.", + [ + 'bg-yellow', + 'bold', + ] + ) . "\n"; + exit( 0 ); + } + echo "\n Enter username: "; + $username = trim(fgets(STDIN)); + echo "\n Enter email: "; + $email = trim(fgets(STDIN)); + echo "\n Enter password: "; + $password = trim(fgets(STDIN)); + echo "\n"; + $result = exec("php yii create/user $username $email $password", $output, $return); + } else { + $result = exec("php yii create/user", $output, $return); + } + $this->handleUserCreation($result, $output, $return); + } + + /** + * Handle user creation result + * + * @param string $result + * @param array $output + * @param int $return + */ + private function handleUserCreation($result, $output, $return) + { + if ($return !== 0) { + $this->printError("User cannot be created. Run 'php yii create/user' manually to check the reason"); + exit( 1 ); + } + $this->writeLine($result); + echo $this->formatMessage("User created successfully", [ 'fg-yellow' ]) . "\n"; + } + + /** + * Congratulate with successfull installation and optionally open browser + */ + private function congratulate() + { + echo "\n" . $this->formatMessage( + "Congratulations. Artbox Basic has been successfully installed.", + [ + 'fg-yellow', + 'bold', + ] + ) . "\n"; + $url = $this->getParamValue('o'); + if (!empty( $url )) { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + shell_exec("explorer '{$url}'"); + } else { + shell_exec("sensible-browser {$url}"); + } + } + } + } + \ No newline at end of file diff --git a/common/config/.gitignore b/common/config/.gitignore index 4104a6e..b2e5776 100644 --- a/common/config/.gitignore +++ b/common/config/.gitignore @@ -1,4 +1,5 @@ main-local.php +db* params-local.php test-local.php settings.php \ No newline at end of file diff --git a/console/controllers/CreateController.php b/console/controllers/CreateController.php index 7315520..71d603b 100644 --- a/console/controllers/CreateController.php +++ b/console/controllers/CreateController.php @@ -12,18 +12,30 @@ */ class CreateController extends Controller { - public function actionUser() + public function actionUser($username = null, $email = null, $password = null) { - $user = new User(); - $user->username = 'admin'; - $user->email = 'admin@example.com'; - $user->setPassword('admin321'); + $username = $username ? : 'admin'; + $user = User::find() + ->where([ 'username' => $username ]) + ->one(); + if (empty( $user )) { + $user = new User(); + $user->username = $username; + } + $user->email = $email ? : 'admin@example.com'; + $user->setPassword($password ? : 'admin321'); $user->generateAuthKey(); if ($user->save()) { - $this->stdout('User created' . "\n", Console::FG_GREEN); + if ($user->isNewRecord) { + $this->stdout('User created' . "\n", Console::FG_GREEN); + } else { + $this->stdout('User updated' . "\n", Console::FG_GREEN); + } } else { $this->stdout('Error!' . "\n", Console::FG_RED); + return self::EXIT_CODE_ERROR; } + return self::EXIT_CODE_NORMAL; } } \ No newline at end of file diff --git a/environments/dbmysql.php b/environments/dbmysql.php new file mode 100644 index 0000000..dbf6d83 --- /dev/null +++ b/environments/dbmysql.php @@ -0,0 +1,9 @@ + 'yii\db\Connection', + 'dsn' => '', + 'username' => '', + 'password' => '', + 'charset' => 'utf8', + ]; + //mysql:host=localhost;dbname=yii2advanced \ No newline at end of file diff --git a/environments/dbpostgresql.php b/environments/dbpostgresql.php new file mode 100644 index 0000000..ee580f4 --- /dev/null +++ b/environments/dbpostgresql.php @@ -0,0 +1,15 @@ + 'yii\db\Connection', + 'dsn' => '', + 'username' => '', + 'password' => '', + 'charset' => 'utf8', + 'schemaMap' => [ + 'pgsql' => [ + 'class' => 'yii\db\pgsql\Schema', + 'defaultSchema' => '', + ], + ], + ]; + //DSN: pgsql:host=127.0.0.1;port=5432;dbname=name \ No newline at end of file diff --git a/environments/dev/common/config/main-local.php b/environments/dev/common/config/main-local.php index 43db30e..11c239d 100644 --- a/environments/dev/common/config/main-local.php +++ b/environments/dev/common/config/main-local.php @@ -1,13 +1,7 @@ [ - 'db' => [ - 'class' => 'yii\db\Connection', - 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8', - ], + 'db' => '', 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer', 'viewPath' => '@common/mail', diff --git a/environments/index.php b/environments/index.php index 5c3cca3..0070519 100644 --- a/environments/index.php +++ b/environments/index.php @@ -30,14 +30,14 @@ */ return [ 'Development' => [ - 'path' => 'dev', - 'setWritable' => [ + 'path' => 'dev', + 'setWritable' => [ 'backend/runtime', 'backend/web/assets', 'frontend/runtime', 'frontend/web/assets', ], - 'setExecutable' => [ + 'setExecutable' => [ 'yii', 'yii_test', ], @@ -45,21 +45,23 @@ return [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], + 'setDbConnection' => 'common/config/main-local.php', ], 'Production' => [ - 'path' => 'prod', - 'setWritable' => [ + 'path' => 'prod', + 'setWritable' => [ 'backend/runtime', 'backend/web/assets', 'frontend/runtime', 'frontend/web/assets', ], - 'setExecutable' => [ + 'setExecutable' => [ 'yii', ], 'setCookieValidationKey' => [ 'backend/config/main-local.php', 'frontend/config/main-local.php', ], + 'setDbConnection' => 'common/config/main-local.php', ], ]; diff --git a/environments/prod/common/config/main-local.php b/environments/prod/common/config/main-local.php index 84c4d9f..195993f 100644 --- a/environments/prod/common/config/main-local.php +++ b/environments/prod/common/config/main-local.php @@ -1,13 +1,7 @@ [ - 'db' => [ - 'class' => 'yii\db\Connection', - 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8', - ], + 'db' => '', 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer', 'viewPath' => '@common/mail', diff --git a/init b/init index 7aaf086..89eb341 100644 --- a/init +++ b/init @@ -1,299 +1,18 @@ #!/usr/bin/env php - * - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -if (!extension_loaded('openssl')) { - die('The OpenSSL PHP extension is required by Yii2.'); -} - -$params = getParams(); -$root = str_replace('\\', '/', __DIR__); -$envs = require("$root/environments/index.php"); -$envNames = array_keys($envs); - -echo "Yii Application Initialization Tool v1.0\n\n"; - -$envName = null; -if (empty($params['env']) || $params['env'] === '1') { - echo "Which environment do you want the application to be initialized in?\n\n"; - foreach ($envNames as $i => $name) { - echo " [$i] $name\n"; - } - echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; - $answer = trim(fgets(STDIN)); - - if (!ctype_digit($answer) || !in_array($answer, range(0, count($envs) - 1))) { - echo "\n Quit initialization.\n"; - exit(0); - } - - if (isset($envNames[$answer])) { - $envName = $envNames[$answer]; - } -} else { - $envName = $params['env']; -} - -if (!in_array($envName, $envNames)) { - $envsList = implode(', ', $envNames); - echo "\n $envName is not a valid environment. Try one of the following: $envsList. \n"; - exit(2); -} - -$env = $envs[$envName]; - -if (empty($params['env'])) { - echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] "; - $answer = trim(fgets(STDIN)); - if (strncasecmp($answer, 'y', 1)) { - echo "\n Quit initialization.\n"; - exit(0); - } -} - -echo "\n Start initialization ...\n\n"; -$files = getFileList("$root/environments/{$env['path']}"); -if (isset($env['skipFiles'])) { - $skipFiles = $env['skipFiles']; - array_walk($skipFiles, function(&$value) use($env, $root) { $value = "$root/$value"; }); - $files = array_diff($files, array_intersect_key($env['skipFiles'], array_filter($skipFiles, 'file_exists'))); -} -$all = false; -foreach ($files as $file) { - if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all, $params)) { - break; - } -} - -$callbacks = ['setCookieValidationKey', 'setWritable', 'setExecutable', 'createSymlink']; -foreach ($callbacks as $callback) { - if (!empty($env[$callback])) { - $callback($root, $env[$callback]); - } -} - -echo "\n ... initialization completed.\n\n"; - -function getFileList($root, $basePath = '') -{ - $files = []; - $handle = opendir($root); - while (($path = readdir($handle)) !== false) { - if ($path === '.git' || $path === '.svn' || $path === '.' || $path === '..') { - continue; - } - $fullPath = "$root/$path"; - $relativePath = $basePath === '' ? $path : "$basePath/$path"; - if (is_dir($fullPath)) { - $files = array_merge($files, getFileList($fullPath, $relativePath)); - } else { - $files[] = $relativePath; - } - } - closedir($handle); - return $files; -} - -function copyFile($root, $source, $target, &$all, $params) -{ - if (!is_file($root . '/' . $source)) { - echo " skip $target ($source not exist)\n"; - return true; - } - if (is_file($root . '/' . $target)) { - if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { - echo " unchanged $target\n"; - return true; - } - if ($all) { - echo " overwrite $target\n"; - } else { - echo " exist $target\n"; - echo " ...overwrite? [Yes|No|All|Quit] "; - - - $answer = !empty($params['overwrite']) ? $params['overwrite'] : trim(fgets(STDIN)); - if (!strncasecmp($answer, 'q', 1)) { - return false; - } else { - if (!strncasecmp($answer, 'y', 1)) { - echo " overwrite $target\n"; - } else { - if (!strncasecmp($answer, 'a', 1)) { - echo " overwrite $target\n"; - $all = true; - } else { - echo " skip $target\n"; - return true; - } - } - } - } - file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); - return true; - } - echo " generate $target\n"; - @mkdir(dirname($root . '/' . $target), 0777, true); - file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); - return true; -} - -function getParams() -{ - $rawParams = []; - if (isset($_SERVER['argv'])) { - $rawParams = $_SERVER['argv']; - array_shift($rawParams); - } - - $params = []; - foreach ($rawParams as $param) { - if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) { - $name = $matches[1]; - $params[$name] = isset($matches[3]) ? $matches[3] : true; - } else { - $params[] = $param; - } - } - return $params; -} - -function setWritable($root, $paths) -{ - foreach ($paths as $writable) { - if (is_dir("$root/$writable")) { - if (@chmod("$root/$writable", 0777)) { - echo " chmod 0777 $writable\n"; - } else { - printError("Operation chmod not permitted for directory $writable."); - } - } else { - printError("Directory $writable does not exist."); - } - } -} - -function setExecutable($root, $paths) -{ - foreach ($paths as $executable) { - if (file_exists("$root/$executable")) { - if (@chmod("$root/$executable", 0755)) { - echo " chmod 0755 $executable\n"; - } else { - printError("Operation chmod not permitted for $executable."); - } - } else { - printError("$executable does not exist."); - } - } -} - -function setCookieValidationKey($root, $paths) -{ - foreach ($paths as $file) { - echo " generate cookie validation key in $file\n"; - $file = $root . '/' . $file; - $length = 32; - $bytes = openssl_random_pseudo_bytes($length); - $key = strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.'); - $content = preg_replace('/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/', "\\1'$key'", file_get_contents($file)); - file_put_contents($file, $content); - } -} - -function createSymlink($root, $links) -{ - foreach ($links as $link => $target) { - //first removing folders to avoid errors if the folder already exists - @rmdir($root . "/" . $link); - //next removing existing symlink in order to update the target - if (is_link($root . "/" . $link)) { - @unlink($root . "/" . $link); - } - if (@symlink($root . "/" . $target, $root . "/" . $link)) { - echo " symlink $root/$target $root/$link\n"; - } else { - printError("Cannot create symlink $root/$target $root/$link."); - } - } -} - -/** - * Prints error message. - * @param string $message message - */ -function printError($message) -{ - echo "\n " . formatMessage("Error. $message", ['fg-red']) . " \n"; -} - -/** - * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream. - * - * - windows without ansicon - * - not tty consoles - * - * @return boolean true if the stream supports ANSI colors, otherwise false. - */ -function ansiColorsSupported() -{ - return DIRECTORY_SEPARATOR === '\\' - ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON' - : function_exists('posix_isatty') && @posix_isatty(STDOUT); -} - -/** - * Get ANSI code of style. - * @param string $name style name - * @return integer ANSI code of style. - */ -function getStyleCode($name) -{ - $styles = [ - 'bold' => 1, - 'fg-black' => 30, - 'fg-red' => 31, - 'fg-green' => 32, - 'fg-yellow' => 33, - 'fg-blue' => 34, - 'fg-magenta' => 35, - 'fg-cyan' => 36, - 'fg-white' => 37, - 'bg-black' => 40, - 'bg-red' => 41, - 'bg-green' => 42, - 'bg-yellow' => 43, - 'bg-blue' => 44, - 'bg-magenta' => 45, - 'bg-cyan' => 46, - 'bg-white' => 47, - ]; - return $styles[$name]; -} - -/** - * Formats message using styles if STDOUT supports it. - * @param string $message message - * @param string[] $styles styles - * @return string formatted message. - */ -function formatMessage($message, $styles) -{ - if (empty($styles) || !ansiColorsSupported()) { - return $message; - } - - return sprintf("\x1b[%sm", implode(';', array_map('getStyleCode', $styles))) . $message . "\x1b[0m"; -} + /** + * Yii Application Initialization Tool + * In order to run in non-interactive mode: + * init --env=Development --overwrite=n + * + * @author Alexander Makarov + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + require( 'Initializer.php' ); + if (!extension_loaded('openssl')) { + die( 'The OpenSSL PHP extension is required by Yii2.' ); + } + + Initializer::initialize(); -- libgit2 0.21.4