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.

WorkflowService   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 472
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 11

Importance

Changes 0
Metric Value
wmc 63
lcom 2
cbo 11
dl 0
loc 472
rs 3.36
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A setTemplates() 0 4 1
A getTemplates() 0 4 1
A getNamedTemplate() 0 15 5
B getDefinitionFor() 0 23 9
B getDefinitionByID() 0 20 7
A getDefinitionsFor() 0 15 2
B getWorkflowFor() 0 22 6
A getWorkflowHistoryFor() 0 7 3
A getDefinitions() 0 4 1
A executeTransition() 0 25 4
A startWorkflow() 0 28 5
A usersWorkflows() 0 35 4
A userPendingItems() 0 21 5
A userSubmittedItems() 0 13 2
A defineFromTemplate() 0 27 4
A reorder() 0 16 3
A providePermissions() 0 53 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace Symbiote\AdvancedWorkflow\Services;
4
5
use Exception;
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\ORM\ArrayList;
9
use SilverStripe\ORM\DataList;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\Security\Member;
12
use SilverStripe\Security\Permission;
13
use SilverStripe\Security\PermissionProvider;
14
use Symbiote\AdvancedWorkflow\Admin\WorkflowDefinitionImporter;
15
use Symbiote\AdvancedWorkflow\Extensions\FileWorkflowApplicable;
16
use Symbiote\AdvancedWorkflow\Extensions\WorkflowApplicable;
17
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowAction;
18
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowDefinition;
19
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowInstance;
20
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowTransition;
21
22
/**
23
 * A central point for interacting with workflows
24
 *
25
 * @author  [email protected]
26
 * @license BSD License (http://silverstripe.org/bsd-license/)
27
 * @package advancedworkflow
28
 */
