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    const LANG_MOUSTACHE = 'moustache';
22    const LANG_EXPRESSION = 'expression';
23    const LANG_PAINLESS = 'painless';
24
25    /**
26     * @var string
27     */
28    private $_lang;
29
30    /**
31     * Factory to create a script object from data structure (reverse toArray).
32     *
33     * @param string|array|AbstractScript $data
34     *
35     * @throws InvalidException
36     *
37     * @return Script|ScriptId
38     */
39    public static function create($data)
40    {
41        if ($data instanceof self) {
42            return $data;
43        }
44
45        if (\is_array($data)) {
46            return self::_createFromArray($data);
47        }
48
49        if (\is_string($data)) {
50            $class = self::class === \get_called_class() ? Script::class : \get_called_class();
51
52            return new $class($data);
53        }
54
55        throw new InvalidException('Failed to create script. Invalid data passed.');
56    }
57
58    private static function _createFromArray(array $data)
59    {
60        $params = $data['script']['params'] ?? [];
61        $lang = $data['script']['lang'] ?? null;
62
63        if (!\is_array($params)) {
64            throw new InvalidException('Script params must be an array');
65        }
66
67        if (isset($data['script']['source'])) {
68            return new Script(
69                $data['script']['source'],
70                $params,
71                $lang
72            );
73        }
74
75        if (isset($data['script']['id'])) {
76            return new ScriptId(
77                $data['script']['id'],
78                $params,
79                $lang
80            );
81        }
82
83        throw new InvalidException('Failed to create script. Invalid data passed.');
84    }
85
86    /**
87     * @param array|null  $params
88     * @param string|null $lang       Script language, see constants
89     * @param string|null $documentId Document ID the script action should be performed on (only relevant in update context)
90     */
91    public function __construct(array $params = null, string $lang = null, string $documentId = null)
92    {
93        if ($params) {
94            $this->setParams($params);
95        }
96
97        if (null !== $lang) {
98            $this->setLang($lang);
99        }
100
101        if (null !== $documentId) {
102            $this->setId($documentId);
103        }
104    }
105
106    /**
107     * @param string $lang
108     *
109     * @return $this
110     */
111    public function setLang(string $lang): AbstractScript
112    {
113        $this->_lang = $lang;
114
115        return $this;
116    }
117
118    /**
119     * @return string|null
120     */
121    public function getLang()
122    {
123        return $this->_lang;
124    }
125
126    /**
127     * Returns an array with the script type as key and the script content as value.
128     *
129     * @return array
130     */
131    abstract protected function getScriptTypeArray(): array;
132
133    /**
134     * {@inheritdoc}
135     */
136    public function toArray(): array
137    {
138        $array = $this->getScriptTypeArray();
139
140        if (!empty($this->_params)) {
141            $array['params'] = $this->_convertArrayable($this->_params);
142        }
143
144        if (null !== $this->_lang) {
145            $array['lang'] = $this->_lang;
146        }
147
148        return ['script' => $array];
149    }
150}
151