freenetis-github/application/libraries/SqlScriptParser.php @ 18ac9009
18ac9009 | Ondřej Fibich | <?php
|
|
/*
|
|||
* This file is part of open source system FreenetIS
|
|||
* and it is release under GPLv3 licence.
|
|||
*
|
|||
* More info about licence can be found:
|
|||
* http://www.gnu.org/licenses/gpl-3.0.html
|
|||
*
|
|||
* More info about project can be found:
|
|||
* http://www.freenetis.org/
|
|||
*/
|
|||
/**
|
|||
* SQL script file parser for parsing of SQL queries passed as single string
|
|||
* delimited by semicolons. Comment are started using "--".
|
|||
*
|
|||
* @author Ondřej Fibich <fibich@freenetis.org>
|
|||
* @since 1.2
|
|||
*/
|
|||
class SqlScriptParser
|
|||
{
|
|||
// states for finite automata that is used for parsing
|
|||
/** In query state */
|
|||
const STATE_QUERY = 0;
|
|||
/** In query string state */
|
|||
const STATE_STRING = 2;
|
|||
/** In query string escaped character state */
|
|||
const STATE_STRING_ESCAPE = 3;
|
|||
/** In maybe comment state */
|
|||
const STATE_COMMENT_START = 4;
|
|||
/** In comment state */
|
|||
const STATE_COMMENT = 5;
|
|||
/**
|
|||
* Parse SQL queries that must be ended by semicolons.
|
|||
*
|
|||
* @param string $script_content SQL script file content
|
|||
* @return array parsed queries, each query is trimed for white spaces
|
|||
* @throws InvalidArgumentException on invalid script content
|
|||
*/
|
|||
public function parse_queries($script_content)
|
|||
{
|
|||
if (!is_string($script_content) || empty($script_content))
|
|||
{
|
|||
return array();
|
|||
}
|
|||
$s = self::STATE_QUERY; // FA status
|
|||
$queries = array(); // parsed queries (result)
|
|||
$buffer = array(); // help letter buffer
|
|||
$string_term = NULL; // last string termination character (" or ')
|
|||
$script_content_char_array = str_split($script_content);
|
|||
$script_length = count($script_content_char_array);
|
|||
for ($i = 0; $i < $script_length; $i++)
|
|||
{
|
|||
$letter = $script_content_char_array[$i];
|
|||
switch ($s)
|
|||
{
|
|||
case self::STATE_QUERY:
|
|||
case self::STATE_COMMENT_START:
|
|||
$s = $this->fa_in_query($s, $letter, $buffer, $queries,
|
|||
$string_term);
|
|||
break;
|
|||
case self::STATE_STRING:
|
|||
$s = $this->fa_in_string($letter, $buffer, $string_term);
|
|||
break;
|
|||
case self::STATE_STRING_ESCAPE:
|
|||
$buffer[] = $letter;
|
|||
$s = self::STATE_STRING; // only one letter after \
|
|||
break;
|
|||
case self::STATE_COMMENT:
|
|||
$s = $this->fa_in_comment($letter);
|
|||
break;
|
|||
default:
|
|||
throw new Exception('Unhandled FA status: ' . $s);
|
|||
}
|
|||
}
|
|||
return $queries;
|
|||
}
|
|||
private function fa_in_query($status, $letter, array &$buffer,
|
|||
array &$queries, &$string_term)
|
|||
{
|
|||
switch ($letter)
|
|||
{
|
|||
case '-':
|
|||
if ($status === self::STATE_COMMENT_START)
|
|||
{
|
|||
array_pop($buffer); // remove -
|
|||
return self::STATE_COMMENT;
|
|||
}
|
|||
$buffer[] = $letter;
|
|||
return self::STATE_COMMENT_START;
|
|||
case '"':
|
|||
case '\'':
|
|||
$buffer[] = $letter;
|
|||
$string_term = $letter;
|
|||
return self::STATE_STRING;
|
|||
case ';': // query end
|
|||
if (empty($buffer))
|
|||
{
|
|||
throw new InvalidArgumentException('empty SQL query parsed');
|
|||
}
|
|||
$queries[] = trim(implode('', $buffer));
|
|||
$buffer = array();
|
|||
return self::STATE_QUERY;
|
|||
default:
|
|||
$buffer[] = $letter;
|
|||
return $status;
|
|||
}
|
|||
}
|
|||
private function fa_in_string($letter, array &$buffer, &$string_term)
|
|||
{
|
|||
$buffer[] = $letter;
|
|||
switch ($letter)
|
|||
{
|
|||
case '\\':
|
|||
return self::STATE_STRING_ESCAPE;
|
|||
case $string_term:
|
|||
$string_term = NULL;
|
|||
return self::STATE_QUERY;
|
|||
default:
|
|||
return self::STATE_STRING;
|
|||
}
|
|||
}
|
|||
private function fa_in_comment($letter)
|
|||
{
|
|||
return ($letter === "\n") ? self::STATE_QUERY : self::STATE_COMMENT;
|
|||
}
|
|||
}
|