Completed
Pull Request — master (#5662)
by Jeremy
13:02 queued 04:26
created

ManyToManyPersister::getDeleteSQL()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2
Metric Value
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 9.4285
cc 2
eloc 9
nc 2
nop 1
crap 2
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\ORM\Mapping\ClassMetadata;
24
use Doctrine\ORM\Persisters\SqlExpressionVisitor;
25
use Doctrine\ORM\Persisters\SqlValueVisitor;
26
use Doctrine\ORM\PersistentCollection;
27
use Doctrine\ORM\Query;
28
use Doctrine\ORM\Utility\PersisterHelper;
29
30
/**
31
 * Persister for many-to-many collections.
32
 *
33
 * @author  Roman Borschel <[email protected]>
34
 * @author  Guilherme Blanco <[email protected]>
35
 * @author  Alexander <[email protected]>
36
 * @since   2.0
37
 */
38
class ManyToManyPersister extends AbstractCollectionPersister
39
{
40
    /**
41
     * {@inheritdoc}
42
     */
43 17
    public function delete(PersistentCollection $collection)
44
    {
45 17
        $mapping = $collection->getMapping();
46
47 17
        if ( ! $mapping['isOwningSide']) {
48
            return; // ignore inverse side
49
        }
50
51 17
        $types = array();
52 17
        $class = $this->em->getClassMetadata($mapping['sourceEntity']);
53
54 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
55 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...
56
        }
57
58 17
        $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection), $types);
59 17
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 329
    public function update(PersistentCollection $collection)
65
    {
66 329
        $mapping = $collection->getMapping();
67
68 329
        if ( ! $mapping['isOwningSide']) {
69 231
            return; // ignore inverse side
70
        }
71
72 328
        list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection);
73 328
        list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection);
74
75 328
        foreach ($collection->getDeleteDiff() as $element) {
76 12
            $this->conn->executeUpdate(
77
                $deleteSql,
78 12
                $this->getDeleteRowSQLParameters($collection, $element),
79
                $deleteTypes
80
            );
81
        }
82
83 328
        foreach ($collection->getInsertDiff() as $element) {
84 328
            $this->conn->executeUpdate(
85
                $insertSql,
86 328
                $this->getInsertRowSQLParameters($collection, $element),
87
                $insertTypes
88
            );
89
        }
90 328
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 3
    public function get(PersistentCollection $collection, $index)
96
    {
97 3
        $mapping = $collection->getMapping();
98
99 3
        if ( ! isset($mapping['indexBy'])) {
100
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
101
        }
102
103 3
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
104 3
        $mappedKey = $mapping['isOwningSide']
105 2
            ? $mapping['inversedBy']
106 3
            : $mapping['mappedBy'];
107
108 3
        return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 18
    public function count(PersistentCollection $collection)
115
    {
116 18
        $conditions     = array();
117 18
        $params         = array();
118 18
        $types          = array();
119 18
        $mapping        = $collection->getMapping();
120 18
        $id             = $this->uow->getEntityIdentifier($collection->getOwner());
121 18
        $sourceClass    = $this->em->getClassMetadata($mapping['sourceEntity']);
122 18
        $targetClass    = $this->em->getClassMetadata($mapping['targetEntity']);
123 18
        $association    = ( ! $mapping['isOwningSide'])
124 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...
125 18
            : $mapping;
126
127 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...
128 18
        $joinColumns    = ( ! $mapping['isOwningSide'])
129 4
            ? $association['joinTable']['inverseJoinColumns']
130 18
            : $association['joinTable']['joinColumns'];
131
132 18
        foreach ($joinColumns as $joinColumn) {
133 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...
134 18
            $referencedName = $joinColumn['referencedColumnName'];
135 18
            $conditions[]   = 't.' . $columnName . ' = ?';
136 18
            $params[]       = $id[$sourceClass->getFieldForColumn($referencedName)];
137 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...
138
        }
139
140 18
        list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
141
142 18
        if ($filterSql) {
143 3
            $conditions[] = $filterSql;
144
        }
145
146
        // If there is a provided criteria, make part of conditions
147
        // @todo Fix this. Current SQL returns something like:
148
        //
149
        /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) {
150
            // A join is needed on the target entity
151
            $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
152
            $targetJoinSql   = ' JOIN ' . $targetTableName . ' te'
153
                . ' ON' . implode(' AND ', $this->getOnConditionSQL($association));
154
155
            // And criteria conditions needs to be added
156
            $persister    = $this->uow->getEntityPersister($targetClass->name);
157
            $visitor      = new SqlExpressionVisitor($persister, $targetClass);
158
            $conditions[] = $visitor->dispatch($expression);
159
160
            $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL;
161
        }*/
162
163
        $sql = 'SELECT COUNT(*)'
164 18
            . ' FROM ' . $joinTableName . ' t'
165 18
            . $joinTargetEntitySQL
166 18
            . ' WHERE ' . implode(' AND ', $conditions);
167
168 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 168 which is incompatible with the return type declared by the interface Doctrine\ORM\Persisters\...lectionPersister::count of type integer.
Loading history...
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174 8
    public function slice(PersistentCollection $collection, $offset, $length = null)
175
    {
176 8
        $mapping   = $collection->getMapping();
177 8
        $persister = $this->uow->getEntityPersister($mapping['targetEntity']);
178
179 8
        return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length);
180
    }
