Completed
Push — master ( 766562...9055a2 )
by Lars
03:30
created

ActiveRecord::groupBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 1
cts 1
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
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 13
  public function getPrimaryKeyName()
191
  {
192 13
    return $this->primaryKeyName;
193 13
  }
194 12
195
  /**
196
   * @return mixed|null
197 1
   */
198
  public function getPrimaryKey()
199
  {
200
    $id = $this->{$this->primaryKeyName};
201
    if ($id) {
202
      return $id;
203
    }
204
205
    return null;
206 1
  }
207
208 1
  /**
209 1
   * @param mixed $primaryKey
210 1
   * @param bool  $dirty
211
   *
212
   * @return $this
213
   */
214 1
  public function setPrimaryKey($primaryKey, $dirty = true)
215
  {
216
    if ($dirty === true) {
217
      $this->dirty[$this->primaryKeyName] = $primaryKey;
218
    } else {
219
      $this->array[$this->primaryKeyName] = $primaryKey;
220
    }
221
222
    return $this;
223
  }
224
225
  /**
226
   * @return string
227
   */
228
  public function getTable()
229
  {
230 23
    return $this->table;
231
  }
232 23
233 23
  /**
234
   * Function to reset the $params and $sqlExpressions.
235 23
   *
236
   * @return $this
237
   */
238
  public function reset()
239
  {
240
    $this->params = array();
241
    $this->sqlExpressions = array();
242
243 6
    return $this;
244
  }
245 6
246
  /**
247 6
   * Reset the dirty data.
248
   *
249
   * @return $this
250
   */
251
  public function resetDirty()
252
  {
253
    $this->dirty = array();
254
255
    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 11
   *                  If not set, just find the first record in database.
274
   *                  </p>
275 11
   *
276 6
   * @return false|$this <p>
277 6
   *                     If we could find the record, assign in to current object and return it,
278
   *                     otherwise return "false".
279 11
   *                     </p>
280 11
   */
281 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 11
  {
283 11
    if ($id) {
284 11
      $this->reset()->eq($this->primaryKeyName, $id);
285 11
    }
286 11
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 11
            array(
290
                'select',
291 11
                'from',
292 11
                'join',
293 11
                'where',
294
                'group',
295 11
                'having',
296
                'order',
297
                'limit',
298
            )
299
        ),
300
        $this->params,
301
        $this->reset(),
302
        true
303 1
    );
304
  }
305 1
306
  /**
307 1
   * @param string $query
308
   *
309
   * @return $this[]
310
   */
311 1
  public function fetchManyByQuery($query)
312
  {
313
    $list = $this->fetchByQuery($query);
314
315
    if (!$list || empty($list)) {
316
      return array();
317
    }
318
319 1
    return $list;
320
  }
321 1
322
  /**
323 1
   * @param string $query
324
   *
325
   * @return $this|null
326
   */
327 1
  public function fetchOneByQuery($query)
328 1
  {
329 1
    $list = $this->fetchByQuery($query);
330
331
    if (!$list || empty($list)) {
332
      return null;
333 1
    }
334
335
    if (is_array($list) && count($list) > 0) {
336
      $this->array = $list[0]->getArray();
337
    } else {
338
      $this->array = $list->getArray();
339
    }
340
341
    return $this;
342
  }
343 2
344
  /**
345 2
   * @param mixed $id
346 2
   *
347 1
   * @return $this
348
   *
349
   * @throws FetchingException <p>Will be thrown, if we can not find the id.</p>
350 1
   */
351
  public function fetchById($id)