29
30
class WorkflowService implements PermissionProvider
31
{
32
    /**
33
     * An array of templates that we can create from
34
     *
35
     * @var array
36
     */
37
    protected $templates;
38
39
    /**
40
     * Set the list of templates that can be created
41
     *
42
     * @param array $templates
43
     */
44
    public function setTemplates($templates)
45
    {
46
        $this->templates = $templates;
47
    }
48
49
    /**
50
     * Return the list of available templates
51
     * @return array
52
     */
53
    public function getTemplates()
54
    {
55
        return $this->templates;
56
    }
57
58
    /**
59
     * Get a template by name
60
     *
61
     * @param string $name
62
     * @return WorkflowTemplate|null
63
     */
64
    public function getNamedTemplate($name)
65
    {
66
        if ($importedTemplate = singleton(WorkflowDefinitionImporter::class)->getImportedWorkflows($name)) {
67
            return $importedTemplate;
68
        }
69
70
        if (!is_array($this->templates)) {
71
            return;
72
        }
73
        foreach ($this->templates as $template) {
74
            if ($template->getName() == $name) {
75
                return $template;
76
            }
77
        }
78
    }
79
80
    /**
81
     * Gets the workflow definition for a given dataobject, if there is one
82
     *
83
     * Will recursively query parent elements until it finds one, if available
84
     *
85
     * @param DataObject $dataObject
86
     */
87
    public function getDefinitionFor(DataObject $dataObject)
88
    {
89
        if ($dataObject->hasExtension(WorkflowApplicable::class)
90
            || $dataObject->hasExtension(FileWorkflowApplicable::class)
91
        ) {
92
            if ($dataObject->WorkflowDefinitionID) {
93
                return DataObject::get_by_id(WorkflowDefinition::class, $dataObject->WorkflowDefinitionID);
94
            }
95
            if ($dataObject->hasMethod('useInheritedWorkflow') && !$dataObject->useInheritedWorkflow()) {
96
                return null;
97
            }
98
            if ($dataObject->ParentID) {
99
                return $this->getDefinitionFor($dataObject->Parent());
100
            }
101
            if ($dataObject->hasMethod('workflowParent')) {
102
                $obj = $dataObject->workflowParent();
103
                if ($obj) {
104
                    return $this->getDefinitionFor($obj);
105
                }
106
            }
107
        }
108
        return null;
109
    }
110
111
    /**
112
     *  Retrieves a workflow definition by ID for a data object.
113
     *
114
     *  @param DataObject $object
115
     *  @param integer $workflowID
116
     *  @return WorkflowDefinition|null
117
     */
118
    public function getDefinitionByID($object, $workflowID)
119
    {
120
        // Make sure the correct extensions have been applied to the data object.
121
122
        $workflow = null;
123
        if ($object->hasExtension(WorkflowApplicable::class)
124
            || $object->hasExtension(FileWorkflowApplicable::class)
125
        ) {
126
            // Validate the workflow ID against the data object.
127
128
            if (($object->WorkflowDefinitionID == $workflowID)
129
                || ($workflow = $object->AdditionalWorkflowDefinitions()->byID($workflowID))
130
            ) {
131
                if (is_null($workflow)) {
132
                    $workflow = DataObject::get_by_id(WorkflowDefinition::class, $workflowID);
133
                }
134
            }
135
        }
136
        return $workflow ? $workflow : null;
137
    }
138
139
    /**
140
     * Retrieves and collates the workflow definitions for a data object, where the first element will be the
141
     * main workflow definition.
142
     *
143
     * @param DataObject object
144
     * @return array
145
     */
146
147
    public function getDefinitionsFor($object)
148
    {
149
150
        // Retrieve the main workflow definition.
151
152
        $default = $this->getDefinitionFor($object);
153
        if ($default) {
154
            // Merge the additional workflow definitions.
155
156
            return array_merge(array(
157
                $default
158
            ), $object->AdditionalWorkflowDefinitions()->toArray());
159
        }
160
        return null;
161
    }
162
163
    /**
164
     * Gets the workflow for the given item
165
     *
166
     * The item can be
167
     *
168
     * a data object in which case the ActiveWorkflow will be returned,
169
     * an action, in which case the Workflow will be returned
170
     * an integer, in which case the workflow with that ID will be returned
171
     *
172
     * @param mixed $item
173
     * @param bool $includeComplete
174
     * @return WorkflowInstance|null
175
     */
176
    public function getWorkflowFor($item, $includeComplete = false)
177
    {
178
        $id = $item;
0 ignored issues
show
Unused Code introduced by
$id 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...
179
180
        if ($item instanceof WorkflowAction) {
181
            $id = $item->WorkflowID;
0 ignored issues
show
Documentation introduced by
The property WorkflowID does not exist on object<Symbiote\Advanced...Objects\WorkflowAction>. 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...
182
            return DataObject::get_by_id(WorkflowInstance::class, $id);
183
        } elseif (is_object($item) && ($item->hasExtension(WorkflowApplicable::class)
184
                || $item->hasExtension(FileWorkflowApplicable::class))
185
        ) {
186
            $filter = sprintf(
187
                '"TargetClass" = \'%s\' AND "TargetID" = %d',
188
                Convert::raw2sql(ClassInfo::baseDataClass($item)),
0 ignored issues
show
Deprecated Code introduced by
The method SilverStripe\Core\ClassInfo::baseDataClass() has been deprecated with message: 4.0..5.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
189
                $item->ID
190
            );
191
            $complete = $includeComplete ? 'OR "WorkflowStatus" = \'Complete\' ' : '';
192
            return DataObject::get_one(
193
                WorkflowInstance::class,
194
                $filter . ' AND ("WorkflowStatus" = \'Active\' OR "WorkflowStatus"=\'Paused\' ' . $complete . ')'
195
            );
196
        }
197
    }
198
199
    /**
200
     * Get all the workflow action instances for an item
201
     *
202
     * @return DataList|null
203
     */
204
    public function getWorkflowHistoryFor($item, $limit = null)
205
    {
206
        if ($active = $this->getWorkflowFor($item, true)) {
207
            $limit = $limit ? "0,$limit" : '';
208
            return $active->Actions('', 'ID DESC ', null, $limit);
0 ignored issues
show
Bug introduced by
The method Actions() does not exist on SilverStripe\ORM\DataObject. Did you maybe mean getCMSActions()?

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...
209
        }
210
    }
211
212
    /**
213
     * Get all the available workflow definitions
214
     *
215
     * @return DataList
216
     */
217
    public function getDefinitions()
218
    {
219
        return DataList::create(WorkflowDefinition::class);
220
    }
221
222
    /**
223
     * Given a transition ID, figure out what should happen to
224
     * the given $subject.
225
     *
226
     * In the normal case, this will load the current workflow instance for the object
227
     * and then transition as expected. However, in some cases (eg to start the workflow)
228
     * it is necessary to instead create a new instance.
229
     *
230
     * @param DataObject $target
231
     * @param int $transitionId
232
     * @throws Exception
233
     */
234
    public function executeTransition(DataObject $target, $transitionId)
235
    {
236
        $workflow   = $this->getWorkflowFor($target);
237
        $transition = DataObject::get_by_id(WorkflowTransition::class, $transitionId);
238
239
        if (!$transition) {
240
            throw new Exception(_t('WorkflowService.INVALID_TRANSITION_ID', "Invalid transition ID $transitionId"));
241
        }
242
243
        if (!$workflow) {
244
            throw new Exception(_t(
245
                'WorkflowService.INVALID_WORKFLOW_TARGET',
246
                "A transition was executed on a target that does not have a workflow."
247
            ));
248
        }
249
250
        if ($transition->Action()->WorkflowDefID != $workflow->DefinitionID) {
0 ignored issues
show
Bug introduced by
The method Action() does not exist on SilverStripe\ORM\DataObject. Did you maybe mean getCMSActions()?

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...
251
            throw new Exception(_t(
252
                'WorkflowService.INVALID_TRANSITION_WORKFLOW',
253
                "Transition #$transition->ID is not attached to workflow #$workflow->ID."
254
            ));
255
        }
256
257
        $workflow->performTransition($transition);
258
    }
259
260
    /**
261
     * Starts the workflow for the given data object, assuming it or a parent has
262
     * a definition specified.
263
     *
264
     * @param DataObject $object
265
     * @param int $workflowID
266
     */
267
    public function startWorkflow(DataObject $object, $workflowID = null)
268
    {
269
        $existing = $this->getWorkflowFor($object);
270
        if ($existing) {
271
            throw new ExistingWorkflowException(_t(
272
                'WorkflowService.EXISTING_WORKFLOW_ERROR',
273
                "That object already has a workflow running"
274
            ));
275
        }
276
277
        $definition = null;
278
        if ($workflowID) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $workflowID of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
279
            // Retrieve the workflow definition that has been triggered.
280
281
            $definition = $this->getDefinitionByID($object, $workflowID);
282
        }
283
        if (is_null($definition)) {
284
            // Fall back to the main workflow definition.
285
286
            $definition = $this->getDefinitionFor($object);
287
        }
288
289
        if ($definition) {
290
            $instance = new WorkflowInstance();
291
            $instance->beginWorkflow($definition, $object);
292
            $instance->execute();
293
        }
294
    }
