2011-06-17 21:06:54 +00:00
|
|
|
<?php
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* This file is part of MySQLDumper released under the GNU/GPL 2 license
|
|
|
|
* http://www.mysqldumper.net
|
|
|
|
*
|
|
|
|
* @package MySQLDumper
|
2011-06-19 21:26:17 +00:00
|
|
|
* @subpackage SQL-Parser
|
2011-06-19 13:47:44 +00:00
|
|
|
* @version SVN: $Rev$
|
|
|
|
* @author $Author$
|
2011-06-18 11:10:40 +00:00
|
|
|
*/
|
2011-06-17 21:06:54 +00:00
|
|
|
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* Class to parse MySQL queries.
|
|
|
|
* This enables you to analyze and modify MySQL queries, which the user has entered.
|
|
|
|
*
|
|
|
|
* @package MySQLDumper
|
|
|
|
* @subpackage SQL-Browser
|
|
|
|
*/
|
2011-06-19 13:57:40 +00:00
|
|
|
class Msd_Sql_Parser implements Iterator
|
2011-06-17 21:06:54 +00:00
|
|
|
{
|
2011-06-19 13:47:44 +00:00
|
|
|
/**
|
|
|
|
* Parsed MySQL statements.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private $_parsedStatements = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Holds the summary of the parsing process.
|
|
|
|
* The summary contains the count of each statement in the query.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private $_parsingSummary = array();
|
|
|
|
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* MySQL comment types.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2011-06-17 21:06:54 +00:00
|
|
|
protected $_sqlComments = array(
|
2011-06-19 14:42:30 +00:00
|
|
|
'--' => "\n", '/*' => '*/', '/*!' => '*/'
|
2011-06-17 21:06:54 +00:00
|
|
|
);
|
|
|
|
|
2011-06-19 14:42:30 +00:00
|
|
|
/**
|
2011-06-19 15:43:36 +00:00
|
|
|
* Whether to save debug output
|
|
|
|
*
|
2011-06-19 14:42:30 +00:00
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
private $_debug = false;
|
|
|
|
|
2011-06-19 15:43:36 +00:00
|
|
|
/**
|
|
|
|
* Debug output buffer
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $_debugOutput = '';
|
|
|
|
|
|
|
|
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* Class constructor.
|
|
|
|
* Creates a new instance of the MySQL parser and optionally assign the raw MySQL query.
|
|
|
|
*
|
2011-06-19 21:26:17 +00:00
|
|
|
* @param Msd_Sql_Object $sqlObject SQL-Object holding the data to be parsed
|
|
|
|
* @param bool $debug If turned on, detection of queries is logged
|
2011-06-18 11:10:40 +00:00
|
|
|
*/
|
2011-06-19 21:26:17 +00:00
|
|
|
public function __construct(Msd_Sql_Object $sqlObject, $debug = false)
|
2011-06-17 21:06:54 +00:00
|
|
|
{
|
2011-06-20 08:07:13 +00:00
|
|
|
$this->_sql = $sqlObject;
|
2011-06-19 14:42:30 +00:00
|
|
|
$this->_debug = $debug;
|
2011-06-17 21:06:54 +00:00
|
|
|
}
|
|
|
|
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* Parses a raw MySQL query.
|
|
|
|
* This could include more than one MySQL statement.
|
|
|
|
*
|
|
|
|
* @throws Msd_Sql_Parser_Exception
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2011-06-19 21:26:17 +00:00
|
|
|
public function parse()
|
2011-06-17 21:06:54 +00:00
|
|
|
{
|
|
|
|
$statementCounter = 0;
|
2011-06-20 08:07:13 +00:00
|
|
|
while ($this->_sql->hasMoreToProcess() && $this->_sql->movePointerToNextCommand()!==false) {
|
|
|
|
$startPosition = $this->_sql->getPointer();
|
|
|
|
// get first "word" of query to extract the kind we have to process
|
2011-06-20 09:13:45 +00:00
|
|
|
$endOfFirstWord = $this->_sql->getPosition(' ', false);
|
2011-06-20 08:07:13 +00:00
|
|
|
// get substring from actual position to found position
|
|
|
|
$sqlQuery = $this->_sql->getData($endOfFirstWord - $startPosition);
|
|
|
|
$statement = strtolower($sqlQuery);
|
2011-06-19 14:42:30 +00:00
|
|
|
// check for comments or conditional comments
|
2011-06-19 21:26:17 +00:00
|
|
|
$commentCheck = substr($sqlQuery, 0, 2);
|
2011-06-19 14:42:30 +00:00
|
|
|
if (isset($this->_sqlComments[$commentCheck]) || substr($statement, 0, 3) == '/*!') {
|
2011-06-19 21:26:17 +00:00
|
|
|
$statement = 'Comment';
|
2011-06-17 21:06:54 +00:00
|
|
|
}
|
2011-06-19 14:42:30 +00:00
|
|
|
|
2011-06-20 08:07:13 +00:00
|
|
|
try {
|
|
|
|
$foundStatement = $this->_parseStatement($this->_sql, ucfirst($statement));
|
|
|
|
} catch (Msd_Sql_Parser_Exception $e) {
|
|
|
|
// stop parsing by setting pointer to the end
|
|
|
|
$this->_sql->setPointer($this->_sql->getLength()-1);
|
2011-06-20 09:13:45 +00:00
|
|
|
echo "<br>Error: ".$e->getMessage();
|
2011-06-20 08:07:13 +00:00
|
|
|
}
|
2011-06-19 15:43:36 +00:00
|
|
|
if ($this->_debug) {
|
|
|
|
$this->_debugOutput .= '<br />Extracted statement: '.$foundStatement;
|
|
|
|
}
|
|
|
|
$this->_parsedStatements[] = $foundStatement;
|
2011-06-19 21:26:17 +00:00
|
|
|
$statementCounter++;
|
2011-06-19 15:43:36 +00:00
|
|
|
// increment query type counter
|
2011-06-19 13:47:44 +00:00
|
|
|
if (!isset($this->_parsingSummary[$statement])) {
|
|
|
|
$this->_parsingSummary[$statement] = 0;
|
|
|
|
}
|
|
|
|
$this->_parsingSummary[$statement]++;
|
2011-06-17 21:06:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-18 11:10:40 +00:00
|
|
|
/**
|
|
|
|
* Creates an instance of a statement parser class and invokes statement parsing.
|
|
|
|
*
|
2011-06-19 16:10:32 +00:00
|
|
|
* @throws Msd_Sql_Parser_Exception
|
|
|
|
*
|
2011-06-19 21:26:17 +00:00
|
|
|
* @param Msd_Sql_Object $sqlObject MySQL statement to parse
|
|
|
|
* @param string $statement Parser class to use
|
2011-06-18 11:10:40 +00:00
|
|
|
*
|
2011-06-19 13:47:44 +00:00
|
|
|
* @return array
|
2011-06-18 11:10:40 +00:00
|
|
|
*/
|
2011-06-19 21:26:17 +00:00
|
|
|
private function _parseStatement(Msd_Sql_Object $sqlObject, $statement)
|
2011-06-18 11:10:40 +00:00
|
|
|
{
|
2011-06-19 21:26:17 +00:00
|
|
|
$statementPath = '/Msd/Sql/Parser/Statement/' . $statement;
|
|
|
|
if (!file_exists(LIBRARY_PATH . $statementPath . '.php')) {
|
2011-06-20 09:13:45 +00:00
|
|
|
throw new Msd_Sql_Parser_Exception("Can't find statement class for statement: '" . $statement ."'");
|
2011-06-18 11:10:40 +00:00
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
$statementClass = 'Msd_Sql_Parser_Statement_' . $statement;
|
2011-06-20 08:07:13 +00:00
|
|
|
$parserObject = new $statementClass();
|
2011-06-19 21:26:17 +00:00
|
|
|
return $parserObject->parse($sqlObject);
|
2011-06-19 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
2011-06-19 13:57:40 +00:00
|
|
|
/**
|
|
|
|
* Returns the array with the parsed statements.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getParsedStatements()
|
|
|
|
{
|
|
|
|
return $this->_parsedStatements;
|
|
|
|
}
|
|
|
|
|
2011-06-19 13:47:44 +00:00
|
|
|
/**
|
|
|
|
* Returns the parsing summary.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getSummary()
|
|
|
|
{
|
|
|
|
return $this->_parsingSummary;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rewind (reset) the internal pointer position af the parsed statements array.
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function rewind()
|
|
|
|
{
|
|
|
|
return reset($this->_parsedStatements);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the current value af the parsed statements array.
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function current()
|
|
|
|
{
|
|
|
|
return current($this->_parsedStatements);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the current key af the parsed statements array.
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function key()
|
|
|
|
{
|
|
|
|
return key($this->_parsedStatements);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the internal pointer af the parsed statements array to the next position.
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function next()
|
|
|
|
{
|
|
|
|
return next($this->_parsedStatements);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates the internal pointer position af the parsed statements array.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function valid()
|
|
|
|
{
|
|
|
|
return key($this->_parsedStatements) !== null;
|
2011-06-18 11:10:40 +00:00
|
|
|
}
|
2011-06-19 15:43:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get debug output buffer
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getDebugOutput()
|
|
|
|
{
|
|
|
|
return $this->_debugOutput;
|
|
|
|
}
|
2011-06-17 21:06:54 +00:00
|
|
|
}
|