352
  {
353
    $obj = $this->fetchByIdIfExists($id);
354
    if ($obj === null) {
355
      throw new FetchingException("No row with primary key '$id' in table '$this->table'.");
356
    }
357
358 4
    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 4
361
  /**
362 4
   * @param mixed $id
363 2
   *
364
   * @return $this|null
365
   */
366 2
  public function fetchByIdIfExists($id)
367
  {
368
    $list = $this->fetch($id);
369
370
    if (!$list || $list->isEmpty()) {
371
      return null;
372
    }
373
374 2
    return $list;
375
  }
376 2
377
  /**
378
   * @param array $ids
379
   *
380 2
   * @return $this[]
381 2
   */
382 1
  public function fetchByIds(array $ids)
383
  {
384
    if (empty($ids)) {
385 1
      return array();
386
    }
387
388
    $list = $this->fetchAll($ids);
389
    if (is_array($list) && count($list) > 0) {
390
      return $list;
391
    }
392
393 2
    return array();
394
  }
395 2
396 2
  /**
397 2
   * @param string $query
398 2
   *
399 2
   * @return $this[]|$this
400
   */
401 2
  public function fetchByQuery($query)
402 2
  {
403
    $list = self::query(
404
        $query,
405
        $this->params,
406 2
        $this->reset()
407
    );
408
409
    if (is_array($list)) {
410
      if (count($list) === 0) {
411
        return array();
412
      }
413
414
      return $list;
415
    }
416
417
    $this->array = $list->getArray();
418
419 1
    return $this;
420
  }
421 1
422
  /**
423 1
   * @param array $ids
424 1
   *
425 1
   * @return $this[]
426 1
   */
427
  public function fetchByIdsPrimaryKeyAsArrayIndex(array $ids)
428 1
  {
429
    $result = $this->fetchAll($ids);
430
431
    $resultNew = array();
432
    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
      $resultNew[$item->getPrimaryKey()] = $item;
434
    }
435
436
    return $resultNew;
437
  }
438
439
  /**
440
   * Function to find all records in database.
441 6
   *
442
   * @param array|null $ids <p>
443 6
   *                        If call this function using this param, we will find the record by using this id's.
444 3
   *                        If not set, just find all records in database.
445 3
   *                        </p>
446
   *
447 6
   * @return $this[]
448 6
   */
449 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 6
  {
451 6
    if ($ids) {
452 6
      $this->reset()->in($this->primaryKeyName, $ids);
453 6
    }
454 6
455 6
    return self::query(
456 6
        $this->_buildSql(
457 6
            array(
458
                'select',
459 6
                'from',
460 6
                'join',
461 6
                'where',
462 6
                'groupBy',
463
                'having',
464
                'orderBy',
465
                'limit',
466
            )
467
        ),
468
        $this->params,
469
        $this->reset()
470 1
    );
471
  }
472 1
473 1
  /**
474
   * Function to delete current record in database.
475 1
   *
476 1
   * @return bool
477 1
   */
478
  public function delete()
