Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
61.54% covered (warning)
61.54%
16 / 26
CRAP
77.02% covered (warning)
77.02%
191 / 248
Utils
0.00% covered (danger)
0.00%
0 / 1
61.54% covered (warning)
61.54%
16 / 26
143.30
77.02% covered (warning)
77.02%
191 / 248
 makeValidator
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
13 / 13
 makeValidatorFromRule
0.00% covered (danger)
0.00%
0 / 1
10.90
79.17% covered (warning)
79.17%
19 / 24
 validationRequired
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationNullable
0.00% covered (danger)
0.00%
0 / 1
2.01
88.89% covered (warning)
88.89%
8 / 9
 validationSometimes
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
9 / 9
 validationInt
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationString
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationBool
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
7 / 7
 validationMin
0.00% covered (danger)
0.00%
0 / 1
3.00
92.31% covered (success)
92.31%
12 / 13
 validationMax
0.00% covered (danger)
0.00%
0 / 1
3.11
76.92% covered (warning)
76.92%
10 / 13
 validationUuid
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationMixedCase
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
7 / 7
 validationContainDigit
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationContainSymbol
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 validationDate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 validationDateFormat
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 validationEmail
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 validationArray
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 validationClosure
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 8
 getConfig
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 getResquest
0.00% covered (danger)
0.00%
0 / 1
9.16
87.50% covered (warning)
87.50%
21 / 24
 getPackageInfo
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 translate
0.00% covered (danger)
0.00%
0 / 1
3.21
71.43% covered (warning)
71.43%
5 / 7
 log
0.00% covered (danger)
0.00%
0 / 1
110
0.00% covered (danger)
0.00%
0 / 29
 setRawProperty
