Completed
Pull Request — master (#5669)
by Jeremy
48:42 queued 40:04
created

ManyToManyPersister::getDeleteRowSQLParameters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
crap 1
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\Collection;
21
22
use Doctrine\Common\Collections\Criteria;
23
use Doctrine\DBAL\Query\QueryBuilder;
24
use Doctrine\ORM\Mapping\ClassMetadata;
25
use Doctrine\ORM\Persisters\Collection\Expr\MappingVisitor;
26
use Doctrine\ORM\PersistentCollection;
27
use Doctrine\ORM\Query;
28
use Doctrine\ORM\Query\Parameter;
29
use Doctrine\ORM\Query\QueryExpressionVisitor;
30
use Doctrine\ORM\Utility\PersisterHelper;
31
32
/**
33
 * Persister for many-to-many collections.
34
 *
35
 * @author  Roman Borschel <[email protected]>
36
 * @author  Guilherme Blanco <[email protected]>
37
 * @author  Alexander <[email protected]>
38
 * @since   2.0
39
 */
40
class ManyToManyPersister extends AbstractCollectionPersister
41
{
42
    /**
43
     * {@inheritdoc}
44
     */
45 17
    public function delete(PersistentCollection $collection)
46
    {
47 17
        $mapping = $collection->getMapping();
48
49 17
        if ( ! $mapping['isOwningSide']) {
50
            return; // ignore inverse side
51
        }
52
53 17
        $types = array();
54 17
        $class = $this->em->getClassMetadata($mapping['sourceEntity']);
55
56 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
57 17
            $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
58
        }
59
60 17
        $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types);
61 17
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66 339
    public function update(PersistentCollection $collection)
67
    {
68 339
        $mapping = $collection->getMapping();
69
70 339
        if ( ! $mapping['isOwningSide']) {
71 241
            return; // ignore inverse side
72
        }
73
74 338
        list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection);
75 338
        list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection);
76
77 338
        foreach ($collection->getDeleteDiff() as $element) {
78 12
            $this->conn->executeUpdate(
79
                $deleteSql,
80 12
                $this->getDeleteRowSQLParameters($collection, $element),
81
                $deleteTypes
82
            );
83
        }
84
85 338
        foreach ($collection->getInsertDiff() as $element) {
86 338
            $this->conn->executeUpdate(
87
                $insertSql,
88 338
                $this->getInsertRowSQLParameters($collection, $element),
89
                $insertTypes
90
            );
91
        }
92 338
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 3
    public function get(PersistentCollection $collection, $index)
98
    {
99 3
        $mapping = $collection->getMapping();
100
101 3
        if ( ! isset($mapping['indexBy'])) {
102
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
103
        }
104
105 3
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
106 3
        $mappedKey = $mapping['isOwningSide']
107 2
            ? $mapping['inversedBy']
108 3
            : $mapping['mappedBy'];
109
110 3
        return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 18
    public function count(PersistentCollection $collection)
117
    {
118 18
        $conditions     = array();
119 18
        $params         = array();
120 18
        $types          = array();
121 18
        $mapping        = $collection->getMapping();
122 18
        $id             = $this->uow->getEntityIdentifier($collection->getOwner());
123 18
        $sourceClass    = $this->em->getClassMetadata($mapping['sourceEntity']);
124 18
        $targetClass    = $this->em->getClassMetadata($mapping['targetEntity']);
125 18
        $association    = ( ! $mapping['isOwningSide'])
126 4
            ? $targetClass->associationMappings[$mapping['mappedBy']]
0 ignored issues
show
Bug introduced by
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...
127 18
            : $mapping;
128
129 18
        $joinTableName  = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
130 18
        $joinColumns    = ( ! $mapping['isOwningSide'])
131 4
            ? $association['joinTable']['inverseJoinColumns']
132 18
            : $association['joinTable']['joinColumns'];
133
134 18
        foreach ($joinColumns as $joinColumn) {
135 18
            $columnName     = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
136 18
            $referencedName = $joinColumn['referencedColumnName'];
137 18
            $conditions[]   = 't.' . $columnName . ' = ?';
138 18
            $params[]       = $id[$sourceClass->getFieldForColumn($referencedName)];
139 18
            $types[]        = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
140
        }
141
142 18
        list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
143
144 18
        if ($filterSql) {
145 3
            $conditions[] = $filterSql;
146
        }
147
148
        // If there is a provided criteria, make part of conditions
149
        // @todo Fix this. Current SQL returns something like:
150
        //
151
        /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) {
152
            // A join is needed on the target entity
153
            $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
154
            $targetJoinSql   = ' JOIN ' . $targetTableName . ' te'
155
                . ' ON' . implode(' AND ', $this->getOnConditionSQL($association));
156
157
            // And criteria conditions needs to be added
158
            $persister    = $this->uow->getEntityPersister($targetClass->name);
159
            $visitor      = new SqlExpressionVisitor($persister, $targetClass);
160
            $conditions[] = $visitor->dispatch($expression);
161
162
            $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL;
163
        }*/
164
165
        $sql = 'SELECT COUNT(*)'
166 18
            . ' FROM ' . $joinTableName . ' t'
167 18
            . $joinTargetEntitySQL
168 18
            . ' WHERE ' . implode(' AND ', $conditions);
169
170 18
        return $this->conn->fetchColumn($sql, $params, 0, $types);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->conn->fetchColumn...l, $params, 0, $types); of type string|boolean adds the type boolean to the return on line 170 which is incompatible with the return type declared by the interface Doctrine\ORM\Persisters\...lectionPersister::count of type integer.
