Passed
Branch dev (f56f10)
by Wilmer
04:41 queued 01:34
created

TestTrait   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 199
rs 10
c 0
b 0
f 0
wmc 25

12 Methods

Rating   Name   Duplication   Size   Complexity  
A assertIsOneOf() 0 3 1
A createLogger() 0 6 2
A createCache() 0 6 2
A createProfiler() 0 6 2
A createQueryCache() 0 6 2
A assertEqualsWithoutLE() 0 6 1
A createSchemaCache() 0 6 2
A invokeMethod() 0 14 2
A setInaccessibleProperty() 0 16 3
A prepareDatabase() 0 20 4
A replaceQuotes() 0 8 1
A getInaccessibleProperty() 0 19 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestSupport;
6
7
use Psr\Log\LoggerInterface;
8
use ReflectionClass;
9
use ReflectionException;
10
use ReflectionObject;
11
use Yiisoft\Cache\ArrayCache;
12
use Yiisoft\Cache\Cache;
13
use Yiisoft\Cache\CacheInterface;
14
use Yiisoft\Db\Cache\QueryCache;
15
use Yiisoft\Db\Cache\SchemaCache;
16
use Yiisoft\Db\Connection\ConnectionInterface;
17
use Yiisoft\Log\Logger;
18
use Yiisoft\Profiler\Profiler;
19
use Yiisoft\Profiler\ProfilerInterface;
20
21
trait TestTrait
22
{
23
    protected ?CacheInterface $cache = null;
24
    protected ?LoggerInterface $logger = null;
25
    protected ?ProfilerInterface $profiler = null;
26
    protected ?QueryCache $queryCache = null;
27
    protected ?SchemaCache $schemaCache = null;
28
29
    /**
30
     * Asserting two strings equality ignoring line endings.
31
     *
32
     * @param string $expected
33
     * @param string $actual
34
     * @param string $message
35
     */
36
    protected function assertEqualsWithoutLE(string $expected, string $actual, string $message = ''): void
37
    {
38
        $expected = str_replace("\r\n", "\n", $expected);
39
        $actual = str_replace("\r\n", "\n", $actual);
40
41
        $this->assertEquals($expected, $actual, $message);
0 ignored issues
show
Bug introduced by
The method assertEquals() does not exist on Yiisoft\Db\TestSupport\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

41
        $this->/** @scrutinizer ignore-call */ 
42
               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...
42
    }
43
44
    /**
45
     * Asserts that value is one of expected values.
46
     *
47
     * @param mixed $actual
48
     * @param array $expected
49
     * @param string $message
50
     */
51
    protected function assertIsOneOf(mixed $actual, array $expected, string $message = ''): void
52
    {
53
        self::assertThat($actual, new IsOneOfAssert($expected), $message);
54
    }
55
56
    protected function createCache(): Cache
57
    {
58
        if ($this->cache === null) {
59
            $this->cache = new Cache(new ArrayCache());
60
        }
61
        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...
62
    }
63
64
    protected function createLogger(): Logger
65
    {
66
        if ($this->logger === null) {
67
            $this->logger = new Logger();
68
        }
69
        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...
70
    }
71
72
    protected function createProfiler(): Profiler
73
    {
74
        if ($this->profiler === null) {
75
            $this->profiler = new Profiler($this->createLogger());
76
        }
77
        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...
78
    }
79
80
    protected function createQueryCache(): QueryCache
81
    {
82
        if ($this->queryCache === null) {
83
            $this->queryCache = new QueryCache($this->createCache());
84
        }
85
        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...
86
    }
87
88
    protected function createSchemaCache(): SchemaCache
89
    {
90
        if ($this->schemaCache === null) {
91
            $this->schemaCache = new SchemaCache($this->createCache());
92
        }
93
        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...
94
    }
95
96
    /**
97
     * Gets an inaccessible object property.
98
     *
99
     * @param object $object
100
     * @param string $propertyName
101
     * @param bool $revoke whether to make property inaccessible after getting.
102
     *
103
     * @return mixed
104
     */
105
    protected function getInaccessibleProperty(object $object, string $propertyName, bool $revoke = true): mixed
106
    {
107
        $class = new ReflectionClass($object);
108
109
        while (!$class->hasProperty($propertyName)) {
110
            $class = $class->getParentClass();
111
        }
112
113
        $property = $class->getProperty($propertyName);
114
115
        $property->setAccessible(true);
116
117
        $result = $property->getValue($object);
118
119
        if ($revoke) {
120
            $property->setAccessible(false);
121
        }
122
123
        return $result;
124
    }
125
126
    /**
127
     * Invokes an inaccessible method.
128
     *
129
     * @param object $object
130
     * @param string $method
131
     * @param array $args
132
     * @param bool $revoke whether to make method inaccessible after execution.
133
     *
134
     * @throws ReflectionException
135
     *
136
     * @return mixed
137
     */
138
    protected function invokeMethod(object $object, string $method, array $args = [], bool $revoke = true): mixed
139
    {
140
        $reflection = new ReflectionObject($object);
141
142
        $method = $reflection->getMethod($method);
143
144
        $method->setAccessible(true);
145
146
        $result = $method->invokeArgs($object, $args);
147
148
        if ($revoke) {
149
            $method->setAccessible(false);
150
        }
151
        return $result;
152
    }
153
154
    protected function prepareDatabase(ConnectionInterface $db, string $fixture): void
155
    {
156
        $db->open();
157
158
        if ($this->drivername === 'oci') {
159
            [$drops, $creates] = explode('/* STATEMENTS */', file_get_contents($fixture), 2);
160
            [$statements, $triggers, $data] = explode('/* TRIGGERS */', $creates, 3);
161
            $lines = array_merge(
162
                explode('--', $drops),
163
                explode(';', $statements),
164
                explode('/', $triggers),
165
                explode(';', $data)
166
            );
167
        } else {
168
            $lines = explode(';', file_get_contents($fixture));
169
        }
170
171
        foreach ($lines as $line) {
172
            if (trim($line) !== '') {
173
                $db->getPDO()->exec($line);
0 ignored issues
show
Bug introduced by
The method getPDO() does not exist on Yiisoft\Db\Connection\ConnectionInterface. It seems like you code against a sub-type of Yiisoft\Db\Connection\ConnectionInterface such as Yiisoft\Db\Connection\ConnectionPDOInterface. ( Ignorable by Annotation )

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

173
                $db->/** @scrutinizer ignore-call */ 
174
                     getPDO()->exec($line);
Loading history...
174
            }
175
        }
176
    }
177
178
    /**
179
     * Adjust dbms specific escaping.
180
     *
181
     * @param $sql
182
     *
183
     * @return array|string|null
184
     */
185
    protected function replaceQuotes($sql): string|array|null
186
    {
187
        return match ($this->drivername) {
188
            'mysql', 'sqlite' => str_replace(['[[', ']]'], '`', $sql),
189
            'oci' => str_replace(['[[', ']]'], '"', $sql),
190
            'pgsql' => str_replace(['\\[', '\\]'], ['[', ']'], preg_replace('/(\[\[)|((?<!(\[))]])/', '"', $sql)),
191
            'sqlsrv' => str_replace(['[[', ']]'], ['[', ']'], $sql),
192
            default => $sql,
193
        };
194
    }
195
196
    /**
197
     * Sets an inaccessible object property to a designated value.
198
     *
199
     * @param object $object
200
     * @param string $propertyName
201
     * @param $value
202
     * @param bool $revoke whether to make property inaccessible after setting
203
     */
204
    protected function setInaccessibleProperty(object $object, string $propertyName, $value, bool $revoke = true): void
205
    {
206
        $class = new ReflectionClass($object);
207
208
        while (!$class->hasProperty($propertyName)) {
209
            $class = $class->getParentClass();
210
        }
211
212
        $property = $class->getProperty($propertyName);
213
214
        $property->setAccessible(true);
215
216
        $property->setValue($object, $value);
217
218
        if ($revoke) {
219
            $property->setAccessible(false);
220
        }
221
    }
222
}
223