Completed
Push — master ( d676b1...ecc147 )
by Dispositif
02:12
created

AbstractWikiTemplate::hydrateTemplateParameter()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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