Test Failed
Push — master ( c65274...a98fd4 )
by Alexander
18:33
created

TokenRepository::pull()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 7
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Wearesho\Yii\Repositories;
6
7
use Wearesho\Yii\Exceptions\DeliveryLimitReachedException;
8
use Wearesho\Yii\Exceptions\InvalidRecipientException;
9
use Wearesho\Yii\Exceptions\InvalidTokenException;
10
11
use Wearesho\Yii\Interfaces\TokenableEntityInterface;
12
use Wearesho\Yii\Interfaces\TokenGeneratorInterface;
13
use Wearesho\Yii\Interfaces\TokenInterface;
14
use Wearesho\Yii\Interfaces\TokenRecordInterface;
15
use Wearesho\Yii\Interfaces\TokenRepositoryInterface;
16
use Wearesho\Yii\Interfaces\TokenRepositoryConfigInterface;
17
18
use Horat1us\Yii\Validation;
19
use Wearesho\Delivery;
20
21
class TokenRepository implements TokenRepositoryInterface
22
{
23
    protected TokenRecordInterface $model;
24
25
    protected TokenRepositoryConfigInterface $config;
26
27
    protected TokenGeneratorInterface $generator;
28
29
    protected Delivery\ServiceInterface $deliveryService;
30
31
    /**
32
     * TokensRepository constructor.
33
     * @param TokenRecordInterface|null $model
34
     * @param TokenRepositoryConfigInterface $config
35
     * @param TokenGeneratorInterface $generator
36
     * @param Delivery\ServiceInterface $service
37
     */
38
    public function __construct(
39
        TokenRecordInterface           $model,
40
        TokenRepositoryConfigInterface $config,
41
        TokenGeneratorInterface        $generator,
42
        Delivery\ServiceInterface      $service
43
    ) {
44
        $this->model = $model;
45 13
        $this->generator = $generator;
46
        $this->config = $config;
47
        $this->deliveryService = $service;
48
    }
49
50
    /**
51 13
     * Creating token (for example, when we receive first-stage data)
52 13
     * Will increase sending counter
53 13
     *
54 13
     * @param TokenableEntityInterface $entity
55 13
     * @return TokenInterface|TokenRecordInterface
56
     * @throws Validation\Failure
57
     */
58
    public function push(TokenableEntityInterface $entity): TokenInterface
59
    {
60
        $record = $this->pull($entity->getTokenRecipient());
61
        if (!$record instanceof TokenRecordInterface) {
0 ignored issues
show
introduced by
$record is always a sub-type of Wearesho\Yii\Interfaces\TokenRecordInterface.
Loading history...
62
            $class = get_class($this->model);
63
            /** @var TokenRecordInterface $record */
64
            $record = new $class;
65 6
66
            $record->setRecipient($entity->getTokenRecipient());
67 6
            $record->setToken($this->generator->getToken());
68 6
        }
69 6
70
        $record->setData($entity->getTokenData());
71 6
        Validation\Exception::saveOrThrow($record);
72
73 6
        return $record;
74 6
    }
75
76
    /**
77 6
     * Creating and sending token
78 6
     * Internal should use push() method
79
     *
80 6
     * @param TokenableEntityInterface $entity
81
     * @throws DeliveryLimitReachedException
82
     * @throws Delivery\Exception
83
     * @throws Validation\Failure
84
     * @todo: preventing two write operations (update+update, insert+update)
85
     *
86
     */
87
    public function send(TokenableEntityInterface $entity): void
88
    {
89
        $token = $this->push($entity);
90
        if ($token->getDeliveryCount() >= $this->config->getDeliveryLimit()) {
91
            throw new DeliveryLimitReachedException(
92
                $token->getDeliveryCount(),
93
                $this->config->getExpirePeriod()
94 1
            );
95
        }
96 1
97 1
        $this->deliveryService->send($token);
98 1
99 1
        if ($token instanceof TokenRecordInterface) {
100 1
            $token->increaseDeliveryCount();
101
            Validation\Exception::saveOrThrow($token);
102
        }
103
    }
104 1
105
    /**
106 1
     * Pulling active token to process it (for example, sending sms)
107 1
     */
108 1
    public function pull(string $tokenRecipient): ?TokenRecordInterface
109
    {
110 1
        /** @noinspection PhpIncompatibleReturnTypeInspection */
111
        return $this->model->find()
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->model->fin...$tokenRecipient)->one() could return the type array which is incompatible with the type-hinted return Wearesho\Yii\Interfaces\TokenRecordInterface|null. Consider adding an additional type-check to rule them out.
Loading history...
112
            ->notExpired($this->config->getExpirePeriod())
113
            ->whereRecipient($tokenRecipient)
114
            ->one();
115
    }
116
117
    /**
118 12
     * Will return token with data to process registration if token valid
119
     * Or throw one of exceptions
120 12
     * Should delete token from storage if token valid to prevent double validation for single token
121 12
     *
122 12
     * @return TokenInterface
123 12
     * @throws DeliveryLimitReachedException
124
     * @throws InvalidRecipientException
125
     * @throws InvalidTokenException
126
     * @throws Validation\Failure
127
     */
128
    public function verify(string $tokenRecipient, string $token): TokenInterface
129
    {
130
        $record = $this->pull($tokenRecipient);
131
132
        if (!$record instanceof TokenRecordInterface) {
0 ignored issues
show
introduced by
$record is always a sub-type of Wearesho\Yii\Interfaces\TokenRecordInterface.
Loading history...
133
            throw new InvalidRecipientException($tokenRecipient);
134
        }
135
136
        $record->increaseVerifyCount();
137
138
        Validation\Exception::saveOrThrow($record);
139 7
140
        if ($this->config->getVerifyLimit() < $record->getVerifyCount()) {
141 7
            throw new DeliveryLimitReachedException(
142
                $record->getVerifyCount(),
143 7
                $this->config->getExpirePeriod()
144 2
            );
145
        }
146
147 5
        if ($record->getToken() !== $token) {
148
            throw new InvalidTokenException($token);
149 5
        }
150
151 5
        $record->delete();
152
153
        return $record;
154
    }
155
}
156