Completed
Push — master ( 9ad54b...895d73 )
by Federico
03:21
created

Attributes::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Jade\Lexer;
4
5
use Jade\Compiler\CommonUtils;
6
7
/**
8
 * Class Jade\Lexer\Attributes.
9
 */
10
class Attributes
11
{
12
    protected $token;
13
14
    public function __construct($token = null)
15
    {
16
        $this->token = $token;
17
    }
18
19
    protected function parseSpace($states, $escapedAttribute, &$key, &$val, $char, $previousNonBlankChar, $nextChar)
20
    {
21
        if (
22
            in_array($states->current(), array('expr', 'array', 'string', 'object')) ||
23
            (
24
                ($char === ' ' || $char === "\t") &&
25
                (
26
                    !preg_match('/^[a-zA-Z0-9_\\x7f-\\xff"\'\\]\\)\\}]$/', $previousNonBlankChar) ||
27
                    !preg_match('/^[a-zA-Z0-9_]$/', $nextChar)
28
                )
29
            )
30
        ) {
31
            $val .= $char;
32
33
            return;
34
        }
35
36
        $states->push('key');
37
        $val = trim($val);
38
        $key = trim($key);
39
40
        if (empty($key)) {
41
            return;
42
        }
43
44
        $key = preg_replace(
45
            array('/^[\'\"]|[\'\"]$/', '/\!/'), '', $key
46
        );
47
        $this->token->escaped[$key] = $escapedAttribute;
48
49
        $this->token->attributes[$key] = ('' === $val) ? true : $this->interpolate($val);
50
51
        $key = '';
52
        $val = '';
53
    }
54
55
    protected function replaceInterpolationsInStrings($match)
56
    {
57
        $quote = $match[1];
58
59
        return str_replace('\\#{', '#{', preg_replace_callback('/(?<!\\\\)#{([^}]+)}/', function ($match) use ($quote) {
60
            return $quote . ' . ' . CommonUtils::addDollarIfNeeded(preg_replace_callback(
61
                    '/(?<![a-zA-Z0-9_\$])(\$?[a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)(?![a-zA-Z0-9_])/',
62
                    function ($match) {
63
                        return CommonUtils::getGetter($match[1], $match[2]);
64
                    },
65
                    $match[1]
66
                )) . ' . ' . $quote;
67
        }, $match[0]));
68
    }
69
70
    protected function interpolate($attr)
71
    {
72
        return preg_replace_callback('/([\'"]).*?(?<!\\\\)(?:\\\\\\\\)*\1/', array($this, 'replaceInterpolationsInStrings'), $attr);
73
    }
74
75
    protected function parseEqual($states, &$escapedAttribute, &$key, &$val, $char, $previousChar)
76
    {
77
        switch ($states->current()) {
78
            case 'key char':
79
                $key .= $char;
80
                break;
81
82
            case 'val':
83
            case 'expr':
84
            case 'array':
85
            case 'string':
86
            case 'object':
87
                $val .= $char;
88
                break;
89
90
            default:
91
                $escapedAttribute = '!' !== $previousChar;
92
                $states->push('val');
93
        }
94
    }
95
96
    protected function parsePairs($states, $char, &$val)
97
    {
98
        switch ($char) {
99
            case '(':
100
                $states->pushFor('expr', 'val', 'expr');
101
                break;
102
103
            case ')':
104
                $states->popFor('val', 'expr');
105
                break;
106
107
            case '{':
108
                $states->pushFor('object', 'val');
109
                break;
110
111
            case '}':
112
                $states->popFor('object');
113
                break;
114
115
            case '[':
116
                $states->pushFor('array', 'val');
117
                break;
118
119
            case ']':
120
                $states->popFor('array');
121
                break;
122
123
            default:
124
                return false;
125
        }
126
        $val .= $char;
127
128
        return true;
129
    }
130
131
    protected function parseString(&$states, &$key, &$val, &$quote, $char)
132
    {
133
        if (($char === '"' || $char === "'") && !CommonUtils::escapedEnd($val)) {
134
            $stringParser = new StringAttribute($char);
135
            $stringParser->parse($states, $val, $quote);
136
137
            return;
138
        }
139
        ${in_array($states->current(), array('key', 'key char')) ? 'key' : 'val'} .= $char;
140
    }
141
142
    public function parseChar($char, &$nextChar, &$key, &$val, &$quote, $states, &$escapedAttribute, &$previousChar, &$previousNonBlankChar)
143
    {
144
        if ($this->parsePairs($states, $char, $val)) {
145
            return;
146
        }
147
148
        switch ($char) {
149
            case ',':
150
            case "\n":
151
            case "\t":
152
            case ' ':
153
                $this->parseSpace($states, $escapedAttribute, $key, $val, $char, $previousNonBlankChar, $nextChar);
154
                break;
155
156
            case '=':
157
                $this->parseEqual($states, $escapedAttribute, $key, $val, $char, $previousChar);
158
                break;
159
160
            default:
161
                $this->parseString($states, $key, $val, $quote, $char);
162
        }
163
    }
164
165
    protected function getParseFunction(&$key, &$val, &$quote, $states, &$escapedAttribute, &$previousChar, &$previousNonBlankChar, $parser)
166
    {
167
        return function ($char, $nextChar = '') use (&$key, &$val, &$quote, $states, &$escapedAttribute, &$previousChar, &$previousNonBlankChar, $parser) {
168
            $parser->parseChar($char, $nextChar, $key, $val, $quote, $states, $escapedAttribute, $previousChar, $previousNonBlankChar);
169
            $previousChar = $char;
170
            if (trim($char) !== '') {
171
                $previousNonBlankChar = $char;
172
            }
173
        };
174
    }
175
176
    /**
177
     * @return object
178
     */
179
    public function parseWith($str)
180
    {
181
        $parser = $this;
182
183
        $key = '';
184
        $val = '';
185
        $quote = '';
186
        $states = new AttributesState();
187
        $escapedAttribute = '';
188
        $previousChar = '';
189
        $previousNonBlankChar = '';
190
191
        $parse = $this->getParseFunction($key, $val, $quote, $states, $escapedAttribute, $previousChar, $previousNonBlankChar, $parser);
192
193
        for ($i = 0; $i < strlen($str); $i++) {
194
            $parse(substr($str, $i, 1), substr($str, $i + 1, 1));
195
        }
196
197
        $parse(',');
198
    }
199
}
200