Passed
Pull Request — 5.1 (#1748)
by guanguans
09:27
created

Attribute   F

Complexity

Total Complexity 136

Size/Duplication

Total Lines 635
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 269
dl 0
loc 635
rs 2
c 0
b 0
f 0
wmc 136

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getOrigin() 0 6 3
C autoWriteTimestamp() 0 34 14
A getData() 0 10 4
C getAttr() 0 47 14
A getPk() 0 3 1
A readonly() 0 9 2
A allowField() 0 9 2
B getChangedData() 0 24 10
A getKey() 0 8 3
A appendData() 0 16 4
D writeTransform() 0 57 20
A withAttribute() 0 15 3
B data() 0 39 11
B getRelationAttribute() 0 32 10
D readTransform() 0 62 22
A isAutoWriteTimestamp() 0 5 1
A isPk() 0 10 5
B setAttr() 0 25 7

How to fix   Complexity   

Complex Class

Complex classes like Attribute 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.

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

1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
12
namespace think\model\concern;
13
14
use InvalidArgumentException;
15
use think\db\Expression;
16
use think\Exception;
17
use think\Loader;
18
use think\model\Relation;
19
20
trait Attribute
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
21
{
22
    /**
23
     * 数据表主键 复合主键使用数组定义
24
     * @var string|array
25
     */
26
    protected $pk = 'id';
27
28
    /**
29
     * 数据表字段信息 留空则自动获取
30
     * @var array
31
     */
32
    protected $field = [];
33
34
    /**
35
     * JSON数据表字段
36
     * @var array
37
     */
38
    protected $json = [];
39
40
    /**
41
     * JSON数据取出是否需要转换为数组
42
     * @var bool
43
     */
44
    protected $jsonAssoc = false;
45
46
    /**
47
     * JSON数据表字段类型
48
     * @var array
49
     */
50
    protected $jsonType = [];
51
52
    /**
53
     * 数据表废弃字段
54
     * @var array
55
     */
56
    protected $disuse = [];
57
58
    /**
59
     * 数据表只读字段
60
     * @var array
61
     */
62
    protected $readonly = [];
63
64
    /**
65
     * 数据表字段类型
66
     * @var array
67
     */
68
    protected $type = [];
69
70
    /**
71
     * 当前模型数据
72
     * @var array
73
     */
74
    private $data = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "data" must be prefixed with an underscore
Loading history...
75
76
    /**
77
     * 修改器执行记录
78
     * @var array
79
     */
80
    private $set = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "set" must be prefixed with an underscore
Loading history...
81
82
    /**
83
     * 原始数据
84
     * @var array
85
     */
86
    private $origin = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "origin" must be prefixed with an underscore
Loading history...
87
88
    /**
89
     * 动态获取器
90
     * @var array
91
     */
92
    private $withAttr = [];
0 ignored issues
show
Coding Style introduced by
Private member variable "withAttr" must be prefixed with an underscore
Loading history...
93
94
    /**
95
     * 获取模型对象的主键
96
     * @access public
97
     * @return string|array
98
     */
99
    public function getPk()
100
    {
101
        return $this->pk;
102
    }
103
104
    /**
105
     * 判断一个字段名是否为主键字段
106
     * @access public
107
     * @param  string $key 名称
108
     * @return bool
109
     */
110
    protected function isPk($key)
111
    {
112
        $pk = $this->getPk();
113
        if (is_string($pk) && $pk == $key) {
114
            return true;
115
        } elseif (is_array($pk) && in_array($key, $pk)) {
116
            return true;
117
        }
118
119
        return false;
120
    }
121
122
    /**
123
     * 获取模型对象的主键值
124
     * @access public
125
     * @return integer
126
     */
127
    public function getKey()
128
    {
129
        $pk = $this->getPk();
130
        if (is_string($pk) && array_key_exists($pk, $this->data)) {
131
            return $this->data[$pk];
132
        }
133
134
        return;
135
    }
136
137
    /**
138
     * 设置允许写入的字段
139
     * @access public
140
     * @param  array|string|true $field 允许写入的字段 如果为true只允许写入数据表字段
141
     * @return $this
142
     */
143
    public function allowField($field)
144
    {
145
        if (is_string($field)) {
146
            $field = explode(',', $field);
147
        }
148
149
        $this->field = $field;
0 ignored issues
show
Documentation Bug introduced by
It seems like $field can also be of type true. However, the property $field is declared as type array. 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...
150
151
        return $this;
152
    }
153
154
    /**
155
     * 设置只读字段
156
     * @access public
157
     * @param  array|string $field 只读字段
158
     * @return $this
159
     */
160
    public function readonly($field)
161
    {
162
        if (is_string($field)) {
163
            $field = explode(',', $field);
164
        }
165
166
        $this->readonly = $field;
167
168
        return $this;
169
    }
170
171
    /**
172
     * 设置数据对象值
173
     * @access public
174
     * @param  mixed $data  数据或者属性名
175
     * @param  mixed $value 值
176
     * @return $this
177
     */
178
    public function data($data, $value = null)
179
    {
180
        if (is_string($data)) {
181
            $this->data[$data] = $value;
182
            return $this;
183
        }
184
185
        // 清空数据
186
        $this->data = [];
187
188
        if (is_object($data)) {
189
            $data = get_object_vars($data);
190
        }
191
192
        if ($this->disuse) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->disuse of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
193
            // 废弃字段
194
            foreach ((array) $this->disuse as $key) {
195
                if (array_key_exists($key, $data)) {
196
                    unset($data[$key]);
197
                }
198
            }
199
        }
200
201
        if (true === $value) {
202
            // 数据对象赋值
203
            foreach ($data as $key => $value) {
204
                $this->setAttr($key, $value, $data);
205
            }
206
        } elseif (is_array($value)) {
207
            foreach ($value as $name) {
208
                if (isset($data[$name])) {
209
                    $this->data[$name] = $data[$name];
210
                }
211
            }
212
        } else {
213
            $this->data = $data;
214
        }
215
216
        return $this;
217
    }
218
219
    /**
220
     * 批量设置数据对象值
221
     * @access public
222
     * @param  mixed $data  数据
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
223
     * @param  bool  $set   是否需要进行数据处理
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 3 found
Loading history...
224
     * @return $this
225
     */
226
    public function appendData($data, $set = false)
227
    {
228
        if ($set) {
229
            // 进行数据处理
230
            foreach ($data as $key => $value) {
231
                $this->setAttr($key, $value, $data);
232
            }
233
        } else {
234
            if (is_object($data)) {
235
                $data = get_object_vars($data);
236
            }
237
238
            $this->data = array_merge($this->data, $data);
239
        }
240
241
        return $this;
242
    }
243
244
    /**
245
     * 获取对象原始数据 如果不存在指定字段返回null
246
     * @access public
247
     * @param  string $name 字段名 留空获取全部
248
     * @return mixed
249
     */
250
    public function getOrigin($name = null)
251
    {
252
        if (is_null($name)) {
253
            return $this->origin;
254
        }
255
        return array_key_exists($name, $this->origin) ? $this->origin[$name] : null;
256
    }
257
258
    /**
259
     * 获取对象原始数据 如果不存在指定字段返回false
260
     * @access public
261
     * @param  string $name 字段名 留空获取全部
262
     * @return mixed
263
     * @throws InvalidArgumentException
264
     */
265
    public function getData($name = null)
266
    {
267
        if (is_null($name)) {
268
            return $this->data;
269
        } elseif (array_key_exists($name, $this->data)) {
270
            return $this->data[$name];
271
        } elseif (array_key_exists($name, $this->relation)) {
272
            return $this->relation[$name];
273
        }
274
        throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
275
    }
276
277
    /**
278
     * 获取变化的数据 并排除只读数据
279
     * @access public
280
     * @return array
281
     */
282
    public function getChangedData()
283
    {
284
        if ($this->force) {
285
            $data = $this->data;
286
        } else {
287
            $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
288
                if ((empty($a) || empty($b)) && $a !== $b) {
289
                    return 1;
290
                }
291
292
                return is_object($a) || $a != $b ? 1 : 0;
293
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
294
        }
295
296
        if (!empty($this->readonly)) {
297
            // 只读字段不允许更新
298
            foreach ($this->readonly as $key => $field) {
299
                if (isset($data[$field])) {
300
                    unset($data[$field]);
301
                }
302
            }
303
        }
304
305
        return $data;
306
    }
307
308
    /**
309
     * 修改器 设置数据对象值
310
     * @access public
311
     * @param  string $name  属性名
312
     * @param  mixed  $value 属性值
313
     * @param  array  $data  数据
314
     * @return void
315
     */
316
    public function setAttr($name, $value, $data = [])
317
    {
318
        if (isset($this->set[$name])) {
319
            return;
320
        }
321
322
        if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
323
            // 自动写入的时间戳字段
324
            $value = $this->autoWriteTimestamp($name);
325
        } else {
326
            // 检测修改器
327
            $method = 'set' . Loader::parseName($name, 1) . 'Attr';
328
329
            if (method_exists($this, $method)) {
330
                $value = $this->$method($value, array_merge($this->data, $data));
331
332
                $this->set[$name] = true;
333
            } elseif (isset($this->type[$name])) {
334
                // 类型转换
335
                $value = $this->writeTransform($value, $this->type[$name]);
336
            }
337
        }
338
339
        // 设置数据对象属性
340
        $this->data[$name] = $value;
341
    }