Loading history...
171
    }
172
173
    /**
174
     * {@inheritDoc}
175
     */
176 8
    public function slice(PersistentCollection $collection, $offset, $length = null)
177
    {
178 8
        $mapping   = $collection->getMapping();
179 8
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
180
181 8
        return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);
182
    }
183
    /**
184
     * {@inheritdoc}
185
     */
186 7
    public function containsKey(PersistentCollection $collection, $key)
187
    {
188 7
        $mapping = $collection->getMapping();
189
190 7
        if ( ! isset($mapping['indexBy'])) {
191
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
192
        }
193
194 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
195
196 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
197
198 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
199
    }
200
201
    /**
202
     * {@inheritDoc}
203
     */
204 7
    public function contains(PersistentCollection $collection, $element)
205
    {
206 7
        if ( ! $this->isValidEntityState($element)) {
207 2
            return false;
208
        }
209
210 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true);
211
212 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
213
214 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
215
    }
216
217
    /**
218
     * {@inheritDoc}
219
     */
220 2
    public function removeElement(PersistentCollection $collection, $element)
221
    {
222 2
        if ( ! $this->isValidEntityState($element)) {
223 2
            return false;
224
        }
225
226 2
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false);
227
228 2
        $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
1 ignored issue
show
Security introduced by
'DELETE FROM ' . $quoted...' AND ', $whereClauses) is used as a query on line 230. If $quotedJoinTable can contain user-input, it is usually preferable to use a parameter placeholder like :paramName and pass the dynamic input as second argument array('param' => $quotedJoinTable).

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

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

    // Safe
    $con->executeQuery(
        "SELECT * FROM users WHERE email = :email",
        array('email' => $email)
    );
}
Loading history...
229
230 2
        return (bool) $this->conn->executeUpdate($sql, $params, $types);
231
    }
232
233
    /**
234
     * {@inheritDoc}
235
     */
236 17
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
237
    {
238 17
        $mapping       = $collection->getMapping();
239 17
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
240
241 17
        $queryBuilder = $this->createQueryBuilderForAssociation($collection);
242
243 17
        if ($whereExpr = $criteria->getWhereExpression()) {
0 ignored issues
show
Unused Code introduced by
$whereExpr is not used, you could remove the assignment.

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

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

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

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

Loading history...
244 12
            $mappingVisitor = new MappingVisitor(
245 12
                $this->quoteStrategy,
246
                $targetClass,
0 ignored issues
show
Compatibility introduced by
$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...
247 12
                $this->platform
248
            );
249
250 12
            $mappedExpr = $mappingVisitor->dispatch($criteria->getWhereExpression());
251
252 12
            $whereClauseExpressionVisitor = new QueryExpressionVisitor(['te']);
253 12
            $whereExpr = $whereClauseExpressionVisitor->dispatch($mappedExpr);
254 12
            $queryBuilder->where($whereExpr . '');
255
256
            /** @var Parameter $parameter */
257 12
            foreach($whereClauseExpressionVisitor->getParameters() as $parameter) {
258 11
                $queryBuilder->setParameter($parameter->getName(), $parameter->getValue(), $parameter->getType());
259
            }
260
        }
261
262 17
        $this->applyCriteriaOrdering($queryBuilder, $criteria, $targetClass);
0 ignored issues
show
Compatibility introduced by
$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...
263
264 17
        $this->applyCriteriaLimit($queryBuilder, $criteria);
265
266 17
        $rsm = new Query\ResultSetMappingBuilder($this->em);
267 17
        $rsm->addRootEntityFromClassMetadata($targetClass->name, 'te');
0 ignored issues
show
Bug introduced by
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...
268
269 17
        $stmt = $queryBuilder->execute();
270
271
        return $this
272 17
            ->em
273 17
            ->newHydrator(Query::HYDRATE_OBJECT)
274 17
            ->hydrateAll($stmt, $rsm);
0 ignored issues
show
Bug introduced by
It seems like $stmt defined by $queryBuilder->execute() on line 269 can also be of type integer; however, Doctrine\ORM\Internal\Hy...tHydrator::hydrateAll() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
275
    }