181
    /**
182
     * {@inheritdoc}
183
     */
184 7
    public function containsKey(PersistentCollection $collection, $key)
185
    {
186 7
        $mapping = $collection->getMapping();
187
188 7
        if ( ! isset($mapping['indexBy'])) {
189
            throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
190
        }
191
192 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true);
193
194 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
195
196 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
197
    }
198
199
    /**
200
     * {@inheritDoc}
201
     */
202 7
    public function contains(PersistentCollection $collection, $element)
203
    {
204 7
        if ( ! $this->isValidEntityState($element)) {
205 2
            return false;
206
        }
207
208 7
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true);
209
210 7
        $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
211
212 7
        return (bool) $this->conn->fetchColumn($sql, $params, 0, $types);
213
    }
214
215
    /**
216
     * {@inheritDoc}
217
     */
218 2
    public function removeElement(PersistentCollection $collection, $element)
219
    {
220 2
        if ( ! $this->isValidEntityState($element)) {
221 2
            return false;
222
        }
223
224 2
        list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false);
225
226 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 228. 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...
227
228 2
        return (bool) $this->conn->executeUpdate($sql, $params, $types);
229
    }
230
231
    /**
232
     * {@inheritDoc}
233
     */
234 7
    public function loadCriteria(PersistentCollection $collection, Criteria $criteria)
