Completed
Push — EZP-25721_improve_warning ( d7a071...6ed08c )
by André
17:24
created

Handler   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 564
Duplicated Lines 15.96 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 90
loc 564
rs 7.92
c 0
b 0
f 0
wmc 51
lcom 1
cbo 9

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A create() 0 6 1
A load() 0 10 2
A loadByLogin() 0 12 3
A loadByEmail() 0 10 2
A update() 0 4 1
A delete() 0 4 1
A createRole() 0 4 1
A createRoleDraft() 0 8 1
A internalCreateRole() 0 17 2
A loadRole() 15 15 3
A loadRoleByIdentifier() 15 15 3
A loadRoleDraftByRoleId() 15 15 3
A loadRoles() 13 13 3
A updateRole() 0 4 1
A deleteRole() 0 10 2
A publishRoleDraft() 0 27 4
A addPolicyByRoleDraft() 0 13 1
A addPolicy() 0 11 1
A updatePolicy() 0 8 2
A deletePolicy() 0 7 1
A loadPoliciesByUserId() 12 12 2
A assignRole() 0 5 2
A unassignRole() 0 4 1
A removeRoleAssignment() 0 4 1
A loadRoleAssignment() 0 10 2
A loadRoleAssignmentsByRoleId() 10 10 2
A loadRoleAssignmentsByGroupId() 10 10 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Handler 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 Handler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * File containing the UserHandler interface.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Persistence\Legacy\User;
10
11
use eZ\Publish\SPI\Persistence\User;
12
use eZ\Publish\SPI\Persistence\User\Handler as BaseUserHandler;
13
use eZ\Publish\SPI\Persistence\User\Role;
14
use eZ\Publish\SPI\Persistence\User\RoleCreateStruct;
15
use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct;
16
use eZ\Publish\SPI\Persistence\User\Policy;
17
use eZ\Publish\Core\Persistence\Legacy\Exception\RoleNotFound;
18
use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway as RoleGateway;
19
use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter;
20
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
21
use LogicException;
22
23
/**
24
 * Storage Engine handler for user module.
25
 */