479 1
  {
480 1
    return self::execute(
481 1
        $this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql(
482
            array(
483
                'delete',
484
                'from',
485
                'where',
486
            )
487
        ),
488
        $this->params
489
    );
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 2
  /**
513
   * Function to build update SQL, and update current record in database, just write the dirty data into database.
514 2
   *
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 2
   *                  </p>
519 2
   */
520 2
  public function update()
521
  {
522 2
    if (count($this->dirty) == 0) {
523 2
      return true;
524
    }
525 2
526 2
    foreach ($this->dirty as $field => $value) {
527 2
      $this->addCondition($field, '=', $value, ',', 'set');
528
    }
529 2
530 2
    $result = self::execute(
531 2
        $this->eq($this->primaryKeyName, $this->{$this->primaryKeyName})->_buildSql(
532 2
            array(
533 2
                'update',
534 2
                'set',
535
                'where',
536 2
            )
537
        ),
538
        $this->params
539
    );
540
    if ($result) {
541
      $this->resetDirty();
542
      $this->reset();
543
544
      return $result;
545 1
    }
546
547 1
    return false;
548 1
  }
549
550
  /**
551
   * @return $this
552
   */
553
  public static function fetchEmpty()
554
  {
555
    $class = get_called_class();
556
    return new $class;
557
  }
558
559 4
  /**
560
   * Function to build insert SQL, and insert current record into database.
561 4
   *
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 4
   *                  </p>
566
   */
567
  public function insert()
568
  {
569 4
    if (!self::$db instanceof DB) {
570 4
      self::$db = DB::getInstance();
571
    }
572 4
573 4
    if (count($this->dirty) === 0) {
574
      return true;
575 4
    }
576 4
577
    $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 4
        array(
580
            '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 4
        array(
586
            'operator' => 'VALUES',
587 4
            'target'   => new ActiveRecordExpressionsWrap(array('target' => $value)),
588 4
        )
589
    );
590 4
591
    $result = self::execute($this->_buildSql(array('insert', 'values')), $this->params);
592
    if ($result) {
593
      $this->{$this->primaryKeyName} = $result;
594
595
      $this->resetDirty();
596
      $this->reset();
597
598
      return $result;
599
    }
600
601
    return false;
602
  }
603 1
604
  /**
605 1
   * Helper function to copy an existing active record (and insert it into the database).
606
   *
607 1
   * @param bool $insert
608 1
   *
609 1
   * @return $this
610 1
   */
611 1
  public function copy($insert = true)
612
  {
613 1
    $new = clone $this;
614
615
    if ($insert) {
616
      $new->setPrimaryKey(null);
617
      $id = $new->insert();
618
      $new->setPrimaryKey($id);
619
    }
620
621
    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 24
   * @return bool|int|Result              <p>
631
   *                                      "Result" by "<b>SELECT</b>"-queries<br />
632 24
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
633 1
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
634 1
   *                                      "true" by e.g. "DROP"-queries<br />
635
   *                                      "false" on error
636 24
   *                                      </p>
637
   */
638
  public static function execute($sql, array $param = array())
639
  {
640
    if (!self::$db instanceof DB) {
641
      self::$db = DB::getInstance();
642
    }
643
644
    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 17
   *                                  </p>
660
   * @param bool              $single <p>
661 17
   *                                  If set to true, we will find record and fetch in current object, otherwise
662
   *                                  will find all records.
663 17
   *                                  </p>
664
   *
665
   * @return bool|$this|array
666
   */
667 17
  public static function query($sql, array $param = array(), ActiveRecord $obj = null, $single = false)
668 17
  {
669 17
    $result = self::execute($sql, $param);
670 17
671
    if (!$result) {
672
      return false;
673
    }
674 17
675
    $useObject = is_object($obj);
676 17
    if ($useObject === true) {
677 11
      $called_class = $obj;
678 11
    } else {
679 8
      $called_class = get_called_class();
680
    }
681
682 17
    self::setNewDataAreDirty(false);
683
684 17
    if ($single) {
685
      $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
    } else {
687
      $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
    self::setNewDataAreDirty(true);
691
692
    return $return;
693
  }
694
695
  /**
696
   * Helper function to get relation of this object.
697 3
   * There was three types of relations: {BELONGS_TO, HAS_ONE, HAS_MANY}
698
   *
699 3
   * @param string $name <p>The name of the relation (the array key from the definition).</p>
700
   *
701
   * @return mixed
702 3
   *
703
   * @throws ActiveRecordException <p>If the relation can't be found .</p>
704 2
   */
705 2
  protected function &getRelation($name)
706 2
  {
707 2
    $relation = $this->relations[$name];
708 3
    if (
709 3
        $relation instanceof self
710
        ||
711
        (
712
            is_array($relation)
713 2
            &&
714
            $relation[0] instanceof self
715 2
        )
716 2
    ) {
717 1
      return $relation;
718 1
    }
719 1
720 1
    /* @var $obj ActiveRecord */
721
    $obj = new $relation[1];
722 2
723
    $this->relations[$name] = $obj;
724 2
    if (isset($relation[3]) && is_array($relation[3])) {
725 2
      foreach ((array)$relation[3] as $func => $args) {
726 2
        call_user_func_array(array($obj, $func), (array)$args);
727 2
      }
728
    }
729 1
730
    $backref = isset($relation[4]) ? $relation[4] : '';
731 1
    $relationInstanceOfSelf = ($relation instanceof self);
732
    if (
733
        $relationInstanceOfSelf === false
734
        &&
735 1
        self::HAS_ONE == $relation[0]
736 2
    ) {
737 2
738 2
      $this->relations[$name] = $obj->eq($relation[2], $this->{$this->primaryKeyName})->fetch();
739 2
740
      if ($backref) {
741 2
        $this->relations[$name] && $backref && $obj->__set($backref, $this);
742 2
      }
743 1
744 1
    } elseif (
745 1
        is_array($relation)
746 1
        &&
747
        self::HAS_MANY == $relation[0]
748 2
    ) {
749 2
750 2
      $this->relations[$name] = $obj->eq($relation[2], $this->{$this->primaryKeyName})->fetchAll();
751 2
      if ($backref) {
752 2
        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
          $o->__set($backref, $this);
754 2
        }
755
      }
756 2
757 1
    } elseif (
758 1
        $relationInstanceOfSelf === false
759
        &&
760 2
        self::BELONGS_TO == $relation[0]
761
    ) {
762
763
      $this->relations[$name] = $obj->eq($obj->primaryKeyName, $this->{$relation[2]})->fetch();
764 2
765
      if ($backref) {
766
        $this->relations[$name] && $backref && $obj->__set($backref, $this);
767
      }
768
769
    } else {
770
      throw new ActiveRecordException("Relation $name not found.");
771
    }
772
773
    return $this->relations[$name];
774 21
  }
775
776
  /**
777
   * Helper function to build SQL with sql parts.
778 21
   *
779 15
   * @param string       $n <p>The SQL part will be build.</p>
780 21
   * @param int          $i <p>The index of $n in $sql array.</p>
781
   * @param ActiveRecord $o <p>The reference to $this.</p>
782 14
   */
783
  private function _buildSqlCallback(&$n, $i, $o)
784 14
  {
785
    if (
786
        'select' === $n
787 21
        &&
788
        null === $o->$n
789 21
    ) {
790 21
791 17
      $n = strtoupper($n) . ' ' . $o->table . '.*';
792 21
793
    } elseif (
794 17
        (
795
            'update' === $n
796 21
            ||
797
            'from' === $n
798 1
        )
799
        &&
800 1
        null === $o->$n
801
    ) {
802 21
803
      $n = strtoupper($n) . ' ' . $o->table;
804
805 21
    } elseif ('delete' === $n) {
806
807
      $n = strtoupper($n) . ' ';
808
809
    } else {
810
811
      $n = (null !== $o->$n) ? $o->$n . ' ' : '';
812
813
    }
814 21
  }
815
816 21
  /**
817
   * Helper function to build SQL with sql parts.
818
   *
819
   * @param array $sqls <p>The SQL part will be build.</p>
820
   *
821 21
   * @return string
822
   */
823
  protected function _buildSql($sqls = array())
824
  {
825
    array_walk($sqls, array($this, '_buildSqlCallback'), $this);
826
827
    // DEBUG
828
    //echo 'SQL: ', implode(' ', $sqls), "\n", 'PARAMS: ', implode(', ', $this->params), "\n";
829
830
    return implode(' ', $sqls);
831
  }
832
833
  /**
834
   * Magic function to make calls witch in function mapping stored in $operators and $sqlPart.
835 16
   * also can call function of DB object.
836
   *
837 16
   * @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 16
   *
842
   * @throws ActiveRecordException
843 16
   */
844
  public function __call($name, $args)
845 14
  {
846 14
    if (!self::$db instanceof DB) {
847 14
      self::$db = DB::getInstance();
848 14
    }
849 14
850 14
    $nameTmp = strtolower($name);
851
852 16
    if (array_key_exists($nameTmp, self::$operators)) {
853
854 11
      $this->addCondition(
855
          $args[0],
856 11
          self::$operators[$nameTmp],
857 11
          isset($args[1]) ? $args[1] : null,
858
          (is_string(end($args)) && 'or' === strtolower(end($args))) ? 'OR' : 'AND'
859 11
      );
860
861 11
    } elseif (array_key_exists($nameTmp = str_replace('by', '', $nameTmp), $this->sqlParts)) {
862
863
      $this->$name = new ActiveRecordExpressions(
864
          array(
865
              'operator' => $this->sqlParts[$nameTmp],
866
              'target'   => implode(', ', $args),
867
          )
868
      );
869
870
    } elseif (is_callable($callback = array(self::$db, $name))) {
871 16
872
      return call_user_func_array($callback, $args);
873
874
    } else {
875
876
      throw new ActiveRecordException("Method $name not exist.");
877
878
    }
879
880
    return $this;
881
  }
882 1
883
  /**
884 1
   * Make wrap when build the SQL expressions of WHERE.
885 1
   *
886 1
   * @param string $op <p>If given, this param will build one "ActiveRecordExpressionsWrap" and include the stored expressions add into WHERE,
887 1
   *                   otherwise it will stored the expressions into an array.</p>
888 1
   *
889
   * @return $this
890 1
   */
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 1
                array(
899
                    'delimiter' => ' ',
900
                    '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 1
                )
902
            ), 'or' === strtolower($op) ? 'OR' : 'AND'
903
        );
904
      }
