Completed
Pull Request — master (#230)
by Helpful
03:29
created

WorkflowInstance   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 699
Duplicated Lines 1.86 %

Coupling/Cohesion

Components 1
Dependencies 22
Metric Value
wmc 88
lcom 1
cbo 22
dl 13
loc 699
rs 1.2663

29 Methods

Rating   Name   Duplication   Size   Complexity  
C getCMSFields() 0 44 7
A fieldLabels() 0 10 1
A onBeforeWrite() 0 12 2
B updateWorkflow() 0 25 6
A getTarget() 0 12 4
A Target() 0 4 1
C beginWorkflow() 0 29 7
C execute() 0 45 7
A checkTransitions() 0 9 3
B performTransition() 0 29 2
A getAssignedMembers() 13 14 2
A canView() 0 20 4
A canEdit() 0 9 2
A canDelete() 0 12 3
D userHasAccess() 0 25 9
A canEditTarget() 0 6 2
A canViewTarget() 0 8 2
A canPublishTarget() 0 6 2
A validTransitions() 0 11 1
A getWorkflowFields() 0 16 1
A getFrontEndWorkflowFields() 0 9 1
B getFrontEndWorkflowActions() 0 27 4
A getFrontEndDataObject() 0 7 1
A getFrontEndRequiredFields() 0 7 1
A setFrontendFormRequirements() 0 5 1
A doFrontEndAction() 0 5 1
A getVersionedConnection() 0 13 2
A getCurrentAction() 0 12 2
C getMostRecentActionForUser() 0 29 7

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

1
<?php
2
/**
3
 * A WorkflowInstance is created whenever a user 'starts' a workflow.
4
 *
5
 * This 'start' is triggered automatically when the user clicks the relevant
6
 * button (eg 'apply for approval'). This creates a standalone object
7
 * that maintains the state of the workflow process.
8
 *
9
 * @method WorkflowDefinition Definition()
10
 * @method WorkflowActionInstance CurrentAction()
11
 * @method Member Initiator()
12
 *
13
 * @author  [email protected]
14
 * @license BSD License (http://silverstripe.org/bsd-license/)
15
 * @package advancedworkflow
16
 */
17
class WorkflowInstance extends DataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
18
{
19
20
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
21
        'Title'                => 'Varchar(128)',
22
        'WorkflowStatus'    => "Enum('Active,Paused,Complete,Cancelled','Active')",
23
        'TargetClass'        => 'Varchar(64)',
24
        'TargetID'            => 'Int',
25
    );
26
27
    private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
28
        'Definition'    => 'WorkflowDefinition',
29
        'CurrentAction' => 'WorkflowActionInstance',
30
        'Initiator'        => 'Member',
31
    );
32
33
    private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
34
        'Actions'        => 'WorkflowActionInstance',
35
    );
36
37
    /**
38
     * The list of users who are responsible for performing the current WorkflowAction
39
     *
40
     * @var array
41
     */
42
    private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
43
        'Users'            => 'Member',
44
        'Groups'        => 'Group'
45
    );
46
47
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
        'Title',
49
        'WorkflowStatus',
50
        'Created'
51
    );
52
53
    /**
54
     * Get the CMS view of the instance. This is used to display the log of
55
     * this workflow, and options to reassign if the workflow hasn't been
56
     * finished yet
57
     *
58
     * @return \FieldList
59
     */
60
    public function getCMSFields()
