GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

WorkflowTemplate   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 374
Duplicated Lines 10.7 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 60
lcom 1
cbo 4
dl 40
loc 374
rs 3.6
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getName() 0 4 1
A getVersion() 0 4 1
A getDescription() 0 4 1
A getRemindDays() 0 4 1
A getSort() 0 4 1
A setStructure() 0 4 1
B createRelations() 6 43 8
B createAction() 0 31 7
A createUsers() 0 6 1
A createGroups() 0 6 1
B updateActionTransitions() 0 38 7
C updateDefinition() 6 69 11
F addManyManyToObject() 28 53 18

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 WorkflowTemplate 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 WorkflowTemplate, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Symbiote\AdvancedWorkflow\Templates;
4
5
use ArrayObject;
6
use Exception;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\Security\Group;
10
use SilverStripe\Security\Member;
11
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowDefinition;
12
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowTransition;
13
14
/**
15
 * A class that wraps around an array description of a workflow
16
 *
17
 * array(
18
 *  'Step Name' = array(
19
 *      'type'      => class name
20
 *      'transitions'   => array(
21
 *          'transition name' => 'target step',
22
 *          'next name' => 'other step'
23
 *      )
24
 *  ),
25
 *  'Next Step' = array(
26
 *
27
 *  ),
28
 * )
29
 *
30
 * This can be defined in config yml as follows
31
 *
32
 * SilverStripe\Core\Injector\Injector:
33
 *   SimpleReviewApprove:
34
 *     class: Symbiote\AdvancedWorkflow\Templates\WorkflowTemplate
35
 *     constructor:
36
 *       - Review and Approve
37
 *       - Description of the workflow template
38
 *       - 0.1 (version number)
39
 *     properties:
40
 *       structure:
41
 *         Apply for approval:
42
 *           type: AssignUsersToWorkflowAction
43
 *           transitions:
44
 *             notify: Notify users
45
 *         Notify users:
46
 *           type: NotifyUsersWorkflowAction
47
 *           transitions:
48
 *             approval: Approval
49
 *         Approval:
50
 *           type: SimpleApprovalWorkflowAction
51
 *           transitions:
52
 *             Approve: Publish
53
 *             Reject: Reject
54
 *         Publish:
55
 *           type: PublishItemWorkflowAction
56
 *         Reject:
57
 *           type: CancelWorkflowAction
58
 *   Symbiote\AdvancedWorkflow\Services\WorkflowService:
59
 *     properties:
60
 *       templates:
61
 *         - %$SimpleReviewApprove
62
 *
63
 * When updating a template, there's a few things that can be done to assist
64
 * the system when changing things around
65
 *
66
 * 1. Update the 'version' number
67
 *
68
 * @author [email protected]
69
 * @license BSD License http://silverstripe.org/bsd-license/
70
 */