342
343
    /**
344
     * 是否需要自动写入时间字段
345
     * @access public
346
     * @param  bool $auto
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
347
     * @return $this
348
     */
349
    public function isAutoWriteTimestamp($auto)
350
    {
351
        $this->autoWriteTimestamp = $auto;
0 ignored issues
show
Bug Best Practice introduced by
The property autoWriteTimestamp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
352
353
        return $this;
354
    }
355
356
    /**
357
     * 自动写入时间戳
358
     * @access protected
359
     * @param  string $name 时间戳字段
360
     * @return mixed
361
     */
362
    protected function autoWriteTimestamp($name)
363
    {
364
        if (isset($this->type[$name])) {
365
            $type = $this->type[$name];
366
367
            if (strpos($type, ':')) {
368
                list($type, $param) = explode(':', $type, 2);
369
            }
370
371
            switch ($type) {
372
                case 'datetime':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
373
                case 'date':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
374
                    $format = !empty($param) ? $param : $this->dateFormat;
375
                    $format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u';
376
                    $value = $this->formatDateTime($format);
0 ignored issues
show
Bug introduced by
It seems like formatDateTime() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

376
                    /** @scrutinizer ignore-call */ 
377
                    $value = $this->formatDateTime($format);
Loading history...
377
                    break;
378
                case 'timestamp':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
379
                case 'integer':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
380
                default:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
381
                    $value = time();
382
                    break;
383
            }
384
        } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
385
            'datetime',
386
            'date',
387
            'timestamp',
388
        ])) {
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
389
            $format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u';
390
            $value  = $this->formatDateTime($this->dateFormat . $format);
391
        } else {
392
            $value = time();
393
        }