61
    {
62
        $fields = new FieldList();
63
64
        if (Permission::check('REASSIGN_ACTIVE_WORKFLOWS')) {
65
            if ($this->WorkflowStatus == 'Paused' || $this->WorkflowStatus == 'Active') {
0 ignored issues
show
Documentation introduced by
The property WorkflowStatus does not exist on object<WorkflowInstance>. 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...
66
                $cmsUsers = Member::mapInCMSGroups();
67
68
                $fields->push(new HiddenField('DirectUpdate', '', 1));
69
70
                $fields->push(new HeaderField('InstanceReassignHeader', _t('WorkflowInstance.REASSIGN_HEADER', 'Reassign workflow')));
71
                $fields->push(new CheckboxSetField('Users', _t('WorkflowDefinition.USERS', 'Users'), $cmsUsers));
72
                $fields->push(new TreeMultiselectField('Groups', _t('WorkflowDefinition.GROUPS', 'Groups'), 'Group'));
73
            }
74
        }
75
76
        if ($this->canEdit()) {
77
            $action = $this->CurrentAction();
78
            if ($action->exists()) {
79
                $actionFields = $this->getWorkflowFields();
80
                $fields->merge($actionFields);
81
82
                $transitions = $action->getValidTransitions();
83
                if ($transitions) {
84
                    $fields->replaceField('TransitionID', DropdownField::create("TransitionID", "Next action", $transitions->map()));
85
                }
86
            }
87
        }
88
89
        $items = WorkflowActionInstance::get()->filter(array(
90
            'Finished'        => 1,
91
            'WorkflowID'    => $this->ID
92
        ));
93
94
        $grid = new GridField(
95
            'Actions',
96
            _t('WorkflowInstance.ActionLogTitle', 'Log'),
97
            $items
98
        );
99
100
        $fields->push($grid);
101
102
        return $fields;
103
    }
104
105
    public function fieldLabels($includerelations = true)
106
    {
107
        $labels = parent::fieldLabels($includerelations);
108
        $labels['Title'] = _t('WorkflowInstance.TitleLabel', 'Title');
109
        $labels['WorkflowStatus'] = _t('WorkflowInstance.WorkflowStatusLabel', 'Workflow Status');
110
        $labels['TargetClass'] = _t('WorkflowInstance.TargetClassLabel', 'Target Class');
111
        $labels['TargetID'] = _t('WorkflowInstance.TargetIDLabel', 'Target');
112
113
        return $labels;
114
    }
115
116
    /**
117
     * See if we've been saved in context of managing the workflow directly
118
     */
119
    public function onBeforeWrite()
120
    {
121
        parent::onBeforeWrite();
122
123
        $vars = $this->record;
124
125
        if (isset($vars['DirectUpdate'])) {
126
            // Unset now so that we don't end up in an infinite loop!
127
            unset($this->record['DirectUpdate']);
128
            $this->updateWorkflow($vars);
0 ignored issues
show
Documentation introduced by
$vars is of type array<string,?,{"DirectUpdate":"?"}>, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
        }
130
    }
131
132
    /**
133
     * Update the current state of the workflow
134
     *
135
     * Typically, this is triggered by someone modifiying the workflow instance via the modeladmin form
136
     * side of things when administering things, such as re-assigning or manually approving a stuck workflow
137
     *
138
     * Note that this is VERY similar to AdvancedWorkflowExtension::updateworkflow
139
     * but without the formy bits. These two implementations should PROBABLY
140
     * be merged
141
     *
142
     * @todo refactor with AdvancedWorkflowExtension
143
     *
144
     * @param type $data
145
     * @return
146
     */
147
    public function updateWorkflow($data)
148
    {
149
        $action = $this->CurrentAction();
150
151
        if (!$this->getTarget() || !$this->getTarget()->canEditWorkflow()) {
152
            return;
153
        }
154
155
        $allowedFields = $this->getWorkflowFields()->saveableFields();
156
        unset($allowedFields['TransitionID']);
157
        foreach ($allowedFields as $field) {
158
            $fieldName = $field->getName();
159
            $action->$fieldName = $data[$fieldName];
160
        }
161
        $action->write();
162
163
        $svc = singleton('WorkflowService');
164
        if (isset($data['TransitionID']) && $data['TransitionID']) {
165
            $svc->executeTransition($this->getTarget(), $data['TransitionID']);
166
        } else {
167
            // otherwise, just try to execute the current workflow to see if it
168
            // can now proceed based on user input
169
            $this->execute();
170
        }
171
    }
172
173
    /**
174
     * Get the target-object that this WorkflowInstance "points" to.
175
     *
176
     * Workflows are not restricted to being active on SiteTree objects,
177
     * so we need to account for being attached to anything.
178
     *
179
     * Sets Versioned::set_reading_mode() to allow fetching of Draft _and_ Published
180
     * content.
181
     *
182
     * @return (null | DataObject)
0 ignored issues
show
Documentation introduced by
The doc-type (null could not be parsed: Expected ")" at position 2, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
183
     */
184
    public function getTarget()
