Completed
Push — master ( 707b9c...0cc92a )
by vistart
20:07
created

TimestampTrait   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 74.32%

Importance

Changes 19
Bugs 1 Features 0
Metric Value
wmc 40
c 19
b 1
f 0
lcom 1
cbo 2
dl 0
loc 239
ccs 55
cts 74
cp 0.7432
rs 8.2608

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getIsExpired() 0 8 3
B removeIfExpired() 0 11 6
A onRemoveExpired() 0 4 1
A getCurrentDatetime() 0 5 1
A currentDatetime() 0 10 3
B offsetDatetime() 0 10 7
A getInitDatetime() 0 5 1
A initDatetime() 0 10 3
B isInitDatetime() 0 10 5
A onUpdateCurrentDatetime() 0 4 1
A getTimestampBehaviors() 0 11 1
A getCreatedAt() 0 8 2
A getCreatedAtRules() 0 9 2
A getUpdatedAt() 0 8 2
A getUpdatedAtRules() 0 9 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link http://vistart.name/
9
 * @copyright Copyright (c) 2016 vistart
10
 * @license http://vistart.name/license/
11
 */
12
13
namespace vistart\Models\traits;
14
15
use Closure;
16
use yii\behaviors\TimestampBehavior;
17
18
/**
19
 * Entity features concerning timestamp.
20
 * @property-read array $timestampBehaviors
21
 * @property-read string|int createdAt
22
 * @property-read string|int updatedAt
23
 * @property-read array $createdAtRules
24
 * @property-read array $updatedAtRules
25
 * @property-read boolean isExpired
26
 * @version 2.0
27
 * @author vistart <[email protected]>
28
 */
