Completed
Push — 3.x-quoter ( dfb785...2b5e8a )
by Paul
02:02
created

Quoter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\SqlQuery\Common;
10
11
/**
12
 *
13
 * A quoting mechanism for identifier names (not values).
14
 *
15
 * @package Aura.SqlQuery
16
 *
17
 */
18
class Quoter implements QuoterInterface
19
{
20
    /**
21
     *
22
     * The prefix to use when quoting identifier names.
23
     *
24
     * @var string
25
     *
26
     */
27
    protected $quote_name_prefix = '"';
28
29
    /**
30
     *
31
     * The suffix to use when quoting identifier names.
32
     *
33
     * @var string
34
     *
35
     */
36
    protected $quote_name_suffix = '"';
37
38
    /**
39
     *
40
     * Returns the prefix to use when quoting identifier names.
41
     *
42
     * @return string
43
     *
44
     */
45 257
    public function getQuoteNamePrefix()
46
    {
47 257
        return $this->quote_name_prefix;
48
    }
49
50
    /**
51
     *
52
     * Returns the suffix to use when quoting identifier names.
53
     *
54
     * @return string
55
     *
56
     */
57 257
    public function getQuoteNameSuffix()
58
    {
59 257
        return $this->quote_name_suffix;
60
    }
61
62
    /**
63
     *
64
     * Quotes a single identifier name (table, table alias, table column,
65
     * index, sequence).
66
     *
67
     * If the name contains `' AS '`, this method will separately quote the
68
     * parts before and after the `' AS '`.
69
     *
70
     * If the name contains a space, this method will separately quote the
71
     * parts before and after the space.
72
     *
73
     * If the name contains a dot, this method will separately quote the
74
     * parts before and after the dot.
75
     *
76
     * @param string $spec The identifier name to quote.
77
     *
78
     * @return string The quoted identifier name.
79
     *
80
     * @see replaceName()
81
     *
82
     * @see quoteNameWithSeparator()
83
     *
84
     */
85 243
    public function quoteName($spec)
86
    {
87 243
        $spec = trim($spec);
88 243
        $seps = array(' AS ', ' ', '.');
89 243
        foreach ($seps as $sep) {
90 243
            $pos = strripos($spec, $sep);
91 243
            if ($pos) {
92 31
                return $this->quoteNameWithSeparator($spec, $sep, $pos);
93
            }
94 243
        }
95 243
        return $this->replaceName($spec);
96
    }
97
98
    /**
99
     *
100
     * Quotes an identifier that has a separator.
101
     *
102
     * @param string $spec The identifier name to quote.
103
     *
104
     * @param string $sep The separator, typically a dot or space.
105
     *
106
     * @param int $pos The position of the separator.
107
     *
108
     * @return string The quoted identifier name.
109
     *
110
     */
111 31
    protected function quoteNameWithSeparator($spec, $sep, $pos)
112
    {
113 31
        $len = strlen($sep);
114 31
        $part1 = $this->quoteName(substr($spec, 0, $pos));
115 31
        $part2 = $this->replaceName(substr($spec, $pos + $len));
116 31
        return "{$part1}{$sep}{$part2}";
117
    }
118
119
    /**
120
     *
121
     * Quotes all fully-qualified identifier names ("table.col") in a string,
122
     * typically an SQL snippet for a SELECT clause.
123
     *
124
     * Does not quote identifier names that are string literals (i.e., inside
125
     * single or double quotes).
126
     *
127
     * Looks for a trailing ' AS alias' and quotes the alias as well.
128
     *
129
     * @param string $text The string in which to quote fully-qualified
130
     * identifier names to quote.
131
     *
132
     * @return string|array The string with names quoted in it.
133
     *
134
     * @see replaceNamesIn()
135
     *
136
     */
137 244
    public function quoteNamesIn($text)
138
    {
139 244
        $list = $this->getListForQuoteNamesIn($text);
140 244
        $last = count($list) - 1;
141 244
        $text = null;
142 244
        foreach ($list as $key => $val) {
143
            // skip elements 2, 5, 8, 11, etc. as artifacts of the back-
144
            // referenced split; these are the trailing/ending quote
145
            // portions, and already included in the previous element.
146
            // this is the same as skipping every third element from zero.
147 244
            if (($key+1) % 3) {
148 244
                $text .= $this->quoteNamesInLoop($val, $key == $last);
149 244
            }
150 244
        }
151 244
        return $text;
152
    }
153
154
    /**
155
     *
156
     * Returns a list of candidate elements for quoting.
157
     *
158
     * @param string $text The text to split into quoting candidates.
159
     *
160
     * @return array
161
     *
162
     */
163 244
    protected function getListForQuoteNamesIn($text)
164
    {
165
        // look for ', ", \', or \" in the string.
166
        // match closing quotes against the same number of opening quotes.
167 244
        $apos = "'";
168 244
        $quot = '"';
169 244
        return preg_split(
170 244
            "/(($apos+|$quot+|\\$apos+|\\$quot+).*?\\2)/",
171 244
            $text,
172 244
            -1,
173
            PREG_SPLIT_DELIM_CAPTURE
174 244
        );
175
    }
176
177
    /**
178
     *
179
     * The in-loop functionality for quoting identifier names.
180
     *
181
     * @param string $val The name to be quoted.
182
     *
183
     * @param bool $is_last Is this the last loop?
184
     *
185
     * @return string The quoted name.
186
     *
187
     */
188 244
    protected function quoteNamesInLoop($val, $is_last)
189
    {
190 244
        if ($is_last) {
191 244
            return $this->replaceNamesAndAliasIn($val);
192
        }
193 31
        return $this->replaceNamesIn($val);
194
    }
195
196
    /**
197
     *
198
     * Replaces the names and alias in a string.
199
     *
200
     * @param string $val The name to be quoted.
201
     *
202
     * @return string The quoted name.
203
     *
204
     */
205 244
    protected function replaceNamesAndAliasIn($val)
206
    {
207 244
        $quoted = $this->replaceNamesIn($val);
208 244
        $pos = strripos($quoted, ' AS ');
209 244
        if ($pos) {
210 11
            $alias = $this->replaceName(substr($quoted, $pos + 4));
211 11
            $quoted = substr($quoted, 0, $pos) . " AS $alias";
212 11
        }
213 244
        return $quoted;
214
    }
215
216
    /**
217
     *
218
     * Quotes an identifier name (table, index, etc); ignores empty values and
219
     * values of '*'.
220
     *
221
     * @param string $name The identifier name to quote.
222
     *
223
     * @return string The quoted identifier name.
224
     *
225
     * @see quoteName()
226
     *
227
     */
228 254
    protected function replaceName($name)
229
    {
230 254
        $name = trim($name);
231 254
        if ($name == '*') {
232 1
            return $name;
233
        }
234
235 254
        return $this->quote_name_prefix
236
             . $name
237 254
             . $this->quote_name_suffix;
238
    }
239
240
    /**
241
     *
242
     * Quotes all fully-qualified identifier names ("table.col") in a string.
243
     *
244
     * @param string $text The string in which to quote fully-qualified
245
     * identifier names to quote.
246
     *
247
     * @return string|array The string with names quoted in it.
248
     *
249
     * @see quoteNamesIn()
250
     *
251
     */
252 244
    protected function replaceNamesIn($text)
253
    {
254 244
        $is_string_literal = strpos($text, "'") !== false
255 244
                        || strpos($text, '"') !== false;
256 244
        if ($is_string_literal) {
257 31
            return $text;
258
        }
259
260 244
        $word = "[a-z_][a-z0-9_]*";
261
262 244
        $find = "/(\\b)($word)\\.($word)(\\b)/i";
263
264
        $repl = '$1'
265 244
              . $this->quote_name_prefix
266 244
              . '$2'
267 244
              . $this->quote_name_suffix
268 244
              . '.'
269 244
              . $this->quote_name_prefix
270 244
              . '$3'
271 244
              . $this->quote_name_suffix
272 244
              . '$4'
273 244
              ;
274
275 244
        $text = preg_replace($find, $repl, $text);
276
277 244
        return $text;
278
    }
279
280
}
281