276
277
    /**
278
     * Generates the filter SQL for a given mapping.
279
     *
280
     * This method is not used for actually grabbing the related entities
281
     * but when the extra-lazy collection methods are called on a filtered
282
     * association. This is why besides the many to many table we also
283
     * have to join in the actual entities table leading to additional
284
     * JOIN.
285
     *
286
     * @param array $mapping Array containing mapping information.
287
     *
288
     * @return string[] ordered tuple:
289
     *                   - JOIN condition to add to the SQL
290
     *                   - WHERE condition to add to the SQL
291
     */
292 32
    public function getFilterSql($mapping)
293
    {
294 32
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
295 32
        $rootClass   = $this->em->getClassMetadata($targetClass->rootEntityName);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
296 32
        $filterSql   = $this->generateFilterConditionSQL($rootClass, 'te');
0 ignored issues
show
Compatibility introduced by
$rootClass 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...
297
298 32
        if ('' === $filterSql) {
299 32
            return array('', '');
300
        }
301
302
        // A join is needed if there is filtering on the target entity
303 6
        $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$rootClass 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...
304 6
        $joinSql   = ' JOIN ' . $tableName . ' te'
305 6
            . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));
306
307 6
        return array($joinSql, $filterSql);
308
    }
309
310
    /**
311
     * Generates the filter SQL for a given entity and table alias.
312
     *
313
     * @param ClassMetadata $targetEntity     Metadata of the target entity.
314
     * @param string        $targetTableAlias The table alias of the joined/selected table.
315
     *
316
     * @return string The SQL query part to add to a query.
317
     */
318 32
    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
319
    {
320 32
        $filterClauses = array();
321
322 32
        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
323 6
            if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
324 6
                $filterClauses[] = '(' . $filterExpr . ')';
325
            }
326
        }
327
328 32
        return $filterClauses
329 6
            ? '(' . implode(' AND ', $filterClauses) . ')'
330 32
            : '';
331
    }
332
333
    /**
334
     * Generate ON condition
335
     *
336
     * @param  array $mapping
337
     *
338
     * @return array
339
     */
340 23
    protected function getOnConditionSQL($mapping)
341
    {
342 23
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
343 23
        $association = ( ! $mapping['isOwningSide'])
344 3
            ? $targetClass->associationMappings[$mapping['mappedBy']]
0 ignored issues
show
Bug introduced by
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...
345 23
            : $mapping;
346
347 23
        $joinColumns = $mapping['isOwningSide']
348 20
            ? $association['joinTable']['inverseJoinColumns']
349 23
            : $association['joinTable']['joinColumns'];
350
351 23
        $conditions = array();
352
353 23
        foreach ($joinColumns as $joinColumn) {
354 23
            $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
355 23
            $refColumnName  = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
356
357 23
            $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName;
358
        }
359
360 23
        return $conditions;
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     *
366
     * @override
367
     */
368 17
    protected function getDeleteSQL(PersistentCollection $collection)
369
    {
370 17
        $columns    = array();
371 17
        $mapping    = $collection->getMapping();
372 17
        $class      = $this->em->getClassMetadata(get_class($collection->getOwner()));
373 17
        $joinTable  = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
374
375 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
376 17
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
377
        }
378
379 17
        return 'DELETE FROM ' . $joinTable
380 17
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
381
    }
382
383
    /**
384
     * {@inheritdoc}
385
     *
386
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql.
387
     * @override
388
     */
389 17
    protected function getDeleteSQLParameters(PersistentCollection $collection)
