/settings.php files. * * To execute this script via a web browser: * http:///database/create.php?dbms= * * To execute this script from the command line: * php ./create.php * * Where is the Database Management System you are using * ('mysql' or 'pgsql'). * * The command line example above assumes 'php' is in your PATH. If it * is not, you need to provide the full path to the PHP executable. * * This script is configured to execute the table creation procedures * only when called directly. This gives you the option to include * the file and use its functions elsewhere (provided the name of * your script is _NOT_ named 'create.php'). */ if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) { if (isset($_GET) && isset($_GET['dbms'])) { if (create($_GET['dbms'], 'web')) { echo "\nYour tables were created!\n"; } else { echo "\nNo tables were created.\n"; } } elseif (isset($_SERVER['argv']) && isset($_SERVER['argv'][1])) { if (create($_SERVER['argv'][1], 'shell')) { echo "\nYour tables were created!\n"; } else { echo "\nNo tables were created.\n"; } } } /** * Creates all tables for all sites in /drupal/sites/* * * Calls the various functions needed to gather and implement the tables: * + Gets the $db_url and $db_prefix from each settings.php file. * + Gets the SQL script for the DBMS specified. * + Assembles the queries for each site, prefixing table names as needed. * + Bunches all of the sites' queries per db_url and removes duplicates. * + Connects to each db_url and executes the queries for it. * * WARNING: If the variable_get() function has not been defined yet, * this function will define a fake version of it. This is necessary * to avoid having to include bootstrap.inc * * @param $dbms * The string with the DBMS' name ('mysql', 'pgsql'). * @param $interface * The string saying how the user is interacting ('web', 'shell'). * * @return void */ function create($dbms, $interface) { if ($interface == 'web') { $dbms_text = htmlspecialchars($dbms); } else { $dbms_text = $dbms; } $settings = get_site_settings(); if (empty($settings)) { trigger_error('Found no valid settings.php files in /drupal/sites/*.', E_USER_ERROR); } $queries = get_queries_from_file($dbms); if ($queries === false) { trigger_error("'$dbms_text' is an invalid/unsupported DBMS.", E_USER_ERROR); } define_variable_get(); $dbms_len = strlen($dbms); $success = false; foreach ($settings as $db_url => $sites) { if (substr($db_url, 0, $dbms_len) != $dbms) { echo "Drupal's database functions only work with one DBMS at a time.\n" . "Your command requested building tables for '$dbms_text'\n" . 'but the $db_url in sites/' . key($sites) . "/settings.php\n" . "specified a different DBMS.\n"; continue; } $queries_for_db_url = get_queries_for_db_url($sites, $queries); execute_arrays_queries($dbms, $db_url, $queries_for_db_url); $success = true; } return $success; } /** * Obtains configuration settings specified in all settings.php files. * * NOTE: This method will not return data from a settings file that has * its $db_url set to the default value shipped by Drupal. * * An example of an array returned by this function: * @verbatim * Array ( * [mysql://usr:pw@localhost/drp] => Array ( * [localhost2] => Array ( * [default] => site2_ * [named] => Array ( * [users] => share_ * [sessions] => share_ * ) * ) * [localhost3] => Array ( * [default] => site3_ * [named] => Array ( * [users] => share_ * [sessions] => share_ * ) * ) * [localhost5] => Array ( * [default] => site5_ * [named] => Array ( * [sessions] => whatever_ * ) * ) * ) * ) * @endverbatim * * @return * The four dimensional associative array settings for all sites. * The main array's keys is the $db_url. * The sub-array's keys are the site directory names. * The sub-sub-array has two keys: 'default' and 'named'. * 'named' is an array with table names for keys and table name prefixes * for values. * 'default' is a string that should prefix all other tables. * Returns FALSE if the sites directory can not be found. */ function get_site_settings() { $sites_dir = '../sites'; $default_url = 'mysql://username:password@localhost/database'; $settings = array(); $dh = opendir($sites_dir); if (!$dh) { return false; } while (false !== ($site = readdir($dh))) { if (is_dir($sites_dir . '/' . $site)) { switch ($site) { case '.': case '..': case 'CVS': continue 2; } // Get the $db_url and $db_prefix variables. include $sites_dir . '/' . $site . '/settings.php'; if ($db_url == $default_url) { continue; } if (is_string($db_prefix)) { $settings[$db_url][$site]['default'] = $db_prefix; $settings[$db_url][$site]['named'] = array(); } else { if (array_key_exists('default', $db_prefix)) { $settings[$db_url][$site]['default'] = $db_prefix['default']; unset($db_prefix['default']); } else { $settings[$db_url][$site]['default'] = ''; } $settings[$db_url][$site]['named'] = $db_prefix; } } } closedir($dh); return $settings; } /** * Grabs the contents of the query script and puts each query into * an array element. * * @param $dbms * The name of the DBMS you are want the queries for ('mysql', 'pgsql'). * * @return * An array with each query in a separate element. Returns FALSE if * the database query script does not exist. */ function get_queries_from_file($dbms) { $file = './database.' . $dbms; if (!file_exists($file)) { return false; } $func = ''; $contents = ''; $fh = fopen($file, 'r'); while (!feof($fh)) { $line = fgets($fh, 5000); if (preg_match("/^\s*[a-z)']/i", $line)) { /* * Mush functions into one line because they can contain ';' at the * ends of lines and at the end of the query, which trips up the split * ';[\r\n]+' that is used to put queries into separate array elements. */ if (preg_match('/^\s*CREATE FUNCTION/i', $line)) { // Beginning of CREATE FUNCTION statement. $func = trim($line); } elseif ($func) { // CREATE FUNCTION statement is still in the works. $line = ' ' . trim($line); if (preg_match("/ ' LANGUAGE '\w+';/i", $line)) { // End of CREATE FUNCTION statement. $contents .= $func . $line . "\n"; $func = ''; } else { // Middle of CREATE FUNCTION statement. $func .= $line; } } else { // Regular query content. $contents .= $line; } } } $queries = preg_split('/;[\r\n]+/', $contents); $last_key = count($queries) - 1; if (trim($queries[$last_key]) == '') { unset($queries[$last_key]); } return $queries; } /** * Assembles all of the queries for a db_url. * * The process includes getting the table names prefixed (via * prefix_queries_in_array()) and then eliminating duplicate queries. * * @param $sites * The associative array of sites containing the sub-array of its * table name prefixes. * @param $queries * The enumerated array containing one query per element. * * @return * The array of queries for the db_url. */ function get_queries_for_db_url($sites, $queries) { $out = array(); foreach ($sites as $site => $prefixes) { $out = array_merge($out, prefix_queries_in_array($queries, $prefixes['default'], $prefixes['named'])); } return array_unique($out); } /** * Goes through each element of the queries array, prefixing the table * names as needed. * * @param $queries * The enumerated array containing one query per element. * @param $default * The string for prefixing all other tables. * @param $named * The associative array of specific tables to prefix. * Each key is a table name and the value is the prefix for that table. * * @return * The array of queries with the table names prefixed as needed. */ function prefix_queries_in_array($queries, $default, $named) { $commands = 'CREATE TABLE|CREATE SEQUENCE|' . 'ALTER TABLE|ALTER SEQUENCE|' . 'INSERT INTO|UPDATE|REPLACE|DELETE FROM|' . 'DROP TABLE|DROP SEQUENCE|DROP INDEX'; $out = array(); foreach ($named as $table => $prefix) { foreach ($queries as $key => $query) { $query = ltrim($query); if (preg_match("/^($commands) ($table)([\s(]+.*)$/is", $query, $match)) { $out[$key] = $match[1] . ' ' . $prefix . $match[2] . $match[3]; unset($queries[$key]); } elseif (preg_match("/^CREATE INDEX ([a-z0-9_]+) ON ($table)([\s(]+.*)$/is", $query, $match)) { $out[$key] = 'CREATE INDEX ' . $prefix . $match[1] . ' ON ' . $prefix . $match[2] . $match[3]; unset($queries[$key]); } } } if (trim($default) == '') { return array_merge($out, $queries); } foreach ($queries as $key => $query) { $query = ltrim($query); if (preg_match("/^($commands) ([a-z0-9_]+)([\s(]+.*)$/is", $query, $match)) { $out[$key] = $match[1] . ' ' . $default . $match[2] . $match[3]; } elseif (preg_match('/^CREATE INDEX ([a-z0-9_]+) ON ([a-z0-9_]+)([\s(]+.*)$/is', $query, $match)) { $out[$key] = 'CREATE INDEX ' . $default . $match[1] . ' ON ' . $default . $match[2] . $match[3]; } else { $out[$key] = $query; } } ksort($out); return $out; } /** * Runs all queries in an array. * * @param $dbms * The string with the DBMS' name ('mysql', 'pgsql'). * @param $db_url * The string holding the database connection information. * @param $queries * The array containing one query per element. * * @return void */ function execute_arrays_queries($dbms, $db_url, $queries) { global $active_db; $includes_dir = '../includes'; require_once $includes_dir . '/database.' . $dbms . '.inc'; /* * Don't need to catch errors here due to die() in db_connect(). * * The return value must be stored in a varialbe named $active_db * because that's the connection variable used in _db_query(). */ $active_db = db_connect($db_url); ksort($queries); foreach ($queries as $query) { // Don't need to catch errors here due to triger_error() in _db_query(). _db_query($query); } } /** * Defines a fake version of variable_get() if it hasn't been defined yet. * * Necessary becasue we don't want to include bootstrap.inc. * * @return void */ function define_variable_get() { if (!function_exists('variable_get')) { function variable_get($name, $default) { return $default; } } } ?>