@@ -7,7 +7,8 @@ discard block |
||
7 | 7 | abstract class TagVisitor extends Visitor |
8 | 8 | { |
9 | 9 | /** |
10 | - * @param Nodes\Tag $tag |
|
10 | + * @param Tag $tag |
|
11 | + * @param boolean $newLinePrettyPrint |
|
11 | 12 | */ |
12 | 13 | protected function visitTagAttributes(Tag $tag, $newLinePrettyPrint, $close = '>') |
13 | 14 | { |
@@ -25,7 +26,7 @@ discard block |
||
25 | 26 | } |
26 | 27 | |
27 | 28 | /** |
28 | - * @param Nodes\Tag $tag |
|
29 | + * @param Tag $tag |
|
29 | 30 | */ |
30 | 31 | protected function initTagName(Tag $tag) |
31 | 32 | { |
@@ -47,7 +48,7 @@ discard block |
||
47 | 48 | } |
48 | 49 | |
49 | 50 | /** |
50 | - * @param Nodes\Tag $tag |
|
51 | + * @param Tag $tag |
|
51 | 52 | */ |
52 | 53 | protected function visitTagContents(Tag $tag) |
53 | 54 | { |
@@ -64,7 +65,7 @@ discard block |
||
64 | 65 | } |
65 | 66 | |
66 | 67 | /** |
67 | - * @param Nodes\Tag $tag |
|
68 | + * @param Tag $tag |
|
68 | 69 | */ |
69 | 70 | protected function compileTag(Tag $tag) |
70 | 71 | { |
@@ -78,7 +79,7 @@ discard block |
||
78 | 79 | } |
79 | 80 | |
80 | 81 | /** |
81 | - * @param Nodes\Tag $tag |
|
82 | + * @param Tag $tag |
|
82 | 83 | */ |
83 | 84 | protected function visitTag(Tag $tag) |
84 | 85 | { |
@@ -6,95 +6,95 @@ |
||
6 | 6 | |
7 | 7 | abstract class TagVisitor extends Visitor |
8 | 8 | { |
9 | - /** |
|
10 | - * @param Nodes\Tag $tag |
|
11 | - */ |
|
12 | - protected function visitTagAttributes(Tag $tag, $newLinePrettyPrint, $close = '>') |
|
13 | - { |
|
14 | - $open = '<' . $tag->name; |
|
15 | - |
|
16 | - if (count($tag->attributes)) { |
|
17 | - $this->buffer($this->indent() . $open, false); |
|
18 | - $this->visitAttributes($tag->attributes); |
|
19 | - $this->buffer($this->getClassesDisplayCode() . $close . $this->newline(), false); |
|
20 | - |
|
21 | - return; |
|
22 | - } |
|
23 | - |
|
24 | - $this->buffer($open . $close, $newLinePrettyPrint ? null : false); |
|
25 | - } |
|
26 | - |
|
27 | - /** |
|
28 | - * @param Nodes\Tag $tag |
|
29 | - */ |
|
30 | - protected function initTagName(Tag $tag) |
|
31 | - { |
|
32 | - if (isset($tag->buffer)) { |
|
33 | - if (preg_match('`^[a-z][a-zA-Z0-9]+(?!\()`', $tag->name)) { |
|
34 | - $tag->name = '$' . $tag->name; |
|
35 | - } |
|
36 | - $tag->name = trim($this->createCode('echo ' . $tag->name . ';')); |
|
37 | - } |
|
38 | - } |
|
39 | - |
|
40 | - protected function trimLastLine() |
|
41 | - { |
|
42 | - $key = count($this->buffer) - 1; |
|
43 | - $this->buffer[$key] = substr($this->buffer[$key], 0, -1); |
|
44 | - if ($this->prettyprint && substr($this->buffer[$key], -1) === ' ') { |
|
45 | - $this->buffer[$key] = substr($this->buffer[$key], 0, -1); |
|
46 | - } |
|
47 | - } |
|
48 | - |
|
49 | - /** |
|
50 | - * @param Nodes\Tag $tag |
|
51 | - */ |
|
52 | - protected function visitTagContents(Tag $tag) |
|
53 | - { |
|
54 | - $inc = $tag->keepWhiteSpaces() ? -$this->indents : 1; |
|
55 | - $this->indents += $inc; |
|
56 | - if (isset($tag->code)) { |
|
57 | - $this->visitCode($tag->code); |
|
58 | - } |
|
59 | - $this->visit($tag->block); |
|
60 | - if ($tag->keepWhiteSpaces() && substr(end($this->buffer), -1) === "\n") { |
|
61 | - $this->trimLastLine(); |
|
62 | - } |
|
63 | - $this->indents -= $inc; |
|
64 | - } |
|
65 | - |
|
66 | - /** |
|
67 | - * @param Nodes\Tag $tag |
|
68 | - */ |
|
69 | - protected function compileTag(Tag $tag) |
|
70 | - { |
|
71 | - $selfClosing = (in_array(strtolower($tag->name), $this->selfClosing) || $tag->selfClosing) && !$this->xml; |
|
72 | - $this->visitTagAttributes($tag, !$tag->keepWhiteSpaces() && $this->prettyprint, (!$selfClosing || $this->terse) ? '>' : ' />'); |
|
73 | - |
|
74 | - if (!$selfClosing) { |
|
75 | - $this->visitTagContents($tag); |
|
76 | - $this->buffer('</' . $tag->name . '>', $tag->keepWhiteSpaces() ? false : null); |
|
77 | - } |
|
78 | - } |
|
79 | - |
|
80 | - /** |
|
81 | - * @param Nodes\Tag $tag |
|
82 | - */ |
|
83 | - protected function visitTag(Tag $tag) |
|
84 | - { |
|
85 | - $this->initTagName($tag); |
|
86 | - |
|
87 | - $insidePrettyprint = !$tag->canInline() && $this->prettyprint && !$tag->isInline(); |
|
88 | - $prettyprint = $tag->keepWhiteSpaces() || $insidePrettyprint; |
|
89 | - |
|
90 | - if ($this->prettyprint && !$insidePrettyprint) { |
|
91 | - $this->buffer[] = $this->indent(); |
|
92 | - } |
|
93 | - |
|
94 | - $this->tempPrettyPrint($prettyprint, 'compileTag', $tag); |
|
95 | - |
|
96 | - if (!$prettyprint && $this->prettyprint && !$tag->isInline()) { |
|
97 | - $this->buffer[] = $this->newline(); |
|
98 | - } |
|
99 | - } |
|
9 | + /** |
|
10 | + * @param Nodes\Tag $tag |
|
11 | + */ |
|
12 | + protected function visitTagAttributes(Tag $tag, $newLinePrettyPrint, $close = '>') |
|
13 | + { |
|
14 | + $open = '<' . $tag->name; |
|
15 | + |
|
16 | + if (count($tag->attributes)) { |
|
17 | + $this->buffer($this->indent() . $open, false); |
|
18 | + $this->visitAttributes($tag->attributes); |
|
19 | + $this->buffer($this->getClassesDisplayCode() . $close . $this->newline(), false); |
|
20 | + |
|
21 | + return; |
|
22 | + } |
|
23 | + |
|
24 | + $this->buffer($open . $close, $newLinePrettyPrint ? null : false); |
|
25 | + } |
|
26 | + |
|
27 | + /** |
|
28 | + * @param Nodes\Tag $tag |
|
29 | + */ |
|
30 | + protected function initTagName(Tag $tag) |
|
31 | + { |
|
32 | + if (isset($tag->buffer)) { |
|
33 | + if (preg_match('`^[a-z][a-zA-Z0-9]+(?!\()`', $tag->name)) { |
|
34 | + $tag->name = '$' . $tag->name; |
|
35 | + } |
|
36 | + $tag->name = trim($this->createCode('echo ' . $tag->name . ';')); |
|
37 | + } |
|
38 | + } |
|
39 | + |
|
40 | + protected function trimLastLine() |
|
41 | + { |
|
42 | + $key = count($this->buffer) - 1; |
|
43 | + $this->buffer[$key] = substr($this->buffer[$key], 0, -1); |
|
44 | + if ($this->prettyprint && substr($this->buffer[$key], -1) === ' ') { |
|
45 | + $this->buffer[$key] = substr($this->buffer[$key], 0, -1); |
|
46 | + } |
|
47 | + } |
|
48 | + |
|
49 | + /** |
|
50 | + * @param Nodes\Tag $tag |
|
51 | + */ |
|
52 | + protected function visitTagContents(Tag $tag) |
|
53 | + { |
|
54 | + $inc = $tag->keepWhiteSpaces() ? -$this->indents : 1; |
|
55 | + $this->indents += $inc; |
|
56 | + if (isset($tag->code)) { |
|
57 | + $this->visitCode($tag->code); |
|
58 | + } |
|
59 | + $this->visit($tag->block); |
|
60 | + if ($tag->keepWhiteSpaces() && substr(end($this->buffer), -1) === "\n") { |
|
61 | + $this->trimLastLine(); |
|
62 | + } |
|
63 | + $this->indents -= $inc; |
|
64 | + } |
|
65 | + |
|
66 | + /** |
|
67 | + * @param Nodes\Tag $tag |
|
68 | + */ |
|
69 | + protected function compileTag(Tag $tag) |
|
70 | + { |
|
71 | + $selfClosing = (in_array(strtolower($tag->name), $this->selfClosing) || $tag->selfClosing) && !$this->xml; |
|
72 | + $this->visitTagAttributes($tag, !$tag->keepWhiteSpaces() && $this->prettyprint, (!$selfClosing || $this->terse) ? '>' : ' />'); |
|
73 | + |
|
74 | + if (!$selfClosing) { |
|
75 | + $this->visitTagContents($tag); |
|
76 | + $this->buffer('</' . $tag->name . '>', $tag->keepWhiteSpaces() ? false : null); |
|
77 | + } |
|
78 | + } |
|
79 | + |
|
80 | + /** |
|
81 | + * @param Nodes\Tag $tag |
|
82 | + */ |
|
83 | + protected function visitTag(Tag $tag) |
|
84 | + { |
|
85 | + $this->initTagName($tag); |
|
86 | + |
|
87 | + $insidePrettyprint = !$tag->canInline() && $this->prettyprint && !$tag->isInline(); |
|
88 | + $prettyprint = $tag->keepWhiteSpaces() || $insidePrettyprint; |
|
89 | + |
|
90 | + if ($this->prettyprint && !$insidePrettyprint) { |
|
91 | + $this->buffer[] = $this->indent(); |
|
92 | + } |
|
93 | + |
|
94 | + $this->tempPrettyPrint($prettyprint, 'compileTag', $tag); |
|
95 | + |
|
96 | + if (!$prettyprint && $this->prettyprint && !$tag->isInline()) { |
|
97 | + $this->buffer[] = $this->newline(); |
|
98 | + } |
|
99 | + } |
|
100 | 100 | } |
@@ -11,17 +11,17 @@ discard block |
||
11 | 11 | */ |
12 | 12 | protected function visitTagAttributes(Tag $tag, $newLinePrettyPrint, $close = '>') |
13 | 13 | { |
14 | - $open = '<' . $tag->name; |
|
14 | + $open = '<'.$tag->name; |
|
15 | 15 | |
16 | 16 | if (count($tag->attributes)) { |
17 | - $this->buffer($this->indent() . $open, false); |
|
17 | + $this->buffer($this->indent().$open, false); |
|
18 | 18 | $this->visitAttributes($tag->attributes); |
19 | - $this->buffer($this->getClassesDisplayCode() . $close . $this->newline(), false); |
|
19 | + $this->buffer($this->getClassesDisplayCode().$close.$this->newline(), false); |
|
20 | 20 | |
21 | 21 | return; |
22 | 22 | } |
23 | 23 | |
24 | - $this->buffer($open . $close, $newLinePrettyPrint ? null : false); |
|
24 | + $this->buffer($open.$close, $newLinePrettyPrint ? null : false); |
|
25 | 25 | } |
26 | 26 | |
27 | 27 | /** |
@@ -31,9 +31,9 @@ discard block |
||
31 | 31 | { |
32 | 32 | if (isset($tag->buffer)) { |
33 | 33 | if (preg_match('`^[a-z][a-zA-Z0-9]+(?!\()`', $tag->name)) { |
34 | - $tag->name = '$' . $tag->name; |
|
34 | + $tag->name = '$'.$tag->name; |
|
35 | 35 | } |
36 | - $tag->name = trim($this->createCode('echo ' . $tag->name . ';')); |
|
36 | + $tag->name = trim($this->createCode('echo '.$tag->name.';')); |
|
37 | 37 | } |
38 | 38 | } |
39 | 39 | |
@@ -73,7 +73,7 @@ discard block |
||
73 | 73 | |
74 | 74 | if (!$selfClosing) { |
75 | 75 | $this->visitTagContents($tag); |
76 | - $this->buffer('</' . $tag->name . '>', $tag->keepWhiteSpaces() ? false : null); |
|
76 | + $this->buffer('</'.$tag->name.'>', $tag->keepWhiteSpaces() ? false : null); |
|
77 | 77 | } |
78 | 78 | } |
79 | 79 |
@@ -11,7 +11,7 @@ discard block |
||
11 | 11 | abstract class Visitor extends KeywordsCompiler |
12 | 12 | { |
13 | 13 | /** |
14 | - * @param Nodes\Node $node |
|
14 | + * @param Node $node |
|
15 | 15 | * |
16 | 16 | * @return array |
17 | 17 | */ |
@@ -23,7 +23,7 @@ discard block |
||
23 | 23 | } |
24 | 24 | |
25 | 25 | /** |
26 | - * @param Nodes\Node $node |
|
26 | + * @param Node $node |
|
27 | 27 | * |
28 | 28 | * @return mixed |
29 | 29 | */ |
@@ -56,7 +56,7 @@ discard block |
||
56 | 56 | } |
57 | 57 | |
58 | 58 | /** |
59 | - * @param Nodes\Literal $node |
|
59 | + * @param Literal $node |
|
60 | 60 | */ |
61 | 61 | protected function visitLiteral(Literal $node) |
62 | 62 | { |
@@ -65,7 +65,7 @@ discard block |
||
65 | 65 | } |
66 | 66 | |
67 | 67 | /** |
68 | - * @param Nodes\Block $block |
|
68 | + * @param Block $block |
|
69 | 69 | */ |
70 | 70 | protected function visitBlock(Block $block) |
71 | 71 | { |
@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | } |
76 | 76 | |
77 | 77 | /** |
78 | - * @param Nodes\Doctype $doctype |
|
78 | + * @param Doctype $doctype |
|
79 | 79 | * |
80 | 80 | * @throws \Exception |
81 | 81 | */ |
@@ -97,7 +97,6 @@ discard block |
||
97 | 97 | } |
98 | 98 | |
99 | 99 | /** |
100 | - * @param Nodes\Mixin $mixin |
|
101 | 100 | */ |
102 | 101 | protected function visitMixinBlock() |
103 | 102 | { |
@@ -119,7 +118,7 @@ discard block |
||
119 | 118 | } |
120 | 119 | |
121 | 120 | /** |
122 | - * @param Nodes\Comment $comment |
|
121 | + * @param Comment $comment |
|
123 | 122 | */ |
124 | 123 | protected function visitComment(Comment $comment) |
125 | 124 | { |
@@ -10,129 +10,129 @@ |
||
10 | 10 | |
11 | 11 | abstract class Visitor extends KeywordsCompiler |
12 | 12 | { |
13 | - /** |
|
14 | - * @param Nodes\Node $node |
|
15 | - * |
|
16 | - * @return array |
|
17 | - */ |
|
18 | - public function visit(Node $node) |
|
19 | - { |
|
20 | - $this->visitNode($node); |
|
21 | - |
|
22 | - return $this->buffer; |
|
23 | - } |
|
24 | - |
|
25 | - /** |
|
26 | - * @param Nodes\Node $node |
|
27 | - * |
|
28 | - * @return mixed |
|
29 | - */ |
|
30 | - protected function visitNode(Node $node) |
|
31 | - { |
|
32 | - $fqn = get_class($node); |
|
33 | - $parts = explode('\\', $fqn); |
|
34 | - $name = strtolower(end($parts)); |
|
35 | - $method = 'visit' . ucfirst($name); |
|
36 | - |
|
37 | - try { |
|
38 | - return $this->$method($node); |
|
39 | - } catch (\ErrorException $e) { |
|
40 | - if (!in_array($e->getCode(), array(8, 33))) { |
|
41 | - throw $e; |
|
42 | - } |
|
43 | - |
|
44 | - throw new \ErrorException( |
|
45 | - 'Error on the ' . $name . |
|
46 | - (isset($node->name) ? ' "' . $node->name . '"' : '') . |
|
47 | - ($this->filename ? ' in ' . $this->filename : '') . |
|
48 | - ' line ' . $node->line . ":\n" . $e->getMessage(), |
|
49 | - 34, |
|
50 | - 1, |
|
51 | - __FILE__, |
|
52 | - __LINE__, |
|
53 | - $e |
|
54 | - ); |
|
55 | - } |
|
56 | - } |
|
57 | - |
|
58 | - /** |
|
59 | - * @param Nodes\Literal $node |
|
60 | - */ |
|
61 | - protected function visitLiteral(Literal $node) |
|
62 | - { |
|
63 | - $str = preg_replace('/\\n/', '\\\\n', $node->string); |
|
64 | - $this->buffer($str); |
|
65 | - } |
|
66 | - |
|
67 | - /** |
|
68 | - * @param Nodes\Block $block |
|
69 | - */ |
|
70 | - protected function visitBlock(Block $block) |
|
71 | - { |
|
72 | - foreach ($block->nodes as $n) { |
|
73 | - $this->visit($n); |
|
74 | - } |
|
75 | - } |
|
76 | - |
|
77 | - /** |
|
78 | - * @param Nodes\Doctype $doctype |
|
79 | - * |
|
80 | - * @throws \Exception |
|
81 | - */ |
|
82 | - protected function visitDoctype(Doctype $doctype = null) |
|
83 | - { |
|
84 | - $doc = (empty($doctype->value) || $doctype === null || !isset($doctype->value)) |
|
85 | - ? 'default' |
|
86 | - : strtolower($doctype->value); |
|
87 | - |
|
88 | - $str = isset($this->doctypes[$doc]) |
|
89 | - ? $this->doctypes[$doc] |
|
90 | - : "<!DOCTYPE {$doc}>"; |
|
91 | - |
|
92 | - $this->buffer($str . $this->newline()); |
|
93 | - |
|
94 | - $this->terse = (strtolower($str) === '<!doctype html>'); |
|
95 | - |
|
96 | - $this->xml = ($doc === 'xml'); |
|
97 | - } |
|
98 | - |
|
99 | - /** |
|
100 | - * @param Nodes\Mixin $mixin |
|
101 | - */ |
|
102 | - protected function visitMixinBlock() |
|
103 | - { |
|
104 | - $name = var_export($this->visitedMixin->name, true); |
|
105 | - |
|
106 | - $code = $this->restrictedScope |
|
107 | - ? "\\Jade\\Compiler::callMixinBlock($name, \$attributes);" |
|
108 | - : "\$__varHandler = get_defined_vars(); \\Jade\\Compiler::callMixinBlockWithVars($name, \$__varHandler, \$attributes); extract(array_diff_key(\$__varHandler, array('__varHandler' => 1)));"; |
|
109 | - |
|
110 | - $this->buffer($this->createCode($code)); |
|
111 | - } |
|
112 | - |
|
113 | - /** |
|
114 | - * @param Nodes\Text $text |
|
115 | - */ |
|
116 | - protected function visitText($text) |
|
117 | - { |
|
118 | - $this->buffer($this->interpolate($text->value)); |
|
119 | - } |
|
120 | - |
|
121 | - /** |
|
122 | - * @param Nodes\Comment $comment |
|
123 | - */ |
|
124 | - protected function visitComment(Comment $comment) |
|
125 | - { |
|
126 | - if ($comment->buffer) { |
|
127 | - $this->buffer('<!--' . $comment->value . '-->'); |
|
128 | - } |
|
129 | - } |
|
130 | - |
|
131 | - /** |
|
132 | - * @param array $attributes |
|
133 | - */ |
|
134 | - protected function visitAttributes($attributes) |
|
135 | - { |
|
136 | - $this->tempPrettyPrint(false, 'compileAttributes', $attributes); |
|
137 | - } |
|
13 | + /** |
|
14 | + * @param Nodes\Node $node |
|
15 | + * |
|
16 | + * @return array |
|
17 | + */ |
|
18 | + public function visit(Node $node) |
|
19 | + { |
|
20 | + $this->visitNode($node); |
|
21 | + |
|
22 | + return $this->buffer; |
|
23 | + } |
|
24 | + |
|
25 | + /** |
|
26 | + * @param Nodes\Node $node |
|
27 | + * |
|
28 | + * @return mixed |
|
29 | + */ |
|
30 | + protected function visitNode(Node $node) |
|
31 | + { |
|
32 | + $fqn = get_class($node); |
|
33 | + $parts = explode('\\', $fqn); |
|
34 | + $name = strtolower(end($parts)); |
|
35 | + $method = 'visit' . ucfirst($name); |
|
36 | + |
|
37 | + try { |
|
38 | + return $this->$method($node); |
|
39 | + } catch (\ErrorException $e) { |
|
40 | + if (!in_array($e->getCode(), array(8, 33))) { |
|
41 | + throw $e; |
|
42 | + } |
|
43 | + |
|
44 | + throw new \ErrorException( |
|
45 | + 'Error on the ' . $name . |
|
46 | + (isset($node->name) ? ' "' . $node->name . '"' : '') . |
|
47 | + ($this->filename ? ' in ' . $this->filename : '') . |
|
48 | + ' line ' . $node->line . ":\n" . $e->getMessage(), |
|
49 | + 34, |
|
50 | + 1, |
|
51 | + __FILE__, |
|
52 | + __LINE__, |
|
53 | + $e |
|
54 | + ); |
|
55 | + } |
|
56 | + } |
|
57 | + |
|
58 | + /** |
|
59 | + * @param Nodes\Literal $node |
|
60 | + */ |
|
61 | + protected function visitLiteral(Literal $node) |
|
62 | + { |
|
63 | + $str = preg_replace('/\\n/', '\\\\n', $node->string); |
|
64 | + $this->buffer($str); |
|
65 | + } |
|
66 | + |
|
67 | + /** |
|
68 | + * @param Nodes\Block $block |
|
69 | + */ |
|
70 | + protected function visitBlock(Block $block) |
|
71 | + { |
|
72 | + foreach ($block->nodes as $n) { |
|
73 | + $this->visit($n); |
|
74 | + } |
|
75 | + } |
|
76 | + |
|
77 | + /** |
|
78 | + * @param Nodes\Doctype $doctype |
|
79 | + * |
|
80 | + * @throws \Exception |
|
81 | + */ |
|
82 | + protected function visitDoctype(Doctype $doctype = null) |
|
83 | + { |
|
84 | + $doc = (empty($doctype->value) || $doctype === null || !isset($doctype->value)) |
|
85 | + ? 'default' |
|
86 | + : strtolower($doctype->value); |
|
87 | + |
|
88 | + $str = isset($this->doctypes[$doc]) |
|
89 | + ? $this->doctypes[$doc] |
|
90 | + : "<!DOCTYPE {$doc}>"; |
|
91 | + |
|
92 | + $this->buffer($str . $this->newline()); |
|
93 | + |
|
94 | + $this->terse = (strtolower($str) === '<!doctype html>'); |
|
95 | + |
|
96 | + $this->xml = ($doc === 'xml'); |
|
97 | + } |
|
98 | + |
|
99 | + /** |
|
100 | + * @param Nodes\Mixin $mixin |
|
101 | + */ |
|
102 | + protected function visitMixinBlock() |
|
103 | + { |
|
104 | + $name = var_export($this->visitedMixin->name, true); |
|
105 | + |
|
106 | + $code = $this->restrictedScope |
|
107 | + ? "\\Jade\\Compiler::callMixinBlock($name, \$attributes);" |
|
108 | + : "\$__varHandler = get_defined_vars(); \\Jade\\Compiler::callMixinBlockWithVars($name, \$__varHandler, \$attributes); extract(array_diff_key(\$__varHandler, array('__varHandler' => 1)));"; |
|
109 | + |
|
110 | + $this->buffer($this->createCode($code)); |
|
111 | + } |
|
112 | + |
|
113 | + /** |
|
114 | + * @param Nodes\Text $text |
|
115 | + */ |
|
116 | + protected function visitText($text) |
|
117 | + { |
|
118 | + $this->buffer($this->interpolate($text->value)); |
|
119 | + } |
|
120 | + |
|
121 | + /** |
|
122 | + * @param Nodes\Comment $comment |
|
123 | + */ |
|
124 | + protected function visitComment(Comment $comment) |
|
125 | + { |
|
126 | + if ($comment->buffer) { |
|
127 | + $this->buffer('<!--' . $comment->value . '-->'); |
|
128 | + } |
|
129 | + } |
|
130 | + |
|
131 | + /** |
|
132 | + * @param array $attributes |
|
133 | + */ |
|
134 | + protected function visitAttributes($attributes) |
|
135 | + { |
|
136 | + $this->tempPrettyPrint(false, 'compileAttributes', $attributes); |
|
137 | + } |
|
138 | 138 | } |
@@ -32,7 +32,7 @@ discard block |
||
32 | 32 | $fqn = get_class($node); |
33 | 33 | $parts = explode('\\', $fqn); |
34 | 34 | $name = strtolower(end($parts)); |
35 | - $method = 'visit' . ucfirst($name); |
|
35 | + $method = 'visit'.ucfirst($name); |
|
36 | 36 | |
37 | 37 | try { |
38 | 38 | return $this->$method($node); |
@@ -42,10 +42,10 @@ discard block |
||
42 | 42 | } |
43 | 43 | |
44 | 44 | throw new \ErrorException( |
45 | - 'Error on the ' . $name . |
|
46 | - (isset($node->name) ? ' "' . $node->name . '"' : '') . |
|
47 | - ($this->filename ? ' in ' . $this->filename : '') . |
|
48 | - ' line ' . $node->line . ":\n" . $e->getMessage(), |
|
45 | + 'Error on the '.$name. |
|
46 | + (isset($node->name) ? ' "'.$node->name.'"' : ''). |
|
47 | + ($this->filename ? ' in '.$this->filename : ''). |
|
48 | + ' line '.$node->line.":\n".$e->getMessage(), |
|
49 | 49 | 34, |
50 | 50 | 1, |
51 | 51 | __FILE__, |
@@ -89,7 +89,7 @@ discard block |
||
89 | 89 | ? $this->doctypes[$doc] |
90 | 90 | : "<!DOCTYPE {$doc}>"; |
91 | 91 | |
92 | - $this->buffer($str . $this->newline()); |
|
92 | + $this->buffer($str.$this->newline()); |
|
93 | 93 | |
94 | 94 | $this->terse = (strtolower($str) === '<!doctype html>'); |
95 | 95 | |
@@ -124,7 +124,7 @@ discard block |
||
124 | 124 | protected function visitComment(Comment $comment) |
125 | 125 | { |
126 | 126 | if ($comment->buffer) { |
127 | - $this->buffer('<!--' . $comment->value . '-->'); |
|
127 | + $this->buffer('<!--'.$comment->value.'-->'); |
|
128 | 128 | } |
129 | 129 | } |
130 | 130 |
@@ -32,7 +32,7 @@ |
||
32 | 32 | * |
33 | 33 | * @param string $directory the directory to search in pug templates |
34 | 34 | * |
35 | - * @return array count of cached files and error count |
|
35 | + * @return integer[] count of cached files and error count |
|
36 | 36 | */ |
37 | 37 | public function cacheDirectory($directory) |
38 | 38 | { |
@@ -9,35 +9,35 @@ |
||
9 | 9 | */ |
10 | 10 | abstract class Cache extends Filters |
11 | 11 | { |
12 | - /** |
|
13 | - * Get cached input/file a matching cache file exists. |
|
14 | - * Else, render the input, cache it in a file and return it. |
|
15 | - * |
|
16 | - * @param string input |
|
17 | - * |
|
18 | - * @throws \InvalidArgumentException |
|
19 | - * @throws \Exception |
|
20 | - * |
|
21 | - * @return string |
|
22 | - */ |
|
23 | - public function cache($input) |
|
24 | - { |
|
25 | - $cache = new CacheHelper($this); |
|
12 | + /** |
|
13 | + * Get cached input/file a matching cache file exists. |
|
14 | + * Else, render the input, cache it in a file and return it. |
|
15 | + * |
|
16 | + * @param string input |
|
17 | + * |
|
18 | + * @throws \InvalidArgumentException |
|
19 | + * @throws \Exception |
|
20 | + * |
|
21 | + * @return string |
|
22 | + */ |
|
23 | + public function cache($input) |
|
24 | + { |
|
25 | + $cache = new CacheHelper($this); |
|
26 | 26 | |
27 | - return $cache->cache($input); |
|
28 | - } |
|
27 | + return $cache->cache($input); |
|
28 | + } |
|
29 | 29 | |
30 | - /** |
|
31 | - * Scan a directory recursively, compile them and save them into the cache directory. |
|
32 | - * |
|
33 | - * @param string $directory the directory to search in pug templates |
|
34 | - * |
|
35 | - * @return array count of cached files and error count |
|
36 | - */ |
|
37 | - public function cacheDirectory($directory) |
|
38 | - { |
|
39 | - $cache = new CacheHelper($this); |
|
30 | + /** |
|
31 | + * Scan a directory recursively, compile them and save them into the cache directory. |
|
32 | + * |
|
33 | + * @param string $directory the directory to search in pug templates |
|
34 | + * |
|
35 | + * @return array count of cached files and error count |
|
36 | + */ |
|
37 | + public function cacheDirectory($directory) |
|
38 | + { |
|
39 | + $cache = new CacheHelper($this); |
|
40 | 40 | |
41 | - return $cache->cacheDirectory($directory); |
|
42 | - } |
|
41 | + return $cache->cacheDirectory($directory); |
|
42 | + } |
|
43 | 43 | } |
@@ -7,6 +7,9 @@ |
||
7 | 7 | */ |
8 | 8 | abstract class Keywords extends Cache |
9 | 9 | { |
10 | + /** |
|
11 | + * @param string $keyword |
|
12 | + */ |
|
10 | 13 | protected function hasKeyword($keyword) |
11 | 14 | { |
12 | 15 | return $this->hasValidCustomKeywordsOption() && isset($this->options['customKeywords'][$keyword]); |
@@ -7,77 +7,77 @@ |
||
7 | 7 | */ |
8 | 8 | abstract class Keywords extends Cache |
9 | 9 | { |
10 | - protected function hasKeyword($keyword) |
|
11 | - { |
|
12 | - return $this->hasValidCustomKeywordsOption() && isset($this->options['customKeywords'][$keyword]); |
|
13 | - } |
|
10 | + protected function hasKeyword($keyword) |
|
11 | + { |
|
12 | + return $this->hasValidCustomKeywordsOption() && isset($this->options['customKeywords'][$keyword]); |
|
13 | + } |
|
14 | 14 | |
15 | - protected function hasValidCustomKeywordsOption() |
|
16 | - { |
|
17 | - return isset($this->options['customKeywords']) && ( |
|
18 | - is_array($this->options['customKeywords']) || |
|
19 | - $this->options['customKeywords'] instanceof \ArrayAccess |
|
20 | - ); |
|
21 | - } |
|
15 | + protected function hasValidCustomKeywordsOption() |
|
16 | + { |
|
17 | + return isset($this->options['customKeywords']) && ( |
|
18 | + is_array($this->options['customKeywords']) || |
|
19 | + $this->options['customKeywords'] instanceof \ArrayAccess |
|
20 | + ); |
|
21 | + } |
|
22 | 22 | |
23 | - /** |
|
24 | - * Set custom keyword. |
|
25 | - * |
|
26 | - * @param string $keyword the keyword to be found. |
|
27 | - * @param callable $action action to be executed when the keyword is found. |
|
28 | - */ |
|
29 | - public function setKeyword($keyword, $action) |
|
30 | - { |
|
31 | - if (!is_callable($action)) { |
|
32 | - throw new \InvalidArgumentException("Please add a callable action for your keyword $keyword", 30); |
|
33 | - } |
|
23 | + /** |
|
24 | + * Set custom keyword. |
|
25 | + * |
|
26 | + * @param string $keyword the keyword to be found. |
|
27 | + * @param callable $action action to be executed when the keyword is found. |
|
28 | + */ |
|
29 | + public function setKeyword($keyword, $action) |
|
30 | + { |
|
31 | + if (!is_callable($action)) { |
|
32 | + throw new \InvalidArgumentException("Please add a callable action for your keyword $keyword", 30); |
|
33 | + } |
|
34 | 34 | |
35 | - if (!$this->hasValidCustomKeywordsOption()) { |
|
36 | - $this->options['customKeywords'] = array(); |
|
37 | - } |
|
35 | + if (!$this->hasValidCustomKeywordsOption()) { |
|
36 | + $this->options['customKeywords'] = array(); |
|
37 | + } |
|
38 | 38 | |
39 | - $this->options['customKeywords'][$keyword] = $action; |
|
40 | - } |
|
39 | + $this->options['customKeywords'][$keyword] = $action; |
|
40 | + } |
|
41 | 41 | |
42 | - /** |
|
43 | - * Add custom keyword. |
|
44 | - * |
|
45 | - * @param string $keyword the keyword to be found. |
|
46 | - * @param callable $action action to be executed when the keyword is found. |
|
47 | - */ |
|
48 | - public function addKeyword($keyword, $action) |
|
49 | - { |
|
50 | - if ($this->hasKeyword($keyword)) { |
|
51 | - throw new \InvalidArgumentException("The keyword $keyword is already set.", 31); |
|
52 | - } |
|
42 | + /** |
|
43 | + * Add custom keyword. |
|
44 | + * |
|
45 | + * @param string $keyword the keyword to be found. |
|
46 | + * @param callable $action action to be executed when the keyword is found. |
|
47 | + */ |
|
48 | + public function addKeyword($keyword, $action) |
|
49 | + { |
|
50 | + if ($this->hasKeyword($keyword)) { |
|
51 | + throw new \InvalidArgumentException("The keyword $keyword is already set.", 31); |
|
52 | + } |
|
53 | 53 | |
54 | - $this->setKeyword($keyword, $action); |
|
55 | - } |
|
54 | + $this->setKeyword($keyword, $action); |
|
55 | + } |
|
56 | 56 | |
57 | - /** |
|
58 | - * Replace custom keyword. |
|
59 | - * |
|
60 | - * @param string $keyword the keyword to be found. |
|
61 | - * @param callable $action action to be executed when the keyword is found. |
|
62 | - */ |
|
63 | - public function replaceKeyword($keyword, $action) |
|
64 | - { |
|
65 | - if (!$this->hasKeyword($keyword)) { |
|
66 | - throw new \InvalidArgumentException("The keyword $keyword is not set.", 32); |
|
67 | - } |
|
57 | + /** |
|
58 | + * Replace custom keyword. |
|
59 | + * |
|
60 | + * @param string $keyword the keyword to be found. |
|
61 | + * @param callable $action action to be executed when the keyword is found. |
|
62 | + */ |
|
63 | + public function replaceKeyword($keyword, $action) |
|
64 | + { |
|
65 | + if (!$this->hasKeyword($keyword)) { |
|
66 | + throw new \InvalidArgumentException("The keyword $keyword is not set.", 32); |
|
67 | + } |
|
68 | 68 | |
69 | - $this->setKeyword($keyword, $action); |
|
70 | - } |
|
69 | + $this->setKeyword($keyword, $action); |
|
70 | + } |
|
71 | 71 | |
72 | - /** |
|
73 | - * Remove custom keyword. |
|
74 | - * |
|
75 | - * @param string $keyword the keyword to be removed. |
|
76 | - */ |
|
77 | - public function removeKeyword($keyword) |
|
78 | - { |
|
79 | - if ($this->hasKeyword($keyword)) { |
|
80 | - unset($this->options['customKeywords'][$keyword]); |
|
81 | - } |
|
82 | - } |
|
72 | + /** |
|
73 | + * Remove custom keyword. |
|
74 | + * |
|
75 | + * @param string $keyword the keyword to be removed. |
|
76 | + */ |
|
77 | + public function removeKeyword($keyword) |
|
78 | + { |
|
79 | + if ($this->hasKeyword($keyword)) { |
|
80 | + unset($this->options['customKeywords'][$keyword]); |
|
81 | + } |
|
82 | + } |
|
83 | 83 | } |
@@ -18,7 +18,7 @@ |
||
18 | 18 | * @param Filter $node |
19 | 19 | * @param Compiler $compiler |
20 | 20 | * |
21 | - * @return mixed |
|
21 | + * @return string |
|
22 | 22 | */ |
23 | 23 | protected function getNodeString(Filter $node, Compiler $compiler = null) |
24 | 24 | { |
@@ -10,45 +10,45 @@ |
||
10 | 10 | */ |
11 | 11 | abstract class AbstractFilter implements FilterInterface |
12 | 12 | { |
13 | - /** |
|
14 | - * Returns the node string value, line by line. |
|
15 | - * If the compiler is present, that means we need |
|
16 | - * to interpolate line contents. |
|
17 | - * |
|
18 | - * @param Filter $node |
|
19 | - * @param Compiler $compiler |
|
20 | - * |
|
21 | - * @return mixed |
|
22 | - */ |
|
23 | - protected function getNodeString(Filter $node, Compiler $compiler = null) |
|
24 | - { |
|
25 | - return array_reduce($node->block->nodes, function ($result, $line) use ($compiler) { |
|
26 | - return $result . ($compiler |
|
27 | - ? $compiler->interpolate($line->value) |
|
28 | - : $line->value |
|
29 | - ) . "\n"; |
|
30 | - }); |
|
31 | - } |
|
13 | + /** |
|
14 | + * Returns the node string value, line by line. |
|
15 | + * If the compiler is present, that means we need |
|
16 | + * to interpolate line contents. |
|
17 | + * |
|
18 | + * @param Filter $node |
|
19 | + * @param Compiler $compiler |
|
20 | + * |
|
21 | + * @return mixed |
|
22 | + */ |
|
23 | + protected function getNodeString(Filter $node, Compiler $compiler = null) |
|
24 | + { |
|
25 | + return array_reduce($node->block->nodes, function ($result, $line) use ($compiler) { |
|
26 | + return $result . ($compiler |
|
27 | + ? $compiler->interpolate($line->value) |
|
28 | + : $line->value |
|
29 | + ) . "\n"; |
|
30 | + }); |
|
31 | + } |
|
32 | 32 | |
33 | - public function __invoke(Filter $node, Compiler $compiler) |
|
34 | - { |
|
35 | - $nodes = $node->block->nodes; |
|
36 | - $indent = strlen($nodes[0]->value) - strlen(ltrim($nodes[0]->value)); |
|
37 | - $code = ''; |
|
38 | - foreach ($nodes as $line) { |
|
39 | - $code .= substr($compiler->interpolate($line->value), $indent) . "\n"; |
|
40 | - } |
|
33 | + public function __invoke(Filter $node, Compiler $compiler) |
|
34 | + { |
|
35 | + $nodes = $node->block->nodes; |
|
36 | + $indent = strlen($nodes[0]->value) - strlen(ltrim($nodes[0]->value)); |
|
37 | + $code = ''; |
|
38 | + foreach ($nodes as $line) { |
|
39 | + $code .= substr($compiler->interpolate($line->value), $indent) . "\n"; |
|
40 | + } |
|
41 | 41 | |
42 | - if (method_exists($this, 'parse')) { |
|
43 | - $code = $this->parse($code); |
|
44 | - } |
|
42 | + if (method_exists($this, 'parse')) { |
|
43 | + $code = $this->parse($code); |
|
44 | + } |
|
45 | 45 | |
46 | - if (isset($this->tag)) { |
|
47 | - $code = '<' . $this->tag . (isset($this->textType) ? ' type="text/' . $this->textType . '"' : '') . '>' . |
|
48 | - $code . |
|
49 | - '</' . $this->tag . '>'; |
|
50 | - } |
|
46 | + if (isset($this->tag)) { |
|
47 | + $code = '<' . $this->tag . (isset($this->textType) ? ' type="text/' . $this->textType . '"' : '') . '>' . |
|
48 | + $code . |
|
49 | + '</' . $this->tag . '>'; |
|
50 | + } |
|
51 | 51 | |
52 | - return $code; |
|
53 | - } |
|
52 | + return $code; |
|
53 | + } |
|
54 | 54 | } |
@@ -22,11 +22,11 @@ discard block |
||
22 | 22 | */ |
23 | 23 | protected function getNodeString(Filter $node, Compiler $compiler = null) |
24 | 24 | { |
25 | - return array_reduce($node->block->nodes, function ($result, $line) use ($compiler) { |
|
26 | - return $result . ($compiler |
|
25 | + return array_reduce($node->block->nodes, function($result, $line) use ($compiler) { |
|
26 | + return $result.($compiler |
|
27 | 27 | ? $compiler->interpolate($line->value) |
28 | 28 | : $line->value |
29 | - ) . "\n"; |
|
29 | + )."\n"; |
|
30 | 30 | }); |
31 | 31 | } |
32 | 32 | |
@@ -36,7 +36,7 @@ discard block |
||
36 | 36 | $indent = strlen($nodes[0]->value) - strlen(ltrim($nodes[0]->value)); |
37 | 37 | $code = ''; |
38 | 38 | foreach ($nodes as $line) { |
39 | - $code .= substr($compiler->interpolate($line->value), $indent) . "\n"; |
|
39 | + $code .= substr($compiler->interpolate($line->value), $indent)."\n"; |
|
40 | 40 | } |
41 | 41 | |
42 | 42 | if (method_exists($this, 'parse')) { |
@@ -44,9 +44,9 @@ discard block |
||
44 | 44 | } |
45 | 45 | |
46 | 46 | if (isset($this->tag)) { |
47 | - $code = '<' . $this->tag . (isset($this->textType) ? ' type="text/' . $this->textType . '"' : '') . '>' . |
|
48 | - $code . |
|
49 | - '</' . $this->tag . '>'; |
|
47 | + $code = '<'.$this->tag.(isset($this->textType) ? ' type="text/'.$this->textType.'"' : '').'>'. |
|
48 | + $code. |
|
49 | + '</'.$this->tag.'>'; |
|
50 | 50 | } |
51 | 51 | |
52 | 52 | return $code; |
@@ -75,6 +75,7 @@ discard block |
||
75 | 75 | * is missing in the executor include whitelist. |
76 | 76 | * Returns false in any other case. |
77 | 77 | * |
78 | + * @param string $extension |
|
78 | 79 | * @return bool |
79 | 80 | */ |
80 | 81 | protected function whiteListNeeded($extension) |
@@ -125,6 +126,7 @@ discard block |
||
125 | 126 | * Throw a invalid argument exception if the option does not exists. |
126 | 127 | * |
127 | 128 | * @param string name |
129 | + * @param string $name |
|
128 | 130 | * |
129 | 131 | * @throws \InvalidArgumentException |
130 | 132 | */ |
@@ -265,6 +267,7 @@ discard block |
||
265 | 267 | * the possibility to add $scope variables. |
266 | 268 | * |
267 | 269 | * @param string input |
270 | + * @param string $input |
|
268 | 271 | * |
269 | 272 | * @throws \ErrorException |
270 | 273 | * |
@@ -287,7 +290,7 @@ discard block |
||
287 | 290 | * Add a variable or an array of variables to be shared with all templates that will be rendered |
288 | 291 | * by the instance of Pug. |
289 | 292 | * |
290 | - * @param array|string $variables|$key an associatives array of variable names and values, or a |
|
293 | + * @param array|string $variables an associatives array of variable names and values, or a |
|
291 | 294 | * variable name if you wish to sahre only one |
292 | 295 | * @param mixed $value if you pass an array as first argument, the second |
293 | 296 | * argument will be ignored, else it will used as the |
@@ -9,304 +9,304 @@ |
||
9 | 9 | */ |
10 | 10 | class Jade extends Keywords |
11 | 11 | { |
12 | - /** |
|
13 | - * @var string |
|
14 | - */ |
|
15 | - protected $streamName = 'jade'; |
|
16 | - |
|
17 | - /** |
|
18 | - * @var array |
|
19 | - */ |
|
20 | - protected $options = array( |
|
21 | - 'allowMixedIndent' => true, |
|
22 | - 'allowMixinOverride' => true, |
|
23 | - 'cache' => null, |
|
24 | - 'classAttribute' => null, |
|
25 | - 'customKeywords' => array(), |
|
26 | - 'extension' => array('.pug', '.jade'), |
|
27 | - 'filterAutoLoad' => true, |
|
28 | - 'indentChar' => ' ', |
|
29 | - 'indentSize' => 2, |
|
30 | - 'keepBaseName' => false, |
|
31 | - 'keepNullAttributes' => false, |
|
32 | - 'phpSingleLine' => false, |
|
33 | - 'postRender' => null, |
|
34 | - 'preRender' => null, |
|
35 | - 'prettyprint' => false, |
|
36 | - 'restrictedScope' => false, |
|
37 | - 'singleQuote' => false, |
|
38 | - 'stream' => null, |
|
39 | - 'upToDateCheck' => true, |
|
40 | - ); |
|
41 | - |
|
42 | - /** |
|
43 | - * Built-in filters. |
|
44 | - * |
|
45 | - * @var array |
|
46 | - */ |
|
47 | - protected $filters = array( |
|
48 | - 'php' => 'Jade\Filter\Php', |
|
49 | - 'css' => 'Jade\Filter\Css', |
|
50 | - 'cdata' => 'Jade\Filter\Cdata', |
|
51 | - 'escaped' => 'Jade\Filter\Escaped', |
|
52 | - 'javascript' => 'Jade\Filter\Javascript', |
|
53 | - ); |
|
54 | - |
|
55 | - /** |
|
56 | - * @var array |
|
57 | - */ |
|
58 | - protected $sharedVariables = array(); |
|
59 | - |
|
60 | - /** |
|
61 | - * Merge local options with constructor $options. |
|
62 | - * |
|
63 | - * @param array $options |
|
64 | - */ |
|
65 | - public function __construct(array $options = array()) |
|
66 | - { |
|
67 | - if (is_null($this->options['stream'])) { |
|
68 | - $this->options['stream'] = $this->streamName . '.stream'; |
|
69 | - } |
|
70 | - $this->options = array_merge($this->options, $options); |
|
71 | - } |
|
72 | - |
|
73 | - /** |
|
74 | - * Returns true if suhosin extension is loaded and the stream name |
|
75 | - * is missing in the executor include whitelist. |
|
76 | - * Returns false in any other case. |
|
77 | - * |
|
78 | - * @return bool |
|
79 | - */ |
|
80 | - protected function whiteListNeeded($extension) |
|
81 | - { |
|
82 | - return extension_loaded($extension) && |
|
83 | - false === strpos( |
|
84 | - ini_get($extension . '.executor.include.whitelist'), |
|
85 | - $this->options['stream'] |
|
86 | - ); |
|
87 | - } |
|
88 | - |
|
89 | - /** |
|
90 | - * Returns list of requirements in an array identified by keys. |
|
91 | - * For each of them, the value can be true if the requirement is |
|
92 | - * fullfilled, false else. |
|
93 | - * |
|
94 | - * If a requirement name is specified, returns only the matching |
|
95 | - * boolean value for this requirement. |
|
96 | - * |
|
97 | - * @param string name |
|
98 | - * |
|
99 | - * @throws \InvalidArgumentException |
|
100 | - * |
|
101 | - * @return array|bool |
|
102 | - */ |
|
103 | - public function requirements($name = null) |
|
104 | - { |
|
105 | - $requirements = array( |
|
106 | - 'streamWhiteListed' => !$this->whiteListNeeded('suhosin'), |
|
107 | - 'cacheFolderExists' => $this->options['cache'] && is_dir($this->options['cache']), |
|
108 | - 'cacheFolderIsWritable' => $this->options['cache'] && is_writable($this->options['cache']), |
|
109 | - ); |
|
110 | - |
|
111 | - if ($name) { |
|
112 | - if (!isset($requirements[$name])) { |
|
113 | - throw new \InvalidArgumentException($name . ' is not in the requirements list (' . implode(', ', array_keys($requirements)) . ')', 19); |
|
114 | - } |
|
115 | - |
|
116 | - return $requirements[$name]; |
|
117 | - } |
|
118 | - |
|
119 | - return $requirements; |
|
120 | - } |
|
121 | - |
|
122 | - /** |
|
123 | - * Get standard or custom option, return the previously setted value or the default value else. |
|
124 | - * |
|
125 | - * Throw a invalid argument exception if the option does not exists. |
|
126 | - * |
|
127 | - * @param string name |
|
128 | - * |
|
129 | - * @throws \InvalidArgumentException |
|
130 | - */ |
|
131 | - public function getOption($name) |
|
132 | - { |
|
133 | - if (!array_key_exists($name, $this->options)) { |
|
134 | - throw new \InvalidArgumentException("$name is not a valid option name.", 2); |
|
135 | - } |
|
136 | - |
|
137 | - return $this->options[$name]; |
|
138 | - } |
|
139 | - |
|
140 | - /** |
|
141 | - * Set one standard option (listed in $this->options). |
|
142 | - * |
|
143 | - * @param string name |
|
144 | - * @param mixed option value |
|
145 | - * |
|
146 | - * @throws \InvalidArgumentException |
|
147 | - * |
|
148 | - * @return $this |
|
149 | - */ |
|
150 | - public function setOption($name, $value) |
|
151 | - { |
|
152 | - if (!array_key_exists($name, $this->options)) { |
|
153 | - throw new \InvalidArgumentException("$name is not a valid option name.", 3); |
|
154 | - } |
|
155 | - |
|
156 | - $this->options[$name] = $value; |
|
157 | - |
|
158 | - return $this; |
|
159 | - } |
|
160 | - |
|
161 | - /** |
|
162 | - * Set multiple standard options. |
|
163 | - * |
|
164 | - * @param array option list |
|
165 | - * |
|
166 | - * @throws \InvalidArgumentException |
|
167 | - * |
|
168 | - * @return $this |
|
169 | - */ |
|
170 | - public function setOptions($options) |
|
171 | - { |
|
172 | - foreach ($options as $name => $value) { |
|
173 | - $this->setOption($name, $value); |
|
174 | - } |
|
175 | - |
|
176 | - return $this; |
|
177 | - } |
|
178 | - |
|
179 | - /** |
|
180 | - * Set one custom option. |
|
181 | - * |
|
182 | - * @param string name |
|
183 | - * @param mixed option value |
|
184 | - * |
|
185 | - * @return $this |
|
186 | - */ |
|
187 | - public function setCustomOption($name, $value) |
|
188 | - { |
|
189 | - $this->options[$name] = $value; |
|
190 | - |
|
191 | - return $this; |
|
192 | - } |
|
193 | - |
|
194 | - /** |
|
195 | - * Set multiple custom options. |
|
196 | - * |
|
197 | - * @param array options list |
|
198 | - * |
|
199 | - * @return $this |
|
200 | - */ |
|
201 | - public function setCustomOptions(array $options) |
|
202 | - { |
|
203 | - $this->options = array_merge($this->options, $options); |
|
204 | - |
|
205 | - return $this; |
|
206 | - } |
|
207 | - |
|
208 | - /** |
|
209 | - * Compile PHP code from a Pug input or a Pug file. |
|
210 | - * |
|
211 | - * @param string input |
|
212 | - * |
|
213 | - * @throws \Exception |
|
214 | - * |
|
215 | - * @return string |
|
216 | - */ |
|
217 | - public function compile($input) |
|
218 | - { |
|
219 | - $parser = new Parser($input, null, $this->options); |
|
220 | - $compiler = new Compiler($this->options, $this->filters, $parser->getFilename()); |
|
221 | - $php = $compiler->compile($parser->parse()); |
|
222 | - if (version_compare(PHP_VERSION, '7.0.0') < 0) { |
|
223 | - $php = preg_replace_callback('/(' . preg_quote('\\Jade\\Compiler::getPropertyFromAnything', '/') . '\\(((?>[^()]+)|(?-2))*\\))[ \t]*(\\(((?>[^()]+)|(?-2))*\\))/', function ($match) { |
|
224 | - return 'call_user_func(' . $match[1] . ', ' . $match[4] . ')'; |
|
225 | - }, $php); |
|
226 | - } |
|
227 | - $postRender = $this->getOption('postRender'); |
|
228 | - if (is_callable($postRender)) { |
|
229 | - $php = call_user_func($postRender, $php); |
|
230 | - } |
|
231 | - |
|
232 | - return $php; |
|
233 | - } |
|
234 | - |
|
235 | - /** |
|
236 | - * Compile HTML code from a Pug input or a Pug file. |
|
237 | - * |
|
238 | - * @param sring Pug input or file |
|
239 | - * @param array vars to pass to the view |
|
240 | - * |
|
241 | - * @throws \Exception |
|
242 | - * |
|
243 | - * @return string |
|
244 | - */ |
|
245 | - public function render($input, array $vars = array()) |
|
246 | - { |
|
247 | - $file = $this->options['cache'] |
|
248 | - ? $this->cache($input) |
|
249 | - : $this->stream($this->compile($input)); |
|
250 | - |
|
251 | - extract(array_merge($this->sharedVariables, $vars)); |
|
252 | - ob_start(); |
|
253 | - try { |
|
254 | - include $file; |
|
255 | - } catch (\Exception $e) { |
|
256 | - ob_end_clean(); |
|
257 | - throw $e; |
|
258 | - } |
|
259 | - |
|
260 | - return ob_get_clean(); |
|
261 | - } |
|
262 | - |
|
263 | - /** |
|
264 | - * Create a stream wrapper to allow |
|
265 | - * the possibility to add $scope variables. |
|
266 | - * |
|
267 | - * @param string input |
|
268 | - * |
|
269 | - * @throws \ErrorException |
|
270 | - * |
|
271 | - * @return string |
|
272 | - */ |
|
273 | - public function stream($input) |
|
274 | - { |
|
275 | - if ($this->whiteListNeeded('suhosin')) { |
|
276 | - throw new \ErrorException('To run Pug.php on the fly, add "' . $this->options['stream'] . '" to the "suhosin.executor.include.whitelist" settings in your php.ini file.', 4); |
|
277 | - } |
|
278 | - |
|
279 | - if (!in_array($this->options['stream'], stream_get_wrappers())) { |
|
280 | - stream_wrapper_register($this->options['stream'], 'Jade\Stream\Template'); |
|
281 | - } |
|
282 | - |
|
283 | - return $this->options['stream'] . '://data;' . $input; |
|
284 | - } |
|
285 | - |
|
286 | - /** |
|
287 | - * Add a variable or an array of variables to be shared with all templates that will be rendered |
|
288 | - * by the instance of Pug. |
|
289 | - * |
|
290 | - * @param array|string $variables|$key an associatives array of variable names and values, or a |
|
291 | - * variable name if you wish to sahre only one |
|
292 | - * @param mixed $value if you pass an array as first argument, the second |
|
293 | - * argument will be ignored, else it will used as the |
|
294 | - * variable value for the variable name you passed as first |
|
295 | - * argument |
|
296 | - */ |
|
297 | - public function share($variables, $value = null) |
|
298 | - { |
|
299 | - if (!is_array($variables)) { |
|
300 | - $variables = array(strval($variables) => $value); |
|
301 | - } |
|
302 | - $this->sharedVariables = array_merge($this->sharedVariables, $variables); |
|
303 | - } |
|
304 | - |
|
305 | - /** |
|
306 | - * Remove all previously set shared variables. |
|
307 | - */ |
|
308 | - public function resetSharedVariables() |
|
309 | - { |
|
310 | - $this->sharedVariables = array(); |
|
311 | - } |
|
12 | + /** |
|
13 | + * @var string |
|
14 | + */ |
|
15 | + protected $streamName = 'jade'; |
|
16 | + |
|
17 | + /** |
|
18 | + * @var array |
|
19 | + */ |
|
20 | + protected $options = array( |
|
21 | + 'allowMixedIndent' => true, |
|
22 | + 'allowMixinOverride' => true, |
|
23 | + 'cache' => null, |
|
24 | + 'classAttribute' => null, |
|
25 | + 'customKeywords' => array(), |
|
26 | + 'extension' => array('.pug', '.jade'), |
|
27 | + 'filterAutoLoad' => true, |
|
28 | + 'indentChar' => ' ', |
|
29 | + 'indentSize' => 2, |
|
30 | + 'keepBaseName' => false, |
|
31 | + 'keepNullAttributes' => false, |
|
32 | + 'phpSingleLine' => false, |
|
33 | + 'postRender' => null, |
|
34 | + 'preRender' => null, |
|
35 | + 'prettyprint' => false, |
|
36 | + 'restrictedScope' => false, |
|
37 | + 'singleQuote' => false, |
|
38 | + 'stream' => null, |
|
39 | + 'upToDateCheck' => true, |
|
40 | + ); |
|
41 | + |
|
42 | + /** |
|
43 | + * Built-in filters. |
|
44 | + * |
|
45 | + * @var array |
|
46 | + */ |
|
47 | + protected $filters = array( |
|
48 | + 'php' => 'Jade\Filter\Php', |
|
49 | + 'css' => 'Jade\Filter\Css', |
|
50 | + 'cdata' => 'Jade\Filter\Cdata', |
|
51 | + 'escaped' => 'Jade\Filter\Escaped', |
|
52 | + 'javascript' => 'Jade\Filter\Javascript', |
|
53 | + ); |
|
54 | + |
|
55 | + /** |
|
56 | + * @var array |
|
57 | + */ |
|
58 | + protected $sharedVariables = array(); |
|
59 | + |
|
60 | + /** |
|
61 | + * Merge local options with constructor $options. |
|
62 | + * |
|
63 | + * @param array $options |
|
64 | + */ |
|
65 | + public function __construct(array $options = array()) |
|
66 | + { |
|
67 | + if (is_null($this->options['stream'])) { |
|
68 | + $this->options['stream'] = $this->streamName . '.stream'; |
|
69 | + } |
|
70 | + $this->options = array_merge($this->options, $options); |
|
71 | + } |
|
72 | + |
|
73 | + /** |
|
74 | + * Returns true if suhosin extension is loaded and the stream name |
|
75 | + * is missing in the executor include whitelist. |
|
76 | + * Returns false in any other case. |
|
77 | + * |
|
78 | + * @return bool |
|
79 | + */ |
|
80 | + protected function whiteListNeeded($extension) |
|
81 | + { |
|
82 | + return extension_loaded($extension) && |
|
83 | + false === strpos( |
|
84 | + ini_get($extension . '.executor.include.whitelist'), |
|
85 | + $this->options['stream'] |
|
86 | + ); |
|
87 | + } |
|
88 | + |
|
89 | + /** |
|
90 | + * Returns list of requirements in an array identified by keys. |
|
91 | + * For each of them, the value can be true if the requirement is |
|
92 | + * fullfilled, false else. |
|
93 | + * |
|
94 | + * If a requirement name is specified, returns only the matching |
|
95 | + * boolean value for this requirement. |
|
96 | + * |
|
97 | + * @param string name |
|
98 | + * |
|
99 | + * @throws \InvalidArgumentException |
|
100 | + * |
|
101 | + * @return array|bool |
|
102 | + */ |
|
103 | + public function requirements($name = null) |
|
104 | + { |
|
105 | + $requirements = array( |
|
106 | + 'streamWhiteListed' => !$this->whiteListNeeded('suhosin'), |
|
107 | + 'cacheFolderExists' => $this->options['cache'] && is_dir($this->options['cache']), |
|
108 | + 'cacheFolderIsWritable' => $this->options['cache'] && is_writable($this->options['cache']), |
|
109 | + ); |
|
110 | + |
|
111 | + if ($name) { |
|
112 | + if (!isset($requirements[$name])) { |
|
113 | + throw new \InvalidArgumentException($name . ' is not in the requirements list (' . implode(', ', array_keys($requirements)) . ')', 19); |
|
114 | + } |
|
115 | + |
|
116 | + return $requirements[$name]; |
|
117 | + } |
|
118 | + |
|
119 | + return $requirements; |
|
120 | + } |
|
121 | + |
|
122 | + /** |
|
123 | + * Get standard or custom option, return the previously setted value or the default value else. |
|
124 | + * |
|
125 | + * Throw a invalid argument exception if the option does not exists. |
|
126 | + * |
|
127 | + * @param string name |
|
128 | + * |
|
129 | + * @throws \InvalidArgumentException |
|
130 | + */ |
|
131 | + public function getOption($name) |
|
132 | + { |
|
133 | + if (!array_key_exists($name, $this->options)) { |
|
134 | + throw new \InvalidArgumentException("$name is not a valid option name.", 2); |
|
135 | + } |
|
136 | + |
|
137 | + return $this->options[$name]; |
|
138 | + } |
|
139 | + |
|
140 | + /** |
|
141 | + * Set one standard option (listed in $this->options). |
|
142 | + * |
|
143 | + * @param string name |
|
144 | + * @param mixed option value |
|
145 | + * |
|
146 | + * @throws \InvalidArgumentException |
|
147 | + * |
|
148 | + * @return $this |
|
149 | + */ |
|
150 | + public function setOption($name, $value) |
|
151 | + { |
|
152 | + if (!array_key_exists($name, $this->options)) { |
|
153 | + throw new \InvalidArgumentException("$name is not a valid option name.", 3); |
|
154 | + } |
|
155 | + |
|
156 | + $this->options[$name] = $value; |
|
157 | + |
|
158 | + return $this; |
|
159 | + } |
|
160 | + |
|
161 | + /** |
|
162 | + * Set multiple standard options. |
|
163 | + * |
|
164 | + * @param array option list |
|
165 | + * |
|
166 | + * @throws \InvalidArgumentException |
|
167 | + * |
|
168 | + * @return $this |
|
169 | + */ |
|
170 | + public function setOptions($options) |
|
171 | + { |
|
172 | + foreach ($options as $name => $value) { |
|
173 | + $this->setOption($name, $value); |
|
174 | + } |
|
175 | + |
|
176 | + return $this; |
|
177 | + } |
|
178 | + |
|
179 | + /** |
|
180 | + * Set one custom option. |
|
181 | + * |
|
182 | + * @param string name |
|
183 | + * @param mixed option value |
|
184 | + * |
|
185 | + * @return $this |
|
186 | + */ |
|
187 | + public function setCustomOption($name, $value) |
|
188 | + { |
|
189 | + $this->options[$name] = $value; |
|
190 | + |
|
191 | + return $this; |
|
192 | + } |
|
193 | + |
|
194 | + /** |
|
195 | + * Set multiple custom options. |
|
196 | + * |
|
197 | + * @param array options list |
|
198 | + * |
|
199 | + * @return $this |
|
200 | + */ |
|
201 | + public function setCustomOptions(array $options) |
|
202 | + { |
|
203 | + $this->options = array_merge($this->options, $options); |
|
204 | + |
|
205 | + return $this; |
|
206 | + } |
|
207 | + |
|
208 | + /** |
|
209 | + * Compile PHP code from a Pug input or a Pug file. |
|
210 | + * |
|
211 | + * @param string input |
|
212 | + * |
|
213 | + * @throws \Exception |
|
214 | + * |
|
215 | + * @return string |
|
216 | + */ |
|
217 | + public function compile($input) |
|
218 | + { |
|
219 | + $parser = new Parser($input, null, $this->options); |
|
220 | + $compiler = new Compiler($this->options, $this->filters, $parser->getFilename()); |
|
221 | + $php = $compiler->compile($parser->parse()); |
|
222 | + if (version_compare(PHP_VERSION, '7.0.0') < 0) { |
|
223 | + $php = preg_replace_callback('/(' . preg_quote('\\Jade\\Compiler::getPropertyFromAnything', '/') . '\\(((?>[^()]+)|(?-2))*\\))[ \t]*(\\(((?>[^()]+)|(?-2))*\\))/', function ($match) { |
|
224 | + return 'call_user_func(' . $match[1] . ', ' . $match[4] . ')'; |
|
225 | + }, $php); |
|
226 | + } |
|
227 | + $postRender = $this->getOption('postRender'); |
|
228 | + if (is_callable($postRender)) { |
|
229 | + $php = call_user_func($postRender, $php); |
|
230 | + } |
|
231 | + |
|
232 | + return $php; |
|
233 | + } |
|
234 | + |
|
235 | + /** |
|
236 | + * Compile HTML code from a Pug input or a Pug file. |
|
237 | + * |
|
238 | + * @param sring Pug input or file |
|
239 | + * @param array vars to pass to the view |
|
240 | + * |
|
241 | + * @throws \Exception |
|
242 | + * |
|
243 | + * @return string |
|
244 | + */ |
|
245 | + public function render($input, array $vars = array()) |
|
246 | + { |
|
247 | + $file = $this->options['cache'] |
|
248 | + ? $this->cache($input) |
|
249 | + : $this->stream($this->compile($input)); |
|
250 | + |
|
251 | + extract(array_merge($this->sharedVariables, $vars)); |
|
252 | + ob_start(); |
|
253 | + try { |
|
254 | + include $file; |
|
255 | + } catch (\Exception $e) { |
|
256 | + ob_end_clean(); |
|
257 | + throw $e; |
|
258 | + } |
|
259 | + |
|
260 | + return ob_get_clean(); |
|
261 | + } |
|
262 | + |
|
263 | + /** |
|
264 | + * Create a stream wrapper to allow |
|
265 | + * the possibility to add $scope variables. |
|
266 | + * |
|
267 | + * @param string input |
|
268 | + * |
|
269 | + * @throws \ErrorException |
|
270 | + * |
|
271 | + * @return string |
|
272 | + */ |
|
273 | + public function stream($input) |
|
274 | + { |
|
275 | + if ($this->whiteListNeeded('suhosin')) { |
|
276 | + throw new \ErrorException('To run Pug.php on the fly, add "' . $this->options['stream'] . '" to the "suhosin.executor.include.whitelist" settings in your php.ini file.', 4); |
|
277 | + } |
|
278 | + |
|
279 | + if (!in_array($this->options['stream'], stream_get_wrappers())) { |
|
280 | + stream_wrapper_register($this->options['stream'], 'Jade\Stream\Template'); |
|
281 | + } |
|
282 | + |
|
283 | + return $this->options['stream'] . '://data;' . $input; |
|
284 | + } |
|
285 | + |
|
286 | + /** |
|
287 | + * Add a variable or an array of variables to be shared with all templates that will be rendered |
|
288 | + * by the instance of Pug. |
|
289 | + * |
|
290 | + * @param array|string $variables|$key an associatives array of variable names and values, or a |
|
291 | + * variable name if you wish to sahre only one |
|
292 | + * @param mixed $value if you pass an array as first argument, the second |
|
293 | + * argument will be ignored, else it will used as the |
|
294 | + * variable value for the variable name you passed as first |
|
295 | + * argument |
|
296 | + */ |
|
297 | + public function share($variables, $value = null) |
|
298 | + { |
|
299 | + if (!is_array($variables)) { |
|
300 | + $variables = array(strval($variables) => $value); |
|
301 | + } |
|
302 | + $this->sharedVariables = array_merge($this->sharedVariables, $variables); |
|
303 | + } |
|
304 | + |
|
305 | + /** |
|
306 | + * Remove all previously set shared variables. |
|
307 | + */ |
|
308 | + public function resetSharedVariables() |
|
309 | + { |
|
310 | + $this->sharedVariables = array(); |
|
311 | + } |
|
312 | 312 | } |
@@ -65,7 +65,7 @@ discard block |
||
65 | 65 | public function __construct(array $options = array()) |
66 | 66 | { |
67 | 67 | if (is_null($this->options['stream'])) { |
68 | - $this->options['stream'] = $this->streamName . '.stream'; |
|
68 | + $this->options['stream'] = $this->streamName.'.stream'; |
|
69 | 69 | } |
70 | 70 | $this->options = array_merge($this->options, $options); |
71 | 71 | } |
@@ -81,7 +81,7 @@ discard block |
||
81 | 81 | { |
82 | 82 | return extension_loaded($extension) && |
83 | 83 | false === strpos( |
84 | - ini_get($extension . '.executor.include.whitelist'), |
|
84 | + ini_get($extension.'.executor.include.whitelist'), |
|
85 | 85 | $this->options['stream'] |
86 | 86 | ); |
87 | 87 | } |
@@ -110,7 +110,7 @@ discard block |
||
110 | 110 | |
111 | 111 | if ($name) { |
112 | 112 | if (!isset($requirements[$name])) { |
113 | - throw new \InvalidArgumentException($name . ' is not in the requirements list (' . implode(', ', array_keys($requirements)) . ')', 19); |
|
113 | + throw new \InvalidArgumentException($name.' is not in the requirements list ('.implode(', ', array_keys($requirements)).')', 19); |
|
114 | 114 | } |
115 | 115 | |
116 | 116 | return $requirements[$name]; |
@@ -220,8 +220,8 @@ discard block |
||
220 | 220 | $compiler = new Compiler($this->options, $this->filters, $parser->getFilename()); |
221 | 221 | $php = $compiler->compile($parser->parse()); |
222 | 222 | if (version_compare(PHP_VERSION, '7.0.0') < 0) { |
223 | - $php = preg_replace_callback('/(' . preg_quote('\\Jade\\Compiler::getPropertyFromAnything', '/') . '\\(((?>[^()]+)|(?-2))*\\))[ \t]*(\\(((?>[^()]+)|(?-2))*\\))/', function ($match) { |
|
224 | - return 'call_user_func(' . $match[1] . ', ' . $match[4] . ')'; |
|
223 | + $php = preg_replace_callback('/('.preg_quote('\\Jade\\Compiler::getPropertyFromAnything', '/').'\\(((?>[^()]+)|(?-2))*\\))[ \t]*(\\(((?>[^()]+)|(?-2))*\\))/', function($match) { |
|
224 | + return 'call_user_func('.$match[1].', '.$match[4].')'; |
|
225 | 225 | }, $php); |
226 | 226 | } |
227 | 227 | $postRender = $this->getOption('postRender'); |
@@ -273,14 +273,14 @@ discard block |
||
273 | 273 | public function stream($input) |
274 | 274 | { |
275 | 275 | if ($this->whiteListNeeded('suhosin')) { |
276 | - throw new \ErrorException('To run Pug.php on the fly, add "' . $this->options['stream'] . '" to the "suhosin.executor.include.whitelist" settings in your php.ini file.', 4); |
|
276 | + throw new \ErrorException('To run Pug.php on the fly, add "'.$this->options['stream'].'" to the "suhosin.executor.include.whitelist" settings in your php.ini file.', 4); |
|
277 | 277 | } |
278 | 278 | |
279 | 279 | if (!in_array($this->options['stream'], stream_get_wrappers())) { |
280 | 280 | stream_wrapper_register($this->options['stream'], 'Jade\Stream\Template'); |
281 | 281 | } |
282 | 282 | |
283 | - return $this->options['stream'] . '://data;' . $input; |
|
283 | + return $this->options['stream'].'://data;'.$input; |
|
284 | 284 | } |
285 | 285 | |
286 | 286 | /** |
@@ -50,7 +50,7 @@ discard block |
||
50 | 50 | } |
51 | 51 | |
52 | 52 | /** |
53 | - * @param $code |
|
53 | + * @param string $code |
|
54 | 54 | * |
55 | 55 | * @return string |
56 | 56 | */ |
@@ -112,6 +112,10 @@ discard block |
||
112 | 112 | return $this->token('newline'); |
113 | 113 | } |
114 | 114 | |
115 | + /** |
|
116 | + * @param string $firstChar |
|
117 | + * @param integer $indents |
|
118 | + */ |
|
115 | 119 | protected function getTokenFromIndent($firstChar, $indents) |
116 | 120 | { |
117 | 121 | if ($this->length() && $firstChar === "\n") { |
@@ -7,126 +7,126 @@ |
||
7 | 7 | */ |
8 | 8 | abstract class InputHandler |
9 | 9 | { |
10 | - /** |
|
11 | - * @var string |
|
12 | - */ |
|
13 | - public $input; |
|
14 | - |
|
15 | - /** |
|
16 | - * @var array |
|
17 | - */ |
|
18 | - protected $deferred = array(); |
|
19 | - |
|
20 | - /** |
|
21 | - * @var array |
|
22 | - */ |
|
23 | - protected $indentStack = array(); |
|
24 | - |
|
25 | - /** |
|
26 | - * @var array |
|
27 | - */ |
|
28 | - protected $stash = array(); |
|
29 | - |
|
30 | - /** |
|
31 | - * Set lexer input. |
|
32 | - * |
|
33 | - * @param string $input input string |
|
34 | - */ |
|
35 | - public function setInput($input) |
|
36 | - { |
|
37 | - $this->input = preg_replace("/\r\n|\r/", "\n", $input); |
|
38 | - $this->lineno = 1; |
|
39 | - $this->deferred = array(); |
|
40 | - $this->indentStack = array(); |
|
41 | - $this->stash = array(); |
|
42 | - } |
|
43 | - |
|
44 | - /** |
|
45 | - * @return int |
|
46 | - */ |
|
47 | - public function length() |
|
48 | - { |
|
49 | - return strlen($this->input); |
|
50 | - } |
|
51 | - |
|
52 | - /** |
|
53 | - * @param $code |
|
54 | - * |
|
55 | - * @return string |
|
56 | - */ |
|
57 | - protected function normalizeCode($code) |
|
58 | - { |
|
59 | - return $code = (substr($code, -1) === ':' && substr($code, -2, 1) !== ':') |
|
60 | - ? substr($code, 0, -1) |
|
61 | - : $code; |
|
62 | - } |
|
63 | - |
|
64 | - protected function testIndent($indent) |
|
65 | - { |
|
66 | - if (!preg_match('/^' . $indent . '/', substr($this->input, 1), $matches)) { |
|
67 | - return; |
|
68 | - } |
|
69 | - |
|
70 | - if (!isset($this->identRE)) { |
|
71 | - $this->identRE = $indent; |
|
72 | - } |
|
73 | - |
|
74 | - return array( |
|
75 | - "\n" . $matches[0], |
|
76 | - $matches[0], |
|
77 | - ); |
|
78 | - } |
|
79 | - |
|
80 | - protected function getNextIndent() |
|
81 | - { |
|
82 | - if (substr($this->input, 0, 1) !== "\n") { |
|
83 | - return; |
|
84 | - } |
|
85 | - |
|
86 | - $indents = isset($this->identRE) |
|
87 | - ? array($this->identRE) |
|
88 | - : ($this->allowMixedIndent |
|
89 | - ? array('[\\t ]*') |
|
90 | - : array('\\t+', ' *') |
|
91 | - ); |
|
92 | - |
|
93 | - foreach ($indents as $indent) { |
|
94 | - if ($matches = $this->testIndent($indent)) { |
|
95 | - return $matches; |
|
96 | - } |
|
97 | - } |
|
98 | - } |
|
99 | - |
|
100 | - protected function getWhiteSpacesTokens($indents) |
|
101 | - { |
|
102 | - if ($indents && count($this->indentStack) && $indents === $this->indentStack[0]) { |
|
103 | - return $this->token('newline'); |
|
104 | - } |
|
105 | - |
|
106 | - if ($indents) { |
|
107 | - array_unshift($this->indentStack, $indents); |
|
108 | - |
|
109 | - return $this->token('indent', $indents); |
|
110 | - } |
|
111 | - |
|
112 | - return $this->token('newline'); |
|
113 | - } |
|
114 | - |
|
115 | - protected function getTokenFromIndent($firstChar, $indents) |
|
116 | - { |
|
117 | - if ($this->length() && $firstChar === "\n") { |
|
118 | - return $this->token('newline'); |
|
119 | - } |
|
120 | - |
|
121 | - if (count($this->indentStack) && $indents < $this->indentStack[0]) { |
|
122 | - while (count($this->indentStack) && $indents < $this->indentStack[0]) { |
|
123 | - array_push($this->stash, $this->token('outdent')); |
|
124 | - array_shift($this->indentStack); |
|
125 | - } |
|
126 | - |
|
127 | - return array_pop($this->stash); |
|
128 | - } |
|
129 | - |
|
130 | - return $this->getWhiteSpacesTokens($indents); |
|
131 | - } |
|
10 | + /** |
|
11 | + * @var string |
|
12 | + */ |
|
13 | + public $input; |
|
14 | + |
|
15 | + /** |
|
16 | + * @var array |
|
17 | + */ |
|
18 | + protected $deferred = array(); |
|
19 | + |
|
20 | + /** |
|
21 | + * @var array |
|
22 | + */ |
|
23 | + protected $indentStack = array(); |
|
24 | + |
|
25 | + /** |
|
26 | + * @var array |
|
27 | + */ |
|
28 | + protected $stash = array(); |
|
29 | + |
|
30 | + /** |
|
31 | + * Set lexer input. |
|
32 | + * |
|
33 | + * @param string $input input string |
|
34 | + */ |
|
35 | + public function setInput($input) |
|
36 | + { |
|
37 | + $this->input = preg_replace("/\r\n|\r/", "\n", $input); |
|
38 | + $this->lineno = 1; |
|
39 | + $this->deferred = array(); |
|
40 | + $this->indentStack = array(); |
|
41 | + $this->stash = array(); |
|
42 | + } |
|
43 | + |
|
44 | + /** |
|
45 | + * @return int |
|
46 | + */ |
|
47 | + public function length() |
|
48 | + { |
|
49 | + return strlen($this->input); |
|
50 | + } |
|
51 | + |
|
52 | + /** |
|
53 | + * @param $code |
|
54 | + * |
|
55 | + * @return string |
|
56 | + */ |
|
57 | + protected function normalizeCode($code) |
|
58 | + { |
|
59 | + return $code = (substr($code, -1) === ':' && substr($code, -2, 1) !== ':') |
|
60 | + ? substr($code, 0, -1) |
|
61 | + : $code; |
|
62 | + } |
|
63 | + |
|
64 | + protected function testIndent($indent) |
|
65 | + { |
|
66 | + if (!preg_match('/^' . $indent . '/', substr($this->input, 1), $matches)) { |
|
67 | + return; |
|
68 | + } |
|
69 | + |
|
70 | + if (!isset($this->identRE)) { |
|
71 | + $this->identRE = $indent; |
|
72 | + } |
|
73 | + |
|
74 | + return array( |
|
75 | + "\n" . $matches[0], |
|
76 | + $matches[0], |
|
77 | + ); |
|
78 | + } |
|
79 | + |
|
80 | + protected function getNextIndent() |
|
81 | + { |
|
82 | + if (substr($this->input, 0, 1) !== "\n") { |
|
83 | + return; |
|
84 | + } |
|
85 | + |
|
86 | + $indents = isset($this->identRE) |
|
87 | + ? array($this->identRE) |
|
88 | + : ($this->allowMixedIndent |
|
89 | + ? array('[\\t ]*') |
|
90 | + : array('\\t+', ' *') |
|
91 | + ); |
|
92 | + |
|
93 | + foreach ($indents as $indent) { |
|
94 | + if ($matches = $this->testIndent($indent)) { |
|
95 | + return $matches; |
|
96 | + } |
|
97 | + } |
|
98 | + } |
|
99 | + |
|
100 | + protected function getWhiteSpacesTokens($indents) |
|
101 | + { |
|
102 | + if ($indents && count($this->indentStack) && $indents === $this->indentStack[0]) { |
|
103 | + return $this->token('newline'); |
|
104 | + } |
|
105 | + |
|
106 | + if ($indents) { |
|
107 | + array_unshift($this->indentStack, $indents); |
|
108 | + |
|
109 | + return $this->token('indent', $indents); |
|
110 | + } |
|
111 | + |
|
112 | + return $this->token('newline'); |
|
113 | + } |
|
114 | + |
|
115 | + protected function getTokenFromIndent($firstChar, $indents) |
|
116 | + { |
|
117 | + if ($this->length() && $firstChar === "\n") { |
|
118 | + return $this->token('newline'); |
|
119 | + } |
|
120 | + |
|
121 | + if (count($this->indentStack) && $indents < $this->indentStack[0]) { |
|
122 | + while (count($this->indentStack) && $indents < $this->indentStack[0]) { |
|
123 | + array_push($this->stash, $this->token('outdent')); |
|
124 | + array_shift($this->indentStack); |
|
125 | + } |
|
126 | + |
|
127 | + return array_pop($this->stash); |
|
128 | + } |
|
129 | + |
|
130 | + return $this->getWhiteSpacesTokens($indents); |
|
131 | + } |
|
132 | 132 | } |
@@ -63,7 +63,7 @@ discard block |
||
63 | 63 | |
64 | 64 | protected function testIndent($indent) |
65 | 65 | { |
66 | - if (!preg_match('/^' . $indent . '/', substr($this->input, 1), $matches)) { |
|
66 | + if (!preg_match('/^'.$indent.'/', substr($this->input, 1), $matches)) { |
|
67 | 67 | return; |
68 | 68 | } |
69 | 69 | |
@@ -72,7 +72,7 @@ discard block |
||
72 | 72 | } |
73 | 73 | |
74 | 74 | return array( |
75 | - "\n" . $matches[0], |
|
75 | + "\n".$matches[0], |
|
76 | 76 | $matches[0], |
77 | 77 | ); |
78 | 78 | } |
@@ -9,6 +9,8 @@ |
||
9 | 9 | { |
10 | 10 | /** |
11 | 11 | * Helper to create tokens. |
12 | + * @param string $regex |
|
13 | + * @param string $type |
|
12 | 14 | */ |
13 | 15 | protected function scan($regex, $type, $captureIndex = 1) |
14 | 16 | { |
@@ -7,284 +7,284 @@ |
||
7 | 7 | */ |
8 | 8 | abstract class Scanner extends MixinScanner |
9 | 9 | { |
10 | - /** |
|
11 | - * Helper to create tokens. |
|
12 | - */ |
|
13 | - protected function scan($regex, $type, $captureIndex = 1) |
|
14 | - { |
|
15 | - if (preg_match($regex, $this->input, $matches)) { |
|
16 | - $this->consume($matches[0]); |
|
17 | - |
|
18 | - return $this->token($type, isset($matches[$captureIndex]) && strlen($matches[$captureIndex]) > 0 ? $matches[$captureIndex] : ''); |
|
19 | - } |
|
20 | - } |
|
21 | - |
|
22 | - /** |
|
23 | - * Scan comment from input & return it if found. |
|
24 | - * |
|
25 | - * @return object|null |
|
26 | - */ |
|
27 | - protected function scanComment() |
|
28 | - { |
|
29 | - $indent = count($this->indentStack) ? $this->indentStack[0] : 0; |
|
30 | - if (preg_match('/^ *\/\/(-)?([^\n]*(\n+[ \t]{' . ($indent + 1) . ',}[^\n]*)*)/', $this->input, $matches)) { |
|
31 | - $this->consume($matches[0]); |
|
32 | - $value = isset($matches[2]) ? $matches[2] : ''; |
|
33 | - if (isset($matches[3])) { |
|
34 | - $value .= "\n"; |
|
35 | - } |
|
36 | - $token = $this->token('comment', $value); |
|
37 | - $token->buffer = '-' !== $matches[1]; |
|
38 | - |
|
39 | - return $token; |
|
40 | - } |
|
41 | - } |
|
42 | - |
|
43 | - /** |
|
44 | - * @return object |
|
45 | - */ |
|
46 | - protected function scanInterpolation() |
|
47 | - { |
|
48 | - return $this->scan('/^#{(.*?)}/', 'interpolation'); |
|
49 | - } |
|
50 | - |
|
51 | - /** |
|
52 | - * @return object |
|
53 | - */ |
|
54 | - protected function scanTag() |
|
55 | - { |
|
56 | - if (preg_match('/^(\w[-:\w]*)(\/?)/', $this->input, $matches)) { |
|
57 | - $this->consume($matches[0]); |
|
58 | - $name = $matches[1]; |
|
59 | - |
|
60 | - if (':' === substr($name, -1) && ':' !== substr($name, -2, 1)) { |
|
61 | - $name = substr($name, 0, -1); |
|
62 | - $this->defer($this->token(':')); |
|
63 | - |
|
64 | - while (' ' === substr($this->input, 0, 1)) { |
|
65 | - $this->consume(' '); |
|
66 | - } |
|
67 | - } |
|
68 | - |
|
69 | - $token = $this->token('tag', $name); |
|
70 | - $token->selfClosing = ($matches[2] === '/'); |
|
71 | - |
|
72 | - return $token; |
|
73 | - } |
|
74 | - } |
|
75 | - |
|
76 | - /** |
|
77 | - * @return object |
|
78 | - */ |
|
79 | - protected function scanFilter() |
|
80 | - { |
|
81 | - return $this->scan('/^(?<!:):(?!:)(\w+(?:-\w+)*)/', 'filter'); |
|
82 | - } |
|
83 | - |
|
84 | - /** |
|
85 | - * @return object |
|
86 | - */ |
|
87 | - protected function scanDoctype() |
|
88 | - { |
|
89 | - return $this->scan('/^(?:!!!|doctype) *([^\n]+)?/', 'doctype'); |
|
90 | - } |
|
91 | - |
|
92 | - /** |
|
93 | - * @return object |
|
94 | - */ |
|
95 | - protected function scanId() |
|
96 | - { |
|
97 | - return $this->scan('/^#([\w-]+)/', 'id'); |
|
98 | - } |
|
99 | - |
|
100 | - /** |
|
101 | - * @return object |
|
102 | - */ |
|
103 | - protected function scanClassName() |
|
104 | - { |
|
105 | - // http://www.w3.org/TR/CSS21/grammar.html#scanner |
|
106 | - // |
|
107 | - // ident: |
|
108 | - // -?{nmstart}{nmchar}* |
|
109 | - // nmstart: |
|
110 | - // [_a-z]|{nonascii}|{escape} |
|
111 | - // nonascii: |
|
112 | - // [\240-\377] |
|
113 | - // escape: |
|
114 | - // {unicode}|\\[^\r\n\f0-9a-f] |
|
115 | - // unicode: |
|
116 | - // \\{h}{1,6}(\r\n|[ \t\r\n\f])? |
|
117 | - // nmchar: |
|
118 | - // [_a-z0-9-]|{nonascii}|{escape} |
|
119 | - // |
|
120 | - // /^(-?(?!=[0-9-])(?:[_a-z0-9-]|[\240-\377]|\\{h}{1,6}(?:\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f])+)/ |
|
121 | - return $this->scan('/^\.([\w-]+)/', 'class'); |
|
122 | - } |
|
123 | - |
|
124 | - /** |
|
125 | - * @return object |
|
126 | - */ |
|
127 | - protected function scanText() |
|
128 | - { |
|
129 | - return $this->scan('/^(?:\| ?| ?)?([^\n]+)/', 'text'); |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * @return object |
|
134 | - */ |
|
135 | - protected function scanAssignment() |
|
136 | - { |
|
137 | - if (preg_match('/^(\$?\w+) += *([^;\n]+|\'[^\']+\'|"[^"]+")( *;? *)/', $this->input, $matches)) { |
|
138 | - $this->consume($matches[0]); |
|
139 | - |
|
140 | - return $this->token('code', (substr($matches[1], 0, 1) === '$' ? '' : '$') . $matches[1] . '=' . $matches[2]); |
|
141 | - } |
|
142 | - } |
|
143 | - |
|
144 | - /** |
|
145 | - * @return object |
|
146 | - */ |
|
147 | - protected function scanConditional() |
|
148 | - { |
|
149 | - if (preg_match('/^(if|unless|else if|elseif|else|while)\b([^\n]*)/', $this->input, $matches)) { |
|
150 | - $this->consume($matches[0]); |
|
151 | - |
|
152 | - /*switch ($matches[1]) { |
|
10 | + /** |
|
11 | + * Helper to create tokens. |
|
12 | + */ |
|
13 | + protected function scan($regex, $type, $captureIndex = 1) |
|
14 | + { |
|
15 | + if (preg_match($regex, $this->input, $matches)) { |
|
16 | + $this->consume($matches[0]); |
|
17 | + |
|
18 | + return $this->token($type, isset($matches[$captureIndex]) && strlen($matches[$captureIndex]) > 0 ? $matches[$captureIndex] : ''); |
|
19 | + } |
|
20 | + } |
|
21 | + |
|
22 | + /** |
|
23 | + * Scan comment from input & return it if found. |
|
24 | + * |
|
25 | + * @return object|null |
|
26 | + */ |
|
27 | + protected function scanComment() |
|
28 | + { |
|
29 | + $indent = count($this->indentStack) ? $this->indentStack[0] : 0; |
|
30 | + if (preg_match('/^ *\/\/(-)?([^\n]*(\n+[ \t]{' . ($indent + 1) . ',}[^\n]*)*)/', $this->input, $matches)) { |
|
31 | + $this->consume($matches[0]); |
|
32 | + $value = isset($matches[2]) ? $matches[2] : ''; |
|
33 | + if (isset($matches[3])) { |
|
34 | + $value .= "\n"; |
|
35 | + } |
|
36 | + $token = $this->token('comment', $value); |
|
37 | + $token->buffer = '-' !== $matches[1]; |
|
38 | + |
|
39 | + return $token; |
|
40 | + } |
|
41 | + } |
|
42 | + |
|
43 | + /** |
|
44 | + * @return object |
|
45 | + */ |
|
46 | + protected function scanInterpolation() |
|
47 | + { |
|
48 | + return $this->scan('/^#{(.*?)}/', 'interpolation'); |
|
49 | + } |
|
50 | + |
|
51 | + /** |
|
52 | + * @return object |
|
53 | + */ |
|
54 | + protected function scanTag() |
|
55 | + { |
|
56 | + if (preg_match('/^(\w[-:\w]*)(\/?)/', $this->input, $matches)) { |
|
57 | + $this->consume($matches[0]); |
|
58 | + $name = $matches[1]; |
|
59 | + |
|
60 | + if (':' === substr($name, -1) && ':' !== substr($name, -2, 1)) { |
|
61 | + $name = substr($name, 0, -1); |
|
62 | + $this->defer($this->token(':')); |
|
63 | + |
|
64 | + while (' ' === substr($this->input, 0, 1)) { |
|
65 | + $this->consume(' '); |
|
66 | + } |
|
67 | + } |
|
68 | + |
|
69 | + $token = $this->token('tag', $name); |
|
70 | + $token->selfClosing = ($matches[2] === '/'); |
|
71 | + |
|
72 | + return $token; |
|
73 | + } |
|
74 | + } |
|
75 | + |
|
76 | + /** |
|
77 | + * @return object |
|
78 | + */ |
|
79 | + protected function scanFilter() |
|
80 | + { |
|
81 | + return $this->scan('/^(?<!:):(?!:)(\w+(?:-\w+)*)/', 'filter'); |
|
82 | + } |
|
83 | + |
|
84 | + /** |
|
85 | + * @return object |
|
86 | + */ |
|
87 | + protected function scanDoctype() |
|
88 | + { |
|
89 | + return $this->scan('/^(?:!!!|doctype) *([^\n]+)?/', 'doctype'); |
|
90 | + } |
|
91 | + |
|
92 | + /** |
|
93 | + * @return object |
|
94 | + */ |
|
95 | + protected function scanId() |
|
96 | + { |
|
97 | + return $this->scan('/^#([\w-]+)/', 'id'); |
|
98 | + } |
|
99 | + |
|
100 | + /** |
|
101 | + * @return object |
|
102 | + */ |
|
103 | + protected function scanClassName() |
|
104 | + { |
|
105 | + // http://www.w3.org/TR/CSS21/grammar.html#scanner |
|
106 | + // |
|
107 | + // ident: |
|
108 | + // -?{nmstart}{nmchar}* |
|
109 | + // nmstart: |
|
110 | + // [_a-z]|{nonascii}|{escape} |
|
111 | + // nonascii: |
|
112 | + // [\240-\377] |
|
113 | + // escape: |
|
114 | + // {unicode}|\\[^\r\n\f0-9a-f] |
|
115 | + // unicode: |
|
116 | + // \\{h}{1,6}(\r\n|[ \t\r\n\f])? |
|
117 | + // nmchar: |
|
118 | + // [_a-z0-9-]|{nonascii}|{escape} |
|
119 | + // |
|
120 | + // /^(-?(?!=[0-9-])(?:[_a-z0-9-]|[\240-\377]|\\{h}{1,6}(?:\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f])+)/ |
|
121 | + return $this->scan('/^\.([\w-]+)/', 'class'); |
|
122 | + } |
|
123 | + |
|
124 | + /** |
|
125 | + * @return object |
|
126 | + */ |
|
127 | + protected function scanText() |
|
128 | + { |
|
129 | + return $this->scan('/^(?:\| ?| ?)?([^\n]+)/', 'text'); |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * @return object |
|
134 | + */ |
|
135 | + protected function scanAssignment() |
|
136 | + { |
|
137 | + if (preg_match('/^(\$?\w+) += *([^;\n]+|\'[^\']+\'|"[^"]+")( *;? *)/', $this->input, $matches)) { |
|
138 | + $this->consume($matches[0]); |
|
139 | + |
|
140 | + return $this->token('code', (substr($matches[1], 0, 1) === '$' ? '' : '$') . $matches[1] . '=' . $matches[2]); |
|
141 | + } |
|
142 | + } |
|
143 | + |
|
144 | + /** |
|
145 | + * @return object |
|
146 | + */ |
|
147 | + protected function scanConditional() |
|
148 | + { |
|
149 | + if (preg_match('/^(if|unless|else if|elseif|else|while)\b([^\n]*)/', $this->input, $matches)) { |
|
150 | + $this->consume($matches[0]); |
|
151 | + |
|
152 | + /*switch ($matches[1]) { |
|
153 | 153 | case 'if': $code = 'if (' . $matches[2] . '):'; break; |
154 | 154 | case 'unless': $code = 'if (!(' . $matches[2] . ')):'; break; |
155 | 155 | case 'else if': $code = 'elseif (' . $matches[2] . '):'; break; |
156 | 156 | case 'else': $code = 'else (' . $matches[2] . '):'; break; |
157 | 157 | }*/ |
158 | - $code = $this->normalizeCode($matches[0]); |
|
159 | - $token = $this->token('code', $code); |
|
160 | - $token->buffer = false; |
|
161 | - |
|
162 | - return $token; |
|
163 | - } |
|
164 | - } |
|
165 | - |
|
166 | - /** |
|
167 | - * @return object |
|
168 | - */ |
|
169 | - protected function scanEach() |
|
170 | - { |
|
171 | - if (preg_match('/^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? +in *([^\n]+)/', $this->input, $matches)) { |
|
172 | - $this->consume($matches[0]); |
|
173 | - |
|
174 | - $token = $this->token('each', $matches[1]); |
|
175 | - $token->key = $matches[2]; |
|
176 | - $token->code = $this->normalizeCode($matches[3]); |
|
177 | - |
|
178 | - return $token; |
|
179 | - } |
|
180 | - } |
|
181 | - |
|
182 | - /** |
|
183 | - * @return object |
|
184 | - */ |
|
185 | - protected function scanCustomKeyword() |
|
186 | - { |
|
187 | - if ( |
|
188 | - count($this->customKeywords) && |
|
189 | - preg_match('/^([\w-]+)([^\n]*)/', $this->input, $matches) && |
|
190 | - isset($this->customKeywords[$matches[1]]) && |
|
191 | - is_callable($this->customKeywords[$matches[1]]) |
|
192 | - ) { |
|
193 | - $this->consume($matches[0]); |
|
194 | - |
|
195 | - $token = $this->token('customKeyword', $matches[1]); |
|
196 | - $token->args = trim($matches[2]); |
|
197 | - |
|
198 | - return $token; |
|
199 | - } |
|
200 | - } |
|
201 | - |
|
202 | - /** |
|
203 | - * @return object |
|
204 | - */ |
|
205 | - protected function scanCode() |
|
206 | - { |
|
207 | - if (preg_match('/^(!?=|-)([^\n]+)/', $this->input, $matches)) { |
|
208 | - $this->consume($matches[0]); |
|
209 | - $flags = $matches[1]; |
|
210 | - $code = $this->normalizeCode($matches[2]); |
|
211 | - |
|
212 | - $token = $this->token('code', $code); |
|
213 | - $token->escape = $flags[0] === '='; |
|
214 | - $token->buffer = '=' === $flags[0] || (isset($flags[1]) && '=' === $flags[1]); |
|
215 | - |
|
216 | - return $token; |
|
217 | - } |
|
218 | - } |
|
219 | - |
|
220 | - /** |
|
221 | - * @throws \ErrorException |
|
222 | - * |
|
223 | - * @return object |
|
224 | - */ |
|
225 | - protected function scanAttributes() |
|
226 | - { |
|
227 | - if (substr($this->input, 0, 1) === '(') { |
|
228 | - // cant use ^ anchor in the regex because the pattern is recursive |
|
229 | - // but this restriction is asserted by the if above |
|
230 | - //$this->input = preg_replace('/([a-zA-Z0-9\'"\\]\\}\\)])([\t ]+[a-zA-Z])/', '$1,$2', $this->input); |
|
231 | - if (!preg_match('/\((?:"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|[^()\'"]++|(?R))*+\)/', $this->input, $matches)) { |
|
232 | - throw new \ErrorException('Unable to find attributes closing parenthesis.', 21); |
|
233 | - } |
|
234 | - $this->consume($matches[0]); |
|
235 | - |
|
236 | - //$str = preg_replace('/()([a-zA-Z0-9_\\x7f-\\xff\\)\\]\\}"\'])(\s+[a-zA-Z_])/', '$1,$2', $str); |
|
237 | - |
|
238 | - $token = $this->token('attributes'); |
|
239 | - $token->attributes = array(); |
|
240 | - $token->escaped = array(); |
|
241 | - $token->selfClosing = false; |
|
242 | - |
|
243 | - $parser = new Attributes($token); |
|
244 | - $parser->parseWith(substr($matches[0], 1, strlen($matches[0]) - 2)); |
|
245 | - |
|
246 | - if ($this->length() && '/' === $this->input[0]) { |
|
247 | - $this->consume(1); |
|
248 | - $token->selfClosing = true; |
|
249 | - } |
|
250 | - |
|
251 | - return $token; |
|
252 | - } |
|
253 | - } |
|
254 | - |
|
255 | - /** |
|
256 | - * @return object |
|
257 | - */ |
|
258 | - protected function scanPipelessText() |
|
259 | - { |
|
260 | - if ($this->pipeless && "\n" !== substr($this->input, 0, 1)) { |
|
261 | - $pos = strpos($this->input, "\n"); |
|
262 | - |
|
263 | - if ($pos === false) { |
|
264 | - $pos = $this->length(); |
|
265 | - } |
|
266 | - |
|
267 | - $str = substr($this->input, 0, $pos); // do not include the \n char |
|
268 | - |
|
269 | - $this->consume($str); |
|
270 | - |
|
271 | - return $this->token('text', ltrim($str)); |
|
272 | - } |
|
273 | - } |
|
274 | - |
|
275 | - /** |
|
276 | - * @return object |
|
277 | - */ |
|
278 | - protected function scanColon() |
|
279 | - { |
|
280 | - return $this->scan('/^:(?!:) */', ':'); |
|
281 | - } |
|
282 | - |
|
283 | - /** |
|
284 | - * @return object |
|
285 | - */ |
|
286 | - protected function scanAndAttributes() |
|
287 | - { |
|
288 | - return $this->scan('/^&attributes(\(((?>[^()]+|(?1))*)\))/', '&attributes', 2); |
|
289 | - } |
|
158 | + $code = $this->normalizeCode($matches[0]); |
|
159 | + $token = $this->token('code', $code); |
|
160 | + $token->buffer = false; |
|
161 | + |
|
162 | + return $token; |
|
163 | + } |
|
164 | + } |
|
165 | + |
|
166 | + /** |
|
167 | + * @return object |
|
168 | + */ |
|
169 | + protected function scanEach() |
|
170 | + { |
|
171 | + if (preg_match('/^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? +in *([^\n]+)/', $this->input, $matches)) { |
|
172 | + $this->consume($matches[0]); |
|
173 | + |
|
174 | + $token = $this->token('each', $matches[1]); |
|
175 | + $token->key = $matches[2]; |
|
176 | + $token->code = $this->normalizeCode($matches[3]); |
|
177 | + |
|
178 | + return $token; |
|
179 | + } |
|
180 | + } |
|
181 | + |
|
182 | + /** |
|
183 | + * @return object |
|
184 | + */ |
|
185 | + protected function scanCustomKeyword() |
|
186 | + { |
|
187 | + if ( |
|
188 | + count($this->customKeywords) && |
|
189 | + preg_match('/^([\w-]+)([^\n]*)/', $this->input, $matches) && |
|
190 | + isset($this->customKeywords[$matches[1]]) && |
|
191 | + is_callable($this->customKeywords[$matches[1]]) |
|
192 | + ) { |
|
193 | + $this->consume($matches[0]); |
|
194 | + |
|
195 | + $token = $this->token('customKeyword', $matches[1]); |
|
196 | + $token->args = trim($matches[2]); |
|
197 | + |
|
198 | + return $token; |
|
199 | + } |
|
200 | + } |
|
201 | + |
|
202 | + /** |
|
203 | + * @return object |
|
204 | + */ |
|
205 | + protected function scanCode() |
|
206 | + { |
|
207 | + if (preg_match('/^(!?=|-)([^\n]+)/', $this->input, $matches)) { |
|
208 | + $this->consume($matches[0]); |
|
209 | + $flags = $matches[1]; |
|
210 | + $code = $this->normalizeCode($matches[2]); |
|
211 | + |
|
212 | + $token = $this->token('code', $code); |
|
213 | + $token->escape = $flags[0] === '='; |
|
214 | + $token->buffer = '=' === $flags[0] || (isset($flags[1]) && '=' === $flags[1]); |
|
215 | + |
|
216 | + return $token; |
|
217 | + } |
|
218 | + } |
|
219 | + |
|
220 | + /** |
|
221 | + * @throws \ErrorException |
|
222 | + * |
|
223 | + * @return object |
|
224 | + */ |
|
225 | + protected function scanAttributes() |
|
226 | + { |
|
227 | + if (substr($this->input, 0, 1) === '(') { |
|
228 | + // cant use ^ anchor in the regex because the pattern is recursive |
|
229 | + // but this restriction is asserted by the if above |
|
230 | + //$this->input = preg_replace('/([a-zA-Z0-9\'"\\]\\}\\)])([\t ]+[a-zA-Z])/', '$1,$2', $this->input); |
|
231 | + if (!preg_match('/\((?:"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|[^()\'"]++|(?R))*+\)/', $this->input, $matches)) { |
|
232 | + throw new \ErrorException('Unable to find attributes closing parenthesis.', 21); |
|
233 | + } |
|
234 | + $this->consume($matches[0]); |
|
235 | + |
|
236 | + //$str = preg_replace('/()([a-zA-Z0-9_\\x7f-\\xff\\)\\]\\}"\'])(\s+[a-zA-Z_])/', '$1,$2', $str); |
|
237 | + |
|
238 | + $token = $this->token('attributes'); |
|
239 | + $token->attributes = array(); |
|
240 | + $token->escaped = array(); |
|
241 | + $token->selfClosing = false; |
|
242 | + |
|
243 | + $parser = new Attributes($token); |
|
244 | + $parser->parseWith(substr($matches[0], 1, strlen($matches[0]) - 2)); |
|
245 | + |
|
246 | + if ($this->length() && '/' === $this->input[0]) { |
|
247 | + $this->consume(1); |
|
248 | + $token->selfClosing = true; |
|
249 | + } |
|
250 | + |
|
251 | + return $token; |
|
252 | + } |
|
253 | + } |
|
254 | + |
|
255 | + /** |
|
256 | + * @return object |
|
257 | + */ |
|
258 | + protected function scanPipelessText() |
|
259 | + { |
|
260 | + if ($this->pipeless && "\n" !== substr($this->input, 0, 1)) { |
|
261 | + $pos = strpos($this->input, "\n"); |
|
262 | + |
|
263 | + if ($pos === false) { |
|
264 | + $pos = $this->length(); |
|
265 | + } |
|
266 | + |
|
267 | + $str = substr($this->input, 0, $pos); // do not include the \n char |
|
268 | + |
|
269 | + $this->consume($str); |
|
270 | + |
|
271 | + return $this->token('text', ltrim($str)); |
|
272 | + } |
|
273 | + } |
|
274 | + |
|
275 | + /** |
|
276 | + * @return object |
|
277 | + */ |
|
278 | + protected function scanColon() |
|
279 | + { |
|
280 | + return $this->scan('/^:(?!:) */', ':'); |
|
281 | + } |
|
282 | + |
|
283 | + /** |
|
284 | + * @return object |
|
285 | + */ |
|
286 | + protected function scanAndAttributes() |
|
287 | + { |
|
288 | + return $this->scan('/^&attributes(\(((?>[^()]+|(?1))*)\))/', '&attributes', 2); |
|
289 | + } |
|
290 | 290 | } |
@@ -27,7 +27,7 @@ discard block |
||
27 | 27 | protected function scanComment() |
28 | 28 | { |
29 | 29 | $indent = count($this->indentStack) ? $this->indentStack[0] : 0; |
30 | - if (preg_match('/^ *\/\/(-)?([^\n]*(\n+[ \t]{' . ($indent + 1) . ',}[^\n]*)*)/', $this->input, $matches)) { |
|
30 | + if (preg_match('/^ *\/\/(-)?([^\n]*(\n+[ \t]{'.($indent + 1).',}[^\n]*)*)/', $this->input, $matches)) { |
|
31 | 31 | $this->consume($matches[0]); |
32 | 32 | $value = isset($matches[2]) ? $matches[2] : ''; |
33 | 33 | if (isset($matches[3])) { |
@@ -137,7 +137,7 @@ discard block |
||
137 | 137 | if (preg_match('/^(\$?\w+) += *([^;\n]+|\'[^\']+\'|"[^"]+")( *;? *)/', $this->input, $matches)) { |
138 | 138 | $this->consume($matches[0]); |
139 | 139 | |
140 | - return $this->token('code', (substr($matches[1], 0, 1) === '$' ? '' : '$') . $matches[1] . '=' . $matches[2]); |
|
140 | + return $this->token('code', (substr($matches[1], 0, 1) === '$' ? '' : '$').$matches[1].'='.$matches[2]); |
|
141 | 141 | } |
142 | 142 | } |
143 | 143 |
@@ -56,6 +56,9 @@ discard block |
||
56 | 56 | return $extensions->getExtensions(); |
57 | 57 | } |
58 | 58 | |
59 | + /** |
|
60 | + * @param string $path |
|
61 | + */ |
|
59 | 62 | protected function hasValidTemplateExtension($path) |
60 | 63 | { |
61 | 64 | $extensions = new ExtensionsHelper($this->extension); |
@@ -79,6 +82,9 @@ discard block |
||
79 | 82 | return $extensions->findValidTemplatePath($path, ''); |
80 | 83 | } |
81 | 84 | |
85 | + /** |
|
86 | + * @param string|null $path |
|
87 | + */ |
|
82 | 88 | protected function getTemplateContents($path, $value = null) |
83 | 89 | { |
84 | 90 | if ($path !== null) { |
@@ -144,6 +150,9 @@ discard block |
||
144 | 150 | return $this->lexer->advance(); |
145 | 151 | } |
146 | 152 | |
153 | + /** |
|
154 | + * @param integer $n |
|
155 | + */ |
|
147 | 156 | public function skip($n) |
148 | 157 | { |
149 | 158 | while ($n--) { |
@@ -199,6 +208,9 @@ discard block |
||
199 | 208 | return $block; |
200 | 209 | } |
201 | 210 | |
211 | + /** |
|
212 | + * @param string $type |
|
213 | + */ |
|
202 | 214 | protected function expect($type) |
203 | 215 | { |
204 | 216 | if ($this->peekType() === $type) { |
@@ -211,6 +223,9 @@ discard block |
||
211 | 223 | throw new \ErrorException("\n" . sprintf('Expected %s, but got %s in %dth line : %s', $type, $this->peekType(), $lineNumber, $lineString) . "\n", 24); |
212 | 224 | } |
213 | 225 | |
226 | + /** |
|
227 | + * @param string $type |
|
228 | + */ |
|
214 | 229 | protected function accept($type) |
215 | 230 | { |
216 | 231 | if ($this->peekType() === $type) { |
@@ -7,757 +7,757 @@ |
||
7 | 7 | |
8 | 8 | class Parser |
9 | 9 | { |
10 | - public static $includeNotFound = ".alert.alert-danger.\n\tPage not found."; |
|
11 | - |
|
12 | - protected $allowMixedIndent; |
|
13 | - protected $basedir; |
|
14 | - protected $extending; |
|
15 | - protected $extension; |
|
16 | - protected $filename; |
|
17 | - protected $input; |
|
18 | - protected $lexer; |
|
19 | - protected $notFound; |
|
20 | - protected $options = array(); |
|
21 | - protected $preRender; |
|
22 | - |
|
23 | - protected $blocks = array(); |
|
24 | - protected $mixins = array(); |
|
25 | - protected $contexts = array(); |
|
26 | - |
|
27 | - public function __construct($input, $filename = null, array $options = array()) |
|
28 | - { |
|
29 | - $defaultOptions = array( |
|
30 | - 'allowMixedIndent' => true, |
|
31 | - 'basedir' => null, |
|
32 | - 'customKeywords' => array(), |
|
33 | - 'extension' => array('.pug', '.jade'), |
|
34 | - 'notFound' => null, |
|
35 | - 'preRender' => null, |
|
36 | - ); |
|
37 | - foreach ($defaultOptions as $key => $default) { |
|
38 | - $this->$key = isset($options[$key]) ? $options[$key] : $default; |
|
39 | - $this->options[$key] = $this->$key; |
|
40 | - } |
|
41 | - |
|
42 | - $this->setInput($filename, $input); |
|
43 | - |
|
44 | - if ($this->input && $this->input[0] === "\xef" && $this->input[1] === "\xbb" && $this->input[2] === "\xbf") { |
|
45 | - $this->input = substr($this->input, 3); |
|
46 | - } |
|
47 | - |
|
48 | - $this->lexer = new Lexer($this->input, $this->options); |
|
49 | - array_push($this->contexts, $this); |
|
50 | - } |
|
51 | - |
|
52 | - protected function getExtensions() |
|
53 | - { |
|
54 | - $extensions = new ExtensionsHelper($this->extension); |
|
55 | - |
|
56 | - return $extensions->getExtensions(); |
|
57 | - } |
|
58 | - |
|
59 | - protected function hasValidTemplateExtension($path) |
|
60 | - { |
|
61 | - $extensions = new ExtensionsHelper($this->extension); |
|
62 | - |
|
63 | - return $extensions->hasValidTemplateExtension($path); |
|
64 | - } |
|
65 | - |
|
66 | - protected function getTemplatePath($path) |
|
67 | - { |
|
68 | - $isAbsolutePath = substr($path, 0, 1) === '/'; |
|
69 | - if ($isAbsolutePath && !isset($this->options['basedir'])) { |
|
70 | - throw new \ErrorException("The 'basedir' option need to be set to use absolute path like $path", 29); |
|
71 | - } |
|
72 | - |
|
73 | - $path = ($isAbsolutePath |
|
74 | - ? rtrim($this->options['basedir'], '/\\') |
|
75 | - : dirname($this->filename) |
|
76 | - ) . DIRECTORY_SEPARATOR . $path; |
|
77 | - $extensions = new ExtensionsHelper($this->extension); |
|
78 | - |
|
79 | - return $extensions->findValidTemplatePath($path, ''); |
|
80 | - } |
|
81 | - |
|
82 | - protected function getTemplateContents($path, $value = null) |
|
83 | - { |
|
84 | - if ($path !== null) { |
|
85 | - $contents = file_get_contents($path); |
|
86 | - if (is_callable($this->preRender)) { |
|
87 | - $contents = call_user_func($this->preRender, $contents); |
|
88 | - } |
|
89 | - |
|
90 | - return $contents; |
|
91 | - } |
|
92 | - |
|
93 | - $notFound = isset($this->options['notFound']) |
|
94 | - ? $this->options['notFound'] |
|
95 | - : static::$includeNotFound; |
|
96 | - |
|
97 | - if ($notFound !== false) { |
|
98 | - return $notFound; |
|
99 | - } |
|
100 | - |
|
101 | - $value = $value ?: $path; |
|
102 | - throw new \InvalidArgumentException("The included file '$value' does not exists.", 22); |
|
103 | - } |
|
104 | - |
|
105 | - protected function setInput($filename, $input) |
|
106 | - { |
|
107 | - if ($filename === null && file_exists($input)) { |
|
108 | - $filename = $input; |
|
109 | - $input = file_get_contents($input); |
|
110 | - } |
|
111 | - |
|
112 | - $this->input = preg_replace('`\r\n|\r`', "\n", $input); |
|
113 | - if (is_callable($this->preRender)) { |
|
114 | - $this->input = call_user_func($this->preRender, $this->input); |
|
115 | - } |
|
116 | - $this->filename = $filename; |
|
117 | - } |
|
118 | - |
|
119 | - public function getFilename() |
|
120 | - { |
|
121 | - return $this->filename; |
|
122 | - } |
|
123 | - |
|
124 | - /** |
|
125 | - * Get a parser with the same settings. |
|
126 | - * |
|
127 | - * @return Parser |
|
128 | - */ |
|
129 | - public function subParser($input) |
|
130 | - { |
|
131 | - return new static($input, $this->filename, $this->options); |
|
132 | - } |
|
133 | - |
|
134 | - public function context($parser = null) |
|
135 | - { |
|
136 | - if ($parser === null) { |
|
137 | - return array_pop($this->contexts); |
|
138 | - } |
|
139 | - array_push($this->contexts, $parser); |
|
140 | - } |
|
141 | - |
|
142 | - public function advance() |
|
143 | - { |
|
144 | - return $this->lexer->advance(); |
|
145 | - } |
|
146 | - |
|
147 | - public function skip($n) |
|
148 | - { |
|
149 | - while ($n--) { |
|
150 | - $this->advance(); |
|
151 | - } |
|
152 | - } |
|
153 | - |
|
154 | - public function peek() |
|
155 | - { |
|
156 | - return $this->lookahead(1); |
|
157 | - } |
|
158 | - |
|
159 | - public function line() |
|
160 | - { |
|
161 | - return $this->lexer->lineno; |
|
162 | - } |
|
163 | - |
|
164 | - public function lookahead($n = 1) |
|
165 | - { |
|
166 | - return $this->lexer->lookahead($n); |
|
167 | - } |
|
168 | - |
|
169 | - public function parse() |
|
170 | - { |
|
171 | - $block = new Nodes\Block(); |
|
172 | - $block->line = $this->line(); |
|
173 | - |
|
174 | - while ($this->peekType() !== 'eos') { |
|
175 | - if ($this->peekType() === 'newline') { |
|
176 | - $this->advance(); |
|
177 | - continue; |
|
178 | - } |
|
179 | - $block->push($this->parseExpression()); |
|
180 | - } |
|
181 | - |
|
182 | - if ($parser = $this->extending) { |
|
183 | - $this->context($parser); |
|
184 | - // $parser->blocks = $this->blocks; |
|
185 | - try { |
|
186 | - $ast = $parser->parse(); |
|
187 | - } catch (\Exception $e) { |
|
188 | - throw new ParserException($parser->getFilename() . ' (' . $block->line . ') : ' . $e->getMessage(), 23, $e); |
|
189 | - } |
|
190 | - $this->context(); |
|
191 | - |
|
192 | - foreach ($this->mixins as $name => $v) { |
|
193 | - $ast->unshift($this->mixins[$name]); |
|
194 | - } |
|
195 | - |
|
196 | - return $ast; |
|
197 | - } |
|
198 | - |
|
199 | - return $block; |
|
200 | - } |
|
201 | - |
|
202 | - protected function expect($type) |
|
203 | - { |
|
204 | - if ($this->peekType() === $type) { |
|
205 | - return $this->lexer->advance(); |
|
206 | - } |
|
207 | - |
|
208 | - $lineNumber = $this->line(); |
|
209 | - $lines = explode("\n", $this->input); |
|
210 | - $lineString = isset($lines[$lineNumber]) ? $lines[$lineNumber] : ''; |
|
211 | - throw new \ErrorException("\n" . sprintf('Expected %s, but got %s in %dth line : %s', $type, $this->peekType(), $lineNumber, $lineString) . "\n", 24); |
|
212 | - } |
|
213 | - |
|
214 | - protected function accept($type) |
|
215 | - { |
|
216 | - if ($this->peekType() === $type) { |
|
217 | - return $this->advance(); |
|
218 | - } |
|
219 | - } |
|
220 | - |
|
221 | - protected function parseExpression() |
|
222 | - { |
|
223 | - $_types = array('tag', 'mixin', 'block', 'case', 'when', 'default', 'extends', 'include', 'doctype', 'filter', 'comment', 'text', 'each', 'customKeyword', 'code', 'call', 'interpolation'); |
|
224 | - |
|
225 | - if (in_array($this->peekType(), $_types)) { |
|
226 | - $_method = 'parse' . ucfirst($this->peekType()); |
|
227 | - |
|
228 | - return $this->$_method(); |
|
229 | - } |
|
230 | - |
|
231 | - switch ($this->peekType()) { |
|
232 | - case 'yield': |
|
233 | - $this->advance(); |
|
234 | - $block = new Nodes\Block(); |
|
235 | - $block->yield = true; |
|
236 | - |
|
237 | - return $block; |
|
238 | - |
|
239 | - case 'id': |
|
240 | - case 'class': |
|
241 | - $token = $this->advance(); |
|
242 | - $this->lexer->defer($this->lexer->token('tag', 'div')); |
|
243 | - $this->lexer->defer($token); |
|
244 | - |
|
245 | - return $this->parseExpression(); |
|
246 | - |
|
247 | - default: |
|
248 | - throw new \ErrorException($this->filename . ' (' . $this->line() . ') : Unexpected token "' . $this->peekType() . '"', 25); |
|
249 | - } |
|
250 | - } |
|
251 | - |
|
252 | - protected function parseText() |
|
253 | - { |
|
254 | - $token = $this->expect('text'); |
|
255 | - if (preg_match('/^(.*?)#\[([^\]\n]+)\]/', $token->value)) { |
|
256 | - $block = new Nodes\Block(); |
|
257 | - $this->parseInlineTags($block, $token->value); |
|
258 | - |
|
259 | - return $block; |
|
260 | - } |
|
261 | - $node = new Nodes\Text($token->value); |
|
262 | - $node->line = $this->line(); |
|
263 | - |
|
264 | - return $node; |
|
265 | - } |
|
266 | - |
|
267 | - protected function parseBlockExpansion() |
|
268 | - { |
|
269 | - if (':' === $this->peekType()) { |
|
270 | - $this->advance(); |
|
271 | - |
|
272 | - return new Nodes\Block($this->parseExpression()); |
|
273 | - } |
|
274 | - |
|
275 | - return $this->block(); |
|
276 | - } |
|
277 | - |
|
278 | - protected function parseCase() |
|
279 | - { |
|
280 | - $value = $this->expect('case')->value; |
|
281 | - $node = new Nodes\CaseNode($value); |
|
282 | - $node->line = $this->line(); |
|
283 | - $node->block = $this->block(); |
|
284 | - |
|
285 | - return $node; |
|
286 | - } |
|
287 | - |
|
288 | - protected function parseWhen() |
|
289 | - { |
|
290 | - $value = $this->expect('when')->value; |
|
291 | - |
|
292 | - return new Nodes\When($value, $this->parseBlockExpansion()); |
|
293 | - } |
|
294 | - |
|
295 | - protected function parseDefault() |
|
296 | - { |
|
297 | - $this->expect('default'); |
|
298 | - |
|
299 | - return new Nodes\When('default', $this->parseBlockExpansion()); |
|
300 | - } |
|
301 | - |
|
302 | - protected function parseCode() |
|
303 | - { |
|
304 | - $token = $this->expect('code'); |
|
305 | - $buffer = isset($token->buffer) ? $token->buffer : false; |
|
306 | - $escape = isset($token->escape) ? $token->escape : true; |
|
307 | - $node = new Nodes\Code($token->value, $buffer, $escape); |
|
308 | - $node->line = $this->line(); |
|
309 | - |
|
310 | - $i = 1; |
|
311 | - while ($this->lookahead($i)->type === 'newline') { |
|
312 | - $i++; |
|
313 | - } |
|
314 | - |
|
315 | - if ($this->lookahead($i)->type === 'indent') { |
|
316 | - $this->skip($i - 1); |
|
317 | - $node->block = $this->block(); |
|
318 | - } |
|
319 | - |
|
320 | - return $node; |
|
321 | - } |
|
322 | - |
|
323 | - protected function parseComment() |
|
324 | - { |
|
325 | - $token = $this->expect('comment'); |
|
326 | - $node = new Nodes\Comment($token->value, $token->buffer); |
|
327 | - $node->line = $this->line(); |
|
328 | - |
|
329 | - return $node; |
|
330 | - } |
|
331 | - |
|
332 | - protected function parseDoctype() |
|
333 | - { |
|
334 | - $token = $this->expect('doctype'); |
|
335 | - $node = new Nodes\Doctype($token->value); |
|
336 | - $node->line = $this->line(); |
|
337 | - |
|
338 | - return $node; |
|
339 | - } |
|
340 | - |
|
341 | - protected function parseFilter() |
|
342 | - { |
|
343 | - $token = $this->expect('filter'); |
|
344 | - $attributes = $this->accept('attributes'); |
|
345 | - |
|
346 | - $this->lexer->pipeless = true; |
|
347 | - $block = $this->parseTextBlock(); |
|
348 | - $this->lexer->pipeless = false; |
|
349 | - |
|
350 | - $node = new Nodes\Filter($token->value, $block, $attributes); |
|
351 | - $node->line = $this->line(); |
|
352 | - |
|
353 | - return $node; |
|
354 | - } |
|
355 | - |
|
356 | - protected function parseEach() |
|
357 | - { |
|
358 | - $token = $this->expect('each'); |
|
359 | - $node = new Nodes\Each($token->code, $token->value, $token->key); |
|
360 | - $node->line = $this->line(); |
|
361 | - $node->block = $this->block(); |
|
362 | - if ($this->peekType() === 'code' && $this->peek()->value === 'else') { |
|
363 | - $this->advance(); |
|
364 | - $node->alternative = $this->block(); |
|
365 | - } |
|
366 | - |
|
367 | - return $node; |
|
368 | - } |
|
369 | - |
|
370 | - protected function parseCustomKeyword() |
|
371 | - { |
|
372 | - $token = $this->expect('customKeyword'); |
|
373 | - $node = new Nodes\CustomKeyword($token->value, $token->args); |
|
374 | - $node->line = $this->line(); |
|
375 | - if ('indent' === $this->peekType()) { |
|
376 | - $node->block = $this->block(); |
|
377 | - } |
|
378 | - |
|
379 | - return $node; |
|
380 | - } |
|
381 | - |
|
382 | - protected function parseExtends() |
|
383 | - { |
|
384 | - $extendValue = $this->expect('extends')->value; |
|
385 | - $path = $this->getTemplatePath($extendValue); |
|
386 | - |
|
387 | - $string = $this->getTemplateContents($path, $extendValue); |
|
388 | - $parser = new static($string, $path, $this->options); |
|
389 | - // need to be a reference, or be seted after the parse loop |
|
390 | - $parser->blocks = &$this->blocks; |
|
391 | - $parser->contexts = $this->contexts; |
|
392 | - $this->extending = $parser; |
|
393 | - |
|
394 | - return new Nodes\Literal(''); |
|
395 | - } |
|
396 | - |
|
397 | - protected function parseBlock() |
|
398 | - { |
|
399 | - $block = $this->expect('block'); |
|
400 | - $mode = $block->mode; |
|
401 | - $name = trim($block->value); |
|
402 | - |
|
403 | - $block = 'indent' === $this->peekType() |
|
404 | - ? $this->block() |
|
405 | - : new Nodes\Block(empty($name) |
|
406 | - ? new Nodes\MixinBlock() |
|
407 | - : new Nodes\Literal('') |
|
408 | - ); |
|
409 | - |
|
410 | - if (isset($this->blocks[$name])) { |
|
411 | - $prev = &$this->blocks[$name]; |
|
412 | - |
|
413 | - switch ($prev->mode) { |
|
414 | - case 'append': |
|
415 | - $block->nodes = array_merge($block->nodes, $prev->nodes); |
|
416 | - $prev = $block; |
|
417 | - break; |
|
418 | - |
|
419 | - case 'prepend': |
|
420 | - $block->nodes = array_merge($prev->nodes, $block->nodes); |
|
421 | - $prev = $block; |
|
422 | - break; |
|
423 | - |
|
424 | - case 'replace': |
|
425 | - default: |
|
426 | - break; |
|
427 | - } |
|
428 | - |
|
429 | - return $this->blocks[$name]; |
|
430 | - } |
|
431 | - |
|
432 | - $block->mode = $mode; |
|
433 | - $this->blocks[$name] = $block; |
|
434 | - |
|
435 | - return $block; |
|
436 | - } |
|
437 | - |
|
438 | - protected function parseInclude() |
|
439 | - { |
|
440 | - $token = $this->expect('include'); |
|
441 | - $includeValue = trim($token->value); |
|
442 | - $path = $this->getTemplatePath($includeValue); |
|
443 | - |
|
444 | - if ($path && !$this->hasValidTemplateExtension($path)) { |
|
445 | - return new Nodes\Text(file_get_contents($path)); |
|
446 | - } |
|
447 | - |
|
448 | - $string = $this->getTemplateContents($path, $includeValue); |
|
449 | - |
|
450 | - $parser = new static($string, $path, $this->options); |
|
451 | - $parser->blocks = $this->blocks; |
|
452 | - $parser->mixins = $this->mixins; |
|
453 | - |
|
454 | - $this->context($parser); |
|
455 | - try { |
|
456 | - $ast = $parser->parse(); |
|
457 | - } catch (\Exception $e) { |
|
458 | - throw new \ErrorException($path . ' (' . $parser->lexer->lineno . ') : ' . $e->getMessage(), 27); |
|
459 | - } |
|
460 | - $this->context(); |
|
461 | - $ast->filename = $path; |
|
462 | - |
|
463 | - if ('indent' === $this->peekType() && method_exists($ast, 'includeBlock')) { |
|
464 | - $block = $ast->includeBlock(); |
|
465 | - if (is_object($block)) { |
|
466 | - $handler = count($block->nodes) === 1 && isset($block->nodes[0]->block) |
|
467 | - ? $block->nodes[0]->block |
|
468 | - : $block; |
|
469 | - $handler->push($this->block()); |
|
470 | - } |
|
471 | - } |
|
472 | - |
|
473 | - return $ast; |
|
474 | - } |
|
475 | - |
|
476 | - protected function parseCall() |
|
477 | - { |
|
478 | - $token = $this->expect('call'); |
|
479 | - $name = $token->value; |
|
480 | - |
|
481 | - $arguments = isset($token->arguments) |
|
482 | - ? $token->arguments |
|
483 | - : null; |
|
484 | - |
|
485 | - $mixin = new Nodes\Mixin($name, $arguments, new Nodes\Block(), true); |
|
486 | - |
|
487 | - $this->tag($mixin); |
|
488 | - |
|
489 | - if ($mixin->block->isEmpty()) { |
|
490 | - $mixin->block = null; |
|
491 | - } |
|
492 | - |
|
493 | - return $mixin; |
|
494 | - } |
|
495 | - |
|
496 | - protected function parseMixin() |
|
497 | - { |
|
498 | - $token = $this->expect('mixin'); |
|
499 | - $name = $token->value; |
|
500 | - $arguments = $token->arguments; |
|
501 | - |
|
502 | - // definition |
|
503 | - if ('indent' === $this->peekType()) { |
|
504 | - $mixin = new Nodes\Mixin($name, $arguments, $this->block(), false); |
|
505 | - $this->mixins[$name] = $mixin; |
|
506 | - |
|
507 | - return $mixin; |
|
508 | - } |
|
509 | - |
|
510 | - // call |
|
511 | - return new Nodes\Mixin($name, $arguments, null, true); |
|
512 | - } |
|
513 | - |
|
514 | - protected function parseTextBlock() |
|
515 | - { |
|
516 | - $block = new Nodes\Block(); |
|
517 | - $block->line = $this->line(); |
|
518 | - $spaces = $this->expect('indent')->value; |
|
519 | - |
|
520 | - if (!isset($this->_spaces)) { |
|
521 | - $this->_spaces = $spaces; |
|
522 | - } |
|
523 | - |
|
524 | - $indent = str_repeat(' ', $spaces - $this->_spaces + 1); |
|
525 | - |
|
526 | - while ($this->peekType() != 'outdent') { |
|
527 | - switch ($this->peekType()) { |
|
528 | - case 'newline': |
|
529 | - $this->lexer->advance(); |
|
530 | - break; |
|
531 | - |
|
532 | - case 'indent': |
|
533 | - foreach ($this->parseTextBlock()->nodes as $n) { |
|
534 | - $block->push($n); |
|
535 | - } |
|
536 | - break; |
|
537 | - |
|
538 | - default: |
|
539 | - $this->parseInlineTags($block, $indent . $this->advance()->value); |
|
540 | - } |
|
541 | - } |
|
542 | - |
|
543 | - if (isset($this->_spaces) && $spaces === $this->_spaces) { |
|
544 | - unset($this->_spaces); |
|
545 | - } |
|
546 | - |
|
547 | - $this->expect('outdent'); |
|
548 | - |
|
549 | - return $block; |
|
550 | - } |
|
551 | - |
|
552 | - protected function block() |
|
553 | - { |
|
554 | - $block = new Nodes\Block(); |
|
555 | - $block->line = $this->line(); |
|
556 | - $this->expect('indent'); |
|
557 | - |
|
558 | - while ($this->peekType() !== 'outdent') { |
|
559 | - if ($this->peekType() === 'newline') { |
|
560 | - $this->lexer->advance(); |
|
561 | - continue; |
|
562 | - } |
|
563 | - |
|
564 | - $block->push($this->parseExpression()); |
|
565 | - } |
|
566 | - |
|
567 | - $this->expect('outdent'); |
|
568 | - |
|
569 | - return $block; |
|
570 | - } |
|
571 | - |
|
572 | - protected function parseInterpolation() |
|
573 | - { |
|
574 | - $token = $this->advance(); |
|
575 | - $tag = new Nodes\Tag($token->value); |
|
576 | - $tag->buffer = true; |
|
577 | - |
|
578 | - return $this->tag($tag); |
|
579 | - } |
|
580 | - |
|
581 | - protected function parseASTFilter() |
|
582 | - { |
|
583 | - $token = $this->expect('tag'); |
|
584 | - $attributes = $this->accept('attributes'); |
|
585 | - $this->expect(':'); |
|
586 | - $block = $this->block(); |
|
587 | - $node = new Nodes\Filter($token->value, $block, $attributes); |
|
588 | - $node->line = $this->line(); |
|
589 | - |
|
590 | - return $node; |
|
591 | - } |
|
592 | - |
|
593 | - protected function parseTag() |
|
594 | - { |
|
595 | - $i = 2; |
|
596 | - |
|
597 | - if ('attributes' === $this->lookahead($i)->type) { |
|
598 | - $i++; |
|
599 | - } |
|
600 | - |
|
601 | - if (':' === $this->lookahead($i)->type) { |
|
602 | - $i++; |
|
603 | - |
|
604 | - if ('indent' === $this->lookahead($i)->type) { |
|
605 | - return $this->parseASTFilter(); |
|
606 | - } |
|
607 | - } |
|
608 | - |
|
609 | - $token = $this->advance(); |
|
610 | - $tag = new Nodes\Tag($token->value); |
|
611 | - |
|
612 | - $tag->selfClosing = isset($token->selfClosing) |
|
613 | - ? $token->selfClosing |
|
614 | - : false; |
|
615 | - |
|
616 | - return $this->tag($tag); |
|
617 | - } |
|
618 | - |
|
619 | - public function parseInlineTags($block, $str) |
|
620 | - { |
|
621 | - while (preg_match('/^(.*?)#\[([^\]\n]+)\]/', $str, $matches)) { |
|
622 | - if (!empty($matches[1])) { |
|
623 | - $text = new Nodes\Text($matches[1]); |
|
624 | - $text->line = $this->line(); |
|
625 | - $block->push($text); |
|
626 | - } |
|
627 | - $parser = $this->subParser($matches[2]); |
|
628 | - $tag = $parser->parse(); |
|
629 | - $tag->line = $this->line(); |
|
630 | - $block->push($tag); |
|
631 | - $str = substr($str, strlen($matches[0])); |
|
632 | - } |
|
633 | - if (substr($str, 0, 1) === ' ') { |
|
634 | - $str = substr($str, 1); |
|
635 | - } |
|
636 | - $text = new Nodes\Text($str); |
|
637 | - $text->line = $this->line(); |
|
638 | - $block->push($text); |
|
639 | - } |
|
640 | - |
|
641 | - protected function peekType() |
|
642 | - { |
|
643 | - return ($peek = $this->peek()) |
|
644 | - ? $peek->type |
|
645 | - : null; |
|
646 | - } |
|
647 | - |
|
648 | - protected function tag($tag) |
|
649 | - { |
|
650 | - $tag->line = $this->line(); |
|
651 | - |
|
652 | - while (true) { |
|
653 | - switch ($type = $this->peekType()) { |
|
654 | - case 'id': |
|
655 | - $token = $this->advance(); |
|
656 | - $peek = $this->peek(); |
|
657 | - $escaped = isset($peek->escaped, $peek->escaped[$type]) && $peek->escaped[$type]; |
|
658 | - $value = $escaped || !isset($peek->attributes, $peek->attributes[$type]) |
|
659 | - ? "'" . $token->value . "'" |
|
660 | - : $peek->attributes[$type]; |
|
661 | - $tag->setAttribute($token->type, $value, $escaped); |
|
662 | - unset($peek->attributes[$type]); |
|
663 | - continue; |
|
664 | - |
|
665 | - case 'class': |
|
666 | - $token = $this->advance(); |
|
667 | - $tag->setAttribute($token->type, "'" . $token->value . "'"); |
|
668 | - continue; |
|
669 | - |
|
670 | - case 'attributes': |
|
671 | - $token = $this->advance(); |
|
672 | - $obj = $token->attributes; |
|
673 | - $escaped = $token->escaped; |
|
674 | - $nameList = array_keys($obj); |
|
675 | - |
|
676 | - if ($token->selfClosing) { |
|
677 | - $tag->selfClosing = true; |
|
678 | - } |
|
679 | - |
|
680 | - foreach ($nameList as $name) { |
|
681 | - $value = $obj[$name]; |
|
682 | - $normalizedValue = strtolower($value); |
|
683 | - if ($normalizedValue === 'true' || $normalizedValue === 'false') { |
|
684 | - $value = $normalizedValue === 'true'; |
|
685 | - } |
|
686 | - $tag->setAttribute($name, $value, $escaped[$name]); |
|
687 | - } |
|
688 | - continue; |
|
689 | - |
|
690 | - case '&attributes': |
|
691 | - $token = $this->advance(); |
|
692 | - $tag->setAttribute('&attributes', $token->value); |
|
693 | - continue; |
|
694 | - |
|
695 | - default: |
|
696 | - break 2; |
|
697 | - } |
|
698 | - } |
|
699 | - |
|
700 | - $dot = false; |
|
701 | - $tag->textOnly = false; |
|
702 | - if ('.' === $this->peek()->value) { |
|
703 | - $dot = $tag->textOnly = true; |
|
704 | - $this->advance(); |
|
705 | - } |
|
706 | - |
|
707 | - switch ($this->peekType()) { |
|
708 | - case 'text': |
|
709 | - $this->parseInlineTags($tag->block, $this->expect('text')->value); |
|
710 | - break; |
|
711 | - |
|
712 | - case 'code': |
|
713 | - $tag->code = $this->parseCode(); |
|
714 | - break; |
|
715 | - |
|
716 | - case ':': |
|
717 | - $this->advance(); |
|
718 | - $tag->block = new Nodes\Block(); |
|
719 | - $tag->block->push($this->parseExpression()); |
|
720 | - break; |
|
721 | - } |
|
722 | - |
|
723 | - while ('newline' === $this->peekType()) { |
|
724 | - $this->advance(); |
|
725 | - } |
|
726 | - |
|
727 | - if ('script' === $tag->name) { |
|
728 | - $type = $tag->getAttribute('type'); |
|
729 | - |
|
730 | - if ($type !== null) { |
|
731 | - $type = preg_replace('/^[\'\"]|[\'\"]$/', '', $type); |
|
732 | - |
|
733 | - if (!$dot && 'text/javascript' != $type['value']) { |
|
734 | - $tag->textOnly = false; |
|
735 | - } |
|
736 | - } |
|
737 | - } |
|
738 | - |
|
739 | - if ('indent' === $this->peekType()) { |
|
740 | - if ($tag->textOnly) { |
|
741 | - $this->lexer->pipeless = true; |
|
742 | - $tag->block = $this->parseTextBlock(); |
|
743 | - $this->lexer->pipeless = false; |
|
744 | - |
|
745 | - return $tag; |
|
746 | - } |
|
747 | - |
|
748 | - $block = $this->block(); |
|
749 | - |
|
750 | - if ($tag->block && !$tag->block->isEmpty()) { |
|
751 | - foreach ($block->nodes as $n) { |
|
752 | - $tag->block->push($n); |
|
753 | - } |
|
754 | - |
|
755 | - return $tag; |
|
756 | - } |
|
757 | - |
|
758 | - $tag->block = $block; |
|
759 | - } |
|
760 | - |
|
761 | - return $tag; |
|
762 | - } |
|
10 | + public static $includeNotFound = ".alert.alert-danger.\n\tPage not found."; |
|
11 | + |
|
12 | + protected $allowMixedIndent; |
|
13 | + protected $basedir; |
|
14 | + protected $extending; |
|
15 | + protected $extension; |
|
16 | + protected $filename; |
|
17 | + protected $input; |
|
18 | + protected $lexer; |
|
19 | + protected $notFound; |
|
20 | + protected $options = array(); |
|
21 | + protected $preRender; |
|
22 | + |
|
23 | + protected $blocks = array(); |
|
24 | + protected $mixins = array(); |
|
25 | + protected $contexts = array(); |
|
26 | + |
|
27 | + public function __construct($input, $filename = null, array $options = array()) |
|
28 | + { |
|
29 | + $defaultOptions = array( |
|
30 | + 'allowMixedIndent' => true, |
|
31 | + 'basedir' => null, |
|
32 | + 'customKeywords' => array(), |
|
33 | + 'extension' => array('.pug', '.jade'), |
|
34 | + 'notFound' => null, |
|
35 | + 'preRender' => null, |
|
36 | + ); |
|
37 | + foreach ($defaultOptions as $key => $default) { |
|
38 | + $this->$key = isset($options[$key]) ? $options[$key] : $default; |
|
39 | + $this->options[$key] = $this->$key; |
|
40 | + } |
|
41 | + |
|
42 | + $this->setInput($filename, $input); |
|
43 | + |
|
44 | + if ($this->input && $this->input[0] === "\xef" && $this->input[1] === "\xbb" && $this->input[2] === "\xbf") { |
|
45 | + $this->input = substr($this->input, 3); |
|
46 | + } |
|
47 | + |
|
48 | + $this->lexer = new Lexer($this->input, $this->options); |
|
49 | + array_push($this->contexts, $this); |
|
50 | + } |
|
51 | + |
|
52 | + protected function getExtensions() |
|
53 | + { |
|
54 | + $extensions = new ExtensionsHelper($this->extension); |
|
55 | + |
|
56 | + return $extensions->getExtensions(); |
|
57 | + } |
|
58 | + |
|
59 | + protected function hasValidTemplateExtension($path) |
|
60 | + { |
|
61 | + $extensions = new ExtensionsHelper($this->extension); |
|
62 | + |
|
63 | + return $extensions->hasValidTemplateExtension($path); |
|
64 | + } |
|
65 | + |
|
66 | + protected function getTemplatePath($path) |
|
67 | + { |
|
68 | + $isAbsolutePath = substr($path, 0, 1) === '/'; |
|
69 | + if ($isAbsolutePath && !isset($this->options['basedir'])) { |
|
70 | + throw new \ErrorException("The 'basedir' option need to be set to use absolute path like $path", 29); |
|
71 | + } |
|
72 | + |
|
73 | + $path = ($isAbsolutePath |
|
74 | + ? rtrim($this->options['basedir'], '/\\') |
|
75 | + : dirname($this->filename) |
|
76 | + ) . DIRECTORY_SEPARATOR . $path; |
|
77 | + $extensions = new ExtensionsHelper($this->extension); |
|
78 | + |
|
79 | + return $extensions->findValidTemplatePath($path, ''); |
|
80 | + } |
|
81 | + |
|
82 | + protected function getTemplateContents($path, $value = null) |
|
83 | + { |
|
84 | + if ($path !== null) { |
|
85 | + $contents = file_get_contents($path); |
|
86 | + if (is_callable($this->preRender)) { |
|
87 | + $contents = call_user_func($this->preRender, $contents); |
|
88 | + } |
|
89 | + |
|
90 | + return $contents; |
|
91 | + } |
|
92 | + |
|
93 | + $notFound = isset($this->options['notFound']) |
|
94 | + ? $this->options['notFound'] |
|
95 | + : static::$includeNotFound; |
|
96 | + |
|
97 | + if ($notFound !== false) { |
|
98 | + return $notFound; |
|
99 | + } |
|
100 | + |
|
101 | + $value = $value ?: $path; |
|
102 | + throw new \InvalidArgumentException("The included file '$value' does not exists.", 22); |
|
103 | + } |
|
104 | + |
|
105 | + protected function setInput($filename, $input) |
|
106 | + { |
|
107 | + if ($filename === null && file_exists($input)) { |
|
108 | + $filename = $input; |
|
109 | + $input = file_get_contents($input); |
|
110 | + } |
|
111 | + |
|
112 | + $this->input = preg_replace('`\r\n|\r`', "\n", $input); |
|
113 | + if (is_callable($this->preRender)) { |
|
114 | + $this->input = call_user_func($this->preRender, $this->input); |
|
115 | + } |
|
116 | + $this->filename = $filename; |
|
117 | + } |
|
118 | + |
|
119 | + public function getFilename() |
|
120 | + { |
|
121 | + return $this->filename; |
|
122 | + } |
|
123 | + |
|
124 | + /** |
|
125 | + * Get a parser with the same settings. |
|
126 | + * |
|
127 | + * @return Parser |
|
128 | + */ |
|
129 | + public function subParser($input) |
|
130 | + { |
|
131 | + return new static($input, $this->filename, $this->options); |
|
132 | + } |
|
133 | + |
|
134 | + public function context($parser = null) |
|
135 | + { |
|
136 | + if ($parser === null) { |
|
137 | + return array_pop($this->contexts); |
|
138 | + } |
|
139 | + array_push($this->contexts, $parser); |
|
140 | + } |
|
141 | + |
|
142 | + public function advance() |
|
143 | + { |
|
144 | + return $this->lexer->advance(); |
|
145 | + } |
|
146 | + |
|
147 | + public function skip($n) |
|
148 | + { |
|
149 | + while ($n--) { |
|
150 | + $this->advance(); |
|
151 | + } |
|
152 | + } |
|
153 | + |
|
154 | + public function peek() |
|
155 | + { |
|
156 | + return $this->lookahead(1); |
|
157 | + } |
|
158 | + |
|
159 | + public function line() |
|
160 | + { |
|
161 | + return $this->lexer->lineno; |
|
162 | + } |
|
163 | + |
|
164 | + public function lookahead($n = 1) |
|
165 | + { |
|
166 | + return $this->lexer->lookahead($n); |
|
167 | + } |
|
168 | + |
|
169 | + public function parse() |
|
170 | + { |
|
171 | + $block = new Nodes\Block(); |
|
172 | + $block->line = $this->line(); |
|
173 | + |
|
174 | + while ($this->peekType() !== 'eos') { |
|
175 | + if ($this->peekType() === 'newline') { |
|
176 | + $this->advance(); |
|
177 | + continue; |
|
178 | + } |
|
179 | + $block->push($this->parseExpression()); |
|
180 | + } |
|
181 | + |
|
182 | + if ($parser = $this->extending) { |
|
183 | + $this->context($parser); |
|
184 | + // $parser->blocks = $this->blocks; |
|
185 | + try { |
|
186 | + $ast = $parser->parse(); |
|
187 | + } catch (\Exception $e) { |
|
188 | + throw new ParserException($parser->getFilename() . ' (' . $block->line . ') : ' . $e->getMessage(), 23, $e); |
|
189 | + } |
|
190 | + $this->context(); |
|
191 | + |
|
192 | + foreach ($this->mixins as $name => $v) { |
|
193 | + $ast->unshift($this->mixins[$name]); |
|
194 | + } |
|
195 | + |
|
196 | + return $ast; |
|
197 | + } |
|
198 | + |
|
199 | + return $block; |
|
200 | + } |
|
201 | + |
|
202 | + protected function expect($type) |
|
203 | + { |
|
204 | + if ($this->peekType() === $type) { |
|
205 | + return $this->lexer->advance(); |
|
206 | + } |
|
207 | + |
|
208 | + $lineNumber = $this->line(); |
|
209 | + $lines = explode("\n", $this->input); |
|
210 | + $lineString = isset($lines[$lineNumber]) ? $lines[$lineNumber] : ''; |
|
211 | + throw new \ErrorException("\n" . sprintf('Expected %s, but got %s in %dth line : %s', $type, $this->peekType(), $lineNumber, $lineString) . "\n", 24); |
|
212 | + } |
|
213 | + |
|
214 | + protected function accept($type) |
|
215 | + { |
|
216 | + if ($this->peekType() === $type) { |
|
217 | + return $this->advance(); |
|
218 | + } |
|
219 | + } |
|
220 | + |
|
221 | + protected function parseExpression() |
|
222 | + { |
|
223 | + $_types = array('tag', 'mixin', 'block', 'case', 'when', 'default', 'extends', 'include', 'doctype', 'filter', 'comment', 'text', 'each', 'customKeyword', 'code', 'call', 'interpolation'); |
|
224 | + |
|
225 | + if (in_array($this->peekType(), $_types)) { |
|
226 | + $_method = 'parse' . ucfirst($this->peekType()); |
|
227 | + |
|
228 | + return $this->$_method(); |
|
229 | + } |
|
230 | + |
|
231 | + switch ($this->peekType()) { |
|
232 | + case 'yield': |
|
233 | + $this->advance(); |
|
234 | + $block = new Nodes\Block(); |
|
235 | + $block->yield = true; |
|
236 | + |
|
237 | + return $block; |
|
238 | + |
|
239 | + case 'id': |
|
240 | + case 'class': |
|
241 | + $token = $this->advance(); |
|
242 | + $this->lexer->defer($this->lexer->token('tag', 'div')); |
|
243 | + $this->lexer->defer($token); |
|
244 | + |
|
245 | + return $this->parseExpression(); |
|
246 | + |
|
247 | + default: |
|
248 | + throw new \ErrorException($this->filename . ' (' . $this->line() . ') : Unexpected token "' . $this->peekType() . '"', 25); |
|
249 | + } |
|
250 | + } |
|
251 | + |
|
252 | + protected function parseText() |
|
253 | + { |
|
254 | + $token = $this->expect('text'); |
|
255 | + if (preg_match('/^(.*?)#\[([^\]\n]+)\]/', $token->value)) { |
|
256 | + $block = new Nodes\Block(); |
|
257 | + $this->parseInlineTags($block, $token->value); |
|
258 | + |
|
259 | + return $block; |
|
260 | + } |
|
261 | + $node = new Nodes\Text($token->value); |
|
262 | + $node->line = $this->line(); |
|
263 | + |
|
264 | + return $node; |
|
265 | + } |
|
266 | + |
|
267 | + protected function parseBlockExpansion() |
|
268 | + { |
|
269 | + if (':' === $this->peekType()) { |
|
270 | + $this->advance(); |
|
271 | + |
|
272 | + return new Nodes\Block($this->parseExpression()); |
|
273 | + } |
|
274 | + |
|
275 | + return $this->block(); |
|
276 | + } |
|
277 | + |
|
278 | + protected function parseCase() |
|
279 | + { |
|
280 | + $value = $this->expect('case')->value; |
|
281 | + $node = new Nodes\CaseNode($value); |
|
282 | + $node->line = $this->line(); |
|
283 | + $node->block = $this->block(); |
|
284 | + |
|
285 | + return $node; |
|
286 | + } |
|
287 | + |
|
288 | + protected function parseWhen() |
|
289 | + { |
|
290 | + $value = $this->expect('when')->value; |
|
291 | + |
|
292 | + return new Nodes\When($value, $this->parseBlockExpansion()); |
|
293 | + } |
|
294 | + |
|
295 | + protected function parseDefault() |
|
296 | + { |
|
297 | + $this->expect('default'); |
|
298 | + |
|
299 | + return new Nodes\When('default', $this->parseBlockExpansion()); |
|
300 | + } |
|
301 | + |
|
302 | + protected function parseCode() |
|
303 | + { |
|
304 | + $token = $this->expect('code'); |
|
305 | + $buffer = isset($token->buffer) ? $token->buffer : false; |
|
306 | + $escape = isset($token->escape) ? $token->escape : true; |
|
307 | + $node = new Nodes\Code($token->value, $buffer, $escape); |
|
308 | + $node->line = $this->line(); |
|
309 | + |
|
310 | + $i = 1; |
|
311 | + while ($this->lookahead($i)->type === 'newline') { |
|
312 | + $i++; |
|
313 | + } |
|
314 | + |
|
315 | + if ($this->lookahead($i)->type === 'indent') { |
|
316 | + $this->skip($i - 1); |
|
317 | + $node->block = $this->block(); |
|
318 | + } |
|
319 | + |
|
320 | + return $node; |
|
321 | + } |
|
322 | + |
|
323 | + protected function parseComment() |
|
324 | + { |
|
325 | + $token = $this->expect('comment'); |
|
326 | + $node = new Nodes\Comment($token->value, $token->buffer); |
|
327 | + $node->line = $this->line(); |
|
328 | + |
|
329 | + return $node; |
|
330 | + } |
|
331 | + |
|
332 | + protected function parseDoctype() |
|
333 | + { |
|
334 | + $token = $this->expect('doctype'); |
|
335 | + $node = new Nodes\Doctype($token->value); |
|
336 | + $node->line = $this->line(); |
|
337 | + |
|
338 | + return $node; |
|
339 | + } |
|
340 | + |
|
341 | + protected function parseFilter() |
|
342 | + { |
|
343 | + $token = $this->expect('filter'); |
|
344 | + $attributes = $this->accept('attributes'); |
|
345 | + |
|
346 | + $this->lexer->pipeless = true; |
|
347 | + $block = $this->parseTextBlock(); |
|
348 | + $this->lexer->pipeless = false; |
|
349 | + |
|
350 | + $node = new Nodes\Filter($token->value, $block, $attributes); |
|
351 | + $node->line = $this->line(); |
|
352 | + |
|
353 | + return $node; |
|
354 | + } |
|
355 | + |
|
356 | + protected function parseEach() |
|
357 | + { |
|
358 | + $token = $this->expect('each'); |
|
359 | + $node = new Nodes\Each($token->code, $token->value, $token->key); |
|
360 | + $node->line = $this->line(); |
|
361 | + $node->block = $this->block(); |
|
362 | + if ($this->peekType() === 'code' && $this->peek()->value === 'else') { |
|
363 | + $this->advance(); |
|
364 | + $node->alternative = $this->block(); |
|
365 | + } |
|
366 | + |
|
367 | + return $node; |
|
368 | + } |
|
369 | + |
|
370 | + protected function parseCustomKeyword() |
|
371 | + { |
|
372 | + $token = $this->expect('customKeyword'); |
|
373 | + $node = new Nodes\CustomKeyword($token->value, $token->args); |
|
374 | + $node->line = $this->line(); |
|
375 | + if ('indent' === $this->peekType()) { |
|
376 | + $node->block = $this->block(); |
|
377 | + } |
|
378 | + |
|
379 | + return $node; |
|
380 | + } |
|
381 | + |
|
382 | + protected function parseExtends() |
|
383 | + { |
|
384 | + $extendValue = $this->expect('extends')->value; |
|
385 | + $path = $this->getTemplatePath($extendValue); |
|
386 | + |
|
387 | + $string = $this->getTemplateContents($path, $extendValue); |
|
388 | + $parser = new static($string, $path, $this->options); |
|
389 | + // need to be a reference, or be seted after the parse loop |
|
390 | + $parser->blocks = &$this->blocks; |
|
391 | + $parser->contexts = $this->contexts; |
|
392 | + $this->extending = $parser; |
|
393 | + |
|
394 | + return new Nodes\Literal(''); |
|
395 | + } |
|
396 | + |
|
397 | + protected function parseBlock() |
|
398 | + { |
|
399 | + $block = $this->expect('block'); |
|
400 | + $mode = $block->mode; |
|
401 | + $name = trim($block->value); |
|
402 | + |
|
403 | + $block = 'indent' === $this->peekType() |
|
404 | + ? $this->block() |
|
405 | + : new Nodes\Block(empty($name) |
|
406 | + ? new Nodes\MixinBlock() |
|
407 | + : new Nodes\Literal('') |
|
408 | + ); |
|
409 | + |
|
410 | + if (isset($this->blocks[$name])) { |
|
411 | + $prev = &$this->blocks[$name]; |
|
412 | + |
|
413 | + switch ($prev->mode) { |
|
414 | + case 'append': |
|
415 | + $block->nodes = array_merge($block->nodes, $prev->nodes); |
|
416 | + $prev = $block; |
|
417 | + break; |
|
418 | + |
|
419 | + case 'prepend': |
|
420 | + $block->nodes = array_merge($prev->nodes, $block->nodes); |
|
421 | + $prev = $block; |
|
422 | + break; |
|
423 | + |
|
424 | + case 'replace': |
|
425 | + default: |
|
426 | + break; |
|
427 | + } |
|
428 | + |
|
429 | + return $this->blocks[$name]; |
|
430 | + } |
|
431 | + |
|
432 | + $block->mode = $mode; |
|
433 | + $this->blocks[$name] = $block; |
|
434 | + |
|
435 | + return $block; |
|
436 | + } |
|
437 | + |
|
438 | + protected function parseInclude() |
|
439 | + { |
|
440 | + $token = $this->expect('include'); |
|
441 | + $includeValue = trim($token->value); |
|
442 | + $path = $this->getTemplatePath($includeValue); |
|
443 | + |
|
444 | + if ($path && !$this->hasValidTemplateExtension($path)) { |
|
445 | + return new Nodes\Text(file_get_contents($path)); |
|
446 | + } |
|
447 | + |
|
448 | + $string = $this->getTemplateContents($path, $includeValue); |
|
449 | + |
|
450 | + $parser = new static($string, $path, $this->options); |
|
451 | + $parser->blocks = $this->blocks; |
|
452 | + $parser->mixins = $this->mixins; |
|
453 | + |
|
454 | + $this->context($parser); |
|
455 | + try { |
|
456 | + $ast = $parser->parse(); |
|
457 | + } catch (\Exception $e) { |
|
458 | + throw new \ErrorException($path . ' (' . $parser->lexer->lineno . ') : ' . $e->getMessage(), 27); |
|
459 | + } |
|
460 | + $this->context(); |
|
461 | + $ast->filename = $path; |
|
462 | + |
|
463 | + if ('indent' === $this->peekType() && method_exists($ast, 'includeBlock')) { |
|
464 | + $block = $ast->includeBlock(); |
|
465 | + if (is_object($block)) { |
|
466 | + $handler = count($block->nodes) === 1 && isset($block->nodes[0]->block) |
|
467 | + ? $block->nodes[0]->block |
|
468 | + : $block; |
|
469 | + $handler->push($this->block()); |
|
470 | + } |
|
471 | + } |
|
472 | + |
|
473 | + return $ast; |
|
474 | + } |
|
475 | + |
|
476 | + protected function parseCall() |
|
477 | + { |
|
478 | + $token = $this->expect('call'); |
|
479 | + $name = $token->value; |
|
480 | + |
|
481 | + $arguments = isset($token->arguments) |
|
482 | + ? $token->arguments |
|
483 | + : null; |
|
484 | + |
|
485 | + $mixin = new Nodes\Mixin($name, $arguments, new Nodes\Block(), true); |
|
486 | + |
|
487 | + $this->tag($mixin); |
|
488 | + |
|
489 | + if ($mixin->block->isEmpty()) { |
|
490 | + $mixin->block = null; |
|
491 | + } |
|
492 | + |
|
493 | + return $mixin; |
|
494 | + } |
|
495 | + |
|
496 | + protected function parseMixin() |
|
497 | + { |
|
498 | + $token = $this->expect('mixin'); |
|
499 | + $name = $token->value; |
|
500 | + $arguments = $token->arguments; |
|
501 | + |
|
502 | + // definition |
|
503 | + if ('indent' === $this->peekType()) { |
|
504 | + $mixin = new Nodes\Mixin($name, $arguments, $this->block(), false); |
|
505 | + $this->mixins[$name] = $mixin; |
|
506 | + |
|
507 | + return $mixin; |
|
508 | + } |
|
509 | + |
|
510 | + // call |
|
511 | + return new Nodes\Mixin($name, $arguments, null, true); |
|
512 | + } |
|
513 | + |
|
514 | + protected function parseTextBlock() |
|
515 | + { |
|
516 | + $block = new Nodes\Block(); |
|
517 | + $block->line = $this->line(); |
|
518 | + $spaces = $this->expect('indent')->value; |
|
519 | + |
|
520 | + if (!isset($this->_spaces)) { |
|
521 | + $this->_spaces = $spaces; |
|
522 | + } |
|
523 | + |
|
524 | + $indent = str_repeat(' ', $spaces - $this->_spaces + 1); |
|
525 | + |
|
526 | + while ($this->peekType() != 'outdent') { |
|
527 | + switch ($this->peekType()) { |
|
528 | + case 'newline': |
|
529 | + $this->lexer->advance(); |
|
530 | + break; |
|
531 | + |
|
532 | + case 'indent': |
|
533 | + foreach ($this->parseTextBlock()->nodes as $n) { |
|
534 | + $block->push($n); |
|
535 | + } |
|
536 | + break; |
|
537 | + |
|
538 | + default: |
|
539 | + $this->parseInlineTags($block, $indent . $this->advance()->value); |
|
540 | + } |
|
541 | + } |
|
542 | + |
|
543 | + if (isset($this->_spaces) && $spaces === $this->_spaces) { |
|
544 | + unset($this->_spaces); |
|
545 | + } |
|
546 | + |
|
547 | + $this->expect('outdent'); |
|
548 | + |
|
549 | + return $block; |
|
550 | + } |
|
551 | + |
|
552 | + protected function block() |
|
553 | + { |
|
554 | + $block = new Nodes\Block(); |
|
555 | + $block->line = $this->line(); |
|
556 | + $this->expect('indent'); |
|
557 | + |
|
558 | + while ($this->peekType() !== 'outdent') { |
|
559 | + if ($this->peekType() === 'newline') { |
|
560 | + $this->lexer->advance(); |
|
561 | + continue; |
|
562 | + } |
|
563 | + |
|
564 | + $block->push($this->parseExpression()); |
|
565 | + } |
|
566 | + |
|
567 | + $this->expect('outdent'); |
|
568 | + |
|
569 | + return $block; |
|
570 | + } |
|
571 | + |
|
572 | + protected function parseInterpolation() |
|
573 | + { |
|
574 | + $token = $this->advance(); |
|
575 | + $tag = new Nodes\Tag($token->value); |
|
576 | + $tag->buffer = true; |
|
577 | + |
|
578 | + return $this->tag($tag); |
|
579 | + } |
|
580 | + |
|
581 | + protected function parseASTFilter() |
|
582 | + { |
|
583 | + $token = $this->expect('tag'); |
|
584 | + $attributes = $this->accept('attributes'); |
|
585 | + $this->expect(':'); |
|
586 | + $block = $this->block(); |
|
587 | + $node = new Nodes\Filter($token->value, $block, $attributes); |
|
588 | + $node->line = $this->line(); |
|
589 | + |
|
590 | + return $node; |
|
591 | + } |
|
592 | + |
|
593 | + protected function parseTag() |
|
594 | + { |
|
595 | + $i = 2; |
|
596 | + |
|
597 | + if ('attributes' === $this->lookahead($i)->type) { |
|
598 | + $i++; |
|
599 | + } |
|
600 | + |
|
601 | + if (':' === $this->lookahead($i)->type) { |
|
602 | + $i++; |
|
603 | + |
|
604 | + if ('indent' === $this->lookahead($i)->type) { |
|
605 | + return $this->parseASTFilter(); |
|
606 | + } |
|
607 | + } |
|
608 | + |
|
609 | + $token = $this->advance(); |
|
610 | + $tag = new Nodes\Tag($token->value); |
|
611 | + |
|
612 | + $tag->selfClosing = isset($token->selfClosing) |
|
613 | + ? $token->selfClosing |
|
614 | + : false; |
|
615 | + |
|
616 | + return $this->tag($tag); |
|
617 | + } |
|
618 | + |
|
619 | + public function parseInlineTags($block, $str) |
|
620 | + { |
|
621 | + while (preg_match('/^(.*?)#\[([^\]\n]+)\]/', $str, $matches)) { |
|
622 | + if (!empty($matches[1])) { |
|
623 | + $text = new Nodes\Text($matches[1]); |
|
624 | + $text->line = $this->line(); |
|
625 | + $block->push($text); |
|
626 | + } |
|
627 | + $parser = $this->subParser($matches[2]); |
|
628 | + $tag = $parser->parse(); |
|
629 | + $tag->line = $this->line(); |
|
630 | + $block->push($tag); |
|
631 | + $str = substr($str, strlen($matches[0])); |
|
632 | + } |
|
633 | + if (substr($str, 0, 1) === ' ') { |
|
634 | + $str = substr($str, 1); |
|
635 | + } |
|
636 | + $text = new Nodes\Text($str); |
|
637 | + $text->line = $this->line(); |
|
638 | + $block->push($text); |
|
639 | + } |
|
640 | + |
|
641 | + protected function peekType() |
|
642 | + { |
|
643 | + return ($peek = $this->peek()) |
|
644 | + ? $peek->type |
|
645 | + : null; |
|
646 | + } |
|
647 | + |
|
648 | + protected function tag($tag) |
|
649 | + { |
|
650 | + $tag->line = $this->line(); |
|
651 | + |
|
652 | + while (true) { |
|
653 | + switch ($type = $this->peekType()) { |
|
654 | + case 'id': |
|
655 | + $token = $this->advance(); |
|
656 | + $peek = $this->peek(); |
|
657 | + $escaped = isset($peek->escaped, $peek->escaped[$type]) && $peek->escaped[$type]; |
|
658 | + $value = $escaped || !isset($peek->attributes, $peek->attributes[$type]) |
|
659 | + ? "'" . $token->value . "'" |
|
660 | + : $peek->attributes[$type]; |
|
661 | + $tag->setAttribute($token->type, $value, $escaped); |
|
662 | + unset($peek->attributes[$type]); |
|
663 | + continue; |
|
664 | + |
|
665 | + case 'class': |
|
666 | + $token = $this->advance(); |
|
667 | + $tag->setAttribute($token->type, "'" . $token->value . "'"); |
|
668 | + continue; |
|
669 | + |
|
670 | + case 'attributes': |
|
671 | + $token = $this->advance(); |
|
672 | + $obj = $token->attributes; |
|
673 | + $escaped = $token->escaped; |
|
674 | + $nameList = array_keys($obj); |
|
675 | + |
|
676 | + if ($token->selfClosing) { |
|
677 | + $tag->selfClosing = true; |
|
678 | + } |
|
679 | + |
|
680 | + foreach ($nameList as $name) { |
|
681 | + $value = $obj[$name]; |
|
682 | + $normalizedValue = strtolower($value); |
|
683 | + if ($normalizedValue === 'true' || $normalizedValue === 'false') { |
|
684 | + $value = $normalizedValue === 'true'; |
|
685 | + } |
|
686 | + $tag->setAttribute($name, $value, $escaped[$name]); |
|
687 | + } |
|
688 | + continue; |
|
689 | + |
|
690 | + case '&attributes': |
|
691 | + $token = $this->advance(); |
|
692 | + $tag->setAttribute('&attributes', $token->value); |
|
693 | + continue; |
|
694 | + |
|
695 | + default: |
|
696 | + break 2; |
|
697 | + } |
|
698 | + } |
|
699 | + |
|
700 | + $dot = false; |
|
701 | + $tag->textOnly = false; |
|
702 | + if ('.' === $this->peek()->value) { |
|
703 | + $dot = $tag->textOnly = true; |
|
704 | + $this->advance(); |
|
705 | + } |
|
706 | + |
|
707 | + switch ($this->peekType()) { |
|
708 | + case 'text': |
|
709 | + $this->parseInlineTags($tag->block, $this->expect('text')->value); |
|
710 | + break; |
|
711 | + |
|
712 | + case 'code': |
|
713 | + $tag->code = $this->parseCode(); |
|
714 | + break; |
|
715 | + |
|
716 | + case ':': |
|
717 | + $this->advance(); |
|
718 | + $tag->block = new Nodes\Block(); |
|
719 | + $tag->block->push($this->parseExpression()); |
|
720 | + break; |
|
721 | + } |
|
722 | + |
|
723 | + while ('newline' === $this->peekType()) { |
|
724 | + $this->advance(); |
|
725 | + } |
|
726 | + |
|
727 | + if ('script' === $tag->name) { |
|
728 | + $type = $tag->getAttribute('type'); |
|
729 | + |
|
730 | + if ($type !== null) { |
|
731 | + $type = preg_replace('/^[\'\"]|[\'\"]$/', '', $type); |
|
732 | + |
|
733 | + if (!$dot && 'text/javascript' != $type['value']) { |
|
734 | + $tag->textOnly = false; |
|
735 | + } |
|
736 | + } |
|
737 | + } |
|
738 | + |
|
739 | + if ('indent' === $this->peekType()) { |
|
740 | + if ($tag->textOnly) { |
|
741 | + $this->lexer->pipeless = true; |
|
742 | + $tag->block = $this->parseTextBlock(); |
|
743 | + $this->lexer->pipeless = false; |
|
744 | + |
|
745 | + return $tag; |
|
746 | + } |
|
747 | + |
|
748 | + $block = $this->block(); |
|
749 | + |
|
750 | + if ($tag->block && !$tag->block->isEmpty()) { |
|
751 | + foreach ($block->nodes as $n) { |
|
752 | + $tag->block->push($n); |
|
753 | + } |
|
754 | + |
|
755 | + return $tag; |
|
756 | + } |
|
757 | + |
|
758 | + $tag->block = $block; |
|
759 | + } |
|
760 | + |
|
761 | + return $tag; |
|
762 | + } |
|
763 | 763 | } |
@@ -73,7 +73,7 @@ discard block |
||
73 | 73 | $path = ($isAbsolutePath |
74 | 74 | ? rtrim($this->options['basedir'], '/\\') |
75 | 75 | : dirname($this->filename) |
76 | - ) . DIRECTORY_SEPARATOR . $path; |
|
76 | + ).DIRECTORY_SEPARATOR.$path; |
|
77 | 77 | $extensions = new ExtensionsHelper($this->extension); |
78 | 78 | |
79 | 79 | return $extensions->findValidTemplatePath($path, ''); |
@@ -185,7 +185,7 @@ discard block |
||
185 | 185 | try { |
186 | 186 | $ast = $parser->parse(); |
187 | 187 | } catch (\Exception $e) { |
188 | - throw new ParserException($parser->getFilename() . ' (' . $block->line . ') : ' . $e->getMessage(), 23, $e); |
|
188 | + throw new ParserException($parser->getFilename().' ('.$block->line.') : '.$e->getMessage(), 23, $e); |
|
189 | 189 | } |
190 | 190 | $this->context(); |
191 | 191 | |
@@ -208,7 +208,7 @@ discard block |
||
208 | 208 | $lineNumber = $this->line(); |
209 | 209 | $lines = explode("\n", $this->input); |
210 | 210 | $lineString = isset($lines[$lineNumber]) ? $lines[$lineNumber] : ''; |
211 | - throw new \ErrorException("\n" . sprintf('Expected %s, but got %s in %dth line : %s', $type, $this->peekType(), $lineNumber, $lineString) . "\n", 24); |
|
211 | + throw new \ErrorException("\n".sprintf('Expected %s, but got %s in %dth line : %s', $type, $this->peekType(), $lineNumber, $lineString)."\n", 24); |
|
212 | 212 | } |
213 | 213 | |
214 | 214 | protected function accept($type) |
@@ -223,7 +223,7 @@ discard block |
||
223 | 223 | $_types = array('tag', 'mixin', 'block', 'case', 'when', 'default', 'extends', 'include', 'doctype', 'filter', 'comment', 'text', 'each', 'customKeyword', 'code', 'call', 'interpolation'); |
224 | 224 | |
225 | 225 | if (in_array($this->peekType(), $_types)) { |
226 | - $_method = 'parse' . ucfirst($this->peekType()); |
|
226 | + $_method = 'parse'.ucfirst($this->peekType()); |
|
227 | 227 | |
228 | 228 | return $this->$_method(); |
229 | 229 | } |
@@ -245,7 +245,7 @@ discard block |
||
245 | 245 | return $this->parseExpression(); |
246 | 246 | |
247 | 247 | default: |
248 | - throw new \ErrorException($this->filename . ' (' . $this->line() . ') : Unexpected token "' . $this->peekType() . '"', 25); |
|
248 | + throw new \ErrorException($this->filename.' ('.$this->line().') : Unexpected token "'.$this->peekType().'"', 25); |
|
249 | 249 | } |
250 | 250 | } |
251 | 251 | |
@@ -455,7 +455,7 @@ discard block |
||
455 | 455 | try { |
456 | 456 | $ast = $parser->parse(); |
457 | 457 | } catch (\Exception $e) { |
458 | - throw new \ErrorException($path . ' (' . $parser->lexer->lineno . ') : ' . $e->getMessage(), 27); |
|
458 | + throw new \ErrorException($path.' ('.$parser->lexer->lineno.') : '.$e->getMessage(), 27); |
|
459 | 459 | } |
460 | 460 | $this->context(); |
461 | 461 | $ast->filename = $path; |
@@ -536,7 +536,7 @@ discard block |
||
536 | 536 | break; |
537 | 537 | |
538 | 538 | default: |
539 | - $this->parseInlineTags($block, $indent . $this->advance()->value); |
|
539 | + $this->parseInlineTags($block, $indent.$this->advance()->value); |
|
540 | 540 | } |
541 | 541 | } |
542 | 542 | |
@@ -656,7 +656,7 @@ discard block |
||
656 | 656 | $peek = $this->peek(); |
657 | 657 | $escaped = isset($peek->escaped, $peek->escaped[$type]) && $peek->escaped[$type]; |
658 | 658 | $value = $escaped || !isset($peek->attributes, $peek->attributes[$type]) |
659 | - ? "'" . $token->value . "'" |
|
659 | + ? "'".$token->value."'" |
|
660 | 660 | : $peek->attributes[$type]; |
661 | 661 | $tag->setAttribute($token->type, $value, $escaped); |
662 | 662 | unset($peek->attributes[$type]); |
@@ -664,7 +664,7 @@ discard block |
||
664 | 664 | |
665 | 665 | case 'class': |
666 | 666 | $token = $this->advance(); |
667 | - $tag->setAttribute($token->type, "'" . $token->value . "'"); |
|
667 | + $tag->setAttribute($token->type, "'".$token->value."'"); |
|
668 | 668 | continue; |
669 | 669 | |
670 | 670 | case 'attributes': |