Skip to content

Commit 52e5fba

Browse files
committed
Refactor CacheStorage and DatabaseStorage to support nullable store values and improve configuration handling
1 parent d6aca64 commit 52e5fba

File tree

8 files changed

+96
-81
lines changed

8 files changed

+96
-81
lines changed

EXAMPLE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ SIMPLE_OTP_DB_CONNECTION=mysql
542542
// For simple applications, use Cache:
543543
// .env
544544
SIMPLE_OTP_STORAGE=cache
545-
SIMPLE_OTP_CACHE_STORE=default
545+
SIMPLE_OTP_CACHE_STORE=null
546546
```
547547

548548
## Example 9: Scheduled Cleanup

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ return [
5353
'drivers' => [
5454
'cache' => [
5555
'driver' => 'cache',
56-
'store' => env('SIMPLE_OTP_CACHE_STORE', 'default'),
56+
'store' => env('SIMPLE_OTP_CACHE_STORE', null), // null means use default cache store
5757
],
5858

5959
'database' => [
@@ -142,7 +142,7 @@ Add these variables to your `.env` file:
142142
```env
143143
# Storage Configuration
144144
SIMPLE_OTP_STORAGE=cache
145-
SIMPLE_OTP_CACHE_STORE=default
145+
SIMPLE_OTP_CACHE_STORE=null
146146
SIMPLE_OTP_DB_CONNECTION=mysql
147147
SIMPLE_OTP_REDIS_CONNECTION=default
148148
SIMPLE_OTP_REDIS_PREFIX=simple_otp:

composer.json

