Completed
Push — develop ( dac91a...dc8379 )
by Novikov
9s
created

Cron   C

Complexity

Total Complexity 68

Size/Duplication

Total Lines 539
Duplicated Lines 7.24 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 68
c 3
b 1
f 1
lcom 1
cbo 0
dl 39
loc 539
rs 5.6756

31 Methods

Rating   Name   Duplication   Size   Complexity  
F parse() 0 78 18
A setCommand() 0 6 1
A getCommand() 0 4 1
A setDayOfMonth() 0 13 4
A getDayOfMonth() 0 4 1
A setDayOfWeek() 0 13 4
A getDayOfWeek() 0 4 1
A setHour() 13 13 4
A getHour() 0 4 1
A setMinute() 13 13 4
A getMinute() 0 4 1
A setMonth() 13 13 4
A getMonth() 0 4 1
A setComment() 0 6 1
A getComment() 0 4 1
A setLogFile() 0 6 1
A getLogFile() 0 4 1
A setErrorFile() 0 6 1
A getErrorFile() 0 4 1
A setLastRunTime() 0 6 1
A getLastRunTime() 0 4 1
A setErrorSize() 0 6 1
A getErrorSize() 0 4 1
A setLogSize() 0 6 1
A getLogSize() 0 4 1
A setStatus() 0 6 1
A getStatus() 0 4 1
A getExpression() 0 4 1
A isSuspended() 0 4 1
A setSuspended() 0 8 2
B __toString() 0 20 5

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

1
<?php
2
3
namespace FOA\CronBundle\Manager;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Cron represents a cron command. It holds:
9
 * - time data
10
 * - command
11
 * - comment
12
 * - log files
13
 * - cron execution status
14
 *
15
 * @author Novikov Viktor
16
 */
17
class Cron
18
{
19
    const ERROR_MINUTE = 1;
20
    const ERROR_HOUR = 2;
21
    const ERROR_DAY_OF_MONTH = 3;
22
    const ERROR_MONTH = 4;
23
    const ERROR_DAY_OF_WEEK = 5;
24
25
    /**
26
     * @var string
27
     */
28
    protected $minute = '*';
29
30
    /**
31
     * @var string
32
     */
33
    protected $hour = '*';
34
35
    /**
36
     * @var string
37
     */
38
    protected $dayOfMonth = '*';
39
40
    /**
41
     * @var string
42
     */
43
    protected $month = '*';
44
45
    /**
46
     * @var string
47
     */
48
    protected $dayOfWeek = '*';
49
50
    /**
51
     * @var string
52
     */
53
    protected $command;
54
55
    /**
56
     * @var string
57
     */
58
    protected $logFile = null;
59
60
    /**
61
     * The size of the log file
62
     *
63
     * @var string
64
     */
65
    protected $logSize = null;
66
67
    /**
68
     * @var string
69
     */
70
    protected $errorFile = null;
71
72
    /**
73
     * The size of the error file
74
     *
75
     * @var string
76
     */
77
    protected $errorSize = null;
78
79
    /**
80
     * The last run time based on when log files have been written
81
     *
82
     * @var int
83
     */
84
    protected $lastRunTime = null;
85
86
    /**
87
     * The status of the cron, based on the log files
88
     *
89
     * @var string
90
     */
91
    protected $status;
92
93
    /**
94
     * @var string
95
     */
96
    protected $comment;
97
98
    /**
99
     * isSuspended
100
     *
101
     * @var boolean
102
     * @access protected
103
     */
104
    protected $isSuspended = false;
105
106
    /**
107
     * Parses a cron line into a Cron instance
108
     *
109
     * @static
110
     *
111
     * @param $cron string The cron line
112
     *
113
     * @return Cron
114
     */
115
    public static function parse($cron)
116
    {
117
        if (substr($cron, 0, 12) == '#suspended: ') {
118
            $cron = substr($cron, 12);
119
            $isSuspended = true;
120
        }
121
122
        $parts = explode(' ', $cron);
123
124
        $command = implode(' ', array_slice($parts, 5));
125
126
        // extract comment
127
        if (strpos($command, '#')) {
128
            list($command, $comment) = explode('#', $command);
129
            $comment = trim($comment);
130
        }
131
132
        // extract error file
133
        if (strpos($command, '2>')) {
134
            list($command, $errorFile) = explode('2>', $command);
135
            $errorFile = trim($errorFile);
136
        }
137
138
        // extract log file
139
        if (strpos($command, '>')) {
140
            list($command, $logFile) = explode('>', $command);
141
            $logFile = trim($logFile);
142
        }
143
144
        // compute last run time, and file size
145
        $lastRunTime = null;
146
        $logSize = null;
147
        $errorSize = null;
148
        if (isset($logFile) && file_exists($logFile)) {
149
            $lastRunTime = filemtime($logFile);
150
            $logSize = filesize($logFile);
151
        }
152
        if (isset($errorFile) && file_exists($errorFile)) {
153
            $lastRunTime = max($lastRunTime ?: 0, filemtime($errorFile));
154
            $errorSize = filesize($errorFile);
155
        }
156
157
        // compute status
158
        $status = 'error';
159
        if (!$logSize && !$errorSize) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logSize of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $errorSize of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
160
            $status = 'unknown';
161
        } elseif (!$errorSize || $errorSize == 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errorSize of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
162
            $status = 'success';
163
        }
164
165
        // create cron instance
166
        $cron = new self();
167
        $cron->setMinute($parts[0])
168
            ->setHour($parts[1])
169
            ->setDayOfMonth($parts[2])
170
            ->setMonth($parts[3])
171
            ->setDayOfWeek($parts[4])
172
            ->setCommand(\trim($command))
173
            ->setLastRunTime($lastRunTime)
174
            ->setLogSize($logSize)
175
            ->setErrorSize($errorSize)
176
            ->setStatus($status);
177
178
        if (isset($isSuspended)) {
179
            $cron->setSuspended($isSuspended);
180
        }
181
        if (isset($comment)) {
182
            $cron->setComment($comment);
183
        }
184
        if (isset($logFile)) {
185
            $cron->setLogFile($logFile);
186
        }
187
        if (isset($errorFile)) {
188
            $cron->setErrorFile($errorFile);
189
        }
190
191
        return $cron;
192
    }
