16 protected $_db = null;
17 protected $_currentPath = null; //!< Path with current files (keep out of version control; empty = path + "current/").
19 protected $_current = null;
21 protected function flatten($sql){
22 return preg_replace('/\\s+/
','',$sql);
29 protected function dir($path){
31 foreach((new \GlobIterator($path . $this->mask,\GlobIterator::NEW_CURRENT_AND_KEY)) as $filename => $info)
32 $dir[$filename] = file_get_contents($path . $filename);
40 public function diff(){
41 $diff = array_fill_keys(array_keys($this->current),self::ACTION_UNDO);
43 foreach($this->dir($this->path) as $filename => $sql) $diff[$filename] = array_key_exists($filename,$this->current)
44 ? ($redo || ($redo = $this->flatten($this->current[$filename]) != $this->flatten($sql)) ? self::ACTION_REDO : null) //redo all after first redo
47 return array_filter($diff);
50 protected function split($sql){
52 $delimiter = '/(?:$|
' . str_replace('\\[action\\]
','(
' . implode('|
',$this->constants('ACTION_
')) . ')
',preg_quote($this->delimiter,'/
')) . ')/
';
53 $action = self::ACTION_EXEC;
54 while(preg_match($delimiter,$sql,$match,PREG_OFFSET_CAPTURE)) switch(count($match)){
56 $parts[$action] = $sql;
59 $parts[$action] = substr($sql,0,$match[0][1]);
60 $sql = substr($sql,$match[1][1] + strlen($action = $match[1][0]));
70 public function execute(&$notes = null){
71 $log = $this->component('log
');
72 if($diff = $this->diff()){
76 foreach(array_reverse($diff) as $filename => $action) switch($action){
77 case self::ACTION_UNDO:
78 case self::ACTION_REDO:
79 $log->info("Migrate: undo $filename",__FILE__,__LINE__);
80 if(trim($sql = $this->split($this->current[$filename])[$action] ?? null)) $this->_db->execute($sql);
83 foreach($diff as $filename => $action) switch($action){
84 case self::ACTION_REDO:
85 case self::ACTION_EXEC:
86 $parts = $this->split(file_get_contents($this->path . $filename));
87 $log->info("Migrate: $action $filename",__FILE__,__LINE__,array_key_exists(self::ACTION_NOTE,$parts) ? ['notes
' => $notes[$filename] = $parts[self::ACTION_NOTE]] : null);
88 if(trim($sql = $parts[$action] ?? null)) $this->_db->execute($sql);
91 $this->current = $this->dir($this->path);
95 $this->_db->rollBack();
102 protected function getCurrent(){
103 if($this->_current === null) $this->_current = $this->dir($this->currentPath);
104 return $this->_current;
107 protected function setCurrent($value){
108 foreach($this->dir($this->current) as $filename => $sql) unlink($this->currentPath . $filename);
109 foreach(($this->_current = $value) as $filename => $sql) file_put_contents($this->currentPath . $filename,$sql);
112 protected function getCurrentPath(){
113 return $this->_currentPath ?: $this->path . 'current/
';