Completed
Push — master ( db1323...27dbeb )
by Sébastien
03:00
created

MysqlInformationSchema   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 91.96%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 38
c 2
b 1
f 0
lcom 1
cbo 10
dl 0
loc 283
ccs 103
cts 112
cp 0.9196
rs 8.4

13 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 21 5
A getUniqueKeys() 0 16 4
A getIndexesInformation() 0 5 1
A getPrimaryKey() 0 9 2
A getPrimaryKeys() 0 9 2
A getColumnsInformation() 0 5 1
A getForeignKeys() 0 5 1
A getReferences() 0 5 1
A getTablesInformation() 0 5 1
C getTableConfig() 0 28 7
B getSchemaConfig() 0 20 6
A loadCacheInformation() 0 16 4
A clearCacheInformation() 0 10 3
1
<?php
2
namespace Soluble\Schema\Source;
3
4
use Soluble\Schema\Exception;
5
use Soluble\Schema\Source;
6
use Soluble\Schema\Db\Wrapper\MysqlConnectionAdapter;
7
use ArrayObject;
8
9
class MysqlInformationSchema extends Source\AbstractSchemaSource
10
{
11
    /**
12
     * Schema name
13
     *
14
     * @var string
15
     */
16
    protected $schema;
17
18
    /**
19
     * @var MysqlConnectionAdapter
20
     */
21
    protected $adapter;
22
23
24
    /**
25
     * Whether to include full schema options like comment, collations...
26
     * @var boolean
27
     */
28
    protected $include_options = true;
29
30
    /**
31
     *
32
     * @var array
33
     */
34
    protected static $localCache = array();
35
36
37
    /**
38
     *
39
     * @var boolean
40
     */
41
    protected $useLocalCaching = true;
42
43
    /**
44
     *
45
     * @var array
46
     */
47
    protected static $fullyCachedSchemas = array();
48
49
50
    /**
51
     *
52
     * @var Mysql\MysqlDriverInterface
53
     */
54
    protected $driver;
55
56
    /**
57
     * Constructor
58
     *
59
     * @param \PDO|\mysqli $connection
60
     * @param string|null $schema default schema, taken from adapter if not given
61
     * @throws Exception\InvalidArgumentException for invalid connection
62
     * @throws Exception\InvalidUsageException thrown if no schema can be found.
63
     */
64 23
    public function __construct($connection, $schema = null)
65
    {
66
        try {
67 23
            $this->adapter = new MysqlConnectionAdapter($connection);
68 23
        } catch (Exception\InvalidArgumentException $e) {
69
            $msg = "MysqlInformationSchema requires a valid 'mysqli' or 'pdo:mysql' connection object ({$e->getMessage()}).";
70
            throw new Exception\InvalidArgumentException($msg);
71
        }
72
73 23
        if ($schema === null) {
74 23
            $schema = $this->adapter->getCurrentSchema();
75 23
            if ($schema === false || $schema == '') {
76
                $msg = "Database name (schema) parameter missing and no default schema set on connection";
77
                throw new Exception\InvalidUsageException($msg);
78
            }
79 23
        }
80
81 23
        $this->driver = new Mysql\MysqlDriver51($this->adapter, $schema);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Soluble\Schema\Sour...this->adapter, $schema) of type object<Soluble\Schema\Source\Mysql\MysqlDriver51> is incompatible with the declared type object<Soluble\Schema\So...l\MysqlDriverInterface> of property $driver.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
82
83 23
        $this->setDefaultSchema($schema);
84 23
    }
85
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 1
    public function getUniqueKeys($table, $include_primary = false)
91
    {
92 1
        $this->loadCacheInformation($table);
93 1
        $uniques = (array) self::$localCache[$this->schema]['tables'][$table]['unique_keys'];
94 1
        if ($include_primary) {
95
            try {
96 1
                $pks = $this->getPrimaryKeys($table);
97 1
                if (count($pks) > 0) {
98 1
                    $uniques = array_merge($uniques, array('PRIMARY' => $pks));
99 1
                }
100 1
            } catch (Exception\NoPrimaryKeyException $e) {
101
                // Ignore exception
102
            }
103 1
        }
104 1
        return $uniques;
105
    }