193
194
    /**
195
     * @param string $command
196
     *
197
     * @return $this
198
     */
199
    public function setCommand($command)
200
    {
201
        $this->command = $command;
202
203
        return $this;
204
    }
205
206
    /**
207
     * @return string
208
     */
209
    public function getCommand()
210
    {
211
        return $this->command;
212
    }
213
214
    /**
215
     * @param string $dayOfMonth
216
     *
217
     * @return $this
218
     * @throws InvalidArgumentException
219
     */
220
    public function setDayOfMonth($dayOfMonth)
221
    {
222
        if (is_numeric($dayOfMonth) && ($dayOfMonth < 1 || $dayOfMonth > 31)) {
223
            throw new InvalidArgumentException(
224
                sprintf('Invalid day of month "%d"', $dayOfMonth),
225
                self::ERROR_DAY_OF_MONTH
226
            );
227
        }
228
229
        $this->dayOfMonth = $dayOfMonth;
0 ignored issues
show
Documentation Bug introduced by
It seems like $dayOfMonth can also be of type integer or double. However, the property $dayOfMonth is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
230
231
        return $this;
232
    }
233
234
    /**
235
     * @return string
236
     */
237
    public function getDayOfMonth()
238
    {
239
        return $this->dayOfMonth;
240
    }
241
242
    /**
243
     * @param string $dayOfWeek
244
     *
245
     * @return $this
246
     * @throws InvalidArgumentException
247
     */
248
    public function setDayOfWeek($dayOfWeek)
249
    {
250
        if (is_numeric($dayOfWeek) && ($dayOfWeek < 0 || $dayOfWeek > 7)) {
251
            throw new InvalidArgumentException(
252
                sprintf('Invalid day of week "%d"', $dayOfWeek),
253
                self::ERROR_DAY_OF_WEEK
254
            );
255
        }
256
257
        $this->dayOfWeek = $dayOfWeek;
0 ignored issues
show
Documentation Bug introduced by
It seems like $dayOfWeek can also be of type integer or double. However, the property $dayOfWeek is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
258
259
        return $this;
260
    }
261
262
    /**
263
     * @return string
264
     */
265
    public function getDayOfWeek()
266
    {
267
        return $this->dayOfWeek;
268
    }
269
270
    /**
271
     * @param string $hour
272
     *
273
     * @return $this
274
     * @throws InvalidArgumentException
275
     */
276 View Code Duplication
    public function setHour($hour)
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...
277
    {
278
        if (is_numeric($hour) && ($hour < 0 || $hour > 23)) {
279
            throw new InvalidArgumentException(
280
                sprintf('Invalid hour "%d"', $hour),
281
                self::ERROR_HOUR
282
            );
283
        }
284
285
        $this->hour = $hour;
0 ignored issues
show
Documentation Bug introduced by
It seems like $hour can also be of type integer or double. However, the property $hour is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
286
287
        return $this;
288
    }
289
290
    /**
291
     * @return string
292
     */
293
    public function getHour()
294
    {
295
        return $this->hour;
296
    }
297
298
    /**
299
     * @param string $minute
300
     *
301
     * @return $this
302
     * @throws InvalidArgumentException
303
     */
304 View Code Duplication
    public function setMinute($minute)
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...
305
    {
306
        if (is_numeric($minute) && ($minute < 0 || $minute > 59)) {
307
            throw new InvalidArgumentException(
308
                sprintf('Invalid minute "%d"', $minute),
309
                self::ERROR_MINUTE
310
            );
311
        }
312
313
        $this->minute = $minute;
0 ignored issues
show
Documentation Bug introduced by
It seems like $minute can also be of type integer or double. However, the property $minute is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
314
315
        return $this;
316
    }
317
318
    /**
319
     * @return string
320
     */
321
    public function getMinute()
322
    {
323
        return $this->minute;
324
    }
