Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
JoinGroupBase
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 5
380
0.00% covered (danger)
0.00%
0 / 1
 table
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
 leftJoin
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 join
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 straightJoin
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 addJoin
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
90
 getAutoAlias
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3namespace Wikimedia\Rdbms;
4
5use InvalidArgumentException;
6
7/**
8 * Shared code between SelectQueryBuilder and JoinGroup to represent tables and join conditions.
9 *
10 * @internal
11 */
12abstract class JoinGroupBase {
13    /** @var array */
14    protected $tables = [];
15
16    /** @var array */
17    protected $joinConds = [];
18
19    protected $lastAlias;
20
21    /**
22     * Add a single table or a single parenthesized group.
23     *
24     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table, a
25     *   JoinGroup containing multiple tables, or a SelectQueryBuilder representing a subquery.
26     * @param-taint $table exec_sql
27     * @param string|null $alias The table alias, or null for no alias
28     * @param-taint $alias exec_sql
29     * @return $this
30     */
31    public function table( $table, $alias = null ) {
32        if ( $table instanceof JoinGroup ) {
33            $alias ??= $table->getAlias();
34            $table = $table->getRawTables();
35        } elseif ( $table instanceof SelectQueryBuilder ) {
36            $alias ??= $this->getAutoAlias();
37            $table = new Subquery( $table->getSQL() );
38        } elseif ( $table instanceof Subquery ) {
39            if ( $alias === null ) {
40                throw new InvalidArgumentException( __METHOD__ .
41                    ': Subquery as table must provide an alias.' );
42            }
43        } elseif ( !is_string( $table ) ) {
44            throw new InvalidArgumentException( __METHOD__ .
45                ': $table must be either string, JoinGroup or SelectQueryBuilder' );
46        }
47        if ( $alias === null ) {
48            $this->tables[] = $table;
49            $this->lastAlias = $table;
50        } else {
51            $this->tables[$alias] = $table;
52            $this->lastAlias = $alias;
53        }
54        return $this;
55    }
56
57    /**
58     * Left join a table or group of tables. This should be called after table().
59     *
60     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table, a
61     *   JoinGroup containing multiple tables, or a SelectQueryBuilder representing a subquery.
62     * @param string|null $alias The alias name, or null to automatically
63     *   generate an alias which will be unique to this builder
64     * @param string|array $conds The conditions for the ON clause
65     * @return $this
66     */
67    public function leftJoin( $table, $alias = null, $conds = [] ) {
68        $this->addJoin( 'LEFT JOIN', $table, $alias, $conds );
69        return $this;
70    }
71
72    /**
73     * Inner join a table or group of tables. This should be called after table().
74     *
75     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table, a
76     *   JoinGroup containing multiple tables, or a SelectQueryBuilder representing a subquery.
77     * @param string|null $alias The alias name, or null to automatically
78     *   generate an alias which will be unique to this builder
79     * @param string|array $conds The conditions for the ON clause
80     * @return $this
81     */
82    public function join( $table, $alias = null, $conds = [] ) {
83        $this->addJoin( 'JOIN', $table, $alias, $conds );
84        return $this;
85    }
86
87    /**
88     * Straight join a table or group of tables. This should be called after table().
89     *
90     * @param string|JoinGroup|SelectQueryBuilder $table The unqualified name of a table, a
91     *   JoinGroup containing multiple tables, or a SelectQueryBuilder representing a subquery.
92     * @param string|null $alias The alias name, or null to automatically
93     *   generate an alias which will be unique to this builder
94     * @param string|array $conds The conditions for the ON clause
95     * @return $this
96     */
97    public function straightJoin( $table, $alias = null, $conds = [] ) {
98        $this->addJoin( 'STRAIGHT_JOIN', $table, $alias, $conds );
99        return $this;
100    }
101
102    /**
103     * Private helper for functions that add joins
104     * @param string $type
105     * @param string|JoinGroup|SelectQueryBuilder $table
106     * @param string|null $alias
107     * @param string|array $joinConds
108     */
109    private function addJoin( $type, $table, $alias, $joinConds ) {
110        if ( !$this->tables ) {
111            throw new \LogicException( __METHOD__ .
112                ': cannot add a join unless a regular table is added first' );
113        }
114        if ( $alias === null ) {
115            if ( is_string( $table ) ) {
116                $alias = $table;
117            } else {
118                $alias = $this->getAutoAlias();
119            }
120        }
121        if ( isset( $this->joinConds[$alias] ) ) {
122            throw new \LogicException( __METHOD__ .
123                ": a join with alias \"$alias\" has already been added" );
124        }
125        if ( $table instanceof JoinGroup ) {
126            $conflicts = array_intersect_key( $this->joinConds, $table->getRawJoinConds() );
127            if ( $conflicts ) {
128                $conflict = reset( $conflicts );
129                throw new \LogicException( __METHOD__ .
130                    ": a join with alias \"$conflict\" has already been added" );
131            }
132            $this->tables[$alias] = $table->getRawTables();
133            $this->joinConds += $table->getRawJoinConds();
134        } elseif ( $table instanceof SelectQueryBuilder ) {
135            $this->tables[$alias] = new Subquery( $table->getSQL() );
136        } elseif ( is_string( $table ) ) {
137            $this->tables[$alias] = $table;
138        } else {
139            throw new InvalidArgumentException( __METHOD__ .
140                ': $table must be either string, JoinGroup or SelectQueryBuilder' );
141        }
142        $this->joinConds[$alias] = [ $type, $joinConds ];
143        $this->lastAlias = $alias;
144    }
145
146    abstract protected function getAutoAlias();
147}