Skip to content

Commit 4812d51

Browse files
authored
Merge pull request #1 from Paneon/feature/if-conditions
Feature/if conditions
2 parents c522e1b + 6f68773 commit 4812d51

File tree

10 files changed

+158
-27
lines changed

10 files changed

+158
-27
lines changed

src/Compiler.php

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@
33
namespace Paneon\VueToTwig;
44

55
use DOMAttr;
6-
use DOMCharacterData;
76
use DOMDocument;
87
use DOMElement;
98
use DOMNode;
109
use DOMText;
1110
use Exception;
11+
use Paneon\VueToTwig\Models\Replacements;
1212
use Psr\Log\LoggerInterface;
1313

1414
class Compiler
1515
{
16-
protected const DOUBLE_CURLY_OPEN = '__DOUBLE_CURLY_OPEN__';
17-
protected const DOUBLE_CURLY_CLOSE = '__DOUBLE_CURLY_CLOSE__';
1816

1917
/** @var String[] */
2018
protected $components;
@@ -50,8 +48,11 @@ public function convert(): string
5048

5149
$rootNode = $this->getRootNode($templateElement);
5250
$resultNode = $this->convertNode($rootNode);
51+
$html = $this->document->saveHTML($resultNode);
5352

54-
return $this->replacePlaceholders($this->document->saveHTML($resultNode));
53+
$html = $this->replacePlaceholders($html);
54+
55+
return $html;
5556
}
5657

5758
public function convertNode(DOMNode $node): DOMNode
@@ -96,13 +97,13 @@ private function handleAttributeBinding(DOMElement $node)
9697
foreach (iterator_to_array($node->attributes) as $attribute) {
9798

9899
if (strpos($attribute->name, 'v-bind:') !== 0 && strpos($attribute->name, ':') !== 0) {
99-
$this->logger->debug("- skip: " . $attribute->name);
100+
$this->logger->debug("- skip: ".$attribute->name);
100101
continue;
101102
}
102103

103104
$name = substr($attribute->name, strpos($attribute->name, ':') + 1);
104105
$value = $attribute->value;
105-
$this->logger->debug('- handle: ' . $name . ' = ' . $value);
106+
$this->logger->debug('- handle: '.$name.' = '.$value);
106107

107108

108109
switch ($name) {
@@ -121,7 +122,9 @@ private function handleAttributeBinding(DOMElement $node)
121122
$this->logger->debug('- setAttribute "'.$name.'" with value');
122123
$node->setAttribute(
123124
$name,
124-
self::DOUBLE_CURLY_OPEN.$value.self::DOUBLE_CURLY_CLOSE
125+
Replacements::getSanitizedConstant('DOUBLE_CURLY_OPEN') .
126+
$value .
127+
Replacements::getSanitizedConstant('DOUBLE_CURLY_CLOSE')
125128
);
126129
}
127130
}
@@ -147,7 +150,7 @@ private function handleAttributeBinding(DOMElement $node)
147150
}
148151
}
149152

150-
$this->logger->debug('=> remove original ' . $attribute->name);
153+
$this->logger->debug('=> remove original '.$attribute->name);
151154
$node->removeAttribute($attribute->name);
152155
}
153156
}
@@ -162,9 +165,10 @@ private function handleIf(DOMElement $node): void
162165

163166
if ($node->hasAttribute('v-if')) {
164167
$condition = $node->getAttribute('v-if');
168+
$condition = $this->sanitizeCondition($condition);
165169

166170
// Open with if
167-
$openIf = $this->document->createTextNode('{% if ' . $condition . ' %}');
171+
$openIf = $this->document->createTextNode('{% if '.$condition.' %}');
168172
$node->parentNode->insertBefore($openIf, $node);
169173

170174
// Close with endif
@@ -176,9 +180,10 @@ private function handleIf(DOMElement $node): void
176180
$node->removeAttribute('v-if');
177181
} elseif ($node->hasAttribute('v-else-if')) {
178182
$condition = $node->getAttribute('v-else-if');
183+
$condition = $this->sanitizeCondition($condition);
179184

180185
// Replace old endif with else
181-
$this->lastCloseIf->textContent = '{% elseif ' . $condition . ' %}';
186+
$this->lastCloseIf->textContent = '{% elseif '.$condition.' %}';
182187

183188
// Close with new endif
184189
$closeIf = $this->document->createTextNode('{% endif %}');
@@ -211,12 +216,18 @@ private function handleFor(DOMElement $node)
211216
/*
212217
* Variations:
213218
* (1) item in array
214-
* (2) key, item in array
215-
* (3) key, item, index in object
219+
* (2)
220+
* (3) key, item in array
221+
* (4) key, item, index in object
216222
*/
217223

224+
// (2)
225+
if (preg_match('/(\d+)/', $listName)) {
226+
$listName = '1..'.$listName;
227+
}
228+
218229
// (1)
219-
$forCommand = '{% for ' . $forLeft . ' in ' . $listName . ' %}';
230+
$forCommand = '{% for '.$forLeft.' in '.$listName.' %}';
220231

221232
if (strpos($forLeft, ',')) {
222233
$forLeft = str_replace('(', '', $forLeft);
@@ -228,12 +239,12 @@ private function handleFor(DOMElement $node)
228239
$forKey = $forLeftArray[1];
229240
$forIndex = $forLeftArray[2] ?? null;
230241

231-
// (2)
232-
$forCommand = '{% for ' . $forKey . ', ' . $forValue . ' in ' . $listName . ' %}';
242+
// (3)
243+
$forCommand = '{% for '.$forKey.', '.$forValue.' in '.$listName.' %}';
233244

234245
if ($forIndex) {
235-
// (3)
236-
$forCommand .= ' {% set ' . $forIndex . ' = loop.index0 %}';
246+
// (4)
247+
$forCommand .= ' {% set '.$forIndex.' = loop.index0 %}';
237248
}
238249
}
239250

@@ -284,10 +295,23 @@ private function getRootNode(DOMElement $element): \DOMNode
284295
return $firstTagNode;
285296
}
286297

