Issues (4)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Source/Model.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @author Rémy M. Böhler <[email protected]>
4
 */
5
6
namespace Rorm;
7
8
use Iterator;
9
use Traversable;
10
use JsonSerializable;
11
12
/**
13
 * Class Model
14
 */
15
abstract class Model implements Iterator, JsonSerializable
16
{
17
    /** @var string */
18
    public static $_table;
19
20
    /** @var string|array */
21
    public static $_idColumn = 'id';
22
23
    /** @var bool */
24
    public static $_autoId = true;
25
26
    /** @var array */
27
    public static $_ignoreColumns = array();
28
29
    /** @var string */
30
    public static $_connection = Rorm::CONNECTION_DEFAULT;
31
32
    /** @var array */
33
    public $_data = array();
34
35
    /**
36
     * @return string
37
     */
38 27
    public static function getTable()
39
    {
40 27
        if (isset(static::$_table)) {
41 18
            return static::$_table;
42
        }
43
44 11
        return strtolower(str_replace('\\', '_', get_called_class()));
45
    }
46
47
    /**
48
     * @return \PDO
49
     */
50 29
    public static function getDatabase()
51
    {
52 29
        return Rorm::getDatabase(static::$_connection);
53
    }
54
55
    /**
56
     * @return static
57
     */
58 19
    public static function create()
59
    {
60 19
        return new static();
61
    }
62
63
    /**
64
     * @param mixed $id , ...
65
     * @return static
0 ignored issues
show
Should the return type not be Model|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
66
     */
67 5
    public static function find($id)
68
    {
69 5
        $query = static::query();
70 5
        call_user_func_array(array($query, 'whereId'), func_get_args());
71 5
        return $query->findOne();
72
    }
73
74
    /**
75
     * @return static[]
76
     */
77 2
    public static function findAll()
78
    {
79 2
        return static::query()->findAll();
80
    }
81
82
    /**
83
     * @return QueryBuilder
84
     */
85 24
    public static function query()
86
    {
87 24
        return new QueryBuilder(static::getTable(), static::$_idColumn, get_called_class(), static::getDatabase());
88
    }
89
90
    /**
91
     * @param string $query
92
     * @param array $params
93
     * @return Query
94
     */
95 3
    public static function customQuery($query, array $params = array())
96
    {
97 3
        $ormQuery = new Query(get_called_class(), static::getDatabase());
98 3
        $ormQuery->setQuery($query);
99 3
        if (!empty($params)) {
100 1
            $ormQuery->setParams($params);
101 1
        }
102 3
        return $ormQuery;
103
    }
104
105
    /**
106
     * @return array|mixed
107
     */
108 3
    public function getId()
109
    {
110 3
        if (is_array(static::$_idColumn)) {
111 1
            $result = array();
112 1
            foreach (static::$_idColumn as $key) {
113 1
                $result[$key] = $this->get($key);
114 1
            }
115 1
            return $result;
116
        } else {
117 3
            return $this->get(static::$_idColumn);
118
        }
119
    }
120
121
    /**
122
     * @return bool
123
     */
124 10
    public function hasId()
125
    {
126 10
        if (is_array(static::$_idColumn)) {
127 3
            foreach (static::$_idColumn as $key) {
128 3
                $value = $this->get($key);
129 3
                if (empty($value)) {
130 1
                    return false;
131
                }
132 3
            }
133 3
            return true;
134
        } else {
135 8
            $value = $this->get(static::$_idColumn);
136 8
            return !empty($value);
137
        }
138
    }
139
140
    /**
141
     * @return bool
142
     * @throws QueryException
143
     * @throws \PDOException
144
     */
145 10
    public function save()
146
    {
147 10
        if (empty($this->_data)) {
148 1
            throw new QueryException('can not save empty data!');
149
        }
150
151 9
        $dbh = static::getDatabase();
152 9
        $quoteIdentifier = Rorm::getIdentifierQuoter($dbh);
153 9
        $quotedTable = $quoteIdentifier(static::getTable());
154
155 9
        $idColumns = static::$_idColumn;
156 9
        if (!is_array($idColumns)) {
157 7
            $idColumns = array($idColumns);
158 7
        }
159 9
        $doMerge = $this->hasId();
160
161
        // ignore fields
162 9
        $notSetFields = static::$_ignoreColumns;
163
164
        /**
165
         * Different queries are built for each driver
166
         *
167
         * IDEA: probably split into methods (saveMySQL, saveSQLite)
168
         */
169 9
        if (Rorm::isMySQL($dbh)) {
170
            /**
171
             * MySQL
172
             * Instead of REPLACE INTO we use INSERT INTO ON DUPLICATE KEY UPDATE.
173
             * Because REPLACE INTO does DELETE and INSERT,
174
             * which does not play nice with TRIGGERs and FOREIGN KEY CONSTRAINTS
175
             */
176 5
            $sql = 'INSERT INTO ' . $quotedTable . ' ';
177
178 5
            $insertData = array();
179 5
            $updateData = array();
180
181 5
            foreach ($this->_data as $column => $value) {
182 5
                if (in_array($column, $notSetFields)) {
183 1
                    continue;
184
                }
185
186 5
                $quotedColumn = $quoteIdentifier($column);
187 5
                $insertData[$quotedColumn] = Rorm::quote($dbh, $value);
188
189 5
                if ($doMerge && !in_array($column, $idColumns)) {
190 2
                    $updateData[] = $quotedColumn . ' = VALUES(' . $quotedColumn . ')';
191 2
                }
192 5
            }
193 5
            unset($column, $value, $quotedColumn);
194
195
            // insert
196
            $sql .=
197 5
                '(' . implode(', ', array_keys($insertData)) . ')' .
198 5
                ' VALUES ' .
199 5
                '(' . implode(', ', $insertData) . ')';
200
201 5
            if ($doMerge && count($updateData) > 0) {
202
                // update
203 2
                $sql .= ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updateData);
204 2
            }
205
206
            // execute (most likely throws PDOException if there is an error)
207 5
            if ($dbh->exec($sql) === false) {
208
                return false;
209
            }
210
211
            // update generated id
212 5
            if (static::$_autoId && !$doMerge) {
213
                // last insert id
214 4
                $this->set(static::$_idColumn, $dbh->lastInsertId());
215 4
            }
216
217 5
            return true;
218
        } else {
219
            /**
220
             * SQLite
221
             */
222 4
            if ($doMerge) {
223 2
                $sql = 'INSERT OR REPLACE INTO ' . $quotedTable . ' ';
224 2
            } else {
225 3
                $sql = 'INSERT INTO ' . $quotedTable . ' ';
226
            }
227
228
            // build (column) VALUES (values)
229 4
            $quotedData = array();
230 4
            foreach ($this->_data as $column => $value) {
231 4
                if (in_array($column, $notSetFields)) {
232
                    continue;
233
                }
234
235 4
                $quotedData[$quoteIdentifier($column)] = Rorm::quote($dbh, $value);
236 4
            }
237 4
            unset($column, $value);
238
239 4
            $sql .= '(' . implode(', ', array_keys($quotedData)) . ') VALUES (' . implode(', ', $quotedData) . ')';
240
241
            // execute (most likely throws PDOException if there is an error)
242 4
            if ($dbh->exec($sql) === false) {
243
                return false;
244
            }
245
246
            // update generated id
247 4
            if (static::$_autoId && !$this->hasId()) {
248
                // last insert id
249 3
                $this->set(static::$_idColumn, $dbh->lastInsertId());
250 3
            }
251
252 4
            return true;
253
        }
254
    }
