Passed
Push — master ( 612a2b...947f8b )
by kill
15:18
created

Model::clear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace puck;
3
4
use puck\tools\Str;
5
6
class Model {
7
    /**
8
     * Per page limit for pagination
9
     *
10
     * @var int
11
     */
12
    public static $pageLimit = 20;
13
    /**
14
     * Variable that holds total pages count of last paginate() query
15
     *
16
     * @var int
17
     */
18
    public static $totalPages = 0;
19
    /**
20
     * Models path
21
     *
22
     * @var modelPath
23
     */
24
    protected static $modelPath;
25
    /**
26
     * An array that holds object data
27
     *
28
     * @var array
29
     */
30
    public $data;
31
    /**
32
     * Flag to define is object is new or loaded from database
33
     *
34
     * @var boolean
35
     */
36
    public $isNew = true;
37
    /**
38
     * Return type: 'Array' to return results as array, 'Object' as object
39
     * 'Json' as json string
40
     *
41
     * @var string
42
     */
43
    public $returnType = 'Object';
44
    /**
45
     * An array that holds insert/update/select errors
46
     *
47
     * @var array
48
     */
49
    public $errors = null;
50
    /**
51
     * Primary key for an object. 'id' is a default value.
52
     *
53
     * @var stating
54
     */
55
    protected $primaryKey = 'id';
56
    /**
57
     * Table name for an object. Class name will be used by default
58
     *
59
     * @var stating
60
     */
61
    protected $dbTable;
62
    protected $dbConn = null;
63
    protected $prefix;
64
    /**
65
     * Working instance of MysqliDb created earlier
66
     *
67
     * @var Mysql
68
     */
69
    private $db;
70
    /**
71
     * An array that holds has* objects which should be loaded togeather with main
72
     * object togeather with main object
73
     *
74
     * @var string
75
     */
76
    private $_with = Array();
77
78
    /**
79
     * @param array $data Data to preload on object creation
0 ignored issues
show
Bug introduced by
There is no parameter named $data. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
80
     */
81
    public function __construct() {
82
        $this->db = app('db')->connect($this->dbConn);
83
        if ($this->prefix) {
84
            $this->db = $this->db->setPrefix($this->prefix);
85
        }
86
        if (!$this->dbTable) {
87
            $classFull = get_class($this);
88
            $classArray = explode("\\", $classFull);
89
            $classSelf = array_pop($classArray);
90
            $classSelf = Str::snake($classSelf);
91
            $this->dbTable = $classSelf;
0 ignored issues
show
Documentation Bug introduced by
It seems like $classSelf of type string is incompatible with the declared type object<puck\stating> of property $dbTable.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
92
        }
93
        $this->db = $this->db->table($this->dbTable);
94
    }
95
96
    /**
97
     * Helper function to create a virtual table class
98
     *
99
     * @param string tableName Table name
100
     * @return dbObject
101
     */
102
    public static function table($tableName) {
103
        $tableName = preg_replace("/[^-a-z0-9_]+/i", '', $tableName);
104
        if (!class_exists($tableName))
105
            eval ("class $tableName extends dbObject {}");
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
106
        return new $tableName ();
107
    }
108
109
    /**
110
     * Catches calls to undefined static methods.
111
     *
112
     * Transparently creating dbObject class to provide smooth API like name::get() name::orderBy()->get()
113
     *
114
     * @param string $method
115
     * @param mixed $arg
116
     *
117
     * @return mixed
118
     */
119
    public static function __callStatic($method, $arg) {
120
        $obj = new static;
121
        $result = call_user_func_array(array($obj, $method), $arg);
122
        if (method_exists($obj, $method))
123
            return $result;
124
        return $obj;
125
    }
126
127
    public static function autoload($path = null) {
128
        if ($path)
129
            static::$modelPath = $path . "/";
0 ignored issues
show
Documentation Bug introduced by
It seems like $path . '/' of type string is incompatible with the declared type object<puck\modelPath> of property $modelPath.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
130
        else
131
            static::$modelPath = __DIR__ . "/models/";
0 ignored issues
show
Documentation Bug introduced by
It seems like __DIR__ . '/models/' of type string is incompatible with the declared type object<puck\modelPath> of property $modelPath.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
132
        spl_autoload_register("dbObject::dbObjectAutoload");
133
    }
134
135
    private static function dbObjectAutoload($classname) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
136
        $filename = static::$modelPath . $classname . ".php";
137
        if (file_exists($filename))
138
            include($filename);
139
    }
