1<?php
2
3namespace Elastica\Script;
4
5use Elastica\AbstractUpdateAction;
6use Elastica\Exception\InvalidException;
7
8/**
9 * Base class for Script object.
10 *
11 * Wherever scripting is supported in the Elasticsearch API, scripts can be referenced as "inline", "id" or "file".
12 *
13 * @author Nicolas Assing <nicolas.assing@gmail.com>
14 * @author Tobias Schultze <http://tobion.de>
15 * @author Martin Janser <martin.janser@liip.ch>
16 *
17 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html
18 */
19abstract class AbstractScript extends AbstractUpdateAction
20{
21    public const LANG_MOUSTACHE = 'moustache';
22    public const LANG_EXPRESSION = 'expression';
23    public const LANG_PAINLESS = 'painless';
24
25    /**
26     * @var string|null
27     */
28    private $_lang;
29
30    /**
31     * @var bool|null
32     */
33    private $scriptedUpsert;
34
35    /**
36     * @param string|null $lang       Script language, see constants
37     * @param string|null $documentId Document ID the script action should be performed on (only relevant in update context)
38     */
39    public function __construct(?array $params = null, ?string $lang = null, ?string $documentId = null)
40    {
41        if ($params) {
42            $this->setParams($params);
43        }
44
45        if (null !== $lang) {
46            $this->setLang($lang);
47        }
48
49        if (null !== $documentId) {
50            $this->setId($documentId);
51        }
52    }
53
54    /**
55     * Factory to create a script object from data structure (reverse toArray).
56     *
57     * @param AbstractScript|array|string $data
58     *
59     * @throws InvalidException
60     *
61     * @return Script|ScriptId|self
62     */
63    public static function create($data)
64    {
65        if ($data instanceof self) {
66            return $data;
67        }
68
69        if (\is_array($data)) {
70            return self::_createFromArray($data);
71        }
72
73        if (\is_string($data)) {
74            $class = self::class === static::class ? Script::class : static::class;
75
76            return new $class($data);
77        }
78
79        throw new InvalidException('Failed to create script. Invalid data passed.');
80    }
81
82    public function setLang(string $lang): self
83    {
84        $this->_lang = $lang;
85
86        return $this;
87    }
88
89    public function getLang(): ?string
90    {
91        return $this->_lang;
92    }
93
94    /**
95     * {@inheritdoc}
96     */
97    public function toArray(): array
98    {
99        $array = $this->getScriptTypeArray();
100
101        if ($this->_params) {
102            $array['params'] = $this->_convertArrayable($this->_params);
103        }
104
105        if (null !== $this->_lang) {
106            $array['lang'] = $this->_lang;
107        }
108
109        return ['script' => $array];
110    }
111
112    /**
113     * Returns an array with the script type as key and the script content as value.
114     */
115    abstract protected function getScriptTypeArray(): array;
116
117    private static function _createFromArray(array $data)
118    {
119        $params = $data['script']['params'] ?? [];
120        $lang = $data['script']['lang'] ?? null;
121
122        if (!\is_array($params)) {
123            throw new InvalidException('Script params must be an array');
124        }
125
126        if (isset($data['script']['source'])) {
127            return new Script(
128                $data['script']['source'],
129                $params,
130                $lang
131            );
132        }
133
134        if (isset($data['script']['id'])) {
135            return new ScriptId(
136                $data['script']['id'],
137                $params,
138                $lang
139            );
140        }
141
142        throw new InvalidException('Failed to create script. Invalid data passed.');
143    }
144
145    public function setScriptedUpsert(bool $scriptedUpsert): void
146    {
147        $this->scriptedUpsert = $scriptedUpsert;
148    }
149
150    public function getScriptedUpsert(): ?bool
151    {
152        return $this->scriptedUpsert;
153    }
154}
155