Completed
Push — master ( e37ca9...d5b8a7 )
by Dispositif
02:39
created

mergeWrongParametersFromUser()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 31
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 3
b 0
f 0
nc 2
nop 1
dl 0
loc 31
rs 8.8333
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
     *
76
     * @throws Exception
77
     */
78
    public function getParam(string $name): ?string
79
    {
80
        try {
81
            $this->checkParamName($name);
82
        } catch (Exception $e) {
83
            return null;
84
        }
85
        $name = $this->getAliasParam($name);
86
87
        return ($this->parametersValues[$name]) ?? null;
88
    }
89
90
    /**
91
     * TODO return bool + log() ?
92
     * todo check keyNum <= count($parametersByOrder).
93
     *
94
     * @param $name string|int
95
     *
96
     * @throws Exception
97
     */
98
    protected function checkParamName($name): void
99
    {
100
        // todo verify/useless ?
101
        if (is_int($name)) {
102
            $name = (string) $name;
103
        }
104
105
        // that parameter exists in template ?
106
        if (in_array($name, $this->parametersByOrder)
107
            || array_key_exists($name, static::PARAM_ALIAS)
108
        ) {
109
            return;
110
        }
111
112
        // keyNum parameter ?
113
        //        if (!in_array($name, ['1', '2', '3', '4'])) {
114
        throw new Exception(sprintf('no parameter "%s" in template "%s"', $name, get_called_class()));
115
    }
116
117
    /**
118
     * @param string $name
119
     *
120
     * @return string
121
     */
122
    public function getAliasParam(string $name): string
123
    {
124
        if (array_key_exists($name, static::PARAM_ALIAS)) {
125
            $name = static::PARAM_ALIAS[$name];
126
        }
127
128
        return $name;
129
    }
130
131
    /**
132
     * TODO check if method set{ParamName} exists.
133
     *
134
     * @param string $name
135
     * @param string $value
136
     *
137
     * @return AbstractParametersObject
138
     *
139
     * @throws Exception
140
     */
141
    public function setParam(string $name, string $value): AbstractParametersObject
142
    {
143
        try {
144
            $this->checkParamName($name);
145
        } catch (Throwable $e) {
146
            $this->log[] = sprintf('no parameter "%s" in AbstractParametersObject "%s"', $name, get_called_class());
147
148
            return $this;
149
        }
150
151
        $name = $this->getAliasParam($name);
152
        $value = trim($value);
153
        if (!empty($value) || $this->parametersValues[$name]) {
154
            $this->parametersValues[$name] = $value;
155
        }
156
157
        return $this;
158
    }
159
160
    /**
161
     * @param $param
162
     *
163
     * @return string|null
164
     *
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
     *
213
     * @throws Exception
214
     */
215
    public function hydrate(array $data): self
216
    {
217
        foreach ($data as $name => $value) {
218
            if (is_string($value)) {
219
                $this->hydrateTemplateParameter($name, $value);
220
            }
221
        }
222
223
        // todo?
224
        $this->setParamOrderByUser($data);
225
226
        return $this;
227
    }
228
229
    /**
230
     * @param        $name  string|int
231
     * @param string $value
232
     *
233
     * @throws Exception
234
     */
235
    protected function hydrateTemplateParameter($name, string $value): void
236
    {
237
        // Gestion alias
238
        try {
239
            $this->checkParamName($name);
240
            $name = $this->getAliasParam($name);
241
        } catch (Throwable $e) {
242
            unset($e);
243
            // hack : 1 => "ouvrage collectif"
244
            $name = (string) $name;
245
            $this->log[] = "parameter $name not found";
246
            $this->parametersErrorFromHydrate[$name] = $value;
247
248
            return;
249
        }
250
251
        if (empty($value)) {
252
            // optional parameter
253
            if (!isset(static::REQUIRED_PARAMETERS[$name])) {
254
                unset($this->parametersValues[$name]);
255
256
                return;
257
            }
258
            // required parameter
259
            $this->parametersValues[$name] = '';
260
        }
261
262
        $method = $this->setterMethodName($name);
263
        if (method_exists($this, $method)) {
264
            $this->$method($value);
265
266
            return;
267
        }
268
269
        $this->parametersValues[$name] = $value;
270
    }
271
272
    /**
273
     * Define the serialize order of parameters (from user initial choice).
274
     * default : $params = ['param1'=>'', 'param2' => '', ...]
275
     * OK with $params = ['a','b','c'].
276
     *
277
     * @param array
278
     *
279
     * @throws Exception
280
     */
281
    public function setParamOrderByUser(array $params = []): void
282
    {
283
        $validParams = [];
284
        foreach ($params as $key => $value) {
285
            $name = (is_int($key)) ? $value : $key;
286
287
            try {
288
                $this->checkParamName($name);
289
                $name = $this->getAliasParam($name);
290
                $validParams[] = $name;
291
            } catch (Throwable $e) {
292
                unset($e);
293
                $this->log[] = "Parameter $name do not exists";
294
295
                continue;
296
            }
297
        }
298
        $this->paramOrderByUser = $validParams;
299
    }
