Passed
Pull Request — master (#240)
by Wilmer
27:17 queued 12:08
created

TestTrait::createSchemaCache()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestUtility;
6
7
use Psr\Log\LoggerInterface;
8
use ReflectionClass;
9
use ReflectionObject;
10
use Yiisoft\Cache\ArrayCache;
11
use Yiisoft\Cache\Cache;
12
use Yiisoft\Cache\CacheInterface;
13
use Yiisoft\Db\Cache\QueryCache;
14
use Yiisoft\Db\Cache\SchemaCache;
15
use Yiisoft\Db\Connection\ConnectionInterface;
16
use Yiisoft\Log\Logger;
17
use Yiisoft\Profiler\Profiler;
18
use Yiisoft\Profiler\ProfilerInterface;
19
20
trait TestTrait
21
{
22
    protected ?CacheInterface $cache = null;
23
    protected ?LoggerInterface $logger = null;
24
    protected ?ProfilerInterface $profiler = null;
25
    protected ?QueryCache $queryCache = null;
26
    protected ?SchemaCache $schemaCache = null;
27
28
    /**
29
     * Asserting two strings equality ignoring line endings.
30
     *
31
     * @param string $expected
32
     * @param string $actual
33
     * @param string $message
34
     */
35
    protected function assertEqualsWithoutLE(string $expected, string $actual, string $message = ''): void
36
    {
37
        $expected = str_replace("\r\n", "\n", $expected);
38
        $actual = str_replace("\r\n", "\n", $actual);
39
40
        $this->assertEquals($expected, $actual, $message);
0 ignored issues
show
Bug introduced by
The method assertEquals() does not exist on Yiisoft\Db\TestUtility\TestTrait. Did you maybe mean assertEqualsWithoutLE()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

40
        $this->/** @scrutinizer ignore-call */ 
41
               assertEquals($expected, $actual, $message);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
41
    }
42
43
    /**
44
     * Asserts that value is one of expected values.
45
     *
46
     * @param mixed $actual
47
     * @param array $expected
48
     * @param string $message
49
     */
50
    protected function assertIsOneOf($actual, array $expected, $message = ''): void
51
    {
52
        self::assertThat($actual, new IsOneOfAssert($expected), $message);
53
    }
54
55
    protected function createCache(): Cache
56
    {
57
        if ($this->cache === null) {
58
            $this->cache = new Cache(new ArrayCache());
59
        }
60
        return $this->cache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->cache returns the type null which is incompatible with the type-hinted return Yiisoft\Cache\Cache.
Loading history...
61
    }
62
63
    protected function createConnection(string $dsn = null): ?ConnectionInterface
64
    {
65
        $db = null;
66
67
        if ($dsn !== null) {
68
            $class = self::DB_CONNECTION_CLASS;
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\T...it::DB_CONNECTION_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
69
            $db = new $class($dsn, $this->createQueryCache(), $this->createSchemaCache());
70
            $db->setLogger($this->createLogger());
71
            $db->setProfiler($this->createProfiler());
72
            $db->setUsername(self::DB_USERNAME);
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_USERNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
73
            $db->setPassword(self::DB_PASSWORD);
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_PASSWORD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
74
            $db->setCharset(self::DB_CHARSET);
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_CHARSET was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
75
        }
76
77
        return $db;
78
    }
79
80
    protected function createLogger(): Logger
81
    {
82
        if ($this->logger === null) {
83
            $this->logger = new Logger();
84
        }
85
        return $this->logger;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->logger returns the type null which is incompatible with the type-hinted return Yiisoft\Log\Logger.
Loading history...
86
    }
87
88
    protected function createProfiler(): Profiler
89
    {
90
        if ($this->profiler === null) {
91
            $this->profiler = new Profiler($this->createLogger());
92
        }
93
        return $this->profiler;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->profiler returns the type null which is incompatible with the type-hinted return Yiisoft\Profiler\Profiler.
Loading history...
94
    }
95
96
    protected function createQueryCache(): QueryCache
97
    {
98
        if ($this->queryCache === null) {
99
            $this->queryCache = new QueryCache($this->createCache());
100
        }
101
        return $this->queryCache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->queryCache could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Cache\QueryCache. Consider adding an additional type-check to rule them out.
Loading history...
102
    }
103
104
    protected function createSchemaCache(): SchemaCache
105
    {
106
        if ($this->schemaCache === null) {
107
            $this->schemaCache = new SchemaCache($this->createCache());
108
        }
109
        return $this->schemaCache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->schemaCache could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Cache\SchemaCache. Consider adding an additional type-check to rule them out.
Loading history...
110
    }
111
112
    /**
113
     * @param bool $reset whether to clean up the test database.
114
     *
115
     * @return ConnectionInterface
116
     */
117
    protected function getConnection($reset = false): ConnectionInterface
118
    {
119
        if ($reset === false && isset($this->connection)) {
120
            return $this->connection;
121
        }
122
123
        if ($reset === false) {
124
            return $this->createConnection(self::DB_DSN);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createConnection(self::DB_DSN) could return the type null which is incompatible with the type-hinted return Yiisoft\Db\Connection\ConnectionInterface. Consider adding an additional type-check to rule them out.
Loading history...
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_DSN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
125
        }
126
127
        try {
128
            $this->prepareDatabase();
129
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\TestUtility\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
130
            $this->markTestSkipped('Something wrong when preparing database: ' . $e->getMessage());
0 ignored issues
show
Bug introduced by
It seems like markTestSkipped() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

130
            $this->/** @scrutinizer ignore-call */ 
131
                   markTestSkipped('Something wrong when preparing database: ' . $e->getMessage());
Loading history...
131
        }
132
133
        return $this->connection;
134
    }
135
136
    /**
137
     * Gets an inaccessible object property.
138
     *
139
     * @param object $object
140
     * @param string $propertyName
141
     * @param bool $revoke whether to make property inaccessible after getting.
142
     *
143
     * @return mixed
144
     */
145
    protected function getInaccessibleProperty(object $object, string $propertyName, bool $revoke = true)
146
    {
147
        $class = new ReflectionClass($object);
148
149
        while (!$class->hasProperty($propertyName)) {
150
            $class = $class->getParentClass();
151
        }
152
153
        $property = $class->getProperty($propertyName);
154
155
        $property->setAccessible(true);
156
157
        $result = $property->getValue($object);
158
159
        if ($revoke) {
160
            $property->setAccessible(false);
161
        }
162
163
        return $result;
164
    }
165
166
    /**
167
     * Invokes a inaccessible method.
168
     *
169
     * @param object $object
170
     * @param string $method
171
     * @param array $args
172
     * @param bool $revoke whether to make method inaccessible after execution.
173
     *
174
     * @return mixed
175
     */
176
    protected function invokeMethod(object $object, string $method, array $args = [], bool $revoke = true)
177
    {
178
        $reflection = new ReflectionObject($object);
179
180
        $method = $reflection->getMethod($method);
181
182
        $method->setAccessible(true);
183
184
        $result = $method->invokeArgs($object, $args);
185
186
        if ($revoke) {
187
            $method->setAccessible(false);
188
        }
189
        return $result;
190
    }
191
192
    protected function prepareDatabase(string $dsn = null, $fixture = null): void
193
    {
194
        $fixture = $fixture ?? self::DB_FIXTURES_PATH;
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_FIXTURES_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
195
196
        if ($dsn !== null) {
197
            $this->connection = $this->createConnection($dsn);
0 ignored issues
show
Bug Best Practice introduced by
The property connection does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
198
        }
199
200
        $this->connection->open();
201
202
        if (self::DB_DRIVERNAME === 'oci') {
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_DRIVERNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
203
            [$drops, $creates] = explode('/* STATEMENTS */', file_get_contents($fixture), 2);
204
            [$statements, $triggers, $data] = explode('/* TRIGGERS */', $creates, 3);
205
            $lines = array_merge(
206
                explode('--', $drops),
207
                explode(';', $statements),
208
                explode('/', $triggers),
209
                explode(';', $data)
210
            );
211
        } else {
212
            $lines = explode(';', file_get_contents($fixture));
213
        }
214
215
        foreach ($lines as $line) {
216
            if (trim($line) !== '') {
217
                $this->connection->getPDO()->exec($line);
218
            }
219
        }
220
    }
221
222
    /**
223
     * Adjust dbms specific escaping.
224
     *
225
     * @param $sql
226
     *
227
     * @return mixed
228
     */
229
    protected function replaceQuotes($sql)
230
    {
231
        switch (self::DB_DRIVERNAME) {
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Db\TestUtility\TestTrait::DB_DRIVERNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
232
            case 'mysql':
233
            case 'sqlite':
234
                return str_replace(['[[', ']]'], '`', $sql);
235
            case 'oci':
236
                return str_replace(['[[', ']]'], '"', $sql);
237
            case 'pgsql':
238
                // more complex replacement needed to not conflict with postgres array syntax
239
                return str_replace(['\\[', '\\]'], ['[', ']'], preg_replace('/(\[\[)|((?<!(\[))]])/', '"', $sql));
240
            case 'mssql':
241
                return str_replace(['[[', ']]'], ['[', ']'], $sql);
242
            default:
243
                return $sql;
244
        }
245
    }
246
247
    /**
248
     * Sets an inaccessible object property to a designated value.
249
     *
250
     * @param object $object
251
     * @param string $propertyName
252
     * @param $value
253
     * @param bool $revoke whether to make property inaccessible after setting
254
     */
255
    protected function setInaccessibleProperty(object $object, string $propertyName, $value, bool $revoke = true): void
256
    {
257
        $class = new ReflectionClass($object);
258
259
        while (!$class->hasProperty($propertyName)) {
260
            $class = $class->getParentClass();
261
        }
262
263
        $property = $class->getProperty($propertyName);
264
265
        $property->setAccessible(true);
266
267
        $property->setValue($object, $value);
268
269
        if ($revoke) {
270
            $property->setAccessible(false);
271
        }
272
    }
273
}
274