185
    {
186
        if ($this->TargetID && $this->TargetClass) {
0 ignored issues
show
Documentation introduced by
The property TargetID does not exist on object<WorkflowInstance>. 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...
Documentation introduced by
The property TargetClass does not exist on object<WorkflowInstance>. 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...
187
            $versionable = Injector::inst()->get($this->TargetClass)->has_extension('Versioned');
0 ignored issues
show
Documentation introduced by
The property TargetClass does not exist on object<WorkflowInstance>. 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...
188
            if ($versionable) {
189
                Versioned::set_reading_mode("Stage.Stage");
190
            }
191
192
            // Default
193
            return DataObject::get_by_id($this->TargetClass, $this->TargetID);
0 ignored issues
show
Documentation introduced by
The property TargetClass does not exist on object<WorkflowInstance>. 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...
Documentation introduced by
The property TargetID does not exist on object<WorkflowInstance>. 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...
194
        }
195
    }
196
197
    /**
198
     *
199
     * @see {@link {$this->getTarget()}
200
     * @return (null | DataObject)
0 ignored issues
show
Documentation introduced by
The doc-type (null could not be parsed: Expected ")" at position 2, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
201
     */
202
    public function Target()
203
    {
204
        return $this->getTarget();
205
    }
206
207
    /**
208
     * Start a workflow based on a particular definition for a particular object.
209
     *
210
     * The object is optional; if not specified, it is assumed that this workflow
211
     * is simply a task based checklist type of workflow.
212
     *
213
     * @param WorkflowDefinition $definition
214
     * @param DataObject $for
215
     */
216
    public function beginWorkflow(WorkflowDefinition $definition, DataObject $for=null)
217
    {
218
        if (!$this->ID) {
219
            $this->write();
220
        }
221
222
        if ($for && ($for->hasExtension('WorkflowApplicable') || $for->hasExtension('FileWorkflowApplicable'))) {
223
            $this->TargetClass = ClassInfo::baseDataClass($for);
0 ignored issues
show
Documentation introduced by
The property TargetClass does not exist on object<WorkflowInstance>. 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...
224
            $this->TargetID = $for->ID;
0 ignored issues
show
Documentation introduced by
The property TargetID does not exist on object<WorkflowInstance>. 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...
225
        }
226
227
        // lets create the first WorkflowActionInstance.
228
        $action = $definition->getInitialAction()->getInstanceForWorkflow();
229
        $action->WorkflowID   = $this->ID;
0 ignored issues
show
Documentation introduced by
The property WorkflowID does not exist on object<WorkflowActionInstance>. 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...
230
        $action->write();
231
232
        $title = $for && $for->hasField('Title') ?
233
                sprintf(_t('WorkflowInstance.TITLE_FOR_DO', '%s - %s'), $definition->Title, $for->Title) :
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<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...
234
                sprintf(_t('WorkflowInstance.TITLE_STUB', 'Instance #%s of %s'), $this->ID, $definition->Title);
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<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...
235
236
        $this->Title           = $title;
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<WorkflowInstance>. 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...
237
        $this->DefinitionID    = $definition->ID;
0 ignored issues
show
Documentation introduced by
The property DefinitionID does not exist on object<WorkflowInstance>. 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...
238
        $this->CurrentActionID = $action->ID;
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
239
        $this->InitiatorID     = Member::currentUserID();
0 ignored issues
show
Documentation introduced by
The property InitiatorID does not exist on object<WorkflowInstance>. 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...
240
        $this->write();
241
242
        $this->Users()->addMany($definition->Users());
0 ignored issues
show
Documentation Bug introduced by
The method Users does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Documentation Bug introduced by
The method Users does not exist on object<WorkflowDefinition>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
243
        $this->Groups()->addMany($definition->Groups());
0 ignored issues
show
Documentation Bug introduced by
The method Groups does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Documentation Bug introduced by
The method Groups does not exist on object<WorkflowDefinition>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
244
    }
245
246
    /**
247
     * Execute this workflow. In rare cases this will actually execute all actions,
248
     * but typically, it will stop and wait for the user to input something
249
     *
250
     * The basic process is to get the current action, and see whether it has been finished
251
     * by some process, if not it attempts to execute it.
252
     *
253
     * If it has been finished, we check to see if there's some transitions to follow. If there's
254
     * only one transition, then we execute that immediately.
255
     *
256
     * If there's multiple transitions, we just stop and wait for the user to manually
257
     * trigger a transition.
258
     *
259
     * If there's no transitions, we make the assumption that we've finished the workflow and
260
     * mark it as such.
261
     *
262
     *
263
     */
