Failed Conditions
Push — master ( 6744b4...2b8acb )
by Marco
60:45 queued 60:36
created

ORM/Persisters/Entity/BasicEntityPersister.php (94 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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Persisters\Entity;
21
22
use Doctrine\Common\Collections\Criteria;
23
use Doctrine\Common\Collections\Expr\Comparison;
24
use Doctrine\Common\Util\ClassUtils;
25
use Doctrine\DBAL\Connection;
26
use Doctrine\DBAL\LockMode;
27
use Doctrine\DBAL\Types\Type;
28
use Doctrine\ORM\EntityManagerInterface;
29
use Doctrine\ORM\Mapping\ClassMetadata;
30
use Doctrine\ORM\Mapping\MappingException;
31
use Doctrine\ORM\OptimisticLockException;
32
use Doctrine\ORM\ORMException;
33
use Doctrine\ORM\PersistentCollection;
34
use Doctrine\ORM\Persisters\SqlExpressionVisitor;
35
use Doctrine\ORM\Persisters\SqlValueVisitor;
36
use Doctrine\ORM\Query;
37
use Doctrine\ORM\UnitOfWork;
38
use Doctrine\ORM\Utility\IdentifierFlattener;
39
use Doctrine\ORM\Utility\PersisterHelper;
40
41
/**
42
 * A BasicEntityPersister maps an entity to a single table in a relational database.
43
 *
44
 * A persister is always responsible for a single entity type.
45
 *
46
 * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent
47
 * state of entities onto a relational database when the UnitOfWork is committed,
48
 * as well as for basic querying of entities and their associations (not DQL).
49
 *
50
 * The persisting operations that are invoked during a commit of a UnitOfWork to
51
 * persist the persistent entity state are:
52
 *
53
 *   - {@link addInsert} : To schedule an entity for insertion.
54
 *   - {@link executeInserts} : To execute all scheduled insertions.
55
 *   - {@link update} : To update the persistent state of an entity.
56
 *   - {@link delete} : To delete the persistent state of an entity.
57
 *
58
 * As can be seen from the above list, insertions are batched and executed all at once
59
 * for increased efficiency.
60
 *
61
 * The querying operations invoked during a UnitOfWork, either through direct find
62
 * requests or lazy-loading, are the following:
63
 *
64
 *   - {@link load} : Loads (the state of) a single, managed entity.
65
 *   - {@link loadAll} : Loads multiple, managed entities.
66
 *   - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading).
67
 *   - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading).
68
 *   - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading).
69
 *
70
 * The BasicEntityPersister implementation provides the default behavior for
71
 * persisting and querying entities that are mapped to a single database table.
72
 *
73
 * Subclasses can be created to provide custom persisting and querying strategies,
74
 * i.e. spanning multiple tables.
75
 *
76
 * @author Roman Borschel <[email protected]>
77
 * @author Giorgio Sironi <[email protected]>
78
 * @author Benjamin Eberlei <[email protected]>
79
 * @author Alexander <[email protected]>
80
 * @author Fabio B. Silva <[email protected]>
81
 * @author Rob Caiger <[email protected]>
82
 * @since 2.0
83
 */
84
class BasicEntityPersister implements EntityPersister
85
{
86
    /**
87
     * @var array
88
     */
89
    static private $comparisonMap = [
90
        Comparison::EQ          => '= %s',
91
        Comparison::IS          => '= %s',
92
        Comparison::NEQ         => '!= %s',
93
        Comparison::GT          => '> %s',
94
        Comparison::GTE         => '>= %s',
95
        Comparison::LT          => '< %s',
96
        Comparison::LTE         => '<= %s',
97
        Comparison::IN          => 'IN (%s)',
98
        Comparison::NIN         => 'NOT IN (%s)',
99
        Comparison::CONTAINS    => 'LIKE %s',
100
        Comparison::STARTS_WITH => 'LIKE %s',
101
        Comparison::ENDS_WITH   => 'LIKE %s',
102
    ];
103
104
    /**
105
     * Metadata object that describes the mapping of the mapped entity class.
106
     *
107
     * @var \Doctrine\ORM\Mapping\ClassMetadata
108
     */
109
    protected $class;
110
111
    /**
112
     * The underlying DBAL Connection of the used EntityManager.
113
     *
114
     * @var \Doctrine\DBAL\Connection $conn
115
     */
116
    protected $conn;
117
118
    /**
119
     * The database platform.
120
     *
121
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
122
     */
123
    protected $platform;
124
125
    /**
126
     * The EntityManager instance.
127
     *
128
     * @var EntityManagerInterface
129
     */
130
    protected $em;
131
132
    /**
133
     * Queued inserts.
134
     *
135
     * @var array
136
     */
137
    protected $queuedInserts = [];
138
139
    /**
140
     * The map of column names to DBAL mapping types of all prepared columns used
141
     * when INSERTing or UPDATEing an entity.
142
     *
143
     * @var array
144
     *
145
     * @see prepareInsertData($entity)
146
     * @see prepareUpdateData($entity)
147
     */
148
    protected $columnTypes = [];
149
150
    /**
151
     * The map of quoted column names.
152
     *
153
     * @var array
154
     *
155
     * @see prepareInsertData($entity)
156
     * @see prepareUpdateData($entity)
157
     */
158
    protected $quotedColumns = [];
159
160
    /**
161
     * The INSERT SQL statement used for entities handled by this persister.
162
     * This SQL is only generated once per request, if at all.
163
     *
164
     * @var string
165
     */
166
    private $insertSql;
167
168
    /**
169
     * The quote strategy.
170
     *
171
     * @var \Doctrine\ORM\Mapping\QuoteStrategy
172
     */
173
    protected $quoteStrategy;
174
175
    /**
176
     * The IdentifierFlattener used for manipulating identifiers
177
     *
178
     * @var \Doctrine\ORM\Utility\IdentifierFlattener
179
     */
180
    private $identifierFlattener;
181
182
    /**
183
     * @var CachedPersisterContext
184
     */
185
    protected $currentPersisterContext;
186
187
    /**
188
     * @var CachedPersisterContext
189
     */
190
    private $limitsHandlingContext;
191
192
    /**
193
     * @var CachedPersisterContext
194
     */
195
    private $noLimitsContext;
196
197
    /**
198
     * Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
199
     * and persists instances of the class described by the given ClassMetadata descriptor.
200
     *
201
     * @param EntityManagerInterface $em
202
     * @param ClassMetadata          $class
203
     */
204 1136
    public function __construct(EntityManagerInterface $em, ClassMetadata $class)
205
    {
206 1136
        $this->em                    = $em;
207 1136
        $this->class                 = $class;
208 1136
        $this->conn                  = $em->getConnection();
209 1136
        $this->platform              = $this->conn->getDatabasePlatform();
210 1136
        $this->quoteStrategy         = $em->getConfiguration()->getQuoteStrategy();
211 1136
        $this->identifierFlattener   = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
212 1136
        $this->noLimitsContext       = $this->currentPersisterContext = new CachedPersisterContext(
213 1136
            $class,
214 1136
            new Query\ResultSetMapping(),
215 1136
            false
216
        );
217 1136
        $this->limitsHandlingContext = new CachedPersisterContext(
218 1136
            $class,
219 1136
            new Query\ResultSetMapping(),
220 1136
            true
221
        );
222 1136
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 17
    public function getClassMetadata()
228
    {
229 17
        return $this->class;
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235 10
    public function getResultSetMapping()
236
    {
237 10
        return $this->currentPersisterContext->rsm;
238
    }
239
240
    /**
241
     * {@inheritdoc}
242
     */
243 1022
    public function addInsert($entity)
244
    {
245 1022
        $this->queuedInserts[spl_object_hash($entity)] = $entity;
246 1022
    }
247
248
    /**
249
     * {@inheritdoc}
250
     */
251 93
    public function getInserts()
252
    {
253 93
        return $this->queuedInserts;
254
    }
255
256
    /**
257
     * {@inheritdoc}
258
     */
259 1003
    public function executeInserts()
260
    {
261 1003
        if ( ! $this->queuedInserts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->queuedInserts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
262 640
            return [];
263
        }
264
265 954
        $postInsertIds  = [];
266 954
        $idGenerator    = $this->class->idGenerator;
267 954
        $isPostInsertId = $idGenerator->isPostInsertGenerator();
268
269 954
        $stmt       = $this->conn->prepare($this->getInsertSQL());
270 954
        $tableName  = $this->class->getTableName();
271
272 954
        foreach ($this->queuedInserts as $entity) {
273 954
            $insertData = $this->prepareInsertData($entity);
274
275 954
            if (isset($insertData[$tableName])) {
276 927
                $paramIndex = 1;
277
278 927
                foreach ($insertData[$tableName] as $column => $value) {
279 927
                    $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]);
280
                }
281
            }
282
283 954
            $stmt->execute();
284
285 953 View Code Duplication
            if ($isPostInsertId) {
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...
286 865
                $generatedId = $idGenerator->generate($this->em, $entity);
0 ignored issues
show
$this->em of type object<Doctrine\ORM\EntityManagerInterface> is not a sub-type of object<Doctrine\ORM\EntityManager>. It seems like you assume a concrete implementation of the interface Doctrine\ORM\EntityManagerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
287
                $id = [
288 865
                    $this->class->identifier[0] => $generatedId
289
                ];
290 865
                $postInsertIds[] = [
291 865
                    'generatedId' => $generatedId,
292 865
                    'entity' => $entity,
293
                ];
294
            } else {
295 261
                $id = $this->class->getIdentifierValues($entity);
296
            }
297
298 953
            if ($this->class->isVersioned) {
299 953
                $this->assignDefaultVersionValue($entity, $id);
300
            }
301
        }
302
303 953
        $stmt->closeCursor();
304 953
        $this->queuedInserts = [];
305
306 953
        return $postInsertIds;
307
    }
308
309
    /**
310
     * Retrieves the default version value which was created
311
     * by the preceding INSERT statement and assigns it back in to the
312
     * entities version field.
313
     *
314
     * @param object $entity
315
     * @param array  $id
316
     *
317
     * @return void
318
     */
319 200
    protected function assignDefaultVersionValue($entity, array $id)
320
    {
321 200
        $value = $this->fetchVersionValue($this->class, $id);
322
323 200
        $this->class->setFieldValue($entity, $this->class->versionField, $value);
324 200
    }
325
326
    /**
327
     * Fetches the current version value of a versioned entity.
328
     *
329
     * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass
330
     * @param array                               $id
331
     *
332
     * @return mixed
333
     */
334 209
    protected function fetchVersionValue($versionedClass, array $id)
335
    {
336 209
        $versionField = $versionedClass->versionField;
337 209
        $fieldMapping = $versionedClass->fieldMappings[$versionField];
338 209
        $tableName    = $this->quoteStrategy->getTableName($versionedClass, $this->platform);
339 209
        $identifier   = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform);
340 209
        $columnName   = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform);
341
342
        // FIXME: Order with composite keys might not be correct
343 209
        $sql = 'SELECT ' . $columnName
344 209
             . ' FROM '  . $tableName
345 209
             . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';
346
347
348 209
        $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id);
