GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( c341ca...96b029 )
by Brendan
12:53 queued 05:42
created

MysqlDump::setAddIndexForForeignKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Graze\Morphism\Parse;
3
4
use GlobIterator;
5
use RuntimeException;
6
7
/**
8
 * Represents a dump of one or more databases.
9
 */
10
class MysqlDump
11
{
12
    /**
13
     * @var CreateDatabase[]
14
     *
15
     * indexed by (string) database name; when enumerated, reflects the order
16
     * in which the databases declarations were parsed.
17
     */
18
    public $databases = [];
19
20
    /** @var CreateDatabase */
21
    private $database = null;
22
    /** @var string */
23
    private $defaultDatabaseName = '';
24
    /** @var string */
25
    private $defaultEngine = 'InnoDB';
26
    /** @var CollationInfo */
27
    private $defaultCollation = null;
28
    /** @var bool */
29
    private $addIndexForForeignKey = true;
30
31
    /**
32
     * Constructor
33
     */
34
    public function __construct()
35
    {
36
        $this->defaultCollation = new CollationInfo();
37
    }
38
39
    /**
40
     * Parses one or more MySQL dump files from the specified paths.
41
     *
42
     * Each path may refer either to a file, or to a directory from which each
43
     * contained file is parsed. There is no recursion into sub-directories.
44
     *
45
     * @param string[]    $paths            files or directories to parse
46
     * @param string|null $defaultEngine    default database engine to use (e.g. InnoDB)
47
     * @param string|null $defaultCollation default collation to use (e.g. utf8)
48
     * @param string|null $defaultDatabaseName database name to use if unspecified in stream
49
     * @return MysqlDump
50
     */
51
    public static function parseFromPaths(array $paths, $defaultEngine = null, $defaultCollation = null, $defaultDatabaseName = null)
52
    {
53
        $dump = new self;
54
        if (!is_null($defaultEngine)) {
55
            $dump->setDefaultEngine($defaultEngine);
56
        }
57
        if (!is_null($defaultCollation)) {
58
            $dump->setDefaultCollation(new CollationInfo($defaultCollation));
59
        }
60
        if (!is_null($defaultDatabaseName)) {
61
            $dump->setDefaultDatabase($defaultDatabaseName);
62
        }
63
64
        $files = [];
65
        foreach ($paths as $path) {
66
            if (is_dir($path)) {
67
                foreach (new GlobIterator("$path/*.sql") as $fileInfo) {
68
                    $files[] = $fileInfo->getPathname();
69
                }
70
            } else {
71
                $files[] = $path;
72
            }
73
        }
74
75
        foreach ($files as $file) {
76
            $stream = TokenStream::newFromFile($file);
77
            try {
78
                $dump->parse($stream);
79
            } catch (RuntimeException $e) {
80
                $message = $stream->contextualise($e->getMessage());
81
                throw new RuntimeException($message);
82
            }
83
        }
84
85
        return $dump;
86
    }
87
88
    /**
89
     * Sets the name to use for the database if none is specified in the stream.
90
     *
91
     * @param string $databaseName
92
     */
93
    public function setDefaultDatabase($databaseName)
94
    {
95
        $this->defaultDatabaseName = $databaseName;
96
    }
97
98
    /**
99
     * Sets the default storage engine to assume when the ENGINE= option is
100
     * not specified in a CREATE TABLE.
101
     *
102
     * @param string $engine  name of storage engine, e.g. 'InnoDB'
103
     */
104
    public function setDefaultEngine($engine)
105
    {
106
        $this->defaultEngine = $engine;
107
    }
108
109
    /**
110
     * Sets whether to add an index for each foreign key if one isn't defined, this is the default behaviour of MySQL.
111
     *
112
     * @param bool $addIndexForForeignKey
113
     */
114
    public function setAddIndexForForeignKey($addIndexForForeignKey)
115
    {
116
        $this->addIndexForForeignKey = $addIndexForForeignKey;
117
    }
118
119
    /**
120
     * Sets the default collation to assume when no collation or charset
121
     * is specified in CREATE DATABASE or CREATE TABLE.
122
     *
123
     * @param CollationInfo $collation
124
     */
125
    public function setDefaultCollation(CollationInfo $collation)
126
    {
127
        $this->defaultCollation = clone $collation;
128
    }
129
130
    /**
131
     * Parses a sequence of CREATE DATABASE and CREATE TABLE clauses from $stream.
132
     *
133
     * All other SQL statements and comments are skipped. After a CREATE DATABASE,
134
     * any subsequent CREATE TABLEs are assumed to belong to that database. Any
135
     * CREATE TABLEs encountered before the first CREATE DATABASE are assigned to
136
     * an anonymous dummy database named ''.
137
     *
138
     * @param TokenStream $stream
139
     * @param array $flags
140
     */
141
    public function parse(TokenStream $stream, array $flags = [])
142
    {
143
        $flags += [
144
            'matchTables' => [
145
                'include' => '',
146
                'exclude' => '',
147
            ]
148
        ];
149
150
        while (true) {
151
            if ($stream->peek('CREATE DATABASE')) {
152
                $this->database = new CreateDatabase($this->defaultCollation);
153
                $this->database->parse($stream);
154
                $stream->expect(Token::SYMBOL, ';');
155
156
                $this->databases[$this->database->name] = $this->database;
157
            } elseif ($stream->peek('CREATE TABLE')) {
158
                if (is_null($this->database)) {
159
                    $name = $this->defaultDatabaseName;
160
                    $this->database = new CreateDatabase($this->defaultCollation);
161
                    $this->database->name = $name;
162
                    $this->databases[$name] = $this->database;
163
                }
164
                $table = new CreateTable($this->database->getCollation());
165
                $table->setDefaultEngine($this->defaultEngine);
166
                $table->setAddIndexForForeignKey($this->addIndexForForeignKey);
167
                $table->parse($stream);
168
                $stream->expect(Token::SYMBOL, ';');
169
170
                $includeTablesRegex = $flags['matchTables']['include'];
171
                $excludeTablesRegex = $flags['matchTables']['exclude'];
172
                if (($includeTablesRegex == '' || preg_match($includeTablesRegex, $table->getName())) &&
173
                    ($excludeTablesRegex == '' || !preg_match($excludeTablesRegex, $table->getName()))
174
                ) {
175
                    $this->database->addTable($table);
176
                }
177
            } elseif (!$this->skipQuery($stream)) {
178
                break;
179
            }
180
        }
181
    }
182
183
    /**
184
     * Skip any statements which aren't create database/table statements.
185
     *
186
     * @param TokenStream $stream
187
     * @return bool
188
     */
189
    private function skipQuery(TokenStream $stream)
190
    {
191
        while (true) {
192
            $token = $stream->nextToken();
193
            if ($token->isEof()) {
194
                return false;
195
            }
196
            if ($token->eq(Token::SYMBOL, ';')) {
197
                return true;
198
            }
199
        }
200
201
        // Shoud never get here, but treat it as EOF
202
        return false;
203
    }
204
205
    /**
206
     * Returns an array of SQL DDL statements for creating the database schema.
207
     *
208
     * @return string[]
209
     */
210
    public function getDDL()
211
    {
212
        $ddl = [];
213
214
        foreach ($this->databases as $database) {
215
            if ($database->name !== '') {
216
                $ddl = array_merge($ddl, $database->getDDL());
217
                $ddl[] = "USE " . Token::escapeIdentifier($database->name);
218
            }
219
            foreach ($database->tables as $table) {
220
                $ddl = array_merge($ddl, $table->getDDL());
221
            }
222
        }
223
224
        return $ddl;
225
    }
226
227
    /**
228
     * Returns an array of SQL DDL statements for transforming the database
229
     * schema into the one represented by $that.
230
     *
231
     * $flags           |
232
     * :----------------|
233
     * 'createDatabase' | (bool) include 'CREATE DATABASE' statements and dependent CREATE TABLEs [default: true]
234
     * 'dropDatabase'   | (bool) include 'DROP DATABASE' statements [default: true]
235
     * 'createTable'    | (bool) include 'CREATE TABLE' statements [default: true]
236
     * 'dropTable'      | (bool) include 'DROP TABLE' statements [default: true]
237
     * 'alterEngine'    | (bool) include 'ALTER TABLE ... ENGINE=' [default: true]
238
     * 'matchTables'    | [$database => ['include' => $regex, 'exclude' => $regex], ...] tables to include / exclude
239
     *
240
     * @param MysqlDump $that
241
     * @param bool[] $flags controls what to include in the generated DDL
242
     * @return string[]
243
     */
244
    public function diff(MysqlDump $that, array $flags = [])
245
    {
246
        $flags += [
247
            'createDatabase' => true,
248
            'dropDatabase'   => true,
249
            'createTable'    => true,
250
            'dropTable'      => true,
251
            'alterEngine'    => true,
252
            'matchTables'    => [
253
                'include' => '',
254
                'exclude' => '',
255
            ],
256
        ];
257
258
        $thisDatabaseNames = array_keys($this->databases);
259
        $thatDatabaseNames = array_keys($that->databases);
260
261
        $commonDatabaseNames  = array_intersect($thisDatabaseNames, $thatDatabaseNames);
262
        $droppedDatabaseNames = array_diff($thisDatabaseNames, $thatDatabaseNames);
263
        $createdDatabaseNames = array_diff($thatDatabaseNames, $thisDatabaseNames);
264
265
        $diff = [];
266
267
        if ($flags['dropDatabase'] && count($droppedDatabaseNames) > 0) {
268
            foreach ($droppedDatabaseNames as $databaseName) {
269
                $diff[] = "DROP DATABASE IF EXISTS " . Token::escapeIdentifier($databaseName);
270
            }
271
        }
272
273
        if ($flags['createDatabase']) {
274
            foreach ($createdDatabaseNames as $databaseName) {
275
                $matchTables = $flags['matchTables'][$databaseName];
276
                $includeTablesRegex = $matchTables['include'];
277
                $excludeTablesRegex = $matchTables['exclude'];
278
                $thatDatabase = $that->databases[$databaseName];
279
                $diff[] = $thatDatabase->getDDL();
280
                $diff[] = "USE " . Token::escapeIdentifier($databaseName);
281
                foreach ($thatDatabase->tables as $table) {
282
                    if (($includeTablesRegex == '' || preg_match($includeTablesRegex, $table->getName())) &&
283
                        ($excludeTablesRegex == '' || !preg_match($excludeTablesRegex, $table->getName()))
284
                    ) {
285
                        $diff[] = $table->getDDL();
286
                    }
287
                }
288
            }
289
        }
290
291
        foreach ($commonDatabaseNames as $databaseName) {
292
            $matchTables = $flags['matchTables'][$databaseName];
293
            $thisDatabase = $this->databases[$databaseName];
294
            $thatDatabase = $that->databases[$databaseName];
295
            $databaseDiff = $thisDatabase->diff($thatDatabase, [
296
                'createTable' => $flags['createTable'],
297
                'dropTable'   => $flags['dropTable'],
298
                'alterEngine' => $flags['alterEngine'],
299
                'matchTables' => $matchTables,
300
            ]);
301
302
            if ($databaseDiff !== '') {
303
                if ($databaseName !== $this->defaultDatabaseName) {
304
                    $diff[] = "USE " . Token::escapeIdentifier($databaseName);
305
                }
306
                $diff = array_merge($diff, $databaseDiff);
307
            }
308
        }
309
310
        return $diff;
311
    }
312
}
313