Completed
Push — master ( f40c6a...9a6de3 )
by BENOIT
04:32
created

Previewer::stringify()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 1
dl 0
loc 16
rs 9.1111
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace BenTools\Where\Helper;
5
6
/**
7
 * @internal
8
 */
9
final class Previewer
10
{
11
    /**
12
     * @param string $expression
13
     * @param array  $values
14
     * @return string
15
     * @throws \InvalidArgumentException
16
     */
17
    public static function preview(string $expression, array $values = []): string
18
    {
19
        if (0 === \count($values)) {
20
            return $expression;
21
        }
22
23
        if (self::isSequentialArray($values)) {
24
            return self::previewUnnamed($expression, $values);
25
        }
26
27
        return self::previewNamed($expression, $values);
28
    }
29
30
    /**
31
     * @param string $expression
32
     * @param array  $values
33
     * @return string
34
     * @throws \InvalidArgumentException
35
     */
36
    private static function previewUnnamed(string $expression, array $values): string
37
    {
38
        if (\count($values) !== \preg_match_all("/([\?])/", $expression)) {
39
            throw new \InvalidArgumentException("Number of variables doesn't match number of parameters in statement");
40
        }
41
42
        $preview = $expression;
43
44
        foreach ($values as $value) {
45
            $preview = \preg_replace("/([\?])/", self::escape($value), $preview, 1);
46
        }
47
48
        return $preview;
49
    }
50
51
    /**
52
     * @param string $expression
53
     * @param array  $values
54
     * @return string
55
     * @throws \InvalidArgumentException
56
     */
57
    private static function previewNamed(string $expression, array $values): string
58
    {
59
        $preview = $expression;
60
        $keywords = [];
61
62
        foreach ($values as $key => $value) {
63
            if (!\in_array($key, $keywords, true)) {
64
                $keywords[] = $key;
65
            }
66
        }
67
        $nbPlaceholders = \preg_match_all('#:([a-zA-Z0-9_]+)#', $expression, $placeholders);
68
        if ($nbPlaceholders > 0 && \count(\array_unique($placeholders[1])) !== \count($values)) {
69
            throw new \InvalidArgumentException("Number of variables doesn't match number of parameters in statement");
70
        }
71
        foreach ($keywords as $keyword) {
72
            $pattern = "/(\:\b" . $keyword . "\b)/i";
73
            $preview = \preg_replace($pattern, self::escape($values[$keyword]), $preview);
74
        }
75
76
        return $preview;
77
    }
78
79
    /**
80
     * @param $value
81
     * @return string
82
     * @throws \InvalidArgumentException
83
     */
84
    private static function escape($value)
85
    {
86
        $type = \gettype($value);
87
        switch ($type) {
88
            case 'NULL':
89
                return 'NULL';
90
            case 'boolean':
91
                return $value ? 'TRUE' : 'FALSE';
92
            case 'double':
93
            case 'integer':
94
                return $value;
95
            default:
96
                return "'" . \addslashes(self::stringify($value)) . "'";
97
        }
98
    }
99
100
    /**
101
     * @param $value
102
     * @return string
103
     * @throws \InvalidArgumentException
104
     */
105
    private static function stringify($value): string
106
    {
107
        if (\is_scalar($value)) {
108
            return (string) $value;
109
        }
110
111
        if (\is_object($value) && \is_callable([$value, '__toString'])) {
112
            return (string) $value;
113
        }
114
115
        if ($value instanceof \DateTimeInterface) {
116
            return $value->format('Y-m-d H:i:s');
117
        }
118
119
        throw new \InvalidArgumentException(\sprintf('Expected string or stringable object, %s returned', \is_object($value) ? \get_class($value) : \gettype($value)));
120
    }
121
122
    /**
123
     * @param array $array
124
     * @return bool
125
     */
126
    private static function isSequentialArray(array $array): bool
127
    {
128
        return isset($array[0]) && \array_keys($array) === \range(0, \count($array) - 1);
129
    }
130
}
131