349
350 209
        $value = $this->conn->fetchColumn(
351 209
            $sql,
352 209
            array_values($flatId),
353 209
            0,
354 209
            $this->extractIdentifierTypes($id, $versionedClass)
355
        );
356
357 209
        return Type::getType($fieldMapping['type'])->convertToPHPValue($value, $this->platform);
358
    }
359
360 209
    private function extractIdentifierTypes(array $id, ClassMetadata $versionedClass) : array
361
    {
362 209
        $types = [];
363
364 209
        foreach ($id as $field => $value) {
365 209
            $types = array_merge($types, $this->getTypes($field, $value, $versionedClass));
366
        }
367
368 209
        return $types;
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374 97
    public function update($entity)
375
    {
376 97
        $tableName  = $this->class->getTableName();
377 97
        $updateData = $this->prepareUpdateData($entity);
378
379 97
        if ( ! isset($updateData[$tableName]) || ! ($data = $updateData[$tableName])) {
380 8
            return;
381
        }
382
383 89
        $isVersioned     = $this->class->isVersioned;
384 89
        $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
385
386 89
        $this->updateTable($entity, $quotedTableName, $data, $isVersioned);
387
388 87
        if ($isVersioned) {
389 12
            $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
390
391 12
            $this->assignDefaultVersionValue($entity, $id);
392
        }
393 87
    }
394
395
    /**
396
     * Performs an UPDATE statement for an entity on a specific table.
397
     * The UPDATE can optionally be versioned, which requires the entity to have a version field.
398
     *
399
     * @param object  $entity          The entity object being updated.
400
     * @param string  $quotedTableName The quoted name of the table to apply the UPDATE on.
401
     * @param array   $updateData      The map of columns to update (column => value).
402
     * @param boolean $versioned       Whether the UPDATE should be versioned.
403
     *
404
     * @return void
405
     *
406
     * @throws \Doctrine\ORM\ORMException
407
     * @throws \Doctrine\ORM\OptimisticLockException
408
     */
409 120
    protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false)
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
410
    {
411 120
        $set    = [];
412 120
        $types  = [];
413 120
        $params = [];
414
415 120
        foreach ($updateData as $columnName => $value) {
416 120
            $placeholder = '?';
417 120
            $column      = $columnName;
418
419
            switch (true) {
420 120
                case isset($this->class->fieldNames[$columnName]):
421 60
                    $fieldName  = $this->class->fieldNames[$columnName];
422 60
                    $column     = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform);
423
424 60 View Code Duplication
                    if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) {
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...
425 3
                        $type        = Type::getType($this->columnTypes[$columnName]);
426 3
                        $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
427
                    }
428
429 60
                    break;
430
431 62
                case isset($this->quotedColumns[$columnName]):
432 62
                    $column = $this->quotedColumns[$columnName];
433
434 62
                    break;
435
            }
436
437 120
            $params[]   = $value;
438 120
            $set[]      = $column . ' = ' . $placeholder;
439 120
            $types[]    = $this->columnTypes[$columnName];
440
        }
441
442 120
        $where      = [];
443 120
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
444
445 120
        foreach ($this->class->identifier as $idField) {
446 120
            if ( ! isset($this->class->associationMappings[$idField])) {
447 117
                $params[]   = $identifier[$idField];
448 117
                $types[]    = $this->class->fieldMappings[$idField]['type'];
449 117
                $where[]    = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform);
450
451 117
                continue;
452
            }
453
454 4
            $params[]       = $identifier[$idField];
455 4
            $where[]        = $this->class->associationMappings[$idField]['joinColumns'][0]['name'];
456 4
            $targetMapping  = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']);
457
458
            switch (true) {
459 4 View Code Duplication
                case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])):
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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...
460 3
                    $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
461 3
                    break;
462
463 1 View Code Duplication
                case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])):
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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...
464 1
                    $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
465 1
                    break;
466
467
                default:
468 4
                    throw ORMException::unrecognizedField($targetMapping->identifier[0]);
0 ignored issues
show
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
469
            }
470
471
        }
472
473 120
        if ($versioned) {
474 18
            $versionField       = $this->class->versionField;
475 18
            $versionFieldType   = $this->class->fieldMappings[$versionField]['type'];
476 18
            $versionColumn      = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform);
477
478 18
            $where[]    = $versionColumn;
479 18
            $types[]    = $this->class->fieldMappings[$versionField]['type'];
480 18
            $params[]   = $this->class->reflFields[$versionField]->getValue($entity);
481
482
            switch ($versionFieldType) {
483 18
                case Type::SMALLINT:
484 18
                case Type::INTEGER:
485 1
                case Type::BIGINT:
486 17
                    $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
487 17
                    break;
488
489 1
                case Type::DATETIME:
490 1
                    $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
491 1
                    break;
492
            }
493
        }
494
495 120
        $sql = 'UPDATE ' . $quotedTableName
496 120
             . ' SET ' . implode(', ', $set)
497 120
             . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
498
499 120
        $result = $this->conn->executeUpdate($sql, $params, $types);
500
501 120
        if ($versioned && ! $result) {
502 4
            throw OptimisticLockException::lockFailed($entity);
503
        }
504 117
    }
505
506
    /**
507
     * @todo Add check for platform if it supports foreign keys/cascading.
508
     *
509
     * @param array $identifier
510
     *
511
     * @return void
512
     */
513 65
    protected function deleteJoinTableRecords($identifier)
514
    {
515 65
        foreach ($this->class->associationMappings as $mapping) {
516 49
            if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY) {
517 48
                continue;
518
            }
519
520
            // @Todo this only covers scenarios with no inheritance or of the same level. Is there something
521
            // like self-referential relationship between different levels of an inheritance hierarchy? I hope not!
522 24
            $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
523 24
            $class           = $this->class;
524 24
            $association     = $mapping;
525 24
            $otherColumns    = [];
526 24
            $otherKeys       = [];
527 24
            $keys            = [];
528
529 24 View Code Duplication
            if ( ! $mapping['isOwningSide']) {
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...
530 6
                $class       = $this->em->getClassMetadata($mapping['targetEntity']);
531 6
                $association = $class->associationMappings[$mapping['mappedBy']];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
532
            }
533
534 24
            $joinColumns = $mapping['isOwningSide']
535 20
                ? $association['joinTable']['joinColumns']
536 24
                : $association['joinTable']['inverseJoinColumns'];
537
538
539 24
            if ($selfReferential) {
540 1
                $otherColumns = (! $mapping['isOwningSide'])
541
                    ? $association['joinTable']['joinColumns']
542 1
                    : $association['joinTable']['inverseJoinColumns'];
543
            }
544
545 24
            foreach ($joinColumns as $joinColumn) {
546 24
                $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
547
            }
548
549 24
            foreach ($otherColumns as $joinColumn) {
550 1
                $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
551
            }
552
553 24
            if (isset($mapping['isOnDeleteCascade'])) {
554 5
                continue;
555
            }
556
557 20
            $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform);
