1<?php
2
3namespace Doctrine\DBAL\Query\Expression;
4
5use Doctrine\DBAL\Connection;
6use function func_get_arg;
7use function func_get_args;
8use function func_num_args;
9use function implode;
10use function sprintf;
11
12/**
13 * ExpressionBuilder class is responsible to dynamically create SQL query parts.
14 */
15class ExpressionBuilder
16{
17    public const EQ  = '=';
18    public const NEQ = '<>';
19    public const LT  = '<';
20    public const LTE = '<=';
21    public const GT  = '>';
22    public const GTE = '>=';
23
24    /**
25     * The DBAL Connection.
26     *
27     * @var Connection
28     */
29    private $connection;
30
31    /**
32     * Initializes a new <tt>ExpressionBuilder</tt>.
33     *
34     * @param Connection $connection The DBAL Connection.
35     */
36    public function __construct(Connection $connection)
37    {
38        $this->connection = $connection;
39    }
40
41    /**
42     * Creates a conjunction of the given boolean expressions.
43     *
44     * Example:
45     *
46     *     [php]
47     *     // (u.type = ?) AND (u.role = ?)
48     *     $expr->andX('u.type = ?', 'u.role = ?'));
49     *
50     * @param mixed $x Optional clause. Defaults = null, but requires
51     *                 at least one defined when converting to string.
52     *
53     * @return CompositeExpression
54     */
55    public function andX($x = null)
56    {
57        return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
58    }
59
60    /**
61     * Creates a disjunction of the given boolean expressions.
62     *
63     * Example:
64     *
65     *     [php]
66     *     // (u.type = ?) OR (u.role = ?)
67     *     $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?'));
68     *
69     * @param mixed $x Optional clause. Defaults = null, but requires
70     *                 at least one defined when converting to string.
71     *
72     * @return CompositeExpression
73     */
74    public function orX($x = null)
75    {
76        return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args());
77    }
78
79    /**
80     * Creates a comparison expression.
81     *
82     * @param mixed  $x        The left expression.
83     * @param string $operator One of the ExpressionBuilder::* constants.
84     * @param mixed  $y        The right expression.
85     *
86     * @return string
87     */
88    public function comparison($x, $operator, $y)
89    {
90        return $x . ' ' . $operator . ' ' . $y;
91    }
92
93    /**
94     * Creates an equality comparison expression with the given arguments.
95     *
96     * First argument is considered the left expression and the second is the right expression.
97     * When converted to string, it will generated a <left expr> = <right expr>. Example:
98     *
99     *     [php]
100     *     // u.id = ?
101     *     $expr->eq('u.id', '?');
102     *
103     * @param mixed $x The left expression.
104     * @param mixed $y The right expression.
105     *
106     * @return string
107     */
108    public function eq($x, $y)
109    {
110        return $this->comparison($x, self::EQ, $y);
111    }
112
113    /**
114     * Creates a non equality comparison expression with the given arguments.
115     * First argument is considered the left expression and the second is the right expression.
116     * When converted to string, it will generated a <left expr> <> <right expr>. Example:
117     *
118     *     [php]
119     *     // u.id <> 1
120     *     $q->where($q->expr()->neq('u.id', '1'));
121     *
122     * @param mixed $x The left expression.
123     * @param mixed $y The right expression.
124     *
125     * @return string
126     */
127    public function neq($x, $y)
128    {
129        return $this->comparison($x, self::NEQ, $y);
130    }
131
132    /**
133     * Creates a lower-than comparison expression with the given arguments.
134     * First argument is considered the left expression and the second is the right expression.
135     * When converted to string, it will generated a <left expr> < <right expr>. Example:
136     *
137     *     [php]
138     *     // u.id < ?
139     *     $q->where($q->expr()->lt('u.id', '?'));
140     *
141     * @param mixed $x The left expression.
142     * @param mixed $y The right expression.
143     *
144     * @return string
145     */
146    public function lt($x, $y)
147    {
148        return $this->comparison($x, self::LT, $y);
149    }
150
151    /**
152     * Creates a lower-than-equal comparison expression with the given arguments.
153     * First argument is considered the left expression and the second is the right expression.
154     * When converted to string, it will generated a <left expr> <= <right expr>. Example:
155     *
156     *     [php]
157     *     // u.id <= ?
158     *     $q->where($q->expr()->lte('u.id', '?'));
159     *
160     * @param mixed $x The left expression.
161     * @param mixed $y The right expression.
162     *
163     * @return string
164     */
165    public function lte($x, $y)
166    {
167        return $this->comparison($x, self::LTE, $y);
168    }
169
170    /**
171     * Creates a greater-than comparison expression with the given arguments.
172     * First argument is considered the left expression and the second is the right expression.
173     * When converted to string, it will generated a <left expr> > <right expr>. Example:
174     *
175     *     [php]
176     *     // u.id > ?
177     *     $q->where($q->expr()->gt('u.id', '?'));
178     *
179     * @param mixed $x The left expression.
180     * @param mixed $y The right expression.
181     *
182     * @return string
183     */
184    public function gt($x, $y)
185    {
186        return $this->comparison($x, self::GT, $y);
187    }
188
189    /**
190     * Creates a greater-than-equal comparison expression with the given arguments.
191     * First argument is considered the left expression and the second is the right expression.
192     * When converted to string, it will generated a <left expr> >= <right expr>. Example:
193     *
194     *     [php]
195     *     // u.id >= ?
196     *     $q->where($q->expr()->gte('u.id', '?'));
197     *
198     * @param mixed $x The left expression.
199     * @param mixed $y The right expression.
200     *
201     * @return string
202     */
203    public function gte($x, $y)
204    {
205        return $this->comparison($x, self::GTE, $y);
206    }
207
208    /**
209     * Creates an IS NULL expression with the given arguments.
210     *
211     * @param string $x The field in string format to be restricted by IS NULL.
212     *
213     * @return string
214     */
215    public function isNull($x)
216    {
217        return $x . ' IS NULL';
218    }
219
220    /**
221     * Creates an IS NOT NULL expression with the given arguments.
222     *
223     * @param string $x The field in string format to be restricted by IS NOT NULL.
224     *
225     * @return string
226     */
227    public function isNotNull($x)
228    {
229        return $x . ' IS NOT NULL';
230    }
231
232    /**
233     * Creates a LIKE() comparison expression with the given arguments.
234     *
235     * @param string $x Field in string format to be inspected by LIKE() comparison.
236     * @param mixed  $y Argument to be used in LIKE() comparison.
237     *
238     * @return string
239     */
240    public function like($x, $y/*, ?string $escapeChar = null */)
241    {
242        return $this->comparison($x, 'LIKE', $y) .
243            (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : '');
244    }
245
246    /**
247     * Creates a NOT LIKE() comparison expression with the given arguments.
248     *
249     * @param string $x Field in string format to be inspected by NOT LIKE() comparison.
250     * @param mixed  $y Argument to be used in NOT LIKE() comparison.
251     *
252     * @return string
253     */
254    public function notLike($x, $y/*, ?string $escapeChar = null */)
255    {
256        return $this->comparison($x, 'NOT LIKE', $y) .
257            (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : '');
258    }
259
260    /**
261     * Creates a IN () comparison expression with the given arguments.
262     *
263     * @param string          $x The field in string format to be inspected by IN() comparison.
264     * @param string|string[] $y The placeholder or the array of values to be used by IN() comparison.
265     *
266     * @return string
267     */
268    public function in($x, $y)
269    {
270        return $this->comparison($x, 'IN', '(' . implode(', ', (array) $y) . ')');
271    }
272
273    /**
274     * Creates a NOT IN () comparison expression with the given arguments.
275     *
276     * @param string          $x The field in string format to be inspected by NOT IN() comparison.
277     * @param string|string[] $y The placeholder or the array of values to be used by NOT IN() comparison.
278     *
279     * @return string
280     */
281    public function notIn($x, $y)
282    {
283        return $this->comparison($x, 'NOT IN', '(' . implode(', ', (array) $y) . ')');
284    }
285
286    /**
287     * Quotes a given input parameter.
288     *
289     * @param mixed    $input The parameter to be quoted.
290     * @param int|null $type  The type of the parameter.
291     *
292     * @return string
293     */
294    public function literal($input, $type = null)
295    {
296        return $this->connection->quote($input, $type);
297    }
298}
299