1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Config\Definition\Builder;
13
14use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
15
16/**
17 * This class builds an if expression.
18 *
19 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
20 * @author Christophe Coevoet <stof@notk.org>
21 */
22class ExprBuilder
23{
24    protected $node;
25    public $ifPart;
26    public $thenPart;
27
28    public function __construct(NodeDefinition $node)
29    {
30        $this->node = $node;
31    }
32
33    /**
34     * Marks the expression as being always used.
35     *
36     * @return $this
37     */
38    public function always(\Closure $then = null)
39    {
40        $this->ifPart = function () { return true; };
41
42        if (null !== $then) {
43            $this->thenPart = $then;
44        }
45
46        return $this;
47    }
48
49    /**
50     * Sets a closure to use as tests.
51     *
52     * The default one tests if the value is true.
53     *
54     * @return $this
55     */
56    public function ifTrue(\Closure $closure = null)
57    {
58        if (null === $closure) {
59            $closure = function ($v) { return true === $v; };
60        }
61
62        $this->ifPart = $closure;
63
64        return $this;
65    }
66
67    /**
68     * Tests if the value is a string.
69     *
70     * @return $this
71     */
72    public function ifString()
73    {
74        $this->ifPart = function ($v) { return \is_string($v); };
75
76        return $this;
77    }
78
79    /**
80     * Tests if the value is null.
81     *
82     * @return $this
83     */
84    public function ifNull()
85    {
86        $this->ifPart = function ($v) { return null === $v; };
87
88        return $this;
89    }
90
91    /**
92     * Tests if the value is empty.
93     *
94     * @return ExprBuilder
95     */
96    public function ifEmpty()
97    {
98        $this->ifPart = function ($v) { return empty($v); };
99
100        return $this;
101    }
102
103    /**
104     * Tests if the value is an array.
105     *
106     * @return $this
107     */
108    public function ifArray()
109    {
110        $this->ifPart = function ($v) { return \is_array($v); };
111
112        return $this;
113    }
114
115    /**
116     * Tests if the value is in an array.
117     *
118     * @return $this
119     */
120    public function ifInArray(array $array)
121    {
122        $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); };
123
124        return $this;
125    }
126
127    /**
128     * Tests if the value is not in an array.
129     *
130     * @return $this
131     */
132    public function ifNotInArray(array $array)
133    {
134        $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); };
135
136        return $this;
137    }
138
139    /**
140     * Transforms variables of any type into an array.
141     *
142     * @return $this
143     */
144    public function castToArray()
145    {
146        $this->ifPart = function ($v) { return !\is_array($v); };
147        $this->thenPart = function ($v) { return [$v]; };
148
149        return $this;
150    }
151
152    /**
153     * Sets the closure to run if the test pass.
154     *
155     * @return $this
156     */
157    public function then(\Closure $closure)
158    {
159        $this->thenPart = $closure;
160
161        return $this;
162    }
163
164    /**
165     * Sets a closure returning an empty array.
166     *
167     * @return $this
168     */
169    public function thenEmptyArray()
170    {
171        $this->thenPart = function () { return []; };
172
173        return $this;
174    }
175
176    /**
177     * Sets a closure marking the value as invalid at processing time.
178     *
179     * if you want to add the value of the node in your message just use a %s placeholder.
180     *
181     * @return $this
182     *
183     * @throws \InvalidArgumentException
184     */
185    public function thenInvalid(string $message)
186    {
187        $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
188
189        return $this;
190    }
191
192    /**
193     * Sets a closure unsetting this key of the array at processing time.
194     *
195     * @return $this
196     *
197     * @throws UnsetKeyException
198     */
199    public function thenUnset()
200    {
201        $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); };
202
203        return $this;
204    }
205
206    /**
207     * Returns the related node.
208     *
209     * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition
210     *
211     * @throws \RuntimeException
212     */
213    public function end()
214    {
215        if (null === $this->ifPart) {
216            throw new \RuntimeException('You must specify an if part.');
217        }
218        if (null === $this->thenPart) {
219            throw new \RuntimeException('You must specify a then part.');
220        }
221
222        return $this->node;
223    }
224
225    /**
226     * Builds the expressions.
227     *
228     * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build
229     *
230     * @return array
231     */
232    public static function buildExpressions(array $expressions)
233    {
234        foreach ($expressions as $k => $expr) {
235            if ($expr instanceof self) {
236                $if = $expr->ifPart;
237                $then = $expr->thenPart;
238                $expressions[$k] = function ($v) use ($if, $then) {
239                    return $if($v) ? $then($v) : $v;
240                };
241            }
242        }
243
244        return $expressions;
245    }
246}
247