Lines changed: 59 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,62 @@
11
{
2-
"name": "aslnbxrz/simple-otp",
3-
"description": "A simple and flexible OTP (One-Time Password) management package for Laravel with multiple storage drivers and delivery methods",
4-
"type": "library",
5-
"keywords": ["laravel", "otp", "sms", "email", "verification", "authentication"],
6-
"license": "MIT",
7-
"authors": [
8-
{
9-
"name": "Aslnbxrz",
10-
"email": "aslnbxrz@example.com"
11-
}
12-
],
13-
"require": {
14-
"php": "^8.1",
15-
"illuminate/support": "^10.0|^11.0",
16-
"illuminate/database": "^10.0|^11.0",
17-
"illuminate/cache": "^10.0|^11.0",
18-
"illuminate/mail": "^10.0|^11.0",
19-
"illuminate/notifications": "^10.0|^11.0"
20-
},
21-
"require-dev": {
22-
"phpunit/phpunit": "^10.0",
23-
"orchestra/testbench": "^8.0",
24-
"mockery/mockery": "^1.4"
25-
},
26-
"autoload": {
27-
"psr-4": {
28-
"Aslnbxrz\\SimpleOTP\\": "src/"
29-
}
30-
},
31-
"autoload-dev": {
32-
"psr-4": {
33-
"Aslnbxrz\\SimpleOTP\\Tests\\": "tests/"
34-
}
35-
},
36-
"extra": {
37-
"laravel": {
38-
"providers": [
39-
"Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider"
40-
],
41-
"aliases": {
42-
"SimpleOTP": "Aslnbxrz\\SimpleOTP\\Facades\\SimpleOTP"
43-
}
44-
}
45-
},
46-
"minimum-stability": "dev",
47-
"prefer-stable": true,
48-
"scripts": {
49-
"test": "phpunit",
50-
"test-coverage": "phpunit --coverage-html coverage"
51-
},
52-
"config": {
53-
"sort-packages": true
2+
"name": "aslnbxrz/simple-otp",
3+
"description": "A simple and flexible OTP (One-Time Password) management package for Laravel with multiple storage drivers and delivery methods",
4+
"type": "library",
5+
"keywords": [
6+
"laravel",
7+
"otp",
8+
"sms",
9+
"email",
10+
"verification",
11+
"authentication"
12+
],
13+
"license": "MIT",
14+
"authors": [
15+
{
16+
"name": "Aslnbxrz",
17+
"email": "aslnbxrz@example.com"
5418
}
19+
],
20+
"require": {
21+
"php": "^8.1",
22+
"illuminate/support": "^10.0|^11.0|^12.0",
23+
"illuminate/database": "^10.0|^11.0|^12.0",
24+
"illuminate/cache": "^10.0|^11.0|^12.0",
25+
"illuminate/mail": "^10.0|^11.0|^12.0",
26+
"illuminate/notifications": "^10.0|^11.0|^12.0"
27+
},
28+
"require-dev": {
29+
"phpunit/phpunit": "^10.0",
30+
"orchestra/testbench": "^8.0",
31+
"mockery/mockery": "^1.4"
32+
},
33+
"autoload": {
34+
"psr-4": {
35+
"Aslnbxrz\\SimpleOTP\\": "src/"
36+
}
37+
},
38+
"autoload-dev": {
39+
"psr-4": {
40+
"Aslnbxrz\\SimpleOTP\\Tests\\": "tests/"
41+
}
42+
},
43+
"extra": {
44+
"laravel": {
45+
"providers": [
46+
"Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider"
47+
],
48+
"aliases": {
49+
"SimpleOTP": "Aslnbxrz\\SimpleOTP\\Facades\\SimpleOTP"
50+
}
51+
}
52+
},
53+
"minimum-stability": "dev",
54+
"prefer-stable": true,
55+
"scripts": {
56+
"test": "phpunit",
57+
"test-coverage": "phpunit --coverage-html coverage"
58+
},
59+
"config": {
60+
"sort-packages": true
61+
}
5562
}

config/simple-otp.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
| Supported: "cache", "database", "redis"
1414
|
1515
*/
16-
'default' => env('SIMPLE_OTP_STORAGE', 'cache'),
16+
'default' => env('SIMPLE_OTP_STORAGE', 'database'),
1717

1818
/*
1919
|--------------------------------------------------------------------------
@@ -27,7 +27,7 @@
2727
'drivers' => [
2828
'cache' => [
2929
'driver' => 'cache',
30-
'store' => env('SIMPLE_OTP_CACHE_STORE', 'default'),
30+
'store' => env('SIMPLE_OTP_CACHE_STORE', null), // null means use default cache store
3131
],
3232

3333
'database' => [

src/OTPService.php

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ public function generate(string $identifier, string $recipient, array $options =
4040
'options' => $options,
4141
];
4242

43-
if (!$this->storage->store($identifier, $code, $this->config['ttl'], $metadata)) {
43+
$ttlMinutes = $this->config['otp']['ttl'] ?? 5;
44+
$storeResult = $this->storage->store($identifier, $code, $ttlMinutes, $metadata);
45+
46+
if (!$storeResult) {
4447
throw new OTPException('Failed to store OTP code');
4548
}
4649

@@ -60,7 +63,7 @@ public function generate(string $identifier, string $recipient, array $options =
6063
'success' => true,
6164
'message' => $this->getMessage('sent'),
6265
'identifier' => $identifier,
63-
'expires_at' => now()->addMinutes($this->config['ttl']),
66+
'expires_at' => now()->addMinutes($ttlMinutes),
6467
];
6568
}
6669

@@ -78,7 +81,8 @@ public function verify(string $identifier, string $code): bool
7881
}
7982

8083
// Check max attempts
81-
if ($otpData['attempts'] >= $this->config['max_attempts']) {
84+
$maxAttempts = $this->config['otp']['max_attempts'] ?? 3;
85+
if ($otpData['attempts'] >= $maxAttempts) {
8286
$this->storage->delete($identifier);
8387
throw new OTPException($this->getMessage('max_attempts'));
8488
}
@@ -145,8 +149,8 @@ public function delete(string $identifier): bool
145149

146150
protected function generateCode(): string
147151
{
148-
$type = $this->config['type'];
149-
$length = $this->config['length'];
152+
$type = $this->config['otp']['type'] ?? 'numeric';
153+
$length = $this->config['otp']['length'] ?? 6;
150154

151155
switch ($type) {
152156
case 'numeric':
@@ -193,23 +197,25 @@ protected function generateAlphaCode(int $length): string
193197

194198
protected function isRateLimited(string $identifier): bool
195199
{
196-
if (!$this->config['rate_limit']['enabled']) {
200+
$rateLimit = $this->config['otp']['rate_limit'] ?? ['enabled' => false];
201+
if (!($rateLimit['enabled'] ?? false)) {
197202
return false;
198203
}
199204

200205
$key = "simple_otp_rate_limit:{$identifier}";
201-
202-
return RateLimiter::tooManyAttempts($key, $this->config['rate_limit']['max_attempts']);
206+
$max = $rateLimit['max_attempts'] ?? 5;
207+
return RateLimiter::tooManyAttempts($key, $max);
203208
}
204209

205210
protected function incrementRateLimit(string $identifier): void
206211
{
207-
if (!$this->config['rate_limit']['enabled']) {
212+
$rateLimit = $this->config['otp']['rate_limit'] ?? ['enabled' => false];
213+
if (!($rateLimit['enabled'] ?? false)) {
208214
return;
209215
}
210216

211217
$key = "simple_otp_rate_limit:{$identifier}";
212-
$decayMinutes = $this->config['rate_limit']['decay_minutes'];
218+
$decayMinutes = $rateLimit['decay_minutes'] ?? 60;
213219

214220
RateLimiter::hit($key, $decayMinutes * 60);
215221
}

src/SimpleOTPServiceProvider.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ protected function registerStorageDriver(): void
5959

6060
switch ($driver) {
6161
case 'cache':
62-
$store = $app['cache']->store($driverConfig['store']);
63-
return new CacheStorage($store, $driverConfig['store']);
62+
$storeName = $driverConfig['store'];
63+
$store = $storeName ? $app['cache']->store($storeName) : $app['cache']->store();
64+
return new CacheStorage($store, $storeName);
6465

6566
case 'database':
6667
$connection = $driverConfig['connection']

src/Storage/CacheStorage.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
class CacheStorage implements OTPStorageContract
99
{
1010
protected Repository $cache;
11-
protected string $store;
11+
protected ?string $store;
1212

13-
public function __construct(Repository $cache, string $store = 'default')
13+
public function __construct(Repository $cache, ?string $store = null)
1414
{
1515
$this->cache = $cache;
1616
$this->store = $store;
@@ -81,6 +81,7 @@ public function cleanup(): int
8181

8282
protected function getKey(string $identifier): string
8383
{
84-
return "simple_otp:{$this->store}:{$identifier}";
84+
$storePrefix = $this->store ? "{$this->store}:" : '';
85+
return "simple_otp:{$storePrefix}{$identifier}";
8586
}
8687
}

src/Storage/DatabaseStorage.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace Aslnbxrz\SimpleOTP\Storage;
44

5+
use Aslnbxrz\SimpleOTP\Contracts\OTPStorageContract;
56
use Illuminate\Database\ConnectionInterface;
67
use Illuminate\Support\Facades\DB;
7-
use Aslnbxrz\SimpleOTP\Contracts\OTPStorageContract;
88

99
class DatabaseStorage implements OTPStorageContract
1010
{
@@ -20,7 +20,7 @@ public function __construct(ConnectionInterface $connection, string $table = 'si
2020
public function store(string $identifier, string $code, int $ttl, array $metadata = []): bool
2121
{
2222
$now = now();
23-
23+
2424
$data = [
2525
'identifier' => $identifier,
2626
'code' => $code,
@@ -50,15 +50,15 @@ public function get(string $identifier): ?array
5050
'created_at' => $record->created_at->timestamp,
5151
'expires_at' => $record->expires_at->timestamp,
5252
'attempts' => $record->attempts,
53-
'verified' => (bool) $record->verified,
53+
'verified' => (bool)$record->verified,
5454
'metadata' => json_decode($record->metadata, true) ?: [],
5555
];
5656
}
5757

5858
public function update(string $identifier, array $metadata): bool
5959
{
6060
$updateData = [];
61-
61+
6262
foreach ($metadata as $key => $value) {
6363
if (in_array($key, ['attempts', 'verified'])) {
6464
$updateData[$key] = $value;
@@ -67,7 +67,7 @@ public function update(string $identifier, array $metadata): bool
6767
$existingRecord = $this->connection->table($this->table)
6868
->where('identifier', $identifier)
6969
->first();
70-
70+
7171
if ($existingRecord) {
7272
$existingMetadata = json_decode($existingRecord->metadata, true) ?: [];
7373
$existingMetadata[$key] = $value;
@@ -81,15 +81,15 @@ public function update(string $identifier, array $metadata): bool
8181
}
8282

8383
return $this->connection->table($this->table)
84-
->where('identifier', $identifier)
85-
->update($updateData) > 0;
84+
->where('identifier', $identifier)
85+
->update($updateData) > 0;
8686
}
8787

8888
public function delete(string $identifier): bool
8989
{
9090
return $this->connection->table($this->table)
91-
->where('identifier', $identifier)
92-
->delete() > 0;
91+
->where('identifier', $identifier)
92+
->delete() > 0;
9393
}
9494

9595
public function cleanup(): int

0 commit comments

Comments
 (0)