26
class Handler implements BaseUserHandler
27
{
28
    /**
29
     * Gateway for storing user data.
30
     *
31
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway
32
     */
33
    protected $userGateway;
34
35
    /**
36
     * Gateway for storing role data.
37
     *
38
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway
39
     */
40
    protected $roleGateway;
41
42
    /**
43
     * Mapper for user related objects.
44
     *
45
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Mapper
46
     */
47
    protected $mapper;
48
49
    /**
50
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter
51
     */
52
    protected $limitationConverter;
53
54
    /**
55
     * Construct from userGateway.
56
     *
57
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Gateway $userGateway
58
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway $roleGateway
59
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Mapper $mapper
60
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter $limitationConverter
61
     */
62
    public function __construct(Gateway $userGateway, RoleGateway $roleGateway, Mapper $mapper, LimitationConverter $limitationConverter)
63
    {
64
        $this->userGateway = $userGateway;
65
        $this->roleGateway = $roleGateway;
66
        $this->mapper = $mapper;
67
        $this->limitationConverter = $limitationConverter;
68
    }
69
70
    /**
71
     * Create a user.
72
     *
73
     * The User struct used to create the user will contain an ID which is used
74
     * to reference the user.
75
     *
76
     * @param \eZ\Publish\SPI\Persistence\User $user
77
     *
78
     * @return \eZ\Publish\SPI\Persistence\User
79
     */
80
    public function create(User $user)
81
    {
82
        $this->userGateway->createUser($user);
83
84
        return $user;
85
    }
86
87
    /**
88
     * Loads user with user ID.
89
     *
90
     * @param mixed $userId
91
     *
92
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
93
     *
94
     * @return \eZ\Publish\SPI\Persistence\User
95
     */
96
    public function load($userId)
97
    {
98
        $data = $this->userGateway->load($userId);
99
100
        if (empty($data)) {
101
            throw new NotFound('user', $userId);
102
        }
103
104
        return $this->mapper->mapUser(reset($data));
105
    }
106
107
    /**
108
     * Loads user with user login.
109
     *
110
     * @param string $login
111
     *
112
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
113
     *
114
     * @return \eZ\Publish\SPI\Persistence\User
115
     */
116
    public function loadByLogin($login)
117
    {
118
        $data = $this->userGateway->loadByLogin($login);
119
120
        if (empty($data)) {
121
            throw new NotFound('user', $login);
122
        } elseif (isset($data[1])) {
123
            throw new LogicException("Found more then one user with login '{$login}'");
124
        }
125
126
        return $this->mapper->mapUser($data[0]);
127
    }
128
129
    /**
130
     * Loads user(s) with user email.
131
     *
132
     * As earlier eZ Publish versions supported several users having same email (ini config),
133
     * this function may return several users.
134
     *
135
     * @param string $email
136
     *
137
     * @return \eZ\Publish\SPI\Persistence\User[]
138
     */
139
    public function loadByEmail($email)
140
    {
141
        $data = $this->userGateway->loadByEmail($email);
142
143
        if (empty($data)) {
144
            return array();
145
        }
146
147
        return $this->mapper->mapUsers($data);
148
    }
149
150
    /**
151
     * Update the user information specified by the user struct.
152
     *
153
     * @param \eZ\Publish\SPI\Persistence\User $user
154
     */
155
    public function update(User $user)
156
    {
157
        $this->userGateway->updateUser($user);
158
    }
159
160
    /**
161
     * Delete user with the given ID.
162
     *
163
     * @param mixed $userId
164
     */
165
    public function delete($userId)
166
    {
167
        $this->userGateway->deleteUser($userId);
168
    }
169
170
    /**
171
     * Create new role draft.
172
     *
173
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
174
     *
175
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
176
     *
177
     * @return \eZ\Publish\SPI\Persistence\User\Role
178
     */
179
    public function createRole(RoleCreateStruct $createStruct)
180
    {
181
        return $this->internalCreateRole($createStruct);
182
    }
183
184
    /**
185
     * Creates a draft of existing defined role.
186
     *
187
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
188
     *
189
     * @param mixed $roleId
190
     *
191
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with defined status is not found
192
     *
193
     * @return \eZ\Publish\SPI\Persistence\User\Role
194
     */
195
    public function createRoleDraft($roleId)
196
    {
197
        $createStruct = $this->mapper->createCreateStructFromRole(
198
            $this->loadRole($roleId)
199
        );
200
201
        return $this->internalCreateRole($createStruct, $roleId);
202
    }
203
204
    /**
205
     * Internal method for creating Role.
206
     *
207
     * Used by self::createRole() and self::createRoleDraft()
208
     *
209
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
210
     * @param mixed|null $roleId Used by self::createRoleDraft() to retain Role id in the draft
211
     *
212
     * @return \eZ\Publish\SPI\Persistence\User\Role
213
     */
214
    protected function internalCreateRole(RoleCreateStruct $createStruct, $roleId = null)
215
    {
216
        $createStruct = clone $createStruct;
217
        $role = $this->mapper->createRoleFromCreateStruct(
218
            $createStruct
219
        );
220
        $role->id = $roleId;
221
        $role->status = Role::STATUS_DRAFT;
222
223
        $this->roleGateway->createRole($role);
224
225
        foreach ($role->policies as $policy) {
226
            $this->addPolicyByRoleDraft($role->id, $policy);
227
        }
228
229
        return $role;
230
    }
231
232
    /**
233
     * Loads a specified role (draft) by $roleId and $status.
234
     *
235
     * @param mixed $roleId
236
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
237
     *
238
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with given status does not exist
239
     *
240
     * @return \eZ\Publish\SPI\Persistence\User\Role
241
     */
242 View Code Duplication
    public function loadRole($roleId, $status = Role::STATUS_DEFINED)
243
    {
244
        $data = $this->roleGateway->loadRole($roleId, $status);
245
246
        if (empty($data)) {
247
            throw new RoleNotFound($roleId, $status);
248
        }
249
250
        $role = $this->mapper->mapRole($data);
251
        foreach ($role->policies as $policy) {
252
            $this->limitationConverter->toSPI($policy);
253
        }
254
255
        return $role;
256
    }
257
258
    /**
259
     * Loads a specified role (draft) by $identifier and $status.
260
     *
261
     * @param string $identifier
262
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
263
     *
264
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
265
     *
266
     * @return \eZ\Publish\SPI\Persistence\User\Role
267
     */
268 View Code Duplication
    public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED)
269
    {
270
        $data = $this->roleGateway->loadRoleByIdentifier($identifier, $status);
271
272
        if (empty($data)) {
273
            throw new RoleNotFound($identifier, $status);
274
        }
275
276
        $role = $this->mapper->mapRole($data);
277
        foreach ($role->policies as $policy) {
278
            $this->limitationConverter->toSPI($policy);
279
        }
280
281
        return $role;
282
    }
283
284
    /**
285
     * Loads a role draft by the original role ID.
286
     *
287
     * @param mixed $roleId ID of the role the draft was created from.
288
     *
289
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
290
     *
291
     * @return \eZ\Publish\SPI\Persistence\User\Role
292
     */
293 View Code Duplication
    public function loadRoleDraftByRoleId($roleId)
