Completed
Push — master ( 8b9586...6460ce )
by James Ekow Abaka
06:03
created

Descriptor::describeKey()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 12
cts 12
cp 1
rs 8.2222
c 0
b 0
f 0
cc 7
eloc 13
nc 5
nop 1
crap 7
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 16
    public function __construct($driver) {
22 16
        $this->driver = $driver;
23 16
    }
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 6
    public function describe() {
202 6
        $defaultSchema = $this->driver->getDefaultSchema();
203
        $description = array(
204 6
            'schemata' => array(),
205
        );
206
207 6
        $schemata = $this->getSchemata();
208
209 6
        foreach ($schemata as $schema) {
210 6
            if ($schema['name'] == $defaultSchema) {
211 6
                $description['tables'] = $this->describeTables($defaultSchema);
212 6
                $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 6
                $description['schemata'][$schema['name']]['views'] = $this->describeViews($schema['name']);
217
            }
218
        }
219
220 6
        return $description;
221
    }
222
223 3
    public function setCleanDefaults($cleanDefaults) {
224 3
        $this->cleanDefaults = $cleanDefaults;
225 3
    }
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 6
    private function throwTableExceptions($tables, $requestedTables) {
236 6
        $foundTables = array();
237 6
        foreach ($tables as $table) {
238 3
            $foundTables[] = $table['name'];
239
        }
240
241 6
        foreach ($requestedTables as $requestedTable) {
242 6
            if (array_search($requestedTable, $foundTables) === false) {
243 6
                throw new exceptions\TableNotFoundException("$requestedTable not found on target database.");
244
            }
245
        }
246
    }
247
248 16
    public function describeTables($schema, $requestedTables = array(), $includeViews = false) {
249 16
        $description = array();
250 16
        $tables = $this->getTables($schema, $requestedTables, $includeViews);
251
252 16
        if (count($requestedTables) > 0 && count($tables) < count($requestedTables)) {
253 6
            $this->throwTableExceptions($tables, $requestedTables);
254
        }
255
256 10
        foreach ($tables as $table) {
257 10
            $table['columns'] = $this->describeColumns($table);
258 10
            $table['primary_key'] = $this->describePrimaryKey($table);
259 10
            $table['unique_keys'] = $this->describeUniqueKeys($table);
260 10
            $table['foreign_keys'] = $this->describeForeignKeys($table);
261 10
            $table['indices'] = $this->describeIndices($table);
262 10
            $table['auto_increment'] = $this->hasAutoIncrementingKey($table);
263 10
            $table['schema'] = $this->fixSchema($table['schema']);
264
265 10
            $description[$table['name']] = $table;
266
        }
267 10
        return $description;
268
    }
269
270 10
    private function describeColumns($table) {
271 10
        $columns = array();
272 10
        $columnDetails = $this->getColumns($table);
273 10
        foreach ($columnDetails as $column) {
274 10
            $columns[$column['name']] = $column;
275 10
            $columns[$column['name']]['nulls'] = $columns[$column['name']]['nulls'] == 'YES' ? true : false;
276
277 10
            if ($this->cleanDefaults) {
278 10
                $columns[$column['name']]['default'] = $this->cleanDefaultValue($column['default']);
279
            }
280
        }
281
282 10
        return $columns;
283
    }
284
285 6
    private function describeViews($schema) {
286 6
        $description = array();
287 6
        $views = $this->getViews($schema);
288 6
        foreach ($views as $view) {
289 6
            $description[$view['name']] = array(
290 6
                'name' => $view['name'],
291 6
                'schema' => $view['schema'],
292 6
                'definition' => $view['definition']
293
            );
294
        }
295 6
        return $description;
296
    }
297
298 10
    private function describePrimaryKey($table) {
299 10
        return $this->describeKey($this->getPrimaryKey($table));
300
    }
301
302 10
    private function describeUniqueKeys($table) {
303 10
        return $this->describeKey($this->getUniqueKeys($table));
304
    }
305
306 10
    private function describeForeignKeys($table) {
307 10
        return $this->describeKey($this->getForeignKeys($table));
308
    }
309
310 10
    private function describeIndices($table) {
311 10
        return $this->describeKey($this->getIndices($table));
312
    }
313
314 10
    private function describeKey($constraintColumns) {
315 10
        $constraints = array();
316 10
        foreach ($constraintColumns as $column) {
317 7
            $name = $column['name'];
318 7
            unset($column['name']);
319 7
            foreach ($column as $key => $value) {
320 7
                if ($key == 'column' || $key == 'foreign_column') {
321 7
                    $constraints[$name]["{$key}s"][] = $value;
322
                } else {
323 6
                    if ($key === 'schema' || $key === 'foreign_schema') {
324 6
                        $value = $this->fixSchema($value);
325
                    }
326 7
                    $constraints[$name][$key] = $value;
327
                }
328
            }
329
        }
330 10
        return $constraints;
331
    }
332
333 10
    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...
334 10
        $defaultSchema = $this->driver->getDefaultSchema();
335 10
        if ($schema == false || $schema == $defaultSchema) {
336 9
            return '';
337
        } else {
338 3
            return $schema;
339
        }
340
    }
341
342 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...
343 1
        return $defaultValue;
344
    }
345
346
}
347