235
    {
236 7
        $mapping       = $collection->getMapping();
237 7
        $owner         = $collection->getOwner();
238 7
        $ownerMetadata = $this->em->getClassMetadata(get_class($owner));
239 7
        $id            = $this->uow->getEntityIdentifier($owner);
240 7
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
241 7
        $onConditions  = $this->getOnConditionSQL($mapping);
242 7
        $whereClauses  = $params = array();
243
244 7
        if ( ! $mapping['isOwningSide']) {
245 1
            $associationSourceClass = $targetClass;
246 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...
247 1
            $sourceRelationMode = 'relationToTargetKeyColumns';
248
        } else {
249 6
            $associationSourceClass = $ownerMetadata;
250 6
            $sourceRelationMode = 'relationToSourceKeyColumns';
251
        }
252
253 7
        foreach ($mapping[$sourceRelationMode] as $key => $value) {
254 7
            $whereClauses[] = sprintf('t.%s = ?', $key);
255 7
            $params[] = $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...
256
                ? $id[$ownerMetadata->getFieldForColumn($value)]
257 7
                : $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...
258
        }
259
260 7
        $parameters = $this->expandCriteriaParameters($criteria);
261
262 7
        foreach ($parameters as $parameter) {
263 2
            list($name, $value) = $parameter;
264 2
            $field = $this->quoteStrategy->getColumnName($name, $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...
265 2
            $whereClauses[]     = sprintf('te.%s = ?', $field);
266 2
            $params[]           = $value;
267
        }
268
269 7
        $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...
270 7
        $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...
271
272 7
        $rsm = new Query\ResultSetMappingBuilder($this->em);
273 7
        $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...
274
275 7
        $sql = 'SELECT ' . $rsm->generateSelectClause()
276 7
            . ' FROM ' . $tableName . ' te'
277 7
            . ' JOIN ' . $joinTable  . ' t ON'
278 7
            . implode(' AND ', $onConditions)
279 7
            . ' WHERE ' . implode(' AND ', $whereClauses);
280
281 7
        $sql .= $this->getOrderingSql($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...
282
283 7
        $sql .= $this->getLimitSql($criteria);
284
285 7
        $stmt = $this->conn->executeQuery($sql, $params);
286
287
        return $this
288 7
            ->em
289 7
            ->newHydrator(Query::HYDRATE_OBJECT)
290 7
            ->hydrateAll($stmt, $rsm);
291
    }
292
293
    /**
294
     * Generates the filter SQL for a given mapping.
295
     *
296
     * This method is not used for actually grabbing the related entities
297
     * but when the extra-lazy collection methods are called on a filtered
298
     * association. This is why besides the many to many table we also
299
     * have to join in the actual entities table leading to additional
300
     * JOIN.
301
     *
302
     * @param array $mapping Array containing mapping information.
303
     *
304
     * @return string[] ordered tuple:
305
     *                   - JOIN condition to add to the SQL
306
     *                   - WHERE condition to add to the SQL
307
     */
308 32
    public function getFilterSql($mapping)
309
    {
310 32
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
311 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...
312 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...
313
314 32
        if ('' === $filterSql) {
315 32
            return array('', '');
316
        }
317
318
        // A join is needed if there is filtering on the target entity
319 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...
320 6
        $joinSql   = ' JOIN ' . $tableName . ' te'
321 6
            . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping));
322
323 6
        return array($joinSql, $filterSql);
324
    }
325
326
    /**
327
     * Generates the filter SQL for a given entity and table alias.
328
     *
329
     * @param ClassMetadata $targetEntity     Metadata of the target entity.
330
     * @param string        $targetTableAlias The table alias of the joined/selected table.
331
     *
332
     * @return string The SQL query part to add to a query.
333
     */
334 32
    protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
335
    {
336 32
        $filterClauses = array();
337
338 32
        foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
339 6
            if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
340 6
                $filterClauses[] = '(' . $filterExpr . ')';
341
            }
342
        }
343
344 32
        return $filterClauses
345 6
            ? '(' . implode(' AND ', $filterClauses) . ')'
346 32
            : '';
347
    }
348
349
    /**
350
     * Generate ON condition
351
     *
352
     * @param  array $mapping
353
     *
354
     * @return array
355
     */
356 13
    protected function getOnConditionSQL($mapping)
357
    {
358 13
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
359 13
        $association = ( ! $mapping['isOwningSide'])
360 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...
361 13
            : $mapping;
362
363 13
        $joinColumns = $mapping['isOwningSide']
364 10
            ? $association['joinTable']['inverseJoinColumns']
365 13
            : $association['joinTable']['joinColumns'];
366
367 13
        $conditions = array();
368
369 13
        foreach ($joinColumns as $joinColumn) {
370 13
            $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...
371 13
            $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...
372
373 13
            $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName;
374
        }
375
376 13
        return $conditions;
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     *
382
     * @override
383
     */
384 17
    protected function getDeleteSQL(PersistentCollection $collection)
385
    {
386 17
        $columns    = array();
387 17
        $mapping    = $collection->getMapping();
388 17
        $class      = $this->em->getClassMetadata(get_class($collection->getOwner()));
389 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...
390
391 17
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
392 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...
393
        }
394
395 17
        return 'DELETE FROM ' . $joinTable
396 17
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
397
    }
