Issues (83)

Security Analysis    not enabled

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/Mouf/Database/TDBM/DbRow.php (9 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
3
namespace Mouf\Database\TDBM;
4
5
/*
6
 Copyright (C) 2006-2016 David Négrier - THE CODING MACHINE
7
8
 This program is free software; you can redistribute it and/or modify
9
 it under the terms of the GNU General Public License as published by
10
 the Free Software Foundation; either version 2 of the License, or
11
 (at your option) any later version.
12
13
 This program is distributed in the hope that it will be useful,
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 GNU General Public License for more details.
17
18
 You should have received a copy of the GNU General Public License
19
 along with this program; if not, write to the Free Software
20
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
23
/**
24
 * Instances of this class represent a row in a database.
25
 *
26
 * @author David Negrier
27
 */
28
class DbRow
29
{
30
    /**
31
     * The service this object is bound to.
32
     *
33
     * @var TDBMService
34
     */
35
    protected $tdbmService;
36
37
    /**
38
     * The object containing this db row.
39
     *
40
     * @var AbstractTDBMObject
41
     */
42
    private $object;
43
44
    /**
45
     * The name of the table the object if issued from.
46
     *
47
     * @var string
48
     */
49
    private $dbTableName;
50
51
    /**
52
     * The array of columns returned from database.
53
     *
54
     * @var array
55
     */
56
    private $dbRow = array();
57
58
    /**
59
     * @var AbstractTDBMObject[]
60
     */
61
    private $references = array();
62
63
    /**
64
     * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
65
     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
66
     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
67
     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
68
     *
69
     * @var string
70
     */
71
    private $status;
72
73
    /**
74
     * The values of the primary key.
75
     * This is set when the object is in "loaded" state.
76
     *
77
     * @var array An array of column => value
78
     */
79
    private $primaryKeys;
80
81
    /**
82
     * You should never call the constructor directly. Instead, you should use the
83
     * TDBMService class that will create TDBMObjects for you.
84
     *
85
     * Used with id!=false when we want to retrieve an existing object
86
     * and id==false if we want a new object
87
     *
88
     * @param AbstractTDBMObject $object      The object containing this db row
89
     * @param string             $table_name
90
     * @param array              $primaryKeys
91
     * @param TDBMService        $tdbmService
92
     *
93
     * @throws TDBMException
94
     * @throws TDBMInvalidOperationException
95
     */
96
    public function __construct(AbstractTDBMObject $object, $table_name, array $primaryKeys = array(), TDBMService $tdbmService = null, array $dbRow = array())
97
    {
98
        $this->object = $object;
99
        $this->dbTableName = $table_name;
100
101
        $this->status = TDBMObjectStateEnum::STATE_DETACHED;
102
103
        if ($tdbmService === null) {
104
            if (!empty($primaryKeys)) {
105
                throw new TDBMException('You cannot pass an id to the DbRow constructor without passing also a TDBMService.');
106
            }
107
        } else {
108
            $this->tdbmService = $tdbmService;
109
110
            if (!empty($primaryKeys)) {
111
                $this->_setPrimaryKeys($primaryKeys);
112
                if (!empty($dbRow)) {
113
                    $this->dbRow = $dbRow;
114
                    $this->status = TDBMObjectStateEnum::STATE_LOADED;
115
                } else {
116
                    $this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
117
                }
118
                $tdbmService->_addToCache($this);
119
            } else {
120
                $this->status = TDBMObjectStateEnum::STATE_NEW;
121
                $this->tdbmService->_addToToSaveObjectList($this);
122
            }
123
        }
124
    }
125
126
    public function _attach(TDBMService $tdbmService)
127
    {
128
        if ($this->status !== TDBMObjectStateEnum::STATE_DETACHED) {
129
            throw new TDBMInvalidOperationException('Cannot attach an object that is already attached to TDBM.');
130
        }
131
        $this->tdbmService = $tdbmService;
132
        $this->status = TDBMObjectStateEnum::STATE_NEW;
133
        $this->tdbmService->_addToToSaveObjectList($this);
134
    }
135
136
    /**
137
     * Sets the state of the TDBM Object
138
     * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
139
     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
140
     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
141
     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
142
     *
143
     * @param string $state
144
     */
145
    public function _setStatus($state)
146
    {
147
        $this->status = $state;
148
    }
149
150
    /**
151
     * This is an internal method. You should not call this method yourself. The TDBM library will do it for you.
152
     * If the object is in state 'not loaded', this method performs a query in database to load the object.
153
     *
154
     * A TDBMException is thrown is no object can be retrieved (for instance, if the primary key specified
155
     * cannot be found).
156
     */
157
    public function _dbLoadIfNotLoaded()
158
    {
159
        if ($this->status == TDBMObjectStateEnum::STATE_NOT_LOADED) {
160
            $connection = $this->tdbmService->getConnection();
161
162
            /// buildFilterFromFilterBag($filter_bag)
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
163
            list($sql_where, $parameters) = $this->tdbmService->buildFilterFromFilterBag($this->primaryKeys);
164
165
            $sql = 'SELECT * FROM '.$connection->quoteIdentifier($this->dbTableName).' WHERE '.$sql_where;
0 ignored issues
show
'SELECT * FROM ' . $conn... ' WHERE ' . $sql_where is used as a query on line 166. If $sql_where can contain user-input, it is usually preferable to use a parameter placeholder like :paramName and pass the dynamic input as second argument array('param' => $sql_where).

Instead of embedding dynamic parameters in SQL, Doctrine also allows you to pass them separately and insert a placeholder instead:

function findUser(Doctrine\DBAL\Connection $con, $email) {
    // Unsafe
    $con->executeQuery("SELECT * FROM users WHERE email = '".$email."'");

    // Safe
    $con->executeQuery(
        "SELECT * FROM users WHERE email = :email",
        array('email' => $email)
    );
}
Loading history...
166
            $result = $connection->executeQuery($sql, $parameters);
167
168
            if ($result->rowCount() === 0) {
169
                throw new TDBMException("Could not retrieve object from table \"$this->dbTableName\" using filter \"\".");
170
            }
171
172
            $row = $result->fetch(\PDO::FETCH_ASSOC);
173
174
            $this->dbRow = [];
175
            $types = $this->tdbmService->_getColumnTypesForTable($this->dbTableName);
176
177
            foreach ($row as $key => $value) {
178
                $this->dbRow[$key] = $types[$key]->convertToPHPValue($value, $connection->getDatabasePlatform());
179
            }
180
181
            $result->closeCursor();
182
183
            $this->status = TDBMObjectStateEnum::STATE_LOADED;
184
        }
185
    }
186
187
    public function get($var)
188
    {
189
        $this->_dbLoadIfNotLoaded();
190
191
        // Let's first check if the key exist.
192
        if (!isset($this->dbRow[$var])) {
193
            /*
194
            // Unable to find column.... this is an error if the object has been retrieved from database.
195
            // If it's a new object, well, that may not be an error after all!
196
            // Let's check if the column does exist in the table
197
            $column_exist = $this->db_connection->checkColumnExist($this->dbTableName, $var);
198
            // If the column DOES exist, then the object is new, and therefore, we should
199
            // return null.
200
            if ($column_exist === true) {
201
                return null;
202
            }
203
204
            // Let's try to be accurate in error reporting. The checkColumnExist returns an array of closest matches.
205
            $result_array = $column_exist;
206
207
            if (count($result_array)==1)
208
            $str = "Could not find column \"$var\" in table \"$this->dbTableName\". Maybe you meant this column: '".$result_array[0]."'";
209
            else
210
            $str = "Could not find column \"$var\" in table \"$this->dbTableName\". Maybe you meant one of those columns: '".implode("', '",$result_array)."'";
211
212
            throw new TDBMException($str);*/
213
            return;
214
        }
215
216
        $value = $this->dbRow[$var];
217
        if ($value instanceof \DateTime) {
218
            if (method_exists('DateTimeImmutable', 'createFromMutable')) { // PHP 5.6+ only
219
                return \DateTimeImmutable::createFromMutable($value);
220
            } else {
221
                return new \DateTimeImmutable($value->format('c'));
222
            }
223
        }
224
225
        return $this->dbRow[$var];
226
    }
227
228
    /**
229
     * Returns true if a column is set, false otherwise.
230
     *
231
     * @param string $var
232
     *
233
     * @return bool
234
     */
235
    /*public function has($var) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
236
        $this->_dbLoadIfNotLoaded();
237
238
        return isset($this->dbRow[$var]);
239
    }*/
240
241 View Code Duplication
    public function set($var, $value)
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
242
    {
243
        $this->_dbLoadIfNotLoaded();
244
245
        /*
246
        // Ok, let's start by checking the column type
247
        $type = $this->db_connection->getColumnType($this->dbTableName, $var);
248
249
        // Throws an exception if the type is not ok.
250
        if (!$this->db_connection->checkType($value, $type)) {
251
            throw new TDBMException("Error! Invalid value passed for attribute '$var' of table '$this->dbTableName'. Passed '$value', but expecting '$type'");
252
        }
253
        */
254
255
        /*if ($var == $this->getPrimaryKey() && isset($this->dbRow[$var]))
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
256
            throw new TDBMException("Error! Changing primary key value is forbidden.");*/
257
        $this->dbRow[$var] = $value;
258
        if ($this->tdbmService !== null && $this->status === TDBMObjectStateEnum::STATE_LOADED) {
259
            $this->status = TDBMObjectStateEnum::STATE_DIRTY;
260
            $this->tdbmService->_addToToSaveObjectList($this);
261
        }
262
    }
263
264
    /**
265
     * @param string             $foreignKeyName
266
     * @param AbstractTDBMObject $bean
267
     */
268 View Code Duplication
    public function setRef($foreignKeyName, AbstractTDBMObject $bean = null)
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
269
    {
270
        $this->references[$foreignKeyName] = $bean;
271
272
        if ($this->tdbmService !== null && $this->status === TDBMObjectStateEnum::STATE_LOADED) {
273
            $this->status = TDBMObjectStateEnum::STATE_DIRTY;
274
            $this->tdbmService->_addToToSaveObjectList($this);
275
        }
276
    }
277
278
    /**
279
     * @param string $foreignKeyName A unique name for this reference
280
     *
281
     * @return AbstractTDBMObject|null
282
     */
283
    public function getRef($foreignKeyName)
284
    {
285
        if (array_key_exists($foreignKeyName, $this->references)) {
286
            return $this->references[$foreignKeyName];
287
        } elseif ($this->status === TDBMObjectStateEnum::STATE_NEW || $this->tdbmService === null) {
288
            // If the object is new and has no property, then it has to be empty.
289
            return;
290
        } else {
291
            $this->_dbLoadIfNotLoaded();
292
293
            // Let's match the name of the columns to the primary key values
294
            $fk = $this->tdbmService->_getForeignKeyByName($this->dbTableName, $foreignKeyName);
295
296
            $values = [];
297
            foreach ($fk->getLocalColumns() as $column) {
298
                if (!isset($this->dbRow[$column])) {
299
                    return;
300
                }
301
                $values[] = $this->dbRow[$column];
302
            }
303
304
            $filter = array_combine($this->tdbmService->getPrimaryKeyColumns($fk->getForeignTableName()), $values);
305
306
            return $this->tdbmService->findObjectByPk($fk->getForeignTableName(), $filter, [], true);
307
        }
308
    }
309
310
    /**
311
     * Returns the name of the table this object comes from.
312
     *
313
     * @return string
314
     */
315
    public function _getDbTableName()
316
    {
317
        return $this->dbTableName;
318
    }
319
320
    /**
321
     * Method used internally by TDBM. You should not use it directly.
322
     * This method returns the status of the TDBMObject.
323
     * This is one of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
324
     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
325
     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
326
     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
327
     *
328
     * @return string
329
     */
330
    public function _getStatus()
331
    {
332
        return $this->status;
333
    }
334
335
    /**
336
     * Override the native php clone function for TDBMObjects.
337
     */
338
    public function __clone()
339
    {
340
        // Let's load the row (before we lose the ID!)
341
        $this->_dbLoadIfNotLoaded();
342
343
        //Let's set the status to detached
344
        $this->status = TDBMObjectStateEnum::STATE_DETACHED;
345
346
        $this->primaryKeys = [];
347
348
        //Now unset the PK from the row
349
        if ($this->tdbmService) {
350
            $pk_array = $this->tdbmService->getPrimaryKeyColumns($this->dbTableName);
351
            foreach ($pk_array as $pk) {
0 ignored issues
show
The expression $pk_array of type string|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...
352
                $this->dbRow[$pk] = null;
353
            }
354
        }
355
    }
356
357
    /**
358
     * Returns raw database row.
359
     *
360
     * @return array
361
     *
362
     * @throws TDBMMissingReferenceException
363
     */
364
    public function _getDbRow()
365
    {
366
        // Let's merge $dbRow and $references
367
        $dbRow = $this->dbRow;
368
369
        foreach ($this->references as $foreignKeyName => $reference) {
370
            // Let's match the name of the columns to the primary key values
371
            $fk = $this->tdbmService->_getForeignKeyByName($this->dbTableName, $foreignKeyName);
372
            $localColumns = $fk->getLocalColumns();
373
374
            if ($reference !== null) {
375
                $refDbRows = $reference->_getDbRows();
376
                $firstRefDbRow = reset($refDbRows);
377
                if ($firstRefDbRow->_getStatus() == TDBMObjectStateEnum::STATE_DELETED) {
378
                    throw TDBMMissingReferenceException::referenceDeleted($this->dbTableName, $reference);
379
                }
380
                $pkValues = array_values($firstRefDbRow->_getPrimaryKeys());
381 View Code Duplication
                for ($i = 0, $count = count($localColumns); $i < $count; ++$i) {
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...
382
                    $dbRow[$localColumns[$i]] = $pkValues[$i];
383
                }
384
            } else {
385 View Code Duplication
                for ($i = 0, $count = count($localColumns); $i < $count; ++$i) {
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...
386
                    $dbRow[$localColumns[$i]] = null;
387
                }
388
            }
389
        }
390
391
        return $dbRow;
392
    }
393
394
    /**
395
     * Returns references array.
396
     *
397
     * @return AbstractTDBMObject[]
398
     */
399
    public function _getReferences()
400
    {
401
        return $this->references;
402
    }
403
404
    /**
405
     * Returns the values of the primary key.
406
     * This is set when the object is in "loaded" state.
407
     *
408
     * @return array
409
     */
410
    public function _getPrimaryKeys()
411
    {
412
        return $this->primaryKeys;
413
    }
414
415
    /**
416
     * Sets the values of the primary key.
417
     * This is set when the object is in "loaded" state.
418
     *
419
     * @param array $primaryKeys
420
     */
421
    public function _setPrimaryKeys(array $primaryKeys)
422
    {
423
        $this->primaryKeys = $primaryKeys;
424
        foreach ($this->primaryKeys as $column => $value) {
425
            $this->dbRow[$column] = $value;
426
        }
427
    }
428
429
    /**
430
     * Returns the TDBMObject this bean is associated to.
431
     *
432
     * @return AbstractTDBMObject
433
     */
434
    public function getTDBMObject()
435
    {
436
        return $this->object;
437
    }
438
439
    /**
440
     * Sets the TDBMObject this bean is associated to.
441
     * Only used when cloning.
442
     *
443
     * @param AbstractTDBMObject $object
444
     */
445
    public function setTDBMObject(AbstractTDBMObject $object)
446
    {
447
        $this->object = $object;
448
    }
449
}
450