264
    public function execute()
265
    {
266
        if (!$this->CurrentActionID) {
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
267
            throw new Exception(
268
                sprintf(_t('WorkflowInstance.EXECUTE_EXCEPTION', 'Attempted to start an invalid workflow instance #%s!'), $this->ID)
269
            );
270
        }
271
272
        $action     = $this->CurrentAction();
273
        $transition = false;
274
275
        // if the action has already finished, it means it has either multiple (or no
276
        // transitions at the time), so a subsequent check should be run.
277
        if ($action->Finished) {
0 ignored issues
show
Documentation introduced by
The property Finished does not exist on object<WorkflowActionInstance>. 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...
278
            $transition = $this->checkTransitions($action);
279
        } else {
280
            $result = $action->BaseAction()->execute($this);
0 ignored issues
show
Documentation Bug introduced by
The method BaseAction does not exist on object<WorkflowActionInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
281
282
            // if the action was successful, then the action has finished running and
283
            // next transition should be run (if only one).
284
            // input.
285
            if ($result) {
286
                $action->MemberID = Member::currentUserID();
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<WorkflowActionInstance>. 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...
287
                $action->Finished = true;
0 ignored issues
show
Documentation introduced by
The property Finished does not exist on object<WorkflowActionInstance>. 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...
288
                $action->write();
289
                $transition = $this->checkTransitions($action);
290
            }
291
        }
292
293
        // if the action finished, and there's only one available transition then
294
        // move onto that step - otherwise check if the workflow has finished.
295
        if ($transition) {
296
            $this->performTransition($transition);
297
        } else {
298
            // see if there are any transitions available, even if they are not valid.
299
            if ($action->Finished && !count($action->BaseAction()->Transitions())) {
0 ignored issues
show
Documentation introduced by
The property Finished does not exist on object<WorkflowActionInstance>. 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...
Documentation Bug introduced by
The method BaseAction does not exist on object<WorkflowActionInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
300
                $this->WorkflowStatus  = 'Complete';
0 ignored issues
show
Documentation introduced by
The property WorkflowStatus does not exist on object<WorkflowInstance>. 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...
301
                $this->CurrentActionID = 0;
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
302
            } else {
303
                $this->WorkflowStatus = 'Paused';
0 ignored issues
show
Documentation introduced by
The property WorkflowStatus does not exist on object<WorkflowInstance>. 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...
304
            }
305
306
            $this->write();
307
        }
308
    }
309
310
    /**
311
     * Evaluate all the transitions of an action and determine whether we should
312
     * follow any of them yet.
313
     *
314
     * @param  WorkflowActionInstance $action
315
     * @return WorkflowTransition
316
     */
317
    protected function checkTransitions(WorkflowActionInstance $action)
318
    {
319
        $transitions = $action->getValidTransitions();
320
        // if there's JUST ONE transition, then we need should
321
        // immediately follow it.
322
        if ($transitions && $transitions->count() == 1) {
323
            return $transitions->First();
324
        }
325
    }
326
327
    /**
328
     * Transitions a workflow to the next step defined by the given transition.
329
     *
330
     * After transitioning, the action is 'executed', and next steps
331
     * determined.
332
     *
333
     * @param WorkflowTransition $transition
334
     */
335
    public function performTransition(WorkflowTransition $transition)
336
    {
337
        // first make sure that the transition is valid to execute!
338
        $action          = $this->CurrentAction();
339
        $allTransitions  = $action->BaseAction()->Transitions();
0 ignored issues
show
Documentation Bug introduced by
The method BaseAction does not exist on object<WorkflowActionInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
340
341
        $valid = $allTransitions->find('ID', $transition->ID);
342
        if (!$valid) {
343
            throw new Exception(
344
                sprintf(_t('WorkflowInstance.WORKFLOW_TRANSITION_EXCEPTION', 'Invalid transition state for action #%s'), $action->ID)
345
            );
346
        }
347
348
        $action->actionComplete($transition);
349
350
        $definition = DataObject::get_by_id('WorkflowAction', $transition->NextActionID);
0 ignored issues
show
Documentation introduced by
The property NextActionID does not exist on object<WorkflowTransition>. 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...
351
        $action = $definition->getInstanceForWorkflow();
352
        $action->WorkflowID   = $this->ID;
353
        $action->write();
354
355
        $this->CurrentActionID = $action->ID;
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
356
        $this->write();
357
        $this->components = array(); // manually clear the has_one cache
358
359
        $action->actionStart($transition);
360
361
        $transition->extend('onTransition');
362
        $this->execute();
363
    }