295
296
    /**
297
     * Get all the workflows that this user is responsible for
298
     *
299
     * @param Member $user The user to get workflows for
300
     * @return ArrayList The list of workflow instances this user owns
301
     */
302
    public function usersWorkflows(Member $user)
303
    {
304
305
        $groupIds = $user->Groups()->column('ID');
306
307
        $groupInstances = null;
308
309
        $filter = array('');
0 ignored issues
show
Unused Code introduced by
$filter 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...
310
311
        if (is_array($groupIds)) {
312
            $groupInstances = DataList::create(WorkflowInstance::class)
313
                ->filter(array('Group.ID:ExactMatchMulti' => $groupIds))
314
                ->where('"WorkflowStatus" != \'Complete\'');
315
        }
316
317
        $userInstances = DataList::create(WorkflowInstance::class)
318
            ->filter(array('Users.ID:ExactMatch' => $user->ID))
319
            ->where('"WorkflowStatus" != \'Complete\'');
320
321
        if ($userInstances) {
322
            $userInstances = $userInstances->toArray();
323
        } else {
324
            $userInstances = array();
325
        }
326
327
        if ($groupInstances) {
328
            $groupInstances = $groupInstances->toArray();
329
        } else {
330
            $groupInstances = array();
331
        }
332
333
        $all = array_merge($groupInstances, $userInstances);
334
335
        return ArrayList::create($all);
336
    }
337
338
    /**
339
     * Get items that the passed-in user has awaiting for them to action
340
     *
341
     * @param Member $member
0 ignored issues
show
Bug introduced by
There is no parameter named $member. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
342
     * @return DataList
343
     */
344
    public function userPendingItems(Member $user)
345
    {
346
        // Don't restrict anything for ADMIN users
347
        $userInstances = DataList::create(WorkflowInstance::class)
348
            ->where('"WorkflowStatus" != \'Complete\'')
349
            ->sort('LastEdited DESC');
350
351
        if (Permission::checkMember($user, 'ADMIN')) {
352
            return $userInstances;
353
        }
354
        $instances = new ArrayList();
355
        foreach ($userInstances as $inst) {
356
            $instToArray = $inst->getAssignedMembers();
357
            if (!count($instToArray)>0 || !in_array($user->ID, $instToArray->column())) {
358
                continue;
359
            }
360
            $instances->push($inst);
361
        }
362
363
        return $instances;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $instances; (SilverStripe\ORM\ArrayList) is incompatible with the return type documented by Symbiote\AdvancedWorkflo...rvice::userPendingItems of type SilverStripe\ORM\DataList.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
364
    }
365
366
    /**
367
     * Get items that the passed-in user has submitted for workflow review
368
     *
369
     * @param Member $member
0 ignored issues
show
Bug introduced by
There is no parameter named $member. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
370
     * @return DataList
371
     */
372
    public function userSubmittedItems(Member $user)
373
    {
374
        $userInstances = DataList::create(WorkflowInstance::class)
375
            ->where('"WorkflowStatus" != \'Complete\'')
376
            ->sort('LastEdited DESC');
377
378
        // Restrict the user if they're not an ADMIN.
379
        if (!Permission::checkMember($user, 'ADMIN')) {
380
            $userInstances = $userInstances->filter('InitiatorID:ExactMatch', $user->ID);
381
        }
382
383
        return $userInstances;
384
    }