558
559 20
            $this->conn->delete($joinTableName, array_combine($keys, $identifier));
560
561 20
            if ($selfReferential) {
562 20
                $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier));
563
            }
564
        }
565 65
    }
566
567
    /**
568
     * {@inheritdoc}
569
     */
570 61
    public function delete($entity)
571
    {
572 61
        $self       = $this;
573 61
        $class      = $this->class;
574 61
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
575 61
        $tableName  = $this->quoteStrategy->getTableName($class, $this->platform);
576 61
        $idColumns  = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform);
577 61
        $id         = array_combine($idColumns, $identifier);
578 61
        $types      = array_map(function ($identifier) use ($class, $self) {
579 61
            if (isset($class->fieldMappings[$identifier])) {
580 59
                return $class->fieldMappings[$identifier]['type'];
581
            }
582
583 5
            $targetMapping = $self->em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']);
584
585 5
            if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) {
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
586 4
                return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
587
            }
588
589 1
            if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) {
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
590 1
                return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
591
            }
592
593
            throw ORMException::unrecognizedField($targetMapping->identifier[0]);
0 ignored issues
show
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
594 61
        }, $class->identifier);
595
596 61
        $this->deleteJoinTableRecords($identifier);
597
598 61
        return (bool) $this->conn->delete($tableName, $id, $types);
599
    }
600
601
    /**
602
     * Prepares the changeset of an entity for database insertion (UPDATE).
603
     *
604
     * The changeset is obtained from the currently running UnitOfWork.
605
     *
606
     * During this preparation the array that is passed as the second parameter is filled with
607
     * <columnName> => <value> pairs, grouped by table name.
608
     *
609
     * Example:
610
     * <code>
611
     * array(
612
     *    'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...),
613
     *    'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...),
614
     *    ...
615
     * )
616
     * </code>
617
     *
618
     * @param object $entity The entity for which to prepare the data.
619
     *
620
     * @return array The prepared data.
621
     */
622 1027
    protected function prepareUpdateData($entity)
623
    {
624 1027
        $versionField = null;
625 1027
        $result       = [];
626 1027
        $uow          = $this->em->getUnitOfWork();
627
628 1027
        if (($versioned = $this->class->isVersioned) != false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
629 213
            $versionField = $this->class->versionField;
630
        }
631
632 1027
        foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
633 996
            if (isset($versionField) && $versionField == $field) {
634
                continue;
635
            }
636
637 996
            if (isset($this->class->embeddedClasses[$field])) {
638 9
                continue;
639
            }
640
641 996
            $newVal = $change[1];
642
643 996
            if ( ! isset($this->class->associationMappings[$field])) {
644 958
                $fieldMapping = $this->class->fieldMappings[$field];
645 958
                $columnName   = $fieldMapping['columnName'];
646
647 958
                $this->columnTypes[$columnName] = $fieldMapping['type'];
648
649 958
                $result[$this->getOwningTable($field)][$columnName] = $newVal;
650
651 958
                continue;
652
            }
653
654 851
            $assoc = $this->class->associationMappings[$field];
655
656
            // Only owning side of x-1 associations can have a FK column.
657 851
            if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
658 8
                continue;
659
            }
660
661 851
            if ($newVal !== null) {
662 629
                $oid = spl_object_hash($newVal);
663
664 629
                if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
665
                    // The associated entity $newVal is not yet persisted, so we must
666
                    // set $newVal = null, in order to insert a null value and schedule an
667
                    // extra update on the UnitOfWork.
668 43
                    $uow->scheduleExtraUpdate($entity, [$field => [null, $newVal]]);
669
670 43
                    $newVal = null;
671
                }
672
            }
673
674 851
            $newValId = null;
675
676 851
            if ($newVal !== null) {
677 629
                $newValId = $uow->getEntityIdentifier($newVal);
678
            }
679
680 851
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
681 851
            $owningTable = $this->getOwningTable($field);
682
683 851
            foreach ($assoc['joinColumns'] as $joinColumn) {
684 851
                $sourceColumn = $joinColumn['name'];
685 851
                $targetColumn = $joinColumn['referencedColumnName'];
686 851
                $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
687
688 851
                $this->quotedColumns[$sourceColumn]  = $quotedColumn;
689 851
                $this->columnTypes[$sourceColumn]    = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
0 ignored issues
show
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
690 851
                $result[$owningTable][$sourceColumn] = $newValId
691 629
                    ? $newValId[$targetClass->getFieldForColumn($targetColumn)]
692 851
                    : null;
693
            }
694
        }
695
696 1027
        return $result;
697
    }
698
699
    /**
700
     * Prepares the data changeset of a managed entity for database insertion (initial INSERT).
701
     * The changeset of the entity is obtained from the currently running UnitOfWork.
702
     *
703
     * The default insert data preparation is the same as for updates.
704
     *
705
     * @param object $entity The entity for which to prepare the data.
706
     *
707
     * @return array The prepared data for the tables to update.
708
     *
709
     * @see prepareUpdateData
710
     */
711 1022
    protected function prepareInsertData($entity)
712
    {
713 1022
        return $this->prepareUpdateData($entity);
714
    }
715
716
    /**
717
     * {@inheritdoc}
718
     */
719 926
    public function getOwningTable($fieldName)
720
    {
721 926
        return $this->class->getTableName();
722
    }
723
724
    /**
725
     * {@inheritdoc}
726
     */
727 509
    public function load(array $criteria, $entity = null, $assoc = null, array $hints = [], $lockMode = null, $limit = null, array $orderBy = null)
728
    {
729 509
        $this->switchPersisterContext(null, $limit);
730
731 509
        $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy);
732 508
        list($params, $types) = $this->expandParameters($criteria);
733 508
        $stmt = $this->conn->executeQuery($sql, $params, $types);
734
735 508 View Code Duplication
        if ($entity !== null) {
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...
736 72
            $hints[Query::HINT_REFRESH]         = true;
737 72
            $hints[Query::HINT_REFRESH_ENTITY]  = $entity;
738
        }
739
740 508
        $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
741 508
        $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints);
742
743 508
        return $entities ? $entities[0] : null;
744
    }
745
746
    /**
747
     * {@inheritdoc}
748
     */
749 433
    public function loadById(array $identifier, $entity = null)
750
    {
751 433
        return $this->load($identifier, $entity);
752
    }
753
754
    /**
755
     * {@inheritdoc}
756
     */
757 96
    public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = [])
758
    {
759 96
        if (($foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) {
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->em->getUnitOfWork...assoc['targetEntity']); of type object|boolean adds the type boolean to the return on line 760 which is incompatible with the return type declared by the interface Doctrine\ORM\Persisters\...ter::loadOneToOneEntity of type object.
Loading history...
760
            return $foundEntity;
761
        }
762
763 96
        $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
764
765 96
        if ($assoc['isOwningSide']) {
766 32
            $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']);
767
768
            // Mark inverse side as fetched in the hints, otherwise the UoW would
769
            // try to load it in a separate query (remember: to-one inverse sides can not be lazy).
770 32
            $hints = [];
771
772 32
            if ($isInverseSingleValued) {
773
                $hints['fetched']["r"][$assoc['inversedBy']] = true;
774
            }
775
776
            /* cascade read-only status
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% 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...
777
            if ($this->em->getUnitOfWork()->isReadOnly($sourceEntity)) {
778
                $hints[Query::HINT_READ_ONLY] = true;
779
            }
780
            */
781
782 32
            $targetEntity = $this->load($identifier, null, $assoc, $hints);
783
784
            // Complete bidirectional association, if necessary
785 32
            if ($targetEntity !== null && $isInverseSingleValued) {
786
                $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
787
            }
788
789 32
            return $targetEntity;
790
        }
791
792 64
        $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']);
793 64
        $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']);
794
795
        // TRICKY: since the association is specular source and target are flipped
796 64
        foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
797 64
            if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) {
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
798
                throw MappingException::joinColumnMustPointToMappedField(
799
                    $sourceClass->name, $sourceKeyColumn
0 ignored issues
show
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
800
                );
801
            }
802
803
            // unset the old value and set the new sql aliased value here. By definition
804
            // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
805 64
            $identifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
806 64
                $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
