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 |