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 RoleManager 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 RoleManager, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 16 | class RoleManager extends RepositoryExecutor |
||
| 17 | { |
||
| 18 | protected $supportedStepTypes = array('role'); |
||
| 19 | |||
| 20 | 20 | protected $roleHandler; |
|
| 21 | protected $roleMatcher; |
||
| 22 | 20 | ||
| 23 | 20 | public function __construct(RoleMatcher $roleMatcher, RoleHandler $roleHandler) |
|
| 24 | { |
||
| 25 | $this->roleMatcher = $roleMatcher; |
||
| 26 | $this->roleHandler = $roleHandler; |
||
| 27 | } |
||
| 28 | 1 | ||
| 29 | /** |
||
| 30 | 1 | * Method to handle the create operation of the migration instructions |
|
| 31 | 1 | */ |
|
| 32 | protected function create() |
||
| 33 | 1 | { |
|
| 34 | $roleService = $this->repository->getRoleService(); |
||
| 35 | $userService = $this->repository->getUserService(); |
||
| 36 | 1 | ||
| 37 | $roleCreateStruct = $roleService->newRoleCreateStruct($this->dsl['name']); |
||
| 38 | 1 | ||
| 39 | 1 | // Publish new role |
|
| 40 | 1 | $role = $roleService->createRole($roleCreateStruct); |
|
| 41 | 1 | if(is_callable(array($roleService, 'publishRoleDraft')) { |
|
|
|
|||
| 42 | 1 | $roleService->publishRoleDraft($role); |
|
| 43 | 1 | } |
|
| 44 | |||
| 45 | 1 | if (array_key_exists('policies', $this->dsl)) { |
|
| 46 | foreach($this->dsl['policies'] as $key => $ymlPolicy) { |
||
| 47 | $this->addPolicy($role, $roleService, $ymlPolicy); |
||
| 48 | } |
||
| 49 | 1 | } |
|
| 50 | 1 | ||
| 51 | if (array_key_exists('assign', $this->dsl)) { |
||
| 52 | $this->assignRole($role, $roleService, $userService, $this->dsl['assign']); |
||
| 53 | } |
||
| 54 | |||
| 55 | 1 | $this->setReferences($role); |
|
| 56 | |||
| 57 | 1 | return $role; |
|
| 58 | 1 | } |
|
| 59 | |||
| 60 | 1 | /** |
|
| 61 | * Method to handle the update operation of the migration instructions |
||
| 62 | 1 | */ |
|
| 63 | protected function update() |
||
| 64 | { |
||
| 65 | 1 | $roleCollection = $this->matchRoles('update'); |
|
| 66 | |||
| 67 | if (count($roleCollection) > 1 && array_key_exists('references', $this->dsl)) { |
||
| 68 | throw new \Exception("Can not execute Role update because multiple roles match, and a references section is specified in the dsl. References can be set when only 1 role matches"); |
||
| 69 | } |
||
| 70 | |||
| 71 | 1 | if (count($roleCollection) > 1 && array_key_exists('new_name', $this->dsl)) { |
|
| 72 | 1 | throw new \Exception("Can not execute Role update because multiple roles match, and a new_name is specified in the dsl."); |
|
| 73 | } |
||
| 74 | |||
| 75 | $roleService = $this->repository->getRoleService(); |
||
| 76 | 1 | $userService = $this->repository->getUserService(); |
|
| 77 | 1 | ||
| 78 | foreach ($roleCollection as $key => $role) { |
||
| 79 | 1 | ||
| 80 | /** @var \eZ\Publish\API\Repository\Values\User\Role $role */ |
||
| 81 | 1 | $role = $roleService->loadRoleByIdentifier($this->dsl['name']); |
|
| 82 | 1 | ||
| 83 | 1 | // Updating role name |
|
| 84 | 1 | if (array_key_exists('new_name', $this->dsl)) { |
|
| 85 | $update = $roleService->newRoleUpdateStruct(); |
||
| 86 | 1 | $update->identifier = $this->dsl['new_name']; |
|
| 87 | 1 | $role = $roleService->updateRole($role, $update); |
|
| 88 | 1 | } |
|
| 89 | |||
| 90 | 1 | if (array_key_exists('policies', $this->dsl)) { |
|
| 91 | 1 | $ymlPolicies = $this->dsl['policies']; |
|
| 92 | |||
| 93 | 1 | // Removing all policies so we can add them back. |
|
| 94 | // TODO: Check and update policies instead of remove and add. |
||
| 95 | $policies = $role->getPolicies(); |
||
| 96 | foreach($policies as $policy) { |
||
| 97 | $roleService->deletePolicy($policy); |
||
| 98 | 1 | } |
|
| 99 | |||
| 100 | foreach($ymlPolicies as $ymlPolicy) { |
||
| 101 | 1 | $this->addPolicy($role, $roleService, $ymlPolicy); |
|
| 102 | } |
||
| 103 | 1 | } |
|
| 104 | 1 | ||
| 105 | 1 | if (array_key_exists('assign', $this->dsl)) { |
|
| 106 | 1 | $this->assignRole($role, $roleService, $userService, $this->dsl['assign']); |
|
| 107 | 1 | } |
|
| 108 | |||
| 109 | $roleCollection[$key] = $role; |
||
| 110 | } |
||
| 111 | |||
| 112 | $this->setReferences($roleCollection); |
||
| 113 | |||
| 114 | return $roleCollection; |
||
| 115 | } |
||
| 116 | |||
| 117 | /** |
||
| 118 | 1 | * Method to handle the delete operation of the migration instructions |
|
| 119 | */ |
||
| 120 | 1 | protected function delete() |
|
| 121 | 1 | { |
|
| 122 | $roleCollection = $this->matchRoles('delete'); |
||
| 123 | |||
| 124 | 1 | $roleService = $this->repository->getRoleService(); |
|
| 125 | 1 | ||
| 126 | 1 | foreach ($roleCollection as $role) { |
|
| 127 | 1 | $roleService->deleteRole($role); |
|
| 128 | 1 | } |
|
| 129 | 1 | ||
| 130 | 1 | return $roleCollection; |
|
| 131 | 1 | } |
|
| 132 | 1 | ||
| 133 | 1 | /** |
|
| 134 | * @param string $action |
||
| 135 | 1 | * @return RoleCollection |
|
| 136 | 1 | * @throws \Exception |
|
| 137 | */ |
||
| 138 | 1 | protected function matchRoles($action) |
|
| 139 | 1 | { |
|
| 140 | if (!isset($this->dsl['name']) && !isset($this->dsl['match'])) { |
||
| 141 | 1 | throw new \Exception("The name of a role or a match condition is required to $action it."); |
|
| 142 | } |
||
| 143 | |||
| 144 | // Backwards compat |
||
| 145 | if (!isset($this->dsl['match'])) { |
||
| 146 | $this->dsl['match'] = array('identifier' => $this->dsl['name']); |
||
| 147 | } |
||
| 148 | |||
| 149 | $match = $this->dsl['match']; |
||
| 150 | |||
| 151 | // convert the references passed in the match |
||
| 152 | foreach ($match as $condition => $values) { |
||
| 153 | if (is_array($values)) { |
||
| 154 | foreach ($values as $position => $value) { |
||
| 155 | if ($this->referenceResolver->isReference($value)) { |
||
| 156 | $match[$condition][$position] = $this->referenceResolver->getReferenceValue($value); |
||
| 157 | } |
||
| 158 | 1 | } |
|
| 159 | } else { |
||
| 160 | 1 | if ($this->referenceResolver->isReference($values)) { |
|
| 161 | $match[$condition] = $this->referenceResolver->getReferenceValue($values); |
||
| 162 | 1 | } |
|
| 163 | } |
||
| 164 | 1 | } |
|
| 165 | 1 | ||
| 166 | return $this->roleMatcher->match($match); |
||
| 167 | } |
||
| 168 | |||
| 169 | 1 | /** |
|
| 170 | 1 | * Set references to object attributes to be retrieved later. |
|
| 171 | 1 | * |
|
| 172 | * The Role Manager currently support setting references to role_ids. |
||
| 173 | * |
||
| 174 | * @param \eZ\Publish\API\Repository\Values\User\Role|RoleCollection $role |
||
| 175 | * @throws \InvalidArgumentException When trying to assign a reference to an unsupported attribute |
||
| 176 | * @return boolean |
||
| 177 | */ |
||
| 178 | protected function setReferences($role) |
||
| 179 | { |
||
| 180 | if (!array_key_exists('references', $this->dsl)) { |
||
| 181 | return false; |
||
| 182 | } |
||
| 183 | |||
| 184 | if ($role instanceof RoleCollection) { |
||
| 185 | if (count($role) > 1) { |
||
| 186 | throw new \InvalidArgumentException('Role Manager does not support setting references for creating/updating of multiple roles'); |
||
| 187 | } |
||
| 188 | $role = reset($role); |
||
| 189 | } |
||
| 190 | |||
| 191 | foreach ($this->dsl['references'] as $reference) { |
||
| 192 | 1 | switch ($reference['attribute']) { |
|
| 193 | case 'role_id': |
||
| 194 | 1 | case 'id': |
|
| 195 | 1 | $value = $role->id; |
|
| 196 | 1 | break; |
|
| 197 | 1 | case 'identifier': |
|
| 198 | 1 | case 'role_identifier': |
|
| 199 | $value = $role->identifier; |
||
| 200 | 1 | break; |
|
| 201 | default: |
||
| 202 | throw new \InvalidArgumentException('Role Manager does not support setting references for attribute ' . $reference['attribute']); |
||
| 203 | 1 | } |
|
| 204 | 1 | ||
| 205 | 1 | $this->referenceResolver->addReference($reference['identifier'], $value); |
|
| 206 | 1 | } |
|
| 207 | |||
| 208 | 1 | return true; |
|
| 209 | 1 | } |
|
| 210 | |||
| 211 | /** |
||
| 212 | * Create a new Limitation object based on the type and value in the $limitation array. |
||
| 213 | * |
||
| 214 | * <pre> |
||
| 215 | * $limitation = array( |
||
| 216 | * 'identifier' => Type of the limitation |
||
| 217 | * 'values' => array(Values to base the limitation on) |
||
| 218 | * ) |
||
| 219 | * </pre> |
||
| 220 | * |
||
| 221 | * @param \eZ\Publish\API\Repository\RoleService $roleService |
||
| 222 | * @param array $limitation |
||
| 223 | * @return \eZ\Publish\API\Repository\Values\User\Limitation |
||
| 224 | */ |
||
| 225 | private function createLimitation(RoleService $roleService, array $limitation) |
||
| 226 | { |
||
| 227 | $limitationType = $roleService->getLimitationType($limitation['identifier']); |
||
| 228 | 1 | ||
| 229 | 1 | $limitationValue = is_array($limitation['values']) ? $limitation['values'] : array($limitation['values']); |
|
| 230 | 1 | ||
| 231 | foreach($limitationValue as $id => $value) { |
||
| 232 | if ($this->referenceResolver->isReference($value)) { |
||
| 233 | $value = $this->referenceResolver->getReferenceValue($value); |
||
| 234 | $limitationValue[$id] = $value; |
||
| 235 | } |
||
| 236 | } |
||
| 237 | $limitationValue = $this->roleHandler->convertLimitationToValue($limitation['identifier'], $limitationValue); |
||
| 238 | return $limitationType->buildValue($limitationValue); |
||
| 239 | } |
||
| 240 | |||
| 241 | /** |
||
| 242 | * Assign a role to users and groups in the assignment array. |
||
| 243 | * |
||
| 244 | * <pre> |
||
| 245 | * $assignments = array( |
||
| 246 | * array( |
||
| 247 | * 'type' => 'user', |
||
| 248 | * 'ids' => array(user ids), |
||
| 249 | * 'limitation' => array(limitations) |
||
| 250 | * ) |
||
| 251 | * ) |
||
| 252 | * </pre> |
||
| 253 | * |
||
| 254 | * @param \eZ\Publish\API\Repository\Values\User\Role $role |
||
| 255 | * @param \eZ\Publish\API\Repository\RoleService $roleService |
||
| 256 | * @param \eZ\Publish\API\Repository\UserService $userService |
||
| 257 | * @param array $assignments |
||
| 258 | */ |
||
| 259 | private function assignRole(Role $role, RoleService $roleService, UserService $userService, array $assignments) |
||
| 260 | { |
||
| 261 | foreach ($assignments as $assign) { |
||
| 262 | switch ($assign['type']) { |
||
| 263 | case 'user': |
||
| 264 | foreach ($assign['ids'] as $userId) { |
||
| 265 | $user = $userService->loadUser($userId); |
||
| 266 | |||
| 267 | if (!array_key_exists('limitation', $assign)) { |
||
| 268 | $roleService->assignRoleToUser($role, $user); |
||
| 269 | } else { |
||
| 270 | foreach ($assign['limitation'] as $limitation) { |
||
| 271 | $limitationObject = $this->createLimitation($roleService, $limitation); |
||
| 272 | $roleService->assignRoleToUser($role, $user, $limitationObject); |
||
| 273 | } |
||
| 274 | } |
||
| 275 | } |
||
| 276 | break; |
||
| 277 | case 'group': |
||
| 278 | foreach ($assign['ids'] as $groupId) { |
||
| 279 | 1 | $group = $userService->loadUserGroup($groupId); |
|
| 280 | |||
| 281 | 1 | if (!array_key_exists('limitation', $assign)) { |
|
| 282 | try { |
||
| 283 | 1 | $roleService->assignRoleToUserGroup($role, $group); |
|
| 284 | } catch (InvalidArgumentException $e) {} |
||
| 285 | } else { |
||
| 286 | foreach ($assign['limitation'] as $limitation) { |
||
| 287 | $limitationObject = $this->createLimitation($roleService, $limitation); |
||
| 288 | try { |
||
| 289 | $roleService->assignRoleToUserGroup($role, $group, $limitationObject); |
||
| 290 | 1 | } catch (InvalidArgumentException $e) {} |
|
| 291 | 1 | } |
|
| 292 | } |
||
| 293 | } |
||
| 294 | break; |
||
| 295 | } |
||
| 296 | } |
||
| 297 | } |
||
| 298 | |||
| 299 | /** |
||
| 300 | * Unassign a role from a list of users based on their user ids. |
||
| 301 | * |
||
| 302 | * @param Role $role |
||
| 303 | * @param RoleService $roleService |
||
| 304 | * @param UserService $userService |
||
| 305 | * @param array $userIds |
||
| 306 | */ |
||
| 307 | private function unassignRoleFromUsers( |
||
| 308 | Role $role, |
||
| 309 | RoleService $roleService, |
||
| 310 | UserService $userService, |
||
| 311 | array $userIds |
||
| 312 | ) { |
||
| 313 | foreach ($userIds as $userId) { |
||
| 314 | $user = $userService->loadUser($userId); |
||
| 315 | $roleService->unassignRoleFromUser($role, $user); |
||
| 316 | } |
||
| 317 | } |
||
| 318 | |||
| 319 | /** |
||
| 320 | * Unassign a role from a list of user groups based on their id. |
||
| 321 | * |
||
| 322 | * @param Role $role |
||
| 323 | * @param RoleService $roleService |
||
| 324 | * @param UserService $userService |
||
| 325 | * @param array $userGroupIds |
||
| 326 | */ |
||
| 327 | private function unassignRoleFromGroups( |
||
| 328 | Role $role, |
||
| 329 | RoleService $roleService, |
||
| 330 | UserService $userService, |
||
| 331 | array $userGroupIds |
||
| 332 | ) { |
||
| 333 | foreach ($userGroupIds as $userGroupId) { |
||
| 334 | $userGroup = $userService->loadUserGroup($userGroupId); |
||
| 335 | $roleService->unassignRoleFromUserGroup($role, $userGroup); |
||
| 336 | } |
||
| 337 | } |
||
| 338 | |||
| 339 | /** |
||
| 340 | * Add new policies to the $role Role. |
||
| 341 | * |
||
| 342 | * @param \eZ\Publish\API\Repository\Values\User\Role $role |
||
| 343 | * @param \eZ\Publish\API\Repository\RoleService $roleService |
||
| 344 | * @param array $policy |
||
| 345 | */ |
||
| 346 | private function addPolicy(Role $role, RoleService $roleService, array $policy) |
||
| 347 | { |
||
| 348 | $policyCreateStruct = $roleService->newPolicyCreateStruct($policy['module'], $policy['function']); |
||
| 349 | |||
| 350 | if (array_key_exists('limitations', $policy)) { |
||
| 351 | foreach ($policy['limitations'] as $limitation) { |
||
| 352 | $limitationObject = $this->createLimitation($roleService, $limitation); |
||
| 353 | $policyCreateStruct->addLimitation($limitationObject); |
||
| 354 | } |
||
| 355 | } |
||
| 356 | |||
| 357 | $roleService->addPolicy($role, $policyCreateStruct); |
||
| 360 |