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
5/**
6 * Shared code between SelectQueryBuilder and JoinGroup to represent tables and join conditions.
7 *
8 * @internal
9 */
10abstract class JoinGroupBase {
11    /** @var array */
12    protected $tables = [];
13
14    /** @var array */
15    protected $joinConds = [];
16
17    protected $lastAlias;
18
19    /**
20     * Add a single table or a single parenthesized group.
21     *
22     * @param string|JoinGroup|SelectQueryBuilder $table The table to add. If
23     *   this is a string, it is the table name. If it is a JoinGroup created
24     *   by SelectQueryBuilder::newJoinGroup(), the group will be added. If it
25     *   is a SelectQueryBuilder, a table subquery will be added.
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 table name, or a
61     *   JoinGroup containing multiple tables, or a SelectQueryBuilder
62     *   representing a subquery.
63     * @param string|null $alias The alias name, or null to automatically
64     *   generate an alias which will be unique to this builder
65     * @param string|array $conds The conditions for the ON clause
66     * @return $this
67     */
68    public function leftJoin( $table, $alias = null, $conds = [] ) {
69        $this->addJoin( 'LEFT JOIN', $table, $alias, $conds );
70        return $this;
71    }
72
73    /**
74     * Inner join a table or group of tables. This should be called after table().
75     *
76     * @param string|JoinGroup|SelectQueryBuilder $table The table name, or a
77     *   JoinGroup containing multiple tables, or a SelectQueryBuilder
78     *   representing a subquery.
79     * @param string|null $alias The alias name, or null to automatically
80     *   generate an alias which will be unique to this builder
81     * @param string|array $conds The conditions for the ON clause
82     * @return $this
83     */
84    public function join( $table, $alias = null, $conds = [] ) {
85        $this->addJoin( 'JOIN', $table, $alias, $conds );
86        return $this;
87    }
88
89    /**
90     * Straight join a table or group of tables. This should be called after table().
91     *
92     * @param string|JoinGroup|SelectQueryBuilder $table The table name, or a
93     *   JoinGroup containing multiple tables, or a SelectQueryBuilder
94     *   representing a subquery.
95     * @param string|null $alias The alias name, or null to automatically
96     *   generate an alias which will be unique to this builder
97     * @param string|array $conds The conditions for the ON clause
98     * @return $this
99     */
100    public function straightJoin( $table, $alias = null, $conds = [] ) {
101        $this->addJoin( 'STRAIGHT_JOIN', $table, $alias, $conds );
102        return $this;
103    }
104
105    /**
106     * Private helper for functions that add joins
107     * @param string $type
108     * @param string|JoinGroup|SelectQueryBuilder $table
109     * @param string|null $alias
110     * @param string|array $joinConds
111     */
112    private function addJoin( $type, $table, $alias, $joinConds ) {
113        if ( !$this->tables ) {
114            throw new \LogicException( __METHOD__ .
115                ': cannot add a join unless a regular table is added first' );
116        }
117        if ( $alias === null ) {
118            if ( is_string( $table ) ) {
119                $alias = $table;
120            } else {
121                $alias = $this->getAutoAlias();
122            }
123        }
124        if ( isset( $this->joinConds[$alias] ) ) {
125            throw new \LogicException( __METHOD__ .
126                ": a join with alias \"$alias\" has already been added" );
127        }
128        if ( $table instanceof JoinGroup ) {
129            $conflicts = array_intersect_key( $this->joinConds, $table->getRawJoinConds() );
130            if ( $conflicts ) {
131                $conflict = reset( $conflicts );
132                throw new \LogicException( __METHOD__ .
133                    ": a join with alias \"$conflict\" has already been added" );
134            }
135            $this->tables[$alias] = $table->getRawTables();
136            $this->joinConds += $table->getRawJoinConds();
137        } elseif ( $table instanceof SelectQueryBuilder ) {
138            $this->tables[$alias] = new Subquery( $table->getSQL() );
139        } elseif ( is_string( $table ) ) {
140            $this->tables[$alias] = $table;
141        } else {
142            throw new \InvalidArgumentException( __METHOD__ .
143                ': $table must be either string, JoinGroup or SelectQueryBuilder' );
144        }
145        $this->joinConds[$alias] = [ $type, $joinConds ];
146        $this->lastAlias = $alias;
147    }
148
149    abstract protected function getAutoAlias();
150}