71
class WorkflowTemplate
72
{
73
    protected $name;
74
    protected $description;
75
    protected $version;
76
    protected $remindDays;
77
    protected $sort;
78
79
    /**
80
     * An array representation of the structure of this workflow template
81
     *
82
     * @var array
83
     */
84
    protected $structure;
85
86
    public function __construct($name, $description = '', $version = '0.0', $remindDays = 0, $sort = 0)
87
    {
88
        $this->name = $name;
89
        $this->description = $description;
90
        $this->version = $version;
91
        $this->remindDays = $remindDays;
92
        $this->sort = $sort;
93
    }
94
95
    public function getName()
96
    {
97
        return $this->name;
98
    }
99
100
    public function getVersion()
101
    {
102
        return $this->version;
103
    }
104
105
    public function getDescription()
106
    {
107
        return $this->description;
108
    }
109
110
    public function getRemindDays()
111
    {
112
        return $this->remindDays;
113
    }
114
115
    public function getSort()
116
    {
117
        return $this->sort;
118
    }
119
120
    /**
121
     * Set the structure for this template
122
     *
123
     * @param array $structure
124
     */
125
    public function setStructure($structure)
126
    {
127
        $this->structure = $structure;
128
    }
129
130
    /**
131
     * Creates the relevant data objects for this structure, returning an array
132
     * of actions in the order they've been created
133
     *
134
     * @param WorkflowDefinition $definitino
0 ignored issues
show
Documentation introduced by
There is no parameter named $definitino. Did you maybe mean $definition?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
135
     *              An optional workflow definition to bind the actions into
136
     * @return array
137
     */
138
    public function createRelations($definition = null)
139
    {
140
        $actions = array();
141
        $transitions = new ArrayObject();
142
        $sort = 1;
143
        foreach ($this->structure as $relationName => $relationTemplate) {
144
            $isAction = isset($relationTemplate['type']);
145
            $isUsers = ($relationName == 'users');
146
            $isGroups = ($relationName == 'groups');
147
148
            // Process actions on WorkflowDefinition from the template
149
            if ($isAction) {
150
                $action = $this->createAction($relationName, $relationTemplate, $definition);
151
                // add a sort value in!
152
                $action->Sort = $sort++;
153
                $action->write();
154
155
                $actions[$relationName] = $action;
156
157
                $newTransitions = $this->updateActionTransitions($relationTemplate, $action);
158
                foreach ($newTransitions as $t) {
159
                    $transitions->append($t);
160
                }
161
            }
162
            // Process users on WorkflowDefinition from the template
163
            if ($isUsers) {
164
                $this->createUsers($relationTemplate, $definition);
165
            }
166
            // Process groups on WorkflowDefinition from the template
167
            if ($isGroups) {
168
                $this->createGroups($relationTemplate, $definition);
169
            }
170
        }
171
172 View Code Duplication
        foreach ($transitions as $transition) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
            if (isset($actions[$transition->Target])) {
174
                $transition->NextActionID = $actions[$transition->Target]->ID;
175
            }
176
            $transition->write();
177
        }
178
179
        return $actions;
180
    }
181
182
    /**
183
     * Create a workflow action based on a template
184
     *
185
     * @param string $name
186
     * @param array $template
0 ignored issues
show
Documentation introduced by
There is no parameter named $template. Did you maybe mean $actionTemplate?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
187
     * @param WorkflowDefinition $definition
188
     * @return WorkflowAction
189
     * @throws Exception
190
     */
191
    protected function createAction($name, $actionTemplate, WorkflowDefinition $definition = null)
192
    {
193
        $type = $actionTemplate['type'];
194
        if (!$type || !class_exists($type)) {
195
            throw new Exception(_t(
196
                'WorkflowTemplate.INVALID_TEMPLATE_ACTION',
197
                'Invalid action class specified in template'
198
            ));
199
        }
200
201
        $action = $type::create();
202
203
        $action->Title = $name;
204
205
        if (isset($actionTemplate['properties']) && is_array($actionTemplate['properties'])) {
206
            foreach ($actionTemplate['properties'] as $prop => $val) {
207
                $action->$prop = $val;
208
            }
209
        }
210
211
        // Deal with User + Group many_many relations on an action
212
        $this->addManyManyToObject($action, $actionTemplate);
213
214
        if ($definition) {
215
            $action->WorkflowDefID = $definition->ID;
216
        }
217
218
        $action->write();
219
220
        return $action;
221
    }
222
223
    /**
224
     * Create a WorkflowDefinition->Users relation based on template data. But only if the related groups from the
225
     * export, can be foud in the target environment's DB.
226
     *
227
     * Note: The template gives us a Member Email to work with rather than an ID as it's arguably
228
     * more likely that Member Emails will be the same between environments than their IDs.
229
     *
230
     * @param array $users
231
     * @param WorkflowDefinition $definition
232
     * @param boolean $clear
233
     * @return void
234
     */
235
    protected function createUsers($users, WorkflowDefinition $definition, $clear = false)
236
    {
237
        // Create the necessary relation in WorkflowDefinition_Users
238
        $source = array('users' => $users);
239
        $this->addManyManyToObject($definition, $source, $clear);
240
    }