398
399
    /**
400
     * {@inheritdoc}
401
     *
402
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql.
403
     * @override
404
     */
405 17
    protected function getDeleteSQLParameters(PersistentCollection $collection)
406
    {
407 17
        $mapping    = $collection->getMapping();
408 17
        $identifier = $this->uow->getEntityIdentifier($collection->getOwner());
409
410
        // Optimization for single column identifier
411 17
        if (count($mapping['relationToSourceKeyColumns']) === 1) {
412 15
            return array(reset($identifier));
413
        }
414
415
        // Composite identifier
416 2
        $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
417 2
        $params      = array();
418
419 2
        foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
420 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...
421 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...
422 2
                : $identifier[$sourceClass->getFieldForColumn($columnName)];
423
        }
424
425 2
        return $params;
426
    }
427
428
    /**
429
     * Gets the SQL statement used for deleting a row from the collection.
430
     *
431
     * @param \Doctrine\ORM\PersistentCollection $collection
432
     *
433
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
434
     *                             of types for bound parameters
435
     */
436 328
    protected function getDeleteRowSQL(PersistentCollection $collection)
437
    {
438 328
        $mapping     = $collection->getMapping();
439 328
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
440 328
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
441 328
        $columns     = array();
442 328
        $types       = array();
443
444 328
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
445 328
            $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...
446 328
            $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...
447
        }
448
449 328
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
450 328
            $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...
451 328
            $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...
452
        }
453
454
        return array(
455 328
            '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...
456 328
            . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?',
457 328
            $types,
458
        );
459
    }
460
461
    /**
462
     * Gets the SQL parameters for the corresponding SQL statement to delete the given
463
     * element from the given collection.
464
     *
465
     * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql.
466
     *
467
     * @param \Doctrine\ORM\PersistentCollection $collection
468
     * @param mixed                              $element
469
     *
470
     * @return array
471
     */
472 12
    protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element)
473
    {
474 12
        return $this->collectJoinTableColumnParameters($collection, $element);
475
    }
476
477
    /**
478
     * Gets the SQL statement used for inserting a row in the collection.
479
     *
480
     * @param \Doctrine\ORM\PersistentCollection $collection
481
     *
482
     * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array
483
     *                             of types for bound parameters
484
     */
485 328
    protected function getInsertRowSQL(PersistentCollection $collection)
486
    {
487 328
        $columns     = array();
488 328
        $types       = array();
489 328
        $mapping     = $collection->getMapping();
490 328
        $class       = $this->em->getClassMetadata($mapping['sourceEntity']);
491 328
        $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
492
493 328
        foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
494 328
            $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...
495 328
            $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...
496
        }
497
498 328
        foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
499 328
            $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...
500 328
            $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...
501
        }
502
503
        return array(
504 328
            '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...
505 328
            . ' (' . implode(', ', $columns) . ')'
506 328
            . ' VALUES'
507 328
            . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')',
508 328
            $types,
509
        );
510
    }
511
512
    /**
513
     * Gets the SQL parameters for the corresponding SQL statement to insert the given
514
     * element of the given collection into the database.
515
     *
516
     * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql.
517
     *
518
     * @param \Doctrine\ORM\PersistentCollection $collection
519
     * @param mixed                              $element
520
     *
521
     * @return array
522
     */
523 328
    protected function getInsertRowSQLParameters(PersistentCollection $collection, $element)
524
    {
525 328
        return $this->collectJoinTableColumnParameters($collection, $element);
526
    }
527
528
    /**
529
     * Collects the parameters for inserting/deleting on the join table in the order
530
     * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
531
     *
532
     * @param \Doctrine\ORM\PersistentCollection $collection
533
     * @param object                             $element
534
     *
535
     * @return array
536
     */
537 328
    private function collectJoinTableColumnParameters(PersistentCollection $collection, $element)
