/vendor/october/rain/src/Database/Dongle.php

https://gitlab.com/gideonmarked/PLCPortal · PHP · 253 lines · 138 code · 35 blank · 80 comment · 31 complexity · 400ba81800ae4cc16d11a5ee7f2b966e MD5 · raw file

  1. <?php namespace October\Rain\Database;
  2. use Exception;
  3. /**
  4. * Database driver dongle
  5. *
  6. * This class uses regex to convert MySQL to various other drivers.
  7. */
  8. class Dongle
  9. {
  10. /**
  11. * @var DB Database helper object
  12. */
  13. protected $db;
  14. /**
  15. * @var string Driver to convert to: mysql, sqlite, pgsql, sqlsrv, postgis.
  16. */
  17. protected $driver;
  18. /**
  19. * @var bool Used to determine whether strict mode has been disabled.
  20. */
  21. protected $strictModeDisabled;
  22. /**
  23. * Constructor.
  24. */
  25. public function __construct($driver = 'mysql', $db = null)
  26. {
  27. $this->db = $db;
  28. $this->driver = $driver;
  29. }
  30. /**
  31. * @deprecated use App::hasDatabase()
  32. * Remove this method if year >= 2017
  33. */
  34. public function hasDatabase()
  35. {
  36. return \App::hasDatabase();
  37. }
  38. /**
  39. * Transforms and executes a raw SQL statement
  40. * @param string $sql
  41. * @return mixed
  42. */
  43. public function raw($sql)
  44. {
  45. return $this->db->raw($this->parse($sql));
  46. }
  47. /**
  48. * Transforms an SQL statement to match the active driver.
  49. * @param string $sql
  50. * @return string
  51. */
  52. public function parse($sql)
  53. {
  54. $sql = $this->parseGroupConcat($sql);
  55. $sql = $this->parseConcat($sql);
  56. $sql = $this->parseIfNull($sql);
  57. $sql = $this->parseBooleanExpression($sql);
  58. return $sql;
  59. }
  60. /**
  61. * Transforms GROUP_CONCAT statement.
  62. * @param string $sql
  63. * @return string
  64. */
  65. public function parseGroupConcat($sql)
  66. {
  67. $result = preg_replace_callback('/group_concat\((.+)\)/i', function($matches){
  68. if (!isset($matches[1])) {
  69. return $matches[0];
  70. }
  71. switch ($this->driver) {
  72. default:
  73. case 'mysql':
  74. return $matches[0];
  75. case 'pgsql':
  76. case 'postgis':
  77. case 'sqlite':
  78. case 'sqlsrv':
  79. return str_ireplace(' separator ', ', ', $matches[0]);
  80. }
  81. }, $sql);
  82. if ($this->driver == 'pgsql' || $this->driver == 'postgis') {
  83. $result = preg_replace("/\\(([]a-zA-Z\\-\\_\\.]+)\\,/i", "($1::VARCHAR,", $result);
  84. $result = str_ireplace('group_concat(', 'string_agg(', $result);
  85. }
  86. /*
  87. * Requires https://groupconcat.codeplex.com/
  88. */
  89. if ($this->driver == 'sqlsrv') {
  90. $result = str_ireplace('group_concat(', 'dbo.GROUP_CONCAT_D(', $result);
  91. }
  92. return $result;
  93. }
  94. /**
  95. * Transforms CONCAT statement.
  96. * @param string $sql
  97. * @return string
  98. */
  99. public function parseConcat($sql)
  100. {
  101. return preg_replace_callback('/(?:group_)?concat\((.+)\)(?R)?/i', function($matches){
  102. if (!isset($matches[1])) {
  103. return $matches[0];
  104. }
  105. // This is a group_concat() so ignore it
  106. if (strpos($matches[0], 'group_') === 0) {
  107. return $matches[0];
  108. }
  109. $concatFields = array_map('trim', explode(',', $matches[1]));
  110. switch ($this->driver) {
  111. default:
  112. case 'mysql':
  113. return $matches[0];
  114. case 'pgsql':
  115. case 'postgis':
  116. case 'sqlite':
  117. return implode(' || ', $concatFields);
  118. }
  119. }, $sql);
  120. }
  121. /**
  122. * Transforms IFNULL statement.
  123. * @param string $sql
  124. * @return string
  125. */
  126. public function parseIfNull($sql)
  127. {
  128. if ($this->driver == 'pgsql' || $this->driver == 'postgis') {
  129. return str_ireplace('ifnull(', 'coalesce(', $sql);
  130. }
  131. if ($this->driver == 'sqlsrv') {
  132. return str_ireplace('ifnull(', 'isnull(', $sql);
  133. }
  134. return $sql;
  135. }
  136. /**
  137. * Transforms true|false expressions in a statement.
  138. * @param string $sql
  139. * @return string
  140. */
  141. public function parseBooleanExpression($sql)
  142. {
  143. if ($this->driver != 'sqlite') {
  144. return $sql;
  145. }
  146. return preg_replace_callback('/(\w+)\s*(=|<>)\s*(true|false)($|\s)/i', function ($matches) {
  147. array_shift($matches);
  148. $space = array_pop($matches);
  149. $matches[2] = $matches[2] == 'true' ? 1 : 0;
  150. return implode(' ', $matches) . $space;
  151. }, $sql);
  152. }
  153. /**
  154. * Some drivers require same-type comparisons.
  155. * @param string $sql
  156. * @return string
  157. */
  158. public function cast($sql, $asType = 'INTEGER')
  159. {
  160. if ($this->driver != 'pgsql' && $this->driver != 'postgis') {
  161. return $sql;
  162. }
  163. return 'CAST('.$sql.' AS '.$asType.')';
  164. }
  165. /**
  166. * Alters a table's TIMESTAMP field(s) to be nullable and converts existing values.
  167. *
  168. * This is needed to transition from older Laravel code that set DEFAULT 0, which is an
  169. * invalid date in newer MySQL versions where NO_ZERO_DATE is included in strict mode.
  170. *
  171. * @param string $table
  172. * @param string|array $columns Column name(s). Defaults to ['created_at', 'updated_at']
  173. */
  174. public function convertTimestamps($table, $columns = null)
  175. {
  176. if ($this->driver != 'mysql') {
  177. return;
  178. }
  179. if (!is_array($columns)) {
  180. $columns = is_null($columns) ? ['created_at', 'updated_at'] : [$columns];
  181. }
  182. $prefixedTable = $this->getTablePrefix() . $table;
  183. foreach ($columns as $column) {
  184. $this->db->statement("ALTER TABLE {$prefixedTable} MODIFY `{$column}` TIMESTAMP NULL DEFAULT NULL");
  185. $this->db->update("UPDATE {$prefixedTable} SET {$column} = null WHERE {$column} = 0");
  186. }
  187. }
  188. /**
  189. * Used to disable strict mode during migrations
  190. */
  191. public function disableStrictMode()
  192. {
  193. if ($this->driver != 'mysql') {
  194. return;
  195. }
  196. if ($this->strictModeDisabled || $this->db->getConfig('strict') === false) {
  197. return;
  198. }
  199. $this->db->statement("SET @@SQL_MODE=''");
  200. $this->strictModeDisabled = true;
  201. }
  202. /**
  203. * Returns the driver name as a string, eg: pgsql
  204. * @return string
  205. */
  206. public function getDriver()
  207. {
  208. return $this->driver;
  209. }
  210. /**
  211. * Get the table prefix.
  212. * @return string
  213. */
  214. public function getTablePrefix()
  215. {
  216. return $this->db->getTablePrefix();
  217. }
  218. }