300
301
    /**
302
     * TODO : data transfer object (DTO) to mix userErrorParam data ?
303
     * TODO : refac $inlineStyle as $userPreferences[] and bool flag on serialize().
304
     *
305
     * @param bool|null $cleanOrder
306
     *
307
     * @return string
308
     */
309
    public function serialize(?bool $cleanOrder = false): string
310
    {
311
        $paramsByRenderOrder = $this->paramsByRenderOrder($cleanOrder);
312
        $paramsByRenderOrder = $this->filterEmptyNotRequired($paramsByRenderOrder);
313
314
        // TODO : $option to add or not the wrong parameters ?
315
        // Using the wrong parameters+value from user input ?
316
        $paramsByRenderOrder = $this->mergeWrongParametersFromUser($paramsByRenderOrder);
317
318
        $string = '{{'.static::MODEL_NAME;
319
        foreach ($paramsByRenderOrder as $paramName => $paramValue) {
320
            $string .= ($this->userSeparator) ?? '|';
321
322
            if (!in_array($paramName, ['0', '1', '2', '3', '4', '5'])) {
323
                $string .= $paramName.'=';
324
                // {{template|1=blabla}} -> {{template|blabla}}
325
            }
326
            $string .= $paramValue;
327
        }
328
        // expanded model -> "\n}}"
329
        if ($this->userSeparator && false !== strpos($this->userSeparator, "\n")) {
330
            $string .= "\n";
331
        }
332
        $string .= '}}';
333
334
        return $string;
335
    }
336
337
    /**
338
     * @param bool|null $cleanOrder
339
     *
340
     * @return array
341
     */
342
    protected function paramsByRenderOrder(?bool $cleanOrder = false): array
343
    {
344
        $renderParams = [];
345
346
        // By user order
347
        if (!empty($this->paramOrderByUser) && !$cleanOrder) {
348
            // merge parameter orders (can't use the array operator +)
349
            $newOrder = $this->paramOrderByUser;
350
            foreach ($this->parametersByOrder as $paramName) {
351
                if (!in_array($paramName, $newOrder)) {
352
                    $newOrder = array_merge($newOrder, [$paramName]);
353
                }
354
            }
355
            foreach ($newOrder as $paramName) {
356
                if (isset($this->parametersValues[$paramName])) {
357
                    $renderParams[$paramName] = $this->parametersValues[$paramName];
358
                }
359
            }
360
361
            return $renderParams;
362
        }
363
364
        // default order
365
        foreach ($this->parametersByOrder as $order => $paramName) {
366
            if (isset($this->parametersValues[$paramName])) {
367
                $renderParams[$paramName] = $this->parametersValues[$paramName];
368
            }
369
        }
370
371
        return $renderParams;
372
    }
373
374
    /**
375
     * Delete key if empty value and the key not required.
376
     *
377
     * @param array $params
378
     *
379
     * @return array
380
     */
381
    protected function filterEmptyNotRequired(array $params): array
382
    {
383
        $render = [];
384
        foreach ($params as $name => $value) {
385
            if (empty($value) && !isset(static::REQUIRED_PARAMETERS[$name])) {
386
                continue;
387
            }
388
            $render[$name] = $params[$name];
389
        }
390
391
        return $render;
392
    }
393
394
    /**
395
     * Merge Render data with wrong parameters+value from user input.
396
     * The wrong ones already corrected are not added.
397
     *
398
     * @param array $paramsByRenderOrder
399
     *
400
     * @return array
401
     */
402
    protected function mergeWrongParametersFromUser(array $paramsByRenderOrder): array
403
    {
404
        if (!empty($this->parametersErrorFromHydrate)) {
405
            // FIXED? : si y'a de l'info dans un paramètre erreur et sans value...
406
            //$errorUserData = $this->deleteEmptyValueArray($this->parametersErrorFromHydrate);
407
            $errorUserData = $this->parametersErrorFromHydrate;
408
409
            // Add a note in HTML commentary
410
            foreach ($errorUserData as $param => $value) {
411
                if ('string' === gettype($param) && empty(trim($param))) {
412
                    continue;
413
                }
414
                if (is_int($param)) {
415
416
                    // erreur "|lire en ligne|"
417
                    if(in_array($value, $this->getParamsAndAlias())){
418
                        unset($errorUserData[$param]);
419
                        continue;
420
                    }
421
422
                    // ou 1= 2= 3=
423
                    $errorUserData[$param] = $value.' <!--VALEUR SANS NOM DE PARAMETRE -->';
424
425
                    continue;
426
                }
427
                $errorUserData[$param] = $value." <!--PARAMETRE '$param' N'EXISTE PAS -->";
428
            }
429
            $paramsByRenderOrder = array_merge($paramsByRenderOrder, $errorUserData);
430
        }
431
432
        return $paramsByRenderOrder;
433
    }
434
}
435