325
326
    /**
327
     * @param string $month
328
     *
329
     * @return $this
330
     * @throws InvalidArgumentException
331
     */
332 View Code Duplication
    public function setMonth($month)
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...
333
    {
334
        if (is_numeric($month) && ($month < 1 || $month > 12)) {
335
            throw new InvalidArgumentException(
336
                sprintf('Invalid month "%d"', $month),
337
                self::ERROR_MONTH
338
            );
339
        }
340
341
        $this->month = $month;
0 ignored issues
show
Documentation Bug introduced by
It seems like $month can also be of type integer or double. However, the property $month is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
342
343
        return $this;
344
    }
345
346
    /**
347
     * @return string
348
     */
349
    public function getMonth()
350
    {
351
        return $this->month;
352
    }
353
354
    /**
355
     * @param string $comment
356
     *
357
     * @return $this
358
     */
359
    public function setComment($comment)
360
    {
361
        $this->comment = $comment;
362
363
        return $this;
364
    }
365
366
    /**
367
     * @return string
368
     */
369
    public function getComment()
370
    {
371
        return $this->comment;
372
    }
373
374
    /**
375
     * @param string $logFile
376
     *
377
     * @return $this
378
     */
379
    public function setLogFile($logFile)
380
    {
381
        $this->logFile = $logFile;
382
383
        return $this;
384
    }
385
386
    /**
387
     * @return string
388
     */
389
    public function getLogFile()
390
    {
391
        return $this->logFile;
392
    }
393
394
    /**
395
     * @param string $errorFile
396
     *
397
     * @return $this
398
     */
399
    public function setErrorFile($errorFile)
400
    {
401
        $this->errorFile = $errorFile;
402
403
        return $this;
404
    }
405
406
    /**
407
     * @return string
408
     */
409
    public function getErrorFile()
410
    {
411
        return $this->errorFile;
412
    }
413
414
    /**
415
     * @param int $lastRunTime
416
     *
417
     * @return $this
418
     */
419
    public function setLastRunTime($lastRunTime)
420
    {
421
        $this->lastRunTime = $lastRunTime;
422
423
        return $this;
424
    }
425
426
    /**
427
     * @return int
428
     */
429
    public function getLastRunTime()
430
    {
431
        return $this->lastRunTime;
432
    }
433
434
    /**
435
     * @param string $errorSize
436
     *
437
     * @return $this
438
     */
439
    public function setErrorSize($errorSize)
440
    {
441
        $this->errorSize = $errorSize;
442
443
        return $this;
444
    }
445
446
    /**
447
     * @return string
448
     */
449
    public function getErrorSize()
450
    {
451
        return $this->errorSize;
452
    }
453
454
    /**
455
     * @param string $logSize
456
     *
457
     * @return $this
458
     */
459
    public function setLogSize($logSize)
460
    {
461
        $this->logSize = $logSize;
462
463
        return $this;
464
    }
465
466
    /**
467
     * @return string
468
     */
469
    public function getLogSize()
470
    {
471
        return $this->logSize;
472
    }
473
474
    /**
475
     * @param string $status
476
     *
477
     * @return $this
478
     */
479
    public function setStatus($status)
480
    {
481
        $this->status = $status;
482
483
        return $this;
484
    }
485
486
    /**
487
     * @return string
488
     */
489
    public function getStatus()
490
    {
491
        return $this->status;
492
    }
493
494
    /**
495
     * Concatenate time data to get the time expression
496
     *
497
     * @return string
498
     */
499
    public function getExpression()
500
    {
501
        return sprintf('%s %s %s %s %s', $this->minute, $this->hour, $this->dayOfMonth, $this->month, $this->dayOfWeek);
502
    }
503
504
    /**
505
     * Gets the value of isSuspended
506
     *
507
     * @return boolean
508
     */
509
    public function isSuspended()
510
    {
511
        return $this->isSuspended;
512
    }
513
514
    /**
515
     * Sets the value of isSuspended
516
     *
517
     * @param boolean $isSuspended status
518
     *
519
     * @return Cron
520
     */
521
    public function setSuspended($isSuspended = true)
522
    {
523
        if ($this->isSuspended != $isSuspended) {
524
            $this->isSuspended = $isSuspended;
525
        }
526
527
        return $this;
528
    }
529
530
    /**
531
     * Transforms the cron instance into a cron line
532
     *
533
     * @return string
534
     */
535
    public function __toString()
536
    {
537
        $cronLine = '';
538
        if ($this->isSuspended()) {
539
            $cronLine .= '#suspended: ';
540
        }
541
542
        $cronLine .= $this->getExpression() . ' ' . $this->command;
543
        if ('' != $this->logFile) {
544
            $cronLine .= ' > ' . $this->logFile;
545
        }
546
        if ('' != $this->errorFile) {
547
            $cronLine .= ' 2> ' . $this->errorFile;
548
        }
549
        if ('' != $this->comment) {
550
            $cronLine .= ' #' . $this->comment;
551
        }
552
553
        return $cronLine;
554
    }
555
}
556