0.00% covered (danger)
0.00%
0 / 1
4.43
70.00% covered (warning)
70.00%
7 / 10
 getRawProperty
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
4 / 6
1<?php
2
3namespace LDC\LDCCRM;
4
5use LDC\LDCCRM\Enums\HttpMethods;
6use LDC\LDCCRM\Enums\LogLevel;
7use LDC\LDCCRM\Exceptions\AccessError;
8use LDC\LDCCRM\Exceptions\SkipValidationException;
9use LDC\LDCCRM\Exceptions\ValidationException;
10use LDC\LDCCRM\Transport\Request;
11use Respect\Validation\Validator;
12
13/**
14 * This is the LDCCRM utility class
15 *
16 * @category Utility
17 * @uses \LDC\LDCCRM\Enums\HttpMethods
18 * @uses \LDC\LDCCRM\Transport\Request
19 *
20 * @package LDCCrmClient
21 * @subpackage utilities
22 */
23class Utils
24{
25    /**
26     * Package infos
27     *
28     * @var array
29     */
30    private static $packageInfos;
31
32    /**
33     * Loaded translations
34     *
35     * @var array
36     */
37    private static $translations = [];
38
39    /**
40     * Create a new Validator instance.
41     *
42     * @param array $data
43     * @param array $rules
44     * @return closure
45     * @throws \ErrorException If an unknown validation rule is used.
46     */
47    public static function makeValidator(
48        array $data,
49        array $rules
50    ): object {
51        foreach ($rules as $attribute => $attrRules) {
52            $validators = [];
53            if (\is_string($attrRules)) {
54                $attrRules = \explode('|', $attrRules);
55            }
56            foreach ($attrRules as $rule) {
57                $validators[] = self::makeValidatorFromRule($rule, $data, $attribute);
58            } //end foreach
59        } //end foreach
60        return function () use ($validators) {
61            foreach ($validators as $validator) {
62                try {
63                    if (!$validator['validate']()) {
64                        throw new ValidationException($validator['error']);
65                    }
66                } catch (SkipValidationException $e) {
67                    break;
68                }
69            }
70        };
71    }
72
73    /**
74     * Generate a validator
75     *
76     * @param mixed  $rule
77     * @param array  $data
78     * @param string $attribute
79     * @return array
80     * @throws \ErrorException If a rule is unknown.
81     */
82    private static function makeValidatorFromRule(
83        $rule,
84        array $data,
85        string $attribute
86    ): array {
87        $rParams = [];
88        $closure = null;
89        if (\is_callable($rule) and !is_string($rule)) {
90            /** @var \Closure $closure */
91            $closure = $rule;
92            $rule    = '#closure#';
93        } elseif (\strpos($rule, ':') !== false) {
94            $rParams = \explode(':', $rule, 2);
95            $rule    = \array_shift($rParams);
96        }
97        switch (true) {
98            case in_array($rule, [
99                'nullable', 'sometimes',
100                'int', 'bool', 'uuid', 'string',
101                'date', 'email', 'array',
102                'mixedCase', 'containDigit', 'containSymbol',
103            ]):
104                return self::{sprintf('validation%s', \ucfirst($rule))}(
105                    $attribute,
106                    $data[$attribute] ?? null
107                );
108            case in_array($rule, ['min', 'max']):
109                return self::{sprintf('validation%s', \ucfirst($rule))}(
110                    $attribute,
111                    $data[$attribute],
112                    $rParams
113                );
114            case in_array($rule, ['required', 'sometimes']):
115                return self::{sprintf('validation%s', \ucfirst($rule))}(
116                    $attribute,
117                    $data,
118                );
119            case $rule === 'date_format':
120                return self::validationDateFormat($attribute, $data[$attribute], $rParams);
121            case $rule === '#closure#':
122                /** @var \Closure $rule */
123                return self::validationClosure($attribute, $data[$attribute], $closure);
124            default:
125                throw new \ErrorException("Unknown validation rule $rule " . implode(': ', $rParams));
126        } //end switch
127    }
128
129    // * VALIDATION RULES
130
131    /**
132     * Make Required validator
133     *
134     * @param string $attribute
135     * @param array  $data
136     * @return array
137     */
138    private static function validationRequired(string $attribute, array $data): array
139    {
140        return [
141            'type' => 'required',
142            'error' => sprintf(
143                '%s est obligatoire',
144                self::translate($attribute)
145            ),
146            'validate' => function () use ($attribute, $data) {
147                return isset($data[$attribute]);
148            }
149        ];
150    }
151
152    /**
153     * Make Nullable validator
154     *
155     * @param string $attribute
156     * @param mixed  $value
157     * @return array
158     */
159    private static function validationNullable(string $attribute, $value): array
160    {
161        return [
162            'type' => 'nullable',
163            'error' => sprintf(
164                '%s peut Ãªtre Ã©gale Ã  nul',
165                self::translate($attribute)
166            ),
167            'validate' => function () use ($value) {
168                if (is_null($value)) {
169                    throw new SkipValidationException();
170                }
171                return true;
172            }
173        ];
174    }
175
176    /**
177     * Make Sometimes validator
178     *
179     * @param string $attribute
180     * @param mixed  $data
181     * @return array
182     */
183    private static function validationSometimes(string $attribute, $data): array
184    {
185        return [
186            'type' => 'sometimes',
187            'error' => sprintf(
188                '%s est falcutatif',
189                self::translate($attribute)
190            ),
191            'validate' => function () use ($data) {
192                if (empty($data)) {
193                    throw new SkipValidationException();
194                }
195                return true;
196            }
197        ];
198    }
199
200    /**
201     * Make Int validator
202     *
203     * @param string $attribute
204     * @param mixed  $value
205     * @return array
206     */
207    private static function validationInt(string $attribute, $value): array
208    {
209        return [
210            'type' => 'int',
211            'error' => sprintf(
212                '%s doit Ãªtre un entier',
213                self::translate($attribute)
214            ),
215            'validate' => function () use ($value) {
216                return \is_int($value);
217            }
218        ];
219    }
220
221    /**
222     * Make String validator
223     *
224     * @param string $attribute
225     * @param mixed  $value
226     * @return array
227     */
228    private static function validationString(string $attribute, $value): array
229    {
230        return [
231            'type' => 'string',
232            'error' => sprintf(
233                '%s doit Ãªtre une chaine de charactères',
234                self::translate($attribute)
235            ),
236            'validate' => function () use ($value) {
237                return \is_string($value);
238            }
239        ];
240    }
241
242    /**
243     * Make Bool validator
244     *
245     * @param string $attribute
246     * @param mixed  $value
247     * @return array
248     */
249    private static function validationBool(string $attribute, $value): array
250    {
251        return [
252            'type' => 'bool',
253            'error' => sprintf(
254                '%s doit Ãªtre un booléen',
255                self::translate($attribute)
256            ),
257            'validate' => function () use ($value) {
258                return \is_bool($value) or $value === 1 or $value === 0;
259            }
260        ];
261    }
262
263    /**
264     * Make Min validator
265     *
266     * @param string $attribute
267     * @param mixed  $value
268     * @param array  $params
269     * @return array
270     */
271    private static function validationMin(string $attribute, $value, array $params): array
272    {
273        return [
274            'type' => 'min',
275            'error' => sprintf(
276                '%s doit avoir au moins %s caratères, ou Ãªtre plus grand que %s si c\'est un nombre',
277                self::translate($attribute),
278                $params[0],
279                $params[0]
280            ),
281            'validate' => function () use ($value, $params) {
282                if (\is_string($value)) {
283                    return \strlen($value) >= (int)$params[0];
284                }
285                if (\is_int($value)) {
286                    return $value >= (int)$params[0];
287                }
288                return false;
289            }
290        ];
291    }
292
293    /**
294     * Make Max validator
295     *
296     * @param string $attribute
297     * @param mixed  $value
298     * @param array  $params
299     * @return array
300     */
301    private static function validationMax(string $attribute, $value, array $params): array
302    {
303        return [
304            'type' => 'max',
305            'error' => sprintf(
306                '%s doit avoir au moins %s caratères, ou Ãªtre plus grand que %s si c\'est un nombre',
307                self::translate($attribute),
308                $params[0],
309                $params[0]
310            ),
311            'validate' => function () use ($value, $params) {
312                if (\is_string($value)) {
313                    return \strlen($value) <= (int)$params[0];
314                }
315                if (\is_int($value)) {
316                    return $value <= (int)$params[0];
317                }
318                return false;
319            }
320        ];
321    }
322
323    /**
324     * Make Uuid validator
325     *
326     * @param string $attribute
327     * @param mixed  $value
328     * @return array
329     */
330    private static function validationUuid(string $attribute, $value): array
331    {
332        return [
333            'type' => 'uuid',
334            'error' => sprintf(
335                '%s doit Ãªtre un uuid valide',
336                self::translate($attribute)
337            ),
338            'validate' => function () use ($value) {
339                return Validator::uuid()->validate($value);
340            }
341        ];
342    }
343
344    /**
345     * Make MixedCase validator
346     *
347     * @param string $attribute
348     * @param mixed  $value
349     * @return array
350     */
351    private static function validationMixedCase(string $attribute, $value): array
352    {
353        return [
354            'type' => 'mixedCase',
355            'error' => sprintf(
356                '%s doit contenir des lettre minuscules et majuscules',
357                self::translate($attribute)
358            ),
359            'validate' => function () use ($value) {
360                return \preg_match('/[a-z]/', $value) and \preg_match('/[A-Z]/', $value);
361            }
362        ];
363    }
364
365    /**
366     * Make ContainDigit validator
367     *
368     * @param string $attribute
369     * @param mixed  $value
370     * @return array
371     */
372    private static function validationContainDigit(string $attribute, $value): array
373    {
374        return [
375            'type' => 'containDigit',
376            'error' => sprintf(
377                '%s doit contenir au moins un chiffre',
378                self::translate($attribute)
379            ),
380            'validate' => function () use ($value) {
381                return \preg_match('/[0-9]/', $value);
382            }
383        ];
384    }
385
386    /**
387     * Make ContainSymbol validator
388     *
389     * @param string $attribute
390     * @param mixed  $value
391     * @return array
392     */
393    private static function validationContainSymbol(string $attribute, $value): array
394    {
395        return [
396            'type' => 'containSymbol',
397            'error' => sprintf(
398                '%s doit contenir au moins un caractère spécial comme une virgule par exemple',
399                self::translate($attribute)
400            ),
401            'validate' => function () use ($value) {
402                return \preg_match('/[^a-zA-Z0-9]/', $value);
403            }
404        ];
405    }
406
407    /**
408     * Make Date validator
409     *
410     * @param string $attribute
411     * @param mixed  $value
412     * @return array
413     */
414    private static function validationDate(string $attribute, $value): array
415    {
416        return [
417            'type' => 'date',
418            'error' => sprintf('%s doit Ãªtre une date valide', self::translate($attribute)),
419            'validate' => function () use ($value) {
420                return Validator::dateTime()->validate($value);
421            }
422        ];
423    }
424
425    /**
426     * Make DateFormat validator
427     *
428     * @param string $attribute
429     * @param mixed  $value
430     * @param array  $params
431     * @return array
432     */
433    private static function validationDateFormat(string $attribute, $value, array $params): array
434    {
435        return [
436            'type' => 'date_format',
437            'error' => sprintf('%s doit Ãªtre une date au format %s', self::translate($attribute), $params[0]),
438            'validate' => function () use ($value, $params) {
439                return Validator::dateTime($params[0])->validate($value);
440            }
441        ];
442    }
443
444    /**
445     * Make Email validator
446     *
447     * @param string $attribute
448     * @param mixed  $value
449     * @return array
450     */
451    private static function validationEmail(string $attribute, $value): array
452    {
453        return [
454            'type' => 'email',
455            'error' => sprintf('%s doit Ãªtre un email valide', self::translate($attribute)),
456            'validate' => function () use ($value) {
457                return Validator::email()->validate($value);
458            }
459        ];
460    }
461
462    /**
463     * Make Array validator
464     *
465     * @param string $attribute
466     * @param mixed  $value
467     * @return array
468     */
469    private static function validationArray(string $attribute, $value): array
470    {
471        return [
472            'type' => 'array',
473            'error' => sprintf('%s doit Ãªtre un tableau valide', self::translate($attribute)),
474            'validate' => function () use ($value) {
475                return Validator::arrayType()->validate($value);
476            }
477        ];
478    }
479
480    /**
481     * Make Closure validator
482     *
483     * @param string   $attribute
484     * @param mixed    $value
485     * @param \Closure $closure
486     * @return array
487     */
488    private static function validationClosure(string $attribute, $value, \Closure $closure): array
489    {
490        return [
491            'type' => 'closure',
492            'error' => sprintf('%s doit Ãªtre validé', self::translate($attribute)),
493            'validate' => function () use ($attribute, $value, $closure) {
494                $closure($attribute, $value, function ($message) {
495                    throw new ValidationException($message);
496                });
497                return true;
498            }
499        ];
500    }
501
502    // * END VALIDATION RULES
503
504    /**
505     * This is only used for unit tests.
506     * Get config
507     *
508     * @param string $key
509     * @param string $default
510     * @return string|null
511     */
512    public static function getConfig(string $key, string $default = null): ?string
513    {
514        $value = null;
515        if (function_exists('config')) {
516            $value = \config($key, $default);
517        }
518
519        return $value;
520    }
521
522    /**
523     * Generate a Request from the route name
524     *
525     * @param string $routeName
526     * @param array  $query     Params to pass on to the route.
527     * @param array  $data      Http body params.
528     * @return \LDC\LDCCRM\Transport\Request
529     * @throws \ErrorException If the requested route is unknown.
530     * @throws \ErrorException If a required parameter is missing.
531     */
532    public static function getResquest(string $routeName, array $query = [], array $data = []): Request
533    {
534        // Convert bool to integer.
535        $query = \array_map(function ($v) {
536            return is_bool($v) ? (int)$v : $v;
537        }, $query);
538        $data  = \array_map(function ($v) {
539            return is_bool($v) ? (int)$v : $v;
540        }, $data);
541
542        $routes = \json_decode(\file_get_contents(__DIR__ . '/routes.json'), true);
543        if (!isset($routes[$routeName])) {
544            throw new \ErrorException("Unknown route $routeName from the crm");
545        }
546        $route        = $routes[$routeName];
547        $matches      = [];
548        $matchedIndex = [];
549        \preg_match_all('/{([\w-]*)}/', $route['uri'], $matches);
550        if (\count($matches[0])) {
551            foreach ($matches[0] as $k => $match) {
552                if (!isset($query[$matches[1][$k]])) {
553                    throw new \ErrorException(sprintf('Missing required parameter %s', $matches[1][$k]));
554                }
555                $route['uri']                  = str_replace($match, $query[$matches[1][$k]], $route['uri']);
556                $matchedIndex[$matches[1][$k]] = $query[$matches[1][$k]];
557            }
558        }
559        foreach ($matchedIndex as $key => $value) {
560            unset($query[$key]);
561        }
562        if (count($query)) {
563            $route['uri'] = sprintf('%s?%s', $route['uri'], \http_build_query($query));
564        }
565        return new Request(HttpMethods::make($route['methods'][0]), $route['uri'], [], $data);
566    }
567
568    /**
569     * Get composer.json infos
570     *
571     * @return array
572     */
573    public static function getPackageInfo(): array
574    {
575        if (!static::$packageInfos) {
576            static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../composer.json'), true);
577        }
578        return static::$packageInfos;
579    }
580
581    /**
582     * Translation a string using php file lang
583     *
584     * @param string $key
585     * @param string $lang
586     * @return string
587     */
588    public static function translate(string $key, string $lang = 'fr'): string
589    {
590        try {
591            if (!array_key_exists($lang, self::$translations)) {
592                self::$translations = require __DIR__ . "/../resources/lang/$lang.php";
593            }
594        } finally {
595            if (!isset(self::$translations['validation'][$key])) {
596                return $key;
597            } else {
598                return self::$translations['validation'][$key];
599            }
600        }
601    }
602
603    /**
604     * Write log using syslog
605     * It also write log using \Illuminate\Support\Facades\Log
606     * if it is present
607     *
608     * @param string                     $message The message is prefixed with "LDC CRM:" .
609     * @param \LDC\LDCCRM\Enums\LogLevel $level   The log level to apply.
610     * @return void
611     */
612    public static function log(string $message, LogLevel $level)
613    {
614        $message = "LDC CRM: $message";
615        if (\class_exists('\Illuminate\Support\Facades\Log')) {
616            switch ($level) {
617                case LogLevel::EMERG():
618                    \Illuminate\Support\Facades\Log::emergency($message);
619                    break;
620                case LogLevel::ALERT():
621                    \Illuminate\Support\Facades\Log::alert($message);
622                    break;
623                case LogLevel::CRIT():
624                    \Illuminate\Support\Facades\Log::critical($message);
625                    break;
626                case LogLevel::ERR():
627                    \Illuminate\Support\Facades\Log::error($message);
628                    break;
629                case LogLevel::WARNING():
630                    \Illuminate\Support\Facades\Log::warning($message);
631                    break;
632                case LogLevel::NOTICE():
633                    \Illuminate\Support\Facades\Log::notice($message);
634                    break;
635                case LogLevel::INFO():
636                    \Illuminate\Support\Facades\Log::info($message);
637                    break;
638                case LogLevel::DEBUG():
639                    \Illuminate\Support\Facades\Log::debug($message);
640                    break;
641            }//end switch
642            \Illuminate\Support\Facades\Log::warning($message);
643        }//end if
644        \syslog(\constant($level), $message);
645    }
646
647    /**
648     * Set a property without any validation.
649     *
650     * ! Warning : this can change any property of the object, use with caution.
651     *
652     * @param object $object   The object to inspect.
653     * @param string $property The object property to assign to.
654     * @param mixed  $value    The value to insert.
655     * @return void
656     * @throws LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
657     */
658    public static function setRawProperty(object $object, string $property, $value): void
659    {
660        if (is_object($value) and in_array(BaseEnum::class, class_parents($value))) {
661            $value = $value->value;
662        }
663        $objectName = \get_class($object);
664        $refClass   = new \ReflectionClass($objectName);
665        try {
666            $refProp = $refClass->getProperty($property);
667        } catch (\ReflectionException $e) {
668            throw new AccessError($objectName, $property);
669        }
670        $refProp->setAccessible(true);
671        $refProp->setValue($object, $value);
672    }
673
674    /**
675     * Get a property of an object.
676     *
677     * ! Warning : this will return any property of the object, use with caution.
678     *
679     * @param object $object   The object to inspect.
680     * @param string $property The object property to inspect.
681     * @return mixed|null Returns null if the property was not found any way.
682     */
683    public static function getRawProperty(object $object, string $property)
684    {
685        $refClass = new \ReflectionClass(\get_class($object));
686        try {
687            $refProp = $refClass->getProperty($property);
688        } catch (\ReflectionException $e) {
689            return null;
690        }
691        $refProp->setAccessible(true);
692        return $refProp->getValue($object);
693    }
694}