Completed
Push — master ( 81bc9f...cbc9cf )
by Dispositif
02:15
created

AbstractWikiTemplate::filterEmptyNotRequired()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 11
rs 10
1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application
4
 * 2019 : Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the LICENSE file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Domain\Models\Wiki;
11
12
use App\Domain\Utils\TemplateParser;
13
use App\Domain\Utils\WikiTextUtil;
14
use DomainException;
15
use Exception;
16
use Throwable;
17
18
/**
19
 * TODO detect userPreferences (inlineStyle, spaceStyle...)
20
 * Class AbstractWikiTemplate.
21
 */
22
abstract class AbstractWikiTemplate extends AbstractParametersObject
23
{
24
    const MODEL_NAME = '';
25
26
    const PARAM_ALIAS = [];
27
28
    /**
29
     * todo : modify to [a,b,c] ?
30
     */
31
    const REQUIRED_PARAMETERS = [];
32
33
    public $log = [];
34
35
    public $parametersErrorFromHydrate;
36
37
    public $userSeparator; // todo move to WikiRef
38
39
    /**
40
     * optional
41
     * Not a constant so it can be modified in constructor.
42
     *
43
     * @var array
44
     */
45
    protected $parametersByOrder = [];
46
47
    protected $paramOrderByUser = [];
48
49
    /**
50
     * AbstractWikiTemplate constructor.
51
     *
52
     * @throws Exception
53
     */
54
    public function __construct()
55
    {
56
        if (empty(static::REQUIRED_PARAMETERS)) {
1 ignored issue
show
introduced by
The condition empty(static::REQUIRED_PARAMETERS) is always true.
Loading history...
57
            throw new Exception(sprintf('REQUIRED_PARAMETERS not configured in "%s"', get_called_class()));
58
        }
59
        $this->parametersValues = static::REQUIRED_PARAMETERS;
60
61
        if (empty($this->parametersByOrder)) {
62
            $this->parametersByOrder = static::REQUIRED_PARAMETERS;
63
        }
64
    }
65
66
    public function getParamsAndAlias(): array
67
    {
68
        return array_merge($this->parametersByOrder, array_keys($this::PARAM_ALIAS));
69
    }
70
71
    /**
72
     * @param string $name
73
     *
74
     * @return string|null
75
     * @throws Exception
76
     */
77
    public function getParam(string $name): ?string
78
    {
79
        try {
80
            $this->checkParamName($name);
81
        } catch (Exception $e) {
82
            return null;
83
        }
84
        $name = $this->getAliasParam($name);
85
86
        return ($this->parametersValues[$name]) ?? null;
87
    }
88
89
    /**
90
     * TODO return bool + log() ?
91
     * todo check keyNum <= count($parametersByOrder).
92
     *
93
     * @param $name string|int
94
     *
95
     * @throws Exception
96
     */
97
    protected function checkParamName($name): void
98
    {
99
        // todo verify/useless ?
100
        if (is_int($name)) {
101
            $name = (string)$name;
102
        }
103
104
        // that parameter exists in template ?
105
        if (in_array($name, $this->parametersByOrder)
106
            || array_key_exists($name, static::PARAM_ALIAS)
107
        ) {
108
            return;
109
        }
110
111
        // keyNum parameter ?
112
        //        if (!in_array($name, ['1', '2', '3', '4'])) {
113
        throw new Exception(sprintf('no parameter "%s" in template "%s"', $name, get_called_class()));
114
    }
115
116
    /**
117
     * @param string $name
118
     *
119
     * @return string
120
     */
121
    public function getAliasParam(string $name): string
122
    {
123
        if (array_key_exists($name, static::PARAM_ALIAS)) {
124
            $name = static::PARAM_ALIAS[$name];
125
        }
126
127
        return $name;
128
    }
129
130
    /**
131
     * TODO check if method set{ParamName} exists.
132
     *
133
     * @param string $name
134
     * @param string $value
135
     *
136
     * @return AbstractParametersObject
137
     * @throws Exception
138
     */
139
    public function setParam(string $name, string $value): AbstractParametersObject
140
    {
141
        try {
142
            $this->checkParamName($name);
143
        } catch (Throwable $e) {
144
            $this->log[] = sprintf('no parameter "%s" in AbstractParametersObject "%s"', $name, get_called_class());
145
146
            return $this;
147
        }
148
149
        $name = $this->getAliasParam($name);
150
        $value = trim($value);
151
        if (!empty($value) || $this->parametersValues[$name]) {
152
            $this->parametersValues[$name] = $value;
153
        }
154
155
        return $this;
156
    }
157
158
    /**
159
     * @param $param
160
     *
161
     * @return string|null
162
     * @throws Exception
163
     */
164
    public function __get($param): ?string
165
    {
166
        $this->checkParamName($param);
167
168
        if (!empty($this->parametersValues[$param])) {
169
            return $this->parametersValues[$param];
170
        }
171
172
        // todo param_alias ?
173
        return null;
174
    }
175
176
    public function unsetParam(string $name): void
177
    {
178
        $this->checkParamName($name);
179
        $name = $this->getAliasParam($name);
180
        unset($this->parametersValues[$name]);
181
    }
182
183
    /**
184
     * TODO move/refac.
185
     *
186
     * @param string $tplText
187
     *
188
     * @throws Exception
189
     */
190
    public function hydrateFromText(string $tplText)
191
    {
192
        if (WikiTextUtil::isCommented($tplText)) {
193
            throw new DomainException('HTML comment tag detected');
194
        }
195
        $data = TemplateParser::parseDataFromTemplate($this::MODEL_NAME, $tplText);
196
        $this->detectUserSeparator($tplText);
197
        $this->hydrate($data);
198
    }
199
200
    public function detectUserSeparator($text): void
201
    {
202
        $this->userSeparator = TemplateParser::findUserStyleSeparator($text);
203
    }
204
205
    /**
206
     * @param array $data
207
     *
208
     * @return AbstractWikiTemplate
209
     * @throws Exception
210
     */
211
    public function hydrate(array $data): self
212
    {
213
        foreach ($data as $name => $value) {
214
            if (is_string($value)) {
215
                $this->hydrateTemplateParameter($name, $value);
216
            }
217
        }
218
219
        // todo?
220
        $this->setParamOrderByUser($data);
221
222
        return $this;
223
    }
224
225
    /**
226
     * @param        $name  string|int
227
     * @param string $value
228
     *
229
     * @throws Exception
230
     */
231
    protected function hydrateTemplateParameter($name, string $value): void
232
    {
233
        // Gestion alias
234
        try {
235
            $this->checkParamName($name);
236
            $name = $this->getAliasParam($name);
237
        } catch (Throwable $e) {
238
            unset($e);
239
            // hack : 1 => "ouvrage collectif"
240
            $name = (string)$name;
241
            $this->log[] = "parameter $name not found";
242
            $this->parametersErrorFromHydrate[$name] = $value;
243
244
            return;
245
        }
246
247
        if (empty($value)) {
248
            // optional parameter
249
            if (!isset(static::REQUIRED_PARAMETERS[$name])) {
250
                unset($this->parametersValues[$name]);
251
252
                return;
253
            }
254
            // required parameter
255
            $this->parametersValues[$name] = '';
256
        }
257
258
        $method = $this->setterMethodName($name);
259
        if (method_exists($this, $method)) {
260
            $this->$method($value);
261
262
            return;
263
        }
264
265
        $this->parametersValues[$name] = $value;
266
    }
267
268
    /**
269
     * Define the serialize order of parameters (from user initial choice).
270
     * default : $params = ['param1'=>'', 'param2' => '', ...]
271
     * OK with $params = ['a','b','c'].
272
     *
273
     * @param array
274
     *
275
     * @throws Exception
276
     */
277
    public function setParamOrderByUser(array $params = []): void
278
    {
279
        $validParams = [];
280
        foreach ($params as $key => $value) {
281
            $name = (is_int($key)) ? $value : $key;
282
283
            try {
284
                $this->checkParamName($name);
285
                $name = $this->getAliasParam($name);
286
                $validParams[] = $name;
287
            } catch (Throwable $e) {
288
                unset($e);
289
                $this->log[] = "Parameter $name do not exists";
290
291
                continue;
292
            }
293
        }
294
        $this->paramOrderByUser = $validParams;
295
    }
296
297
    /**
298
     * TODO : data transfer object (DTO) to mix userErrorParam data ?
299
     * TODO : refac $inlineStyle as $userPreferences[] and bool flag on serialize().
300
     *
301
     * @param bool|null $cleanOrder
302
     *
303
     * @return string
304
     */
305
    public function serialize(?bool $cleanOrder = false): string
306
    {
307
        $paramsByRenderOrder = $this->paramsByRenderOrder($cleanOrder);
308
        $paramsByRenderOrder = $this->filterEmptyNotRequired($paramsByRenderOrder);
309
310
        // TODO : $option to add or not the wrong parameters ?
311
        // Using the wrong parameters+value from user input ?
312
        $paramsByRenderOrder = $this->mergeWrongParametersFromUser($paramsByRenderOrder);
313
314
        $string = '{{'.static::MODEL_NAME;
315
        foreach ($paramsByRenderOrder as $paramName => $paramValue) {
316
            $string .= ($this->userSeparator) ?? '|';
317
318
            if (!in_array($paramName, ['0', '1', '2', '3', '4', '5'])) {
319
                $string .= $paramName.'=';
320
                // {{template|1=blabla}} -> {{template|blabla}}
321
            }
322
            $string .= $paramValue;
323
        }
324
        // expanded model -> "\n}}"
325
        if ($this->userSeparator && strpos($this->userSeparator, "\n") !== false) {
326
            $string .= "\n";
327
        }
328
        $string .= '}}';
329
330
        return $string;
331
    }
332
333
    /**
334
     * @param bool|null $cleanOrder
335
     *
336
     * @return array
337
     */
338
    protected function paramsByRenderOrder(?bool $cleanOrder = false): array
339
    {
340
        $renderParams = [];
341
342
        // By user order
343
        if (!empty($this->paramOrderByUser) && !$cleanOrder) {
344
            // merge parameter orders (can't use the array operator +)
345
            $newOrder = $this->paramOrderByUser;
346
            foreach ($this->parametersByOrder as $paramName) {
347
                if (!in_array($paramName, $newOrder)) {
348
                    $newOrder = array_merge($newOrder, [$paramName]);
349
                }
350
            }
351
            foreach ($newOrder as $paramName) {
352
                if (isset($this->parametersValues[$paramName])) {
353
                    $renderParams[$paramName] = $this->parametersValues[$paramName];
354
                }
355
            }
356
357
            return $renderParams;
358
        }
359
360
        // default order
361
        foreach ($this->parametersByOrder as $order => $paramName) {
362
            if (isset($this->parametersValues[$paramName])) {
363
                $renderParams[$paramName] = $this->parametersValues[$paramName];
364
            }
365
        }
366
367
        return $renderParams;
368
    }
369
370
    /**
371
     * Delete key if empty value and the key not required.
372
     *
373
     * @param array $params
374
     *
375
     * @return array
376
     */
377
    protected function filterEmptyNotRequired(array $params): array
378
    {
379
        $render = [];
380
        foreach ($params as $name => $value) {
381
            if (empty($value) && !isset(static::REQUIRED_PARAMETERS[$name])) {
382
                continue;
383
            }
384
            $render[$name] = $params[$name];
385
        }
386
387
        return $render;
388
    }
389
390
    /**
391
     * Merge Render data with wrong parameters+value from user input.
392
     * The wrong ones already corrected are not added.
393
     *
394
     * @param array $paramsByRenderOrder
395
     *
396
     * @return array
397
     */
398
    protected function mergeWrongParametersFromUser(array $paramsByRenderOrder): array
399
    {
400
        if (!empty($this->parametersErrorFromHydrate)) {
401
            // FIXED? : si y'a de l'info dans un paramètre erreur et sans value...
402
            //$errorUserData = $this->deleteEmptyValueArray($this->parametersErrorFromHydrate);
403
            $errorUserData = $this->parametersErrorFromHydrate;
404
405
            // Add a note in HTML commentary
406
            foreach ($errorUserData as $param => $value) {
407
                if ('string' === gettype($param) && empty(trim($param))) {
408
                    continue;
409
                }
410
                if (is_int($param)) {
411
                    // ou 1= 2= 3=
412
                    $errorUserData[$param] = $value." <!--VALEUR SANS NOM DE PARAMETRE -->";
413
                    continue;
414
                }
415
                $errorUserData[$param] = $value." <!--PARAMETRE '$param' N'EXISTE PAS -->";
416
            }
417
            $paramsByRenderOrder = array_merge($paramsByRenderOrder, $errorUserData);
418
        }
419
420
        return $paramsByRenderOrder;
421
    }
422
}
423