807
808 64
            unset($identifier[$targetKeyColumn]);
809
        }
810
811 64
        $targetEntity = $this->load($identifier, null, $assoc);
812
813 64
        if ($targetEntity !== null) {
814 16
            $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
815
        }
816
817 64
        return $targetEntity;
818
    }
819
820
    /**
821
     * {@inheritdoc}
822
     */
823 17
    public function refresh(array $id, $entity, $lockMode = null)
824
    {
825 17
        $sql = $this->getSelectSQL($id, null, $lockMode);
826 17
        list($params, $types) = $this->expandParameters($id);
827 17
        $stmt = $this->conn->executeQuery($sql, $params, $types);
828
829 17
        $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT);
830 17
        $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [Query::HINT_REFRESH => true]);
831 17
    }
832
833
    /**
834
     * {@inheritDoc}
835
     */
836 46
    public function count($criteria = [])
837
    {
838 46
        $sql = $this->getCountSQL($criteria);
839
840 46
        list($params, $types) = ($criteria instanceof Criteria)
841 43
            ? $this->expandCriteriaParameters($criteria)
842 46
            : $this->expandParameters($criteria);
843
844 46
        return (int) $this->conn->executeQuery($sql, $params, $types)->fetchColumn();
845
    }
846
847
    /**
848
     * {@inheritdoc}
849
     */
850 9
    public function loadCriteria(Criteria $criteria)
851
    {
852 9
        $orderBy = $criteria->getOrderings();
853 9
        $limit   = $criteria->getMaxResults();
854 9
        $offset  = $criteria->getFirstResult();
855 9
        $query   = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
856
857 7
        list($params, $types) = $this->expandCriteriaParameters($criteria);
858
859 7
        $stmt       = $this->conn->executeQuery($query, $params, $types);
860 7
        $hydrator   = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
861
862 7
        return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true]
863
        );
864
    }
865
866
    /**
867
     * {@inheritdoc}
868
     */
869 56
    public function expandCriteriaParameters(Criteria $criteria)
870
    {
871 56
        $expression = $criteria->getWhereExpression();
872 56
        $sqlParams  = [];
873 56
        $sqlTypes   = [];
874
875 56
        if ($expression === null) {
876 2
            return [$sqlParams, $sqlTypes];
877
        }
878
879 55
        $valueVisitor = new SqlValueVisitor();
880
881 55
        $valueVisitor->dispatch($expression);
882
883 55
        list($params, $types) = $valueVisitor->getParamsAndTypes();
884
885 55
        foreach ($params as $param) {
886 51
            $sqlParams = array_merge($sqlParams, $this->getValues($param));
887
        }
888
889 55
        foreach ($types as $type) {
890 51
            list ($field, $value) = $type;
891 51
            $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class));
892
        }
893
894 55
        return [$sqlParams, $sqlTypes];
895
    }
896
897
    /**
898
     * {@inheritdoc}
899
     */
900 70
    public function loadAll(array $criteria = [], array $orderBy = null, $limit = null, $offset = null)
901
    {
902 70
        $this->switchPersisterContext($offset, $limit);
903
904 70
        $sql = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
905 66
        list($params, $types) = $this->expandParameters($criteria);
906 66
        $stmt = $this->conn->executeQuery($sql, $params, $types);
907
908 66
        $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
909
910 66
        return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, [UnitOfWork::HINT_DEFEREAGERLOAD => true]
911
        );
912
    }
913
914
    /**
915
     * {@inheritdoc}
916
     */
917 8 View Code Duplication
    public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
918
    {
919 8
        $this->switchPersisterContext($offset, $limit);
920
921 8
        $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
922
923 8
        return $this->loadArrayFromStatement($assoc, $stmt);
0 ignored issues
show
$stmt of type object<Doctrine\DBAL\Driver\Statement> is not a sub-type of object<Doctrine\DBAL\Statement>. It seems like you assume a concrete implementation of the interface Doctrine\DBAL\Driver\Statement to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
924
    }
925
926
    /**
927
     * Loads an array of entities from a given DBAL statement.
928
     *
929
     * @param array                    $assoc
930
     * @param \Doctrine\DBAL\Statement $stmt
931
     *
932
     * @return array
933
     */
934 13 View Code Duplication
    private function loadArrayFromStatement($assoc, $stmt)
935
    {
936 13
        $rsm    = $this->currentPersisterContext->rsm;
937 13
        $hints  = [UnitOfWork::HINT_DEFEREAGERLOAD => true];
938
939 13
        if (isset($assoc['indexBy'])) {
940 7
            $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed.
941 7
            $rsm->addIndexBy('r', $assoc['indexBy']);
942
        }
943
944 13
        return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints);
945
    }
946
947
    /**
948
     * Hydrates a collection from a given DBAL statement.
949
     *
950
     * @param array                    $assoc
951
     * @param \Doctrine\DBAL\Statement $stmt
952
     * @param PersistentCollection     $coll
953
     *
954
     * @return array
955
     */
956 144 View Code Duplication
    private function loadCollectionFromStatement($assoc, $stmt, $coll)
957
    {
958 144
        $rsm   = $this->currentPersisterContext->rsm;
959
        $hints = [
960 144
            UnitOfWork::HINT_DEFEREAGERLOAD => true,
961 144
            'collection' => $coll
962
        ];
963
964 144
        if (isset($assoc['indexBy'])) {
965 10
            $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed.
966 10
            $rsm->addIndexBy('r', $assoc['indexBy']);
967
        }
968
969 144
        return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints);
970
    }
971
972
    /**
973
     * {@inheritdoc}
974
     */
975 82
    public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
976
    {
977 82
        $stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
978
979 82
        return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
0 ignored issues
show
$stmt of type object<Doctrine\DBAL\Driver\Statement> is not a sub-type of object<Doctrine\DBAL\Statement>. It seems like you assume a concrete implementation of the interface Doctrine\DBAL\Driver\Statement to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
980
    }
981
982
    /**
983
     * @param array    $assoc
984
     * @param object   $sourceEntity
985
     * @param int|null $offset
986
     * @param int|null $limit
987
     *
988
     * @return \Doctrine\DBAL\Driver\Statement
989
     *
990
     * @throws \Doctrine\ORM\Mapping\MappingException
991
     */
992 89
    private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
993
    {
994 89
        $this->switchPersisterContext($offset, $limit);
995
996 89
        $sourceClass    = $this->em->getClassMetadata($assoc['sourceEntity']);
997 89
        $class          = $sourceClass;
998 89
        $association    = $assoc;
999 89
        $criteria       = [];
1000 89
        $parameters     = [];
1001
1002 89 View Code Duplication
        if ( ! $assoc['isOwningSide']) {
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...
1003 12
            $class       = $this->em->getClassMetadata($assoc['targetEntity']);
1004 12
            $association = $class->associationMappings[$assoc['mappedBy']];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1005
        }
1006
1007 89
        $joinColumns = $assoc['isOwningSide']
1008 82
            ? $association['joinTable']['joinColumns']
1009 89
            : $association['joinTable']['inverseJoinColumns'];
1010
1011 89
        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1012
1013 89
        foreach ($joinColumns as $joinColumn) {
1014 89
            $sourceKeyColumn    = $joinColumn['referencedColumnName'];
1015 89
            $quotedKeyColumn    = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1016
1017
            switch (true) {
1018 89
                case $sourceClass->containsForeignIdentifier:
0 ignored issues
show
Accessing containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1019 4
                    $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
1020 4
                    $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1021
1022 4 View Code Duplication
                    if (isset($sourceClass->associationMappings[$field])) {
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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...
1023 4
                        $value = $this->em->getUnitOfWork()->getEntityIdentifier($value);
1024 4
                        $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1025
                    }
1026
1027 4
                    break;
1028
1029 87
                case isset($sourceClass->fieldNames[$sourceKeyColumn]):
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1030 87
                    $field = $sourceClass->fieldNames[$sourceKeyColumn];
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1031 87
                    $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1032
1033 87
                    break;
1034
1035
                default:
1036
                    throw MappingException::joinColumnMustPointToMappedField(
1037
                        $sourceClass->name, $sourceKeyColumn
0 ignored issues
show
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1038
                    );
1039
            }
1040
1041 89
            $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value;
1042 89
            $parameters[] = [
1043 89
                'value' => $value,
1044 89
                'field' => $field,
1045 89
                'class' => $sourceClass,
1046
            ];
1047
        }
1048
1049 89
        $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);
1050 89
        list($params, $types) = $this->expandToManyParameters($parameters);
1051
1052 89
        return $this->conn->executeQuery($sql, $params, $types);
1053
    }
1054
1055
    /**
1056
     * {@inheritdoc}
1057
     */
1058 561
    public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null)