905
      $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
    } else {
907
      $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
    return $this;
911 18
  }
912
913 18
  /**
914 8
   * Helper function to build place holder when make SQL expressions.
915 8
   *
916 8
   * @param mixed $value <p>The value will be bind to SQL, just store it in $this->params.</p>
917 18
   *
918 3
   * @return mixed $value
919 3
   */
920 3
  protected function _filterParam($value)
921
  {
922 18
    if (is_array($value)) {
923
      foreach ($value as $key => $val) {
924
        $this->params[$value[$key] = self::PREFIX . ++self::$count] = $val;
925
      }
926
    } elseif (is_string($value)) {
927
      $this->params[$ph = self::PREFIX . ++self::$count] = $value;
928
      $value = $ph;
929
    }
930
931
    return $value;
932
  }
933
934
  /**
935 14
   * Helper function to add condition into WHERE.
936
   *
937 14
   * @param string $field <p>The field name, the source of Expressions</p>
938 14
   * @param string $operator
939
   * @param mixed  $value <p>The target of the Expressions.</p>
940 14
   * @param string $op    <p>The operator to concat this Expressions into WHERE or SET statement.</p>
941 14
   * @param string $name  <p>The Expression will contact to.</p>
942 14
   */
943 14
  public function addCondition($field, $operator, $value, $op = 'AND', $name = 'where')