390
    {
391 17
        $mapping    = $collection->getMapping();
392 17
        $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
393
394
        // Optimization for single column identifier
395 17
        if (count($mapping['relationToSourceKeyColumns']) === 1) {
396 15
            return array(reset($identifier));
397
        }
398
399
        // Composite identifier
400 2
        $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
401 2
        $params      = array();
402
403 2
        foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
404 2
            $params[] = isset($sourceClass->fieldNames[$refColumnName])
0 ignored issues
show
Bug introduced by
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...
405 1
                ? $identifier[$sourceClass->fieldNames[$refColumnName]]
0 ignored issues
show
Bug introduced by
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...
406 2
                : $identifier[$sourceClass->getFieldForColumn($columnName)];
407
        }
408
409 2
        return $params;
410
    }
411
412
    /**
413
     * Gets the SQL statement used for deleting a row from the collection.
414
     *
415
     * @param \Doctrine\ORM\PersistentCollection $collection
416
     *
417
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
418
     *                             of types for bound parameters
419
     */
420 338
    protected function getDeleteRowSQL(PersistentCollection $collection)
421
    {
422 338
        $mapping     = $collection->getMapping();
423 338
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
424 338
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
425 338
        $columns     = array();
426 338
        $types       = array();
427
428 338
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
429 338
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
430 338
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
431
        }
432
433 338
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
434 338
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
435 338
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
436
        }
437
438
        return array(
439 338
            'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
0 ignored issues
show
Compatibility introduced by
$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...
440 338
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',
441 338
            $types,
442
        );
443
    }
444
445
    /**
446
     * Gets the SQL parameters for the corresponding SQL statement to delete the given
447
     * element from the given collection.
448
     *
449
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.
450
     *
451
     * @param \Doctrine\ORM\PersistentCollection $collection
452
     * @param mixed                              $element
453
     *
454
     * @return array
455
     */
456 12
    protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
457
    {
458 12
        return $this->collectJoinTableColumnParameters($collection, $element);
459
    }
460
461
    /**
462
     * Gets the SQL statement used for inserting a row in the collection.
463
     *
464
     * @param \Doctrine\ORM\PersistentCollection $collection
465
     *
466
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
467
     *                             of types for bound parameters
468
     */
469 338
    protected function getInsertRowSQL(PersistentCollection $collection)
470
    {
471 338
        $columns     = array();
472 338
        $types       = array();
473 338
        $mapping     = $collection->getMapping();
474 338
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
475 338
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
476
477 338
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
478 338
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
479 338
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
480
        }
481
482 338
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
483 338
            $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
484 338
            $types[]   = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
485
        }
486
487
        return array(
488 338
            'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform)
0 ignored issues
show
Compatibility introduced by
$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...
489 338
            . ' (' . implode(', ', $columns) . ')'
490 338
            . ' VALUES'
491 338
            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',
492 338
            $types,
493
        );
494
    }
495
496
    /**
497
     * Gets the SQL parameters for the corresponding SQL statement to insert the given
498
     * element of the given collection into the database.
499
     *
500
     * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.
501
     *
502
     * @param \Doctrine\ORM\PersistentCollection $collection
503
     * @param mixed                              $element
504
     *
505
     * @return array
506
     */
507 338
    protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
508
    {
509 338
        return $this->collectJoinTableColumnParameters($collection, $element);
510
    }
511
512
    /**
513
     * Collects the parameters for inserting/deleting on the join table in the order
514
     * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
515
     *
516
     * @param \Doctrine\ORM\PersistentCollection $collection
517
     * @param object                             $element
518
     *
519
     * @return array
520
     */
521 338
    private function collectJoinTableColumnParameters(PersistentCollection $collection, $element)
522
    {
523 338
        $params      = array();
524 338
        $mapping     = $collection->getMapping();
525 338
        $isComposite = count($mapping['joinTableColumns']) > 2;
526
527 338
        $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());
528 338
        $identifier2 = $this->uow->getEntityIdentifier($element);
529
530 338
        if ($isComposite) {
531 21
            $class1 = $this->em->getClassMetadata(get_class($collection->getOwner()));
532 21
            $class2 = $collection->getTypeClass();
533
        }
534
535 338
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
536 338
            $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
537
538 338
            if ( ! $isComposite) {
539 317
                $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
540
541 317
                continue;
542
            }
543
544 21
            if ($isRelationToSource) {
545 21
                $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
0 ignored issues
show
Bug introduced by
The variable $class1 does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
546
547 21
                continue;
548
            }
549
550 21
            $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
0 ignored issues
show
Bug introduced by
The variable $class2 does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
551
        }
552
553 338
        return $params;
554
    }