1059
    {
1060 561
        $this->switchPersisterContext($offset, $limit);
1061
1062 561
        $lockSql    = '';
1063 561
        $joinSql    = '';
1064 561
        $orderBySql = '';
1065
1066 561 View Code Duplication
        if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
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...
1067 90
            $joinSql = $this->getSelectManyToManyJoinSQL($assoc);
1068
        }
1069
1070 561
        if (isset($assoc['orderBy'])) {
1071 5
            $orderBy = $assoc['orderBy'];
1072
        }
1073
1074 561
        if ($orderBy) {
1075 11
            $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name));
1076
        }
1077
1078 559
        $conditionSql = ($criteria instanceof Criteria)
1079 9
            ? $this->getSelectConditionCriteriaSQL($criteria)
1080 557
            : $this->getSelectConditionSQL($criteria, $assoc);
1081
1082 View Code Duplication
        switch ($lockMode) {
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...
1083 554
            case LockMode::PESSIMISTIC_READ:
1084
                $lockSql = ' ' . $this->platform->getReadLockSQL();
1085
                break;
1086
1087 554
            case LockMode::PESSIMISTIC_WRITE:
1088
                $lockSql = ' ' . $this->platform->getWriteLockSQL();
1089
                break;
1090
        }
1091
1092 554
        $columnList = $this->getSelectColumnsSQL();
1093 554
        $tableAlias = $this->getSQLTableAlias($this->class->name);
1094 554
        $filterSql  = $this->generateFilterConditionSQL($this->class, $tableAlias);
1095 554
        $tableName  = $this->quoteStrategy->getTableName($this->class, $this->platform);
1096
1097 554
        if ('' !== $filterSql) {
1098 12
            $conditionSql = $conditionSql
1099 11
                ? $conditionSql . ' AND ' . $filterSql
1100 12
                : $filterSql;
1101
        }
1102
1103 554
        $select = 'SELECT ' . $columnList;
1104 554
        $from   = ' FROM ' . $tableName . ' '. $tableAlias;
1105 554
        $join   = $this->currentPersisterContext->selectJoinSql . $joinSql;
1106 554
        $where  = ($conditionSql ? ' WHERE ' . $conditionSql : '');
1107 554
        $lock   = $this->platform->appendLockHint($from, $lockMode);
1108
        $query  = $select
1109 554
            . $lock
1110 554
            . $join
1111 554
            . $where
1112 554
            . $orderBySql;
1113
1114 554
        return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql;
1115
    }
1116
1117
    /**
1118
     * {@inheritDoc}
1119
     */
1120 41
    public function getCountSQL($criteria = [])
1121
    {
1122 41
        $tableName  = $this->quoteStrategy->getTableName($this->class, $this->platform);
1123 41
        $tableAlias = $this->getSQLTableAlias($this->class->name);
1124
1125 41
        $conditionSql = ($criteria instanceof Criteria)
1126 38
            ? $this->getSelectConditionCriteriaSQL($criteria)
1127 41
            : $this->getSelectConditionSQL($criteria);
1128
1129 41
        $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias);
1130
1131 41
        if ('' !== $filterSql) {
1132 2
            $conditionSql = $conditionSql
1133 2
                ? $conditionSql . ' AND ' . $filterSql
1134 2
                : $filterSql;
1135
        }
1136
1137
        $sql = 'SELECT COUNT(*) '
1138 41
            . 'FROM ' . $tableName . ' ' . $tableAlias
1139 41
            . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql);
1140
1141 41
        return $sql;
1142
    }
1143
1144
    /**
1145
     * Gets the ORDER BY SQL snippet for ordered collections.
1146
     *
1147
     * @param array  $orderBy
1148
     * @param string $baseTableAlias
1149
     *
1150
     * @return string
1151
     *
1152
     * @throws \Doctrine\ORM\ORMException
1153
     */
1154 12
    protected final function getOrderBySQL(array $orderBy, $baseTableAlias)
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
1155
    {
1156 12
        $orderByList = [];
1157
1158 12
        foreach ($orderBy as $fieldName => $orientation) {
1159
1160 12
            $orientation = strtoupper(trim($orientation));
1161
1162 12
            if ($orientation != 'ASC' && $orientation != 'DESC') {
1163 1
                throw ORMException::invalidOrientation($this->class->name, $fieldName);
1164
            }
1165
1166 11
            if (isset($this->class->fieldMappings[$fieldName])) {
1167 9
                $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited'])
1168
                    ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited'])
1169 9
                    : $baseTableAlias;
1170
1171 9
                $columnName    = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform);
1172 9
                $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation;
1173
1174 9
                continue;
1175
            }
1176
1177 2
            if (isset($this->class->associationMappings[$fieldName])) {
1178
1179 2
                if ( ! $this->class->associationMappings[$fieldName]['isOwningSide']) {
1180 1
                    throw ORMException::invalidFindByInverseAssociation($this->class->name, $fieldName);
1181
                }
1182
1183 1
                $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited'])
1184
                    ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited'])
1185 1
                    : $baseTableAlias;
1186
1187 1
                foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) {
1188 1
                    $columnName    = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1189 1
                    $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation;
1190
                }
1191
1192 1
                continue;
1193
            }
1194
1195
            throw ORMException::unrecognizedField($fieldName);
1196
        }
1197
1198 10
        return ' ORDER BY ' . implode(', ', $orderByList);
1199
    }
1200
1201
    /**
1202
     * Gets the SQL fragment with the list of columns to select when querying for
1203
     * an entity in this persister.
1204
     *
1205
     * Subclasses should override this method to alter or change the select column
1206
     * list SQL fragment. Note that in the implementation of BasicEntityPersister
1207
     * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}.
1208
     * Subclasses may or may not do the same.
1209
     *
1210
     * @return string The SQL fragment.
1211
     */
1212 555
    protected function getSelectColumnsSQL()
1213
    {
1214 555
        if ($this->currentPersisterContext->selectColumnListSql !== null) {
1215 145
            return $this->currentPersisterContext->selectColumnListSql;
1216
        }
1217
1218 555
        $columnList = [];
1219 555
        $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); // r for root
1220
1221
        // Add regular columns to select list
1222 555
        foreach ($this->class->fieldNames as $field) {
1223 554
            $columnList[] = $this->getSelectColumnSQL($field, $this->class);
1224
        }
1225
1226 555
        $this->currentPersisterContext->selectJoinSql    = '';
1227 555
        $eagerAliasCounter      = 0;
1228
1229 555
        foreach ($this->class->associationMappings as $assocField => $assoc) {
1230 492
            $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class);
1231
1232 492
            if ($assocColumnSQL) {
1233 414
                $columnList[] = $assocColumnSQL;
1234
            }
1235
1236 492
            $isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide'];
1237 492
            $isAssocFromOneEager     = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER;
1238
1239 492
            if ( ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) {
1240 470
                continue;
1241
            }
1242
1243 190
            if ((($assoc['type'] & ClassMetadata::TO_MANY) > 0) && $this->currentPersisterContext->handlesLimits) {
1244 3
                continue;
1245
            }
1246
1247 187
            $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']);
1248
1249 187
            if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
0 ignored issues
show
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1250 5
                continue; // now this is why you shouldn't use inheritance
1251
            }
1252
1253 182
            $assocAlias = 'e' . ($eagerAliasCounter++);
1254 182
            $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
1255
1256 182
            foreach ($eagerEntity->fieldNames as $field) {
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1257 180
                $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias);
0 ignored issues
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1258
            }
1259
1260 182
            foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) {
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1261 180
                $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL(
1262 180
                    $eagerAssocField, $eagerAssoc, $eagerEntity, $assocAlias
0 ignored issues
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1263
                );
1264
1265 180
                if ($eagerAssocColumnSQL) {
1266 180
                    $columnList[] = $eagerAssocColumnSQL;
1267
                }
1268
            }
1269
1270 182
            $association    = $assoc;
1271 182
            $joinCondition  = [];
1272
1273 182
            if (isset($assoc['indexBy'])) {
1274 1
                $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc['indexBy']);
1275
            }
1276
1277 182 View Code Duplication
            if ( ! $assoc['isOwningSide']) {
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...
1278 176
                $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']);
1279 176
                $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
1280
            }
1281
1282 182
            $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias);
0 ignored issues
show
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1283 182
            $joinTableName  = $this->quoteStrategy->getTableName($eagerEntity, $this->platform);
0 ignored issues
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1284
1285 182
            if ($assoc['isOwningSide']) {
1286 12
                $tableAlias           = $this->getSQLTableAlias($association['targetEntity'], $assocAlias);
1287 12
                $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']);
1288
1289 12 View Code Duplication
                foreach ($association['joinColumns'] as $joinColumn) {
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...
1290 12
                    $sourceCol       = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1291 12
                    $targetCol       = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);
1292 12
                    $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'])
1293 12
                                        . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol;
1294
                }
