1<?php
2/**
3 * Elasticsearch PHP client
4 *
5 * @link      https://github.com/elastic/elasticsearch-php/
6 * @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7 * @license   http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
8 * @license   https://www.gnu.org/licenses/lgpl-2.1.html GNU Lesser General Public License, Version 2.1
9 *
10 * Licensed to Elasticsearch B.V under one or more agreements.
11 * Elasticsearch B.V licenses this file to you under the Apache 2.0 License or
12 * the GNU Lesser General Public License, Version 2.1, at your option.
13 * See the LICENSE file in the project root for more information.
14 */
15
16
17declare(strict_types = 1);
18
19namespace Elasticsearch\Endpoints;
20
21use Elasticsearch\Common\Exceptions\UnexpectedValueException;
22use Elasticsearch\Serializers\SerializerInterface;
23use Elasticsearch\Transport;
24use Exception;
25use GuzzleHttp\Ring\Future\FutureArrayInterface;
26
27abstract class AbstractEndpoint
28{
29    /**
30     * @var array
31     */
32    protected $params =[];
33
34    /**
35     * @var string
36     */
37    protected $index = null;
38
39    /**
40     * @var string
41     */
42    protected $type = null;
43
44    /**
45     * @var string|int
46     */
47    protected $id = null;
48
49    /**
50     * @var string
51     */
52    protected $method = null;
53
54    /**
55     * @var string|array
56     */
57    protected $body = null;
58
59    /**
60     * @var array
61     */
62    private $options = [];
63
64    /**
65     * @var SerializerInterface
66     */
67    protected $serializer;
68
69    /**
70     * @return string[]
71     */
72    abstract public function getParamWhitelist(): array;
73
74    /**
75     * @return string
76     */
77    abstract public function getURI(): string;
78
79    /**
80     * @return string
81     */
82    abstract public function getMethod(): string;
83
84
85    /**
86     * Set the parameters for this endpoint
87     *
88     * @param  string[] $params Array of parameters
89     * @return $this
90     */
91    public function setParams(array $params)
92    {
93        $this->extractOptions($params);
94        $this->checkUserParams($params);
95        $params = $this->convertCustom($params);
96        $this->params = $this->convertArraysToStrings($params);
97
98        return $this;
99    }
100
101    public function getParams(): array
102    {
103        return $this->params;
104    }
105
106    public function getOptions(): array
107    {
108        return $this->options;
109    }
110
111    public function getIndex(): ?string
112    {
113        return $this->index;
114    }
115
116    /**
117     * @return $this
118     */
119    public function setIndex($index)
120    {
121        if ($index === null) {
122            return $this;
123        }
124
125        if (is_array($index) === true) {
126            $index = array_map('trim', $index);
127            $index = implode(",", $index);
128        }
129
130        $this->index = urlencode($index);
131
132        return $this;
133    }
134
135    /**
136     * @deprecated
137     */
138    public function getType(): ?string
139    {
140        return $this->type;
141    }
142
143    /**
144     * @deprecated
145     * @return     $this
146     */
147    public function setType(?string $type)
148    {
149        if ($type === null) {
150            return $this;
151        }
152
153        if (is_array($type) === true) {
154            $type = array_map('trim', $type);
155            $type = implode(",", $type);
156        }
157
158        $this->type = urlencode($type);
159
160        return $this;
161    }
162
163    /**
164     * @param int|string $docID
165     *
166     * @return $this
167     */
168    public function setId($docID)
169    {
170        if ($docID === null) {
171            return $this;
172        }
173
174        if (is_int($docID)) {
175            $docID = (string) $docID;
176        }
177
178        $this->id = urlencode($docID);
179
180        return $this;
181    }
182
183    /**
184     * @return array|string
185     */
186    public function getBody()
187    {
188        return $this->body;
189    }
190
191    protected function getOptionalURI(string $endpoint): string
192    {
193        $uri = [];
194        $uri[] = $this->getOptionalIndex();
195        $uri[] = $this->getOptionalType();
196        $uri[] = $endpoint;
197        $uri =  array_filter($uri);
198
199        return '/' . implode('/', $uri);
200    }
201
202    private function getOptionalIndex(): string
203    {
204        if (isset($this->index) === true) {
205            return $this->index;
206        } else {
207            return '_all';
208        }
209    }
210
211    private function getOptionalType(): string
212    {
213        if (isset($this->type) === true) {
214            return $this->type;
215        } else {
216            return '';
217        }
218    }
219
220    /**
221     * @param array $params
222     *
223     * @throws UnexpectedValueException
224     */
225    private function checkUserParams(array $params)
226    {
227        if (empty($params)) {
228            return; //no params, just return.
229        }
230
231        $whitelist = array_merge(
232            $this->getParamWhitelist(),
233            [ 'pretty', 'human', 'error_trace', 'source', 'filter_path', 'opaqueId' ]
234        );
235
236        $invalid = array_diff(array_keys($params), $whitelist);
237        if (count($invalid) > 0) {
238            sort($invalid);
239            sort($whitelist);
240            throw new UnexpectedValueException(
241                sprintf(
242                    (count($invalid) > 1 ? '"%s" are not valid parameters.' : '"%s" is not a valid parameter.').' Allowed parameters are "%s"',
243                    implode('", "', $invalid),
244                    implode('", "', $whitelist)
245                )
246            );
247        }
248    }
249
250    /**
251     * @param array $params Note: this is passed by-reference!
252     */
253    private function extractOptions(&$params)
254    {
255        // Extract out client options, then start transforming
256        if (isset($params['client']) === true) {
257            // Check if the opaqueId is populated and add the header
258            if (isset($params['client']['opaqueId']) === true) {
259                if (isset($params['client']['headers']) === false) {
260                    $params['client']['headers'] = [];
261                }
262                $params['client']['headers']['x-opaque-id'] = [trim($params['client']['opaqueId'])];
263                unset($params['client']['opaqueId']);
264            }
265
266            $this->options['client'] = $params['client'];
267            unset($params['client']);
268        }
269        $ignore = isset($this->options['client']['ignore']) ? $this->options['client']['ignore'] : null;
270        if (isset($ignore) === true) {
271            if (is_string($ignore)) {
272                $this->options['client']['ignore'] = explode(",", $ignore);
273            } elseif (is_array($ignore)) {
274                $this->options['client']['ignore'] = $ignore;
275            } else {
276                $this->options['client']['ignore'] = [$ignore];
277            }
278        }
279    }
280
281    private function convertCustom(array $params): array
282    {
283        if (isset($params['custom']) === true) {
284            foreach ($params['custom'] as $k => $v) {
285                $params[$k] = $v;
286            }
287            unset($params['custom']);
288        }
289
290        return $params;
291    }
292
293    private function convertArraysToStrings(array $params): array
294    {
295        foreach ($params as $key => &$value) {
296            if (!($key === 'client' || $key == 'custom') && is_array($value) === true) {
297                if ($this->isNestedArray($value) !== true) {
298                    $value = implode(",", $value);
299                }
300            }
301        }
302
303        return $params;
304    }
305
306    private function isNestedArray(array $a): bool
307    {
308        foreach ($a as $v) {
309            if (is_array($v)) {
310                return true;
311            }
312        }
313
314        return false;
315    }
316}
317