Complex classes like RefreshTokenService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RefreshTokenService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class RefreshTokenService extends BaseService implements RefreshTokenServiceInterface |
||
41 | { |
||
42 | /** |
||
43 | * @var string sql refreshTokens table |
||
44 | */ |
||
45 | public $refreshTokensTable = null; |
||
46 | |||
47 | /** |
||
48 | * @var string sql scope refreshToken table |
||
49 | */ |
||
50 | public $scopeRefreshTokenTable = null; |
||
51 | |||
52 | /** |
||
53 | * Save Refresh Token |
||
54 | * @param RefreshTokenModelInterface $refreshToken |
||
55 | * @param null|array $attributes attributes to save |
||
56 | * @return bool |
||
57 | * @throws DatabaseException |
||
58 | * @throws DuplicateIndexException |
||
59 | * @throws DuplicateKeyException |
||
60 | * @since 1.0.0 |
||
61 | */ |
||
62 | protected function insert(RefreshTokenModelInterface $refreshToken, $attributes) |
||
63 | { |
||
64 | $result = false; |
||
65 | if (!$refreshToken->beforeSave(true)) { |
||
66 | return $result; |
||
67 | } |
||
68 | $refreshTokenKey = $refreshToken->getKey(); |
||
69 | $entity = (new Query()) |
||
70 | ->select('*') |
||
71 | ->from($this->refreshTokensTable) |
||
72 | ->where('id = :id', [':id' => $refreshTokenKey]) |
||
73 | ->one($this->db); |
||
74 | if ($entity !== false) { |
||
75 | throw new DuplicateKeyException('Duplicate key "' . $refreshTokenKey . '"'); |
||
76 | } |
||
77 | $values = $refreshToken->getDirtyAttributes($attributes); |
||
|
|||
78 | $refreshTokenParameters = []; |
||
79 | $this->setAttributesDefinitions($refreshToken->attributesDefinition()); |
||
80 | foreach ($values as $key => $value) { |
||
81 | if (($value !== null) && ($key !== 'scopes')) { |
||
82 | $refreshTokenParameters[$key] = $this->convertToDatabase($key, $value); |
||
83 | } |
||
84 | } |
||
85 | $refreshTokenParameters['dateCreated'] = date('Y-m-d H:i:s'); |
||
86 | $refreshTokenParameters['dateUpdated'] = date('Y-m-d H:i:s'); |
||
87 | try { |
||
88 | $this->db->createCommand() |
||
89 | ->insert($this->refreshTokensTable, $refreshTokenParameters) |
||
90 | ->execute(); |
||
91 | if (!empty($values['scopes'])) { |
||
92 | $values['scopes'] = array_unique($values['scopes']); |
||
93 | foreach ($values['scopes'] as $scope) { |
||
94 | $scopeAccessTokenParams = [ |
||
95 | 'scopeId' => $scope, |
||
96 | 'refreshTokenId' => $refreshTokenKey |
||
97 | ]; |
||
98 | $this->db->createCommand() |
||
99 | ->insert($this->scopeRefreshTokenTable, $scopeAccessTokenParams) |
||
100 | ->execute(); |
||
101 | } |
||
102 | } |
||
103 | } catch (DatabaseException $e) { |
||
104 | // @codeCoverageIgnoreStart |
||
105 | // we have a MYSQL exception, we should not discard |
||
106 | Yii::debug('Error while inserting entity', __METHOD__); |
||
107 | throw $e; |
||
108 | // @codeCoverageIgnoreEnd |
||
109 | } |
||
110 | $changedAttributes = array_fill_keys(array_keys($values), null); |
||
111 | $refreshToken->setOldAttributes($values); |
||
112 | $refreshToken->afterSave(true, $changedAttributes); |
||
113 | $result = true; |
||
114 | return $result; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Update Refresh Token |
||
119 | * @param RefreshTokenModelInterface $refreshToken |
||
120 | * @param null|array $attributes attributes to save |
||
121 | * @return bool |
||
122 | * @throws DatabaseException |
||
123 | * @throws DuplicateIndexException |
||
124 | * @throws DuplicateKeyException |
||
125 | */ |
||
126 | protected function update(RefreshTokenModelInterface $refreshToken, $attributes) |
||
127 | { |
||
128 | if (!$refreshToken->beforeSave(false)) { |
||
129 | return false; |
||
130 | } |
||
131 | |||
132 | $values = $refreshToken->getDirtyAttributes($attributes); |
||
133 | $modelKey = $refreshToken->key(); |
||
134 | if (isset($values[$modelKey]) === true) { |
||
135 | $entity = (new Query()) |
||
136 | ->select('*') |
||
137 | ->from($this->refreshTokensTable) |
||
138 | ->where('id = :id', [':id' => $values[$modelKey]]) |
||
139 | ->one($this->db); |
||
140 | if ($entity !== false) { |
||
141 | throw new DuplicateKeyException('Duplicate key "' . $values[$modelKey] . '"'); |
||
142 | } |
||
143 | } |
||
144 | $refreshTokenKey = isset($values[$modelKey]) ? $values[$modelKey] : $refreshToken->getKey(); |
||
145 | |||
146 | $refreshTokenParameters = []; |
||
147 | $this->setAttributesDefinitions($refreshToken->attributesDefinition()); |
||
148 | foreach ($values as $key => $value) { |
||
149 | if ($key !== 'scopes') { |
||
150 | $refreshTokenParameters[$key] = ($value !== null) ? $this->convertToDatabase($key, $value) : null; |
||
151 | } |
||
152 | } |
||
153 | $refreshTokenParameters['dateUpdated'] = date('Y-m-d H:i:s'); |
||
154 | try { |
||
155 | if (array_key_exists($modelKey, $values) === true) { |
||
156 | $oldRefreshTokenKey = $refreshToken->getOldKey(); |
||
157 | $this->db->createCommand() |
||
158 | ->update($this->refreshTokensTable, $refreshTokenParameters, 'id = :id', [':id' => $oldRefreshTokenKey]) |
||
159 | ->execute(); |
||
160 | } else { |
||
161 | $this->db->createCommand() |
||
162 | ->update($this->refreshTokensTable, $refreshTokenParameters, 'id = :id', [':id' => $refreshTokenKey]) |
||
163 | ->execute(); |
||
164 | } |
||
165 | if (isset($values['scopes'])) { |
||
166 | $values['scopes'] = array_unique($values['scopes']); |
||
167 | $scopeRefreshTokens = (new Query()) |
||
168 | ->select('*') |
||
169 | ->from($this->scopeRefreshTokenTable) |
||
170 | ->where('refreshTokenId = :refreshTokenId', [':refreshTokenId' => $refreshTokenKey]) |
||
171 | ->all($this->db); |
||
172 | foreach ($scopeRefreshTokens as $scopeRefreshToken) { |
||
173 | if (($index = array_search($scopeRefreshToken['scopeId'], $values['scopes'])) === false) { |
||
174 | $this->db->createCommand() |
||
175 | ->delete($this->scopeRefreshTokenTable, |
||
176 | 'refreshTokenId = :refreshTokenId AND scopeId = :scopeId', |
||
177 | [':refreshTokenId' => $refreshTokenKey, ':scopeId' => $scopeRefreshToken['scopeId']]) |
||
178 | ->execute(); |
||
179 | } else { |
||
180 | unset($values['scopes'][$index]); |
||
181 | } |
||
182 | } |
||
183 | foreach ($values['scopes'] as $scope) { |
||
184 | $scopeRefreshTokenParams = [ |
||
185 | 'scopeId' => $scope, |
||
186 | 'refreshTokenId' => $refreshTokenKey |
||
187 | ]; |
||
188 | $this->db->createCommand() |
||
189 | ->insert($this->scopeRefreshTokenTable, $scopeRefreshTokenParams) |
||
190 | ->execute(); |
||
191 | } |
||
192 | } |
||
193 | } catch (DatabaseException $e) { |
||
194 | // @codeCoverageIgnoreStart |
||
195 | // we have a MYSQL exception, we should not discard |
||
196 | Yii::debug('Error while updating entity', __METHOD__); |
||
197 | throw $e; |
||
198 | // @codeCoverageIgnoreEnd |
||
199 | } |
||
200 | |||
201 | $changedAttributes = []; |
||
202 | foreach ($values as $name => $value) { |
||
203 | $oldAttributes = $refreshToken->getOldAttributes(); |
||
204 | $changedAttributes[$name] = isset($oldAttributes[$name]) ? $oldAttributes[$name] : null; |
||
205 | $refreshToken->setOldAttribute($name, $value); |
||
206 | } |
||
207 | $refreshToken->afterSave(false, $changedAttributes); |
||
208 | return true; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @inheritdoc |
||
213 | */ |
||
214 | public function save(RefreshTokenModelInterface $refreshToken, $attributes) |
||
215 | { |
||
216 | if ($refreshToken->getIsNewRecord()) { |
||
217 | $result = $this->insert($refreshToken, $attributes); |
||
218 | } else { |
||
219 | $result = $this->update($refreshToken, $attributes); |
||
220 | } |
||
221 | return $result; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * @inheritdoc |
||
226 | */ |
||
227 | public function findOne($key) |
||
228 | { |
||
229 | $record = null; |
||
230 | $refreshTokenData = (new Query()) |
||
231 | ->select('*') |
||
232 | ->from($this->refreshTokensTable) |
||
233 | ->where('id = :id', [':id' => $key]) |
||
234 | ->one($this->db); |
||
235 | |||
236 | if ($refreshTokenData !== false) { |
||
237 | $refreshTokenData['scopes'] = []; |
||
238 | $tmpScopes = (new Query()) |
||
239 | ->select('scopeId') |
||
240 | ->from($this->scopeRefreshTokenTable) |
||
241 | ->all($this->db); |
||
242 | foreach ($tmpScopes as $scope) { |
||
243 | $refreshTokenData['scopes'][] = $scope['scopeId']; |
||
244 | } |
||
245 | |||
246 | $record = Yii::createObject('sweelix\oauth2\server\interfaces\RefreshTokenModelInterface'); |
||
247 | /** @var RefreshTokenModelInterface $record */ |
||
248 | $properties = $record->attributesDefinition(); |
||
249 | $this->setAttributesDefinitions($properties); |
||
250 | $attributes = []; |
||
251 | foreach ($refreshTokenData as $key => $value) { |
||
252 | if (isset($properties[$key]) === true) { |
||
253 | $refreshTokenData[$key] = $this->convertToModel($key, $value); |
||
254 | $record->setAttribute($key, $refreshTokenData[$key]); |
||
255 | $attributes[$key] = $refreshTokenData[$key]; |
||
256 | // @codeCoverageIgnoreStart |
||
257 | } elseif ($record->canSetProperty($key)) { |
||
258 | // TODO: find a way to test attribute population |
||
259 | $record->{$key} = $value; |
||
260 | } |
||
261 | // @codeCoverageIgnoreEnd |
||
262 | } |
||
263 | if (empty($attributes) === false) { |
||
264 | $record->setOldAttributes($attributes); |
||
265 | } |
||
266 | $record->afterFind(); |
||
267 | } |
||
268 | return $record; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * @inheritdoc |
||
273 | */ |
||
274 | public function delete(RefreshTokenModelInterface $refreshToken) |
||
275 | { |
||
276 | $result = false; |
||
277 | if ($refreshToken->beforeDelete()) { |
||
278 | //TODO: check results to return correct information |
||
279 | $this->db->createCommand() |
||
280 | ->delete($this->refreshTokensTable, 'id = :id', [':id' => $refreshToken->getKey()]) |
||
281 | ->execute(); |
||
282 | $refreshToken->setIsNewRecord(true); |
||
283 | $refreshToken->afterDelete(); |
||
284 | $result = true; |
||
285 | } |
||
286 | return $result; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * @inheritdoc |
||
291 | */ |
||
292 | public function findAllByUserId($userId) |
||
293 | { |
||
294 | $refreshTokensList = (new Query()) |
||
295 | ->select('*') |
||
296 | ->from($this->refreshTokensTable) |
||
297 | ->where('userId = :userId', [':userId' => $userId]) |
||
298 | ->all($this->db); |
||
299 | $refreshTokens = []; |
||
300 | foreach ($refreshTokensList as $refreshToken) { |
||
301 | $result = $this->findOne($refreshToken['id']); |
||
302 | if ($result instanceof RefreshTokenModelInterface) { |
||
303 | $refreshTokens[] = $result; |
||
304 | } |
||
305 | } |
||
306 | return $refreshTokens; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * @inheritdoc |
||
311 | */ |
||
312 | public function deleteAllByUserId($userId) |
||
313 | { |
||
314 | $refreshTokens = $this->findAllByUserId($userId); |
||
315 | foreach ($refreshTokens as $refreshToken) { |
||
316 | $this->delete($refreshToken); |
||
317 | } |
||
318 | return true; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * @inheritdoc |
||
323 | */ |
||
324 | public function findAllByClientId($clientId) |
||
340 | |||
341 | /** |
||
342 | * @inheritdoc |
||
343 | */ |
||
344 | public function deleteAllByClientId($clientId) |
||
345 | { |
||
346 | $refreshTokens = $this->findAllByClientId($clientId); |
||
347 | foreach ($refreshTokens as $refreshToken) { |
||
352 | |||
353 | /** |
||
354 | * @inheritdoc |
||
355 | */ |
||
356 | public function deleteAllExpired() |
||
370 | |||
371 | /** |
||
372 | * @inheritdoc |
||
373 | */ |
||
374 | public function findAll() |
||
389 | } |
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.