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