106
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function getIndexesInformation($table)
112
    {
113
        $this->loadCacheInformation($table);
114
        return self::$localCache[$this->schema]['tables'][$table]['indexes'];
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 6
    public function getPrimaryKey($table)
121
    {
122 5
        $pks = $this->getPrimaryKeys($table);
123 2
        if (count($pks) > 1) {
124 2
            $keys = join(',', $pks);
125 1
            throw new Exception\MultiplePrimaryKeyException(__METHOD__ . ". Multiple primary keys found on table '{$this->schema}'.'$table':  $keys");
126
        }
127 1
        return $pks[0];
128 6
    }
129
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 9
    public function getPrimaryKeys($table)
135
    {
136 9
        $this->loadCacheInformation($table);
137 6
        $pks = self::$localCache[$this->schema]['tables'][$table]['primary_keys'];
138 6
        if (count($pks) == 0) {
139 3
            throw new Exception\NoPrimaryKeyException(__METHOD__ . ". No primary keys found on table  '{$this->schema}'.'$table'.");
140
        }
141 4
        return $pks;
142
    }
143
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 3
    public function getColumnsInformation($table)
149
    {
150 2
        $this->loadCacheInformation($table);
151 2
        return self::$localCache[$this->schema]['tables'][$table]['columns'];
152 3
    }
153
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 1
    public function getForeignKeys($table)
159
    {
160 1
        $this->loadCacheInformation($table);
161 1
        return self::$localCache[$this->schema]['tables'][$table]['foreign_keys'];
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 1
    public function getReferences($table)
168
    {
169 1
        $this->loadCacheInformation($table);
170 1
        return self::$localCache[$this->schema]['tables'][$table]['references'];
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176 3
    public function getTablesInformation()
177
    {
178 3
        $this->loadCacheInformation(null);
179 3
        return self::$localCache[$this->schema]['tables'];
180
    }
181
182
    /**
183
     * Get a table configuration
184
     *
185
     * @throws Exception\ErrorException
186
     * @throws Exception\TableNotFoundException
187
     *
188
     * @param string $table table name
189
     * @param boolean|null $include_options include extended information
190
     * @return array
191
     */
192 13
    protected function getTableConfig($table, $include_options = null)
193
    {
194 13
        if ($include_options === null) {
195 13
            $include_options = $this->include_options;
196 13
        }
197
198 13
        $schema = $this->schema;
199
200 13
        if ($this->useLocalCaching &&
201 13
                isset(self::$localCache[$schema]['tables'][$table])) {
202 9
            return self::$localCache[$schema]['tables'][$table];
203
        }
204
205 4
        $config = $this->driver->getSchemaConfig($table, $include_options);
206
207 4
        if (!array_key_exists($table, $config['tables'])) {
208 2
            throw new Exception\TableNotFoundException(__METHOD__ . ". Table '$table' in database schema '{$schema}' not found.");
209 3
        }
210
211 3
        if ($this->useLocalCaching) {
212 3
            if (!array_key_exists($schema, self::$localCache)) {
213 1
                self::$localCache[$schema] = array();
214 1
            }
215 2
            self::$localCache[$schema] = array_merge_recursive(self::$localCache[$schema], (array) $config);
216 2
        }
217
218 2
        return $config['tables'][$table];
219 2
    }
220
221
222
    /**
223
     * Get schema configuration
224
     *
225
     * @throws Exception\ErrorException
226
     * @throws Exception\SchemaNotFoundException
227
     *
228
     * @param boolean|null $include_options include extended information
229
     * @return ArrayObject
230
     */
231 2
    public function getSchemaConfig($include_options = null)
232
    {
233 2
        if ($include_options === null) {
234 2
            $include_options = $this->include_options;
235 2
        }
236 2
        $schema = $this->schema;
237 2
        if ($this->useLocalCaching && in_array($schema, self::$fullyCachedSchemas)) {
238
            return self::$localCache[$schema];
239 1
        }
240
241 2
        $config = $this->driver->getSchemaConfig($table = null, $include_options);
242 2
        if (count($config['tables']) == 0) {
243 1
            throw new Exception\SchemaNotFoundException(__METHOD__ . " Error: schema '{$schema}' not found or without any table or view");
244
        }
245 1
        if ($this->useLocalCaching) {
246 1
            self::$localCache[$schema] = $config;
247 1
            self::$fullyCachedSchemas[] = $schema;
248 1
        }
249 1
        return $config;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $config; (Soluble\Schema\Source\Mysql\ArrayObject) is incompatible with the return type declared by the interface Soluble\Schema\Source\Sc...erface::getSchemaConfig of type ArrayObject.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
250
    }
251
252
    /**
253
     *
254
     * @param string $table
255
     * @throws Exception\InvalidArgumentException
256
     * @throws Exception\TableNotFoundException
257
     *
258
     */
259 16
    protected function loadCacheInformation($table = null)
260 1
    {
261 16
        $schema = $this->schema;
262 16
        $this->checkTableArgument($table);
263
264 14
        if (!in_array($schema, self::$fullyCachedSchemas)) {
265 3
            if ($table !== null) {
266 3
                $this->getTableConfig($table);
267 3
            } else {
268
                $this->getSchemaConfig();
269
            }
270 14
        } elseif ($table !== null) {
271
            // Just in case to check if table exists
272 8
            $this->getTableConfig($table);
273 7
        }
274 13
    }
275
276
    /**
277
     * Clear local cache information for the current schema
278
     *
279
     * @throws Exception\InvalidArgumentException
280
     */
281 1
    public function clearCacheInformation()
282
    {
283 1
        $schema = $this->schema;
284 1
        if (array_key_exists($schema, self::$localCache)) {
285 1
            unset(self::$localCache[$schema]);
286 1
            if (($key = array_search($schema, self::$fullyCachedSchemas)) !== false) {
287 1
                unset(self::$fullyCachedSchemas[$key]);
288 1
            }
289 1
        }
290 1
    }
291
}
292