555
556
    /**
557
     * @param \Doctrine\ORM\PersistentCollection $collection
558
     * @param string                             $key
559
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
560
     *
561
     * @return array ordered vector:
562
     *                - quoted join table name
563
     *                - where clauses to be added for filtering
564
     *                - parameters to be bound for filtering
565
     *                - types of the parameters to be bound for filtering
566
     */
567 7
    private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
568
    {
569 7
        $filterMapping = $collection->getMapping();
570 7
        $mapping       = $filterMapping;
571 7
        $indexBy       = $mapping['indexBy'];
572 7
        $id            = $this->uow->getEntityIdentifier($collection->getOwner());
573 7
        $sourceClass   = $this->em->getClassMetadata($mapping['sourceEntity']);
574 7
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
575
576 7
        if (! $mapping['isOwningSide']) {
577 3
            $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
578 3
            $mapping                = $associationSourceClass->associationMappings[$mapping['mappedBy']];
0 ignored issues
show
Bug introduced by
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...
579 3
            $joinColumns            = $mapping['joinTable']['joinColumns'];
580 3
            $sourceRelationMode     = 'relationToTargetKeyColumns';
581 3
            $targetRelationMode     = 'relationToSourceKeyColumns';
582
        } else {
583 4
            $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
584 4
            $joinColumns            = $mapping['joinTable']['inverseJoinColumns'];
585 4
            $sourceRelationMode     = 'relationToSourceKeyColumns';
586 4
            $targetRelationMode     = 'relationToTargetKeyColumns';
587
        }
588
589 7
        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t';
0 ignored issues
show
Compatibility introduced by
$associationSourceClass 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...
590 7
        $whereClauses    = array();
591 7
        $params          = array();
592 7
        $types           = array();
593
594 7
        $joinNeeded = ! in_array($indexBy, $targetClass->identifier);
0 ignored issues
show
Bug introduced by
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...
595
596 7
        if ($joinNeeded) { // extra join needed if indexBy is not a @id
597 3
            $joinConditions = array();
598
599 3
            foreach ($joinColumns as $joinTableColumn) {
600 3
                $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
601
            }
602
603 3
            $tableName        = $this->quoteStrategy->getTableName($targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
604 3
            $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
605 3
            $columnName       = $targetClass->getColumnName($indexBy);
606
607 3
            $whereClauses[] = 'tr.' . $columnName . ' = ?';
608 3
            $params[]       = $key;
609 3
            $types[]        = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
610
        }
611
612 7
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
613 7
            if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
614 7
                $column         = $mapping[$sourceRelationMode][$joinTableColumn];
615 7
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
616 7
                $params[]       = $sourceClass->containsForeignIdentifier
0 ignored issues
show
Bug introduced by
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...
617
                    ? $id[$sourceClass->getFieldForColumn($column)]
618 7
                    : $id[$sourceClass->fieldNames[$column]];
0 ignored issues
show
Bug introduced by
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...
619 7
                $types[]        = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
620 7
            } elseif ( ! $joinNeeded) {
621 4
                $column = $mapping[$targetRelationMode][$joinTableColumn];
622
623 4
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
624 4
                $params[]       = $key;
625 7
                $types[]        = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
626
            }
627
        }
628
629 7
        if ($addFilters) {
630 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
631
632 7
            if ($filterSql) {
633
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
634
                $whereClauses[] = $filterSql;
635
            }
636
        }
637
638 7
        return array($quotedJoinTable, $whereClauses, $params, $types);
639
    }
640
641
    /**
642
     * @param \Doctrine\ORM\PersistentCollection $collection
643
     * @param object                             $element
644
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
645
     *
646
     * @return array ordered vector:
647
     *                - quoted join table name
648
     *                - where clauses to be added for filtering
649
     *                - parameters to be bound for filtering
650
     *                - types of the parameters to be bound for filtering
651
     */
652 9
    private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
653
    {
654 9
        $filterMapping  = $collection->getMapping();
655 9
        $mapping        = $filterMapping;
656
657 9
        if ( ! $mapping['isOwningSide']) {
658 4
            $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
659 4
            $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']);
660 4
            $sourceId = $this->uow->getEntityIdentifier($element);
661 4
            $targetId = $this->uow->getEntityIdentifier($collection->getOwner());
662
663 4
            $mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
0 ignored issues
show
Bug introduced by
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...
664
        } else {
665 5
            $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
666 5
            $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
667 5
            $sourceId = $this->uow->getEntityIdentifier($collection->getOwner());
668 5
            $targetId = $this->uow->getEntityIdentifier($element);
669
        }
670
671 9
        $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
672 9
        $whereClauses    = array();
673 9
        $params          = array();
674 9
        $types           = array();
675
676 9
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
677 9
            $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
678
679 9
            if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
680 9
                $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
681 9
                $params[]     = $targetId[$targetClass->getFieldForColumn($targetColumn)];
682 9
                $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$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...
683
684 9
                continue;
685
            }