298+
protected function sanitizeCondition(string $condition)
299+
{
300+
$condition = str_replace('&&', 'and', $condition);
301+
$condition = str_replace('||', 'or', $condition);
302+
303+
foreach(Replacements::getConstants() as $constant => $value) {
304+
$condition = str_replace($value, Replacements::getSanitizedConstant($constant), $condition);
305+
}
306+
307+
return $condition;
308+
}
309+
287310
protected function replacePlaceholders(string $string)
288311
{
289-
$string = str_replace(self::DOUBLE_CURLY_OPEN, '{{', $string);
290-
$string = str_replace(self::DOUBLE_CURLY_CLOSE, '}}', $string);
312+
foreach(Replacements::getConstants() as $constant => $value) {
313+
$string = str_replace(Replacements::getSanitizedConstant($constant), $value, $string);
314+
}
291315

292316
return $string;
293317
}

src/Models/BasicEnum.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Paneon\VueToTwig\Models;
4+
5+
use ReflectionClass;
6+
use ReflectionException;
7+
8+
abstract class BasicEnum
9+
{
10+
/** @var mixed[] */
11+
private static $constCacheArray = [];
12+
13+
/**
14+
* @return mixed[]
15+
* @throws ReflectionException
16+
*/
17+
public static function getConstants(): array
18+
{
19+
$calledClass = static::class;
20+
21+
if (!array_key_exists($calledClass, self::$constCacheArray)) {
22+
$reflect = new ReflectionClass($calledClass);
23+
self::$constCacheArray[$calledClass] = $reflect->getConstants();
24+
}
25+
26+
return self::$constCacheArray[$calledClass];
27+
}
28+
29+
public static function isValidName(string $name, bool $strict = false): bool
30+
{
31+
try {
32+
$constants = self::getConstants();
33+
} catch (ReflectionException $e) {
34+
return false;
35+
}
36+
37+
if ($strict) {
38+
return array_key_exists($name, $constants);
39+
}
40+
41+
$keys = array_map('strtolower', array_keys($constants));
42+
43+
return in_array(strtolower($name), $keys);
44+
}
45+
46+
/**
47+
* @param mixed $value
48+
*/
49+
public static function isValidValue($value): bool
50+
{
51+
try {
52+
$values = array_values(self::getConstants());
53+
54+
return in_array($value, $values, $strict = true);
55+
} catch (ReflectionException $e) {
56+
return false;
57+
}
58+
}
59+
}

src/Models/Replacements.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Paneon\VueToTwig\Models;
4+
5+
abstract class Replacements extends BasicEnum
6+
{
7+
public const DOUBLE_CURLY_OPEN = '{{';
8+
public const DOUBLE_CURLY_CLOSE = '}}';
9+
public const GREATER = '>';
10+
public const SMALLER = '<';
11+
public const AMPERSAND = '&';
12+
13+
public static function getSanitizedConstant(string $constant)
14+
{
15+
return '__'.$constant.'__';
16+
}
17+
}

tests/AbstractTestCase.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ protected function assertEqualHtml($expectedResult, $result): void
3838

3939
protected function createDocumentWithHtml(string $html): DOMDocument
4040
{
41-
$vueDocument = new DOMDocument();
41+
$vueDocument = new DOMDocument('1.0', 'utf-8');
4242
@$vueDocument->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
4343

4444
return $vueDocument;
@@ -53,10 +53,6 @@ protected function normalizeHtml($html): string
5353
$html = str_replace('> ', ">", $html);
5454
$html = str_replace(' <', "<", $html);
5555

56-
// Each tag (open and close) on a new line
57-
$html = str_replace('>', ">\n", $html);
58-
$html = str_replace('<', "\n<", $html);
59-
6056
// Remove duplicated new lines
6157
$html = str_replace("\n\n", "\n", $html);
6258

@@ -97,4 +93,4 @@ protected function loadFixturesFromDir(string $dir): array
9793

9894
return $cases;
9995
}
100-
}
96+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>
2+
{% for item in 1..5 %}
3+
<div>
4+
{{ item.text }}
5+
</div>
6+
{% endfor %}
7+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<template>
2+
<div>
3+
<div v-for="item in 5">
4+
{{ item.text }}
5+
</div>
6+
</div>
7+
</template>

tests/fixtures/vue-for/replaces-v-for.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@
2121
</div>
2222
{% endfor %}
2323
</div>
24-
</div>
24+
</div>

tests/fixtures/vue-for/replaces-v-for.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
</div>
1313
</div>
1414
</div>
15-
</template>
15+
</template>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div>
2+
{% if variable >= 1 %}
3+
<div>
4+
<h1>Headline</h1>
5+
</div>
6+
{% elseif variable < 100 and variable >= 1 %}
7+
<div>
8+
<h1>Headline</h1>
9+
</div>
10+
{% endif %}
11+
</div>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<template>
2+
<div>
3+
<div v-if="variable >= 1">
4+
<h1>Headline</h1>
5+
</div>
6+
<div v-else-if="variable < 100 && variable >= 1">
7+
<h1>Headline</h1>
8+
</div>
9+
</div>
10+
</template>

0 commit comments

Comments
 (0)