Failed Conditions
Pull Request — 2.8.x (#8073)
by
unknown
61:56
created

RemoveUselessLeftJoinsWalker::removeUnusedJoins()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 24
rs 9.2222
cc 6
nc 4
nop 1
1
<?php
2
3
namespace Doctrine\ORM\Tools\Pagination;
4
5
use Doctrine\ORM\Query;
6
use Doctrine\ORM\Query\AST\SelectStatement;
7
use Doctrine\ORM\Query\TreeWalkerAdapter;
8
9
class RemoveUselessLeftJoinsWalker extends TreeWalkerAdapter
10
{
11
    public function walkSelectStatement(SelectStatement $AST)
12
    {
13
        $queryComponents = $this->_getQueryComponents();
0 ignored issues
show
Unused Code introduced by
The assignment to $queryComponents is dead and can be removed.
Loading history...
14
        $this->removeUnusedJoins($AST);
15
    }
16
17
    private function removeUnusedJoins(SelectStatement $AST)
18
    {
19
        $from = $AST->fromClause->identificationVariableDeclarations;
20
        $fromRoot = reset($from);
21
22
        if (!isset($AST->whereClause) || !isset($AST->whereClause->conditionalExpression) || !isset($AST->whereClause->conditionalExpression->simpleConditionalExpression)) {
23
            return;
24
        }
25
26
        $expr = $AST->whereClause->conditionalExpression->simpleConditionalExpression;
27
28
        if (!isset($expr->subselect)) {
29
            return;
30
        }
31
32
        $subSelect = $expr->subselect;
33
        $subSelectUsages = $this->findSubSelectUsages($subSelect);
34
35
        foreach ($subSelect->subselectFromClause->identificationVariableDeclarations as $declaration) {
36
            $declaration->joins = $this->filterJoins($declaration->joins, $this->findUnusedJoins($declaration->joins, $subSelectUsages));
37
        }
38
39
        $usages = $this->findSubSelectUsages($AST);
40
        $fromRoot->joins = $this->filterJoins($fromRoot->joins, $this->findUnusedJoins($fromRoot->joins, $usages));
41
    }
42
43
    private function recursiveAddUsages($usages, $parents)
44
    {
45
        foreach ($usages as $id) {
46
            if (array_key_exists($id, $parents) && !in_array($parents[$id], $usages)) {
47
                $usages = $this->recursiveAddUsages(array_merge($usages, [$parents[$id]]), $parents);
48
            }
49
        }
50
51
        return $usages;
52
    }
53
54
    private function findUnusedJoins($joins, $usages)
55
    {
56
        $parents = [];
57
        foreach ($joins as $join) {
58
            $parents[$join->joinAssociationDeclaration->aliasIdentificationVariable] = $join->joinAssociationDeclaration->joinAssociationPathExpression->identificationVariable;
59
        }
60
61
        $usages = $this->recursiveAddUsages($usages, $parents);
62
63
        $unused = [];
64
        foreach ($joins as $join) {
65
            if (Query\AST\Join::JOIN_TYPE_LEFT === $join->joinType && !in_array($join->joinAssociationDeclaration->aliasIdentificationVariable, $usages)) {
66
                $unused[] = $join;
67
            }
68
        }
69
70
        return array_unique($unused);
71
    }
72
73
    private function filterJoins($joins, $toRemove)
74
    {
75
        return array_filter($joins, function (Query\AST\Join $join) use ($toRemove) {
76
            return !in_array($join, $toRemove);
77
        });
78
    }
79
80
    private function extractIdentificationVariable($expression)
81
    {
82
        if ($expression->simpleArithmeticExpression instanceof Query\AST\Literal) {
83
            return null;
84
        }
85
86
        return $expression->simpleArithmeticExpression->identificationVariable;
87
    }
88
89
    private function extractIdentificationVariableFromExpression($expr): array
90
    {
91
        $usages = [];
92
93
        if ($expr instanceof Query\AST\ComparisonExpression) {
94
            $usages[] = $this->extractIdentificationVariable($expr->leftExpression);
95
            $usages[] = $this->extractIdentificationVariable($expr->rightExpression);
96
        } else {
97
            $usages[] = $this->extractIdentificationVariable($expr->expression);
98
        }
99
100
        return $usages;
101
    }
102
103
    private function findSubSelectUsages($select)
104
    {
105
        $usages = [];
106
107
        if (isset($select->whereClause)) {
108
            if ($select->whereClause->conditionalExpression instanceof Query\AST\ConditionalTerm) {
109
                foreach ($select->whereClause->conditionalExpression->conditionalFactors as $factor) {
110
                    $expr = $factor->simpleConditionalExpression;
111
                    $usages = array_merge($usages, $this->extractIdentificationVariableFromExpression($expr));
112
                }
113
            } elseif ($select->whereClause->conditionalExpression instanceof Query\AST\ConditionalPrimary) {
114
                $usages = array_merge($usages, $this->extractIdentificationVariableFromExpression($select->whereClause->conditionalExpression->simpleConditionalExpression));
115
            }
116
        }
117
118
        if (isset($select->orderByClause)) {
119
            foreach ($select->orderByClause->orderByItems as $item) {
120
                if ($item->expression instanceof Query\AST\OrderByItem || $item->expression instanceof Query\AST\PathExpression) {
121
                    $usages[] = $item->expression->identificationVariable;
0 ignored issues
show
Bug introduced by
The property identificationVariable does not seem to exist on Doctrine\ORM\Query\AST\OrderByItem.
Loading history...
122
                }
123
            }
124
        }
125
126
        $usages = array_unique($usages);
127
128
        return $usages;
129
    }
130
}
131