255
256
    /**
257
     * @return bool
258
     */
259 4
    public function delete()
260
    {
261 4
        $dbh = static::getDatabase();
262 4
        $quoteIdentifier = Rorm::getIdentifierQuoter($dbh);
263
264 4
        $idColumns = static::$_idColumn;
265 4
        if (!is_array($idColumns)) {
266 2
            $idColumns = array($idColumns);
267 2
        }
268
269 4
        $where = array();
270 4
        foreach ($idColumns as $columnName) {
271 4
            $where[] = $quoteIdentifier($columnName) . ' = ' . Rorm::quote($dbh, $this->$columnName);
272 4
        }
273
274 4
        $sql = 'DELETE FROM ' . $quoteIdentifier(static::getTable()) . ' WHERE ' . implode(' AND ', $where);
275
276 4
        return $dbh->exec($sql) > 0;
277
    }
278
279
    // data access
280
    /**
281
     * @return array
282
     */
283 2
    public function getData()
284
    {
285 2
        return $this->_data;
286
    }
287
288
    /**
289
     * @param array $data
290
     */
291 15
    public function setData(array $data)
292
    {
293 15
        $this->_data = $data;
294 15
    }
295
296
    /**
297
     * @param string $name
298
     * @return mixed
299
     */
300 15
    public function get($name)
