PageRenderTime 38ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/DbPatch/Command/Patch/Abstract.php

http://github.com/dbpatch/DbPatch
PHP | 479 lines | 217 code | 56 blank | 206 comment | 34 complexity | bbd19e030c2fe94d19761773fa2060da MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * DbPatch
  4. *
  5. * Copyright (c) 2011, Sandy Pleyte.
  6. * Copyright (c) 2010-2011, Martijn De Letter.
  7. *
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * * Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * * Neither the name of the authors nor the names of his
  23. * contributors may be used to endorse or promote products derived
  24. * from this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  27. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  28. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  29. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  30. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  31. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  32. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  33. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  34. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  35. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  36. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  37. * POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. * @package DbPatch
  40. * @subpackage Command_Patch
  41. * @author Sandy Pleyte
  42. * @author Martijn De Letter
  43. * @copyright 2011 Sandy Pleyte
  44. * @copyright 2010-2011 Martijn De Letter
  45. * @license http://www.opensource.org/licenses/MIT MIT License
  46. * @link http://www.github.com/dbpatch/DbPatch
  47. * @since File available since Release 1.0.0
  48. */
  49. /**
  50. * Abstract Patch file
  51. *
  52. * @package DbPatch
  53. * @subpackage Command_Patch
  54. * @author Sandy Pleyte
  55. * @author Martijn De Letter
  56. * @author Rudi de Vries
  57. * @copyright 2011 Sandy Pleyte
  58. * @copyright 2010-2011 Martijn De Letter
  59. * @copyright 2013 Rudi de Vries
  60. * @license http://www.opensource.org/licenses/MIT MIT License
  61. * @link http://www.github.com/dbpatch/DbPatch
  62. * @since File available since Release 1.0.0
  63. */
  64. abstract class DbPatch_Command_Patch_Abstract
  65. {
  66. /**
  67. * @var null|\Zend_Db_Adapter_Abstract
  68. */
  69. protected $db = null;
  70. /**
  71. * @var null|DbPatch_Core_Writer
  72. */
  73. protected $writer = null;
  74. /**
  75. * @var null|DbPatch_Core_Config
  76. */
  77. protected $config = null;
  78. /**
  79. * @var array
  80. */
  81. protected $data = array();
  82. /**
  83. * Creates a new value object
  84. *
  85. * @param array $values the values to fill the value object with.
  86. * If left empty we're creating an empty value object.
  87. * @return void
  88. */
  89. public function __construct(Array $values = null)
  90. {
  91. if (!is_null($values)) {
  92. $this->loadFromArray($values);
  93. }
  94. }
  95. /**
  96. * @abstract
  97. * @return void
  98. */
  99. abstract function apply();
  100. /**
  101. * @abstract
  102. * @return string
  103. */
  104. abstract function getContents();
  105. /**
  106. * @abstract
  107. * @return void
  108. */
  109. abstract function getType();
  110. /**
  111. * Load the values from an array provided.
  112. *
  113. * @throws DbPatch_Exception
  114. * @param array $values the values we're using to set the values in the
  115. * value object
  116. * @return void
  117. */
  118. public function loadFromArray($values)
  119. {
  120. if ($values instanceof Zend_Db_Table_Row_Abstract) {
  121. $values = $values->toArray();
  122. } elseif (is_object($values)) {
  123. $values = (array)$values;
  124. }
  125. if (!is_array($values)) {
  126. throw new DbPatch_Exception('Initial data must be an array or object');
  127. }
  128. foreach ($values as $key => $value) {
  129. $this->$key = $value;
  130. }
  131. }
  132. /**
  133. * Sets the value provided.
  134. *
  135. * @param string $name name of the property
  136. * @param mixed $value the value
  137. * @throws DbPatch_Exception
  138. */
  139. public function __set($name, $value)
  140. {
  141. $name = trim($name);
  142. $method = 'set' . ucfirst($this->to_camel_case($name));
  143. if (method_exists($this, $method)) {
  144. $this->$method($value);
  145. return;
  146. }
  147. $key = $this->from_camel_case($name);
  148. if (array_key_exists($key, $this->data)) {
  149. $this->data[$key] = $value;
  150. return;
  151. }
  152. throw new DbPatch_Exception('[SET] Property ' . $name . ' is not implemented for class ' . get_class($this));
  153. }
  154. /**
  155. * Translates a camel case string into a string with
  156. * underscores (e.g. firstName -&gt; first_name)
  157. *
  158. * @param string $str String in camel case format
  159. * @return string $str Translated into underscore format
  160. */
  161. protected function from_camel_case($str)
  162. {
  163. $str[0] = strtolower($str[0]);
  164. $func = create_function('$c', 'return "_" . strtolower($c[1]);');
  165. return preg_replace_callback('/([A-Z])/', $func, $str);
  166. }
  167. /**
  168. * Translates a string with underscores into camel case
  169. * (e.g. first_name -&gt; firstName)
  170. *
  171. * @param string $str String in underscore format
  172. * @param bool $capitalise_first_char If true, capitalise the first char in $str
  173. * @return string $str translated into camel caps
  174. */
  175. protected function to_camel_case($str, $capitalise_first_char = false)
  176. {
  177. if ($capitalise_first_char) {
  178. $str[0] = strtoupper($str[0]);
  179. }
  180. $func = create_function('$c', 'return strtoupper($c[1]);');
  181. return preg_replace_callback('/_([a-z])/', $func, $str);
  182. }
  183. /**
  184. * Returns the value requested.
  185. *
  186. * @throws DbPatch_Exception
  187. * @param string $name the name of the property
  188. * @return mixed the value
  189. */
  190. public function __get($name)
  191. {
  192. $name = trim($name);
  193. $method = 'get' . ucfirst($this->to_camel_case($name));
  194. if (method_exists($this, $method)) {
  195. return $this->$method();
  196. }
  197. $key = $this->from_camel_case($name);
  198. if (array_key_exists($key, $this->data)) {
  199. return $this->data[$key];
  200. }
  201. throw new DbPatch_Exception('[GET] Property ' . $name . '/' . $key . ' is not implemented for class ' . get_class($this));
  202. }
  203. /**
  204. * Check if a value isset
  205. *
  206. * @throws DbPatch_Exception
  207. * @param string $name Name of the property
  208. * @return bool
  209. */
  210. public function __isset($name)
  211. {
  212. $name = trim($name);
  213. $method = 'get' . ucfirst($this->to_camel_case($name));
  214. if (method_exists($this, $method)) {
  215. $val = $this->$method();
  216. return isset($val);
  217. }
  218. $key = $this->from_camel_case($name);
  219. if (array_key_exists($key, $this->data)) {
  220. return isset($this->data[$key]);
  221. }
  222. throw new DbPatch_Exception('[ISSET] Property ' . $name . ' is not implemented for class ' . get_class($this));
  223. }
  224. /**
  225. * Returns the objects values as an array.
  226. *
  227. * @return array
  228. */
  229. public function toArray()
  230. {
  231. return $this->data;
  232. }
  233. /**
  234. * Returns the field names of the data array.
  235. *
  236. * @return array
  237. */
  238. public function getFields()
  239. {
  240. return array_keys($this->data);
  241. }
  242. /**
  243. * @param \DbPatch_Core_Db $db
  244. * @return DbPatch_Command_Patch_Abstract
  245. */
  246. public function setDb(DbPatch_Core_Db $db)
  247. {
  248. $this->db = $db;
  249. return $this;
  250. }
  251. /**
  252. * @return null|\DbPatch_Core_Db
  253. */
  254. public function getDb()
  255. {
  256. return $this->db;
  257. }
  258. /**
  259. * @param DbPatch_Core_Writer $writer
  260. * @return DbPatch_Command_Patch_Abstract
  261. */
  262. public function setWriter($writer)
  263. {
  264. $this->writer = $writer;
  265. return $this;
  266. }
  267. /**
  268. * @return DbPatch_Core_Writer|null
  269. */
  270. public function getWriter()
  271. {
  272. return $this->writer;
  273. }
  274. /**
  275. * @param DbPatch_Core_Config $config
  276. * @return DbPatch_Command_Patch_Abstract
  277. */
  278. public function setConfig($config)
  279. {
  280. $this->config = $config;
  281. return $this;
  282. }
  283. /**
  284. * @return DbPatch_Core_Config|null
  285. */
  286. public function getConfig()
  287. {
  288. return $this->config;
  289. }
  290. /**
  291. * Extract the first line of comment out of a patch file
  292. *
  293. * @param int $lineNumber Line number where the comment is
  294. * @return string
  295. */
  296. protected function getComment($lineNumber)
  297. {
  298. // Read first line(s)
  299. $lines = array();
  300. $fp = @fopen($this->filename, "r");
  301. if ($fp) {
  302. $counter = 0;
  303. while (!feof($fp) && $counter <= $lineNumber) {
  304. $lines[] = fgets($fp, 4096);
  305. $counter++;
  306. }
  307. fclose($fp);
  308. }
  309. if (count($lines) == 0) {
  310. return '';
  311. }
  312. $line = $lines[$lineNumber];
  313. $comment = '';
  314. $pattern = '/^\s*(\/\/|\/\*|\#|-- )\s?(.*)$/';
  315. if (preg_match($pattern, $line, $matches)) {
  316. $comment = trim(str_replace('*/', '', $matches[2]));
  317. if (substr($comment, 0, 5) == '-----') {
  318. $comment = '';
  319. }
  320. }
  321. return $comment;
  322. }
  323. /**
  324. * Retrieve a file hash
  325. *
  326. * @return string
  327. */
  328. public function getHash()
  329. {
  330. return hash_file('crc32', $this->filename);
  331. }
  332. /**
  333. * Echo contents of the patch
  334. *
  335. * @return void
  336. */
  337. public function show()
  338. {
  339. $fp = @fopen($this->filename, "r");
  340. if ($fp) {
  341. echo PHP_EOL;
  342. while (!feof($fp)) {
  343. $line = fgets($fp, 4096);
  344. echo $line . PHP_EOL;
  345. }
  346. fclose($fp);
  347. echo PHP_EOL;
  348. }
  349. }
  350. /**
  351. * Create empty patch
  352. *
  353. * @param string $filename
  354. * @param string $content
  355. * @return void
  356. */
  357. protected function writeFile($filename, $content)
  358. {
  359. if (!$this->fileExists($filename)) {
  360. $fp = fopen($filename, 'w');
  361. fwrite($fp, $content);
  362. fclose($fp);
  363. $this->getWriter()->success('Created empty patch ' . $filename);
  364. } else {
  365. $this->getWriter()->error('Patch ' . $this->patchNumber . ' already exists!');
  366. }
  367. }
  368. /**
  369. * @param string $filename
  370. * @return bool
  371. */
  372. protected function fileExists($filename)
  373. {
  374. $filename2 = '';
  375. if ($this->getType() == 'PHP') {
  376. $filename2 = str_replace('.php', '.sql', $filename);
  377. } else if ($this->getType() == 'SQL') {
  378. $filename2 = str_replace('.sql', '.php', $filename);
  379. }
  380. return file_exists($filename) || file_exists($filename2);
  381. }
  382. /**
  383. * @param string $patchPrefix
  384. * @param string $extension
  385. * @param int $patchNumberSize
  386. * @return string
  387. */
  388. protected function getPatchFilename($patchPrefix, $extension, $patchNumberSize = 4)
  389. {
  390. $branch = '';
  391. if ($this->branch != 'default') {
  392. $branch .= $this->branch . '-';
  393. }
  394. $filename = $patchPrefix . '-' . $branch . str_pad($this->patchNumber, $patchNumberSize, '0', STR_PAD_LEFT) . '.' . $extension;
  395. return $filename;
  396. }
  397. /**
  398. * @param string $patchDirectory
  399. * @return int
  400. */
  401. protected function getPatchNumberSize($patchDirectory)
  402. {
  403. try {
  404. $iterator = new DirectoryIterator($patchDirectory);
  405. } catch (Exception $e) {
  406. $this->writer->error($e->getMessage());
  407. return 4;
  408. }
  409. $filename = '';
  410. foreach ($iterator as $fileinfo) {
  411. if ($fileinfo->isDot() || substr($fileinfo->getFilename(), 0, 1) == '.') {
  412. continue;
  413. }
  414. $filename = $fileinfo->getFilename();
  415. break;
  416. }
  417. $pattern = '/(\d{3,4})./';
  418. if (preg_match($pattern, $filename, $matches)) {
  419. return strlen($matches[1]);
  420. }
  421. return 4;
  422. }
  423. }