140
141
    /**
142
     * Magic getter function
143
     *
144
     * @param $name Variable name
145
     *
146
     * @return mixed
147
     */
148
    public function __get($name) {
149
        if (property_exists($this, 'hidden') && array_search($name, $this->hidden) !== false)
0 ignored issues
show
Documentation introduced by
The property hidden does not exist on object<puck\Model>. 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...
150
            return null;
151
152
        if (isset ($this->data[$name]) && $this->data[$name] instanceof dbObject)
0 ignored issues
show
Bug introduced by
The class puck\dbObject does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
153
            return $this->data[$name];
154
155
        if (property_exists($this, 'relations') && isset ($this->relations[$name])) {
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
156
            $relationType = strtolower($this->relations[$name][0]);
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
157
            $modelName = $this->relations[$name][1];
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
158
            switch ($relationType) {
159
                case 'hasone':
160
                    $key = isset ($this->relations[$name][2]) ? $this->relations[$name][2] : $name;
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
161
                    $obj = new $modelName;
162
                    $obj->returnType = $this->returnType;
163
                    return $this->data[$name] = $obj->byId($this->data[$key]);
164
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
165
                case 'hasmany':
166
                    $key = $this->relations[$name][2];
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
167
                    $obj = new $modelName;
168
                    $obj->returnType = $this->returnType;
169
                    return $this->data[$name] = $obj->where($key, $this->data[$this->primaryKey])->get();
170
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
171
                default:
172
                    break;
173
            }
174
        }
175
176
        if (isset ($this->data[$name]))
177
            return $this->data[$name];
178
179
        if (property_exists($this->db, $name))
180
            return $this->db->$name;
181
    }
182
183
    /**
184
     * Magic setter function
185
     *
186
     * @return mixed
187
     */
188
    public function __set($name, $value) {
189
        if (property_exists($this, 'hidden') && array_search($name, $this->hidden) !== false)
0 ignored issues
show
Documentation introduced by
The property hidden does not exist on object<puck\Model>. 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...
190
            return;
191
192
        $this->data[$name] = $value;
193
    }
194
195
    public function __isset($name) {
196
        if (isset ($this->data[$name]))
197
            return isset ($this->data[$name]);
198
199
        if (property_exists($this->db, $name))
200
            return isset ($this->db->$name);
201
    }
202
203
    public function __unset($name) {
204
        unset ($this->data[$name]);
205
    }
206
207
    /**
208
     * Save or Update object
209
     *
210
     * @return mixed insert id or false in case of failure
211
     */
212
    public function save($data = null) {
213
        if ($this->isNew)
214
            return $this->insert();
215
        return $this->update($data);
216
    }
217
218
    /**
219
     * @return mixed insert id or false in case of failure
220
     */
221
    public function insert() {
222
        if (!empty ($this->timestamps) && in_array("createdAt", $this->timestamps))
0 ignored issues
show
Documentation introduced by
The property timestamps does not exist on object<puck\Model>. 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...
223
            $this->createdAt = date("Y-m-d H:i:s");
0 ignored issues
show
Documentation introduced by
The property createdAt does not exist on object<puck\Model>. 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...
224
        $sqlData = $this->prepareData();
225
        if (!$this->validate($sqlData))
226
            return false;
227
228
        $id = $this->db->insert($this->dbTable, $sqlData);
229
        if (!empty ($this->primaryKey) && empty ($this->data[$this->primaryKey]))
230
            $this->data[$this->primaryKey] = $id;
231
        $this->isNew = false;
232
233
        return $id;
234
    }