294
    {
295
        $data = $this->roleGateway->loadRoleDraftByRoleId($roleId);
296
297
        if (empty($data)) {
298
            throw new RoleNotFound($roleId, Role::STATUS_DRAFT);
299
        }
300
301
        $role = $this->mapper->mapRole($data);
302
        foreach ($role->policies as $policy) {
303
            $this->limitationConverter->toSPI($policy);
304
        }
305
306
        return $role;
307
    }
308
309
    /**
310
     * Loads all roles.
311
     *
312
     * @return \eZ\Publish\SPI\Persistence\User\Role[]
313
     */
314 View Code Duplication
    public function loadRoles()
315
    {
316
        $data = $this->roleGateway->loadRoles();
317
318
        $roles = $this->mapper->mapRoles($data);
319
        foreach ($roles as $role) {
320
            foreach ($role->policies as $policy) {
321
                $this->limitationConverter->toSPI($policy);
322
            }
323
        }
324
325
        return $roles;
326
    }
327
328
    /**
329
     * Update role (draft).
330
     *
331
     * @param \eZ\Publish\SPI\Persistence\User\RoleUpdateStruct $role
332
     */
333
    public function updateRole(RoleUpdateStruct $role)
334
    {
335
        $this->roleGateway->updateRole($role);
336
    }
337
338
    /**
339
     * Delete the specified role (draft).
340
     *
341
     * @param mixed $roleId
342
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
343
     */
344
    public function deleteRole($roleId, $status = Role::STATUS_DEFINED)
345
    {
346
        $role = $this->loadRole($roleId, $status);
347
348
        foreach ($role->policies as $policy) {
349
            $this->roleGateway->removePolicy($policy->id);
350
        }
351
352
        $this->roleGateway->deleteRole($role->id, $status);
353
    }
354
355
    /**
356
     * Publish the specified role draft.
357
     *
358
     * @param mixed $roleDraftId
359
     */
360
    public function publishRoleDraft($roleDraftId)
361
    {
362
        $roleDraft = $this->loadRole($roleDraftId, Role::STATUS_DRAFT);
363
364
        try {
365
            $originalRoleId = $roleDraft->originalId;
366
            $role = $this->loadRole($originalRoleId);
367
            $roleAssignments = $this->loadRoleAssignmentsByRoleId($role->id);
368
            $this->deleteRole($role->id);
369
370
            foreach ($roleAssignments as $roleAssignment) {
371
                if (empty($roleAssignment->limitationIdentifier)) {
372
                    $this->assignRole($roleAssignment->contentId, $originalRoleId);
373
                } else {
374
                    $this->assignRole(
375
                        $roleAssignment->contentId,
376
                        $originalRoleId,
377
                        [$roleAssignment->limitationIdentifier => $roleAssignment->values]
378
                    );
379
                }
380
            }
381
            $this->roleGateway->publishRoleDraft($roleDraft->id, $role->id);
382
        } catch (NotFound $e) {
383
            // If no published role is found, only publishing is needed, without specifying original role ID as there is none.
384
            $this->roleGateway->publishRoleDraft($roleDraft->id);
385
        }
386
    }
387
388
    /**
389
     * Adds a policy to a role draft.
390
     *
391
     * @param mixed $roleId
392
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
393
     *
394
     * @return \eZ\Publish\SPI\Persistence\User\Policy
395
     */
396
    public function addPolicyByRoleDraft($roleId, Policy $policy)
397
    {
398
        $legacyPolicy = clone $policy;
399
        $legacyPolicy->originalId = $policy->id;
400
        $this->limitationConverter->toLegacy($legacyPolicy);
401
402
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
403
        $policy->id = $legacyPolicy->id;
404
        $policy->originalId = $legacyPolicy->originalId;
405
        $policy->roleId = $legacyPolicy->roleId;
406
407
        return $policy;
408
    }
409
410
    /**
411
     * Adds a policy to a role.
412
     *
413
     * @param mixed $roleId
414
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
415
     *
416
     * @return \eZ\Publish\SPI\Persistence\User\Policy
417
     */
418
    public function addPolicy($roleId, Policy $policy)
419
    {
420
        $legacyPolicy = clone $policy;
421
        $this->limitationConverter->toLegacy($legacyPolicy);
422
423
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
424
        $policy->id = $legacyPolicy->id;
425
        $policy->roleId = $legacyPolicy->roleId;
426
427
        return $policy;
428
    }
429
430
    /**
431
     * Update a policy.
432
     *
433
     * Replaces limitations values with new values.
434
     *
435
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
436
     */
437
    public function updatePolicy(Policy $policy)
