| // +----------------------------------------------------------------------+ // // $Id$ // /** * Writes an XML schema file * * @package MDB2_Schema * @category Database * @access protected * @author Lukas Smith */ class MDB2_Schema_Writer { // {{{ properties var $valid_types = array(); // }}} // {{{ constructor function __construct($valid_types = array()) { $this->valid_types = $valid_types; } function MDB2_Schema_Writer($valid_types = array()) { $this->__construct($valid_types); } // }}} // {{{ raiseError() /** * This method is used to communicate an error and invoke error * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * * @param int|PEAR_Error integer error code or and PEAR_Error instance * @param int error mode, see PEAR_Error docs * * error level (E_USER_NOTICE etc). If error mode is * PEAR_ERROR_CALLBACK, this is the callback function, * either as a function name, or as an array of an * object and method name. For other error modes this * parameter is ignored. * @param string Extra debug information. Defaults to the last * query and native error code. * @return object a PEAR error object * @access public * @see PEAR_Error */ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) { $error =& MDB2_Schema::raiseError($code, $mode, $options, $userinfo); return $error; } // }}} // {{{ _escapeSpecialChars() /** * add escapecharacters to all special characters in a string * * @param string string that should be escaped * @return string escaped string * @access protected */ function _escapeSpecialChars($string) { if (!is_string($string)) { $string = strval($string); } $escaped = ''; for ($char = 0, $count = strlen($string); $char < $count; $char++) { switch ($string[$char]) { case '&': $escaped.= '&'; break; case '>': $escaped.= '>'; break; case '<': $escaped.= '<'; break; case '"': $escaped.= '"'; break; case '\'': $escaped.= '''; break; default: $code = ord($string[$char]); if ($code < 32 || $code > 127) { $escaped.= "&#$code;"; } else { $escaped.= $string[$char]; } break; } } return $escaped; } // }}} // {{{ _dumpBoolean() /** * dump the structure of a sequence * * @param string boolean value or variable definition * @return string with xml boolea definition * @access private */ function _dumpBoolean($boolean) { if (is_string($boolean)) { if ($boolean !== 'true' || $boolean !== 'false' || preg_match('/.*/', $boolean) ) { return $boolean; } } return $boolean ? 'true' : 'false'; } // }}} // {{{ dumpSequence() /** * dump the structure of a sequence * * @param string sequence name * @param string end of line characters * @return mixed string xml sequence definition on success, or a error object * @access public */ function dumpSequence($sequence_definition, $sequence_name, $eol, $dump = MDB2_SCHEMA_DUMP_ALL) { $buffer = "$eol $eol $sequence_name$eol"; if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) { if (!empty($sequence_definition['start'])) { $start = $sequence_definition['start']; $buffer.= " $start$eol"; } } if (!empty($sequence_definition['on'])) { $buffer.= " $eol"; $buffer.= " ".$sequence_definition['on']['table']; $buffer.= "
$eol ".$sequence_definition['on']['field']; $buffer.= "$eol
$eol"; } $buffer.= "
$eol"; return $buffer; } // }}} // {{{ dumpDatabase() /** * Dump a previously parsed database structure in the Metabase schema * XML based format suitable for the Metabase parser. This function * may optionally dump the database definition with initialization * commands that specify the data that is currently present in the tables. * * @param array associative array that takes pairs of tag * names and values that define dump options. * array ( * 'output_mode' => String * 'file' : dump into a file * default: dump using a function * 'output' => String * depending on the 'Output_Mode' * name of the file * name of the function * 'end_of_line' => String * end of line delimiter that should be used * default: "\n" * ); * @param integer determines what data to dump * MDB2_SCHEMA_DUMP_ALL : the entire db * MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db * MDB2_SCHEMA_DUMP_CONTENT : only the content of the db * @return mixed MDB2_OK on success, or a error object * @access public */ function dumpDatabase($database_definition, $arguments, $dump = MDB2_SCHEMA_DUMP_ALL) { if (!empty($arguments['output'])) { if (!empty($arguments['output_mode']) && $arguments['output_mode'] == 'file') { $fp = fopen($arguments['output'], 'w'); if ($fp === false) { return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, 'it was not possible to open output file'); } $output = false; } elseif (is_callable($arguments['output'])) { $output = $arguments['output']; } else { return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, 'no valid output function specified'); } } else { return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, 'no output method specified'); } $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n"; $sequences = array(); if (!empty($database_definition['sequences']) && is_array($database_definition['sequences']) ) { foreach ($database_definition['sequences'] as $sequence_name => $sequence) { $table = !empty($sequence['on']) ? $sequence['on']['table'] :''; $sequences[$table][] = $sequence_name; } } $buffer = ''.$eol; $buffer.= "$eol$eol ".$database_definition['name'].""; $buffer.= "$eol ".$this->_dumpBoolean($database_definition['create']).""; $buffer.= "$eol ".$this->_dumpBoolean($database_definition['overwrite'])."$eol"; if ($output) { call_user_func($output, $buffer); } else { fwrite($fp, $buffer); } if (!empty($database_definition['tables']) && is_array($database_definition['tables'])) { foreach ($database_definition['tables'] as $table_name => $table) { $buffer = "$eol $eol$eol $table_name$eol"; if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_STRUCTURE) { $buffer.= "$eol $eol"; if (!empty($table['fields']) && is_array($table['fields'])) { foreach ($table['fields'] as $field_name => $field) { if (empty($field['type'])) { return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null, 'it was not specified the type of the field "'. $field_name.'" of the table "'.$table_name); } if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) { return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, 'type "'.$field['type'].'" is not yet supported'); } $buffer.= "$eol $eol $field_name$eol "; $buffer.= $field['type']."$eol"; if (!empty($field['unsigned'])) { $buffer.= " ".$this->_dumpBoolean($field['unsigned'])."$eol"; } if (!empty($field['length'])) { $buffer.= ' '.$field['length']."$eol"; } if (!empty($field['notnull'])) { $buffer.= " ".$this->_dumpBoolean($field['notnull'])."$eol"; } else { $buffer.= " false$eol"; } if (!empty($field['fixed']) && $field['type'] === 'text') { $buffer.= " ".$this->_dumpBoolean($field['fixed'])."$eol"; } if (array_key_exists('default', $field) && $field['type'] !== 'clob' && $field['type'] !== 'blob' ) { $buffer.= ' '.$this->_escapeSpecialChars($field['default'])."$eol"; } if (!empty($field['autoincrement'])) { $buffer.= " " . $field['autoincrement'] ."$eol"; } $buffer.= " $eol"; } } if (!empty($table['indexes']) && is_array($table['indexes'])) { foreach ($table['indexes'] as $index_name => $index) { $buffer.= "$eol $eol $index_name$eol"; if (!empty($index['unique'])) { $buffer.= " ".$this->_dumpBoolean($index['unique'])."$eol"; } if (!empty($index['primary'])) { $buffer.= " ".$this->_dumpBoolean($index['primary'])."$eol"; } foreach ($index['fields'] as $field_name => $field) { $buffer.= " $eol $field_name$eol"; if (!empty($field) && is_array($field)) { $buffer.= ' '.$field['sorting']."$eol"; } $buffer.= " $eol"; } $buffer.= " $eol"; } } $buffer.= "$eol $eol"; } if ($output) { call_user_func($output, $buffer); } else { fwrite($fp, $buffer); } $buffer = ''; if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) { if (!empty($table['initialization']) && is_array($table['initialization'])) { $buffer = "$eol $eol"; foreach ($table['initialization'] as $instruction) { switch ($instruction['type']) { case 'insert': $buffer.= "$eol $eol"; foreach ($instruction['data']['field'] as $field) { $field_name = $field['name']; $buffer.= "$eol $eol $field_name$eol"; $buffer.= $this->writeExpression($field['group'], 5, $arguments); $buffer.= " $eol"; } $buffer.= "$eol $eol"; break; case 'update': $buffer.= "$eol $eol"; foreach ($instruction['data']['field'] as $field) { $field_name = $field['name']; $buffer.= "$eol $eol $field_name$eol"; $buffer.= $this->writeExpression($field['group'], 5, $arguments); $buffer.= " $eol"; } if (!empty($instruction['data']['where']) && is_array($instruction['data']['where']) ) { $buffer.= " $eol"; $buffer.= $this->writeExpression($instruction['data']['where'], 5, $arguments); $buffer.= " $eol"; } $buffer.= "$eol $eol"; break; case 'delete': $buffer.= "$eol $eol$eol"; if (!empty($instruction['data']['where']) && is_array($instruction['data']['where']) ) { $buffer.= " $eol"; $buffer.= $this->writeExpression($instruction['data']['where'], 5, $arguments); $buffer.= " $eol"; } $buffer.= "$eol $eol"; break; } } $buffer.= "$eol $eol"; } } $buffer.= "$eol
$eol"; if ($output) { call_user_func($output, $buffer); } else { fwrite($fp, $buffer); } if (isset($sequences[$table_name])) { foreach ($sequences[$table_name] as $sequence) { $result = $this->dumpSequence( $database_definition['sequences'][$sequence], $sequence, $eol, $dump ); if (PEAR::isError($result)) { return $result; } if ($output) { call_user_func($output, $result); } else { fwrite($fp, $result); } } } } } if (isset($sequences[''])) { foreach ($sequences[''] as $sequence) { $result = $this->dumpSequence( $database_definition['sequences'][$sequence], $sequence, $eol, $dump ); if (PEAR::isError($result)) { return $result; } if ($output) { call_user_func($output, $result); } else { fwrite($fp, $result); } } } $buffer = "$eol
$eol"; if ($output) { call_user_func($output, $buffer); } else { fwrite($fp, $buffer); fclose($fp); } return MDB2_OK; } // }}} // {{{ writeExpression() /** * Dumps the structure of an element. Elements can be value, column, * function or expression. * * @param array multi dimensional array that represents the parsed element * of a DML instruction. * @param integer base indentation width * @param array associative array that takes pairs of tag * names and values that define dump options. * * @return string * * @access public * @see MDB2_Schema_Writer::dumpDatabase() */ function writeExpression($element, $offset = 0, $arguments = null) { $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n"; $str = ''; $indent = str_repeat(' ', $offset); $noffset = $offset + 1; switch ($element['type']) { case 'value': $str.= "$indent".$this->_escapeSpecialChars($element['data'])."$eol"; break; case 'column': $str.= "$indent".$this->_escapeSpecialChars($element['data'])."$eol"; break; case 'function': $str.= "$indent$eol$indent ".$this->_escapeSpecialChars($element['data']['name'])."$eol"; if (!empty($element['data']['arguments']) && is_array($element['data']['arguments']) ) { foreach ($element['data']['arguments'] as $v) { $str.= $this->writeExpression($v, $noffset, $arguments); } } $str.= "$indent$eol"; break; case 'expression': $str.= "$indent$eol"; $str.= $this->writeExpression($element['data']['operants'][0], $noffset, $arguments); $str.= "$indent ".$element['data']['operator']."$eol"; $str.= $this->writeExpression($element['data']['operants'][1], $noffset, $arguments); $str.= "$indent$eol"; break; } return $str; } // }}} } ?>