235
236
    private function prepareData() {
237
        $this->errors = Array();
238
        $sqlData = Array();
239
        if (count($this->data) == 0)
240
            return Array();
241
242
        if (method_exists($this, "preLoad"))
243
            $this->preLoad($this->data);
0 ignored issues
show
Documentation Bug introduced by
The method preLoad does not exist on object<puck\Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
244
245
        if (!$this->dbFields)
0 ignored issues
show
Documentation introduced by
The property dbFields does not exist on object<puck\Model>. 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...
246
            return $this->data;
247
248
        foreach ($this->data as $key => &$value) {
249
            if ($value instanceof dbObject && $value->isNew == true) {
0 ignored issues
show
Bug introduced by
The class puck\dbObject does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
250
                $id = $value->save();
251
                if ($id)
252
                    $value = $id;
253
                else
254
                    $this->errors = array_merge($this->errors, $value->errors);
255
            }
256
257
            if (!in_array($key, array_keys($this->dbFields)))
0 ignored issues
show
Documentation introduced by
The property dbFields does not exist on object<puck\Model>. 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...
258
                continue;
259
260
            if (!is_array($value)) {
261
                $sqlData[$key] = $value;
262
                continue;
263
            }
264
265
            if (isset ($this->jsonFields) && in_array($key, $this->jsonFields))
0 ignored issues
show
Documentation introduced by
The property jsonFields does not exist on object<puck\Model>. 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...
266
                $sqlData[$key] = json_encode($value);
267
            else if (isset ($this->arrayFields) && in_array($key, $this->arrayFields))
0 ignored issues
show
Documentation introduced by
The property arrayFields does not exist on object<puck\Model>. 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...
268
                $sqlData[$key] = implode("|", $value);
269
            else
270
                $sqlData[$key] = $value;
271
        }
272
        return $sqlData;
273
    }
274
275
    /**
276
     * @param array $data
277
     */
278
    private function validate($data) {
279
        if (!$this->dbFields)
0 ignored issues
show
Documentation introduced by
The property dbFields does not exist on object<puck\Model>. 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...
280
            return true;
281
282
        foreach ($this->dbFields as $key => $desc) {
0 ignored issues
show
Documentation introduced by
The property dbFields does not exist on object<puck\Model>. 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...
283
            $type = null;
284
            $required = false;
285
            if (isset ($data[$key]))
286
                $value = $data[$key];
287
            else
288
                $value = null;
289
290
            if (is_array($value))
291
                continue;
292
293
            if (isset ($desc[0]))
294
                $type = $desc[0];
295
            if (isset ($desc[1]) && ($desc[1] == 'required'))
296
                $required = true;
297
298
            if ($required && strlen($value) == 0) {
299
                $this->errors[] = Array($this->dbTable . "." . $key => "is required");
300
                continue;
301
            }
302
            if ($value == null)
303
                continue;
304
305
            switch ($type) {
306
                case "text";
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
307
                    $regexp = null;
308
                    break;
309
                case "int":
310
                    $regexp = "/^[0-9]*$/";
311
                    break;
312
                case "double":
313
                    $regexp = "/^[0-9\.]*$/";
314
                    break;
315
                case "bool":
316
                    $regexp = '/^[yes|no|0|1|true|false]$/i';
317
                    break;
318
                case "datetime":
319
                    $regexp = "/^[0-9a-zA-Z -:]*$/";
320
                    break;
321
                default:
322
                    $regexp = $type;
323
                    break;
324
            }
325
            if (!$regexp)
326
                continue;
327
328
            if (!preg_match($regexp, $value)) {
329
                $this->errors[] = Array($this->dbTable . "." . $key => "$type validation failed");
330
                continue;
331
            }
332
        }
333
        return !count($this->errors) > 0;
334
    }
335
336
    /**
337
     * @param array $data Optional update data to apply to the object
338
     */
339
    public function update($data = null) {
340
        if (empty ($this->dbFields))
0 ignored issues
show
Documentation introduced by
The property dbFields does not exist on object<puck\Model>. 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...
341
            return false;
342
343
        if (empty ($this->data[$this->primaryKey]))
344
            return false;
345
346
        if ($data) {
347
            foreach ($data as $k => $v)
348
                $this->$k = $v;
349
        }
350
351
        if (!empty ($this->timestamps) && in_array("updatedAt", $this->timestamps))
0 ignored issues
show
Documentation introduced by
The property timestamps does not exist on object<puck\Model>. 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...
352
            $this->updatedAt = date("Y-m-d H:i:s");
0 ignored issues
show
Documentation introduced by
The property updatedAt does not exist on object<puck\Model>. 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...
353
354
        $sqlData = $this->prepareData();
355
        if (!$this->validate($sqlData))
356
            return false;
357
358
        $this->db->where($this->primaryKey, $this->data[$this->primaryKey]);
359
        return $this->db->update($this->dbTable, $sqlData);
0 ignored issues
show
Documentation introduced by
$sqlData is of type array, but the function expects a integer|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
360
    }
361
362
    /**
363
     * Delete method. Works only if object primaryKey is defined
364
     *
365
     * @return boolean Indicates success. 0 or 1.
366
     */