438
    {
439
        $policy = clone $policy;
440
        $this->limitationConverter->toLegacy($policy);
441
442
        $this->roleGateway->removePolicyLimitations($policy->id);
443
        $this->roleGateway->addPolicyLimitations($policy->id, $policy->limitations === '*' ? array() : $policy->limitations);
0 ignored issues
show
Bug introduced by
It seems like $policy->limitations ===... : $policy->limitations can also be of type string; however, eZ\Publish\Core\Persiste...:addPolicyLimitations() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
444
    }
445
446
    /**
447
     * Removes a policy from a role.
448
     *
449
     * @param mixed $policyId
450
     */
451
    public function deletePolicy($policyId)
452
    {
453
        // Each policy can only be associated to exactly one role. Thus it is
454
        // sufficient to use the policyId for identification and just remove
455
        // the policy completely.
456
        $this->roleGateway->removePolicy($policyId);
457
    }
458
459
    /**
460
     * Returns the user policies associated with the user (including inherited policies from user groups).
461
     *
462
     * @param mixed $userId
463
     *
464
     * @return \eZ\Publish\SPI\Persistence\User\Policy[]
465
     */
466 View Code Duplication
    public function loadPoliciesByUserId($userId)
467
    {
468
        $data = $this->roleGateway->loadPoliciesByUserId($userId);
469
470
        $policies = $this->mapper->mapPolicies($data);
471
472
        foreach ($policies as $policy) {
473
            $this->limitationConverter->toSPI($policy);
474
        }
475
476
        return $policies;
477
    }
478
479
    /**
480
     * Assigns role to a user or user group with given limitations.
481
     *
482
     * The limitation array looks like:
483
     * <code>
484
     *  array(
485
     *      'Subtree' => array(
486
     *          '/1/2/',
487
     *          '/1/4/',
488
     *      ),
489
     *      'Foo' => array( 'Bar' ),
490
     *      …
491
     *  )
492
     * </code>
493
     *
494
     * Where the keys are the limitation identifiers, and the respective values
495
     * are an array of limitation values. The limitation parameter is optional.
496
     *
497
     * @param mixed $contentId The groupId or userId to assign the role to.
498
     * @param mixed $roleId
499
     * @param array $limitation
500
     */
501
    public function assignRole($contentId, $roleId, array $limitation = null)
502
    {
503
        $limitation = $limitation ?: array('' => array(''));
504
        $this->userGateway->assignRole($contentId, $roleId, $limitation);
505
    }
506
507
    /**
508
     * Un-assign a role.
509
     *
510
     * @param mixed $contentId The user or user group Id to un-assign the role from.
511
     * @param mixed $roleId
512
     */
513
    public function unassignRole($contentId, $roleId)
514
    {
515
        $this->userGateway->removeRole($contentId, $roleId);
516
    }
517
518
    /**
519
     * Un-assign a role by assignment ID.
520
     *
521
     * @param mixed $roleAssignmentId The assignment ID.
522
     */
523
    public function removeRoleAssignment($roleAssignmentId)
524
    {
525
        $this->userGateway->removeRoleAssignmentById($roleAssignmentId);
526
    }
527
528
    /**
529
     * Loads role assignment for specified assignment ID.
530
     *
531
     * @param mixed $roleAssignmentId
532
     *
533
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role assignment is not found
534
     *
535
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment
536
     */
537
    public function loadRoleAssignment($roleAssignmentId)
538
    {
539
        $data = $this->roleGateway->loadRoleAssignment($roleAssignmentId);
540
541
        if (empty($data)) {
542
            throw new NotFound('roleAssignment', $roleAssignmentId);
543
        }
544
545
        return $this->mapper->mapRoleAssignments($data)[0];
546
    }
547
548
    /**
549
     * Loads roles assignments Role.
550
     *
551
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
552
     *
553
     * @param mixed $roleId
554
     *
555
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
556
     */
557 View Code Duplication
    public function loadRoleAssignmentsByRoleId($roleId)
558
    {
559
        $data = $this->roleGateway->loadRoleAssignmentsByRoleId($roleId);
560
561
        if (empty($data)) {
562
            return array();
563
        }
564
565
        return $this->mapper->mapRoleAssignments($data);
566
    }
567
568
    /**
569
     * Loads roles assignments to a user/group.
570
     *
571
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
572
     *
573
     * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role.
574
     *                      By the nature of legacy this can currently also be used to get by $userId.
575
     * @param bool $inherit If true also return inherited role assignments from user groups.
576
     *
577
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
578
     */
579 View Code Duplication
    public function loadRoleAssignmentsByGroupId($groupId, $inherit = false)
580
    {
581
        $data = $this->roleGateway->loadRoleAssignmentsByGroupId($groupId, $inherit);
582
583
        if (empty($data)) {
584
            return array();
585
        }
586
587
        return $this->mapper->mapRoleAssignments($data);
588
    }
589
}
590