394
395
        return $value;
396
    }
397
398
    /**
399
     * 数据写入 类型转换
400
     * @access protected
401
     * @param  mixed        $value 值
402
     * @param  string|array $type  要转换的类型
403
     * @return mixed
404
     */
405
    protected function writeTransform($value, $type)
406
    {
407
        if (is_null($value)) {
408
            return;
409
        }
410
411
        if ($value instanceof Expression) {
412
            return $value;
413
        }
414
415
        if (is_array($type)) {
416
            list($type, $param) = $type;
417
        } elseif (strpos($type, ':')) {
418
            list($type, $param) = explode(':', $type, 2);
419
        }
420
421
        switch ($type) {
422
            case 'integer':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
423
                $value = (int) $value;
424
                break;
425
            case 'float':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
426
                if (empty($param)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
427
                    $value = (float) $value;
428
                } else {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
429
                    $value = (float) number_format($value, $param, '.', '');
430
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
431
                break;
432
            case 'boolean':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
433
                $value = (bool) $value;
434
                break;
435
            case 'timestamp':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
436
                if (!is_numeric($value)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
437
                    $value = strtotime($value);
438
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
439
                break;
440
            case 'datetime':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
441
                $format = !empty($param) ? $param : $this->dateFormat;
442
                $value  = is_numeric($value) ? $value : strtotime($value);
443
                $value  = $this->formatDateTime($format, $value);
444
                break;
445
            case 'object':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
446
                if (is_object($value)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
447
                    $value = json_encode($value, JSON_FORCE_OBJECT);
448
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
449
                break;
450
            case 'array':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
451
                $value = (array) $value;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
452
            case 'json':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
453
                $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
454
                $value  = json_encode($value, $option);
455
                break;
456
            case 'serialize':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
457
                $value = serialize($value);
458
                break;
459
        }
460
461
        return $value;
462
    }
463
464
    /**
465
     * 获取器 获取数据对象的值
466
     * @access public
467
     * @param  string $name 名称
468
     * @param  array  $item 数据
469
     * @return mixed
470
     * @throws InvalidArgumentException
471
     */
472
    public function getAttr($name, &$item = null)
473
    {
474
        try {
475
            $notFound = false;
476
            $value    = $this->getData($name);
477
        } catch (InvalidArgumentException $e) {
478
            $notFound = true;
479
            $value    = null;
480
        }
481
482
        // 检测属性获取器
483
        $fieldName = Loader::parseName($name);
484
        $method    = 'get' . Loader::parseName($name, 1) . 'Attr';
485
486
        if (isset($this->withAttr[$fieldName])) {
487
            if ($notFound && $relation = $this->isRelationAttr($name)) {
0 ignored issues
show
Bug introduced by
It seems like isRelationAttr() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

487
            if ($notFound && $relation = $this->/** @scrutinizer ignore-call */ isRelationAttr($name)) {
Loading history...
488
                $modelRelation = $this->$relation();
489
                $value         = $this->getRelationData($modelRelation);
0 ignored issues
show
Bug introduced by
The method getRelationData() does not exist on think\model\concern\Attribute. Did you maybe mean getRelationAttribute()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

489
                /** @scrutinizer ignore-call */ 
490
                $value         = $this->getRelationData($modelRelation);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
490
            }
491
492
            $closure = $this->withAttr[$fieldName];
493
            $value   = $closure($value, $this->data);
494
        } elseif (method_exists($this, $method)) {
495
            if ($notFound && $relation = $this->isRelationAttr($name)) {
496
                $modelRelation = $this->$relation();
497
                $value         = $this->getRelationData($modelRelation);
498
            }
499
500
            $value = $this->$method($value, $this->data);
501
        } elseif (isset($this->type[$name])) {
502
            // 类型转换
503
            $value = $this->readTransform($value, $this->type[$name]);
504
        } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
505
            if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
506
                'datetime',
507
                'date',
508
                'timestamp',
509
            ])) {
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
510
                $value = $this->formatDateTime($this->dateFormat, $value);
511
            } else {
512
                $value = $this->formatDateTime($this->dateFormat, $value, true);
513
            }
514
        } elseif ($notFound) {
515
            $value = $this->getRelationAttribute($name, $item);
516
        }
517
518
        return $value;
519
    }
