Passed
Pull Request — master (#363)
by Def
11:52
created

Quoter::quoteSimpleColumnName()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 8
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Schema;
6
7
use Yiisoft\Db\Connection\ConnectionInterface;
8
9
use function addcslashes;
10
use function explode;
11
use function implode;
12
use function is_string;
13
use function preg_replace_callback;
14
use function str_contains;
15
use function str_replace;
16
use function str_starts_with;
17
use function strlen;
18
use function strpos;
19
use function strrpos;
20
use function substr;
21
22
class Quoter implements QuoterInterface
23
{
24
    public function __construct(
25
        private ConnectionInterface $connection,
26
        /** @psalm-var string[]|string */
27
        private array|string $columnQuoteCharacter,
28
        /** @psalm-var string[]|string */
29
        private array|string $tableQuoteCharacter,
30
        private string $tablePrefix = ''
31
    ) {
32
    }
33
34
    public function getTableNameParts(string $name): array
35
    {
36
        $parts = array_slice(explode('.', $name), -2, 2);
37
        return array_map(function ($part) {
38
            return $this->unquoteSimpleTableName($part);
39
        }, $parts);
40
    }
41
42
    public function ensureNameQuoted(string $name): string
43
    {
44
        $name = str_replace(["'", '"', '`', '[', ']'], '', $name);
45
46
        if ($name && !preg_match('/^{{.*}}$/', $name)) {
47
            return '{{' . $name . '}}';
48
        }
49
50
        return $name;
51
    }
52
53
    public function ensureColumnName(string $name): string
54
    {
55
        if (strrpos($name, '.') !== false) {
56
            $parts = explode('.', $name);
57
            $name = $parts[count($parts)-1];
58
        }
59
        return preg_replace('|^\[\[([_\w\-. ]+)\]\]$|', '\1', $name);
60
    }
61
62
    public function quoteColumnName(string $name): string
63
    {
64
        if (str_contains($name, '(') || str_contains($name, '[[')) {
65
            return $name;
66
        }
67
68
        if (($pos = strrpos($name, '.')) !== false) {
69
            $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.';
70
            $name = substr($name, $pos + 1);
71
        } else {
72
            $prefix = '';
73
        }
74
75
        if (str_contains($name, '{{')) {
76
            return $name;
77
        }
78
79
        return $prefix . $this->quoteSimpleColumnName($name);
80
    }
81
82
    public function quoteSimpleColumnName(string $name): string
83
    {
84
        if (is_string($this->columnQuoteCharacter)) {
85
            $startingCharacter = $endingCharacter = $this->columnQuoteCharacter;
86
        } else {
87
            [$startingCharacter, $endingCharacter] = $this->columnQuoteCharacter;
88
        }
89
90
        return $name === '*' || str_contains($name, $startingCharacter) ? $name : $startingCharacter . $name
91
            . $endingCharacter;
92
    }
93
94
    public function quoteSimpleTableName(string $name): string
95
    {
96
        if (is_string($this->tableQuoteCharacter)) {
97
            $startingCharacter = $endingCharacter = $this->tableQuoteCharacter;
98
        } else {
99
            [$startingCharacter, $endingCharacter] = $this->tableQuoteCharacter;
100
        }
101
102
        return str_contains($name, $startingCharacter) ? $name : $startingCharacter . $name . $endingCharacter;
103
    }
104
105
    public function quoteSql(string $sql): string
106
    {
107
        return preg_replace_callback(
108
            '/({{(%?[\w\-. ]+%?)}}|\\[\\[([\w\-. ]+)]])/',
109
            function ($matches) {
110
                if (isset($matches[3])) {
111
                    return $this->quoteColumnName($matches[3]);
112
                }
113
114
                return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2]));
115
            },
116
            $sql
117
        );
118
    }
119
120
    public function quoteTableName(string $name): string
121
    {
122
        if (str_starts_with($name, '(') && strpos($name, ')') === strlen($name) - 1) {
123
            return $name;
124
        }
125
126
        if (str_contains($name, '{{')) {
127
            return $name;
128
        }
129
130
        if (!str_contains($name, '.')) {
131
            return $this->quoteSimpleTableName($name);
132
        }
133
134
        $parts = $this->getTableNameParts($name);
135
136
        foreach ($parts as $i => $part) {
137
            $parts[$i] = $this->quoteSimpleTableName($part);
138
        }
139
140
        return implode('.', $parts);
141
    }
142
143
    public function quoteValue(mixed $value): mixed
144
    {
145
        if (!is_string($value)) {
146
            return $value;
147
        }
148
149
        if (($quotedValue = $this->connection->quoteValue($value)) !== false) {
150
            return $quotedValue;
151
        }
152
153
        return '\'' . str_replace('\'', '\'\'', addcslashes($value, "\000\032")) . '\'';
154
    }
155
156
    public function unquoteSimpleColumnName(string $name): string
157
    {
158
        if (is_string($this->columnQuoteCharacter)) {
159
            $startingCharacter = $this->columnQuoteCharacter;
160
        } else {
161
            $startingCharacter = $this->columnQuoteCharacter[0];
162
        }
163
164
        return !str_contains($name, $startingCharacter) ? $name : substr($name, 1, -1);
165
    }
166
167
    public function unquoteSimpleTableName(string $name): string
168
    {
169
        if (is_string($this->tableQuoteCharacter)) {
170
            $startingCharacter = $this->tableQuoteCharacter;
171
        } else {
172
            $startingCharacter = $this->tableQuoteCharacter[0];
173
        }
174
175
        return !str_contains($name, $startingCharacter) ? $name : substr($name, 1, -1);
176
    }
177
}
178