301
    {
302 15
        if (array_key_exists($name, $this->_data)) {
303 13
            return $this->_data[$name];
304
        }
305 9
        return null;
306
    }
307
308
    /**
309
     * @param string $name
310
     * @param mixed $value
311
     * @return $this
312
     */
313 15
    public function set($name, $value)
314
    {
315 15
        $this->_data[$name] = $value;
316 15
        return $this;
317
    }
318
319
    /**
320
     * @param string $name
321
     * @return bool
322
     */
323 2
    public function has($name)
324
    {
325 2
        return isset($this->_data[$name]);
326
    }
327
328
    /**
329
     * Remove data from the model
330
     *
331
     * @param string $name
332
     */
333 2
    public function remove($name)
334
    {
335 2
        $this->_data[$name] = null;
336 2
    }
337
338
    /**
339
     * @param string $name
340
     * @return mixed
341
     */
342 12
    public function __get($name)
343
    {
344 12
        return $this->get($name);
345
    }
346
347
    /**
348
     * @param string $name
349
     * @param mixed $value
350
     */
351 10
    public function __set($name, $value)
352
    {
353 10
        $this->set($name, $value);
354 10
    }
355
356
    /**
357
     * @param string $name
358
     * @return bool
359
     */
360 2
    public function __isset($name)
361
    {
362 2
        return $this->has($name);
363
    }
364
365
    /**
366
     * @param string $name
367
     */
368 1
    public function __unset($name)
369
    {
370 1
        $this->remove($name);
371 1
    }
372
373
    /**
374
     * @param array|Traversable $object
375
     * @param array $except
376
     */
377 2
    public function copyDataFrom($object, array $except = array())
378
    {
379 2
        foreach ($object as $key => $value) {
380 2
            if (!in_array($key, $except)) {
381 2
                $this->set($key, $value);
382 2
            }
383 2
        }
384 2
    }
385
386
    // Iterator
387 2
    public function rewind()
388
    {
389 2
        reset($this->_data);
390 2
    }
391
392
    /**
393
     * @return mixed
394
     */
395 2
    public function current()
396
    {
397 2
        return current($this->_data);
398
    }
399
400
    /**
401
     * @return mixed
402
     */
403 2
    public function key()
404
    {
405 2
        return key($this->_data);
406
    }
407
408 2
    public function next()
409
    {
410 2
        next($this->_data);
411 2
    }
412
413
    /**
414
     * @return bool
415
     */
416 2
    public function valid()
417
    {
418 2
        return key($this->_data) !== null;
419
    }
420
421
    // JsonSerializable
422
    /**
423
     * @return mixed
424
     */
425 1
    public function jsonSerialize()
426
    {
427 1
        return $this->_data;
428
    }
429
}
430