Completed
Push — master ( 9055a2...5efc13 )
by Lars
03:52
created

ActiveRecord::copy()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 1
crap 2
1
<?php
2
3
namespace voku\db;
4
5
use Arrayy\Arrayy;
6
use voku\db\exceptions\ActiveRecordException;
7
use voku\db\exceptions\FetchingException;
8
9
/**
10
 * A simple implement of active record via Arrayy.
11
 *
12
 * @method $this select(string $dbProperty)
13
 * @method $this eq(string $dbProperty, string | null $value = null)
14
 * @method $this from(string $table)
15
 * @method $this where(string $where)
16
 * @method $this having(string $having)
17
 * @method $this limit(int $start, int | null $end = null)
18
 *
19
 * @method $this equal(string $dbProperty, string $value)
20
 * @method $this notEqual(string $dbProperty, string $value)
21
 * @method $this ne(string $dbProperty, string $value)
22
 * @method $this greaterThan(string $dbProperty, int $value)
23
 * @method $this gt(string $dbProperty, int $value)
24
 * @method $this lessThan(string $dbProperty, int $value)
25
 * @method $this lt(string $dbProperty, int $value)
26
 * @method $this greaterThanOrEqual(string $dbProperty, int $value)
27
 * @method $this ge(string $dbProperty, int $value)
28
 * @method $this gte(string $dbProperty, int $value)
29
 * @method $this lessThanOrEqual(string $dbProperty, int $value)
30
 * @method $this le(string $dbProperty, int $value)
31
 * @method $this lte(string $dbProperty, int $value)
32
 * @method $this between(string $dbProperty, array $value)
33
 * @method $this like(string $dbProperty, string $value)
34
 * @method $this in(string $dbProperty, array $value)
35
 * @method $this notIn(string $dbProperty, array $value)
36
 * @method $this isnull(string $dbProperty)
37
 * @method $this isNotNull(string $dbProperty)
38
 * @method $this notNull(string $dbProperty)
39
 */
