/src/Propel/Generator/Util/PropelSQLParser.php

https://github.com/esimionato/Propel2 · PHP · 241 lines · 114 code · 18 blank · 109 comment · 5 complexity · f62257a82fae5fcac5e87ada3d4cf041 MD5 · raw file

  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. namespace Propel\Generator\Util;
  10. /**
  11. * Service class for parsing a large SQL string into an array of SQL statements
  12. *
  13. * @author François Zaninotto
  14. * @version $Revision$
  15. * @package propel.generator.util
  16. */
  17. class PropelSQLParser
  18. {
  19. protected $delimiter = ';';
  20. protected $sql = '';
  21. protected $len = 0;
  22. protected $pos = 0;
  23. /**
  24. * Sets the inner SQL string for this object.
  25. * Also resets the parsing cursor (see getNextStatement)
  26. *
  27. * @param string $sql The SQL string to parse
  28. */
  29. public function setSQL($sql)
  30. {
  31. $this->sql = $sql;
  32. $this->pos = 0;
  33. $this->len = strlen($sql);
  34. }
  35. /**
  36. * Gets the inner SQL string for this object.
  37. *
  38. * @return string The SQL string to parse
  39. */
  40. public function getSQL()
  41. {
  42. return $this->sql;
  43. }
  44. /**
  45. * Execute a list of DDL statements based on a string
  46. * Does not use transactions since they are not supported in DDL statements
  47. *
  48. * @param string $input The SQL statements
  49. * @param PDO $connection a connection object
  50. *
  51. * @return integer the number of executed statements
  52. */
  53. public static function executeString($input, $connection)
  54. {
  55. return self::executeStatements(self::parseString($input), $connection);
  56. }
  57. /**
  58. * Execute a list of DDL statements based on the path to the SQL file
  59. * Does not use transactions since they are not supported in DDL statements
  60. *
  61. * @param string $file the path to the SQL file
  62. * @param PDO $connection a connection object
  63. *
  64. * @return integer the number of executed statements
  65. */
  66. public static function executeFile($file, $connection)
  67. {
  68. return self::executeStatements(self::parseFile($file), $connection);
  69. }
  70. /**
  71. * Execute a list of DDL statements based on an array
  72. * Does not use transactions since they are not supported in DDL statements
  73. *
  74. * @param array $statements a list of SQL statements
  75. * @param PDO $connection a connection object
  76. *
  77. * @return integer the number of executed statements
  78. */
  79. protected static function executeStatements($statements, $connection)
  80. {
  81. foreach ($statements as $statement) {
  82. $stmt = $connection->prepare($statement);
  83. if ($stmt instanceof PDOStatement) {
  84. // only execute if has no error
  85. $stmt->execute();
  86. }
  87. }
  88. return count($statements);
  89. }
  90. /**
  91. * Explodes a SQL string into an array of SQL statements.
  92. * @example
  93. * <code>
  94. * echo PropelSQLParser::parseString("-- Table foo
  95. * DROP TABLE foo;
  96. * CREATE TABLE foo (
  97. * id int(11) NOT NULL AUTO_INCREMENT,
  98. * title varchar(255) NOT NULL,
  99. * PRIMARY KEY (id),
  100. * ) ENGINE=InnoDB;");
  101. * // results in
  102. * // array(
  103. * // "DROP TABLE foo;",
  104. * // "CREATE TABLE foo (
  105. * // id int(11) NOT NULL AUTO_INCREMENT,
  106. * // title varchar(255) NOT NULL,
  107. * // PRIMARY KEY (id),
  108. * // ) ENGINE=InnoDB;"
  109. * // )
  110. * </code>
  111. * @param string $input The SQL code to parse
  112. *
  113. * @return array A list of SQL statement strings
  114. */
  115. public static function parseString($input)
  116. {
  117. $parser = new self();
  118. $parser->setSQL($input);
  119. $parser->convertLineFeedsToUnixStyle();
  120. $parser->stripSQLCommentLines();
  121. return $parser->explodeIntoStatements();
  122. }
  123. /**
  124. * Explodes a SQL file into an array of SQL statements.
  125. * @example
  126. * <code>
  127. * echo PropelSQLParser::parseFile('/var/tmp/foo.sql');
  128. * // results in
  129. * // array(
  130. * // "DROP TABLE foo;",
  131. * // "CREATE TABLE foo (
  132. * // id int(11) NOT NULL AUTO_INCREMENT,
  133. * // title varchar(255) NOT NULL,
  134. * // PRIMARY KEY (id),
  135. * // ) ENGINE=InnoDB;"
  136. * // )
  137. * </code>
  138. * @param string $input The absolute path to the file to parse
  139. *
  140. * @return array A list of SQL statement strings
  141. */
  142. public static function parseFile($file)
  143. {
  144. if (!file_exists($file)) {
  145. return array();
  146. }
  147. return self::parseString(file_get_contents($file));
  148. }
  149. public function convertLineFeedsToUnixStyle()
  150. {
  151. $this->setSQL(str_replace(array("\r\n", "\r"), "\n", $this->sql));
  152. }
  153. public function stripSQLCommentLines()
  154. {
  155. $this->setSQL(preg_replace(array(
  156. '#^\s*(//|--|\#).*(\n|$)#m', // //, --, or # style comments
  157. '#^\s*/\*.*?\*/#s' // c-style comments
  158. ), '', $this->sql));
  159. }
  160. /**
  161. * Explodes the inner SQL string into statements based on the SQL statement delimiter (;)
  162. *
  163. * @return array A list of SQL statement strings
  164. */
  165. public function explodeIntoStatements()
  166. {
  167. $this->pos = 0;
  168. $sqlStatements = array();
  169. while ($sqlStatement = $this->getNextStatement()) {
  170. $sqlStatements[] = $sqlStatement;
  171. }
  172. return $sqlStatements;
  173. }
  174. /**
  175. * Gets the next SQL statement in the inner SQL string,
  176. * and advances the cursor to the end of this statement.
  177. *
  178. * @return string A SQL statement
  179. */
  180. public function getNextStatement()
  181. {
  182. $isAfterBackslash = false;
  183. $isInString = false;
  184. $stringQuotes = '';
  185. $parsedString = '';
  186. while ($this->pos < $this->len) {
  187. $char = $this->sql[$this->pos];
  188. // check flags for strings or escaper
  189. switch ($char) {
  190. case "\\":
  191. $isAfterBackslash = true;
  192. break;
  193. case "'":
  194. case "\"":
  195. if ($isInString && $stringQuotes == $char) {
  196. if (!$isAfterBackslash) {
  197. $isInString = false;
  198. }
  199. } elseif (!$isInString) {
  200. $stringQuotes = $char;
  201. $isInString = true;
  202. }
  203. break;
  204. }
  205. $this->pos++;
  206. if ($char !== "\\") {
  207. $isAfterBackslash = false;
  208. }
  209. // check for end of statement
  210. if (!$isInString && $char == $this->delimiter) {
  211. return trim($parsedString);
  212. }
  213. $parsedString .= $char;
  214. }
  215. return trim($parsedString);
  216. }
  217. }