520
521
    /**
522
     * 获取关联属性值
523
     * @access protected
524
     * @param  string   $name  属性名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
525
     * @param  array    $item  数据
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
526
     * @return mixed
527
     */
528
    protected function getRelationAttribute($name, &$item)
529
    {
530
        $relation = $this->isRelationAttr($name);
531
532
        if ($relation) {
533
            $modelRelation = $this->$relation();
534
            if ($modelRelation instanceof Relation) {
535
                $value = $this->getRelationData($modelRelation);
536
537
                if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->getBindAttr()) {
0 ignored issues
show
Bug introduced by
The method getBindAttr() does not exist on think\model\Relation. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

537
                if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->/** @scrutinizer ignore-call */ getBindAttr()) {
Loading history...
Bug Best Practice introduced by
The expression $item of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
538
539
                    foreach ($bindAttr as $key => $attr) {
540
                        $key = is_numeric($key) ? $attr : $key;
541
542
                        if (isset($item[$key])) {
543
                            throw new Exception('bind attr has exists:' . $key);
544
                        } else {
545
                            $item[$key] = $value ? $value->getAttr($attr) : null;
546
                        }
547
                    }
548
549
                    return false;
550
                }
551
552
                // 保存关联对象值
553
                $this->relation[$name] = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property relation does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
554
555
                return $value;
556
            }
557
        }
