From cdf1d0613bfb6ff83dd94d9a1ac0b09b603412b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Wed, 19 Feb 2020 10:31:32 +0100 Subject: [PATCH 1/9] Fix attribute handling --- src/Compiler.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 3d589dc..9b84136 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -314,7 +314,8 @@ private function handleAttributeBinding(DOMElement $node) } $name = substr($attribute->name, strpos($attribute->name, ':') + 1); - $value = $this->builder->sanitizeAttributeValue($attribute->value); + $value = $this->builder->refactorCondition($attribute->value); + $value = $this->builder->sanitizeAttributeValue($value); $this->logger->debug('- handle: ' . $name . ' = ' . $value); $staticValues = $node->hasAttribute($name) ? $node->getAttribute($name) : ''; @@ -579,12 +580,16 @@ private function transformCamelCaseToCSS(string $property): string private function stripEventHandlers(DOMElement $node) { + $removeAttributes = []; /** @var DOMAttr $attribute */ foreach ($node->attributes as $attribute) { if (strpos($attribute->name, 'v-on:') === 0 || strpos($attribute->name, '@') === 0) { - $node->removeAttribute($attribute->name); + $removeAttributes[] = $attribute->name; } } + foreach ($removeAttributes as $removeAttribute) { + $node->removeAttribute($removeAttribute); + } } protected function implodeAttributeValue(string $attribute, array $values, string $oldValue): string From 5f4bbe59b3be6d7db8813bd2ca9e311ed2ee9a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Wed, 19 Feb 2020 10:51:41 +0100 Subject: [PATCH 2/9] Add test for attribute handling --- tests/fixtures/vue-bind/bindings.twig | 1 + tests/fixtures/vue-bind/bindings.vue | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/fixtures/vue-bind/bindings.twig b/tests/fixtures/vue-bind/bindings.twig index 5d4369c..1ee7a13 100644 --- a/tests/fixtures/vue-bind/bindings.twig +++ b/tests/fixtures/vue-bind/bindings.twig @@ -5,5 +5,6 @@
+
diff --git a/tests/fixtures/vue-bind/bindings.vue b/tests/fixtures/vue-bind/bindings.vue index 02db3f0..0a00f44 100644 --- a/tests/fixtures/vue-bind/bindings.vue +++ b/tests/fixtures/vue-bind/bindings.vue @@ -6,6 +6,7 @@
+
From eafac417d1885f849ebd2d8e547b0c1e6ae1f238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Wed, 19 Feb 2020 17:02:59 +0100 Subject: [PATCH 3/9] Fix attribute handling --- src/Compiler.php | 4 +-- src/Utils/TwigBuilder.php | 36 +++++++++++++++++++ .../binding-with-template-string.twig | 3 ++ .../vue-bind/binding-with-template-string.vue | 3 ++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 9b84136..2f6cff4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -314,8 +314,7 @@ private function handleAttributeBinding(DOMElement $node) } $name = substr($attribute->name, strpos($attribute->name, ':') + 1); - $value = $this->builder->refactorCondition($attribute->value); - $value = $this->builder->sanitizeAttributeValue($value); + $value = $this->builder->sanitizeAttributeValue($attribute->value); $this->logger->debug('- handle: ' . $name . ' = ' . $value); $staticValues = $node->hasAttribute($name) ? $node->getAttribute($name) : ''; @@ -389,6 +388,7 @@ private function handleAttributeBinding(DOMElement $node) $dynamicValues[] = $templateStringContent; } else { + $value = $this->builder->refactorCondition($value); $this->logger->debug(sprintf('- setAttribute "%s" with value "%s"', $name, $value)); $dynamicValues[] = Replacements::getSanitizedConstant('DOUBLE_CURLY_OPEN') . diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index 232222a..dff1e0b 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -171,6 +171,41 @@ public function sanitizeAttributeValue(string $value): string public function refactorCondition(string $condition): string { + $refactoredCondition = ''; + $charsCount = mb_strlen($condition, 'UTF-8'); + $quoteChar = null; + $lastChar = null; + $buffer = ''; + + for ($i = 0; $i < $charsCount; $i++) { + $char = mb_substr($condition, $i, 1, 'UTF-8'); + if ($quoteChar === null && ($char === '"' || $char === '\'')) { + $quoteChar = $char; + if ($buffer !== '') { + $refactoredCondition .= $this->refactorConditionPart($buffer); + $buffer = ''; + } + $refactoredCondition .= $char; + } elseif ($quoteChar === $char && $lastChar !== '\\') { + $quoteChar = null; + $refactoredCondition .= $char; + } else { + if ($quoteChar === null) { + $buffer .= $char; + } else { + $refactoredCondition .= $char; + } + } + $lastChar = $char; + } + if ($buffer !== '') { + $refactoredCondition .= $this->refactorConditionPart($buffer); + } + + return $refactoredCondition; + } + + private function refactorConditionPart($condition) { $condition = str_replace('===', '==', $condition); $condition = str_replace('!==', '!=', $condition); $condition = str_replace('&&', 'and', $condition); @@ -183,6 +218,7 @@ public function refactorCondition(string $condition): string $condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition); } + return $condition; } diff --git a/tests/fixtures/vue-bind/binding-with-template-string.twig b/tests/fixtures/vue-bind/binding-with-template-string.twig index 03a0b4e..a0d31b9 100644 --- a/tests/fixtures/vue-bind/binding-with-template-string.twig +++ b/tests/fixtures/vue-bind/binding-with-template-string.twig @@ -3,4 +3,7 @@ Hello World
+
+ Hidden +
diff --git a/tests/fixtures/vue-bind/binding-with-template-string.vue b/tests/fixtures/vue-bind/binding-with-template-string.vue index 5cc0640..3628bcd 100644 --- a/tests/fixtures/vue-bind/binding-with-template-string.vue +++ b/tests/fixtures/vue-bind/binding-with-template-string.vue @@ -4,6 +4,9 @@ Hello World
+
+ Hidden +
From be657bb822296d6dcd8be218edbf84424e5c386a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Wed, 19 Feb 2020 17:04:10 +0100 Subject: [PATCH 4/9] Fix attribute handling --- src/Utils/TwigBuilder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index dff1e0b..7888848 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -218,7 +218,6 @@ private function refactorConditionPart($condition) { $condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition); } - return $condition; } From 8ed4e786254eeae9eb705c074c8a737c42082b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Thu, 20 Feb 2020 09:34:02 +0100 Subject: [PATCH 5/9] Fix refactorTextNode --- src/Utils/TwigBuilder.php | 34 ++++++++++++++++++++++++++++--- tests/TextNodeTest.php | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/TextNodeTest.php diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index 232222a..7dd53de 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -176,6 +176,7 @@ public function refactorCondition(string $condition): string $condition = str_replace('&&', 'and', $condition); $condition = str_replace('||', 'or', $condition); $condition = preg_replace('/!([^=])/', 'not $1', $condition); + $condition = str_replace('+', '~', $condition); $condition = str_replace('.length', '|length', $condition); $condition = str_replace('.trim', '|trim', $condition); @@ -188,10 +189,37 @@ public function refactorCondition(string $condition): string public function refactorTextNode(string $content): string { - $content = str_replace('.length', '|length', $content); - $content = str_replace('.trim', '|trim', $content); + $refactoredContent = ''; + $charsCount = mb_strlen($content, 'UTF-8'); + $open = false; + $lastChar = null; + $quoteChar = null; + $buffer = ''; + + for ($i = 0; $i < $charsCount; $i++) { + $char = mb_substr($content, $i, 1, 'UTF-8'); + if ($open === false) { + $refactoredContent .= $char; + if ($char === '{' && $lastChar === '{') { + $open = true; + } + } else { + $buffer .= $char; + if ($quoteChar === null && ($char === '"' || $char === '\'')) { + $quoteChar = $char; + } elseif ($quoteChar === $char && $lastChar !== '\\') { + $quoteChar = null; + } + if ($quoteChar === null && $char === '}' && $lastChar === '}') { + $open = false; + $refactoredContent .= $this->refactorCondition(trim($buffer, '}')) . '}}'; + $buffer = ''; + } + } + $lastChar = $char; + } - return $content; + return $refactoredContent; } public function createVariableOutput($varName, ?string $fallbackVariableName = null): string diff --git a/tests/TextNodeTest.php b/tests/TextNodeTest.php new file mode 100644 index 0000000..90329ce --- /dev/null +++ b/tests/TextNodeTest.php @@ -0,0 +1,42 @@ +
foo {{ bar.trim }}
'; + $expected = '
foo {{ bar|trim }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } + + public function testTextNodeNoReplace() + { + $html = ''; + $expected = '
foo.trim {{ \'foo === bar\' }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } + + public function testTextNodeDontCloseInQuote() + { + $html = ''; + $expected = '
{{ \'}}\' ~ foo|trim }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } +} From 71c67b9ce8683175bd897c00e1442557474883e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Thu, 20 Feb 2020 10:32:44 +0100 Subject: [PATCH 6/9] Update refactorTextNode --- src/Utils/TwigBuilder.php | 9 ++++++++- tests/TextNodeTest.php | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index 7dd53de..91a88c9 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -176,10 +176,17 @@ public function refactorCondition(string $condition): string $condition = str_replace('&&', 'and', $condition); $condition = str_replace('||', 'or', $condition); $condition = preg_replace('/!([^=])/', 'not $1', $condition); - $condition = str_replace('+', '~', $condition); $condition = str_replace('.length', '|length', $condition); $condition = str_replace('.trim', '|trim', $condition); + if (preg_match_all('/(\S+)\s*\+\s*(\S+)/', $condition, $matches, PREG_SET_ORDER )) { + foreach ($matches as $match) { + if (!is_numeric($match[1]) || !is_numeric($match[2])) { + $condition = str_replace($match[0], str_replace('+', '~', $match[0]), $condition); + } + } + } + foreach (Replacements::getConstants() as $constant => $value) { $condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition); } diff --git a/tests/TextNodeTest.php b/tests/TextNodeTest.php index 90329ce..31da565 100644 --- a/tests/TextNodeTest.php +++ b/tests/TextNodeTest.php @@ -39,4 +39,16 @@ public function testTextNodeDontCloseInQuote() $this->assertEqualHtml($expected, $actual); } + + public function testTextNodeNumbers() + { + $html = ''; + $expected = '
{{ 1 + 1 }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } } From 71b97afabd3f0a5aabc2880bf7bb94b0a7504004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Thu, 20 Feb 2020 11:49:49 +0100 Subject: [PATCH 7/9] Add convertTemplateString and convertConcat --- src/Utils/TwigBuilder.php | 48 ++++++++++++++++++++++++++++++++------- tests/TextNodeTest.php | 12 ++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index 91a88c9..66c174c 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -179,13 +179,7 @@ public function refactorCondition(string $condition): string $condition = str_replace('.length', '|length', $condition); $condition = str_replace('.trim', '|trim', $condition); - if (preg_match_all('/(\S+)\s*\+\s*(\S+)/', $condition, $matches, PREG_SET_ORDER )) { - foreach ($matches as $match) { - if (!is_numeric($match[1]) || !is_numeric($match[2])) { - $condition = str_replace($match[0], str_replace('+', '~', $match[0]), $condition); - } - } - } + $condition = $this->convertConcat($condition); foreach (Replacements::getConstants() as $constant => $value) { $condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition); @@ -219,7 +213,8 @@ public function refactorTextNode(string $content): string } if ($quoteChar === null && $char === '}' && $lastChar === '}') { $open = false; - $refactoredContent .= $this->refactorCondition(trim($buffer, '}')) . '}}'; + $buffer = $this->convertTemplateString(trim($buffer, '}')); + $refactoredContent .= $this->refactorCondition($buffer) . '}}'; $buffer = ''; } } @@ -229,6 +224,43 @@ public function refactorTextNode(string $content): string return $refactoredContent; } + private function convertConcat($content) { + if (preg_match_all('/(\S+)(\s*\+\s*(\S+))+/', $content, $matches, PREG_SET_ORDER )) { + foreach ($matches as $match) { + $parts = explode('+', $match[0]); + $lastPart = null; + $convertedContent = ''; + foreach ($parts as $part) { + $part = trim($part); + if ($lastPart !== null) { + if (is_numeric($lastPart) && is_numeric($part)) { + $convertedContent .= ' + ' . $part; + } else { + $convertedContent .= ' ~ ' . $part; + } + } else { + $convertedContent = $part; + } + $lastPart = $part; + } + $content = str_replace($match[0], $convertedContent, $content); + } + } + + return $content; + } + + private function convertTemplateString($content) { + if (preg_match_all('/\`([^\`]+)\`/', $content, $matches, PREG_SET_ORDER )) { + foreach ($matches as $match) { + $match[1] = str_replace('${', '" ~ ', $match[1]); + $match[1] = str_replace('}', ' ~ "', $match[1]); + $content = str_replace($match[0], '"' . $match[1] . '"', $content); + } + } + return $content; + } + public function createVariableOutput($varName, ?string $fallbackVariableName = null): string { if ($fallbackVariableName) { diff --git a/tests/TextNodeTest.php b/tests/TextNodeTest.php index 31da565..d54bb29 100644 --- a/tests/TextNodeTest.php +++ b/tests/TextNodeTest.php @@ -51,4 +51,16 @@ public function testTextNodeNumbers() $this->assertEqualHtml($expected, $actual); } + + public function testTextNodeWithTemplateString() + { + $html = ''; + $expected = '
{{ "Var = " ~ var ~ "" }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } } From 0bcff46844939cebbad62333b9f2e05efb2231d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Fri, 21 Feb 2020 06:22:42 +0100 Subject: [PATCH 8/9] Update refactorCondition --- src/Utils/TwigBuilder.php | 9 +++++---- tests/TextNodeTest.php | 14 +------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index 66c174c..f079a80 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -179,7 +179,8 @@ public function refactorCondition(string $condition): string $condition = str_replace('.length', '|length', $condition); $condition = str_replace('.trim', '|trim', $condition); - $condition = $this->convertConcat($condition); + $condition = str_replace('+', '~', $condition); +// $condition = $this->convertConcat($condition); foreach (Replacements::getConstants() as $constant => $value) { $condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition); @@ -253,9 +254,9 @@ private function convertConcat($content) { private function convertTemplateString($content) { if (preg_match_all('/\`([^\`]+)\`/', $content, $matches, PREG_SET_ORDER )) { foreach ($matches as $match) { - $match[1] = str_replace('${', '" ~ ', $match[1]); - $match[1] = str_replace('}', ' ~ "', $match[1]); - $content = str_replace($match[0], '"' . $match[1] . '"', $content); + $match[1] = str_replace('${', '\' ~ ', $match[1]); + $match[1] = str_replace('}', ' ~ \'', $match[1]); + $content = str_replace($match[0], '\'' . $match[1] . '\'', $content); } } return $content; diff --git a/tests/TextNodeTest.php b/tests/TextNodeTest.php index d54bb29..ec2a1ba 100644 --- a/tests/TextNodeTest.php +++ b/tests/TextNodeTest.php @@ -40,22 +40,10 @@ public function testTextNodeDontCloseInQuote() $this->assertEqualHtml($expected, $actual); } - public function testTextNodeNumbers() - { - $html = ''; - $expected = '
{{ 1 + 1 }}
'; - - $compiler = $this->createCompiler($html); - - $actual = $compiler->convert(); - - $this->assertEqualHtml($expected, $actual); - } - public function testTextNodeWithTemplateString() { $html = ''; - $expected = '
{{ "Var = " ~ var ~ "" }}
'; + $expected = '
{{ \'Var = \' ~ var ~ \'\' }}
'; $compiler = $this->createCompiler($html); From 0a2ffe842ae9138acec11a062607d649a972d12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20H=C3=BCsges?= Date: Fri, 21 Feb 2020 10:41:05 +0100 Subject: [PATCH 9/9] Update refactorCondition --- src/Utils/TwigBuilder.php | 3 +-- tests/TextNodeTest.php | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Utils/TwigBuilder.php b/src/Utils/TwigBuilder.php index da3e6fc..41e0e4d 100644 --- a/src/Utils/TwigBuilder.php +++ b/src/Utils/TwigBuilder.php @@ -214,7 +214,6 @@ private function refactorConditionPart($condition) { $condition = str_replace('.length', '|length', $condition); $condition = str_replace('.trim', '|trim', $condition); - $condition = str_replace('+', '~', $condition); // $condition = $this->convertConcat($condition); foreach (Replacements::getConstants() as $constant => $value) { @@ -261,7 +260,7 @@ public function refactorTextNode(string $content): string } private function convertConcat($content) { - if (preg_match_all('/(\S+)(\s*\+\s*(\S+))+/', $content, $matches, PREG_SET_ORDER )) { + if (preg_match_all('/(\S*)(\s*\+\s*(\S+))+/', $content, $matches, PREG_SET_ORDER )) { foreach ($matches as $match) { $parts = explode('+', $match[0]); $lastPart = null; diff --git a/tests/TextNodeTest.php b/tests/TextNodeTest.php index ec2a1ba..37469de 100644 --- a/tests/TextNodeTest.php +++ b/tests/TextNodeTest.php @@ -30,8 +30,8 @@ public function testTextNodeNoReplace() public function testTextNodeDontCloseInQuote() { - $html = ''; - $expected = '
{{ \'}}\' ~ foo|trim }}
'; + $html = ''; + $expected = '
{{ \'}}\' or foo|length }}
'; $compiler = $this->createCompiler($html); @@ -51,4 +51,17 @@ public function testTextNodeWithTemplateString() $this->assertEqualHtml($expected, $actual); } + + + public function testTextNodeNumbers() + { + $html = ''; + $expected = '
{{ 1 + 1 }}
'; + + $compiler = $this->createCompiler($html); + + $actual = $compiler->convert(); + + $this->assertEqualHtml($expected, $actual); + } }