686
687
            // relationToSourceKeyColumns
688 9
            $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
689 9
            $params[]     = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
690 9
            $types[]      = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em);
0 ignored issues
show
Compatibility introduced by
$sourceClass 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...
691
        }
692
693 9
        if ($addFilters) {
694 7
            $quotedJoinTable .= ' t';
695
696 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
697
698 7
            if ($filterSql) {
699 3
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
700 3
                $whereClauses[] = $filterSql;
701
            }
702
        }
703
704 9
        return array($quotedJoinTable, $whereClauses, $params, $types);
705
    }
706
707
    /**
708
     * @param QueryBuilder $queryBuilder
709
     * @param Criteria $criteria
710
     * @param ClassMetadata $targetClass
711
     */
712 17
    private function applyCriteriaOrdering(QueryBuilder $queryBuilder, Criteria $criteria, ClassMetadata $targetClass)
713
    {
714 17
        $orderings = $criteria->getOrderings();
715 17
        if ($orderings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $orderings of type string[] 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...
716 2
            foreach ($orderings as $name => $direction) {
717 2
                $field = $this->quoteStrategy->getColumnName(
718
                    $name,
719
                    $targetClass,
720 2
                    $this->platform
721
                );
722
723 2
                $queryBuilder->addOrderBy($field, $direction);
724
            }
725
        }
726 17
    }
727
728
    /**
729
     * @param QueryBuilder $queryBuilder
730
     * @param Criteria $criteria
731
     */
732 17
    private function applyCriteriaLimit(QueryBuilder $queryBuilder, Criteria $criteria)
733
    {
734 17
        $queryBuilder->setFirstResult($criteria->getFirstResult());
735 17
        $queryBuilder->setMaxResults($criteria->getMaxResults());
736 17
    }
737
738
    /**
739
     * @param PersistentCollection $collection
740
     * @return \Doctrine\DBAL\Query\QueryBuilder
741
     */
742 17
    private function createQueryBuilderForAssociation(PersistentCollection $collection)
743
    {
744 17
        $qb = $this->em->getConnection()->createQueryBuilder();
745
746 17
        $mapping       = $collection->getMapping();
747 17
        $owner         = $collection->getOwner();
748 17
        $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
749 17
        $id            = $this->uow->getEntityIdentifier($owner);
750 17
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
751 17
        $onConditions  = $this->getOnConditionSQL($mapping);
752
753
754 17
        if ( ! $mapping['isOwningSide']) {
755 1
            $associationSourceClass = $targetClass;
756 1
            $mapping = $targetClass->associationMappings[$mapping['mappedBy']];
0 ignored issues
show
Bug introduced by
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...
757 1
            $sourceRelationMode = 'relationToTargetKeyColumns';
758
        } else {
759 16
            $associationSourceClass = $ownerMetadata;
760 16
            $sourceRelationMode = 'relationToSourceKeyColumns';
761
        }
762
763 17
        foreach ($mapping[$sourceRelationMode] as $key => $value) {
764 17
            $paramName = sprintf(':t%s', $key);
765 17
            $qb->where(sprintf('t.%s = %s', $key, $paramName));
766
767 17
            $paramValue = $ownerMetadata->containsForeignIdentifier
0 ignored issues
show
Bug introduced by
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...
768
                ? $id[$ownerMetadata->getFieldForColumn($value)]
769 17
                : $id[$ownerMetadata->fieldNames[$value]];
0 ignored issues
show
Bug introduced by
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...
770
771 17
            $qb->setParameter($paramName, $paramValue, $ownerMetadata->getTypeOfField($key));
772
        }
773
774 17
        $tableName    = $this->quoteStrategy->getTableName($targetClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$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...
775 17
        $joinTable    = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform);
0 ignored issues
show
Compatibility introduced by
$associationSourceClass 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...
776
777 17
        $qb->select('te.*');
778 17
        $qb->from($tableName, 'te');
779 17
        $qb->join('te', $joinTable, 't', join(' AND ', $onConditions));
780
781 17
        return $qb;
782
    }
783
}
784