538
    {
539 328
        $params      = array();
540 328
        $mapping     = $collection->getMapping();
541 328
        $isComposite = count($mapping['joinTableColumns']) > 2;
542
543 328
        $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner());
544 328
        $identifier2 = $this->uow->getEntityIdentifier($element);
545
546 328
        if ($isComposite) {
547 21
            $class1 = $this->em->getClassMetadata(get_class($collection->getOwner()));
548 21
            $class2 = $collection->getTypeClass();
549
        }
550
551 328
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
552 328
            $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
553
554 328
            if ( ! $isComposite) {
555 307
                $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
556
557 307
                continue;
558
            }
559
560 21
            if ($isRelationToSource) {
561 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...
562
563 21
                continue;
564
            }
565
566 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...
567
        }
568
569 328
        return $params;
570
    }
571
572
    /**
573
     * @param \Doctrine\ORM\PersistentCollection $collection
574
     * @param string                             $key
575
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
576
     *
577
     * @return array ordered vector:
578
     *                - quoted join table name
579
     *                - where clauses to be added for filtering
580
     *                - parameters to be bound for filtering
581
     *                - types of the parameters to be bound for filtering
582
     */
583 7
    private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters)
584
    {
585 7
        $filterMapping = $collection->getMapping();
586 7
        $mapping       = $filterMapping;
587 7
        $indexBy       = $mapping['indexBy'];
588 7
        $id            = $this->uow->getEntityIdentifier($collection->getOwner());
589 7
        $sourceClass   = $this->em->getClassMetadata($mapping['sourceEntity']);
590 7
        $targetClass   = $this->em->getClassMetadata($mapping['targetEntity']);
591
592 7
        if (! $mapping['isOwningSide']) {
593 3
            $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
594 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...
595 3
            $joinColumns            = $mapping['joinTable']['joinColumns'];
596 3
            $sourceRelationMode     = 'relationToTargetKeyColumns';
597 3
            $targetRelationMode     = 'relationToSourceKeyColumns';
598
        } else {
599 4
            $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
600 4
            $joinColumns            = $mapping['joinTable']['inverseJoinColumns'];
601 4
            $sourceRelationMode     = 'relationToSourceKeyColumns';
602 4
            $targetRelationMode     = 'relationToTargetKeyColumns';
603
        }
604
605 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...
606 7
        $whereClauses    = array();
607 7
        $params          = array();
608 7
        $types           = array();
609
610 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...
611
612 7
        if ($joinNeeded) { // extra join needed if indexBy is not a @id
613 3
            $joinConditions = array();
614
615 3
            foreach ($joinColumns as $joinTableColumn) {
616 3
                $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName'];
617
            }
618
619 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...
620 3
            $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions);
621 3
            $columnName       = $targetClass->getColumnName($indexBy);
622
623 3
            $whereClauses[] = 'tr.' . $columnName . ' = ?';
624 3
            $params[]       = $key;
625 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...
626
        }
627
628 7
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
629 7
            if (isset($mapping[$sourceRelationMode][$joinTableColumn])) {
630 7
                $column         = $mapping[$sourceRelationMode][$joinTableColumn];
631 7
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
632 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...
633
                    ? $id[$sourceClass->getFieldForColumn($column)]
634 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...
635 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...
636 7
            } elseif ( ! $joinNeeded) {
637 4
                $column = $mapping[$targetRelationMode][$joinTableColumn];
638
639 4
                $whereClauses[] = 't.' . $joinTableColumn . ' = ?';
640 4
                $params[]       = $key;
641 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...
642
            }
643
        }
644
645 7
        if ($addFilters) {
646 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
647
648 7
            if ($filterSql) {
649
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
650
                $whereClauses[] = $filterSql;
651
            }
652
        }
653
654 7
        return array($quotedJoinTable, $whereClauses, $params, $types);
655
    }
