Issues (6)

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.

src/Model.php (6 issues)

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
namespace Bedd\Doctrine\Dbal;
3
4
use \Doctrine\DBAL\Schema;
5
use \Doctrine\DBAL\Query\QueryBuilder;
6
7
/**
8
 * Model
9
 */
10
class Model
11
{
12
    /**
13
     * The DBAL connection
14
     *
15
     * @var \Doctrine\DBAL\Connection
16
     */
17
    protected $conn = null;
18
19
    /**
20
     * Shortcut for the return of getSchemaManager()
21
     *
22
     * @var \Doctrine\DBAL\Schema\AbstractSchemaManager
23
     */
24
    protected $sm = null;
25
26
    /**
27
     * Table-Name
28
     *
29
     * @var string
30
     */
31
    protected $table_name = '';
32
33
    /**
34
     * Quoted Table-Name
35
     *
36
     * @var string
37
     */
38
    protected $quoted_table_name = '';
39
40
    /**
41
     * Array with the Columns
42
     *
43
     * @var \Doctrine\DBAL\Schema\Column[]
44
     */
45
    protected $columns = [];
46
47
    /**
48
     * Shortcut with Column-Types
49
     *
50
     * @var array
51
     */
52
    protected $column_types = [];
53
54
    /**
55
     * Caching variable if the table has an auto increment column
56
     *
57
     * @var bool
58
     */
59
    protected $has_autoincrement = null;
60
61
    /**
62
     * Original return type for the read-method
63
     *
64
     * @var string
65
     */
66
    const READ_RETURN_ORIGINAL = 'original';
67
68
    /**
69
     * Simple return type for the read-method
70
     *
71
     * @var string
72
     */
73
    const READ_RETURN_SIMPLE = 'simple';
74
75
    /**
76
     * Complex return type for the read-method
77
     *
78
     * @var string
79
     */
80
    const READ_RETURN_COMPLEX = 'complex';
81
82
    /**
83
     * constructor
84
     *
85
     * @param string $table_name
86
     * @param Connection $conn
87
     */
88
    public function __construct($table_name, \Doctrine\DBAL\Connection $conn)
89
    {
90
        $this->conn = $conn;
91
        $this->table_name = $table_name;
92
        $this->quoted_table_name = $this->conn->quoteIdentifier($this->table_name);
93
        $this->sm = $this->conn->getSchemaManager();
94
        if (!$this->sm->tablesExist([$table_name])) {
95
            throw Schema\SchemaException::tableDoesNotExist($table_name);
96
        }
97
        foreach ($this->sm->listTableColumns($this->table_name) as $colum) {
98
            $this->columns[$colum->getName()] = $colum;
99
            $this->column_types[$colum->getName()] = $colum->getType()->getName();
100
        }
101
    }
102
103
    /**
104
     * Tells of the table has an auto increment column
105
     *
106
     * @return boolean
107
     */
108
    public function hasAutoIncrement()
109
    {
110
        if ($this->has_autoincrement === null) {
111
            $this->has_autoincrement = false;
112
            foreach ($this->columns as $column) {
113
                if ($column->getAutoincrement()) {
114
                    $this->has_autoincrement = true;
115
                    break;
116
                }
117
            }
118
        }
119
        return $this->has_autoincrement;
120
    }
121
122
    /**
123
     * Returns the Column with $column_name
124
     *
125
     * @param string $column_name
126
     *
127
     * @return \Doctrine\DBAL\Schema\Column or null if the Column doesn't exist
128
     */
129
    protected function getColumn($column_name)
130
    {
131
        return isset($this->columns[$column_name]) ? $this->columns[$column_name] : null;
132
    }
133
134
    /**
135
     * Creates a new datarow
136
     *
137
     * @param array $data
138
     *
139
     * @return boolean|string false on error, true on success or an string with the last insert id if the table has an auto increment column
140
     */
141
    public function create(array $data)
142
    {
143
        $return = false;
0 ignored issues
show
$return is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
144
        $qb = $this->conn->createQueryBuilder()->insert($this->quoted_table_name);
145 View Code Duplication
        foreach ($data as $column => $value) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
            if ($this->getColumn($column) === null) {
147
                throw Schema\SchemaException::columnDoesNotExist($column, $this->table_name);
148
            }
149
            $qb->setValue($this->conn->quoteIdentifier($column), $qb->createNamedParameter($value));
150
        }
151
        $return = (bool) $qb->execute();
152
        if ($this->hasAutoIncrement()) {
153
            $return = $this->conn->lastInsertId();
154
        }
155
        return $return;
156
    }
157
158
    /**
159
     * read $columns from $table_name
160
     *
161
     * @param array $columns
162
     * @param array $filters
163
     * @param array $limit
164
     * @param array $order_by
165
     * @param string $return_type
166
     *
167
     * @return array
168
     */