1295
1296
                // Add filter SQL
1297 12
                if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) {
0 ignored issues
show
$eagerEntity of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1298 12
                    $joinCondition[] = $filterSql;
1299
                }
1300
1301
            } else {
1302
1303 176
                $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN';
1304
1305 176 View Code Duplication
                foreach ($association['joinColumns'] as $joinColumn) {
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...
1306 176
                    $sourceCol       = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1307 176
                    $targetCol       = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);
1308
1309 176
                    $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
1310 176
                        . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol;
1311
                }
1312
            }
1313
1314 182
            $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON ';
1315 182
            $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition);
1316
        }
1317
1318 555
        $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
1319
1320 555
        return $this->currentPersisterContext->selectColumnListSql;
1321
    }
1322
1323
    /**
1324
     * Gets the SQL join fragment used when selecting entities from an association.
1325
     *
1326
     * @param string        $field
1327
     * @param array         $assoc
1328
     * @param ClassMetadata $class
1329
     * @param string        $alias
1330
     *
1331
     * @return string
1332
     */
1333 492
    protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
0 ignored issues
show
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1334
    {
1335 492
        if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) ) {
1336 394
            return '';
1337
        }
1338
1339 431
        $columnList    = [];
1340 431
        $targetClass   = $this->em->getClassMetadata($assoc['targetEntity']);
1341 431
        $isIdentifier  = isset($assoc['id']) && $assoc['id'] === true;
1342 431
        $sqlTableAlias = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias));
1343
1344 431
        foreach ($assoc['joinColumns'] as $joinColumn) {
1345 431
            $quotedColumn     = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1346 431
            $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
1347 431
            $type             = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
0 ignored issues
show
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1348
1349 431
            $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn['name'], $isIdentifier, $type);
1350
1351 431
            $columnList[] = sprintf('%s.%s AS %s', $sqlTableAlias, $quotedColumn, $resultColumnName);
1352
        }
1353
1354 431
        return implode(', ', $columnList);
1355
    }
1356
1357
    /**
1358
     * Gets the SQL join fragment used when selecting entities from a
1359
     * many-to-many association.
1360
     *
1361
     * @param array $manyToMany
1362
     *
1363
     * @return string
1364
     */
1365 92
    protected function getSelectManyToManyJoinSQL(array $manyToMany)
1366
    {
1367 92
        $conditions         = [];
1368 92
        $association        = $manyToMany;
1369 92
        $sourceTableAlias   = $this->getSQLTableAlias($this->class->name);
1370
1371 92 View Code Duplication
        if ( ! $manyToMany['isOwningSide']) {
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...
1372 13
            $targetEntity   = $this->em->getClassMetadata($manyToMany['targetEntity']);
1373 13
            $association    = $targetEntity->associationMappings[$manyToMany['mappedBy']];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1374
        }
1375
1376 92
        $joinTableName  = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform);
1377 92
        $joinColumns    = ($manyToMany['isOwningSide'])
1378 84
            ? $association['joinTable']['inverseJoinColumns']
1379 92
            : $association['joinTable']['joinColumns'];
1380
1381 92
        foreach ($joinColumns as $joinColumn) {
1382 92
            $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1383 92
            $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform);
1384 92
            $conditions[]       = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn;
1385
        }
1386
1387 92
        return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions);
1388
    }
1389
1390
    /**
1391
     * {@inheritdoc}
1392
     */
1393 1023
    public function getInsertSQL()
1394
    {
1395 1023
        if ($this->insertSql !== null) {
1396 83
            return $this->insertSql;
1397
        }
1398
1399 1023
        $columns   = $this->getInsertColumnList();
1400 1023
        $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
1401
1402 1023
        if (empty($columns)) {
1403 112
            $identityColumn  = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);
1404 112
            $this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);
1405
1406 112
            return $this->insertSql;
1407
        }
1408
1409 998
        $values  = [];
1410 998
        $columns = array_unique($columns);
1411
1412 998
        foreach ($columns as $column) {
1413 998
            $placeholder = '?';
1414
1415 998
            if (isset($this->class->fieldNames[$column])
1416 998
                && isset($this->columnTypes[$this->class->fieldNames[$column]])
1417 998
                && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) {
1418 6
                $type        = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]);
1419 6
                $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
1420
            }
1421
1422 998
            $values[] = $placeholder;
1423
        }
1424
1425 998
        $columns = implode(', ', $columns);
1426 998
        $values  = implode(', ', $values);
1427
1428 998
        $this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values);
1429
1430 998
        return $this->insertSql;
1431
    }
1432
1433
    /**
1434
     * Gets the list of columns to put in the INSERT SQL statement.
1435
     *
1436
     * Subclasses should override this method to alter or change the list of
1437
     * columns placed in the INSERT statements used by the persister.
1438
     *
1439
     * @return array The list of columns.
1440
     */
1441 955
    protected function getInsertColumnList()
1442
    {
1443 955
        $columns = [];
1444
1445 955
        foreach ($this->class->reflFields as $name => $field) {
1446 955
            if ($this->class->isVersioned && $this->class->versionField == $name) {
1447 200
                continue;
1448
            }
1449
1450 955
            if (isset($this->class->embeddedClasses[$name])) {
1451 8
                continue;
1452
            }
1453
1454 955
            if (isset($this->class->associationMappings[$name])) {
1455 836
                $assoc = $this->class->associationMappings[$name];
1456
1457 836
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
1458 788 View Code Duplication
                    foreach ($assoc['joinColumns'] as $joinColumn) {
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...
1459 788
                        $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1460
                    }
1461
                }
1462
1463 836
                continue;
1464
            }
1465
1466 955
            if (! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] != $name) {
1467 885
                $columns[]                = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform);
1468 955
                $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type'];
1469
            }
1470
        }
1471
1472 955
        return $columns;
1473
    }
1474
1475
    /**
1476
     * Gets the SQL snippet of a qualified column name for the given field name.
1477
     *
1478
     * @param string        $field The field name.
1479
     * @param ClassMetadata $class The class that declares this field. The table this class is
1480
     *                             mapped to must own the column for the given field.
1481
     * @param string        $alias
1482
     *
1483
     * @return string
1484
     */
1485 518 View Code Duplication
    protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
1486
    {
1487 518
        $root         = $alias == 'r' ? '' : $alias ;
1488 518
        $tableAlias   = $this->getSQLTableAlias($class->name, $root);
1489 518
        $fieldMapping = $class->fieldMappings[$field];
1490 518
        $sql          = sprintf('%s.%s', $tableAlias, $this->quoteStrategy->getColumnName($field, $class, $this->platform));
1491 518
        $columnAlias  = $this->getSQLColumnAlias($fieldMapping['columnName']);
1492
1493 518
        $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field);
1494
1495 518
        if (isset($fieldMapping['requireSQLConversion'])) {
1496 3
            $type = Type::getType($fieldMapping['type']);
1497 3
            $sql  = $type->convertToPHPValueSQL($sql, $this->platform);
1498
        }
1499
1500 518
        return $sql . ' AS ' . $columnAlias;
1501
    }
1502
1503
    /**
1504
     * Gets the SQL table alias for the given class name.
1505
     *
1506
     * @param string $className
1507
     * @param string $assocName
1508
     *
1509
     * @return string The SQL table alias.
1510
     *
1511
     * @todo Reconsider. Binding table aliases to class names is not such a good idea.
1512
     */
1513 624
    protected function getSQLTableAlias($className, $assocName = '')
1514
    {
1515 624
        if ($assocName) {
1516 182
            $className .= '#' . $assocName;
1517
        }
1518
1519 624
        if (isset($this->currentPersisterContext->sqlTableAliases[$className])) {
1520 620
            return $this->currentPersisterContext->sqlTableAliases[$className];
1521
        }
1522
1523 624
        $tableAlias = 't' . $this->currentPersisterContext->sqlAliasCounter++;
1524
1525 624
        $this->currentPersisterContext->sqlTableAliases[$className] = $tableAlias;
1526
1527 624
        return $tableAlias;
1528
    }
1529
1530
    /**
1531
     * {@inheritdoc}
1532
     */
1533
    public function lock(array $criteria, $lockMode)
1534
    {
1535
        $lockSql      = '';
1536
        $conditionSql = $this->getSelectConditionSQL($criteria);
1537
1538 View Code Duplication
        switch ($lockMode) {
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...
1539
            case LockMode::PESSIMISTIC_READ:
1540
                $lockSql = $this->platform->getReadLockSQL();
1541
1542
                break;
1543
            case LockMode::PESSIMISTIC_WRITE:
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

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

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
1544
1545
                $lockSql = $this->platform->getWriteLockSQL();
1546
                break;
1547
        }
1548
1549
        $lock  = $this->getLockTablesSql($lockMode);
1550
        $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ';
1551
        $sql = 'SELECT 1 '
1552
             . $lock
1553
             . $where
1554
             . $lockSql;
1555
1556
        list($params, $types) = $this->expandParameters($criteria);
1557
1558
        $this->conn->executeQuery($sql, $params, $types);
1559
    }