367
    public function delete() {
368
        if (empty ($this->data[$this->primaryKey]))
369
            return false;
370
371
        $this->db->where($this->primaryKey, $this->data[$this->primaryKey]);
372
        return $this->db->delete($this->dbTable);
373
    }
374
375
    /**
376
     * 当执行一个自身不存在的方法时,重定向到mysql类中去
377
     *
378
     * @param string $method
379
     * @param mixed $arg
380
     *
381
     * @return mixed
382
     */
383
    public function __call($method, $arg) {
384
        if (method_exists($this, $method))
385
            return call_user_func_array(array($this, $method), $arg);
386
        $this->db->table($this->dbTable);
387
        return call_user_func_array(array($this->db, $method), $arg);;
388
    }
389
390
    /**
391
     * Converts object data to a JSON string.
392
     *
393
     * @return string Converted data
394
     */
395
    public function __toString() {
396
        return $this->toJson();
397
    }
398
399
    /**
400
     * Converts object data to a JSON string.
401
     *
402
     * @return string Converted data
403
     */
404
    public function toJson() {
405
        return json_encode($this->toArray());
406
    }
407
408
    /**
409
     * Converts object data to an associative array.
410
     *
411
     * @return array Converted data
412
     */
413
    public function toArray() {
414
        $data = $this->data;
415
        $this->processAllWith($data);
416
        foreach ($data as &$d) {
417
            if ($d instanceof dbObject)
0 ignored issues
show
Bug introduced by
The class puck\dbObject does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
418
                $d = $d->data;
419
        }
420
        return $data;
421
    }
422
423
    /**
424
     * Function queries hasMany relations if needed and also converts hasOne object names
425
     *
426
     * @param array $data
427
     */
428
    private function processAllWith(&$data, $shouldReset = true) {
429
        if (count($this->_with) == 0)
430
            return;
431
432
        foreach ($this->_with as $name => $opts) {
0 ignored issues
show
Bug introduced by
The expression $this->_with of type string is not traversable.
Loading history...
433
            $relationType = strtolower($opts[0]);
434
            $modelName = $opts[1];
435
            if ($relationType == 'hasone') {
436
                $obj = new $modelName;
437
                $table = $obj->dbTable;
438
                $primaryKey = $obj->primaryKey;
439
440
                if (!isset ($data[$table])) {
441
                    $data[$name] = $this->$name;
442
                    continue;
443
                }
444
                if ($data[$table][$primaryKey] === null) {
445
                    $data[$name] = null;
446
                } else {
447
                    if ($this->returnType == 'Object') {
448
                        $item = new $modelName ($data[$table]);
449
                        $item->returnType = $this->returnType;
450
                        $item->isNew = false;
451
                        $data[$name] = $item;
452
                    } else {
453
                        $data[$name] = $data[$table];
454
                    }
455
                }
456
                unset ($data[$table]);
457
            } else
458
                $data[$name] = $this->$name;
459
        }
460
        if ($shouldReset)
461
            $this->_with = Array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $_with.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
462
    }
463
464
    /**
465
     * Fetch all objects
466
     *
467
     * @access public
468
     * @param integer|array $limit Array to define SQL limit in format Array ($count, $offset)
469
     *                             or only $count
470
     * @param array|string $fields Array or coma separated list of fields to fetch
471
     *
472
     * @return array Array of dbObjects
473
     */
474
    protected function get($limit = null, $fields = null) {
475
        $objects = Array();
476
        $this->processHasOneWith();
477
        $results = $this->db->ArrayBuilder()->get($this->dbTable, $limit, $fields);
0 ignored issues
show
Bug introduced by
It seems like $fields defined by parameter $fields on line 474 can also be of type array or null; however, puck\Mysql::get() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug Compatibility introduced by
The expression $this->db->ArrayBuilder(...able, $limit, $fields); of type puck\Mysql|array adds the type puck\Mysql to the return on line 498 which is incompatible with the return type documented by puck\Model::get of type array.
Loading history...
478
        if ($this->db->count == 0)
479
            return null;
480
481
        foreach ($results as &$r) {
0 ignored issues
show
Bug introduced by
The expression $results of type object<puck\Mysql>|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...
482
            $this->processArrays($r);
483
            $this->data = $r;
484
            $this->processAllWith($r, false);
485
            if ($this->returnType == 'Object') {
486
                $item = new static ($r);
0 ignored issues
show
Unused Code introduced by
The call to Model::__construct() has too many arguments starting with $r.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
487
                $item->isNew = false;
488
                $objects[] = $item;
489
            }
490
        }
491
        $this->_with = Array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $_with.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
492
        if ($this->returnType == 'Object')
493
            return $objects;
494
495
        if ($this->returnType == 'Json')
496
            return json_encode($results);
497
498
        return $results;
499
    }