29
trait TimestampTrait
30
{
31
32
    /**
33
     * @var string the attribute that will receive datetime value
34
     * Set this property to false if you do not want to record the creation time.
35
     */
36
    public $createdAtAttribute = 'create_time';
37
38
    /**
39
     * @var string the attribute that will receive datetime value.
40
     * Set this property to false if you do not want to record the update time.
41
     */
42
    public $updatedAtAttribute = 'update_time';
43
44
    /**
45
     * @var integer Determine the format of timestamp.
46
     */
47
    public $timeFormat = 0;
48
    public static $timeFormatDatetime = 0;
49
    public static $timeFormatTimestamp = 1;
50
    public static $initDatetime = '1970-01-01 00:00:00';
51
    public static $initTimestamp = 0;
52
53
    /**
54
     * @var int|false the expiration in seconds, or false if it will not be expired.
55
     */
56
    public $expiredAt = false;
57
58
    /**
59
     * @var Closure
60
     */
61
    public $expiredRemovingCallback;
62
    public static $eventExpiredRemoved = 'expiredRemoved';
63
64
    /**
65
     * Check this entity whether expired.
66
     * @return boolean
67
     */
68 37
    public function getIsExpired()
69
    {
70 37
        $createdAt = $this->createdAt;
71 37
        if ($this->expiredAt === false || $createdAt === null) {
72 31
            return false;
73
        }
74 6
        return $this->offsetDatetime($this->currentDatetime(), -$this->expiredAt) > $createdAt;
75
    }
76
77
    /**
78
     * Remove myself if expired.
79
     * @return boolean
80
     */
81 36
    public function removeIfExpired()
82
    {
83 36
        if ($this->getIsExpired() && !$this->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
It seems like getIsNewRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
84 1
            if (($this->expiredRemovingCallback instanceof Closure || is_array($this->expiredRemovingCallback)) && is_callable($this->expiredRemovingCallback)) {
85 1
                $result = call_user_func($this->expiredRemovingCallback, $this);
0 ignored issues
show
Unused Code introduced by
$result 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...
86 1
            }
87 1
            $result = $this->delete();
0 ignored issues
show
Bug introduced by
It seems like delete() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
88 1
            $this->trigger(static::$eventExpiredRemoved, new \yii\base\ModelEvent(['data' => ['result' => $result]]));
0 ignored issues
show
Bug introduced by
It seems like trigger() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
89 1
        }
90 36
        return false;
91
    }
92
93 36
    public function onRemoveExpired($event)
94
    {
95 36
        return $event->sender->removeIfExpired();
96
    }
97
98
    /**
99
     * Get the current date & time in format of "Y-m-d H:i:s" or timestamp.
100
     * You can override this method to customize the return value.
101
     * @param \yii\base\ModelEvent $event
102
     * @return string Date & Time.
103
     * @since 1.1
104
     */
105 46
    public static function getCurrentDatetime($event)
106
    {
107 46
        $sender = $event->sender;
108 46
        return $sender->currentDatetime();
109
    }
110
111
    /**
112
     * Get current date & time, by current time format.
113
     * @return string|int Date & time string if format is datetime, or timestamp.
114
     */
115 46
    public function currentDatetime()
116
    {
117 46
        if ($this->timeFormat === self::$timeFormatDatetime) {
118 46
            return date('Y-m-d H:i:s');
119
        }
120
        if ($this->timeFormat === self::$timeFormatTimestamp) {
121
            return time();
122
        }
123
        return null;
124
    }
125
126
    /**
127
     * Get offset date & time, by current time format.
128
     * @param string|int $time Date &time string or timestamp.
129
     * @param int $offset Offset int seconds.
130
     * @return string|int Date & time string if format is datetime, or timestamp.
131
     */
132 6
    public function offsetDatetime($time = null, $offset = 0)
133
    {
134 6
        if ($this->timeFormat === self::$timeFormatDatetime) {
135 6
            return date('Y-m-d H:i:s', strtotime(($offset >= 0 ? "+$offset" : $offset) . " seconds", is_string($time) ? strtotime($time) : (is_int($time) ? $time : time())));
136
        }
137
        if ($this->timeFormat === self::$timeFormatTimestamp) {
138
            return (is_int($time) ? $time : time()) + $offset;
139
        }
140
        return null;
141
    }
142
143
    /**
144
     * Get init date & time in format of "Y-m-d H:i:s" or timestamp.s
145
     * @param \yii\base\ModelEvent $event
146
     * @return string|int
147
     */
148 8
    public static function getInitDatetime($event)
149
    {
150 8
        $sender = $event->sender;
151 8
        return $sender->initDatetime();
152
    }
153
154
    /**
155
     * Get init date & time, by current time format.
156
     * @return string|int Date & time string if format is datetime, or timestamp.
157
     */
158 8
    public function initDatetime()
159
    {
160 8
        if ($this->timeFormat === self::$timeFormatDatetime) {
161 8
            return static::$initDatetime;
162
        }
163
        if ($this->timeFormat === self::$timeFormatTimestamp) {
164
            return static::$initTimestamp;
165
        }
166
        return null;
167
    }
168
169
    /**
170
     * Check whether the attribute is init datetime.
171
     * @param mixed $attribute
172
     * @return boolean
173
     */
174 4
    protected function isInitDatetime($attribute)
175
    {
176 4
        if ($this->timeFormat === self::$timeFormatDatetime) {
177 4
            return $attribute == static::$initDatetime || $attribute == null;
178
        }
179
        if ($this->timeFormat === self::$timeFormatTimestamp) {
180
            return $attribute == static::$initTimestamp || $attribute == null;
181
        }
182
        return false;
183
    }
184
185
    /**
186
     * Get the current date & time in format of "Y-m-d H:i:s".
187
     * This method is ONLY used for being triggered by event. DO NOT call,
188
     * override or modify it directly, unless you know the consequences.
189
     * @param \yii\base\Event $event
190
     * @return string Date & Time.
191
     * @since 1.1
192
     */
193 46
    public function onUpdateCurrentDatetime($event)
194
    {
195 46
        return self::getCurrentDatetime($event);
0 ignored issues
show
Compatibility introduced by
$event of type object<yii\base\Event> is not a sub-type of object<yii\base\ModelEvent>. It seems like you assume a child class of the class yii\base\Event to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
196
    }
197
198
    /**
199
     * Behaviors associated with timestamp.
200
     * @return array behaviors
201
     */
202 13
    public function getTimestampBehaviors()
203
    {
204
        return [
205
            [
206 13
                'class' => TimestampBehavior::className(),
207 13
                'createdAtAttribute' => $this->createdAtAttribute,
208 13
                'updatedAtAttribute' => $this->updatedAtAttribute,
209 13
                'value' => [$this, 'onUpdateCurrentDatetime'],
210
            ]
211 13
        ];
212
    }
213
214
    /**
215
     * Get createdAt.
216
     * @return string timestamp
217
     */
218 37
    public function getCreatedAt()
219
    {
220 37
        $createdAtAttribute = $this->createdAtAttribute;
221 37
        if (!is_string($createdAtAttribute)) {
222
            return null;
223
        }
224 37
        return $this->$createdAtAttribute;
225
    }
226
227
    /**
228
     * Get rules associated with createdAtAttribute.
229
     * @return array rules
230
     */
231 13
    public function getCreatedAtRules()
232
    {
233 13
        if (!$this->createdAtAttribute) {
234
            return [];
235
        }
236
        return [
237 13
            [[$this->createdAtAttribute], 'safe'],
238 13
        ];
239
    }
240
241
    /**
242
     * Get updatedAt.
243
     * @return string timestamp
244
     */
245
    public function getUpdatedAt()
246
    {
247
        $updatedAtAttribute = $this->updatedAtAttribute;
248
        if (!is_string($updatedAtAttribute)) {
249
            return null;
250
        }
251
        return $this->$updatedAtAttribute;
252
    }
253
254
    /**
255
     * Get rules associated with updatedAtAttribute.
256
     * @return array rules
257
     */
258 13
    public function getUpdatedAtRules()
259
    {
260 13
        if (!$this->updatedAtAttribute) {
261 1
            return [];
262
        }
263
        return [
264 12
            [[$this->updatedAtAttribute], 'safe'],
265 12
        ];
266
    }
267
}
268