1560
1561
    /**
1562
     * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister.
1563
     *
1564
     * @param integer $lockMode One of the Doctrine\DBAL\LockMode::* constants.
1565
     *
1566
     * @return string
1567
     */
1568 13
    protected function getLockTablesSql($lockMode)
1569
    {
1570 13
        return $this->platform->appendLockHint(
1571
            'FROM '
1572 13
            . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' '
1573 13
            . $this->getSQLTableAlias($this->class->name),
1574 13
            $lockMode
1575
        );
1576
    }
1577
1578
    /**
1579
     * Gets the Select Where Condition from a Criteria object.
1580
     *
1581
     * @param \Doctrine\Common\Collections\Criteria $criteria
1582
     *
1583
     * @return string
1584
     */
1585 58
    protected function getSelectConditionCriteriaSQL(Criteria $criteria)
1586
    {
1587 58
        $expression = $criteria->getWhereExpression();
1588
1589 58
        if ($expression === null) {
1590 2
            return '';
1591
        }
1592
1593 57
        $visitor = new SqlExpressionVisitor($this, $this->class);
1594
1595 57
        return $visitor->dispatch($expression);
1596
    }
1597
1598
    /**
1599
     * {@inheritdoc}
1600
     */
1601 603
    public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null)
1602
    {
1603 603
        $selectedColumns = [];
1604 603
        $columns         = $this->getSelectConditionStatementColumnSQL($field, $assoc);
1605
1606 599
        if (count($columns) > 1 && $comparison === Comparison::IN) {
1607
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
1608
             *  @todo try to support multi-column IN expressions.
1609
             *  Example: (col1, col2) IN (('val1A', 'val2A'), ('val1B', 'val2B'))
1610
             */
1611 1
            throw ORMException::cantUseInOperatorOnCompositeKeys();
1612
        }
1613
1614 598
        foreach ($columns as $column) {
1615 598
            $placeholder = '?';
1616
1617 598 View Code Duplication
            if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) {
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...
1618 1
                $type        = Type::getType($this->class->fieldMappings[$field]['type']);
1619 1
                $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->platform);
1620
            }
1621
1622 598
            if (null !== $comparison) {
1623
                // special case null value handling
1624 61 View Code Duplication
                if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && null ===$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...
1625 6
                    $selectedColumns[] = $column . ' IS NULL';
1626
1627 6
                    continue;
1628
                }
1629
1630 55 View Code Duplication
                if ($comparison === Comparison::NEQ && null === $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...
1631 3
                    $selectedColumns[] = $column . ' IS NOT NULL';
1632
1633 3
                    continue;
1634
                }
1635
1636 52
                $selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder);
1637
1638 52
                continue;
1639
            }
1640
1641 570
            if (is_array($value)) {
1642 14
                $in = sprintf('%s IN (%s)', $column, $placeholder);
1643
1644 14
                if (false !== array_search(null, $value, true)) {
1645 4
                    $selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column);
1646
1647 4
                    continue;
1648
                }
1649
1650 10
                $selectedColumns[] = $in;
1651
1652 10
                continue;
1653
            }
1654
1655 559
            if (null === $value) {
1656 9
                $selectedColumns[] = sprintf('%s IS NULL', $column);
1657
1658 9
                continue;
1659
            }
1660
1661 551
            $selectedColumns[] = sprintf('%s = %s', $column, $placeholder);
1662
        }
1663
1664 598
        return implode(' AND ', $selectedColumns);
1665
    }
1666
1667
    /**
1668
     * Builds the left-hand-side of a where condition statement.
1669
     *
1670
     * @param string     $field
1671
     * @param array|null $assoc
1672
     *
1673
     * @return string[]
1674
     *
1675
     * @throws \Doctrine\ORM\ORMException
1676
     */
1677 603
    private function getSelectConditionStatementColumnSQL($field, $assoc = null)
1678
    {
1679 603
        if (isset($this->class->fieldMappings[$field])) {
1680 508
            $className = (isset($this->class->fieldMappings[$field]['inherited']))
1681 54
                ? $this->class->fieldMappings[$field]['inherited']
1682 508
                : $this->class->name;
1683
1684 508
            return [$this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)];
1685
        }
1686
1687 294
        if (isset($this->class->associationMappings[$field])) {
1688 147
            $association = $this->class->associationMappings[$field];
1689
            // Many-To-Many requires join table check for joinColumn
1690 147
            $columns = [];
1691 147
            $class   = $this->class;
1692
1693 147
            if ($association['type'] === ClassMetadata::MANY_TO_MANY) {
1694 3
                if ( ! $association['isOwningSide']) {
1695 2
                    $association = $assoc;
1696
                }
1697
1698 3
                $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform);
1699 3
                $joinColumns   = $assoc['isOwningSide']
1700 2
                    ? $association['joinTable']['joinColumns']
1701 3
                    : $association['joinTable']['inverseJoinColumns'];
1702
1703
1704 3
                foreach ($joinColumns as $joinColumn) {
1705 3
                    $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
1706
                }
1707
1708
            } else {
1709 145
                if ( ! $association['isOwningSide']) {
1710 1
                    throw ORMException::invalidFindByInverseAssociation($this->class->name, $field);
1711
                }
1712
1713 144
                $className  = (isset($association['inherited']))
1714 11
                    ? $association['inherited']
1715 144
                    : $this->class->name;
1716
1717 144 View Code Duplication
                foreach ($association['joinColumns'] as $joinColumn) {
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...
1718 144
                    $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
1719
                }
1720
            }
1721 146
            return $columns;
1722
        }
1723
1724 163
        if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
1725
            // very careless developers could potentially open up this normally hidden api for userland attacks,
1726
            // therefore checking for spaces and function calls which are not allowed.
1727
1728
            // found a join column condition, not really a "field"
1729 160
            return [$field];
1730
        }
1731
1732 3
        throw ORMException::unrecognizedField($field);
1733
    }
1734
1735
    /**
1736
     * Gets the conditional SQL fragment used in the WHERE clause when selecting
1737
     * entities in this persister.
1738
     *
1739
     * Subclasses are supposed to override this method if they intend to change
1740
     * or alter the criteria by which entities are selected.
1741
     *
1742
     * @param array      $criteria
1743
     * @param array|null $assoc
1744
     *
1745
     * @return string
1746
     */
1747 597
    protected function getSelectConditionSQL(array $criteria, $assoc = null)
1748
    {
1749 597
        $conditions = [];
1750
1751 597
        foreach ($criteria as $field => $value) {
1752 572
            $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc);
1753
        }
1754
1755 594
        return implode(' AND ', $conditions);
1756
    }
1757
1758
    /**
1759
     * {@inheritdoc}
1760
     */
1761 5 View Code Duplication
    public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
1762
    {
1763 5
        $this->switchPersisterContext($offset, $limit);
1764
1765 5
        $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
1766
1767 5
        return $this->loadArrayFromStatement($assoc, $stmt);
1768
    }
1769
1770
    /**
1771
     * {@inheritdoc}
1772
     */
1773 76
    public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
1774
    {
1775 76
        $stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
1776
1777 76
        return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
1778
    }
1779
1780
    /**
1781
     * Builds criteria and execute SQL statement to fetch the one to many entities from.
1782
     *
1783
     * @param array    $assoc
1784
     * @param object   $sourceEntity
1785
     * @param int|null $offset
1786
     * @param int|null $limit
1787
     *
1788
     * @return \Doctrine\DBAL\Statement
1789
     */
1790 81
    private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
1791
    {
1792 81
        $this->switchPersisterContext($offset, $limit);
1793
1794 81
        $criteria    = [];
1795 81
        $parameters  = [];
1796 81
        $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']];
1797 81
        $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']);
1798 81
        $tableAlias  = $this->getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->class->name);
1799
1800 81
        foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
1801 81
            if ($sourceClass->containsForeignIdentifier) {
0 ignored issues
show
Accessing containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1802 3
                $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
1803 3
                $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1804
1805 3 View Code Duplication
                if (isset($sourceClass->associationMappings[$field])) {
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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...
1806 3
                    $value = $this->em->getUnitOfWork()->getEntityIdentifier($value);
1807 3
                    $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1808
                }
1809
1810 3
                $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
1811 3
                $parameters[]                                   = [
1812 3
                    'value' => $value,
1813 3
                    'field' => $field,
1814 3
                    'class' => $sourceClass,
1815
                ];
1816
1817 3
                continue;
1818
            }
