Command.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db\sqlite;
  8. use yii\db\SqlToken;
  9. use yii\helpers\StringHelper;
  10. /**
  11. * Command represents an SQLite's SQL statement to be executed against a database.
  12. *
  13. * {@inheritdoc}
  14. *
  15. * @author Sergey Makinen <sergey@makinen.ru>
  16. * @since 2.0.14
  17. */
  18. class Command extends \yii\db\Command
  19. {
  20. /**
  21. * {@inheritdoc}
  22. */
  23. public function execute()
  24. {
  25. $sql = $this->getSql();
  26. $params = $this->params;
  27. $statements = $this->splitStatements($sql, $params);
  28. if ($statements === false) {
  29. return parent::execute();
  30. }
  31. $result = null;
  32. foreach ($statements as $statement) {
  33. list($statementSql, $statementParams) = $statement;
  34. $this->setSql($statementSql)->bindValues($statementParams);
  35. $result = parent::execute();
  36. }
  37. $this->setSql($sql)->bindValues($params);
  38. return $result;
  39. }
  40. /**
  41. * {@inheritdoc}
  42. */
  43. protected function queryInternal($method, $fetchMode = null)
  44. {
  45. $sql = $this->getSql();
  46. $params = $this->params;
  47. $statements = $this->splitStatements($sql, $params);
  48. if ($statements === false) {
  49. return parent::queryInternal($method, $fetchMode);
  50. }
  51. list($lastStatementSql, $lastStatementParams) = array_pop($statements);
  52. foreach ($statements as $statement) {
  53. list($statementSql, $statementParams) = $statement;
  54. $this->setSql($statementSql)->bindValues($statementParams);
  55. parent::execute();
  56. }
  57. $this->setSql($lastStatementSql)->bindValues($lastStatementParams);
  58. $result = parent::queryInternal($method, $fetchMode);
  59. $this->setSql($sql)->bindValues($params);
  60. return $result;
  61. }
  62. /**
  63. * Splits the specified SQL code into individual SQL statements and returns them
  64. * or `false` if there's a single statement.
  65. * @param string $sql
  66. * @param array $params
  67. * @return string[]|false
  68. */
  69. private function splitStatements($sql, $params)
  70. {
  71. $semicolonIndex = strpos($sql, ';');
  72. if ($semicolonIndex === false || $semicolonIndex === StringHelper::byteLength($sql) - 1) {
  73. return false;
  74. }
  75. $tokenizer = new SqlTokenizer($sql);
  76. $codeToken = $tokenizer->tokenize();
  77. if (count($codeToken->getChildren()) === 1) {
  78. return false;
  79. }
  80. $statements = [];
  81. foreach ($codeToken->getChildren() as $statement) {
  82. $statements[] = [$statement->getSql(), $this->extractUsedParams($statement, $params)];
  83. }
  84. return $statements;
  85. }
  86. /**
  87. * Returns named bindings used in the specified statement token.
  88. * @param SqlToken $statement
  89. * @param array $params
  90. * @return array
  91. */
  92. private function extractUsedParams(SqlToken $statement, $params)
  93. {
  94. preg_match_all('/(?P<placeholder>[:][a-zA-Z0-9_]+)/', $statement->getSql(), $matches, PREG_SET_ORDER);
  95. $result = [];
  96. foreach ($matches as $match) {
  97. $phName = ltrim($match['placeholder'], ':');
  98. if (isset($params[$phName])) {
  99. $result[$phName] = $params[$phName];
  100. } elseif (isset($params[':' . $phName])) {
  101. $result[':' . $phName] = $params[':' . $phName];
  102. }
  103. }
  104. return $result;
  105. }
  106. }