169
    public function read(array $columns = [], array $filters = [], array $limit = [], array $order_by = [], $return_type = self::READ_RETURN_COMPLEX)
170
    {
171
        $return = [];
0 ignored issues
show
$return is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
172
        $alias_mapping = [];
173
        $qb = $this->conn->createQueryBuilder();
174
        if (empty($columns)) {
175
            $columns = $this->getColumnNames();
176
        }
177
        foreach ($columns as $column) {
178
            $column_name = null;
179
            $alias = null;
180
            $expr = [];
181
            if (is_string($column)) {
182
                $column_name = $alias = $column;
183 View Code Duplication
                if (strpos($alias, ':') !== false) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
                    $alias = explode(':', $alias);
185
                    $alias = array_pop($alias);
186
                }
187
            } elseif (is_array($column)) {
188
                list($column_name, $alias) = $column;
189
            }
190 View Code Duplication
            if (strpos($column_name, ':') !== false) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
                $expr = explode(':', $column_name);
192
                $column_name = array_pop($expr);
193
            }
194
            $alias_mapping[$alias] = $column_name;
195
            if ($this->getColumn($column_name) === null) {
196
                throw Schema\SchemaException::columnDoesNotExist($column_name, $this->table_name);
197
            }
198
            $column_expr = $this->conn->quoteIdentifier($column_name);
199
            $expr = array_reverse($expr);
200
            foreach ($expr as $ex) {
201
                if (in_array($ex, ['max', 'min', 'avg', 'count', 'sum', 'lower', 'upper'])) {
202
                    $column_expr = call_user_func([$this->conn->getDatabasePlatform(), 'get'.ucfirst($ex).'Expression'], $column_expr);
203
                } else {
204
                    throw QueryBuilderException::expressionTypeDoesNotExist($ex);
205
                }
206
            }
207
            if ($column_name !== $alias || !empty($expr)) {
208
                $column_expr .= ' AS '.$this->conn->quoteIdentifier($alias);
209
            }
210
            $qb->addSelect($column_expr);
211
        }
212
        $qb->from($this->quoted_table_name);
213
        $this
214
            ->buildWhere($qb, $filters)
215
            ->buildOrderBy($qb, $order_by)
216
            ->buildLimit($qb, $limit)
217
        ;
218
        $res = $qb->execute();
219
        $return = $res->fetchAll(\PDO::FETCH_ASSOC);
220
        if (in_array($return_type, [self::READ_RETURN_SIMPLE, self::READ_RETURN_COMPLEX])) {
221
            foreach ($return as $index => $row) {
222
                foreach ($row as $column => $value) {
223
                    if ($return_type === self::READ_RETURN_SIMPLE && !$this->isSimpleType($alias_mapping[$column])) {
224
                        continue;
225
                    }
226
                    $return[$index][$column] = $this->conn->convertToPHPValue($value, $this->column_types[$alias_mapping[$column]]);
227
                }
228
            }
229
        }
230
        return $return;
231
    }
232
233
    /**
234
     * Update table rows
235
     *
236
     * @param array $data
237
     * @param array $filters
238
     *
239
     * @return integer The number of rows
240
     */
241
    public function update(array $data, array $filters = [])
242
    {
243
        $qb = $this->conn->createQueryBuilder()->update($this->quoted_table_name);
244 View Code Duplication
        foreach ($data as $column => $value) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
245
            if ($this->getColumn($column) === null) {
246
                throw Schema\SchemaException::columnDoesNotExist($column, $this->table_name);
247
            }
248
            $qb->set($this->conn->quoteIdentifier($column), $qb->createNamedParameter($value));
249
        }
250
        $this->buildWhere($qb, $filters);
251
        return $qb->execute();
252
    }
253
254
    /**
255
     * Delete rows
256
     *
257
     * @param array $filters
258
     *
259
     * @return integer The number of rows
260
     */
261
    public function delete(array $filters = [])
262
    {
263
        $qb = $this->conn->createQueryBuilder()->delete($this->quoted_table_name);
264
        $this->buildWhere($qb, $filters);
265
        return $qb->execute();
266
    }
267
268
    /**
269
     * copy some records and maybe change some values
270
     *
271
     * @param array $filters
272
     * @param array $limit
273
     * @param array $order_by
274
     * @param array $changes
275
     *
276
     * @return array the result of each $this->create()
277
     */
278
    public function copy(array $filters = [], array $limit = [], array $order_by = [], array $changes = [])