364
365
    /**
366
     * Returns a list of all Members that are assigned to this instance, either directly or via a group.
367
     *
368
     * @todo   This could be made more efficient.
369
     * @return ArrayList
370
     */
371 View Code Duplication
    public function getAssignedMembers()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
372
    {
373
        $list    = new ArrayList();
374
        $groups  = $this->Groups();
0 ignored issues
show
Documentation Bug introduced by
The method Groups does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
375
376
        $list->merge($this->Users());
0 ignored issues
show
Documentation Bug introduced by
The method Users does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
377
378
        foreach ($groups as $group) {
379
            $list->merge($group->Members());
380
        }
381
382
        $list->removeDuplicates();
383
        return $list;
384
    }
385
386
    /**
387
     *
388
     * @param \Member $member
389
     * @return boolean
390
     */
391
    public function canView($member=null)
392
    {
393
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by parameter $member on line 391 can be null; however, DataObject::extendedCan() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
394
        if ($extended !== null) {
395
            return $extended;
396
        }
397
398
        $hasAccess = $this->userHasAccess($member);
399
        /*
400
         * If the next action is AssignUsersToWorkflowAction, execute() resets all user+group relations.
401
         * Therefore current user no-longer has permission to view this WorkflowInstance in PendingObjects Gridfield, even though;
402
         * - She had permissions granted via the workflow definition to run the preceeding Action that took her here.
403
         */
404
        if (!$hasAccess) {
405
            if ($this->getMostRecentActionForUser($member)) {
406
                return true;
407
            }
408
        }
409
        return $hasAccess;
410
    }
411
412
    /**
413
     *
414
     * @param \Member $member
415
     * @return boolean
416
     */
417
    public function canEdit($member = null)
418
    {
419
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by parameter $member on line 417 can be null; however, DataObject::extendedCan() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
420
        if ($extended !== null) {
421
            return $extended;
422
        }
423
424
        return $this->userHasAccess($member);
425
    }
426
427
    /**
428
     *
429
     * @param \Member $member
430
     * @return boolean
431
     */
432
    public function canDelete($member = null)
433
    {
434
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by parameter $member on line 432 can be null; however, DataObject::extendedCan() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
435
        if ($extended !== null) {
436
            return $extended;
437
        }
438
439
        if (Permission::checkMember($member, "DELETE_WORKFLOW")) {
440
            return true;
441
        }
442
        return false;
443
    }
444
445
    /**
446
     * Checks whether the given user is in the list of users assigned to this
447
     * workflow
448
     *
449
     * @param $memberID
450
     */
451
    protected function userHasAccess($member)
452
    {
453
        if (!$member) {
454
            if (!Member::currentUserID()) {
455
                return false;
456
            }
457
            $member = Member::currentUser();
458
        }
459
460
        if (Permission::checkMember($member, "ADMIN")) {
461
            return true;
462
        }
463
464
        // This method primarily "protects" access to a WorkflowInstance, but assumes access only to be granted to users assigned-to that WorkflowInstance.
465
        // However; lowly authors (users entering items into a workflow) are not assigned - but we still wish them to see their submitted content.
466
        $inWorkflowGroupOrUserTables = ($member->inGroups($this->Groups()) || $this->Users()->find('ID', $member->ID));
0 ignored issues
show
Documentation Bug introduced by
The method Groups does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Documentation Bug introduced by
The method Users does not exist on object<WorkflowInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
467
        // This method is used in more than just the ModelAdmin. Check for the current controller to determine where canView() expectations differ
468
        if ($this->getTarget() && Controller::curr()->getAction() == 'index' && !$inWorkflowGroupOrUserTables) {
469
            if ($this->getVersionedConnection($this->getTarget()->ID, $member->ID)) {
470
                return true;
471
            }
472
            return false;
473
        }
474
        return $inWorkflowGroupOrUserTables;
475
    }