241
242
    /**
243
     * Create a WorkflowDefinition->Groups relation based on template data, But only if the related groups from the
244
     * export, can be foud in the target environment's DB.
245
     *
246
     * Note: The template gives us a Group Title to work with rther than an ID as it's arguably
247
     * more likely that Group titles will be the same between environments than their IDs.
248
     *
249
     * @param array $groups
250
     * @param WorkflowDefinition $definition
251
     * @param boolean $clear
252
     * @return void
253
     */
254
    protected function createGroups($groups, WorkflowDefinition $definition, $clear = false)
255
    {
256
        // Create the necessary relation in WorkflowDefinition_Groups
257
        $source = array('groups' => $groups);
258
        $this->addManyManyToObject($definition, $source, $clear);
259
    }
260
261
    /**
262
     * Update the transitions for a given action
263
     *
264
     * @param array $actionTemplate
265
     * @param WorkflowAction $action
266
     *
267
     * @return array
268
     */
269
    protected function updateActionTransitions($actionTemplate, $action)
270
    {
271
        $transitions = array();
272
        if (isset($actionTemplate['transitions']) && is_array($actionTemplate['transitions'])) {
273
            $existing = $action->Transitions();
274
            $transitionMap = array();
275
            foreach ($existing as $transition) {
276
                $transitionMap[$transition->Title] = $transition;
277
            }
278
279
            foreach ($actionTemplate['transitions'] as $transitionName => $transitionTemplate) {
280
                $target = $transitionTemplate;
281
                if (is_array($transitionTemplate)) {
282
                    $to = array_keys($transitionTemplate);
283
                    $transitionName = $to[0];
284
                    $target = $transitionTemplate[$transitionName];
285
                }
286
287
                if (isset($transitionMap[$transitionName])) {
288
                    $transition = $transitionMap[$transitionName];
289
                } else {
290
                    $transition = WorkflowTransition::create();
291
                }
292
293
                // Add Member and Group relations to this Transition
294
                $this->addManyManyToObject($transition, $transitionTemplate);
295
296
                $transition->Title = $transitionName;
297
                $transition->ActionID = $action->ID;
298
                // we don't have the NextAction yet other than the target name, so we store that against
299
                // the transition and do a second pass later on to match things up
300
                $transition->Target = $target;
301
                $transitions[] = $transition;
302
            }
303
        }
304
305
        return $transitions;
306
    }
307
308
    /**
309
     * Update a workflow definition
310
     *
311
     * @param WorkflowDefinition $definition The definition to update
312
     */
313
    public function updateDefinition(WorkflowDefinition $definition)
314
    {
315
        $existingActions = array();
316
317
        $existing = $definition->Actions()->column('Title');
0 ignored issues
show
Bug introduced by
The method Actions() does not exist on Symbiote\AdvancedWorkflo...ects\WorkflowDefinition. Did you maybe mean updateAdminActions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
318
        $structure = array_keys($this->structure);
319
320
        $removeNames = array_diff($existing, $structure);
321
322
        foreach ($definition->Actions() as $action) {
0 ignored issues
show
Bug introduced by
The method Actions() does not exist on Symbiote\AdvancedWorkflo...ects\WorkflowDefinition. Did you maybe mean updateAdminActions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
323
            if (in_array($action->Title, $removeNames)) {
324
                $action->delete();
325
                continue;
326
            }
327
            $existingActions[$action->Title] = $action;
328
        }
329
330
        $actions = array();
331
        $transitions = new ArrayObject;
332
        $sort = 1;
333
        // now, go through the structure and create/realign things
334
        foreach ($this->structure as $relationName => $relationTemplate) {
335
            $isAction = isset($relationTemplate['type']);
336
            $isUsers = ($relationName == 'users');
337
            $isGroups = ($relationName == 'groups');
338
339
            if ($isAction) {
340
                $action = null;
0 ignored issues
show
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
341
                if (isset($existingActions[$relationName])) {
342
                    $action = $existingActions[$relationName];
343
                } else {
344
                    $action = $this->createAction($relationName, $relationTemplate, $definition, $transitions);
0 ignored issues
show
Unused Code introduced by
The call to WorkflowTemplate::createAction() has too many arguments starting with $transitions.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
345
                }
346
347
                // add a sort value in!
348
                $action->Sort = $sort++;
349
                $action->write();
350
351
                $actions[$relationName] = $action;
352
353
                $newTransitions = $this->updateActionTransitions($relationTemplate, $action);
354
                foreach ($newTransitions as $t) {
355
                    $transitions->append($t);
356
                }
357
            }
358
            // Process users on WorkflowDefinition from the template
359
            if ($isUsers) {
360
                $this->createUsers($relationTemplate, $definition, true);
361
            }
362
            // Process groups on WorkflowDefinition from the template
363
            if ($isGroups) {
364
                $this->createGroups($relationTemplate, $definition, true);
365
            }
366
        }
367
368 View Code Duplication
        foreach ($transitions as $transition) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
            if (isset($actions[$transition->Target])) {
370
                $transition->NextActionID = $actions[$transition->Target]->ID;
371
            }
372
            $transition->write();
373
        }