1819
1820 79
            $field = $sourceClass->fieldNames[$sourceKeyColumn];
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1821 79
            $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
0 ignored issues
show
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1822
1823 79
            $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
1824 79
            $parameters[] = [
1825 79
                'value' => $value,
1826 79
                'field' => $field,
1827 79
                'class' => $sourceClass,
1828
            ];
1829
1830
        }
1831
1832 81
        $sql                  = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset);
1833 81
        list($params, $types) = $this->expandToManyParameters($parameters);
1834
1835 81
        return $this->conn->executeQuery($sql, $params, $types);
1836
    }
1837
1838
    /**
1839
     * {@inheritdoc}
1840
     */
1841 573
    public function expandParameters($criteria)
1842
    {
1843 573
        $params = [];
1844 573
        $types  = [];
1845
1846 573
        foreach ($criteria as $field => $value) {
1847 548
            if ($value === null) {
1848 3
                continue; // skip null values.
1849
            }
1850
1851 546
            $types  = array_merge($types, $this->getTypes($field, $value, $this->class));
1852 546
            $params = array_merge($params, $this->getValues($value));
1853
        }
1854
1855 573
        return [$params, $types];
1856
    }
1857
1858
    /**
1859
     * Expands the parameters from the given criteria and use the correct binding types if found,
1860
     * specialized for OneToMany or ManyToMany associations.
1861
     *
1862
     * @param mixed[][] $criteria an array of arrays containing following:
1863
     *                             - field to which each criterion will be bound
1864
     *                             - value to be bound
1865
     *                             - class to which the field belongs to
1866
     *
1867
     *
1868
     * @return array
1869
     */
1870 156
    private function expandToManyParameters($criteria)
1871
    {
1872 156
        $params = [];
1873 156
        $types  = [];
1874
1875 156
        foreach ($criteria as $criterion) {
1876 156
            if ($criterion['value'] === null) {
1877 6
                continue; // skip null values.
1878
            }
1879
1880 150
            $types  = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class']));
1881 150
            $params = array_merge($params, $this->getValues($criterion['value']));
1882
        }
1883
1884 156
        return [$params, $types];
1885
    }
1886
1887
    /**
1888
     * Infers field types to be used by parameter type casting.
1889
     *
1890
     * @param string        $field
1891
     * @param mixed         $value
1892
     * @param ClassMetadata $class
1893
     *
1894
     * @return array
1895
     *
1896
     * @throws \Doctrine\ORM\Query\QueryException
1897
     */
1898 705
    private function getTypes($field, $value, ClassMetadata $class)
1899
    {
1900 705
        $types = [];
1901
1902
        switch (true) {
1903 705
            case (isset($class->fieldMappings[$field])):
1904 646
                $types = array_merge($types, [$class->fieldMappings[$field]['type']]);
1905 646
                break;
1906
1907 146
            case (isset($class->associationMappings[$field])):
1908 145
                $assoc = $class->associationMappings[$field];
1909 145
                $class = $this->em->getClassMetadata($assoc['targetEntity']);
1910
1911 145 View Code Duplication
                if (! $assoc['isOwningSide']) {
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...
1912 2
                    $assoc = $class->associationMappings[$assoc['mappedBy']];
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1913 2
                    $class = $this->em->getClassMetadata($assoc['targetEntity']);
1914
                }
1915
1916 145
                $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY
1917 3
                    ? $assoc['relationToTargetKeyColumns']
1918 145
                    : $assoc['sourceToTargetKeyColumns'];
1919
1920 145
                foreach ($columns as $column){
1921 145
                    $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1922
                }
1923 145
                break;
1924
1925
            default:
1926 1
                $types[] = null;
1927 1
                break;
1928
        }
1929
1930 705
        if (is_array($value)) {
1931 16
            return array_map(function ($type) {
1932 16
                $type = Type::getType($type);
1933
1934 16
                return $type->getBindingType() + Connection::ARRAY_PARAM_OFFSET;
1935 16
            }, $types);
1936
        }
1937
1938 695
        return $types;
1939
    }
1940
1941
    /**
1942
     * Retrieves the parameters that identifies a value.
1943
     *
1944
     * @param mixed $value
1945
     *
1946
     * @return array
1947
     */
1948 580
    private function getValues($value)
1949
    {
1950 580
        if (is_array($value)) {
1951 16
            $newValue = [];
1952
1953 16
            foreach ($value as $itemValue) {
1954 16
                $newValue = array_merge($newValue, $this->getValues($itemValue));
1955
            }
1956
1957 16
            return [$newValue];
1958
        }
1959
1960 580
        if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
1961 44
            $class = $this->em->getClassMetadata(get_class($value));
1962 44
            if ($class->isIdentifierComposite) {
0 ignored issues
show
Accessing isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1963 3
                $newValue = [];
1964
1965 3
                foreach ($class->getIdentifierValues($value) as $innerValue) {
1966 3
                    $newValue = array_merge($newValue, $this->getValues($innerValue));
1967
                }
1968
1969 3
                return $newValue;
1970
            }
1971
        }
1972
1973 580
        return [$this->getIndividualValue($value)];
1974
    }
1975
1976
    /**
1977
     * Retrieves an individual parameter value.
1978
     *
1979
     * @param mixed $value
1980
     *
1981
     * @return mixed
1982
     */
1983 580
    private function getIndividualValue($value)
1984
    {
1985 580
        if ( ! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
1986 577
            return $value;
1987
        }
1988
1989 44
        return $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
1990
    }
1991
1992
    /**
1993
     * {@inheritdoc}
1994
     */
1995 14
    public function exists($entity, Criteria $extraConditions = null)
1996
    {
1997 14
        $criteria = $this->class->getIdentifierValues($entity);
1998
1999 14
        if ( ! $criteria) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $criteria of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2000 2
            return false;
2001
        }
2002
2003 13
        $alias = $this->getSQLTableAlias($this->class->name);
2004
2005
        $sql = 'SELECT 1 '
2006 13
             . $this->getLockTablesSql(null)
2007 13
             . ' WHERE ' . $this->getSelectConditionSQL($criteria);
2008
2009 13
        list($params, $types) = $this->expandParameters($criteria);
2010
2011 13
        if (null !== $extraConditions) {
2012 9
            $sql                                 .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions);
2013 9
            list($criteriaParams, $criteriaTypes) = $this->expandCriteriaParameters($extraConditions);
2014
2015 9
            $params = array_merge($params, $criteriaParams);
2016 9
            $types  = array_merge($types, $criteriaTypes);
2017
        }
2018
2019 13
        if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) {
2020 3
            $sql .= ' AND ' . $filterSql;
2021
        }
2022
2023 13
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
2024
    }
2025
2026
    /**
2027
     * Generates the appropriate join SQL for the given join column.
2028
     *
2029
     * @param array $joinColumns The join columns definition of an association.
2030
     *
2031
     * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
2032
     */
2033 12
    protected function getJoinSQLForJoinColumns($joinColumns)
2034
    {
2035
        // if one of the join columns is nullable, return left join
2036 12
        foreach ($joinColumns as $joinColumn) {
2037 12
             if ( ! isset($joinColumn['nullable']) || $joinColumn['nullable']) {
2038 12
                 return 'LEFT JOIN';
2039
             }
2040
        }
2041
2042 5
        return 'INNER JOIN';
2043
    }
2044
2045
    /**
2046
     * {@inheritdoc}
2047
     */
2048 591
    public function getSQLColumnAlias($columnName)
2049
    {
2050 591
        return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform);
2051
    }
2052
2053
    /**
2054
     * Generates the filter SQL for a given entity and table alias.
2055
     *
2056
     * @param ClassMetadata $targetEntity     Metadata of the target entity.
2057
     * @param string        $targetTableAlias The table alias of the joined/selected table.
2058
     *
2059
     * @return string The SQL query part to add to a query.
2060
     */
2061 615 View Code Duplication
    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
2062
    {
2063 615
        $filterClauses = [];
2064
2065 615
        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
2066 22
            if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
2067 22
                $filterClauses[] = '(' . $filterExpr . ')';
2068
            }
2069
        }
2070
2071 615
        $sql = implode(' AND ', $filterClauses);
2072
2073 615
        return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL"
2074
    }
2075
2076
    /**
2077
     * Switches persister context according to current query offset/limits
2078
     *
2079
     * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved
2080
     *
2081
     * @param null|int $offset
2082
     * @param null|int $limit
2083
     */
2084 597
    protected function switchPersisterContext($offset, $limit)
2085
    {
2086 597
        if (null === $offset && null === $limit) {
2087 584
            $this->currentPersisterContext = $this->noLimitsContext;
2088
2089 584
            return;
2090
        }
2091
2092 42
        $this->currentPersisterContext = $this->limitsHandlingContext;
2093 42
    }
2094
}
2095