476
477
    /**
478
     * Can documents in the current workflow state be edited?
479
     */
480
    public function canEditTarget()
481
    {
482
        if ($this->CurrentActionID) {
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
483
            return $this->CurrentAction()->canEditTarget($this->getTarget());
0 ignored issues
show
Bug introduced by
It seems like $this->getTarget() can be null; however, canEditTarget() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
484
        }
485
    }
486
487
    /**
488
     * Does this action restrict viewing of the document?
489
     *
490
     * @return boolean
491
     */
492
    public function canViewTarget()
493
    {
494
        $action = $this->CurrentAction();
495
        if ($action) {
496
            return $action->canViewTarget($this->getTarget());
0 ignored issues
show
Bug introduced by
It seems like $this->getTarget() can be null; however, canViewTarget() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
497
        }
498
        return true;
499
    }
500
501
    /**
502
     * Does this action restrict the publishing of a document?
503
     *
504
     * @return boolean
505
     */
506
    public function canPublishTarget()
507
    {
508
        if ($this->CurrentActionID) {
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
509
            return $this->CurrentAction()->canPublishTarget($this->getTarget());
0 ignored issues
show
Bug introduced by
It seems like $this->getTarget() can be null; however, canPublishTarget() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
510
        }
511
    }
512
513
    /**
514
     * Get the current set of transitions that are valid for the current workflow state,
515
     * and are available to the current user.
516
     *
517
     * @return array
518
     */
519
    public function validTransitions()
520
    {
521
        $action    = $this->CurrentAction();
522
        $transitions = $action->getValidTransitions();
523
524
        // Filter by execute permission
525
        $self = $this;
526
        return $transitions->filterByCallback(function ($transition) use ($self) {
527
            return $transition->canExecute($self);
528
        });
529
    }
530
531
    /* UI RELATED METHODS */
532
533
    /**
534
     * Gets fields for managing this workflow instance in its current step
535
     *
536
     * @return FieldList
537
     */
538
    public function getWorkflowFields()
539
    {
540
        $action    = $this->CurrentAction();
541
        $options   = $this->validTransitions();
542
        $wfOptions = $options->map('ID', 'Title', ' ');
0 ignored issues
show
Unused Code introduced by
The call to ArrayList::map() has too many arguments starting with ' '.

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...
Unused Code introduced by
$wfOptions 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...
543
        $fields    = new FieldList();
544
545
        $fields->push(new HeaderField('WorkflowHeader', $action->Title));
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<WorkflowActionInstance>. 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...
546
547
        $fields->push(HiddenField::create('TransitionID', ''));
548
        // Let the Active Action update the fields that the user can interact with so that data can be
549
        // stored for the workflow.
550
        $action->updateWorkflowFields($fields);
551
552
        return $fields;
553
    }
554
555
    /**
556
     * Gets Front-End form fields from current Action
557
     *
558
     * @return FieldList
559
     */
560
    public function getFrontEndWorkflowFields()
561
    {
562
        $action = $this->CurrentAction();
563
564
        $fields = new FieldList();
565
        $action->updateFrontEndWorkflowFields($fields);
566
567
        return $fields;
568
    }
569
570
    /**
571
     * Gets Transitions for display as Front-End Form Actions
572
     *
573
     * @return FieldList
574
     */
575
    public function getFrontEndWorkflowActions()
576
    {
577
        $action    = $this->CurrentAction();
578
        $options   = $action->getValidTransitions();
579
        $actions   = new FieldList();
580
581
        foreach ($options as $option) {
582
            $btn = new FormAction("transition_{$option->ID}", $option->Title);
583
584
            // add cancel class to passive actions, this prevents js validation (using jquery.validate)
585
            if ($option->Type == 'Passive') {
586
                $btn->addExtraClass('cancel');
587
            }
588
589
            // disable the button if canExecute() returns false
590
            if (!$option->canExecute($this)) {
591
                $btn = $btn->performReadonlyTransformation();
592
                $btn->addExtraClass('hide');
593
            }
594
595
            $actions->push($btn);
596
        }
597
598
        $action->updateFrontEndWorkflowActions($actions);
599
600
        return $actions;
601
    }
602
603
    /**
604
     * Gets Front-End DataObject
605
     *
606
     * @return DataObject
607
     */