558
559
        throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name);
560
    }
561
562
    /**
563
     * 数据读取 类型转换
564
     * @access protected
565
     * @param  mixed        $value 值
566
     * @param  string|array $type  要转换的类型
567
     * @return mixed
568
     */
569
    protected function readTransform($value, $type)
570
    {
571
        if (is_null($value)) {
572
            return;
573
        }
574
575
        if (is_array($type)) {
576
            list($type, $param) = $type;
577
        } elseif (strpos($type, ':')) {
578
            list($type, $param) = explode(':', $type, 2);
579
        }
580
581
        switch ($type) {
582
            case 'integer':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
583
                $value = (int) $value;
584
                break;
585
            case 'float':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
586
                if (empty($param)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
587
                    $value = (float) $value;
588
                } else {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
589
                    $value = (float) number_format($value, $param, '.', '');
590
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
591
                break;
592
            case 'boolean':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
593
                $value = (bool) $value;
594
                break;
595
            case 'timestamp':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
596
                if (!is_null($value)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
597
                    $format = !empty($param) ? $param : $this->dateFormat;
598
                    $value  = $this->formatDateTime($format, $value, true);
599
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
600
                break;
601
            case 'datetime':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
602
                if (!is_null($value)) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
603
                    $format = !empty($param) ? $param : $this->dateFormat;
604
                    $value  = $this->formatDateTime($format, $value);
605
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
606
                break;
607
            case 'json':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
608
                $value = json_decode($value, true);
609
                break;
610
            case 'array':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
611
                $value = empty($value) ? [] : json_decode($value, true);
612
                break;
613
            case 'object':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
614
                $value = empty($value) ? new \stdClass() : json_decode($value);
615
                break;
616
            case 'serialize':
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
617
                try {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
618
                    $value = unserialize($value);
619
                } catch (\Exception $e) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
620
                    $value = null;
621
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
622
                break;
623
            default:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
624
                if (false !== strpos($type, '\\')) {
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
625
                    // 对象类型
626
                    $value = new $type($value);
627
                }
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
628
        }
629
630
        return $value;
631
    }
632
633
    /**
634
     * 设置数据字段获取器
635
     * @access public
636
     * @param  string|array $name       字段名
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 7 found
Loading history...
637
     * @param  callable     $callback   闭包获取器
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
638
     * @return $this
639
     */
640
    public function withAttribute($name, $callback = null)
641
    {
642
        if (is_array($name)) {
643
            foreach ($name as $key => $val) {
644
                $key = Loader::parseName($key);
645
646
                $this->withAttr[$key] = $val;
647
            }
648
        } else {
649
            $name = Loader::parseName($name);
650
651
            $this->withAttr[$name] = $callback;
652
        }
653
654
        return $this;
655
    }
656
}
657