1<?php
2
3namespace Elastica;
4
5use Elastica\Exception\InvalidException;
6
7/**
8 * Class to handle params.
9 *
10 * This function can be used to handle params for queries, filter
11 *
12 * @author Nicolas Ruflin <spam@ruflin.com>
13 */
14class Param implements ArrayableInterface, \Countable
15{
16    /**
17     * Params.
18     *
19     * @var array<string, mixed>|\stdClass
20     */
21    protected $_params = [];
22
23    /**
24     * Raw Params.
25     *
26     * @var array<string, mixed>
27     */
28    protected $_rawParams = [];
29
30    /**
31     * Converts the params to an array. A default implementation exist to create
32     * the an array out of the class name (last part of the class name)
33     * and the params.
34     *
35     * @return array<string, mixed> Filter array
36     */
37    public function toArray()
38    {
39        $data = [$this->_getBaseName() => $this->getParams()];
40
41        if ($this->_rawParams) {
42            $data = \array_merge($data, $this->_rawParams);
43        }
44
45        return $this->_convertArrayable($data);
46    }
47
48    /**
49     * Sets (overwrites) the value at the given key.
50     *
51     * @param string $key   Key to set
52     * @param mixed  $value Key Value
53     *
54     * @return $this
55     */
56    public function setParam($key, $value)
57    {
58        $this->_params[$key] = $value;
59
60        return $this;
61    }
62
63    /**
64     * Sets (overwrites) all params of this object.
65     *
66     * @param array $params Parameter list
67     *
68     * @return $this
69     */
70    public function setParams(array $params)
71    {
72        $this->_params = $params;
73
74        return $this;
75    }
76
77    /**
78     * Adds a param to the list.
79     *
80     * This function can be used to add an array of params
81     *
82     * @param string $key   Param key
83     * @param mixed  $value Value to set
84     *
85     * @return $this
86     */
87    public function addParam($key, $value, ?string $subKey = null)
88    {
89        if (null !== $subKey) {
90            $this->_params[$key][$subKey] = $value;
91        } else {
92            $this->_params[$key][] = $value;
93        }
94
95        return $this;
96    }
97
98    /**
99     * Returns a specific param.
100     *
101     * @param string $key Key to return
102     *
103     * @throws InvalidException If requested key is not set
104     *
105     * @return mixed Key value
106     */
107    public function getParam($key)
108    {
109        if (!$this->hasParam($key)) {
110            throw new InvalidException('Param '.$key.' does not exist');
111        }
112
113        return $this->_params[$key];
114    }
115
116    /**
117     * Test if a param is set.
118     *
119     * @param string $key Key to test
120     *
121     * @return bool True if the param is set, false otherwise
122     */
123    public function hasParam($key)
124    {
125        return isset($this->_params[$key]);
126    }
127
128    /**
129     * Returns the params array.
130     *
131     * @return array Params
132     */
133    public function getParams()
134    {
135        return $this->_params;
136    }
137
138    /**
139     * {@inheritdoc}
140     *
141     * @return int
142     */
143    #[\ReturnTypeWillChange]
144    public function count()
145    {
146        return \count($this->_params);
147    }
148
149    /**
150     * Cast objects to arrays.
151     *
152     * @return array
153     */
154    protected function _convertArrayable(array $array)
155    {
156        $arr = [];
157
158        foreach ($array as $key => $value) {
159            if ($value instanceof ArrayableInterface) {
160                $arr[$value instanceof NameableInterface ? $value->getName() : $key] = $value->toArray();
161            } elseif (\is_array($value)) {
162                $arr[$key] = $this->_convertArrayable($value);
163            } else {
164                $arr[$key] = $value;
165            }
166        }
167
168        return $arr;
169    }
170
171    /**
172     * Param's name
173     * Picks the last part of the class name and makes it snake_case
174     * You can override this method if you want to change the name.
175     *
176     * @return string name
177     */
178    protected function _getBaseName()
179    {
180        return Util::toSnakeCase((new \ReflectionClass($this))->getShortName());
181    }
182
183    /**
184     * Sets params not inside params array.
185     *
186     * @param string $key
187     * @param mixed  $value
188     *
189     * @return $this
190     */
191    protected function _setRawParam($key, $value)
192    {
193        $this->_rawParams[$key] = $value;
194
195        return $this;
196    }
197}
198