656
657
    /**
658
     * @param \Doctrine\ORM\PersistentCollection $collection
659
     * @param object                             $element
660
     * @param boolean                            $addFilters Whether the filter SQL should be included or not.
661
     *
662
     * @return array ordered vector:
663
     *                - quoted join table name
664
     *                - where clauses to be added for filtering
665
     *                - parameters to be bound for filtering
666
     *                - types of the parameters to be bound for filtering
667
     */
668 9
    private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters)
669
    {
670 9
        $filterMapping  = $collection->getMapping();
671 9
        $mapping        = $filterMapping;
672
673 9
        if ( ! $mapping['isOwningSide']) {
674 4
            $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
675 4
            $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']);
676 4
            $sourceId = $this->uow->getEntityIdentifier($element);
677 4
            $targetId = $this->uow->getEntityIdentifier($collection->getOwner());
678
679 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...
680
        } else {
681 5
            $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
682 5
            $targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
683 5
            $sourceId = $this->uow->getEntityIdentifier($collection->getOwner());
684 5
            $targetId = $this->uow->getEntityIdentifier($element);
685
        }
686
687 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...
688 9
        $whereClauses    = array();
689 9
        $params          = array();
690 9
        $types           = array();
691
692 9
        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
693 9
            $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
694
695 9
            if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
696 9
                $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn];
697 9
                $params[]     = $targetId[$targetClass->getFieldForColumn($targetColumn)];
698 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...
699
700 9
                continue;
701
            }
702
703
            // relationToSourceKeyColumns
704 9
            $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn];
705 9
            $params[]     = $sourceId[$sourceClass->getFieldForColumn($targetColumn)];
706 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...
707
        }
708
709 9
        if ($addFilters) {
710 7
            $quotedJoinTable .= ' t';
711
712 7
            list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
713
714 7
            if ($filterSql) {
715 3
                $quotedJoinTable .= ' ' . $joinTargetEntitySQL;
716 3
                $whereClauses[] = $filterSql;
717
            }
718
        }
719
720 9
        return array($quotedJoinTable, $whereClauses, $params, $types);
721
    }
722
723
    /**
724
     * Expands Criteria Parameters by walking the expressions and grabbing all
725
     * parameters and types from it.
726
     *
727
     * @param \Doctrine\Common\Collections\Criteria $criteria
728
     *
729
     * @return array
730
     */
731 7
    private function expandCriteriaParameters(Criteria $criteria)
732
    {
733 7
        $expression = $criteria->getWhereExpression();
734
735 7
        if ($expression === null) {
736 5
            return array();
737
        }
738
739 2
        $valueVisitor = new SqlValueVisitor();
740
741 2
        $valueVisitor->dispatch($expression);
742
743 2
        list($values, $types) = $valueVisitor->getParamsAndTypes();
0 ignored issues
show
Unused Code introduced by
The assignment to $values is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
744
745 2
        return $types;
746
    }
747
748
    /**
749
     * @param Criteria $criteria
750
     * @param ClassMetadata $targetClass
751
     * @return string
752
     */
753 7
    private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass)
754
    {
755 7
        $orderings = $criteria->getOrderings();
756 7
        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...
757 2
            $orderBy = [];
758 2
            foreach ($orderings as $name => $direction) {
759 2
                $field = $this->quoteStrategy->getColumnName(
760
                    $name,
761
                    $targetClass,
762 2
                    $this->platform
763
                );
764 2
                $orderBy[] = $field . ' ' . $direction;
765
            }
766
767 2
            return ' ORDER BY ' . implode(', ', $orderBy);
768
        }
769 5
        return '';
770
    }
771
772
    /**
773
     * @param Criteria $criteria
774
     * @return string
775
     * @throws \Doctrine\DBAL\DBALException
776
     */
777 7
    private function getLimitSql(Criteria $criteria)
778
    {
779 7
        $limit  = $criteria->getMaxResults();
780 7
        $offset = $criteria->getFirstResult();
781 7
        if ($limit !== null || $offset !== null) {
782 3
            return $this->platform->modifyLimitQuery('', $limit, $offset);
783
        }
784 4
        return '';
785
    }
786
}
787