* $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $ * * @category Database * @package MDB2_Schema * @author Christian Weiske * @license BSD http://www.opensource.org/licenses/bsd-license.php * @version CVS: $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $ * @link http://pear.php.net/packages/MDB2_Schema */ require_once 'MDB2/Schema.php'; require_once 'MDB2/Schema/Tool/ParameterException.php'; /** * Command line tool to work with database schemas * * Functionality: * - dump a database schema to stdout * - import schema into database * - create a diff between two schemas * - apply diff to database * * @category Database * @package MDB2_Schema * @author Christian Weiske * @license BSD http://www.opensource.org/licenses/bsd-license.php * @link http://pear.php.net/packages/MDB2_Schema */ class MDB2_Schema_Tool { /** * Run the schema tool * * @param array $args Array of command line arguments */ public function __construct($args) { $strAction = $this->getAction($args); try { $this->{'do' . ucfirst($strAction)}($args); } catch (MDB2_Schema_Tool_ParameterException $e) { $this->{'doHelp' . ucfirst($strAction)}($e->getMessage()); } }//public function __construct($args) /** * Runs the tool with command line arguments * * @return void */ public static function run() { $args = $GLOBALS['argv']; array_shift($args); try { $tool = new MDB2_Schema_Tool($args); } catch (Exception $e) { self::toStdErr($e->getMessage() . "\n"); } }//public static function run() /** * Reads the first parameter from the argument array and * returns the action. * * @param array &$args Command line parameters * * @return string Action to execute */ protected function getAction(&$args) { if (count($args) == 0) { return 'help'; } $arg = array_shift($args); switch ($arg) { case 'h': case 'help': case '-h': case '--help': return 'help'; case 'd': case 'dump': case '-d': case '--dump': return 'dump'; case 'l': case 'load': case '-l': case '--load': return 'load'; case 'i': case 'diff': case '-i': case '--diff': return 'diff'; case 'a': case 'apply': case '-a': case '--apply': return 'apply'; case 'n': case 'init': case '-i': case '--init': return 'init'; default: throw new MDB2_Schema_Tool_ParameterException("Unknown mode \"$arg\""); } }//protected function getAction(&$args) /** * Writes the message to stderr * * @param string $msg Message to print * * @return void */ protected static function toStdErr($msg) { file_put_contents('php://stderr', $msg); }//protected static function toStdErr($msg) /** * Displays generic help to stdout * * @return void */ protected function doHelp() { self::toStdErr(<< '
', 'idxname_format' => '%s', 'debug' => true, 'quote_identifier' => true, 'force_defaults' => false, 'portability' => true, 'use_transactions' => false, ); return $options; }//protected function getSchemaOptions() /** * Checks if the passed parameter is a PEAR_Error object * and throws an exception in that case. * * @param mixed $object Some variable to check * @param string $location Where the error occured * * @return void */ protected function throwExceptionOnError($object, $location = '') { if (PEAR::isError($object)) { //FIXME: exception class //debug_print_backtrace(); throw new Exception('Error ' . $location . "\n" . $object->getMessage() . "\n" . $object->getUserInfo() ); } }//protected function throwExceptionOnError($object, $location = '') /** * Loads a file or a dsn from the arguments * * @param array &$args Array of arguments to the program * * @return array Array of ('file'|'dsn', $value) */ protected function getFileOrDsn(&$args) { if (count($args) == 0) { throw new MDB2_Schema_Tool_ParameterException('File or DSN expected'); } $arg = array_shift($args); if ($arg == '-p') { $bAskPassword = true; $arg = array_shift($args); } else { $bAskPassword = false; } if (strpos($arg, '://') === false) { if (file_exists($arg)) { //File return array('file', $arg); } else { throw new Exception('Schema file does not exist'); } } //read password if necessary if ($bAskPassword) { $password = $this->readPasswordFromStdin($arg); $arg = self::setPasswordIntoDsn($arg, $password); self::toStdErr($arg); } return array('dsn', $arg); }//protected function getFileOrDsn(&$args) /** * Takes a DSN data source name and integrates the given * password into it. * * @param string $dsn Data source name * @param string $password Password * * @return string DSN with password */ protected function setPasswordIntoDsn($dsn, $password) { //simple try to integrate password if (strpos($dsn, '@') === false) { //no @ -> no user and no password return str_replace('://', '://:' . $password . '@', $dsn); } else if (preg_match('|://[^:]+@|', $dsn)) { //user only, no password return str_replace('@', ':' . $password . '@', $dsn); } else if (strpos($dsn, ':@') !== false) { //abstract version return str_replace(':@', ':' . $password . '@', $dsn); } return $dsn; }//protected function setPasswordIntoDsn($dsn, $password) /** * Reads a password from stdin * * @param string $dsn DSN name to put into the message * * @return string Password */ protected function readPasswordFromStdin($dsn) { $stdin = fopen('php://stdin', 'r'); self::toStdErr('Please insert password for ' . $dsn . "\n"); $password = ''; $breakme = false; while (false !== ($char = fgetc($stdin))) { if (ord($char) == 10 || $char == "\n" || $char == "\r") { break; } $password .= $char; } fclose($stdin); return trim($password); }//protected function readPasswordFromStdin() /** * Creates a database schema dump and sends it to stdout * * @param array $args Command line arguments * * @return void */ protected function doDump($args) { $dump_what = MDB2_SCHEMA_DUMP_STRUCTURE; $arg = ''; if (count($args)) { $arg = $args[0]; } switch (strtolower($arg)) { case 'all': $dump_what = MDB2_SCHEMA_DUMP_ALL; array_shift($args); break; case 'data': $dump_what = MDB2_SCHEMA_DUMP_CONTENT; array_shift($args); break; case 'schema': array_shift($args); } list($type, $dsn) = $this->getFileOrDsn($args); if ($type == 'file') { throw new MDB2_Schema_Tool_ParameterException( 'Dumping a schema file as a schema file does not make much sense' ); } $schema = MDB2_Schema::factory($dsn, $this->getSchemaOptions()); $this->throwExceptionOnError($schema); $definition = $schema->getDefinitionFromDatabase(); $this->throwExceptionOnError($definition); $dump_options = array( 'output_mode' => 'file', 'output' => 'php://stdout', 'end_of_line' => "\r\n" ); $op = $schema->dumpDatabase( $definition, $dump_options, $dump_what ); $this->throwExceptionOnError($op); $schema->disconnect(); }//protected function doDump($args) /** * Loads a database schema * * @param array $args Command line arguments * * @return void */ protected function doLoad($args) { list($typeSource, $dsnSource) = $this->getFileOrDsn($args); list($typeDest, $dsnDest) = $this->getFileOrDsn($args); if ($typeDest == 'file') { throw new MDB2_Schema_Tool_ParameterException( 'A schema can only be loaded into a database, not a file' ); } $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions()); $this->throwExceptionOnError($schemaDest); //load definition if ($typeSource == 'file') { $definition = $schemaDest->parseDatabaseDefinitionFile($dsnSource); $where = 'loading schema file'; } else { $schemaSource = MDB2_Schema::factory($dsnSource, $this->getSchemaOptions()); $this->throwExceptionOnError($schemaSource, 'connecting to source database'); $definition = $schemaSource->getDefinitionFromDatabase(); $where = 'loading definition from database'; } $this->throwExceptionOnError($definition, $where); //create destination database from definition $simulate = false; $op = $schemaDest->createDatabase($definition, array(), $simulate); $this->throwExceptionOnError($op, 'creating the database'); }//protected function doLoad($args) /** * Initializes a database with data * * @param array $args Command line arguments * * @return void */ protected function doInit($args) { list($typeSource, $dsnSource) = $this->getFileOrDsn($args); list($typeDest, $dsnDest) = $this->getFileOrDsn($args); if ($typeSource != 'file') { throw new MDB2_Schema_Tool_ParameterException( 'Data must come from a source file' ); } if ($typeDest != 'dsn') { throw new MDB2_Schema_Tool_ParameterException( 'A schema can only be loaded into a database, not a file' ); } $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions()); $this->throwExceptionOnError($schemaDest, 'connecting to destination database'); $definition = $schemaDest->getDefinitionFromDatabase(); $this->throwExceptionOnError($definition, 'loading definition from database'); $op = $schemaDest->writeInitialization($dsnSource, $definition); $this->throwExceptionOnError($op, 'initializing database'); }//protected function doInit($args) }//class MDB2_Schema_Tool ?>