/fixture.class.php

https://github.com/iaindooley/Murphy · PHP · 226 lines · 184 code · 39 blank · 3 comment · 28 complexity · df6269efdc76b9863817403ff86b16de MD5 · raw file

  1. <?php
  2. namespace Murphy;
  3. use Exception,Closure,Args;
  4. class Fixture
  5. {
  6. private $callbacks;
  7. private $data;
  8. private $link;
  9. private static $instance = NULL;
  10. private function __construct()
  11. {
  12. $this->callbacks = array();
  13. $this->data = array();
  14. }
  15. private static function instance()
  16. {
  17. if(self::$instance === NULL)
  18. self::$instance = new Fixture();
  19. return self::$instance;
  20. }
  21. public static function add(Closure $callback)
  22. {
  23. self::instance()->callbacks[] = $callback;
  24. }
  25. public function execute(Closure $db_connect = NULL)
  26. {
  27. $databases = array();
  28. foreach($this->data as $key => $d)
  29. {
  30. if(isset($d['tables']))
  31. {
  32. if(!isset($d['database']))
  33. throw new InvalidFixtureFormatException('You have included a @tables directive for '.$d['tables'].' but no @database directive');
  34. if(!isset($databases[$d['database']]))
  35. $databases[$d['database']] = array();
  36. $databases[$d['database']] = array_merge($databases[$d['database']],$d['tables']);
  37. }
  38. }
  39. $aliases = array();
  40. if(count($databases))
  41. {
  42. if(!$dbconfig_path = Args::get('dbconfig',Args::argv))
  43. {
  44. echo 'You need to include dbconfig in the command line arguments'.PHP_EOL;
  45. exit(1);
  46. }
  47. if(!$dbconfig = include($dbconfig_path))
  48. {
  49. echo 'You need to include dbconfig in the command line arguments'.PHP_EOL;
  50. exit(1);
  51. }
  52. foreach($databases as $database => $tables)
  53. {
  54. $this->link = mysqli_connect($dbconfig['db_host'],
  55. $dbconfig['db_user'],
  56. $dbconfig['db_pass']);
  57. $this->link->select_db($database);
  58. $tables = array_unique($tables);
  59. $create_table_statements = array();
  60. foreach($tables as $table)
  61. {
  62. if(!$query = $this->link->query('SHOW CREATE TABLE `'.$table.'`'))
  63. throw new Exception(mysqli_error($this->link));
  64. if(!$user = $this->link->query("SELECT USER()"))
  65. throw new Exception(mysqli_error($this->link));
  66. $user_row = $user->fetch_assoc();
  67. $user_data = explode("@", $user_row["USER()"]);
  68. $row = $query->fetch_assoc();
  69. $create_table_statements[] = getCreateQuery($row,
  70. $user_data[0],
  71. $user_data[1]);
  72. }
  73. $alias = md5($database);
  74. $aliases[$database] = array($dbconfig['db_host'],
  75. $dbconfig['db_user'],
  76. $dbconfig['db_pass'],
  77. md5($database));
  78. $this->link->query('DROP DATABASE IF EXISTS `'.$alias.'`') or die(mysqli_error($this->link));
  79. $this->link->query('CREATE DATABASE `'.$alias.'`') or die(mysqli_error($this->link));
  80. $this->link->select_db($alias);
  81. foreach($create_table_statements as $stmt)
  82. $this->link->query($stmt) or die(mysqli_error($this->link));
  83. }
  84. if(!$db_connect instanceof Closure)
  85. throw new DbFixtureConnectionException('You have included database fixtures without a callback to pass connection details to');
  86. }
  87. foreach($this->data as $key => $d)
  88. {
  89. if(isset($aliases[$d['database']]))
  90. $this->link->select_db($aliases[$d['database']][3]);
  91. $args = array();
  92. foreach($d['rows'] as $row)
  93. {
  94. foreach($row as $index => $line)
  95. $args[$d['header'][$index]] = $line;
  96. self::instance()->callbacks[$key]($args);
  97. }
  98. }
  99. if($db_connect instanceof Closure)
  100. $db_connect($aliases);
  101. self::$instance = NULL;
  102. }
  103. public function also($database, $file)
  104. {
  105. $this->extractFixtureDataFromFile($database, $file);
  106. return $this;
  107. }
  108. public static function load($database, $file)
  109. {
  110. self::instance()->extractFixtureDataFromFile($database, $file);
  111. return self::instance();
  112. }
  113. private function extractFixtureDataFromFile($database, $path)
  114. {
  115. require($path);
  116. $contents = file($path,FILE_IGNORE_NEW_LINES);
  117. $docblocks = array();
  118. $cur_docblock = NULL;
  119. $previous_docblock = NULL;
  120. foreach($contents as $cont)
  121. {
  122. if(strpos($cont,'/**') !== FALSE)
  123. $cur_docblock = array();
  124. if(strpos($cont,'*/') !== FALSE)
  125. {
  126. $previous_docblock = $cur_docblock;
  127. $cur_docblock = NULL;
  128. }
  129. if($cur_docblock !== NULL)
  130. $cur_docblock[] = $cont;
  131. else if(preg_match('/Murphy\\\\Fixture::add\(/U',$cont,$matches))
  132. $docblocks[] = $previous_docblock;
  133. }
  134. $offset = count($this->data);
  135. foreach($docblocks as $key => $block)
  136. {
  137. $cur_index = $key+$offset;
  138. $this->data[$key+$offset] = array('rows' => array());
  139. foreach($block as $b)
  140. {
  141. if(strpos(trim($b),'/*') !== 0)
  142. {
  143. $b = trim(str_replace('*','',$b));
  144. if(strpos($b,'@database ') === 0){
  145. //$database = trim(str_replace('@database','',$b));
  146. }
  147. else if(strpos($b,'@tables ') === 0)
  148. $this->data[$cur_index]['tables'] = explode(',',trim(str_replace('@tables','',$b)));
  149. else if(!isset($this->data[$cur_index]['header']))
  150. $this->data[$cur_index]['header'] = array_map('trim',explode('|',$b));
  151. else
  152. $this->data[$cur_index]['rows'][] = array_map('trim',explode('|',$b));
  153. }
  154. }
  155. if(!$database)
  156. throw new InvalidFixtureFormatException('You must specify the @database directive for fixture: '.$fixture_name.' in: '.$path);
  157. $this->data[$cur_index]['database'] = $database;
  158. }
  159. }
  160. }
  161. class DuplicateFixtureException extends Exception{}
  162. class InvalidFixtureFormatException extends Exception{}
  163. class DbFixtureConnectionException extends Exception{}
  164. function getCreateQuery($row, $user, $domain) {
  165. if(isset($row['Create Table'])) {
  166. return $row['Create Table'];
  167. }
  168. else {
  169. $query = $row['Create View'];
  170. if($user) {
  171. return preg_replace(
  172. "/DEFINER=`(.*?)`@`(.*?)`/",
  173. "",
  174. $query
  175. );
  176. }
  177. else {
  178. // Drop the ALGORITHM and DEFINER values from the
  179. // CREATE query
  180. return preg_replace(
  181. "/CREATE (.*?) VIEW/",
  182. "CREATE VIEW",
  183. $query
  184. );
  185. }
  186. }
  187. }