Completed
Push — devel ( b03b2f...7508db )
by Philippe
05:13
created

ClientService::addUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * ClientService.php
4
 *
5
 * PHP version 5.6+
6
 *
7
 * @author Philippe Gaultier <[email protected]>
8
 * @copyright 2010-2016 Philippe Gaultier
9
 * @license http://www.sweelix.net/license license
10
 * @version XXX
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\ClientModelInterface;
20
use sweelix\oauth2\server\interfaces\ClientServiceInterface;
21
use yii\db\Exception as DatabaseException;
22
use Yii;
23
24
/**
25
 * This is the client service for redis
26
 *  database structure
27
 *    * oauth2:clients:<cid> : hash (Client)
28
 *    * oauth2:clients:<cid>:users : set
29
 *
30
 * @author Philippe Gaultier <[email protected]>
31
 * @copyright 2010-2016 Philippe Gaultier
32
 * @license http://www.sweelix.net/license license
33
 * @version XXX
34
 * @link http://www.sweelix.net
35
 * @package sweelix\oauth2\server\services\redis
36
 * @since XXX
37
 */
38
class ClientService extends BaseService implements ClientServiceInterface
39
{
40
41
    /**
42
     * @param string $cid client ID
43
     * @return string client Key
44
     * @since XXX
45
     */
46 5
    protected function getClientKey($cid)
47
    {
48 5
        return $this->namespace . ':' . $cid;
49
    }
50
51
    /**
52
     * @param string $cid client ID
53
     * @return string clientUsers Key
54
     * @since XXX
55
     */
56 1
    protected function getClientUsersListKey($cid)
57
    {
58 1
        return $this->namespace . ':' . $cid . ':users';
59
    }
60
61
    /**
62
     * @inheritdoc
63
     */
64 5
    public function save(ClientModelInterface $client, $attributes)
65
    {
66 5
        if ($client->getIsNewRecord()) {
67 5
            $result = $this->insert($client, $attributes);
68 5
        } else {
69 2
            $result = $this->update($client, $attributes);
70
        }
71 5
        return $result;
72
    }
73
74
    /**
75
     * Save Client
76
     * @param ClientModelInterface $client
77
     * @param null|array $attributes attributes to save
78
     * @return bool
79
     * @throws DatabaseException
80
     * @throws DuplicateIndexException
81
     * @throws DuplicateKeyException
82
     * @since XXX
83
     */
84 5
    protected function insert(ClientModelInterface $client, $attributes)
85
    {
86 5
        $result = false;
87 5
        if (!$client->beforeSave(true)) {
88
            return $result;
89
        }
90 5
        $clientKey = $this->getClientKey($client->getKey());
91
        //check if record exists
92 5
        $entityStatus = (int)$this->db->executeCommand('EXISTS', [$clientKey]);
93 5
        if ($entityStatus === 1) {
94 1
            throw new DuplicateKeyException('Duplicate key "'.$clientKey.'"');
95
        }
96
97 5
        $values = $client->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 84 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...
98 5
        $redisParameters = [$clientKey];
99 5
        $this->setAttributesDefinitions($client->attributesDefinition());
100 5
        foreach ($values as $key => $value)
101
        {
102 5
            if ($value !== null) {
103 5
                $redisParameters[] = $key;
104 5
                $redisParameters[] = $this->convertToDatabase($key, $value);
105 5
            }
106 5
        }
107
        //TODO: use EXEC/MULTI to avoid errors
108 5
        $transaction = $this->db->executeCommand('MULTI');
109 5
        if ($transaction === true) {
110
            try {
111 5
                $this->db->executeCommand('HMSET', $redisParameters);
112 5
                $this->db->executeCommand('EXEC');
113 5
            } catch (DatabaseException $e) {
114
                // @codeCoverageIgnoreStart
115
                // we have a REDIS exception, we should not discard
116
                Yii::trace('Error while inserting entity', __METHOD__);
117
                throw $e;
118
                // @codeCoverageIgnoreEnd
119
            }
120 5
        }
121 5
        $changedAttributes = array_fill_keys(array_keys($values), null);
122 5
        $client->setOldAttributes($values);
123 5
        $client->afterSave(true, $changedAttributes);
124 5
        $result = true;
125 5
        return $result;
126
    }
127
128
129
    /**
130
     * Update Client
131
     * @param ClientModelInterface $client
132
     * @param null|array $attributes attributes to save
133
     * @return bool
134
     * @throws DatabaseException
135
     * @throws DuplicateIndexException
136
     * @throws DuplicateKeyException
137
     */
138 2
    protected function update(ClientModelInterface $client, $attributes)
139
    {
140 2
        if (!$client->beforeSave(false)) {
141
            return false;
142
        }
143
144 2
        $values = $client->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 138 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...
145 2
        $modelKey = $client->key();
146 2
        $clientId = isset($values[$modelKey]) ? $values[$modelKey] : $client->getKey();
147 2
        $clientKey = $this->getClientKey($clientId);
148
149
150 2
        if (isset($values[$modelKey]) === true) {
151 1
            $newClientKey = $this->getClientKey($values[$modelKey]);
152 1
            $entityStatus = (int)$this->db->executeCommand('EXISTS', [$newClientKey]);
153 1
            if ($entityStatus === 1) {
154 1
                throw new DuplicateKeyException('Duplicate key "'.$newClientKey.'"');
155
            }
156 1
        }
157
158 2
        $this->db->executeCommand('MULTI');
159
        try {
160 2
            if (array_key_exists($modelKey, $values) === true) {
161 1
                $oldId = $client->getOldKey();
162 1
                $oldClientKey = $this->getClientKey($oldId);
163
164 1
                $this->db->executeCommand('RENAMENX', [$oldClientKey, $clientKey]);
165 1
            }
166
167 2
            $redisUpdateParameters = [$clientKey];
168 2
            $redisDeleteParameters = [$clientKey];
169 2
            $this->setAttributesDefinitions($client->attributesDefinition());
170 2
            foreach ($values as $key => $value)
171
            {
172 2
                if ($value === null) {
173 1
                    $redisDeleteParameters[] = $key;
174 1
                } else {
175 2
                    $redisUpdateParameters[] = $key;
176 2
                    $redisUpdateParameters[] = $this->convertToDatabase($key, $value);
177
                }
178 2
            }
179 2
            if (count($redisDeleteParameters) > 1) {
180 1
                $this->db->executeCommand('HDEL', $redisDeleteParameters);
181 1
            }
182 2
            if (count($redisUpdateParameters) > 1) {
183 2
                $this->db->executeCommand('HMSET', $redisUpdateParameters);
184 2
            }
185
186 2
            $this->db->executeCommand('EXEC');
187 2
        } catch (DatabaseException $e) {
188
            // @codeCoverageIgnoreStart
189
            // we have a REDIS exception, we should not discard
190
            Yii::trace('Error while updating entity', __METHOD__);
191
            throw $e;
192
            // @codeCoverageIgnoreEnd
193
        }
194
195 2
        $changedAttributes = [];
196 2
        foreach ($values as $name => $value) {
197 2
            $oldAttributes = $client->getOldAttributes();
198 2
            $changedAttributes[$name] = isset($oldAttributes[$name]) ? $oldAttributes[$name] : null;
199 2
            $client->setOldAttribute($name, $value);
200 2
        }
201 2
        $client->afterSave(false, $changedAttributes);
202 2
        return true;
203
    }
204
205
    /**
206
     * @inheritdoc
207
     */
208 4
    public function findOne($key)
209
    {
210 4
        $record = null;
211 4
        $clientKey = $this->getClientKey($key);
212 4
        $clientExists = (bool)$this->db->executeCommand('EXISTS', [$clientKey]);
213 4
        if ($clientExists === true) {
214 3
            $clientData = $this->db->executeCommand('HGETALL', [$clientKey]);
215 3
            $record = Yii::createObject('sweelix\oauth2\server\interfaces\ClientModelInterface');
216
            /** @var ClientModelInterface $record */
217 3
            $properties = $record->attributesDefinition();
218 3
            $this->setAttributesDefinitions($properties);
219 3
            $attributes = [];
220 3
            for ($i = 0; $i < count($clientData); $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...
221 3
                if (isset($properties[$clientData[$i]]) === true) {
222 3
                    $clientData[$i + 1] = $this->convertToModel($clientData[$i], $clientData[($i + 1)]);
223 3
                    $record->setAttribute($clientData[$i], $clientData[$i + 1]);
224 3
                    $attributes[$clientData[$i]] = $clientData[$i + 1];
225
                // @codeCoverageIgnoreStart
226
                } elseif ($record->canSetProperty($clientData[$i])) {
227
                    // TODO: find a way to test attribute population
228
                    $record->{$clientData[$i]} = $clientData[$i + 1];
229
                }
230
                // @codeCoverageIgnoreEnd
231 3
            }
232 3
            if (empty($attributes) === false) {
233 3
                $record->setOldAttributes($attributes);
234 3
            }
235 3
            $record->afterFind();
236 3
        }
237 4
        return $record;
238
    }
239
240
    /**
241
     * @inheritdoc
242
     */
243 1
    public function delete(ClientModelInterface $client)
244
    {
245 1
        $result = false;
246 1
        if ($client->beforeDelete()) {
247 1
            $this->db->executeCommand('MULTI');
248 1
            $id = $client->getOldKey();
249 1
            $clientKey = $this->getClientKey($id);
250
251 1
            $this->db->executeCommand('DEL', [$clientKey]);
252
            //TODO: check results to return correct information
253 1
            $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...
254 1
            $client->setIsNewRecord(true);
255 1
            $client->afterDelete();
256 1
            $result = true;
257 1
        }
258 1
        return $result;
259
    }
260
261
    /**
262
     * @inheritdoc
263
     */
264 1
    public function hasUser(ClientModelInterface $client, $userId)
265
    {
266 1
        $key = $client->getKey();
267 1
        $clientUsersListKey = $this->getClientUsersListKey($key);
268 1
        return (bool)$this->db->executeCommand('SISMEMBER', [$clientUsersListKey, $userId]);
269
    }
270
271
    /**
272
     * @inheritdoc
273
     */
274 1
    public function addUser(ClientModelInterface $client, $userId)
275
    {
276 1
        $key = $client->getKey();
277 1
        $clientUsersListKey = $this->getClientUsersListKey($key);
278 1
        $this->db->executeCommand('SADD', [$clientUsersListKey, $userId]);
279
        //TODO: check if we should send back false or not
280 1
        return true;
281
    }
282
283
    /**
284
     * @inheritdoc
285
     */
286 1
    public function removeUser(ClientModelInterface $client, $userId)
287
    {
288 1
        $key = $client->getKey();
289 1
        $clientUsersListKey = $this->getClientUsersListKey($key);
290 1
        $this->db->executeCommand('SREM', [$clientUsersListKey, $userId]);
291
        //TODO: check if we should send back false or not
292 1
        return true;
293
    }
294
295
}
296