ReplacesBindings::formatBindings()   A
last analyzed

Complexity

Conditions 4
Paths 4

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 4
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Mnabialek\LaravelSqlLogger\Objects\Concerns;
4
5
use DateTimeInterface;
6
7
trait ReplacesBindings
8
{
9
    /**
10
     * Replace bindings.
11
     *
12
     * @param string $sql
13
     * @param array $bindings
14
     *
15
     * @return string
16
     */
17
    protected function replaceBindings($sql, array $bindings)
18
    {
19
        $generalRegex = $this->getRegex();
20
21
        foreach ($this->formatBindings($bindings) as $key => $binding) {
22
            $regex = is_numeric($key) ? $generalRegex : $this->getNamedParameterRegex($key);
23
            $sql = preg_replace($regex, $this->value($binding), $sql, 1);
24
        }
25
26
        return $sql;
27
    }
28
29
    /**
30
     * Get final value that will be displayed in query.
31
     *
32
     * @param mixed $value
33
     *
34
     * @return int|string
35
     */
36
    protected function value($value)
37
    {
38
        if ($value === null) {
39
            return 'null';
40
        }
41
42
        if (is_bool($value)) {
43
            return (int) $value;
44
        }
45
46
        return is_numeric($value) ? $value : "'" . $value . "'";
47
    }
48
49
    /**
50
     * Get regex to be used for named parameter with given name.
51
     *
52
     * @param string $name
53
     *
54
     * @return string
55
     */
56
    protected function getNamedParameterRegex($name)
57
    {
58
        if (mb_substr($name, 0, 1) == ':') {
59
            $name = mb_substr($name, 1);
60
        }
61
62
        return $this->wrapRegex($this->notInsideQuotes('\:' . preg_quote($name), false));
63
    }
64
65
    /**
66
     * Format bindings values.
67
     *
68
     * @param array $bindings
69
     *
70
     * @return array
71
     */
72
    protected function formatBindings($bindings)
73
    {
74
        foreach ($bindings as $key => $binding) {
75
            if ($binding instanceof DateTimeInterface) {
76
                $bindings[$key] = $binding->format('Y-m-d H:i:s');
77
            } elseif (is_string($binding)) {
78
                $bindings[$key] = str_replace("'", "\\'", $binding);
79
            }
80
        }
81
82
        return $bindings;
83
    }
84
85
    /**
86
     * Get regex to be used to replace bindings.
87
     *
88
     * @return string
89
     */
90
    protected function getRegex()
91
    {
92
        return $this->wrapRegex(
93
            $this->notInsideQuotes('?')
94
            . '|' .
95
            $this->notInsideQuotes('\:\w+', false)
96
        );
97
    }
98
99
    /**
100
     * Wrap regex.
101
     *
102
     * @param string $regex
103
     *
104
     * @return string
105
     */
106
    protected function wrapRegex($regex)
107
    {
108
        return '#' . $regex . '#ms';
109
    }
110
111
    /**
112
     * Create partial regex to find given text not inside quotes.
113
     *
114
     * @param string $string
115
     * @param bool $quote
116
     *
117
     * @return string
118
     */
119
    protected function notInsideQuotes($string, $quote = true)
120
    {
121
        if ($quote) {
122
            $string = preg_quote($string);
123
        }
124
125
        return
126
            // double quotes - ignore "" and everything inside quotes for example " abc \"err "
127
            '(?:""|"(?:[^"]|\\")*?[^\\\]")(*SKIP)(*F)|' . $string .
128
            '|' .
129
            // single quotes - ignore '' and everything inside quotes for example ' abc \'err '
130
            '(?:\\\'\\\'|\'(?:[^\']|\\\')*?[^\\\]\')(*SKIP)(*F)|' . $string;
131
    }
132
}
133