944 4
  {
945 4
    $value = $this->_filterParam($value);
946 4
    $exp = new ActiveRecordExpressions(
947 14
        array(
948
            'source'   => ('where' == $name ? $this->table . '.' : '') . $field,
949 14
            'operator' => $operator,
950 14
            'target'   => is_array($value)
951 14
                ? new ActiveRecordExpressionsWrap(
952 14
                    'between' === strtolower($operator)
953 14
                        ? array('target' => $value, 'start' => ' ', 'end' => ' ', 'delimiter' => ' AND ')
954 1
                        : array('target' => $value)
955
                ) : $value,
956 14
        )
957 14
    );
958
    if ($exp) {
959
      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
        $this->_addCondition($exp, $op, $name);
961
      } else {
962
        $this->_addExpression($exp, $op);
963
      }
964
    }
965
  }
966
967
  /**
968
   * Helper function to add condition into JOIN.
969 1
   *
970
   * @param string $table <p>The join table name.</p>
971 1
   * @param string $on    <p>The condition of ON.</p>
972
   * @param string $type  <p>The join type, like "LEFT", "INNER", "OUTER".</p>
973 1
   *
974 1
   * @return $this
975 1
   */
976 1
  public function join($table, $on, $type = 'LEFT')
977 1
  {
978
    $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 1
        array(
980
            '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
            'target'   => new ActiveRecordExpressions(
983
                array('source' => $table, 'operator' => 'ON', 'target' => $on)
984
            ),
985
        )
986
    );
987
988
    return $this;
989
  }
990 1
991
  /**
992 1
   * helper function to make wrapper. Stored the expression in to array.
993 1
   *
994 1
   * @param ActiveRecordExpressions $exp      <p>The expression will be stored.</p>
995 1
   * @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
        !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
        ||
1002
        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
    ) {
1004
      $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
    } else {
1006 14
      $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 14
  }
1009 14
1010 14
  /**
1011 4
   * helper function to add condition into WHERE.
1012
   *
1013 4
   * @param ActiveRecordExpressions $exp      <p>The expression will be concat into WHERE or SET statement.</p>
1014 4
   * @param string                  $operator <p>The operator to concat this Expressions into WHERE or SET statement.</p>
1015 4
   * @param string                  $name     <p>The Expression will contact to.</p>
1016
   */
