* @version 1.1 */ error_reporting (E_ALL); set_time_limit (0); ini_set ('memory_limit', '-1'); ini_set ('register_argc_argv', '1'); require 'binary.php'; require 'function.php'; $_corePath = dirname (dirname (__FILE__)); define ('COMPOSER', PHP .' '. $_corePath . DIRECTORY_SEPARATOR .'extra'. DIRECTORY_SEPARATOR .'composer.phar'); require $_corePath . DIRECTORY_SEPARATOR .'class'. DIRECTORY_SEPARATOR .'Xml.php'; set_error_handler ('handleError'); echo "\n"; echo "Starting Titan Framework's installation tool... \n\n"; try { if (PHP_SAPI != 'cli') throw new Exception ("[ERROR] This is a command-line script! You cannot call by browser."); if (PHP_OS != 'Linux') throw new Exception ("[ERROR] This functionality works only in Linux servers (homologated on Debian and Ubuntu)."); if (!(int) ini_get ('register_argc_argv')) throw new Exception ("[ERROR] This is a command-line script! You must enable 'register_argc_argv' directive."); if (!function_exists ('system') || !function_exists ('exec')) throw new Exception ("[ERROR] You need enable OS call functions (verify if PHP is not in safe mode)!"); if (!`which git`) throw new Exception ("[ERROR] You need install GIT package (try 'apt-get install git')!"); $commands = array ('GZIP', 'MV', 'SU', 'GIT'); foreach ($commands as $trash => $command) if (!defined ($command)) throw new Exception ("[ERROR] Configure path for binaries of OS in [". $_corePath . DIRECTORY_SEPARATOR ."update". DIRECTORY_SEPARATOR ."binary.php]! \n"); if ($argc < 4) throw new Exception ("[ERROR] The correct formart of command is:\nphp path/to/core/update/install.php git@your.git.host.com:group/repository.git path/where/will/install/instance branch-name"); $_repos = trim ($argv [1]); $_branch = trim ($argv [3]); if (!file_exists ($argv [2]) || !is_dir ($argv [2])) exec ('mkdir -p '. $argv [2]); $_path = realpath ($argv [2]); /* * Cloning repos in last tag */ exec (GIT .' clone --branch '. $_branch .' '. $_repos .' '. $_path); chdir ($_path); exec (GIT .' fetch --all'); exec (GIT .' describe --abbrev=0 --tags origin/'. $_branch, $out); if (!is_array ($out) || !array_key_exists (0, $out) || preg_replace ('/[^0-9\.\-]/i', '', $out [0]) == '') throw new Exception ("Impossible to get last version of instance on remote repository! Please, verify if Git is installed and if branch [". $_branch ."] has TAGs."); $_last = trim ($out [0]); exec (GIT .' checkout origin/'. $_branch); exec (GIT .' pull origin '. $_branch); exec (GIT .' checkout '. $_last); echo "[SUCCESS] Work copy created at [". $_path ."] with last tag inside this branch on repository [". $_last ."]! \n\n"; unset ($out); exec (GIT ." log -1 --format='%ai#%an' ". $_last, $out); $_authorRevision = ''; $_dateRevision = time (); if (is_array ($out) && array_key_exists (0, $out)) { $aux = explode ('#', trim ($out [0])); $_authorRevision = @$aux [1]; $_dateRevision = strtotime (@$aux [0]); } /* * Installing composer dependencies */ exec (COMPOSER .' install --no-dev', $out); exec (COMPOSER .' update --no-dev', $out); /* * Open configuration file */ $file = 'configure/titan.xml'; if (!file_exists ($file) || !is_readable ($file)) throw new Exception ("[ERROR] Dont exists a valid instance of Titan and is not possible create a new without file [". $_path . DIRECTORY_SEPARATOR . $file ."]! \n"); $xml = new Xml ($file); $_xml = $xml->getArray (); if (!isset ($_xml ['titan-configuration'][0])) throw new Exception ("[ERROR] The tag 'titan-configuration' dont exist in file [". $_path . DIRECTORY_SEPARATOR . $file ."]! \n"); $_xml = $_xml ['titan-configuration'][0]; echo "[INFO] The file 'titan.xml' is loaded! [". $_path . DIRECTORY_SEPARATOR . $file ."] \n"; if (isset ($_xml ['url']) && trim ($_xml ['url']) != '') echo "[INFO] This instance is located at [". $_xml ['url'] ."] \n"; if (isset ($_xml ['timezone']) && trim ($_xml ['timezone']) != '') date_default_timezone_set (trim ($_xml ['timezone'])); /* * Verifying prerequisites */ if (!isset ($_xml ['cache-path']) || trim ($_xml ['cache-path']) == '') throw new Exception ("[ERROR] You need set a cache folder on tag 'titan-configuration' of 'titan.xml'!"); exec ('mkdir -p '. $_xml ['cache-path']); $_cache = realpath ($_xml ['cache-path']); if (!isset ($_xml ['archive'][0]['data-path']) || trim ($_xml ['archive'][0]['data-path']) == '') throw new Exception ("[ERROR] You need set a folder to file upload on tag 'archive' of 'titan.xml'!"); exec ('mkdir -p '. $_xml ['archive'][0]['data-path']); $_conf = array ( 'environment' => '', 'backup' => TRUE, 'file-mode' => '664', 'dir-mode' => '775', 'owner' => 'root', 'group' => 'staff', 'changelog' => 'DEFAULT' ); if (array_key_exists ('update', $_xml)) { foreach ($_conf as $key => $value) if (array_key_exists ($key, $_xml ['update'][0]) && trim ($_xml ['update'][0][$key]) != '') { if (is_bool ($value)) $_conf [$key] = strtoupper (trim ($_xml ['update'][0][$key])) == 'FALSE' ? FALSE : TRUE; else $_conf [$key] = trim ($_xml ['update'][0][$key]); } } if (!is_numeric ($_conf ['file-mode']) || strlen ($_conf ['file-mode']) != 3 || !is_numeric ($_conf ['dir-mode']) || strlen ($_conf ['dir-mode']) != 3) throw new Exception ("[ERROR] You need fix file and folder permissions that will be setted on 'titan.xml' (e.g. 664 and 775)!"); $_conf ['file-mode'] = octdec ('0'. $_conf ['file-mode']); $_conf ['dir-mode'] = octdec ('0'. $_conf ['dir-mode']); if ($_conf ['backup'] && (!isset ($_xml ['backup'][0]['path']) || trim ($_xml ['backup'][0]['path']) == '' || !isset ($_xml ['backup'][0]['validity']) || !is_numeric ($_xml ['backup'][0]['validity']))) throw new Exception ("[ERROR] You need fix backup parameters on tag 'backup' of 'titan.xml'!"); exec ('mkdir -p '. $_xml ['backup'][0]['path']); $_conf ['changelog'] = strtoupper ($_conf ['changelog']); /* * Registring version and release */ $aux = explode ('-', preg_replace ('/[^0-9\.\-]/i', '', $_last)); if (array_key_exists (0, $aux) && trim ($aux [0]) != '') @file_put_contents ('update'. DIRECTORY_SEPARATOR .'VERSION', $aux [0]); $release = ''; if (array_key_exists (1, $aux) && trim ($aux [1]) != '') $release = trim ($aux [1]); @file_put_contents ($_cache . DIRECTORY_SEPARATOR .'RELEASE', '; Generated by auto-deploy script at '. date ('Y-m-d H:i:s') ."\n". 'version = '. $release ."\n". 'environment = "'. $_conf ['environment'] .'"' ."\n". 'date = '. $_dateRevision ."\n". 'author = "'. $_authorRevision .'"'); /* * Setting permissions */ setPermission ($_path, $_conf ['dir-mode'], $_conf ['file-mode'], $_conf ['owner'], $_conf ['group']); setPermission ($_cache, octdec ('0755'), octdec ('0644'), 'www-data', 'staff'); setPermission ($_xml ['archive'][0]['data-path'], octdec ('0755'), octdec ('0644'), 'www-data', 'staff'); if ($_conf ['backup']) setPermission ($_xml ['backup'][0]['path'], octdec ('0755'), octdec ('0644'), 'www-data', 'staff'); echo "[INFO] Permission setted recursively to work copy [". $_path ."]! \n\n"; /* * Connecting to DB */ if (!isset ($_xml ['database'][0]) || !isset ($_xml ['database'][0]['host']) || !isset ($_xml ['database'][0]['name'])) throw new Exception ("[ERROR] You need configure 'database' on 'titan.xml'!"); $_xml ['database'][0]['port'] = isset ($_xml ['database'][0]['port']) && is_numeric ($_xml ['database'][0]['port']) ? trim ($_xml ['database'][0]['port']) : '5432'; if (!in_array ($_xml ['database'][0]['host'], array ('localhost', '127.0.0.1', '::1'))) $dsn = 'pgsql:host='. $_xml ['database'][0]['host'] .' port='. $_xml ['database'][0]['port'] .' dbname='. $_xml ['database'][0]['name'] .' user='. @$_xml ['database'][0]['user'] .' password='. @$_xml ['database'][0]['password']; else $dsn = 'pgsql:dbname='. $_xml ['database'][0]['name'] .' user='. @$_xml ['database'][0]['user'] .' password='. @$_xml ['database'][0]['password']; echo "To connect on database, the bottom DSN will be used. Please, verify and press ENTER if is correct. If not, enter with correct DSN bellow. \n"; echo "If database or user entered does not exists, its will be created. In case of user, the password inserted will be used. Leave password blank to Titan use Unix sockets (recomended). \n"; echo "Use '". $dsn ."' or type a alternative: \n"; $input = fgets (fopen ('php://stdin', 'r')); if (trim ($input) == '') { $dbUser = @$_xml ['database'][0]['user']; $dbPass = @$_xml ['database'][0]['password']; $dbName = @$_xml ['database'][0]['name']; $dbPort = @$_xml ['database'][0]['port']; $dbHost = @$_xml ['database'][0]['host']; } else { $dsn = trim ($input); while (strpos ($dsn, ' ') !== FALSE) $dsn = str_replace (' ', ' ', $dsn); $params = explode (' ', substr ($dsn, 6)); $dbUser = $dbPass = $dbName = ''; $dbHost = 'localhost'; $dbPort = '5432'; foreach ($params as $trash => $param) { $aux = explode ('=', $param); switch ($aux [0]) { case 'user': $dbUser = $aux [1]; break; case 'password': $dbPass = $aux [1]; break; case 'host': $dbHost = $aux [1]; break; case 'port': $dbPort = $aux [1]; break; case 'dbname': $dbName = $aux [1]; break; } } } if (in_array ($dbHost, array ('localhost', '127.0.0.1', '::1')) && !`su - postgres -c "psql -tAc \"SELECT 1 FROM pg_database where datname = '$dbName';\""`) { echo "\n"; echo "Do not exists a database named '$dbName' in this server. You want to create it? (yes/no): "; $input = fgets (fopen ('php://stdin', 'r')); if (trim ($input) == 'yes') { if (!file_exists ('db'. DIRECTORY_SEPARATOR .'last.sql')) throw new Exception ("[CRITICAL] To install instance is necessary a project with standard folder structure. Thus, is needed a DUMP of initial database data and structure in file [". $_path ."/db/last.sql], but this file does not exists!"); if (!`su - postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname = '$dbUser';\""`) exec ('su - postgres -c "psql -c \"CREATE ROLE '. $dbUser .' WITH LOGIN ENCRYPTED PASSWORD \''. $dbPass .'\';\""'); exec ('su - postgres -c "createdb -E utf8 -O '. $dbUser .' -T template0 '. $dbName .'"'); exec ('su - postgres -c "psql -d '. $dbName .' -c \"CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;\""'); exec ('su - postgres -c "psql -d '. $dbName .' -U '. $dbUser .' < '. $_path . DIRECTORY_SEPARATOR .'db'. DIRECTORY_SEPARATOR .'last.sql"'); } } $_db = new PDO ($dsn, $dbUser, $dbPass); $_db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if (isset ($_xml ['timezone']) && trim ($_xml ['timezone']) != '') $_db->exec ("SET timezone TO '". trim ($_xml ['timezone']) ."'"); $schema = isset ($_xml ['database'][0]['schema']) && trim ($_xml ['database'][0]['schema']) != '' ? trim ($_xml ['database'][0]['schema']) : 'public'; $_versionTable = $schema .'._version'; if (!tableExists ($_db, $_versionTable)) $_db->exec ("CREATE TABLE ". $_versionTable ." (_version CHAR(14) NOT NULL, _author VARCHAR(64) NOT NULL, _date TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, CONSTRAINT _version_pkey PRIMARY KEY(_version))"); /* * Updating database to last version */ $_pathToMigrationFiles = 'update'. DIRECTORY_SEPARATOR .'db'. DIRECTORY_SEPARATOR; if (file_exists ($_pathToMigrationFiles) && is_dir ($_pathToMigrationFiles)) { $query = $_db->query ("SELECT MAX(_version) AS v FROM ". $_versionTable); $version = (int) $query->fetchColumn (0); $dh = opendir ($_pathToMigrationFiles); if (!$dh) throw new Exception ("[CRITICAL] Fail to list migration folder [". $_pathToMigrationFiles ."]!"); $files = array (); while (($file = readdir ($dh)) !== false) { preg_match ('/^(?P\d{14})\.sql/', $file, $m); if (!is_array ($m) || !isset ($m ['v'])) continue; if ($version < (int) $m ['v']) $files [] = $m ['v']; } closedir ($dh); echo "[INFO] Has ". sizeof ($files) ." new versions to be applied in DB... \n"; if (sizeof ($files)) { sort ($files); reset ($files); try { $_db->beginTransaction (); foreach ($files as $trash => $file) { echo "[INFO] Updating specific migration file to head revision [". $_pathToMigrationFiles . $file .".sql]... \n"; system (GIT .' checkout origin/'. $_branch .' -- '. $_pathToMigrationFiles . $file .'.sql', $return); if ($return) throw new PDOException ("[CRITICAL] Fail to update specifc migration file [". $_pathToMigrationFiles . $file .".sql] to head revision!"); if (file_exists ($_pathToMigrationFiles . $file .'.sql')) { echo "[SUCCESS] Migration file [". $_pathToMigrationFiles . $file .".sql] updated to head revision! \n"; $sql = file_get_contents ($_pathToMigrationFiles . $file .'.sql'); if (trim ($sql) != '') $_db->exec ($sql); } else echo "[SUCCESS] Migration file [". $_pathToMigrationFiles . $file .".sql] deleted! \n\n"; } $_sthUpdateVersion = $_db->prepare ("INSERT INTO ". $_versionTable ." (_version, _author) VALUES (:version, :author)"); $_sthUpdateVersion->bindParam (':version', $file, PDO::PARAM_STR, 14); $_sthUpdateVersion->bindParam (':author', $_authorRevision, PDO::PARAM_STR, 64); $_sthUpdateVersion->execute (); $_db->commit (); echo "[SUCCESS] DB is now in version [". $file ."]! \n\n"; } catch (PDOException $e) { $_db->rollBack (); throw new Exception ("[CRITICAL] Error for apply SQL in DB [". $_pathToMigrationFiles . $file .".sql]: ". $e->getMessage ()); } } } echo "[SUCCESS] All done! \n\n"; echo "[WARNING] You still need configure your instance! Please, edit configuration files with correct parameters (like [". $_path ."/configure/titan.xml]). \n\n"; echo "Thanks for using Titan Framework! \n"; echo "http://titanframework.com \n"; } catch (Exception $e) { echo $e->getMessage () ." \n"; } echo "\n";