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 classes like EntityAclExtension 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 EntityAclExtension, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class EntityAclExtension extends AbstractAclExtension |
||
32 | { |
||
33 | const NAME = 'entity'; |
||
34 | |||
35 | /** @var ObjectIdAccessor */ |
||
36 | protected $objectIdAccessor; |
||
37 | |||
38 | /** @var EntityClassResolver */ |
||
39 | protected $entityClassResolver; |
||
40 | |||
41 | /** @var MetadataProviderInterface */ |
||
42 | protected $metadataProvider; |
||
43 | |||
44 | /** @var EntitySecurityMetadataProvider */ |
||
45 | protected $entityMetadataProvider; |
||
46 | |||
47 | /** @var AccessLevelOwnershipDecisionMakerInterface */ |
||
48 | protected $decisionMaker; |
||
49 | |||
50 | /** @var EntityOwnerAccessor */ |
||
51 | protected $entityOwnerAccessor; |
||
52 | |||
53 | /** @var PermissionManager */ |
||
54 | protected $permissionManager; |
||
55 | |||
56 | /** @var AclGroupProviderInterface */ |
||
57 | protected $groupProvider; |
||
58 | |||
59 | /** |
||
60 | * key = Permission |
||
61 | * value = The identity of a permission mask builder |
||
62 | * |
||
63 | * @var int[] |
||
64 | */ |
||
65 | protected $permissionToMaskBuilderIdentity; |
||
66 | |||
67 | /** @var array */ |
||
68 | protected $maskBuilderIdentityToPermissions; |
||
69 | |||
70 | /** |
||
71 | * @param ObjectIdAccessor $objectIdAccessor |
||
72 | * @param EntityClassResolver $entityClassResolver |
||
73 | * @param EntitySecurityMetadataProvider $entityMetadataProvider |
||
74 | * @param MetadataProviderInterface $metadataProvider |
||
75 | * @param AccessLevelOwnershipDecisionMakerInterface $decisionMaker |
||
76 | * @param PermissionManager $permissionManager |
||
77 | * @param AclGroupProviderInterface $groupProvider |
||
78 | */ |
||
79 | View Code Duplication | public function __construct( |
|
96 | |||
97 | /** |
||
98 | * {@inheritdoc} |
||
99 | */ |
||
100 | public function getMasks($permission) |
||
106 | |||
107 | /** |
||
108 | * {@inheritdoc} |
||
109 | */ |
||
110 | public function hasMasks($permission) |
||
116 | |||
117 | /** |
||
118 | * @param EntityOwnerAccessor $entityOwnerAccessor |
||
119 | */ |
||
120 | public function setEntityOwnerAccessor(EntityOwnerAccessor $entityOwnerAccessor) |
||
124 | |||
125 | /** |
||
126 | * {@inheritdoc} |
||
127 | */ |
||
128 | public function getAccessLevelNames($object) |
||
143 | |||
144 | /** |
||
145 | * {@inheritdoc} |
||
146 | */ |
||
147 | public function supports($type, $id) |
||
148 | { |
||
149 | if ($type === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) { |
||
150 | return $id === $this->getExtensionKey(); |
||
151 | } |
||
152 | |||
153 | $delim = strpos($type, '@'); |
||
154 | View Code Duplication | if ($delim !== false) { |
|
155 | $type = ltrim(substr($type, $delim + 1), ' '); |
||
156 | } |
||
157 | |||
158 | if ($id === $this->getExtensionKey()) { |
||
159 | $type = $this->entityClassResolver->getEntityClass(ClassUtils::getRealClass($type)); |
||
160 | } else { |
||
161 | $type = ClassUtils::getRealClass($type); |
||
162 | } |
||
163 | |||
164 | return $this->entityClassResolver->isEntity($type); |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * {@inheritdoc} |
||
169 | */ |
||
170 | public function getExtensionKey() |
||
174 | |||
175 | /** |
||
176 | * {@inheritdoc} |
||
177 | */ |
||
178 | public function validateMask($mask, $object, $permission = null) |
||
205 | |||
206 | /** |
||
207 | * {@inheritdoc} |
||
208 | */ |
||
209 | public function getObjectIdentity($val) |
||
222 | |||
223 | /** |
||
224 | * {@inheritdoc} |
||
225 | */ |
||
226 | public function getMaskBuilder($permission) |
||
236 | |||
237 | /** |
||
238 | * {@inheritdoc} |
||
239 | */ |
||
240 | public function getAllMaskBuilders() |
||
249 | |||
250 | /** |
||
251 | * {@inheritdoc} |
||
252 | */ |
||
253 | public function getMaskPattern($mask) |
||
260 | |||
261 | /** |
||
262 | * {@inheritdoc} |
||
263 | * |
||
264 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
265 | */ |
||
266 | public function adaptRootMask($rootMask, $object) |
||
301 | |||
302 | /** |
||
303 | * {@inheritdoc} |
||
304 | */ |
||
305 | public function getServiceBits($mask) |
||
309 | |||
310 | /** |
||
311 | * {@inheritdoc} |
||
312 | */ |
||
313 | public function removeServiceBits($mask) |
||
317 | |||
318 | /** |
||
319 | * {@inheritdoc} |
||
320 | */ |
||
321 | public function getAccessLevel($mask, $permission = null, $object = null) |
||
344 | |||
345 | /** |
||
346 | * {@inheritdoc} |
||
347 | */ |
||
348 | public function getPermissions($mask = null, $setOnly = false, $byCurrentGroup = false) |
||
378 | |||
379 | /** |
||
380 | * {@inheritdoc} |
||
381 | */ |
||
382 | public function getAllowedPermissions(ObjectIdentity $oid) |
||
383 | { |
||
384 | if ($oid->getType() === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) { |
||
385 | $result = array_keys($this->getPermissionsToIdentityMap()); |
||
386 | } else { |
||
387 | $config = $this->entityMetadataProvider->getMetadata($oid->getType()); |
||
388 | $result = $config->getPermissions(); |
||
389 | if (empty($result)) { |
||
390 | $result = array_keys($this->getPermissionsToIdentityMap()); |
||
391 | } |
||
392 | |||
393 | $metadata = $this->getMetadata($oid); |
||
394 | if (!$metadata->hasOwner()) { |
||
395 | $result = array_diff($result, ['ASSIGN', 'SHARE']); |
||
396 | } |
||
397 | } |
||
398 | |||
399 | $allowed = $this->getPermissionsForType($oid->getType()); |
||
400 | |||
401 | return array_values(array_intersect($result, $allowed)); |
||
402 | } |
||
403 | |||
404 | /** |
||
405 | * @param string $type |
||
406 | * @return array |
||
407 | */ |
||
408 | protected function getPermissionsForType($type) |
||
409 | { |
||
410 | $group = $this->groupProvider->getGroup(); |
||
411 | |||
412 | if ($type === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) { |
||
413 | $permissions = $this->permissionManager->getPermissionsForGroup($group); |
||
414 | } else { |
||
415 | $permissions = $this->permissionManager->getPermissionsForEntity($type, $group); |
||
416 | } |
||
417 | |||
418 | return array_map( |
||
419 | function (Permission $permission) { |
||
420 | return $permission->getName(); |
||
421 | }, |
||
422 | $permissions |
||
423 | ); |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * {@inheritdoc} |
||
428 | */ |
||
429 | public function getClasses() |
||
433 | |||
434 | /** |
||
435 | * @SuppressWarnings(PHPMD.NPathComplexity) |
||
436 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||
437 | * {@inheritdoc} |
||
438 | */ |
||
439 | public function decideIsGranting($triggeredMask, $object, TokenInterface $securityToken) |
||
506 | |||
507 | /** |
||
508 | * @param int $accessLevel Current object access level |
||
509 | * @param mixed $object Object for test |
||
510 | * |
||
511 | * @return int |
||
512 | * |
||
513 | * @deprecated since 1.8, use MetadataProviderInterface::getMaxAccessLevel instead |
||
514 | */ |
||
515 | protected function fixMaxAccessLevel($accessLevel, $object) |
||
519 | |||
520 | /** |
||
521 | * Constructs an ObjectIdentity for the given domain object |
||
522 | * |
||
523 | * @param string $descriptor |
||
524 | * |
||
525 | * @return ObjectIdentity |
||
526 | * @throws \InvalidArgumentException |
||
527 | */ |
||
528 | protected function fromDescriptor($descriptor) |
||
543 | |||
544 | /** |
||
545 | * Constructs an ObjectIdentity for the given domain object |
||
546 | * |
||
547 | * @param object $domainObject |
||
548 | * |
||
549 | * @return ObjectIdentity |
||
550 | * @throws InvalidDomainObjectException |
||
551 | */ |
||
552 | protected function fromDomainObject($domainObject) |
||
567 | |||
568 | /** |
||
569 | * Checks that the given mask represents only one access level |
||
570 | * |
||
571 | * @param string $permission |
||
572 | * @param int $mask |
||
573 | * @param mixed $object |
||
574 | * |
||
575 | * @throws InvalidAclMaskException |
||
576 | */ |
||
577 | protected function validateMaskAccessLevel($permission, $mask, $object) |
||
578 | { |
||
579 | $identity = $this->getIdentityForPermission($permission); |
||
580 | if (0 !== ($mask & $this->getMaskBuilderConst($identity, 'GROUP_' . $permission))) { |
||
581 | $maskAccessLevels = []; |
||
582 | $clearedMask = $this->removeServiceBits($mask); |
||
583 | |||
584 | foreach (AccessLevel::$allAccessLevelNames as $accessLevel) { |
||
585 | $levelMask = $this->removeServiceBits( |
||
586 | $this->getMaskBuilderConst($identity, sprintf('MASK_%s_%s', $permission, $accessLevel)) |
||
587 | ); |
||
588 | |||
589 | if (0 !== ($clearedMask & $levelMask)) { |
||
590 | $maskAccessLevels[] = $accessLevel; |
||
591 | } |
||
592 | } |
||
593 | if (count($maskAccessLevels) > 1) { |
||
594 | $msg = sprintf( |
||
595 | 'The %s mask must be in one access level only, but it is in %s access levels.', |
||
596 | $permission, |
||
597 | implode(', ', $maskAccessLevels) |
||
598 | ); |
||
599 | throw $this->createInvalidAclMaskException($mask, $object, $msg); |
||
600 | } |
||
601 | } |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Gets all valid bitmasks for the given object |
||
606 | * |
||
607 | * @param string $permission |
||
608 | * @param mixed $object |
||
609 | * |
||
610 | * @return int |
||
611 | */ |
||
612 | protected function getValidMasks($permission, $object) |
||
662 | |||
663 | /** |
||
664 | * Gets metadata for the given object |
||
665 | * |
||
666 | * @param mixed $object |
||
667 | * |
||
668 | * @return OwnershipMetadataInterface |
||
669 | */ |
||
670 | protected function getMetadata($object) |
||
674 | |||
675 | /** |
||
676 | * Gets class name for given object |
||
677 | * |
||
678 | * @param $object |
||
679 | * |
||
680 | * @return string |
||
681 | */ |
||
682 | protected function getObjectClassName($object) |
||
695 | |||
696 | /** |
||
697 | * Gets the constant value defined in the given permission mask builder |
||
698 | * |
||
699 | * @param int $maskBuilderIdentity The permission mask builder identity |
||
700 | * @param string $constName |
||
701 | * |
||
702 | * @return int |
||
703 | */ |
||
704 | protected function getMaskBuilderConst($maskBuilderIdentity, $constName) |
||
713 | |||
714 | /** |
||
715 | * Check organization. If user try to access entity what was created in organization this user do not have access - |
||
716 | * deny access. We should check organization for all the entities what have ownership |
||
717 | * (USER, BUSINESS_UNIT, ORGANIZATION ownership types) |
||
718 | * |
||
719 | * @param mixed $object |
||
720 | * @param OrganizationContextTokenInterface $securityToken |
||
721 | * @return bool |
||
722 | */ |
||
723 | protected function isAccessDeniedByOrganizationContext($object, OrganizationContextTokenInterface $securityToken) |
||
741 | |||
742 | protected function loadPermissions() |
||
763 | |||
764 | protected function buildPermissionsMap() |
||
784 | |||
785 | /** |
||
786 | * @param bool $byCurrentGroup |
||
787 | * @return array|int[] |
||
788 | */ |
||
789 | protected function getPermissionsToIdentityMap($byCurrentGroup = false) |
||
802 | |||
803 | /** |
||
804 | * @param int $pk |
||
805 | * @return int |
||
806 | */ |
||
807 | protected function getIdentityForPrimaryKey($pk) |
||
813 | |||
814 | /** |
||
815 | * @param string $permission |
||
816 | * @return int |
||
817 | */ |
||
818 | protected function getIdentityForPermission($permission) |
||
824 | |||
825 | /** |
||
826 | * @param int $pk |
||
827 | * @return int |
||
828 | */ |
||
829 | protected function getPermissionNumber($pk) |
||
836 | |||
837 | /** |
||
838 | * @param int|null $identity |
||
839 | * @return array |
||
840 | */ |
||
841 | protected function getPermissionsForIdentity($identity = null) |
||
849 | } |
||
850 |