Skip to content

Improve magic methods support #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/replace.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
var url = 'https://www.pugjs.org';

return url.replace('https://', '').replace(/^www\./, '');
return url.replace('https://', '').replace(/^WWW\./i, '');
74 changes: 55 additions & 19 deletions src/JsPhpize/Compiler/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

class Compiler
{
const DOT_DISABLED = 1;

use DyiadeTrait;
use InterpolationTrait;

Expand Down Expand Up @@ -99,7 +101,8 @@ protected function getBlockHead(Block $block, $indent)
' => $__current_value)';
}

return $block->type . ($block->value
return $block->type . (
$block->value
? ' ' . $this->visitNode($block->value, $indent)
: ''
);
Expand Down Expand Up @@ -204,12 +207,12 @@ protected function visitDyiade(Dyiade $dyiade, $indent)
return $leftHand . ' ' . $dyiade->operator . ' ' . $rightHand;
}

protected function mapNodesArray($array, $indent, $pattern = null)
protected function mapNodesArray($array, $indent, $pattern = null, $options = 0)
{
$visitNode = [$this, 'visitNode'];

return array_map(function ($value) use ($visitNode, $indent, $pattern) {
$value = $visitNode($value, $indent);
return array_map(function ($value) use ($visitNode, $indent, $pattern, $options) {
$value = $visitNode($value, $indent, $options);

if ($pattern) {
$value = sprintf($pattern, $value);
Expand All @@ -219,17 +222,23 @@ protected function mapNodesArray($array, $indent, $pattern = null)
}, $array);
}

protected function visitNodesArray($array, $indent, $glue = '', $pattern = null)
protected function visitNodesArray($array, $indent, $glue = '', $pattern = null, $options = 0)
{
return implode($glue, $this->mapNodesArray($array, $indent, $pattern));
return implode($glue, $this->mapNodesArray($array, $indent, $pattern, $options));
}

protected function visitFunctionCall(FunctionCall $functionCall, $indent)
{
$function = $functionCall->function;
$arguments = $functionCall->arguments;
$applicant = $functionCall->applicant;
$arguments = $this->visitNodesArray($arguments, $indent, ', ');
$arguments = $this->visitNodesArray(
$arguments,
$indent,
', ',
null,
$function instanceof Variable && $function->name === 'isset' ? static::DOT_DISABLED : 0
);
$dynamicCall = $this->visitNode($function, $indent) . '(' . $arguments . ')';

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

return $indent .
($instruction instanceof Block && $instruction->handleInstructions()
(
$instruction instanceof Block && $instruction->handleInstructions()
? $value
: ($isReturnPrepended && !preg_match('/^\s*return(?![a-zA-Z0-9_])/', $value)
: (
$isReturnPrepended && !preg_match('/^\s*return(?![a-zA-Z0-9_])/', $value)
? ' return '
: ''
) . $value . ';'
Expand All @@ -283,14 +294,14 @@ protected function visitInstruction(Instruction $group, $indent)
}, $group->instructions));
}

public function visitNode(Node $node, $indent)
public function visitNode(Node $node, $indent, $options = 0)
{
$method = preg_replace(
'/^(.+\\\\)?([^\\\\]+)$/',
'visit$2',
get_class($node)
);
$php = method_exists($this, $method) ? $this->$method($node, $indent) : '';
$php = method_exists($this, $method) ? $this->$method($node, $indent, $options) : '';

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

protected function handleVariableChildren(DynamicValue $dynamicValue, $indent, $php)
protected function handleVariableChildren(DynamicValue $dynamicValue, $indent, $php, $options = 0)
{
if (count($dynamicValue->children)) {
$arguments = $this->mapNodesArray($dynamicValue->children, $indent);
array_unshift($arguments, $php);
$dot = $this->engine->getHelperName('dot');
$php = $this->helperWrap($dot, $arguments);
$children = $dynamicValue->children;

if (count($children)) {
return $this->wrapVariableChildren($children, $indent, $php, $options);
}

return $php;
}

protected function visitVariable(Variable $variable, $indent)
protected function wrapVariableChildren($children, $indent, $php, $options)
{
$arguments = $this->mapNodesArray($children, $indent);
array_unshift($arguments, $php);
$dot = $this->engine->getHelperName('dot');
$dotDisabled = $options & static::DOT_DISABLED;

if ($dotDisabled) {
$lastChild = end($children);
$dotChild = $lastChild instanceof Constant && $lastChild->dotChild;
$lastChild = array_pop($arguments);
}

$php = $this->helperWrap($dot, $arguments);

if ($dotDisabled) {
$pattern = $dotChild ? '%s->{%s}' : '%s[%s]';
$php = sprintf($pattern, $php, $lastChild);
}

return $php;
}

protected function visitVariable(Variable $variable, $indent, $options = 0)
{
$name = $variable->name;
if (in_array($name, ['Math', 'RegExp'])) {
Expand All @@ -332,8 +365,11 @@ protected function visitVariable(Variable $variable, $indent)
if ($variable->scope) {
$name = '__let_' . spl_object_hash($variable->scope) . $name;
}
if (!$this->engine->getOption('ignoreDollarVariable') || mb_substr($name, 0, 1) !== '$') {
$name = '$' . $name;
}

return $this->handleVariableChildren($variable, $indent, '$' . $name);
return $this->handleVariableChildren($variable, $indent, $name, $options);
}

public function compile(Block $block, $indent = '')
Expand Down
79 changes: 77 additions & 2 deletions src/JsPhpize/Compiler/Helpers/Dot.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ function ($base) {
};
$getCallable = function ($base, $key) use ($getFromArray) {
if (is_callable(array($base, $key))) {
return array($base, $key);
return new JsPhpizeDotCarrier(array($base, $key));
}
if ($base instanceof \ArrayAccess) {
return $getFromArray($base, $key);
}
};
$getRegExp = function ($value) {
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp : null;
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp . $value->flags : null;
};
$fallbackDot = function ($base, $key) use ($getCallable, $getRegExp) {
if (is_string($base)) {
Expand Down Expand Up @@ -99,4 +99,79 @@ function ($base) {
}

return $base;
};

if (!class_exists('JsPhpizeDotCarrier')) {
class JsPhpizeDotCarrier extends ArrayObject
{
public function getValue()
{
if ($this->isArrayAccessible()) {
return $this[0][$this[1]];
}

return $this[0]->{$this[1]} ?? null;
}

public function setValue($value)
{
if ($this->isArrayAccessible()) {
$this[0][$this[1]] = $value;

return;
}

$this[0]->{$this[1]} = $value;
}

public function getCallable()
{
return $this->getArrayCopy();
}

public function __isset($name)
{
$value = $this->getValue();

if ((is_array($value) || $value instanceof ArrayAccess) && isset($value[$name])) {
return true;
}

return is_object($value) && isset($value->$name);
}

public function __get($name)
{
return new self(array($this->getValue(), $name));
}

public function __set($name, $value)
{
$value = $this->getValue();

if (is_array($value)) {
$value[$name] = $value;
$this->setValue($value);

return;
}

$value->$name = $value;
}

public function __toString()
{
return (string) $this->getValue();
}

public function __invoke(...$arguments)
{
return call_user_func_array($this->getCallable(), $arguments);
}

private function isArrayAccessible()
{
return is_array($this[0]) || $this[0] instanceof ArrayAccess && !isset($this[0]->{$this[1]});
}
}
}
79 changes: 77 additions & 2 deletions src/JsPhpize/Compiler/Helpers/Dot.ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ function (&$base) {
};
$getCallable = function (&$base, $key) use ($getFromArray) {
if (is_callable(array($base, $key))) {
return array($base, $key);
return new JsPhpizeDotCarrier(array($base, $key));
}
if ($base instanceof \ArrayAccess) {
return $getFromArray($base, $key);
}
};
$getRegExp = function ($value) {
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp : null;
return is_object($value) && isset($value->isRegularExpression) && $value->isRegularExpression ? $value->regExp . $value->flags : null;
};
$fallbackDot = function (&$base, $key) use ($getCallable, $getRegExp) {
if (is_string($base)) {
Expand Down Expand Up @@ -102,4 +102,79 @@ function (&$base) {
}

return $result;
};

if (!class_exists('JsPhpizeDotCarrier')) {
class JsPhpizeDotCarrier extends ArrayObject
{
public function getValue()
{
if ($this->isArrayAccessible()) {
return $this[0][$this[1]];
}

return $this[0]->{$this[1]} ?? null;
}

public function setValue($value)
{
if ($this->isArrayAccessible()) {
$this[0][$this[1]] = $value;

return;
}

$this[0]->{$this[1]} = $value;
}

public function getCallable()
{
return $this->getArrayCopy();
}

public function __isset($name)
{
$value = $this->getValue();

if ((is_array($value) || $value instanceof ArrayAccess) && isset($value[$name])) {
return true;
}

return is_object($value) && isset($value->$name);
}

public function __get($name)
{
return new self(array($this->getValue(), $name));
}

public function __set($name, $value)
{
$value = $this->getValue();

if (is_array($value)) {
$value[$name] = $value;
$this->setValue($value);

return;
}

$value->$name = $value;
}

public function __toString()
{
return (string) $this->getValue();
}

public function __invoke(...$arguments)
{
return call_user_func_array($this->getCallable(), $arguments);
}

private function isArrayAccessible()
{
return is_array($this[0]) || $this[0] instanceof ArrayAccess && !isset($this[0]->{$this[1]});
}
}
}
Loading