Skip to content

Commit fb51f7b

Browse files
authored
Merge pull request #34 from pug-php/magic-methods
Improve magic methods support
2 parents 41a3f8e + 6d3137e commit fb51f7b

File tree

13 files changed

+642
-78
lines changed

13 files changed

+642
-78
lines changed

examples/replace.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
var url = 'https://www.pugjs.org';
22

3-
return url.replace('https://', '').replace(/^www\./, '');
3+
return url.replace('https://', '').replace(/^WWW\./i, '');

src/JsPhpize/Compiler/Compiler.php

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
class Compiler
2222
{
23+
const DOT_DISABLED = 1;
24+
2325
use DyiadeTrait;
2426
use InterpolationTrait;
2527

@@ -99,7 +101,8 @@ protected function getBlockHead(Block $block, $indent)
99101
' => $__current_value)';
100102
}
101103

102-
return $block->type . ($block->value
104+
return $block->type . (
105+
$block->value
103106
? ' ' . $this->visitNode($block->value, $indent)
104107
: ''
105108
);
@@ -204,12 +207,12 @@ protected function visitDyiade(Dyiade $dyiade, $indent)
204207
return $leftHand . ' ' . $dyiade->operator . ' ' . $rightHand;
205208
}
206209

207-
protected function mapNodesArray($array, $indent, $pattern = null)
210+
protected function mapNodesArray($array, $indent, $pattern = null, $options = 0)
208211
{
209212
$visitNode = [$this, 'visitNode'];
210213

211-
return array_map(function ($value) use ($visitNode, $indent, $pattern) {
212-
$value = $visitNode($value, $indent);
214+
return array_map(function ($value) use ($visitNode, $indent, $pattern, $options) {
215+
$value = $visitNode($value, $indent, $options);
213216

214217
if ($pattern) {
215218
$value = sprintf($pattern, $value);
@@ -219,17 +222,23 @@ protected function mapNodesArray($array, $indent, $pattern = null)
219222
}, $array);
220223
}
221224

222-
protected function visitNodesArray($array, $indent, $glue = '', $pattern = null)
225+
protected function visitNodesArray($array, $indent, $glue = '', $pattern = null, $options = 0)
223226
{
224-
return implode($glue, $this->mapNodesArray($array, $indent, $pattern));
227+
return implode($glue, $this->mapNodesArray($array, $indent, $pattern, $options));
225228
}
226229

227230
protected function visitFunctionCall(FunctionCall $functionCall, $indent)
228231
{
229232
$function = $functionCall->function;
230233
$arguments = $functionCall->arguments;
231234
$applicant = $functionCall->applicant;
232-
$arguments = $this->visitNodesArray($arguments, $indent, ', ');
235+
$arguments = $this->visitNodesArray(
236+
$arguments,
237+
$indent,
238+
', ',
239+
null,
240+
$function instanceof Variable && $function->name === 'isset' ? static::DOT_DISABLED : 0
241+
);
233242
$dynamicCall = $this->visitNode($function, $indent) . '(' . $arguments . ')';
234243

235244
if ($function instanceof Variable && count($function->children) === 0) {
@@ -272,9 +281,11 @@ protected function visitInstruction(Instruction $group, $indent)
272281
$value = $visitNode($instruction, $indent);
273282

274283
return $indent .
275-
($instruction instanceof Block && $instruction->handleInstructions()
284+
(
285+
$instruction instanceof Block && $instruction->handleInstructions()
276286
? $value
277-
: ($isReturnPrepended && !preg_match('/^\s*return(?![a-zA-Z0-9_])/', $value)
287+
: (
288+
$isReturnPrepended && !preg_match('/^\s*return(?![a-zA-Z0-9_])/', $value)
278289
? ' return '
279290
: ''
280291
) . $value . ';'
@@ -283,14 +294,14 @@ protected function visitInstruction(Instruction $group, $indent)
283294
}, $group->instructions));
284295
}
285296

286-
public function visitNode(Node $node, $indent)
297+
public function visitNode(Node $node, $indent, $options = 0)
287298
{
288299
$method = preg_replace(
289300
'/^(.+\\\\)?([^\\\\]+)$/',
290301
'visit$2',
291302
get_class($node)
292303
);
293-
$php = method_exists($this, $method) ? $this->$method($node, $indent) : '';
304+
$php = method_exists($this, $method) ? $this->$method($node, $indent, $options) : '';
294305

295306
if ($node instanceof Value) {
296307
$php = $node->getBefore() . $php . $node->getAfter();
@@ -311,19 +322,41 @@ protected function visitTernary(Ternary $ternary, $indent)
311322
' : ' . $this->visitNode($ternary->falseValue, $indent);
312323
}
313324

314-
protected function handleVariableChildren(DynamicValue $dynamicValue, $indent, $php)
325+
protected function handleVariableChildren(DynamicValue $dynamicValue, $indent, $php, $options = 0)
315326
{
316-
if (count($dynamicValue->children)) {
317-
$arguments = $this->mapNodesArray($dynamicValue->children, $indent);
318-
array_unshift($arguments, $php);
319-
$dot = $this->engine->getHelperName('dot');
320-
$php = $this->helperWrap($dot, $arguments);
327+
$children = $dynamicValue->children;
328+
329+
if (count($children)) {
330+
return $this->wrapVariableChildren($children, $indent, $php, $options);
321331
}
322332

323333
return $php;
324334
}
325335

326-
protected function visitVariable(Variable $variable, $indent)
336+
protected function wrapVariableChildren($children, $indent, $php, $options)
337+
{
338+
$arguments = $this->mapNodesArray($children, $indent);
339+
array_unshift($arguments, $php);
340+
$dot = $this->engine->getHelperName('dot');
341+
$dotDisabled = $options & static::DOT_DISABLED;
342+
343+
if ($dotDisabled) {
344+
$lastChild = end($children);
345+
$dotChild = $lastChild instanceof Constant && $lastChild->dotChild;
346+
$lastChild = array_pop($arguments);
347+
}
348+
349+
$php = $this->helperWrap($dot, $arguments);
350+
351+
if ($dotDisabled) {
352+
$pattern = $dotChild ? '%s->{%s}' : '%s[%s]';
353+
$php = sprintf($pattern, $php, $lastChild);
354+
}
355+
356+
return $php;
357+
}
358+
359+
protected function visitVariable(Variable $variable, $indent, $options = 0)
327360
{
328361
$name = $variable->name;
329362
if (in_array($name, ['Math', 'RegExp'])) {
@@ -332,8 +365,11 @@ protected function visitVariable(Variable $variable, $indent)
332365
if ($variable->scope) {
333366
$name = '__let_' . spl_object_hash($variable->scope) . $name;
334367
}
368+
if (!$this->engine->getOption('ignoreDollarVariable') || mb_substr($name, 0, 1) !== '$') {
369+
$name = '$' . $name;
370+
}
335371

336-
return $this->handleVariableChildren($variable, $indent, '$' . $name);
372+
return $this->handleVariableChildren($variable, $indent, $name, $options);
337373
}
338374

339375
public function compile(Block $block, $indent = '')

src/JsPhpize/Compiler/Helpers/Dot.h

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ function ($base) {
66
};
77
$getCallable = function ($base, $key) use ($getFromArray) {
88
if (is_callable(array($base, $key))) {
9-
return array($base, $key);
9+
return new JsPhpizeDotCarrier(array($base, $key));
1010
}
1111
if ($base instanceof \ArrayAccess) {
1212
return $getFromArray($base, $key);
1313
}
1414
};
1515
$getRegExp = function ($value) {
16-
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp : null;
16+
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp . $value->flags : null;
1717
};
1818
$fallbackDot = function ($base, $key) use ($getCallable, $getRegExp) {
1919
if (is_string($base)) {
@@ -99,4 +99,79 @@ function ($base) {
9999
}
100100

101101
return $base;
102+
};
103+
104+
if (!class_exists('JsPhpizeDotCarrier')) {
105+
class JsPhpizeDotCarrier extends ArrayObject
106+
{
107+
public function getValue()
108+
{
109+
if ($this->isArrayAccessible()) {
110+
return $this[0][$this[1]];
111+
}
112+
113+
return $this[0]->{$this[1]} ?? null;
114+
}
115+
116+
public function setValue($value)
117+
{
118+
if ($this->isArrayAccessible()) {
119+
$this[0][$this[1]] = $value;
120+
121+
return;
122+
}
123+
124+
$this[0]->{$this[1]} = $value;
125+
}
126+
127+
public function getCallable()
128+
{
129+
return $this->getArrayCopy();
130+
}
131+
132+
public function __isset($name)
133+
{
134+
$value = $this->getValue();
135+
136+
if ((is_array($value) || $value instanceof ArrayAccess) && isset($value[$name])) {
137+
return true;
138+
}
139+
140+
return is_object($value) && isset($value->$name);
141+
}
142+
143+
public function __get($name)
144+
{
145+
return new self(array($this->getValue(), $name));
146+
}
147+
148+
public function __set($name, $value)
149+
{
150+
$value = $this->getValue();
151+
152+
if (is_array($value)) {
153+
$value[$name] = $value;
154+
$this->setValue($value);
155+
156+
return;
157+
}
158+
159+
$value->$name = $value;
160+
}
161+
162+
public function __toString()
163+
{
164+
return (string) $this->getValue();
165+
}
166+
167+
public function __invoke(...$arguments)
168+
{
169+
return call_user_func_array($this->getCallable(), $arguments);
170+
}
171+
172+
private function isArrayAccessible()
173+
{
174+
return is_array($this[0]) || $this[0] instanceof ArrayAccess && !isset($this[0]->{$this[1]});
175+
}
176+
}
102177
}

src/JsPhpize/Compiler/Helpers/Dot.ref.h

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ function (&$base) {
66
};
77
$getCallable = function (&$base, $key) use ($getFromArray) {
88
if (is_callable(array($base, $key))) {
9-
return array($base, $key);
9+
return new JsPhpizeDotCarrier(array($base, $key));
1010
}
1111
if ($base instanceof \ArrayAccess) {
1212
return $getFromArray($base, $key);
1313
}
1414
};
1515
$getRegExp = function ($value) {
16-
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp : null;
16+
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp . $value->flags : null;
1717
};
1818
$fallbackDot = function (&$base, $key) use ($getCallable, $getRegExp) {
1919
if (is_string($base)) {
@@ -102,4 +102,79 @@ function (&$base) {
102102
}
103103

104104
return $result;
105+
};
106+
107+
if (!class_exists('JsPhpizeDotCarrier')) {
108+
class JsPhpizeDotCarrier extends ArrayObject
109+
{
110+
public function getValue()
111+
{
112+
if ($this->isArrayAccessible()) {
113+
return $this[0][$this[1]];
114+
}
115+
116+
return $this[0]->{$this[1]} ?? null;
117+
}
118+
119+
public function setValue($value)
120+
{
121+
if ($this->isArrayAccessible()) {
122+
$this[0][$this[1]] = $value;
123+
124+
return;
125+
}
126+
127+
$this[0]->{$this[1]} = $value;
128+
}
129+
130+
public function getCallable()
131+
{
132+
return $this->getArrayCopy();
133+
}
134+
135+
public function __isset($name)
136+
{
137+
$value = $this->getValue();
138+
139+
if ((is_array($value) || $value instanceof ArrayAccess) && isset($value[$name])) {
140+
return true;
141+
}
142+
143+
return is_object($value) && isset($value->$name);
144+
}
145+
146+
public function __get($name)
147+
{
148+
return new self(array($this->getValue(), $name));
149+
}
150+
151+
public function __set($name, $value)
152+
{
153+
$value = $this->getValue();
154+
155+
if (is_array($value)) {
156+
$value[$name] = $value;
157+
$this->setValue($value);
158+
159+
return;
160+
}
161+
162+
$value->$name = $value;
163+
}
164+
165+
public function __toString()
166+
{
167+
return (string) $this->getValue();
168+
}
169+
170+
public function __invoke(...$arguments)
171+
{
172+
return call_user_func_array($this->getCallable(), $arguments);
173+
}
174+
175+
private function isArrayAccessible()
176+
{
177+
return is_array($this[0]) || $this[0] instanceof ArrayAccess && !isset($this[0]->{$this[1]});
178+
}
179+
}
105180
}

0 commit comments

Comments
 (0)