1017 4
  protected function _addCondition($exp, $operator, $name = 'where')
1018
  {
1019 14
    if (!$this->$name) {
1020
      $this->$name = new ActiveRecordExpressions(array('operator' => strtoupper($name), 'target' => $exp));
1021
    } else {
1022
      $this->$name->target = new ActiveRecordExpressions(
1023
          array(
1024 1
              'source'   => $this->$name->target,
1025
              'operator' => $operator,
1026 1
              'target'   => $exp,
1027
          )
1028
      );
1029
    }
1030
  }
1031
1032
  /**
1033
   * @return array
1034
   */
1035
  public function getDirty()
1036
  {
1037
    return $this->dirty;
1038
  }
1039
1040 17
  /**
1041
   * @return bool
1042 17
   */
1043 17
  public static function isNewDataAreDirty()
1044
  {
1045
    return self::$new_data_are_dirty;
1046
  }
1047
1048
  /**
1049
   * @param bool $bool
1050
   */
1051 23
  public static function setNewDataAreDirty($bool)
1052
  {
1053
    self::$new_data_are_dirty = (bool)$bool;
1054 23
  }
1055
1056 23
  /**
1057 23
   * Magic function to SET values of the current object.
1058
   *
1059 20
   * @param mixed $var
1060
   * @param mixed $val
1061 20
   */
1062 20
  public function __set($var, $val)
1063 20
  {
1064
    if (
1065 20
        array_key_exists($var, $this->sqlExpressions)
1066
        ||
1067 1
        array_key_exists($var, $this->defaultSqlExpressions)
1068
    ) {
1069 1
1070
      $this->sqlExpressions[$var] = $val;
1071 20
1072
    } elseif (
1073 20
        array_key_exists($var, $this->relations)
1074 7
        &&
1075 7
        $val instanceof self
1076
    ) {
1077
1078 23
      $this->relations[$var] = $val;
1079
1080
    } else {
1081
1082
      $this->array[$var] = $val;
1083
1084
      if (self::$new_data_are_dirty === true) {
1085 1
        $this->dirty[$var] = $val;
1086
      }
1087 1
1088
    }
1089
  }
1090
1091 1
  /**
1092 1
   * Magic function to UNSET values of the current object.
1093 1
   *
1094
   * @param mixed $var
1095 1
   */
1096 1
  public function __unset($var)
1097 1
  {
1098 1
    if (array_key_exists($var, $this->sqlExpressions)) {
1099
      unset($this->sqlExpressions[$var]);
1100
    }
1101
1102
    if (isset($this->array[$var])) {
1103
      unset($this->array[$var]);
1104
    }
1105
1106
    if (isset($this->dirty[$var])) {
1107
      unset($this->dirty[$var]);
1108
    }
1109
  }
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 2
    return $this;
1123
  }
1124 2
1125
  /**
1126 2
   * Helper function for "ORDER BY".
1127
   *
1128
   * @param $args ...
1129
   *
1130
   * @return $this
1131
   */
1132
  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
    $this->__call('orderBy', func_get_args());
1135
1136 23
    return $this;
1137
  }
1138 23
1139 20
  /**
1140
   * Magic function to GET the values of current object.
1141
   *
1142 23
   * @param $var
1143 3
   *
1144
   * @return mixed
1145
   */
1146 23
  public function &__get($var)
1147 4
  {
1148
    if (array_key_exists($var, $this->sqlExpressions)) {
1149
      return $this->sqlExpressions[$var];
1150 23
    }
1151
1152
    if (array_key_exists($var, $this->relations)) {
1153
      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
    if (isset($this->dirty[$var])) {
1157
      return $this->dirty[$var];
1158
    }
1159
1160
    return parent::__get($var);
1161
  }
1162
}
1163