Completed
Push — devel ( ecbcb2...eb0e77 )
by Philippe
04:34 queued 02:05
created

AuthCodeService::findOne()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 31
c 0
b 0
f 0
ccs 22
cts 22
cp 1
rs 8.439
cc 6
eloc 21
nc 3
nop 1
crap 6
1
<?php
2
/**
3
 * AuthCodeService.php
4
 *
5
 * PHP version 5.6+
6
 *
7
 * @author Philippe Gaultier <[email protected]>
8
 * @copyright 2010-2017 Philippe Gaultier
9
 * @license http://www.sweelix.net/license license
10
 * @version 1.1.0
11
 * @link http://www.sweelix.net
12
 * @package sweelix\oauth2\server\services\redis
13
 */
14
15
namespace sweelix\oauth2\server\services\redis;
16
17
use sweelix\oauth2\server\exceptions\DuplicateIndexException;
18
use sweelix\oauth2\server\exceptions\DuplicateKeyException;
19
use sweelix\oauth2\server\interfaces\AuthCodeModelInterface;
20
use sweelix\oauth2\server\interfaces\AuthCodeServiceInterface;
21
use yii\db\Exception as DatabaseException;
22
use Yii;
23
24
/**
25
 * This is the auth code service for redis
26
 *  database structure
27
 *    * oauth2:authCodes:<aid> : hash (AuthCode)
28
 *
29
 * @author Philippe Gaultier <[email protected]>
30
 * @copyright 2010-2017 Philippe Gaultier
31
 * @license http://www.sweelix.net/license license
32
 * @version 1.1.0
33
 * @link http://www.sweelix.net
34
 * @package sweelix\oauth2\server\services\redis
35
 * @since 1.0.0
36
 */