500
501
    private function processHasOneWith() {
502
        if (count($this->_with) == 0)
503
            return;
504
        foreach ($this->_with as $name => $opts) {
0 ignored issues
show
Bug introduced by
The expression $this->_with of type string is not traversable.
Loading history...
505
            $relationType = strtolower($opts[0]);
506
            $modelName = $opts[1];
507
            $key = null;
508
            if (isset ($opts[2]))
509
                $key = $opts[2];
510
            if ($relationType == 'hasone') {
511
                $this->db->setQueryOption("MYSQLI_NESTJOIN");
512
                $this->join($modelName, $key);
513
            }
514
        }
515
    }
516
517
    /**
518
     * Function to join object with another object.
519
     *
520
     * @access public
521
     * @param string $objectName Object Name
522
     * @param string $key Key for a join from primary object
523
     * @param string $joinType SQL join type: LEFT, RIGHT,  INNER, OUTER
524
     * @param string $primaryKey SQL join On Second primaryKey
525
     *
526
     * @return dbObject
527
     */
528
    private function join($objectName, $key = null, $joinType = 'LEFT', $primaryKey = null) {
529
        $joinObj = new $objectName;
530
        if (!$key)
0 ignored issues
show
Bug Best Practice introduced by
The expression $key of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
531
            $key = $objectName . "id";
532
533
        if (!$primaryKey)
0 ignored issues
show
Bug Best Practice introduced by
The expression $primaryKey of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
534
            $primaryKey = $this->db->getPrefix() . $joinObj->dbTable . "." . $joinObj->primaryKey;
535
536
        if (!strchr($key, '.'))
537
            $joinStr = $this->db->getPrefix() . $this->dbTable . ".{$key} = " . $primaryKey;
538
        else
539
            $joinStr = $this->db->getPrefix() . "{$key} = " . $primaryKey;
540
541
        $this->db->join($joinObj->dbTable, $joinStr, $joinType);
542
        return $this;
543
    }
544
545
    /**
546
     * @param array $data
547
     */
548
    private function processArrays(&$data) {
549
        if (isset ($this->jsonFields) && is_array($this->jsonFields)) {
0 ignored issues
show
Documentation introduced by
The property jsonFields does not exist on object<puck\Model>. 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...
550
            foreach ($this->jsonFields as $key)
0 ignored issues
show
Documentation introduced by
The property jsonFields does not exist on object<puck\Model>. 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...
551
                $data[$key] = json_decode($data[$key]);
552
        }
553
554
        if (isset ($this->arrayFields) && is_array($this->arrayFields)) {
0 ignored issues
show
Documentation introduced by
The property arrayFields does not exist on object<puck\Model>. 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...
555
            foreach ($this->arrayFields as $key)
0 ignored issues
show
Documentation introduced by
The property arrayFields does not exist on object<puck\Model>. 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...
556
                $data[$key] = explode("|", $data[$key]);
557
        }
558
    }
559
560
    /**
561
     * Function to get a total records count
562
     *
563
     * @return int
564
     */
565
    protected function count() {
566
        $res = $this->db->ArrayBuilder()->getValue($this->dbTable, "count(*)");
567
        if (!$res)
568
            return 0;
569
        return $res;
570
    }
571
572
    /**
573
     * Helper function to create dbObject with Json return type
574
     *
575
     * @return dbObject
576
     */
577
    private function JsonBuilder() {
578
        $this->returnType = 'Json';
579
        return $this;
580
    }
581
582
    /*
583
     * Function building hasOne joins for get/getOne method
584
     */
585
586
    /**
587
     * Helper function to create dbObject with Array return type
588
     *
589
     * @return dbObject
590
     */
591
    private function ArrayBuilder() {
592
        $this->returnType = 'Array';
593
        return $this;
594
    }
595
596
    /**
597
     * Helper function to create dbObject with Object return type.
598
     * Added for consistency. Works same way as new $objname ()
599
     *
600
     * @return dbObject
601
     */
602
    private function ObjectBuilder() {
603
        $this->returnType = 'Object';
604
        return $this;
605
    }
