2011-06-19 21:26:17 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* This file is part of MySQLDumper released under the GNU/GPL 2 license
|
|
|
|
* http://www.mysqldumper.net
|
|
|
|
*
|
|
|
|
* @package MySQLDumper
|
|
|
|
* @subpackage SQL-Parser
|
|
|
|
* @version SVN: $Rev$
|
|
|
|
* @author $Author$
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class to represent sql data to be parsed.
|
2011-06-20 18:27:46 +00:00
|
|
|
* Object is intended to be handled over to the parser classes so that they can work on it.
|
2011-06-19 21:26:17 +00:00
|
|
|
*
|
|
|
|
* @package MySQLDumper
|
|
|
|
* @subpackage SQL-Parser
|
|
|
|
*/
|
|
|
|
class Msd_Sql_Object
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Holds string data to be parsed.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $_data = '';
|
|
|
|
|
|
|
|
/**
|
2011-06-20 18:27:46 +00:00
|
|
|
* Holds a pointer to the actual examined part as offset.
|
2011-06-19 21:26:17 +00:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $_pointer = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Holds the parsing state.
|
|
|
|
* This is set by the data examining methods of the parser and reflects the parser mode (select, insert, ...)
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $_state = '';
|
|
|
|
|
2011-06-20 09:13:45 +00:00
|
|
|
/**
|
|
|
|
* Holds parsing errors.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $_errors = array();
|
|
|
|
|
2011-06-19 21:26:17 +00:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param null $sqlData SQl data to be parsed
|
|
|
|
*/
|
|
|
|
public function __construct($sqlData = null)
|
|
|
|
{
|
|
|
|
$this->_data = $sqlData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets data of object
|
|
|
|
*
|
|
|
|
* @param string $sqlData The queries
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setData($sqlData = '')
|
|
|
|
{
|
|
|
|
$this->_data = $sqlData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append data to already given data.
|
|
|
|
*
|
|
|
|
* @param string $sqlData The sql-string to be appended
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
private function appendData($sqlData)
|
|
|
|
{
|
|
|
|
$this->_data .= $sqlData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the actual position of the pointer.
|
|
|
|
*
|
|
|
|
* @return int Pointer position
|
|
|
|
*/
|
|
|
|
public function getPointer()
|
|
|
|
{
|
|
|
|
return $this->_pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the actual position of the pointer.
|
|
|
|
*
|
|
|
|
* @param int $position Position of pointer
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setPointer($position)
|
|
|
|
{
|
|
|
|
$this->_pointer = $position;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move pointer to the beginning of the next command.
|
|
|
|
*
|
|
|
|
* Skip all spaces and line-breaks until a character is found that might be the beginning
|
|
|
|
* of a new sql command.
|
|
|
|
*
|
|
|
|
* @return int New pointer position
|
|
|
|
*/
|
|
|
|
public function movePointerToNextCommand()
|
|
|
|
{
|
|
|
|
$pointer = $this->getPointer();
|
|
|
|
$dataSize = strlen($this->_data);
|
2011-06-20 13:39:03 +00:00
|
|
|
$skip = array(' ', "\n", "\r", "\t");
|
|
|
|
if ($this->_state !== 'Comment') {
|
|
|
|
$skip[] = "\n";
|
|
|
|
}
|
2011-06-20 08:07:13 +00:00
|
|
|
if (in_array($this->_data[$pointer], $skip)) {
|
|
|
|
while ($pointer < $dataSize && in_array($this->_data[$pointer], $skip)) {
|
|
|
|
$pointer++;
|
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|
|
|
|
$this->setPointer($pointer);
|
2011-06-20 13:39:03 +00:00
|
|
|
if ($pointer >= $this->getLength()) {
|
2011-06-20 08:07:13 +00:00
|
|
|
$pointer = false;
|
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
return $pointer;
|
|
|
|
}
|
2011-06-20 13:39:03 +00:00
|
|
|
/**
|
|
|
|
* Move pointer forward by $positions positions.
|
|
|
|
*
|
|
|
|
* @param integer $positions Move pointer forward by $positions
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function movePointerForward($positions)
|
|
|
|
{
|
|
|
|
$this->setPointer($this->getPointer() + $positions);
|
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
|
|
|
|
/**
|
2011-06-20 13:39:03 +00:00
|
|
|
* Get data from actual pointer to given position.
|
2011-06-19 21:26:17 +00:00
|
|
|
*
|
2011-06-20 13:39:03 +00:00
|
|
|
* @param int $endPosition End position of pointer
|
|
|
|
* @param bool $movePointer Move pointer behind fetched data
|
2011-06-19 21:26:17 +00:00
|
|
|
*
|
|
|
|
* @return string Sql data from the pointer position to end or to the nr of chars to fetch
|
|
|
|
*/
|
2011-06-20 13:39:03 +00:00
|
|
|
public function getData($endPosition, $movePointer = true)
|
2011-06-19 21:26:17 +00:00
|
|
|
{
|
2011-06-20 13:39:03 +00:00
|
|
|
$data = substr($this->_data, $this->_pointer, ($endPosition - $this->_pointer));
|
|
|
|
if ($movePointer === true) {
|
|
|
|
$this->setPointer($endPosition +1);
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|
2011-06-20 13:39:03 +00:00
|
|
|
return $data;
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get length of data string.
|
|
|
|
*
|
|
|
|
* @return int Length of data string
|
|
|
|
*/
|
|
|
|
public function getLength()
|
|
|
|
{
|
|
|
|
return strlen($this->_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if pointer has reached the end of the data string.
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasMoreToProcess()
|
|
|
|
{
|
|
|
|
if ($this->_pointer < $this->getLength()) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the parser state.
|
|
|
|
*
|
|
|
|
* @param string $state The parsing state we are actually in.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setState($state)
|
|
|
|
{
|
|
|
|
$this->_state = $state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the next unescaped occurance of $match.
|
|
|
|
*
|
|
|
|
* Begins to search at the actual postion of the pointer.
|
|
|
|
*
|
2011-06-20 13:39:03 +00:00
|
|
|
* @param string $match The string to find
|
|
|
|
* @param bool $includeMatch Whether to add length of $match to position
|
2011-06-19 21:26:17 +00:00
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
2011-06-20 09:13:45 +00:00
|
|
|
public function getPosition($match = ';', $includeMatch = true)
|
2011-06-19 21:26:17 +00:00
|
|
|
{
|
|
|
|
$pointer = $this->getPointer();
|
|
|
|
$offset = $pointer;
|
|
|
|
$notFound = true;
|
2011-06-20 13:39:03 +00:00
|
|
|
$nextHit = false;
|
|
|
|
$length = $this->getLength() - 1; // zero-based
|
2011-06-19 21:26:17 +00:00
|
|
|
while ($notFound && $offset < $length) {
|
|
|
|
$nextHit = strpos($this->_data, $match, $offset);
|
2011-06-20 13:39:03 +00:00
|
|
|
//echo "<br>getPosition: Search for '".$match."' P: ".$offset."-> Hit at :".$nextHit;
|
2011-06-19 21:26:17 +00:00
|
|
|
if ($nextHit === false) {
|
2011-06-20 13:39:03 +00:00
|
|
|
// check special case for comments
|
|
|
|
if ($this->getState() == 'Comment' && strpos($this->_data, "\n", $pointer) === false) {
|
|
|
|
// there is no next line - return statement "as is"
|
|
|
|
$this->setPointer($this->getLength());
|
|
|
|
return $this->getLength();
|
|
|
|
}
|
|
|
|
// we haven't found the correct end of the query - inform user
|
|
|
|
$lang = Msd_Language::getInstance()->getTranslator();
|
2011-06-20 09:13:45 +00:00
|
|
|
$msg = sprintf(
|
2011-06-20 14:05:07 +00:00
|
|
|
$lang->_('L_SQL_INCOMPLETE_STATEMENT_DETECTED'),
|
|
|
|
$this->getState(),
|
|
|
|
$match,
|
|
|
|
$this->getData(200)
|
2011-06-20 09:13:45 +00:00
|
|
|
);
|
|
|
|
$this->setError($msg);
|
|
|
|
$this->setPointer($this->getLength());
|
|
|
|
return false;
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|
2011-06-20 13:39:03 +00:00
|
|
|
|
|
|
|
$data = $this->getData($nextHit, false);
|
|
|
|
if (!$this->isEscaped($data)) {
|
2011-06-19 21:26:17 +00:00
|
|
|
// hit was not escaped - we found the match
|
|
|
|
$notFound = false;
|
2011-06-20 09:13:45 +00:00
|
|
|
if ($includeMatch) {
|
2011-06-20 13:39:03 +00:00
|
|
|
$nextHit += strlen($match)-1;
|
2011-06-20 09:13:45 +00:00
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
} else {
|
2011-06-20 13:39:03 +00:00
|
|
|
// keep on looking, this one was escaped
|
|
|
|
$offset = $nextHit+1;
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return $nextHit;
|
|
|
|
}
|
2011-06-20 09:13:45 +00:00
|
|
|
|
2011-06-20 13:39:03 +00:00
|
|
|
/**
|
2011-06-20 18:27:46 +00:00
|
|
|
* Get data up to the next new line
|
2011-06-20 13:39:03 +00:00
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getDataUntilNewLine()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if hit is escaped.
|
|
|
|
*
|
|
|
|
* @param string $string String to analyse
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function isEscaped($string)
|
|
|
|
{
|
|
|
|
$string = str_replace('\\\\', '', $string);
|
|
|
|
$quotes = substr_count($string, '\'');
|
|
|
|
$escapedQuotes = substr_count($string, '\\\'');
|
|
|
|
if (($quotes - $escapedQuotes) % 2 == 0) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-20 09:13:45 +00:00
|
|
|
/**
|
|
|
|
* Set an error message
|
|
|
|
*
|
|
|
|
* @param string $msg The error message
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setError($msg)
|
|
|
|
{
|
|
|
|
$this->_errors[] = $msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get error messages
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getErrors()
|
|
|
|
{
|
|
|
|
return $this->_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-06-20 18:27:46 +00:00
|
|
|
* Check if errors occurred.
|
2011-06-20 09:13:45 +00:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasErrors()
|
|
|
|
{
|
|
|
|
$ret = false;
|
|
|
|
if (sizeof($this->_errors) > 0) {
|
|
|
|
$ret = true;
|
|
|
|
}
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get actual parsing state
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getState()
|
|
|
|
{
|
|
|
|
return $this->_state;
|
|
|
|
}
|
2011-06-19 21:26:17 +00:00
|
|
|
}
|