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