279
    {
280
        $return = [];
281
        //read
282
        $data = $this->read($this->getColumnNames(), $filters, $limit, $order_by, self::READ_RETURN_SIMPLE);
283
        foreach ($data as $row) {
284
            foreach ($row as $column_name => $value) {
285
                $column = $this->getColumn($column_name);
286
                //changes
287
                if (isset($changes[$column_name])) {
288
                    $row[$column_name] = $changes[$column_name];
289
                }
290
                //remove autoincrements
291
                if ($column->getAutoincrement()) {
292
                    unset($row[$column_name]);
293
                }
294
            }
295
            $return[] = $this->create($row);
296
        }
297
        return $return;
298
    }
299
300
    /**
301
     * Builds the WHERE clause from $filter
302
     *
303
     * @param QueryBuilder $qb
304
     * @param array $filters should be an array with arrays wich contains 3 datas
305
     * [
306
     *    ['column_name', 'expr_type', 'value'],
307
     *    [],
308
     *    ...
309
     * ]
310
     * expr_types: 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'like', 'in', 'notIn', 'notLike'
311
     *
312
     * @return self
313
     */
314
    protected function buildWhere(QueryBuilder $qb, array $filters = [])
315
    {
316
        if (!empty($filters)) {
317
            $expr = $qb->expr()->andX();
318
            foreach ($filters as $f) {
319
                $column = $f[0];
320
                $expr_type = $f[1];
321
                $value = isset($f[2]) ? $f[2] : null;
322
                $type = \PDO::PARAM_STR;
323
                if ($this->getColumn($column) === null) {
324
                    throw Schema\SchemaException::columnDoesNotExist($column, $this->table_name);
325
                }
326
                if (!in_array($expr_type, $this->getExpressionTypes())) {
327
                    throw QueryBuilderException::expressionTypeDoesNotExist($expr_type);
328
                }
329
                if (in_array($expr_type, ['in', 'notIn']) && is_array($value)) {
330
                    switch ($this->column_types[$column]) {
331
                        case 'integer':
332
                            $type = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
333
                            break;
334
                        case 'string':
335
                            $type = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
336
                            break;
337
                    }
338
                }
339
                $expr->add($qb->expr()->$expr_type($this->conn->quoteIdentifier($column), $qb->createNamedParameter($value, $type)));
340
            }
341
            $qb->where($expr);
342
        }
343
        return $this;
344
    }
345
346
    /**
347
     * Build up dynamilcy the LIMIT part
348
     *
349
     * @param QueryBuilder $qb
350
     * @param array $limit
351
     *
352
     * @return self
353
     */
354
    protected function buildLimit(QueryBuilder $qb, array $limit = [])
355
    {
356
        switch (count($limit)) {
357
            case 2:
358
                $qb->setFirstResult((int) $limit[0]);
359
                $qb->setMaxResults((int) $limit[1]);
360
                break;
361
            case 1:
362
                $qb->setMaxResults((int) $limit[0]);
363
                break;
364
        }
365
        return $this;
366
    }
367
368
    /**
369
     * Builds the ORDER BY part
370
     *
371
     * @param QueryBuilder $qb
372
     * @param array $order_by
373
     *
374
     * @return self
375
     */
376
    protected function buildOrderBy(QueryBuilder $qb, array $order_by = [])
377
    {
378
        foreach ($order_by as $order) {
379
            $column = null;
380
            $direction = 'ASC';
381
            if (is_string($order)) {
382
                $column = $order;
383
            } elseif (is_array($order) && count($order) === 2) {
384
                list($column, $direction) = $order;
385
            }
386
            if ($column === null || $this->getColumn($column) === null) {
387
                throw Schema\SchemaException::columnDoesNotExist($column, $this->table_name);
388
            }
389
            if (!in_array($direction, ['ASC', 'DESC'])) {
390
                throw QueryBuilderException::orderByDirectionDoesNotExist($direction);
391
            }
392
            $qb->addOrderBy($this->conn->quoteIdentifier($column), $direction);
393
        }
394
        return $this;
395
    }
396
397
    /**
398
     * Returns the table-Name
399
     *
400
     * @return string
401
     */
402
    public function getTableName()
403
    {
404
        return $this->table_name;
405
    }
406
407
    /**
408
     * List all possible expression types
409
     *
410
     * @return string[]
411
     */
412
    public function getExpressionTypes()
413
    {
414
        return ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'like', 'in', 'notIn', 'notLike', 'isNull', 'isNotNull'];
415
    }
416
417
    /**
418
     * List all possible column names
419
     * @return string[]
420
     */
421
    public function getColumnNames()
422
    {
423
        return array_keys($this->columns);
424
    }
425
426
    /**
427
     * return true if the column type of $column_name is a simple type like string, integer, ...
428
     * @param string $column_name
429
     * @return boolean
430
     */
431
    public function isSimpleType($column_name)
432
    {
433
        return in_array($this->column_types[$column_name], ['string', 'integer']);
434
    }
435
}
436