Completed
Push — master ( 1d3657...1c70eb )
by James Ekow Abaka
04:29
created

Descriptor::throwTableExceptions()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.025

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 9
cts 10
cp 0.9
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 6
nop 2
crap 5.025
1
<?php
2
3
namespace ntentan\atiaa;
4
5
/**
6
 * Does the job of describing the schema of the underlying database. This 
7
 * abstract class is usually extended by database platform specific descriptor
8
 * classes which provide the details of the actual database items. The main work
9
 * of this class is to format the description into a common format.
10
 */
11
abstract class Descriptor {
12
13
    /**
14
     * An instance of the database driver used for accessing the database
15
     * system.
16
     * @var \ntentan\atiaa\Driver;
17
     */
18
    protected $driver;
19
    private $cleanDefaults = false;
20
21 11
    public function __construct($driver) {
22 11
        $this->driver = $driver;
23 11
    }
24
25
    /**
26
     * Returns a list of schemata available on the database.
27
     * @return array
28
     */
29
    abstract protected function getSchemata();
30
31
    /**
32
     * Retrieve the names of all the tables in a given schema. 
33
     * The array returned must be a list of structured arrays which have `name`
34
     * and `schema` as keys. The `name` key should represent the name of the table and
35
     * the `schema` key should represent the name of the schema (which is the same
36
     * as the schema which was passed to the function).
37
     * 
38
     * @param string $schema The name of the schema whose tables should be
39
     *     describled.
40
     * @param array<string> An array contianing names of specific tables 
41
     *     who's descriptions should be retrieved. 
42
     * @return array<array>  
43
     */
44
    abstract protected function getTables($schema, $requestedTables, $includeViews);
45
46
    /**
47
     * Retrieve descriptions of all the columns available in a given table as an array.
48
     * The array returned must contain structured arrays with the following keys.
49
     * 
50
     * name
51
     * : The name of the column.
52
     * 
53
     * type
54
     * : The system specific datatype of the column.
55
     * 
56
     * nulls
57
     * : A boolean which is true for columsn which can contain null values
58
     *   and false for columns which can't.
59
     * 
60
     * default
61
     * : The default value of the column. In cases where there is no default
62
     *   this column is set to null.
63
     * 
64
     * length
65
     * : The maximum character lenght of the column.
66
     * 
67
     * @param array $table An array which contains the name of the table as it's
68
     *     `name` key and the schema of the table as it's `schema` key.
69
     * @return array<array<string>>
70
     */
71
    abstract protected function getColumns(&$table);
72
73
    /**
74
     * Retrieve the descriptions of all the views of a given schema in a array.
75
     * The array returned must contain structured arrays with the following keys.
76
     * 
77
     * name
78
     * : The name of the view.
79
     * 
80
     * schema
81
     * : The schema to which the view belongs.
82
     * 
83
     * definition
84
     * : The SQL query which represents the definition of the view.
85
     * 
86
     * @param string $schema The name of the database schema
87
     * @return array<array<string>>
88
     */
89
    abstract protected function getViews(&$schema);
90
91
    /**
92
     * Retrieve the description of a primary key on a given table.
93
     * The description returned must be an array which contains structured
94
     * arrays with the following keys.
95
     * 
96
     * column
97
     * : The name of a column which is part of the primary key
98
     * 
99
     * name
100
     * : The name of the primary key constraint (must be the same throughout
101
     *   all the items returned).
102
     * 
103
     * For primary keys with multiple columns, the array returned would contain
104
     * one entry for each column.
105
     * 
106
     * @param array $table An array which contains the name of the table as it's
107
     *     `name` key and the schema of the table as it's `schema` key.
108
     * @return array<array<string>>
109
     */
110
    abstract protected function getPrimaryKey(&$table);
111
112
    /**
113
     * Retrieve the description of unique keys on a given table.
114
     * The description returned must be an array which contains structured
115
     * arrays with the following keys.
116
     * 
117
     * column
118
     * : The name of a column which is part of a unique key
119
     * 
120
     * name
121
     * : The name of the unique key constraint.
122
     * 
123
     * For unique keys with multiple columns, the value of the `name` key must
124
     * be the same for only the columns in the key. 
125
     * 
126
     * @param array $table An array which contains the name of the table as it's
127
     *     `name` key and the schema of the table as it's `schema` key.
128
     * @return array<array<string>>
129
     */
130
    abstract protected function getUniqueKeys(&$table);
131
132
    /**
133
     * Retrieve the description of foreign keys on a given table.
134
     * The description returned must be an array which contains structured
135
     * arrays with the following keys.
136
     * 
137
     * name
138
     * : The name of the foreign key constraint.
139
     * 
140
     * table
141
     * : The name of the database table (should be same as passed to the function)
142
     * 
143
     * schema
144
     * : The schema of the database table (should be same as passed to the 
145
     *   function)
146
     * 
147
     * column
148
     * : The foreign key column on the table.
149
     * 
150
     * foreign_table
151
     * : The name of the database table to be referenced.
152
     * 
153
     * foreign_schema
154
     * : The schema which contains the database table to be referenced.
155
     * 
156
     * foreign_column:
157
     * : The column to be refereced on the foreign table.
158
     * 
159
     * For foreign keys with multiple columns, the value of the `name` key must
160
     * be the same for only the columns in the key. 
161
     * 
162
     * @param array $table An array which contains the name of the table as it's
163
     *     `name` key and the schema of the table as it's `schema` key.
164
     * @return array<array<string>>
165
     */
166
    abstract protected function getForeignKeys(&$table);
167
168
    /**
169
     * Retrieve the description of indices on a given table.
170
     * The description returned must be an array which contains structured
171
     * arrays with the following keys.
172
     * 
173
     * column
174
     * : The name of a column which is part of an index
175
     * 
176
     * name
177
     * : The name of the index.
178
     * 
179
     * For unique keys with multiple columns, the value of the `name` key must
180
     * be the same for only the columns in the key. 
181
     * 
182
     * @param array $table An array which contains the name of the table as it's
183
     *     `name` key and the schema of the table as it's `schema` key.
184
     * @return array<array<string>>
185
     */
186
    abstract protected function getIndices(&$table);
187
188
    /**
189
     * Returns a boolean value which tells whether a table has an auto incrementing
190
     * key or not.
191
     * 
192
     * @return boolean
193
     */
194
    abstract protected function hasAutoIncrementingKey(&$table);
195
196
    /**
197
     * Returns the description of the database as an array.
198
     * 
199
     * @return array
200
     */
201 4
    public function describe() {
202 4
        $defaultSchema = $this->driver->getDefaultSchema();
203
        $description = array(
204 4
            'schemata' => array(),
205
        );
206
207 4
        $schemata = $this->getSchemata();
208
209 4
        foreach ($schemata as $schema) {
210 4
            if ($schema['name'] == $defaultSchema) {
211 4
                $description['tables'] = $this->describeTables($defaultSchema);
212 4
                $description['views'] = $this->describeViews($defaultSchema);
213
            } else {
214 2
                $description['schemata'][$schema['name']]['name'] = $schema['name'];
215 2
                $description['schemata'][$schema['name']]['tables'] = $this->describeTables($schema['name']);
216 4
                $description['schemata'][$schema['name']]['views'] = $this->describeViews($schema['name']);
217
            }
218
        }
219
220 4
        return $description;
221
    }
222
223 2
    public function setCleanDefaults($cleanDefaults) {
224 2
        $this->cleanDefaults = $cleanDefaults;
225 2
    }
226
227
    /**
228
     * Throws exceptions for which are found in the list of requested tables
229
     * but not found in the list of found tables.
230
     * 
231
     * @param array $tables
232
     * @param array $requestedTables
233
     * @throws exceptions\TableNotFoundException
234
     */
235 4
    private function throwTableExceptions($tables, $requestedTables) {
236 4
        $foundTables = array();
237 4
        foreach ($tables as $table) {
238 2
            $foundTables[] = $table['name'];
239
        }
240
241 4
        foreach ($requestedTables as $requestedTable) {
242 4
            if (array_search($requestedTable, $foundTables) === false) {
243 4
                throw new exceptions\TableNotFoundException($requestedTable 
244 4
                    ? "$requestedTable not found on target database." 
245 4
                    : "Please specify a table name."
246
                );
247
            }
248
        }
249
    }
250
251 11
    public function describeTables($schema, $requestedTables = array(), $includeViews = false) {
252 11
        $description = array();
253 11
        $tables = $this->getTables($schema, $requestedTables, $includeViews);
254
255 11
        if (count($requestedTables) > 0 && count($tables) < count($requestedTables)) {
256 4
            $this->throwTableExceptions($tables, $requestedTables);
257
        }
258
259 7
        foreach ($tables as $table) {
260 7
            $table['columns'] = $this->describeColumns($table);
261 7
            $table['primary_key'] = $this->describePrimaryKey($table);
262 7
            $table['unique_keys'] = $this->describeUniqueKeys($table);
263 7
            $table['foreign_keys'] = $this->describeForeignKeys($table);
264 7
            $table['indices'] = $this->describeIndices($table);
265 7
            $table['auto_increment'] = $this->hasAutoIncrementingKey($table);
266 7
            $table['schema'] = $this->fixSchema($table['schema']);
267
268 7
            $description[$table['name']] = $table;
269
        }
270 7
        return $description;
271
    }
272
273 7
    private function describeColumns($table) {
274 7
        $columns = array();
275 7
        $columnDetails = $this->getColumns($table);
276 7
        foreach ($columnDetails as $column) {
277 7
            $columns[$column['name']] = $column;
278 7
            $columns[$column['name']]['nulls'] = $columns[$column['name']]['nulls'] == 'YES' ? true : false;
279
280 7
            if ($this->cleanDefaults) {
281 7
                $columns[$column['name']]['default'] = $this->cleanDefaultValue($column['default']);
282
            }
283
        }
284
285 7
        return $columns;
286
    }
287
288 4
    private function describeViews($schema) {
289 4
        $description = array();
290 4
        $views = $this->getViews($schema);
291 4
        foreach ($views as $view) {
292 4
            $description[$view['name']] = array(
293 4
                'name' => $view['name'],
294 4
                'schema' => $view['schema'],
295 4
                'definition' => $view['definition']
296
            );
297
        }
298 4
        return $description;
299
    }
300
301 7
    private function describePrimaryKey($table) {
302 7
        return $this->describeKey($this->getPrimaryKey($table));
303
    }
304
305 7
    private function describeUniqueKeys($table) {
306 7
        return $this->describeKey($this->getUniqueKeys($table));
307
    }
308
309 7
    private function describeForeignKeys($table) {
310 7
        return $this->describeKey($this->getForeignKeys($table));
311
    }
312
313 7
    private function describeIndices($table) {
314 7
        return $this->describeKey($this->getIndices($table));
315
    }
316
317 7
    private function describeKey($constraintColumns) {
318 7
        $constraints = array();
319 7
        foreach ($constraintColumns as $column) {
320 5
            $name = $column['name'];
321 5
            unset($column['name']);
322 5
            foreach ($column as $key => $value) {
323 5
                if ($key == 'column' || $key == 'foreign_column') {
324 5
                    $constraints[$name]["{$key}s"][] = $value;
325
                } else {
326 4
                    if ($key === 'schema' || $key === 'foreign_schema') {
327 4
                        $value = $this->fixSchema($value);
328
                    }
329 5
                    $constraints[$name][$key] = $value;
330
                }
331
            }
332
        }
333 7
        return $constraints;
334
    }
335
336 7
    private function fixSchema($schema) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
337 7
        $defaultSchema = $this->driver->getDefaultSchema();
338 7
        if ($schema == false || $schema == $defaultSchema) {
339 6
            return '';
340
        } else {
341 3
            return $schema;
342
        }
343
    }
344
345 1
    protected function cleanDefaultValue($defaultValue) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
346 1
        return $defaultValue;
347
    }
348
349
}
350