606
607
    /**
608
     * Get object by primary key.
609
     *
610
     * @access public
611
     * @param $id Primary Key
612
     * @param array|string $fields Array or coma separated list of fields to fetch
613
     *
614
     * @return dbObject|array
615
     */
616
    private function byId($id, $fields = null) {
617
        $this->db->where($this->db->getPrefix() . $this->dbTable . '.' . $this->primaryKey, $id);
618
        return $this->getOne($fields);
619
    }
620
621
    /**
622
     * Convinient function to fetch one object. Mostly will be togeather with where()
623
     *
624
     * @access public
625
     * @param array|string $fields Array or coma separated list of fields to fetch
626
     *
627
     * @return dbObject
628
     */
629
    protected function getOne($fields = null) {
630
        $this->processHasOneWith();
631
        $results = $this->db->ArrayBuilder()->getOne($this->dbTable, $fields);
0 ignored issues
show
Bug introduced by
It seems like $fields defined by parameter $fields on line 629 can also be of type array or null; however, puck\Mysql::getOne() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
632
        if ($this->db->count == 0)
633
            return null;
634
635
        $this->processArrays($results);
636
        $this->data = $results;
637
        $this->processAllWith($results);
638
        if ($this->returnType == 'Json')
639
            return json_encode($results);
640
        if ($this->returnType == 'Array')
641
            return $results;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $results; (array) is incompatible with the return type documented by puck\Model::getOne of type puck\dbObject.

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...
642
643
        $item = new static ($results);
0 ignored issues
show
Unused Code introduced by
The call to Model::__construct() has too many arguments starting with $results.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
644
        $item->isNew = false;
645
646
        return $item;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $item; (puck\Model) is incompatible with the return type documented by puck\Model::getOne of type puck\dbObject.

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...
647
    }
648
649
    /**
650
     * Function to set witch hasOne or hasMany objects should be loaded togeather with a main object
651
     *
652
     * @access public
653
     * @param string $objectName Object Name
654
     *
655
     * @return dbObject
656
     */
657
    private function with($objectName) {
658
        if (!property_exists($this, 'relations') && !isset ($this->relations[$name]))
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
Bug introduced by
The variable $name seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
659
            die ("No relation with name $objectName found");
0 ignored issues
show
Coding Style Compatibility introduced by
The method with() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
660
661
        $this->_with[$objectName] = $this->relations[$objectName];
0 ignored issues
show
Documentation introduced by
The property relations does not exist on object<puck\Model>. 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...
662
663
        return $this;
664
    }
665
666
    /*
667
     * Enable models autoload from a specified path
668
     *
669
     * Calling autoload() without path will set path to dbObjectPath/models/ directory
670
     *
671
     * @param string $path
672
     */
673
674
    /**
675
     * Pagination wraper to get()
676
     *
677
     * @access public
678
     * @param int $page Page number
679
     * @param array|string $fields Array or coma separated list of fields to fetch
680
     * @return array
681
     */
682
    private function paginate($page, $fields = null) {
683
        $this->db->pageLimit = self::$pageLimit;
684
        $res = $this->db->paginate($this->dbTable, $page, $fields);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->db->paginate($thi...Table, $page, $fields); of type puck\Mysql|array adds the type puck\Mysql to the return on line 705 which is incompatible with the return type documented by puck\Model::paginate of type array.
Loading history...
685
        self::$totalPages = $this->db->totalPages;
686
        if ($this->db->count == 0) return null;
687
688
        foreach ($res as &$r) {
0 ignored issues
show
Bug introduced by
The expression $res of type object<puck\Mysql>|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...
689
            $this->processArrays($r);
690
            $this->data = $r;
691
            $this->processAllWith($r, false);
692
            if ($this->returnType == 'Object') {
693
                $item = new static ($r);
0 ignored issues
show
Unused Code introduced by
The call to Model::__construct() has too many arguments starting with $r.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
694
                $item->isNew = false;
695
                $objects[] = $item;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$objects was never initialized. Although not strictly required by PHP, it is generally a good practice to add $objects = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
696
            }
697
        }
698
        $this->_with = Array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $_with.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
699
        if ($this->returnType == 'Object')
700
            return $objects;
0 ignored issues
show
Bug introduced by
The variable $objects does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
701
702
        if ($this->returnType == 'Json')
703
            return json_encode($res);
704
705
        return $res;
706
    }
707
708
    public function clear() {
709
        $this->data=[];
710
        $this->isNew=true;
711
    }
712
}
713