AbstractQueuedJob::setCustomConfig()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Symbiote\QueuedJobs\Services;
4
5
use stdClass;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\ORM\DataObject;
8
9
/**
10
 * A base implementation of a queued job that provides some convenience for implementations
11
 *
12
 * This implementation assumes that when you created your job class, you initialised the
13
 * jobData with relevant variables needed to process() your job later on in execution. If you do not,
14
 * please ensure you do before you queueJob() the job, to ensure the signature that is generated is 'correct'.
15
 *
16
 * @author Marcus Nyeholt <[email protected]>
17
 * @license BSD http://silverstripe.org/bsd-license/
18
 */
19
abstract class AbstractQueuedJob implements QueuedJob
20
{
21
    /**
22
     * @var stdClass
23
     */
24
    protected $jobData;
25
26
    /**
27
     * @var array
28
     */
29
    protected $messages = array();
30
31
    /**
32
     * @var int
33
     */
34
    protected $totalSteps = 0;
35
36
    /**
37
     * @var int
38
     */
39
    protected $currentStep = 0;
40
41
    /**
42
     * @var boolean
43
     */
44
    protected $isComplete = false;
45
46
    /**
47
     * Extensions can have a construct but don't have too.
48
     * Without a construct, it's impossible to create a job in the CMS
49
     * @var array params
50
     */
51
    public function __construct($params = array())
52
    {
53
    }
54
55
    /**
56
     * @return string
57
     */
58
    abstract public function getTitle();
59
60
    /**
61
     * Sets a data object for persisting by adding its id and type to the serialised vars
62
     *
63
     * @param DataObject $object
64
     * @param string $name A name to give it, if you want to store more than one
65
     */
66
    protected function setObject(DataObject $object, $name = 'SilverStripe\\Core\\Object')
67
    {
68
        $this->{$name . 'ID'} = $object->ID;
69
        $this->{$name . 'Type'} = $object->ClassName;
70
    }
71
72
    /**
73
     * @param string $name
74
     * @return DataObject|void
75
     */
76
    protected function getObject($name = 'SilverStripe\\Core\\Object')
77
    {
78
        $id = $this->{$name . 'ID'};
79
        $type = $this->{$name . 'Type'};
80
        if ($id) {
81
            return DataObject::get_by_id($type, $id);
82
        }
83
    }
84
85
    /**
86
     * Return a signature for this queued job
87
     *
88
     * @return string
89
     */
90
    public function getSignature()
91
    {
92
        return md5(get_class($this) . serialize($this->jobData));
93
    }
94
95
    /**
96
     * Generate a somewhat random signature
97
     *
98
     * useful if you're want to make sure something is always added
99
     *
100
     * @return string
101
     */
102
    protected function randomSignature()
103
    {
104
        return md5(get_class($this) . time() . mt_rand(0, 100000));
105
    }
106
107
    /**
108
     * By default jobs should just go into the default processing queue
109
     *
110
     * @return string
111
     */
112
    public function getJobType()
113
    {
114
        return QueuedJob::QUEUED;
115
    }
116
117
    /**
118
     * Performs setup tasks the first time this job is run.
119
     *
120
     * This is only executed once for every job. If you want to run something on every job restart, use the
121
     * {@link prepareForRestart} method.
122
     */
123
    public function setup()
124
    {
125
        $this->loadCustomConfig();
126
    }
127
128
    /**
129
     * Run when an already setup job is being restarted.
130
     */
131
    public function prepareForRestart()
132
    {
133
        $this->loadCustomConfig();
134
    }
135
136
    /**
137
     * Do some processing yourself!
138
     */
139
    abstract public function process();
140
141
    /**
142
     * Method for determining whether the job is finished - you may override it if there's
143
     * more to it than just this
144
     */
145
    public function jobFinished()
146
    {
147
        return $this->isComplete;
148
    }
149
150
    /**
151
     * Called when the job is determined to be 'complete'
152
     */
153
    public function afterComplete()
154
    {
155
    }
156
157
    /**
158
     * @return stdClass
159
     */
160
    public function getJobData()
161
    {
162
        // okay, we NEED to store the subsite ID if there's one available
163
        if (!$this->SubsiteID && class_exists('Subsite')) {
0 ignored issues
show
Documentation introduced by
The property SubsiteID does not exist on object<Symbiote\QueuedJo...ices\AbstractQueuedJob>. 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...
164
            /**
165
             * Note: This may need to be checked for 4.x compatibility
166
             */
167
            $this->SubsiteID = \Subsite::currentSubsiteID();
0 ignored issues
show
Documentation introduced by
The property SubsiteID does not exist on object<Symbiote\QueuedJo...ices\AbstractQueuedJob>. 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...
168
        }
169
170
        $data = new stdClass();
171
        $data->totalSteps = $this->totalSteps;
172
        $data->currentStep = $this->currentStep;
173
        $data->isComplete = $this->isComplete;
174
        $data->jobData = $this->jobData;
175
        $data->messages = $this->messages;
176
177
        return $data;
178
    }
179
180
    /**
181
     * @param int $totalSteps
182
     * @param int $currentStep
183
     * @param boolean $isComplete
184
     * @param stdClass $jobData
185
     * @param array $messages
186
     */
187
    public function setJobData($totalSteps, $currentStep, $isComplete, $jobData, $messages)
188
    {
189
        $this->totalSteps = $totalSteps;
190
        $this->currentStep = $currentStep;
191
        $this->isComplete = $isComplete;
192
        $this->jobData = $jobData;
193
        $this->messages = $messages;
194
    }
195
196
    /**
197
     * Gets custom config settings to use when running the job.
198
     *
199
     * @return array|null
200
     */
201
    public function getCustomConfig()
202
    {
203
        return $this->CustomConfig;
0 ignored issues
show
Documentation introduced by
The property CustomConfig does not exist on object<Symbiote\QueuedJo...ices\AbstractQueuedJob>. 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...
204
    }
205
206
    /**
207
     * Sets custom config settings to use when the job is run.
208
     *
209
     * @param array $config
210
     */
211
    public function setCustomConfig(array $config)
212
    {
213
        $this->CustomConfig = $config;
0 ignored issues
show
Documentation introduced by
The property CustomConfig does not exist on object<Symbiote\QueuedJo...ices\AbstractQueuedJob>. 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...
214
    }
215
216
    /**
217
     * Sets custom configuration settings from the job data.
218
     */
219
    private function loadCustomConfig()
220
    {
221
        $custom = $this->getCustomConfig();
222
223
        if (!is_array($custom)) {
224
            return;
225
        }
226
227
        foreach ($custom as $class => $settings) {
228
            foreach ($settings as $setting => $value) {
229
                Config::inst()->update($class, $setting, $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
230
            }
231
        }
232
    }
233
234
    /**
235
     * @param string $message
236
     * @param string $severity
237
     */
238
    public function addMessage($message, $severity = 'INFO')
239
    {
240
        $severity = strtoupper($severity);
241
        $this->messages[] = '[' . date('Y-m-d H:i:s') . "][$severity] $message";
242
    }
243
244
    /**
245
     * Convenience methods for setting and getting job data
246
     *
247
     * @param mixed $name
248
     * @param mixed $value
249
     */
250
    public function __set($name, $value)
251
    {
252
        if (!$this->jobData) {
253
            $this->jobData = new stdClass();
254
        }
255
        $this->jobData->$name = $value;
256
    }
257
258
    /**
259
     * Retrieve some job data
260
     *
261
     * @param mixed $name
262
     * @return mixed
263
     */
264
    public function __get($name)
265
    {
266
        return isset($this->jobData->$name) ? $this->jobData->$name : null;
267
    }
268
}
269