608
    public function getFrontEndDataObject()
609
    {
610
        $action = $this->CurrentAction();
611
        $obj = $action->getFrontEndDataObject();
612
613
        return $obj;
614
    }
615
616
    /**
617
     * Gets Front-End DataObject
618
     *
619
     * @return DataObject
620
     */
621
    public function getFrontEndRequiredFields()
622
    {
623
        $action = $this->CurrentAction();
624
        $validator = $action->getRequiredFields();
625
626
        return $validator;
627
    }
628
629
    public function setFrontendFormRequirements()
630
    {
631
        $action = $this->CurrentAction();
632
        $action->setFrontendFormRequirements();
633
    }
634
635
    public function doFrontEndAction(array $data, Form $form, SS_HTTPRequest $request)
636
    {
637
        $action = $this->CurrentAction();
638
        $action->doFrontEndAction($data, $form, $request);
639
    }
640
641
    /*
642
     * We need a way to "associate" an author with this WorkflowInstance and its Target() to see if she is "allowed" to view WorkflowInstances within GridFields
643
     * @see {@link $this->userHasAccess()}
644
     *
645
     * @param number $recordID
646
     * @param number $userID
647
     * @param number $wasPublished
648
     * @return boolean
649
     */
650
    public function getVersionedConnection($recordID, $userID, $wasPublished = 0)
651
    {
652
        // Turn this into an array and run through implode()
653
        $filter = "RecordID = {$recordID} AND AuthorID = {$userID} AND WasPublished = {$wasPublished}";
654
        $query = new SQLQuery();
0 ignored issues
show
Deprecated Code introduced by
The class SQLQuery has been deprecated with message: since version 4.0

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
655
        $query->setFrom('"SiteTree_versions"')->setSelect('COUNT("ID")')->setWhere($filter);
656
        $query->firstRow();
657
        $hasAuthored = $query->execute();
658
        if ($hasAuthored) {
659
            return true;
660
        }
661
        return false;
662
    }
663
664
    /*
665
     * Simple method to retrieve the current action, on the current WorkflowInstance
666
     */
667
    public function getCurrentAction()
668
    {
669
        $join = '"WorkflowAction"."ID" = "WorkflowActionInstance"."BaseActionID"';
670
        $action = WorkflowAction::get()
671
                    ->leftJoin('WorkflowActionInstance', $join)
672
                    ->where('"WorkflowActionInstance"."ID" = '.$this->CurrentActionID)
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<WorkflowInstance>. 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...
673
                    ->first();
674
        if (!$action) {
675
            return 'N/A';
676
        }
677
        return $action->getField('Title');
678
    }
679
680
    /**
681
     * Tells us if $member has had permissions over some part of the current WorkflowInstance.
682
     *
683
     * @param $member
684
     * @return \WorkflowAction | boolean
685
     */
686
    public function getMostRecentActionForUser($member = null)
687
    {
688
        if (!$member) {
689
            if (!Member::currentUserID()) {
690
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by WorkflowInstance::getMostRecentActionForUser of type WorkflowAction.

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...
691
            }
692
            $member = Member::currentUser();
693
        }
694
695
        // WorkflowActionInstances in reverse creation-order so we get the most recent one's first
696
        $history = $this->Actions()->filter(array(
0 ignored issues
show
Bug introduced by
The method Actions() does not exist on WorkflowInstance. Did you maybe mean getFrontEndWorkflowActions()?

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...
697
            'Finished' =>1,
698
            'BaseAction.ClassName' => 'AssignUsersToWorkflowAction'
699
        ))->Sort('Created', 'DESC');
700
701
        $i=0;
702
        foreach ($history as $inst) {
703
            /*
704
             * This iteration represents the 1st instance in the list - the most recent AssignUsersToWorkflowAction in $history.
705
             * If there's no match for $member here or on the _previous_ AssignUsersToWorkflowAction, then bail out:
706
             */
707
            $assignedMembers = $inst->BaseAction()->getAssignedMembers();
708
            if ($i<=1 && $assignedMembers->count()>0 && $assignedMembers->find('ID', $member->ID)) {
709
                return $inst;
710
            }
711
            ++$i;
712
        }
713
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by WorkflowInstance::getMostRecentActionForUser of type WorkflowAction.

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...
714
    }
715
}
716