385
386
    /**
387
     * Generate a workflow definition based on a template
388
     *
389
     * @param WorkflowDefinition $definition
390
     * @param string $templateName
391
     * @return WorkflowDefinition|null
392
     */
393
    public function defineFromTemplate(WorkflowDefinition $definition, $templateName)
394
    {
395
        $template = null;
0 ignored issues
show
Unused Code introduced by
$template 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...
396
        /* @var $template WorkflowTemplate */
397
398
        if (!is_array($this->templates)) {
399
            return;
400
        }
401
402
        $template = $this->getNamedTemplate($templateName);
403
404
        if (!$template) {
405
            return;
406
        }
407
408
        $template->createRelations($definition);
409
410
        // Set the version and do the write at the end so that we don't trigger an infinite loop!!
411
        if (!$definition->Description) {
0 ignored issues
show
Documentation introduced by
The property Description does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
412
            $definition->Description = $template->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...
413
        }
414
        $definition->TemplateVersion = $template->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...
415
        $definition->RemindDays = $template->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...
416
        $definition->Sort = $template->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...
417
        $definition->write();
418
        return $definition;
419
    }
420
421
    /**
422
     * Reorders actions within a definition
423
     *
424
     * @param WorkflowDefinition|WorkflowAction $objects The objects to be reordered
425
     * @param array $newOrder An array of IDs of the actions in the order they should be.
426
     */
427
    public function reorder($objects, $newOrder)
428
    {
429
        $sortVals = array_values($objects->map('ID', 'Sort')->toArray());
430
        sort($sortVals);
431
432
        // save the new ID values - but only use existing sort values to prevent
433
        // conflicts with items not in the table
434
        foreach ($newOrder as $key => $id) {
435
            if (!$id) {
436
                continue;
437
            }
438
            $object = $objects->find('ID', $id);
439
            $object->Sort = $sortVals[$key];
440
            $object->write();
441
        }
442
    }
443
444
    /**
445
     *
446
     * @return array
447
     */
448
    public function providePermissions()
449
    {
450
        return array(
451
            'CREATE_WORKFLOW' => array(
452
                'name' => _t('AdvancedWorkflow.CREATE_WORKFLOW', 'Create workflow'),
453
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
454
                'help' => _t('AdvancedWorkflow.CREATE_WORKFLOW_HELP', 'Users can create workflow definitions'),
455
                'sort' => 0
456
            ),
457
            'DELETE_WORKFLOW' => array(
458
                'name' => _t('AdvancedWorkflow.DELETE_WORKFLOW', 'Delete workflow'),
459
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
460
                'help' => _t(
461
                    'AdvancedWorkflow.DELETE_WORKFLOW_HELP',
462
                    'Users can delete workflow definitions and active workflows'
463
                ),
464
                'sort' => 1
465
            ),
466
            'APPLY_WORKFLOW' => array(
467
                'name' => _t('AdvancedWorkflow.APPLY_WORKFLOW', 'Apply workflow'),
468
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
469
                'help' => _t('AdvancedWorkflow.APPLY_WORKFLOW_HELP', 'Users can apply workflows to items'),
470
                'sort' => 2
471
            ),
472
            'VIEW_ACTIVE_WORKFLOWS' => array(
473
                'name'     => _t('AdvancedWorkflow.VIEWACTIVE', 'View active workflows'),
474
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
475
                'help'     => _t(
476
                    'AdvancedWorkflow.VIEWACTIVEHELP',
477
                    'Users can view active workflows via the workflows admin panel'
478
                ),
479
                'sort'     => 3
480
            ),
481
            'REASSIGN_ACTIVE_WORKFLOWS' => array(
482
                'name'     => _t('AdvancedWorkflow.REASSIGNACTIVE', 'Reassign active workflows'),
483
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
484
                'help'     => _t(
485
                    'AdvancedWorkflow.REASSIGNACTIVEHELP',
486
                    'Users can reassign active workflows to different users and groups'
487
                ),
488
                'sort'     => 4
489
            ),
490
            'EDIT_EMBARGOED_WORKFLOW' => array(
491
                'name'     => _t('AdvancedWorkflow.EDITEMBARGO', 'Editable embargoed item in workflow'),
492
                'category' => _t('AdvancedWorkflow.ADVANCED_WORKFLOW', 'Advanced Workflow'),
493
                'help'     => _t(
494
                    'AdvancedWorkflow.EDITEMBARGOHELP',
495
                    'Allow users to edit items that have been embargoed by a workflow'
496
                ),
497
                'sort'     => 5
498
            ),
499
        );
500
    }
501
}
502