Passed
Pull Request — master (#437)
by Def
02:19
created

Quoter::unquoteParts()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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