37
class AuthCodeService extends BaseService implements AuthCodeServiceInterface
38
{
39
40
    /**
41
     * @param string $aid auth code ID
42
     * @return string auth code Key
43
     * @since 1.0.0
44
     */
45 5
    protected function getAuthCodeKey($aid)
46
    {
47 5
        return $this->namespace . ':' . $aid;
48
    }
49
50
    /**
51
     * @inheritdoc
52
     */
53 5
    public function save(AuthCodeModelInterface $authCode, $attributes)
54
    {
55 5
        if ($authCode->getIsNewRecord()) {
56 5
            $result = $this->insert($authCode, $attributes);
57 5
        } else {
58 1
            $result = $this->update($authCode, $attributes);
59
        }
60 5
        return $result;
61
    }
62
63
    /**
64
     * Save Auth Code
65
     * @param AuthCodeModelInterface $authCode
66
     * @param null|array $attributes attributes to save
67
     * @return bool
68
     * @throws DatabaseException
69
     * @throws DuplicateIndexException
70
     * @throws DuplicateKeyException
71
     * @since 1.0.0
72
     */
73 5
    protected function insert(AuthCodeModelInterface $authCode, $attributes)
74
    {
75 5
        $result = false;
76 5
        if (!$authCode->beforeSave(true)) {
77
            return $result;
78
        }
79 5
        $authCodeKey = $this->getAuthCodeKey($authCode->getKey());
80
        //check if record exists
81 5
        $entityStatus = (int)$this->db->executeCommand('EXISTS', [$authCodeKey]);
82 5
        if ($entityStatus === 1) {
83 1
            throw new DuplicateKeyException('Duplicate key "'.$authCodeKey.'"');
84
        }
85
86 5
        $values = $authCode->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 73 can also be of type array; however, sweelix\oauth2\server\in...e::getDirtyAttributes() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
87 5
        $redisParameters = [$authCodeKey];
88 5
        $this->setAttributesDefinitions($authCode->attributesDefinition());
89 5
        $expire = null;
90 5
        foreach ($values as $key => $value)
91
        {
92 5
            if ($key === 'expiry') {
93 5
                $expire = $value;
94 5
            }
95 5
            if ($value !== null) {
96 5
                $redisParameters[] = $key;
97 5
                $redisParameters[] = $this->convertToDatabase($key, $value);
98 5
            }
99 5
        }
100
        //TODO: use EXEC/MULTI to avoid errors
101 5
        $transaction = $this->db->executeCommand('MULTI');
102 5
        if ($transaction === true) {
103
            try {
104 5
                $this->db->executeCommand('HMSET', $redisParameters);
105 5
                if ($expire !== null) {
106 5
                    $realData = date('Y-m-d H:i:s');
0 ignored issues
show
Unused Code introduced by
$realData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
107 5
                    $expireData = date('Y-m-d H:i:s', $expire);
0 ignored issues
show
Unused Code introduced by
$expireData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108 5
                    $this->db->executeCommand('EXPIREAT', [$authCodeKey, $expire]);
109 5
                }
110 5
                $this->db->executeCommand('EXEC');
111 5
            } catch (DatabaseException $e) {
112
                // @codeCoverageIgnoreStart
113
                // we have a REDIS exception, we should not discard
114
                Yii::trace('Error while inserting entity', __METHOD__);
115
                throw $e;
116
                // @codeCoverageIgnoreEnd
117
            }
118 5
        }
119 5
        $changedAttributes = array_fill_keys(array_keys($values), null);
120 5
        $authCode->setOldAttributes($values);
121 5
        $authCode->afterSave(true, $changedAttributes);
122 5
        $result = true;
123 5
        return $result;
124
    }
125
126
127
    /**
128
     * Update Auth Code
129
     * @param AuthCodeModelInterface $authCode
130
     * @param null|array $attributes attributes to save
131
     * @return bool
132
     * @throws DatabaseException
133
     * @throws DuplicateIndexException
134
     * @throws DuplicateKeyException
135
     */
136 1
    protected function update(AuthCodeModelInterface $authCode, $attributes)
137
    {
138 1
        if (!$authCode->beforeSave(false)) {
139
            return false;
140
        }
141
142 1
        $values = $authCode->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 136 can also be of type array; however, sweelix\oauth2\server\in...e::getDirtyAttributes() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
143 1
        $modelKey = $authCode->key();
144 1
        $authCodeId = isset($values[$modelKey]) ? $values[$modelKey] : $authCode->getKey();
145 1
        $authCodeKey = $this->getAuthCodeKey($authCodeId);
146
147
148 1
        if (isset($values[$modelKey]) === true) {
149 1
            $newAuthCodeKey = $this->getAuthCodeKey($values[$modelKey]);
150 1
            $entityStatus = (int)$this->db->executeCommand('EXISTS', [$newAuthCodeKey]);
151 1
            if ($entityStatus === 1) {
152 1
                throw new DuplicateKeyException('Duplicate key "'.$newAuthCodeKey.'"');
153
            }
154 1
        }
155
156 1
        $this->db->executeCommand('MULTI');
157
        try {
158 1
            if (array_key_exists($modelKey, $values) === true) {
159 1
                $oldId = $authCode->getOldKey();
160 1
                $oldAuthCodeKey = $this->getAuthCodeKey($oldId);
161
162 1
                $this->db->executeCommand('RENAMENX', [$oldAuthCodeKey, $authCodeKey]);
163 1
            }
164
165 1
            $redisUpdateParameters = [$authCodeKey];
166 1
            $redisDeleteParameters = [$authCodeKey];
167 1
            $this->setAttributesDefinitions($authCode->attributesDefinition());
168 1
            $expire = null;
169 1
            foreach ($values as $key => $value)
170
            {
171 1
                if ($value === null) {
172 1
                    if ($key === 'expiry') {
173
                        $expire = false;
174
                    }
175 1
                    $redisDeleteParameters[] = $key;
176 1
                } else {
177 1
                    if (($key === 'expiry') && ($value > 0)) {
178
                        $expire = $value;
179
                    }
180 1
                    $redisUpdateParameters[] = $key;
181 1
                    $redisUpdateParameters[] = $this->convertToDatabase($key, $value);
182
                }
183 1
            }
184 1
            if (count($redisDeleteParameters) > 1) {
185 1
                $this->db->executeCommand('HDEL', $redisDeleteParameters);
186 1
            }
187 1
            if (count($redisUpdateParameters) > 1) {
188 1
                $this->db->executeCommand('HMSET', $redisUpdateParameters);
189 1
            }
190 1
            if ($expire === false) {
191
                $this->db->executeCommand('PERSIST', [$authCodeKey]);
192 1
            } elseif ($expire > 0) {
193
                $this->db->executeCommand('EXPIREAT', [$authCodeKey, $expire]);
194
            }
195
196 1
            $this->db->executeCommand('EXEC');
197 1
        } catch (DatabaseException $e) {
198
            // @codeCoverageIgnoreStart
199
            // we have a REDIS exception, we should not discard
200
            Yii::trace('Error while updating entity', __METHOD__);
201
            throw $e;
202
            // @codeCoverageIgnoreEnd
203
        }
204
205 1
        $changedAttributes = [];
206 1
        foreach ($values as $name => $value) {
207 1
            $oldAttributes = $authCode->getOldAttributes();
208 1
            $changedAttributes[$name] = isset($oldAttributes[$name]) ? $oldAttributes[$name] : null;
209 1
            $authCode->setOldAttribute($name, $value);
210 1
        }
211 1
        $authCode->afterSave(false, $changedAttributes);
212 1
        return true;
213
    }
214
215
    /**
216
     * @inheritdoc
217
     */
218 4
    public function findOne($key)
219
    {
220 4
        $record = null;
221 4
        $authCodeKey = $this->getAuthCodeKey($key);
222 4
        $authCodeExists = (bool)$this->db->executeCommand('EXISTS', [$authCodeKey]);
223 4
        if ($authCodeExists === true) {
224 4
            $authCodeData = $this->db->executeCommand('HGETALL', [$authCodeKey]);
225 4
            $record = Yii::createObject('sweelix\oauth2\server\interfaces\AuthCodeModelInterface');
226
            /** @var AuthCodeModelInterface $record */
227 4
            $properties = $record->attributesDefinition();
228 4
            $this->setAttributesDefinitions($properties);
229 4
            $attributes = [];
230 4
            for ($i = 0; $i < count($authCodeData); $i += 2) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
231 4
                if (isset($properties[$authCodeData[$i]]) === true) {
232 4
                    $authCodeData[$i + 1] = $this->convertToModel($authCodeData[$i], $authCodeData[($i + 1)]);
233 4
                    $record->setAttribute($authCodeData[$i], $authCodeData[$i + 1]);
234 4
                    $attributes[$authCodeData[$i]] = $authCodeData[$i + 1];
235
                // @codeCoverageIgnoreStart
236
                } elseif ($record->canSetProperty($authCodeData[$i])) {
237
                    // TODO: find a way to test attribute population
238
                    $record->{$authCodeData[$i]} = $authCodeData[$i + 1];
239
                }
240
                // @codeCoverageIgnoreEnd
241 4
            }
242 4
            if (empty($attributes) === false) {
243 4
                $record->setOldAttributes($attributes);
244 4
            }
245 4
            $record->afterFind();
246 4
        }
247 4
        return $record;
248
    }
249
250
    /**
251
     * @inheritdoc
252
     */
253 3
    public function delete(AuthCodeModelInterface $authCode)
254
    {
255 3
        $result = false;
256 3
        if ($authCode->beforeDelete()) {
257 3
            $this->db->executeCommand('MULTI');
258 3
            $id = $authCode->getOldKey();
259 3
            $authCodeKey = $this->getAuthCodeKey($id);
260
261 3
            $this->db->executeCommand('DEL', [$authCodeKey]);
262
            //TODO: check results to return correct information
263 3
            $queryResult = $this->db->executeCommand('EXEC');
0 ignored issues
show
Unused Code introduced by
$queryResult is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
264 3
            $authCode->setIsNewRecord(true);
265 3
            $authCode->afterDelete();
266 3
            $result = true;
267 3
        }
268 3
        return $result;
269
    }
270
271
}
272