374
375
        // Set the version and do the write at the end so that we don't trigger an infinite loop!!
376
        $definition->Description = $this->getDescription();
0 ignored issues
show
Documentation introduced by
The property Description does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
377
        $definition->TemplateVersion = $this->getVersion();
0 ignored issues
show
Documentation introduced by
The property TemplateVersion does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
378
        $definition->RemindDays = $this->getRemindDays();
0 ignored issues
show
Documentation introduced by
The property RemindDays does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
379
        $definition->Sort = $this->getSort();
0 ignored issues
show
Documentation introduced by
The property Sort does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
380
        $definition->write();
381
    }
382
383
    /**
384
     * Given an object, first check it has a ManyMany relation on it and add() Member and Group relations as required.
385
     *
386
     * @param Object $object (e.g. WorkflowDefinition, WorkflowAction, WorkflowTransition)
387
     * @param array $source Usually data taken from a YAML template
388
     * @param boolean $clear Lose/keep Group/Member relations on $object (useful for reloading/refreshing definition)
389
     * @return void
390
     */
391
    protected function addManyManyToObject($object, $source, $clear = false)
392
    {
393
        // Check incoming
394
        if (!is_object($object) || !is_array($source)) {
395
            return;
396
        }
397
398
        // Only some target class variants actually have Group/User relations
399
        $hasUsers = false;
400
        $hasGroups = false;
401
        if ($manyMany = $object->stat('many_many')) {
402
            if (in_array(Member::class, $manyMany)) {
403
                $hasUsers = true;
404
                $userRelationName = array_keys($manyMany);
405
            }
406
            if (in_array(Group::class, $manyMany)) {
407
                $hasGroups = true;
408
                $groupRelationName = array_keys($manyMany);
409
            }
410
        }
411
412
        // Deal with User relations on target object
413 View Code Duplication
        if ($hasUsers) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
414
            if ($clear) {
415
                $relName = $userRelationName[0];
0 ignored issues
show
Bug introduced by
The variable $userRelationName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
416
                $object->$relName()->removeAll();
417
            }
418
            if (isset($source['users']) && is_array($source['users'])) {
419
                foreach ($source['users'] as $user) {
420
                    $email = Convert::raw2sql($user['email']);
421
                    if ($_user = DataObject::get_one(Member::class, "Email = '".$email."'")) {
422
                        $object->Users()->add($_user);
423
                    }
424
                }
425
            }
426
        }
427
428
        // Deal with Group relations on target object
429 View Code Duplication
        if ($hasGroups) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
            if ($clear) {
431
                $relName = $groupRelationName[0];
0 ignored issues
show
Bug introduced by
The variable $groupRelationName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
432
                $object->$relName()->removeAll();
433
            }
434
            if (isset($source['groups']) && is_array($source['groups'])) {
435
                foreach ($source['groups'] as $group) {
436
                    $title = Convert::raw2sql($group['title']);
437
                    if ($_group = DataObject::get_one(Group::class, "Title = '".$title."'")) {
438
                        $object->Groups()->add($_group);
439
                    }
440
                }
441
            }
442
        }
443
    }
444
}
445