Skip to content

Commit b3ffe56

Browse files
Various improvements. (#17)
1 parent 4f710c6 commit b3ffe56

6 files changed

Lines changed: 215 additions & 46 deletions

File tree

.scrutinizer.yml

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,48 @@ build:
2222

2323
phpunit:
2424
services:
25+
db-mssql:
26+
image: mcr.microsoft.com/mssql/server:2017-latest
27+
28+
# Define any additional environment variables that are needed by the service.
29+
env:
30+
SA_PASSWORD: YourStrong!Passw0rd
31+
ACCEPT_EULA: Y
32+
MSSQL_PID: Developer
33+
34+
# We automatically forward these ports from your localhost to the service's port.
35+
# Alternatively, you can also access the service on the "$SERVICE_SOME_NAME_IP"
36+
# environment variable.
37+
ports:
38+
# Forward 127.0.0.1:12345 -> SERVICE_IP:12345
39+
- 1433
40+
41+
# If your service writes data to disk like most databases do, you can significantly
42+
# speed up tests by mounting a ramdisk at those paths.
43+
ramdisks:
44+
- /var/lib/data
45+
46+
db-mysql:
47+
image: mysql:8.0.29
48+
49+
# Define any additional environment variables that are needed by the service.
50+
env:
51+
MYSQL_ALLOW_EMPTY_PASSWORD: 1
52+
MYSQL_ROOT_PASSWORD: ''
53+
MYSQL_DATABASE: yiitest
54+
55+
# We automatically forward these ports from your localhost to the service's port.
56+
# Alternatively, you can also access the service on the "$SERVICE_SOME_NAME_IP"
57+
# environment variable.
58+
ports:
59+
# Forward 127.0.0.1:12345 -> SERVICE_IP:12345
60+
- 3306
61+
62+
# If your service writes data to disk like most databases do, you can significantly
63+
# speed up tests by mounting a ramdisk at those paths.
64+
ramdisks:
65+
- /var/lib/data
66+
2567
db-pgsql:
2668
image: postgres:14
2769

@@ -43,13 +85,67 @@ build:
4385
ramdisks:
4486
- /var/lib/data
4587

46-
dependencies:
47-
override:
48-
- composer require yiisoft/db-pgsql --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
88+
db-oracle:
89+
image: wnameless/oracle-xe-11g-r2:latest
90+
91+
# We automatically forward these ports from your localhost to the service's port.
92+
# Alternatively, you can also access the service on the "$SERVICE_SOME_NAME_IP"
93+
# environment variable.
94+
ports:
95+
# Forward 127.0.0.1:12345 -> SERVICE_IP:12345
96+
- 1521
97+
98+
# If your service writes data to disk like most databases do, you can significantly
99+
# speed up tests by mounting a ramdisk at those paths.
100+
ramdisks:
101+
- /var/lib/data
49102

50103
tests:
104+
before:
105+
- curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
106+
- curl https://packages.microsoft.com/config/ubuntu/18.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
107+
- sudo apt-get update -y
108+
- sudo ACCEPT_EULA=Y apt-get install mssql-tools unixodbc-dev -y
109+
- sudo ls /opt/mssql-tools/bin/sqlcmd*
110+
- /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE yiitest'
111+
- pecl -q install pdo_sqlsrv
112+
- sudo mkdir -p /opt/oracle
113+
- sudo curl -k -L --output /opt/oracle/instantclient-basic-linux.x64-21.3.0.0.0.zip https://download.oracle.com/otn_software/linux/instantclient/213000/instantclient-basic-linux.x64-21.3.0.0.0.zip
114+
- sudo curl -k -L --output /opt/oracle/instantclient-sdk-linux.x64-21.3.0.0.0.zip https://download.oracle.com/otn_software/linux/instantclient/213000/instantclient-sdk-linux.x64-21.3.0.0.0.zip
115+
- sudo unzip /opt/oracle/instantclient-basic-linux.x64-21.3.0.0.0.zip -d /opt/oracle
116+
- sudo unzip /opt/oracle/instantclient-sdk-linux.x64-21.3.0.0.0.zip -d /opt/oracle
117+
- sudo apt-get install libaio1 -y
118+
- export ORACLE_HOME=/opt/oracle/instantclient_21_3
119+
- sudo sh -c "echo /opt/oracle/instantclient_21_3 > /etc/ld.so.conf.d/oracle-instantclient.conf"
120+
- sudo ldconfig
121+
- curl -k -L --output /home/scrutinizer/oci8-3.0.1.tgz https://pecl.php.net/get/oci8-3.0.1.tgz
122+
- cd /home/scrutinizer
123+
- tar -zxf oci8-3.0.1.tgz
124+
- cd oci8-3.0.1
125+
- phpize
126+
- ./configure --with-oci8=instantclient,/opt/oracle/instantclient_21_3
127+
- make
128+
- sudo make install
129+
- sudo ldconfig
130+
- curl -k -L --output /home/scrutinizer/php-8.0.20.tar.gz https://www.php.net/distributions/php-8.0.20.tar.gz
131+
- cd /home/scrutinizer
132+
- tar -zxf php-8.0.20.tar.gz
133+
- cd php-8.0.20/ext/pdo_oci
134+
- phpize
135+
- ./configure --with-pdo-oci=instantclient,/opt/oracle/instantclient_21_3
136+
- make
137+
- sudo make install
138+
- sudo ldconfig
139+
- cd /home/scrutinizer/build/
140+
- composer require yiisoft/db-mssql --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
141+
- composer require yiisoft/db-mysql --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
142+
- composer require yiisoft/db-oracle --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
143+
- composer require yiisoft/db-pgsql --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
144+
- composer require yiisoft/db-sqlite --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
145+
- echo 'extension=pdo_oci' >> /home/scrutinizer/.phpenv/versions/8.0.20/etc/php.ini
146+
51147
override:
52-
- command: ./vendor/bin/phpunit --group Pgsql --coverage-clover ./coverage.xml
148+
- command: ./vendor/bin/phpunit --coverage-clover ./coverage.xml
53149
coverage:
54150
file: coverage.xml
55151
format: php-clover

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ This package implements database-based [PSR-16](https://www.php-fig.org/psr/psr-
2020

2121
|Packages| PHP | Versions | CI-Actions
2222
|:------:|:----:|:------------------------:|:-----------:|
23-
[[db-mssql]](https://github.com/yiisoft/db-mssql)|**7.4 - 8.1**| **2017 - 2019**|[![mssql](https://github.com/yiisoft/cache-db/actions/workflows/mssql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/mssql.yml) | |
24-
[[db-mysql]](https://github.com/yiisoft/db-mysql)|**7.4 - 8.1**| **5.7 - 8.0**|[![mysql](https://github.com/yiisoft/cache-db/actions/workflows/mysql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/mysql.yml)|
25-
[[db-oracle]](https://github.com/yiisoft/db-oracle)|**7.4 - 8.1**| **11c - 12c**|[![oracle](https://github.com/yiisoft/cache-db/actions/workflows/oracle.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/oracle.yml)|
26-
|[[db-pgsql]](https://github.com/yiisoft/db-pgsql)|**7.4 - 8.1**| **9.0 - 14.0**|[![pgsql](https://github.com/yiisoft/cache-db/actions/workflows/pgsql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/pgsql.yml)|
27-
|[[db-sqlite]](https://github.com/yiisoft/db-sqlite)|**7.4 - 8.1**| **3:latest**|[![sqlite](https://github.com/yiisoft/cache-db/actions/workflows/sqlite.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/sqlite.yml)|
23+
[[db-mssql]](https://github.com/yiisoft/db-mssql)|**8.0 - 8.1**| **2017 - 2019**|[![mssql](https://github.com/yiisoft/cache-db/actions/workflows/mssql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/mssql.yml) | |
24+
[[db-mysql]](https://github.com/yiisoft/db-mysql)|**8.0 - 8.1**| **5.7 - 8.0**|[![mysql](https://github.com/yiisoft/cache-db/actions/workflows/mysql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/mysql.yml)|
25+
[[db-oracle]](https://github.com/yiisoft/db-oracle)|**8.0 - 8.1**| **11c - 12c**|[![oracle](https://github.com/yiisoft/cache-db/actions/workflows/oracle.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/oracle.yml)|
26+
|[[db-pgsql]](https://github.com/yiisoft/db-pgsql)|**8.0 - 8.1**| **9.0 - 14.0**|[![pgsql](https://github.com/yiisoft/cache-db/actions/workflows/pgsql.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/pgsql.yml)|
27+
|[[db-sqlite]](https://github.com/yiisoft/db-sqlite)|**8,0 - 8.1**| **3:latest**|[![sqlite](https://github.com/yiisoft/cache-db/actions/workflows/sqlite.yml/badge.svg)](https://github.com/yiisoft/cache-db/actions/workflows/sqlite.yml)|
2828

2929
## Requirements
3030

31-
- PHP 7.4 or higher.
31+
- PHP 8.0 or higher.
3232
- `PDO` PHP extension.
3333

3434
## Installation
@@ -105,10 +105,11 @@ The package is tested with [PHPUnit](https://phpunit.de/). To run tests:
105105

106106
### Mutation testing
107107

108-
The package tests are checked with [Infection](https://infection.github.io/) mutation framework. To run it:
108+
The package tests are checked with [Infection](https://infection.github.io/) mutation framework with
109+
[Infection Static Analysis Plugin](https://github.com/Roave/infection-static-analysis-plugin). To run it:
109110

110111
```shell
111-
./vendor/bin/infection
112+
./vendor/bin/roave-infection-static-analysis-plugin
112113
```
113114

114115
### Static analysis

src/DbCache.php

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use DateInterval;
88
use DateTime;
9+
use InvalidArgumentException;
910
use PDO;
1011
use Psr\SimpleCache\CacheInterface;
1112
use Throwable;
@@ -29,6 +30,7 @@
2930
* DbCache stores cache data in a database table.
3031
*
3132
* Database schema could be initialized by applying migration:
33+
*
3234
* {@see \Yiisoft\Cache\Db\Migration\M202101140204CreateCache}.
3335
*/
3436
final class DbCache implements CacheInterface
@@ -84,7 +86,7 @@ public function getTable(): string
8486
return $this->table;
8587
}
8688

87-
public function get($key, $default = null)
89+
public function get($key, $default = null): mixed
8890
{
8991
$this->validateKey($key);
9092
$value = $this->getData($key, ['data'], 'scalar');
@@ -106,15 +108,19 @@ public function set($key, $value, $ttl = null): bool
106108
return $this->delete($key);
107109
}
108110

109-
$this->db
110-
->createCommand()
111-
->upsert($this->table, $this->buildDataRow($key, $ttl, $value, true))
112-
->noCache()
113-
->execute();
111+
try {
112+
$this->db
113+
->createCommand()
114+
->upsert($this->table, $this->buildDataRow($key, $ttl, $value, true))
115+
->noCache()
116+
->execute();
114117

115-
$this->gc();
118+
$this->gc();
116119

117-
return true;
120+
return true;
121+
} catch (Throwable $e) {
122+
throw new CacheException('Unable to update or insert cache data.', 0, $e);
123+
}
118124
}
119125

120126
public function delete($key): bool
@@ -165,19 +171,23 @@ public function setMultiple($values, $ttl = null): bool
165171
$keys[] = $key;
166172
}
167173

168-
$this->deleteData($keys);
174+
try {
175+
$this->deleteData($keys);
169176

170-
if (!empty($rows) && !$this->isExpiredTtl($ttl)) {
171-
$this->db
172-
->createCommand()
173-
->batchInsert($this->table, ['id', 'expire', 'data'], $rows)
174-
->noCache()
175-
->execute();
176-
}
177+
if (!empty($rows) && !$this->isExpiredTtl($ttl)) {
178+
$this->db
179+
->createCommand()
180+
->batchInsert($this->table, ['id', 'expire', 'data'], $rows)
181+
->noCache()
182+
->execute();
183+
}
177184

178-
$this->gc();
185+
$this->gc();
179186

180-
return true;
187+
return true;
188+
} catch (Throwable $e) {
189+
throw new CacheException('Unable to update or insert cache data.', 0, $e);
190+
}
181191
}
182192

183193
public function deleteMultiple($keys): bool
@@ -224,6 +234,7 @@ private function getData($id, array $fields, string $method)
224234
* Deletes a cache data from the database.
225235
*
226236
* @param array|string|true $id One or more IDs for deleting data.
237+
*
227238
* If `true`, the all cache data will be deleted from the database.
228239
*/
229240
private function deleteData($id): void
@@ -232,12 +243,16 @@ private function deleteData($id): void
232243
return;
233244
}
234245

235-
$condition = $id === true ? '' : ['id' => $id];
236-
$this->db
237-
->createCommand()
238-
->delete($this->table, $condition)
239-
->noCache()
240-
->execute();
246+
try {
247+
$condition = $id === true ? '' : ['id' => $id];
248+
$this->db
249+
->createCommand()
250+
->delete($this->table, $condition)
251+
->noCache()
252+
->execute();
253+
} catch (Throwable $e) {
254+
throw new CacheException('Unable to delete cache data.', 0, $e);
255+
}
241256
}
242257

243258
/**

src/InvalidArgumentException.php

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/DbCacheTest.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
use ArrayIterator;
88
use DateInterval;
9+
use InvalidArgumentException;
910
use IteratorAggregate;
10-
use Psr\SimpleCache\InvalidArgumentException;
1111
use ReflectionException;
1212
use ReflectionObject;
1313
use stdClass;
14+
use Yiisoft\Cache\Db\CacheException;
15+
use Yiisoft\Cache\Db\DbCache;
1416
use Yiisoft\Db\Exception\Exception;
1517

1618
use function array_keys;
@@ -440,6 +442,49 @@ public function testHasThrowExceptionForInvalidKey($key): void
440442
$this->dbCache->has($key);
441443
}
442444

445+
public function testSetThrowExceptionForFailExecuteCommand(): void
446+
{
447+
$cache = new DbCache($this->db, 'noExist');
448+
$this->expectException(CacheException::class);
449+
$cache->set('key', 'value');
450+
}
451+
452+
public function testDeleteThrowExceptionForFailExecuteCommand(): void
453+
{
454+
$cache = new DbCache($this->db, 'noExist');
455+
$this->expectException(CacheException::class);
456+
$cache->delete('key');
457+
}
458+
459+
public function testClearThrowExceptionForFailExecuteCommand(): void
460+
{
461+
$cache = new DbCache($this->db, 'noExist');
462+
$this->expectException(CacheException::class);
463+
$cache->clear();
464+
}
465+
466+
public function testSetMultipleThrowExceptionForFailExecuteCommand(): void
467+
{
468+
$cache = new DbCache($this->db, 'noExist');
469+
$this->expectException(CacheException::class);
470+
$cache->setMultiple(['key-1' => 'value-1', 'key-2' => 'value-2']);
471+
}
472+
473+
public function testGetDataEmptyKey(): void
474+
{
475+
$getData = $this->invokeMethod($this->dbCache, 'getData', ['', [], 'all']);
476+
$this->assertFalse($getData);
477+
478+
$getData = $this->invokeMethod($this->dbCache, 'getData', [[], [], 'all']);
479+
$this->assertSame([], $getData);
480+
}
481+
482+
public function testDeleteDataEmptyKey(): void
483+
{
484+
$getData = $this->invokeMethod($this->dbCache, 'deleteData', ['', [], 'all']);
485+
$this->assertNull($getData);
486+
}
487+
443488
private function getDataProviderData(): array
444489
{
445490
$data = [];

tests/TestCase.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace Yiisoft\Cache\Db\Tests;
66

77
use PHPUnit\Framework\TestCase as AbstractTestCase;
8+
use ReflectionException;
9+
use ReflectionObject;
810
use Yiisoft\Cache\Db\DbCache;
911
use Yiisoft\Cache\Db\Migration\M202101140204CreateCache;
1012
use Yiisoft\Db\Connection\ConnectionInterface;
@@ -30,4 +32,23 @@ protected function createMigrationBuilder(): MigrationBuilder
3032
{
3133
return new MigrationBuilder($this->db, new NullMigrationInformer());
3234
}
35+
36+
/**
37+
* Invokes an inaccessible method.
38+
*
39+
* @param object $object The object to invoke the method on.
40+
* @param string $method The name of the method to invoke.
41+
* @param array $args The arguments to pass to the method.
42+
*
43+
* @throws ReflectionException
44+
*
45+
* @return mixed
46+
*/
47+
protected function invokeMethod(object $object, string $method, array $args = []): mixed
48+
{
49+
$reflection = new ReflectionObject($object);
50+
$method = $reflection->getMethod($method);
51+
$method->setAccessible(true);
52+
return $method->invokeArgs($object, $args);
53+
}
3354
}

0 commit comments

Comments
 (0)