ChangeLogger::getChanges()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace yentu;
4
5
use clearice\io\Io;
6
use yentu\manipulators\AbstractDatabaseManipulator;
7
8
class ChangeLogger
9
{
10
11
    private AbstractDatabaseManipulator $driver;
12
    private string $version;
13
    private $migration;
14
    private $session;
15
    private $changes;
16
    private $expectedOperations = 1;
17
    private $operations;
18
    private static $defaultSchema = '';
19
    private $skippedItemTypes = array();
20
    private $allowedItemTypes = array();
21
    private $dumpQueriesOnly;
22
    private $dryRun;
23
    private $skipOnErrors = false;
24
    private $migrations;
25
    private $io;
26
27
    public function skip($itemType)
28
    {
29
        $this->skippedItemTypes[] = $itemType;
30
    }
31
32
    public function setDumpQueriesOnly($dumpQueriesOnly)
33
    {
34
        if ($dumpQueriesOnly === true) {
35
            $this->io->setOutputLevel(Io::OUTPUT_LEVEL_0);
36
        }
37
        $this->dumpQueriesOnly = $dumpQueriesOnly;
38
    }
39
40
    public function setDryRun($dryRun)
41
    {
42
        $this->dryRun = $dryRun;
43
    }
44
45
    public function allowOnly($itemType)
46
    {
47
        $this->allowedItemTypes[] = $itemType;
48
    }
49
50
    private function __construct(AbstractDatabaseManipulator $driver, Migrations $migrations, Io $io)
51
    {
52
        $this->session = sha1(rand() . time());
53
        $this->driver = $driver;
54
        $this->driver->createHistory();
55
        $this->migrations = $migrations;
56
        $this->io = $io;
57
    }
58
59
    public static function wrap(AbstractDatabaseManipulator $item, Migrations $migrations, Io $io) : ChangeLogger
60
    {
61
        return new ChangeLogger($item, $migrations, $io);
62
    }
63
64
    public function setVersion(string $version) : void
65
    {
66
        $this->version = $version;
67
    }
68
69
    public function setMigration($migration)
70
    {
71
        $this->migration = $migration;
72
    }
73
74
    private function performOperation($method, $matches, $arguments)
75
    {
76
        try {
77
            $return = $this->driver->$method($arguments[0]);
78
            $this->migrations->announce($matches['command'], $matches['item_type'], $arguments[0]);
79
80
            $this->driver->setDumpQuery(false);
81
82
            $this->io->pushOutputLevel(Io::OUTPUT_LEVEL_0);
83
            $this->driver->query(
84
                'INSERT INTO yentu_history(session, version, method, arguments, migration, default_schema) VALUES (?,?,?,?,?,?)', array(
85
                $this->session,
86
                $this->version,
87
                $method,
88
                json_encode($arguments),
89
                $this->migration,
90
                self::$defaultSchema
91
                )
92
            );
93
            $this->io->popOutputLevel();
94
            $this->changes++;
95
            $this->driver->setDisableQuery(false);
96
        } catch (\yentu\exceptions\DatabaseManipulatorException $e) {
97
            if ($this->skipOnErrors) {
98
                $this->io->output("E");
99
                $this->io->output("rror " . preg_replace("/([a-z])([A-Z])/", "$1 $2", $matches['item_type']) . " '" . $arguments[0]['name'] . "'\n", Io::OUTPUT_LEVEL_2);
100
            } else {
101
                throw $e;
102
            }
103
        }
104
        return $return;
105
    }
106
107
    /**
108
     * This magic method records and routes calls to the appropriate objects.
109
     * 
110
     * @method sql(string $query, string $rollback)
111
     * @method quoteIdentifier(string $identifier)
112
     * @method query(string $query)
113
     * 
114
     * @param string $method
115
     * @param array $arguments
116
     * @return mixed
117
     */
118
    public function __call(string $method, array $arguments): mixed
119
    {
120
        $return = null;
121
        if (preg_match("/^(?<command>add|drop|change|execute|reverse|insert)(?<item_type>[a-zA-Z]+)/", $method, $matches)) {
122
            $this->driver->setDumpQuery($this->dumpQueriesOnly);
123
            $this->driver->setDisableQuery($this->dryRun);
124
125
            if (
126
                array_search($matches['item_type'], $this->skippedItemTypes) !== false ||
127
                (array_search($matches['item_type'], $this->allowedItemTypes) === false && count($this->allowedItemTypes) > 0)
128
            ) {
129
                $this->io->output("S");
130
                $this->io->output("kipping " . preg_replace("/([a-z])([A-Z])/", "$1 $2", $matches['item_type']) . " '" 
131
                    . (isset($arguments[0]['name']) ? $arguments[0]['name'] : null) . "'\n", Io::OUTPUT_LEVEL_2);
132
            } else {
133
                $return = $this->performOperation($method, $matches, $arguments);
134
            }
135
136
            $this->operations++;
137
            $this->outputProgress();
138
        } else if (preg_match("/^does([A-Za-z]+)/", $method)) {
139
            $invokable = new \ReflectionMethod($this->driver->getAssertor(), $method);
140
            return $invokable->invokeArgs($this->driver->getAssertor(), $arguments);
141
        } else {
142
            $invokable = new \ReflectionMethod($this->driver, $method);
143
            return $invokable->invokeArgs($this->driver, $arguments);
144
        }
145
146
        return $return;
147
    }
148
149
    public function outputProgress()
150
    {
151
        if ($this->expectedOperations > 0) {
152
            if ($this->operations % 74 === 0) {
153
                $this->io->output(sprintf("%4d%%\n", $this->operations / $this->expectedOperations * 100));
154
            } else {
155
                $this->io->output(sprintf("%4d%%\n", $this->operations / $this->expectedOperations * 100), Io::OUTPUT_LEVEL_2);
156
            }
157
        }
158
    }
159
160
    public function getChanges()
161
    {
162
        return $this->changes;
163
    }
164
165
    public function resetOperations()
166
    {
167
        $operations = $this->operations;
168
        $this->operations = 0;
169
        return $operations;
170
    }
171
172
    public function __clone()
173
    {
174
        $this->driver = clone $this->driver;
175
    }
176
177
    public function setExpectedOperations($expectedOperations)
178
    {
179
        $this->expectedOperations = $expectedOperations;
180
    }
181
182
    public function setSkipOnErrors($skipOnErrors)
183
    {
184
        $this->skipOnErrors = $skipOnErrors;
185
    }
186
187
    public function getDefaultSchema()
188
    {
189
        return $this->driver->getDefaultSchema();
190
    }
191
192
}
193