40
abstract class ActiveRecord extends Arrayy
41
{
42
  /**
43
   * @var DB static
44
   */
45
  protected static $db;
46
47
  /**
48
   * @var array <p>Mapping the function name and the operator, to build Expressions in WHERE condition.</p>
49
   *
50
   * call the function like this:
51
   * <pre>
52
   *   $user->isNotNull()->eq('id', 1);
53
   * </pre>
54
   *
55
   * the result in SQL:
56
   * <pre>
57
   *   WHERE user.id IS NOT NULL AND user.id = :ph1
58
   * </pre>
59
   */
60
  protected static $operators = array(
61
      'equal'              => '=',
62
      'eq'                 => '=',
63
      'notequal'           => '<>',
64
      'ne'                 => '<>',
65
      'greaterthan'        => '>',
66
      'gt'                 => '>',
67
      'lessthan'           => '<',
68
      'lt'                 => '<',
69
      'greaterthanorequal' => '>=',
70
      'ge'                 => '>=',
71
      'gte'                => '>=',
72
      'lessthanorequal'    => '<=',
73
      'le'                 => '<=',
74
      'lte'                => '<=',
75
      'between'            => 'BETWEEN',
76
      'like'               => 'LIKE',
77
      'in'                 => 'IN',
78
      'notin'              => 'NOT IN',
79
      'isnull'             => 'IS NULL',
80
      'isnotnull'          => 'IS NOT NULL',
81
      'notnull'            => 'IS NOT NULL',
82
  );
83
84
  /**
85
   * @var array <p>Part of the SQL, mapping the function name and the operator to build SQL Part.</p>
86
   *
87
   * <br />
88
   *
89
   * call the function like this:
90
   * <pre>
91
   *      $user->orderBy('id DESC', 'name ASC')->limit(2, 1);
92
   * </pre>
93
   *
94
   * the result in SQL:
95
   * <pre>
96
   *      ORDER BY id DESC, name ASC LIMIT 2,1
97
   * </pre>
98
   */
99
  protected $sqlParts = array(
100
      'select' => 'SELECT',
101
      'from'   => 'FROM',
102
      'set'    => 'SET',
103
      'where'  => 'WHERE',
104
      'group'  => 'GROUP BY',
105
      'having' => 'HAVING',
106
      'order'  => 'ORDER BY',
107
      'limit'  => 'LIMIT',
108
      'top'    => 'TOP',
109
  );
110
111
  /**
112
   * @var array <p>The default sql expressions values.</p>
113
   */
114
  protected $defaultSqlExpressions = array(
115
      'expressions' => array(),
116
      'wrap'        => false,
117
      'select'      => null,
118
      'insert'      => null,
119
      'update'      => null,
120
      'set'         => null,
121
      'delete'      => 'DELETE ',
122
      'join'        => null,
123
      'from'        => null,
124
      'values'      => null,
125
      'where'       => null,
126
      'having'      => null,
127
      'limit'       => null,
128
      'order'       => null,
129
      'group'       => null,
130
  );
131
132
  /**
133
   * @var array <p>Stored the Expressions of the SQL.</p>
134
   */
135
  protected $sqlExpressions = array();
136
137
  /**
138
   * @var string <p>The table name in database.</p>
139
   */
140
  protected $table;
141
142
  /**
143
   * @var string  <p>The primary key of this ActiveRecord, just support single primary key.</p>
144
   */
145
  protected $primaryKeyName = 'id';
146
147
  /**
148
   * @var array <p>Stored the dirty data of this object, when call "insert" or "update" function, will write this data
149
   *      into database.</p>
150
   */
151
  protected $dirty = array();
152
153
  /**
154
   * @var bool
155
   */
156
  protected static $new_data_are_dirty = true;
157
158
  /**
159
   * @var array <p>Stored the params will bind to SQL when call DB->query().</p>
160
   */
161
  protected $params = array();
162
163
  /**
164
   * @var ActiveRecordExpressions[] <p>Stored the configure of the relation, or target of the relation.</p>
165
   */
166
  protected $relations = array();
167
168
  /**
169
   * @var int <p>The count of bind params, using this count and const "PREFIX" (:ph) to generate place holder in SQL.</p>
170
   */
171
  private static $count = 0;
172
173
  const BELONGS_TO = 'belongs_to';
174
  const HAS_MANY   = 'has_many';
175
  const HAS_ONE    = 'has_one';
176
177
  const PREFIX = ':active_record';
178
179
  /**
180
   * @return array
181
   */
182
  public function getParams()
183
  {
184
    return $this->params;
185
  }
186
187
  /**
188
   * @return string
189
   */
190
  public function getPrimaryKeyName()
191
  {
192
    return $this->primaryKeyName;
193
  }
194
195
  /**
196
   * @return mixed|null
197
   */
198 13
  public function getPrimaryKey()
199
  {
200 13
    $id = $this->{$this->primaryKeyName};
201 13
    if ($id) {
202 12
      return $id;
203
    }
204
205 1
    return null;
206
  }
207
208
  /**
209
   * @param mixed $primaryKey
210
   * @param bool  $dirty
211
   *
212
   * @return $this
213
   */
214 1
  public function setPrimaryKey($primaryKey, $dirty = true)
215
  {
216 1
    if ($dirty === true) {
217 1
      $this->dirty[$this->primaryKeyName] = $primaryKey;
218 1
    } else {
219
      $this->array[$this->primaryKeyName] = $primaryKey;
220
    }
221
222 1
    return $this;
223
  }
224
225
  /**
226
   * @return string
227
   */
228
  public function getTable()
229
  {
230
    return $this->table;
231
  }
232
233
  /**
234
   * Function to reset the $params and $sqlExpressions.
235
   *
236
   * @return $this
237
   */
238 23
  public function reset()
239
  {
240 23
    $this->params = array();
241 23
    $this->sqlExpressions = array();
242
243 23
    return $this;
244
  }
245
246
  /**
247
   * Reset the dirty data.
248
   *
249
   * @return $this
250
   */
251 6
  public function resetDirty()
252
  {
253 6
    $this->dirty = array();
254
255 6
    return $this;
256
  }
257
258
  /**
259
   * set the DB connection.
260
   *
261
   * @param DB $db
262
   */
263
  public static function setDb($db)
264
  {
265
    self::$db = $db;
266
  }
267
268
  /**
269
   * Function to find one record and assign in to current object.
270
   *
271
   * @param mixed $id <p>
272
   *                  If call this function using this param, we will find the record by using this id.
273
   *                  If not set, just find the first record in database.
274
   *                  </p>
275
   *
276
   * @return false|$this <p>
277
   *                     If we could find the record, assign in to current object and return it,
278
   *                     otherwise return "false".
279
   *                     </p>
280
   */
281 11 View Code Duplication
  public function fetch($id = null)
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...
282
  {
283 11
    if ($id) {
284 6
      $this->reset()->eq($this->primaryKeyName, $id);
285 6
    }
286
287 11
    return self::query(
288 11
        $this->limit(1)->_buildSql(
0 ignored issues
show
Bug introduced by
The call to limit() misses a required argument $|.

This check looks for function calls that miss required arguments.

Loading history...
289
            array(
290 11
                'select',
291 11
                'from',
292 11
                'join',
293 11
                'where',
294 11
                'group',
295 11
                'having',
296 11
                'order',
297 11
                'limit',
298
            )
299 11
        ),
300 11
        $this->params,
301 11
        $this->reset(),
302
        true
303 11
    );
304
  }
305
306
  /**
307
   * @param string $query
308
   *
309
   * @return $this[]
310
   */
311 1
  public function fetchManyByQuery($query)
312
  {
313 1
    $list = $this->fetchByQuery($query);
314
315 1
    if (!$list || empty($list)) {
316
      return array();
317
    }
318
319 1
    return $list;
320
  }
321
322
  /**
323
   * @param string $query
324
   *
325
   * @return $this|null
326
   */
327 1
  public function fetchOneByQuery($query)
328
  {
329 1
    $list = $this->fetchByQuery($query);
330
331 1
    if (!$list || empty($list)) {
332
      return null;
333
    }
334
335 1
    if (is_array($list) && count($list) > 0) {
336 1
      $this->array = $list[0]->getArray();
337 1
    } else {
338
      $this->array = $list->getArray();
339
    }
340
341 1
    return $this;
342
  }
343
344
  /**
345
   * @param mixed $id
346
   *
347
   * @return $this
348
   *
349
   * @throws FetchingException <p>Will be thrown, if we can not find the id.</p>
350
   */
351 2
  public function fetchById($id)
352
  {
353 2
    $obj = $this->fetchByIdIfExists($id);
354 2
    if ($obj === null) {
355 1
      throw new FetchingException("No row with primary key '$id' in table '$this->table'.");
356
    }
357
358 1
    return $obj;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $obj; (boolean|voku\db\ActiveRecord|array) is incompatible with the return type documented by voku\db\ActiveRecord::fetchById of type voku\db\ActiveRecord.

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...
359
  }
360
361
  /**
362
   * @param mixed $id
363
   *
364
   * @return $this|null
365
   */
366 4
  public function fetchByIdIfExists($id)
367
  {
368 4
    $list = $this->fetch($id);
369
370 4
    if (!$list || $list->isEmpty()) {
371 2
      return null;
372
    }
373
374 2
    return $list;
375
  }
376
377
  /**
378
   * @param array $ids
379
   *
380
   * @return $this[]
381
   */
382 2
  public function fetchByIds(array $ids)
383
  {
384 2
    if (empty($ids)) {
385
      return array();
386
    }
387
388 2
    $list = $this->fetchAll($ids);
389 2
    if (is_array($list) && count($list) > 0) {
390 1
      return $list;
391
    }
392
393 1
    return array();
394
  }
395
396
  /**
397
   * @param string $query
398
   *
399
   * @return $this[]|$this
400
   */
401 2
  public function fetchByQuery($query)
402
  {
403 2
    $list = self::query(
404 2
        $query,
405 2
        $this->params,
406 2
        $this->reset()
407 2
    );
408
409 2
    if (is_array($list)) {
410 2
      if (count($list) === 0) {
411
        return array();
412
      }
413
414 2
      return $list;
415
    }
416
417
    $this->array = $list->getArray();
418
419
    return $this;
420
  }
421
422
  /**
423
   * @param array $ids
424
   *
425
   * @return $this[]
426
   */
427 1
  public function fetchByIdsPrimaryKeyAsArrayIndex(array $ids)
428
  {
429 1
    $result = $this->fetchAll($ids);
430
431 1
    $resultNew = array();
432 1
    foreach ($result as $item) {
0 ignored issues
show
Bug introduced by
The expression $result of type boolean|object<voku\db\ActiveRecord>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
433 1
      $resultNew[$item->getPrimaryKey()] = $item;
434 1
    }
435
436 1
    return $resultNew;
437
  }
438
439
  /**
440
   * Function to find all records in database.
441
   *
442
   * @param array|null $ids <p>
443
   *                        If call this function using this param, we will find the record by using this id's.
444
   *                        If not set, just find all records in database.
445
   *                        </p>
446
   *
447
   * @return $this[]
448
   */
449 6 View Code Duplication
  public function fetchAll(array $ids = null)
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...
450
  {
451 6
    if ($ids) {
452 3
      $this->reset()->in($this->primaryKeyName, $ids);
453 3
    }
454
455 6
    return self::query(
456 6
        $this->_buildSql(
457
            array(
458 6
                'select',
459 6
                'from',
460 6
                'join',
461 6
                'where',
462 6
                'groupBy',
463 6
                'having',
464 6
                'orderBy',
465 6
                'limit',
466
            )
467 6
        ),
468 6
        $this->params,
469 6
        $this->reset()
470 6
    );
471
  }
472
473
  /**
474
   * Function to delete current record in database.
475
   *
476
   * @return bool
477
   */
478 1
  public function delete()
479
  {
480 1
    return self::execute(
481 1
        $this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql(
482
            array(
483 1
                'delete',
484 1
                'from',
485 1
                'where',
486
            )
487 1
        ),
488 1
        $this->params
489 1
    );
490
  }
491
492
  /**
493
   * @param string $primaryKeyName
494
   *
495
   * @return $this
496
   */
497
  public function setPrimaryKeyName($primaryKeyName)
498
  {
499
    $this->primaryKeyName = $primaryKeyName;
500
501
    return $this;
502
  }
503
504
  /**
505
   * @param string $table
506
   */
507
  public function setTable($table)
508
  {
509
    $this->table = $table;
510
  }
511
512
  /**
513
   * Function to build update SQL, and update current record in database, just write the dirty data into database.
514
   *
515
   * @return bool|int <p>
516
   *                  If update was successful, it will return the affected rows as int,
517
   *                  otherwise it will return false or true (if there are no dirty data).
518
   *                  </p>
519
   */
520 2
  public function update()
521
  {
522 2
    if (count($this->dirty) == 0) {
523
      return true;
524
    }
525
526 2
    foreach ($this->dirty as $field => $value) {
527 2
      $this->addCondition($field, '=', $value, ',', 'set');
528 2
    }
529
530 2
    $result = self::execute(
531 2
        $this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql(
532
            array(
533 2
                'update',
534 2
                'set',
535 2
                'where',
536
            )
537 2
        ),
538 2
        $this->params
539 2
    );
540 2
    if ($result) {
541 2
      $this->resetDirty();
542 2
      $this->reset();
543
544 2
      return $result;
545
    }
546
547
    return false;
548
  }
549
550
  /**
551
   * @return $this
552
   */
553 1
  public static function fetchEmpty()
554
  {
555 1
    $class = get_called_class();
556 1
    return new $class;
557
  }
558
559
  /**
560
   * Function to build insert SQL, and insert current record into database.
561
   *
562
   * @return bool|int <p>
563
   *                  If insert was successful, it will return the new id,
564
   *                  otherwise it will return false or true (if there are no dirty data).
565
   *                  </p>
566
   */
567 4
  public function insert()
568
  {
569 4
    if (!self::$db instanceof DB) {
570
      self::$db = DB::getInstance();
571
    }
572
573 4
    if (count($this->dirty) === 0) {
574
      return true;
575
    }
576
577 4
    $value = $this->_filterParam($this->dirty);
578 4
    $this->insert = new ActiveRecordExpressions(
0 ignored issues
show
Documentation introduced by
The property insert does not exist on object<voku\db\ActiveRecord>. 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...
579
        array(
580 4
            'operator' => 'INSERT INTO ' . $this->table,
581 4
            'target'   => new ActiveRecordExpressionsWrap(array('target' => array_keys($this->dirty))),
582
        )
583 4
    );
584 4
    $this->values = new ActiveRecordExpressions(
0 ignored issues
show
Documentation introduced by
The property values does not exist on object<voku\db\ActiveRecord>. 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...
585
        array(
586 4
            'operator' => 'VALUES',
587 4
            'target'   => new ActiveRecordExpressionsWrap(array('target' => $value)),
588
        )
589 4
    );
590
591 4
    $result = self::execute($this->_buildSql(array('insert', 'values')), $this->params);
592 4
    if ($result) {
593 4
      $this->{$this->primaryKeyName} = $result;
594
595 4
      $this->resetDirty();
596 4
      $this->reset();
597
598 4
      return $result;
599
    }
600
601
    return false;
602
  }
603
604
  /**
605
   * Helper function to copy an existing active record (and insert it into the database).
606
   *
607
   * @param bool $insert
608
   *
609
   * @return $this
610
   */
611 2
  public function copy($insert = true)
612
  {
613 1
    $new = clone $this;
614
615 1
    if ($insert) {
616 1
      $new->setPrimaryKey(null);
617 1
      $id = $new->insert();
618 1
      $new->setPrimaryKey($id);
619 1
    }
620
621 2
    return $new;
622
  }
623
624
  /**
625
   * Helper function to exec sql.
626
   *
627
   * @param string $sql   <p>The SQL need to be execute.</p>
628
   * @param array  $param <p>The param will be bind to the sql statement.</p>
629
   *
630
   * @return bool|int|Result              <p>
631
   *                                      "Result" by "<b>SELECT</b>"-queries<br />
632
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
633
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
634
   *                                      "true" by e.g. "DROP"-queries<br />
635
   *                                      "false" on error
636
   *                                      </p>
637
   */
638 24
  public static function execute($sql, array $param = array())
639
  {
640 24
    if (!self::$db instanceof DB) {
641 1
      self::$db = DB::getInstance();
642 1
    }
643
644 24
    return self::$db->query($sql, $param);
645
  }
646
647
  /**
648
   * Helper function to query one record by sql and params.
649
   *
650
   * @param string            $sql    <p>
651
   *                                  The SQL query to find the record.
652
   *                                  </p>
653
   * @param array             $param  <p>
654
   *                                  The param will be bind to the $sql query.
655
   *                                  </p>
656
   * @param ActiveRecord|null $obj    <p>
657
   *                                  The object, if find record in database, we will assign the attributes into
658
   *                                  this object.
659
   *                                  </p>
660
   * @param bool              $single <p>
661
   *                                  If set to true, we will find record and fetch in current object, otherwise
662
   *                                  will find all records.
663
   *                                  </p>
664
   *
665
   * @return bool|$this|array
666
   */
667 17
  public static function query($sql, array $param = array(), ActiveRecord $obj = null, $single = false)
668
  {
669 17
    $result = self::execute($sql, $param);
670
671 17
    if (!$result) {
672
      return false;
673
    }
674
675 17
    $useObject = is_object($obj);
676 17
    if ($useObject === true) {
677 17
      $called_class = $obj;
678 17
    } else {
679
      $called_class = get_called_class();
680
    }
681
682 17
    self::setNewDataAreDirty(false);
683
684 17
    if ($single) {
685 11
      $return = $result->fetchObject($called_class, null, true);
0 ignored issues
show
Bug introduced by
It seems like $called_class defined by $obj on line 677 can also be of type null; however, voku\db\Result::fetchObject() does only seem to accept string|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
686 11
    } else {
687 8
      $return = $result->fetchAllObject($called_class, null);
0 ignored issues
show
Bug introduced by
It seems like $called_class defined by $obj on line 677 can also be of type null; however, voku\db\Result::fetchAllObject() does only seem to accept string|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
688
    }
689
690 17
    self::setNewDataAreDirty(true);
691
692 17
    return $return;
693
  }
694
695
  /**
696
   * Helper function to get relation of this object.
697
   * There was three types of relations: {BELONGS_TO, HAS_ONE, HAS_MANY}
698
   *
699
   * @param string $name <p>The name of the relation (the array key from the definition).</p>
700
   *
701
   * @return mixed
702
   *
703
   * @throws ActiveRecordException <p>If the relation can't be found .</p>
704
   */
705 3
  protected function &getRelation($name)
706
  {
707 3
    $relation = $this->relations[$name];
708
    if (
709
        $relation instanceof self
710 3
        ||
711
        (
712 2
            is_array($relation)
713 2
            &&
714 2
            $relation[0] instanceof self
715 2
        )
716 3
    ) {
717 3
      return $relation;
718
    }
719
720
    /* @var $obj ActiveRecord */
721 2
    $obj = new $relation[1];
722
723 2
    $this->relations[$name] = $obj;
724 2
    if (isset($relation[3]) && is_array($relation[3])) {
725 1
      foreach ((array)$relation[3] as $func => $args) {
726 1
        call_user_func_array(array($obj, $func), (array)$args);
727 1
      }
728 1
    }
729
730 2
    $backref = isset($relation[4]) ? $relation[4] : '';
731 2
    $relationInstanceOfSelf = ($relation instanceof self);
732
    if (
733
        $relationInstanceOfSelf === false
734 2
        &&
735 2
        self::HAS_ONE == $relation[0]
736 2
    ) {
737
738 1
      $this->relations[$name] = $obj->eq($relation[2], $this->{$this->primaryKeyName})->fetch();
739
740 1
      if ($backref) {
741
        $this->relations[$name] && $backref && $obj->__set($backref, $this);
742
      }
743
744 1
    } elseif (
745 2
        is_array($relation)
746 2
        &&
747 2
        self::HAS_MANY == $relation[0]
748 2
    ) {
749
750 2
      $this->relations[$name] = $obj->eq($relation[2], $this->{$this->primaryKeyName})->fetchAll();
751 2
      if ($backref) {
752 1
        foreach ($this->relations[$name] as $o) {
0 ignored issues
show
Bug introduced by
The expression $this->relations[$name] of type object<voku\db\ActiveRec...veRecord>|boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
753 1
          $o->__set($backref, $this);
754 1
        }
755 1
      }
756
757 2
    } elseif (
758
        $relationInstanceOfSelf === false
759 2
        &&
760 2
        self::BELONGS_TO == $relation[0]
761 2
    ) {
762
763 2
      $this->relations[$name] = $obj->eq($obj->primaryKeyName, $this->{$relation[2]})->fetch();
764
765 2
      if ($backref) {
766 1
        $this->relations[$name] && $backref && $obj->__set($backref, $this);
767 1
      }
768
769 2
    } else {
770
      throw new ActiveRecordException("Relation $name not found.");
771
    }
772
773 2
    return $this->relations[$name];
774
  }
775
776
  /**
777
   * Helper function to build SQL with sql parts.
778
   *
779
   * @param string       $n <p>The SQL part will be build.</p>
780
   * @param int          $i <p>The index of $n in $sql array.</p>
781
   * @param ActiveRecord $o <p>The reference to $this.</p>
782
   */
783 21
  private function _buildSqlCallback(&$n, $i, $o)
784
  {
785
    if (
786
        'select' === $n
787 21
        &&
788 15
        null === $o->$n
789 21
    ) {
790
791 14
      $n = strtoupper($n) . ' ' . $o->table . '.*';
792
793 14
    } elseif (
794
        (
795
            'update' === $n
796 21
            ||
797
            'from' === $n
798 21
        )
799 21
        &&
800 17
        null === $o->$n
801 21
    ) {
802
803 17
      $n = strtoupper($n) . ' ' . $o->table;
804
805 21
    } elseif ('delete' === $n) {
806
807 1
      $n = strtoupper($n) . ' ';
808
809 1
    } else {
810
811 21
      $n = (null !== $o->$n) ? $o->$n . ' ' : '';
812
813
    }
814 21
  }
815
816
  /**
817
   * Helper function to build SQL with sql parts.
818
   *
819
   * @param array $sqls <p>The SQL part will be build.</p>
820
   *
821
   * @return string
822
   */
823 21
  protected function _buildSql($sqls = array())
824
  {
825 21
    array_walk($sqls, array($this, '_buildSqlCallback'), $this);
826
827
    // DEBUG
828
    //echo 'SQL: ', implode(' ', $sqls), "\n", 'PARAMS: ', implode(', ', $this->params), "\n";
829
830 21
    return implode(' ', $sqls);
831
  }
832
833
  /**
834
   * Magic function to make calls witch in function mapping stored in $operators and $sqlPart.
835
   * also can call function of DB object.
836
   *
837
   * @param string $name <p>The name of the function.</p>
838
   * @param array  $args <p>The arguments of the function.</p>
839
   *
840
   * @return $this|mixed <p>Return the result of callback or the current object to make chain method calls.</p>
841
   *
842
   * @throws ActiveRecordException
843
   */
844 16
  public function __call($name, $args)
845
  {
846 16
    if (!self::$db instanceof DB) {
847
      self::$db = DB::getInstance();
848
    }
849
850 16
    $nameTmp = strtolower($name);
851
852 16
    if (array_key_exists($nameTmp, self::$operators)) {
853
854 14
      $this->addCondition(
855 14
          $args[0],
856 14
          self::$operators[$nameTmp],
857 14
          isset($args[1]) ? $args[1] : null,
858 14
          (is_string(end($args)) && 'or' === strtolower(end($args))) ? 'OR' : 'AND'
859 14
      );
860
861 16
    } elseif (array_key_exists($nameTmp = str_replace('by', '', $nameTmp), $this->sqlParts)) {
862
863 11
      $this->$name = new ActiveRecordExpressions(
864
          array(
865 11
              'operator' => $this->sqlParts[$nameTmp],
866 11
              'target'   => implode(', ', $args),
867
          )
868 11
      );
869
870 11
    } elseif (is_callable($callback = array(self::$db, $name))) {
871
872
      return call_user_func_array($callback, $args);
873
874
    } else {
875
876
      throw new ActiveRecordException("Method $name not exist.");
877
878
    }
879
880 16
    return $this;
881
  }
882
883
  /**
884
   * Make wrap when build the SQL expressions of WHERE.
885
   *
886
   * @param string $op <p>If given, this param will build one "ActiveRecordExpressionsWrap" and include the stored expressions add into WHERE,
887
   *                   otherwise it will stored the expressions into an array.</p>
888
   *
889
   * @return $this
890
   */
891 1
  public function wrap($op = null)
892
  {
893 1
    if (1 === func_num_args()) {
894 1
      $this->wrap = false;
0 ignored issues
show
Documentation introduced by
The property wrap does not exist on object<voku\db\ActiveRecord>. 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...
895 1
      if (is_array($this->expressions) && count($this->expressions) > 0) {
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
896 1
        $this->_addCondition(
897 1
            new ActiveRecordExpressionsWrap(
898
                array(
899 1
                    'delimiter' => ' ',
900 1
                    'target'    => $this->expressions,
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
901
                )
902 1
            ), 'or' === strtolower($op) ? 'OR' : 'AND'
903 1
        );
904 1
      }
905 1
      $this->expressions = array();
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
906 1
    } else {
907 1
      $this->wrap = true;
0 ignored issues
show
Documentation introduced by
The property wrap does not exist on object<voku\db\ActiveRecord>. 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...
908
    }
909
910 1
    return $this;
911
  }
912
913
  /**
914
   * Helper function to build place holder when make SQL expressions.
915
   *
916
   * @param mixed $value <p>The value will be bind to SQL, just store it in $this->params.</p>
917
   *
918
   * @return mixed $value
919
   */
920 18
  protected function _filterParam($value)
921
  {
922 18
    if (is_array($value)) {
923 8
      foreach ($value as $key => $val) {
924 8
        $this->params[$value[$key] = self::PREFIX . ++self::$count] = $val;
925 8
      }
926 18
    } elseif (is_string($value)) {
927 3
      $this->params[$ph = self::PREFIX . ++self::$count] = $value;
928 3
      $value = $ph;
929 3
    }
930
931 18
    return $value;
932
  }
933
934
  /**
935
   * Helper function to add condition into WHERE.
936
   *
937
   * @param string $field <p>The field name, the source of Expressions</p>
938
   * @param string $operator
939
   * @param mixed  $value <p>The target of the Expressions.</p>
940
   * @param string $op    <p>The operator to concat this Expressions into WHERE or SET statement.</p>
941
   * @param string $name  <p>The Expression will contact to.</p>
942
   */
943 14
  public function addCondition($field, $operator, $value, $op = 'AND', $name = 'where')
944
  {
945 14
    $value = $this->_filterParam($value);
946 14
    $exp = new ActiveRecordExpressions(
947
        array(
948 14
            'source'   => ('where' == $name ? $this->table . '.' : '') . $field,
949 14
            'operator' => $operator,
950 14
            'target'   => is_array($value)
951 14
                ? new ActiveRecordExpressionsWrap(
952 4
                    'between' === strtolower($operator)
953 4
                        ? array('target' => $value, 'start' => ' ', 'end' => ' ', 'delimiter' => ' AND ')
954 4
                        : array('target' => $value)
955 14
                ) : $value,
956
        )
957 14
    );
958 14
    if ($exp) {
959 14
      if (!$this->wrap) {
0 ignored issues
show
Documentation introduced by
The property wrap does not exist on object<voku\db\ActiveRecord>. 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...
960 14
        $this->_addCondition($exp, $op, $name);
961 14
      } else {
962 1
        $this->_addExpression($exp, $op);
963
      }
964 14
    }
965 14
  }
966
967
  /**
968
   * Helper function to add condition into JOIN.
969
   *
970
   * @param string $table <p>The join table name.</p>
971
   * @param string $on    <p>The condition of ON.</p>
972
   * @param string $type  <p>The join type, like "LEFT", "INNER", "OUTER".</p>
973
   *
974
   * @return $this
975
   */
976 1
  public function join($table, $on, $type = 'LEFT')
977
  {
978 1
    $this->join = new ActiveRecordExpressions(
0 ignored issues
show
Documentation introduced by
The property join does not exist on object<voku\db\ActiveRecord>. 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...
979
        array(
980 1
            'source'   => $this->join ?: '',
0 ignored issues
show
Documentation introduced by
The property join does not exist on object<voku\db\ActiveRecord>. 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...
981 1
            'operator' => $type . ' JOIN',
982 1
            'target'   => new ActiveRecordExpressions(
983 1
                array('source' => $table, 'operator' => 'ON', 'target' => $on)
984 1
            ),
985
        )
986 1
    );
987
988 1
    return $this;
989
  }
990
991
  /**
992
   * helper function to make wrapper. Stored the expression in to array.
993
   *
994
   * @param ActiveRecordExpressions $exp      <p>The expression will be stored.</p>
995
   * @param string                  $operator <p>The operator to concat this Expressions into WHERE statement.</p>
996
   */
997 1
  protected function _addExpression($exp, $operator)
998
  {
999
    if (
1000 1
        !is_array($this->expressions)
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1001 1
        ||
1002 1
        count($this->expressions) === 0
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1003 1
    ) {
1004 1
      $this->expressions = array($exp);
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1005 1
    } else {
1006 1
      $this->expressions[] = new ActiveRecordExpressions(array('operator' => $operator, 'target' => $exp));
0 ignored issues
show
Bug introduced by
The property expressions does not seem to exist. Did you mean defaultSqlExpressions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1007
    }
1008 1
  }
1009
1010
  /**
1011
   * helper function to add condition into WHERE.
1012
   *
1013
   * @param ActiveRecordExpressions $exp      <p>The expression will be concat into WHERE or SET statement.</p>
1014
   * @param string                  $operator <p>The operator to concat this Expressions into WHERE or SET statement.</p>
1015
   * @param string                  $name     <p>The Expression will contact to.</p>
1016
   */
1017 14
  protected function _addCondition($exp, $operator, $name = 'where')
1018
  {
1019 14
    if (!$this->$name) {
1020 14
      $this->$name = new ActiveRecordExpressions(array('operator' => strtoupper($name), 'target' => $exp));
1021 14
    } else {
1022 4
      $this->$name->target = new ActiveRecordExpressions(
1023
          array(
1024 4
              'source'   => $this->$name->target,
1025 4
              'operator' => $operator,
1026 4
              'target'   => $exp,
1027
          )
1028 4
      );
1029
    }
1030 14
  }
1031
1032
  /**
1033
   * @return array
1034
   */
1035 1
  public function getDirty()
1036
  {
1037 1
    return $this->dirty;
1038
  }
1039
1040
  /**
1041
   * @return bool
1042
   */
1043
  public static function isNewDataAreDirty()
1044
  {
1045
    return self::$new_data_are_dirty;
1046
  }
1047
1048
  /**
1049
   * @param bool $bool
1050
   */
1051 17
  public static function setNewDataAreDirty($bool)
1052
  {
1053 17
    self::$new_data_are_dirty = (bool)$bool;
1054 17
  }
1055
1056
  /**
1057
   * Magic function to SET values of the current object.
1058
   *
1059
   * @param mixed $var
1060
   * @param mixed $val
1061
   */
1062 23
  public function __set($var, $val)
1063
  {
1064
    if (
1065 23
        array_key_exists($var, $this->sqlExpressions)
1066
        ||
1067 23
        array_key_exists($var, $this->defaultSqlExpressions)
1068 23
    ) {
1069
1070 20
      $this->sqlExpressions[$var] = $val;
1071
1072 20
    } elseif (
1073 20
        array_key_exists($var, $this->relations)
1074 20
        &&
1075
        $val instanceof self
1076 20
    ) {
1077
1078 1
      $this->relations[$var] = $val;
1079
1080 1
    } else {
1081
1082 20
      $this->array[$var] = $val;
1083
1084 20
      if (self::$new_data_are_dirty === true) {
1085 9
        $this->dirty[$var] = $val;
1086 9
      }
1087
1088
    }
1089 23
  }
1090
1091
  /**
1092
   * Magic function to UNSET values of the current object.
1093
   *
1094
   * @param mixed $var
1095
   */
1096 1
  public function __unset($var)
1097
  {
1098 1
    if (array_key_exists($var, $this->sqlExpressions)) {
1099
      unset($this->sqlExpressions[$var]);
1100
    }
1101
1102 1
    if (isset($this->array[$var])) {
1103 1
      unset($this->array[$var]);
1104 1
    }
1105
1106 1
    if (isset($this->dirty[$var])) {
1107 1
      unset($this->dirty[$var]);
1108 1
    }
1109 1
  }
1110
1111
  /**
1112
   * Helper function for "GROUP BY".
1113
   *
1114
   * @param array $args
1115
   *
1116
   * @return $this
1117
   */
1118
  public function groupBy($args)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1119
  {
1120
    $this->__call('groupBy', func_get_args());
1121
1122
    return $this;
1123
  }
1124
1125
  /**
1126
   * Helper function for "ORDER BY".
1127
   *
1128
   * @param $args ...
1129
   *
1130
   * @return $this
1131
   */
1132 2
  public function orderBy($args)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1133
  {
1134 2
    $this->__call('orderBy', func_get_args());
1135
1136 2
    return $this;
1137
  }
1138
1139
  /**
1140
   * Magic function to GET the values of current object.
1141
   *
1142
   * @param $var
1143
   *
1144
   * @return mixed
1145
   */
1146 23
  public function &__get($var)
1147
  {
1148 23
    if (array_key_exists($var, $this->sqlExpressions)) {
1149 20
      return $this->sqlExpressions[$var];
1150
    }
1151
1152 23
    if (array_key_exists($var, $this->relations)) {
1153 3
      return $this->getRelation($var);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getRelation($var); of type voku\db\ActiveRecordExpr...iveRecord|boolean|array adds the type array to the return on line 1153 which is incompatible with the return type of the parent method Arrayy\Arrayy::__get of type object|integer|double|string|null|boolean.
Loading history...
1154
    }
1155
1156 23
    if (isset($this->dirty[$var])) {
1157 4
      return $this->dirty[$var];
1158
    }
1159
1160 23
    return parent::__get($var);
1161
  }
1162
}
1163