/** * Process the value of the class constant if needed * * @param compilationContext $compilationContext */ public function processValue($compilationContext) { if ($this->value['type'] == 'constant') { $constant = new Constants(); $compiledExpression = $constant->compile($this->value, $compilationContext); $this->value = array('type' => $compiledExpression->getType(), 'value' => $compiledExpression->getCode()); return; } if ($this->value['type'] == 'static-constant-access') { $expression = new Expression($this->value); $compiledExpression = $expression->compile($compilationContext); $this->value = array('type' => $compiledExpression->getType(), 'value' => $compiledExpression->getCode()); return; } }
/** * Produce the code to register a class constant * * @param CompilationContext $compilationContext * @throws CompilerException * @throws Exception */ public function compile(CompilationContext $compilationContext) { if ($this->value['type'] == 'static-constant-access') { $name = $this->value['left']['value'] . '::' . $this->value['right']['value']; if (defined($name)) { $value = constant($name); if (is_int($value)) { $this->value['type'] = 'int'; $this->value['value'] = $value; } elseif (is_float($value)) { $this->value['type'] = 'double'; $this->value['value'] = $value; } elseif (is_bool($value)) { $this->value['type'] = 'bool'; if (!$value) { $this->value['value'] = 'false'; } else { $this->value['value'] = 'true'; } } elseif (is_string($value)) { $this->value['type'] = 'string'; $this->value['value'] = $value; } elseif (is_null($value)) { $this->value['type'] = 'null'; } } } switch ($this->value['type']) { case 'constant': $constant = new Constants(); $compiledExpression = $constant->compile($this->value, $compilationContext); $this->value['type'] = $compiledExpression->getType(); $this->value['value'] = $compiledExpression->getCode(); /** * With no-break this will be broken (unexpected), use re-compile for new type pass * @todo review variant without break */ $this->compile($compilationContext); break; case 'long': case 'int': $compilationContext->codePrinter->output("zend_declare_class_constant_long(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'double': $compilationContext->codePrinter->output("zend_declare_class_constant_double(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'bool': if ($this->value['value'] == 'false') { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 0 TSRMLS_CC);"); } else { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 1 TSRMLS_CC);"); } break; case 'string': case 'char': $compilationContext->codePrinter->output("zend_declare_class_constant_string(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), \"" . Utils::addSlashes($this->value['value']) . "\" TSRMLS_CC);"); break; case 'null': $compilationContext->codePrinter->output("zend_declare_class_constant_null(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\") TSRMLS_CC);"); break; default: throw new CompilerException('Type "' . $this->value['type'] . '" is not supported.'); } }
/** * Resolves an expression * * @param CompilationContext $compilationContext * @return bool|CompiledExpression|mixed * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $expression = $this->_expression; $type = $expression['type']; switch ($type) { case 'null': return new LiteralCompiledExpression('null', null, $expression); case 'int': case 'integer': return new LiteralCompiledExpression('int', $expression['value'], $expression); case 'long': return new LiteralCompiledExpression('long', $expression['value'], $expression); case 'double': return new LiteralCompiledExpression('double', $expression['value'], $expression); case 'bool': return new LiteralCompiledExpression('bool', $expression['value'], $expression); case 'string': if (!$this->_stringOperation) { if (ctype_digit($expression['value'])) { return new CompiledExpression('int', $expression['value'], $expression); } } return new LiteralCompiledExpression('string', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'istring': return new LiteralCompiledExpression('istring', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'char': if (!strlen($expression['value'])) { throw new CompilerException("Invalid empty char literal", $expression); } if (strlen($expression['value']) > 2) { if (strlen($expression['value']) > 10) { throw new CompilerException("Invalid char literal: '" . substr($expression['value'], 0, 10) . "...'", $expression); } else { throw new CompilerException("Invalid char literal: '" . $expression['value'] . "'", $expression); } } return new LiteralCompiledExpression('char', $expression['value'], $expression); case 'variable': return new CompiledExpression('variable', $expression['value'], $expression); case 'constant': $constant = new Constants(); $constant->setReadOnly($this->isReadOnly()); $constant->setExpectReturn($this->_expecting, $this->_expectingVariable); return $constant->compile($expression, $compilationContext); case 'empty-array': return $this->emptyArray($expression, $compilationContext); case 'array-access': $arrayAccess = new NativeArrayAccess(); $arrayAccess->setReadOnly($this->isReadOnly()); $arrayAccess->setNoisy($this->isNoisy()); $arrayAccess->setExpectReturn($this->_expecting, $this->_expectingVariable); return $arrayAccess->compile($expression, $compilationContext); case 'property-access': $propertyAccess = new PropertyAccess(); $propertyAccess->setReadOnly($this->isReadOnly()); $propertyAccess->setNoisy($this->isNoisy()); $propertyAccess->setExpectReturn($this->_expecting, $this->_expectingVariable); return $propertyAccess->compile($expression, $compilationContext); case 'property-string-access': case 'property-dynamic-access': $propertyAccess = new PropertyDynamicAccess(); $propertyAccess->setReadOnly($this->isReadOnly()); $propertyAccess->setNoisy($this->isNoisy()); $propertyAccess->setExpectReturn($this->_expecting, $this->_expectingVariable); return $propertyAccess->compile($expression, $compilationContext); case 'static-constant-access': $staticConstantAccess = new StaticConstantAccess(); $staticConstantAccess->setReadOnly($this->isReadOnly()); $staticConstantAccess->setExpectReturn($this->_expecting, $this->_expectingVariable); return $staticConstantAccess->compile($expression, $compilationContext); case 'static-property-access': $staticPropertyAccess = new StaticPropertyAccess(); $staticPropertyAccess->setReadOnly($this->isReadOnly()); $staticPropertyAccess->setExpectReturn($this->_expecting, $this->_expectingVariable); return $staticPropertyAccess->compile($expression, $compilationContext); case 'fcall': $functionCall = new FunctionCall(); return $functionCall->compile($this, $compilationContext); case 'mcall': $methodCall = new MethodCall(); return $methodCall->compile($this, $compilationContext); case 'scall': $staticCall = new StaticCall(); return $staticCall->compile($this, $compilationContext); case 'isset': $expr = new IssetOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'fetch': $expr = new FetchOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'empty': $expr = new EmptyOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'array': $array = new NativeArray(); $array->setReadOnly($this->isReadOnly()); $array->setExpectReturn($this->_expecting, $this->_expectingVariable); return $array->compile($expression, $compilationContext); case 'new': $expr = new NewInstanceOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'new-type': $expr = new NewInstanceTypeOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'not': $expr = new NotOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_not': $expr = new BitwiseNotOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'equals': $expr = new EqualsOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'not-equals': $expr = new NotEqualsOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'identical': $expr = new IdenticalOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'not-identical': $expr = new NotIdenticalOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'greater': $expr = new GreaterOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'less': $expr = new LessOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'less-equal': $expr = new LessEqualOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'greater-equal': $expr = new GreaterEqualOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'add': $expr = new AddOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'minus': $expr = new MinusOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'sub': $expr = new SubOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'mul': $expr = new MulOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'div': $expr = new DivOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'mod': $expr = new ModOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'and': $expr = new AndOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'or': $expr = new OrOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_and': $expr = new BitwiseAndOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_or': $expr = new BitwiseOrOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_xor': $expr = new BitwiseXorOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_shiftleft': $expr = new ShiftLeftOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'bitwise_shiftright': $expr = new ShiftRightOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'concat': $expr = new ConcatOperator(); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'irange': $expr = new RangeInclusiveOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'erange': $expr = new RangeExclusiveOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'list': if ($expression['left']['type'] == 'list') { $compilationContext->logger->warning("Unnecessary extra parentheses", "extra-parentheses", $expression); } $numberPrints = $compilationContext->codePrinter->getNumberPrints(); $expr = new Expression($expression['left']); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); $resolved = $expr->compile($compilationContext); if ($compilationContext->codePrinter->getNumberPrints() - $numberPrints <= 1) { if (strpos($resolved->getCode(), ' ') !== false) { return new CompiledExpression($resolved->getType(), '(' . $resolved->getCode() . ')', $expression); } } return $resolved; case 'cast': $expr = new CastOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'type-hint': return $this->compileTypeHint($expression, $compilationContext); case 'instanceof': $expr = new InstanceOfOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'clone': $expr = new CloneOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'ternary': $expr = new TernaryOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'likely': if (!$this->_evalMode) { throw new CompilerException("'likely' operator can only be used in evaluation expressions", $expression); } $expr = new LikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'unlikely': if (!$this->_evalMode) { throw new CompilerException("'unlikely' operator can only be used in evaluation expressions", $expression); } $expr = new UnlikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'typeof': $expr = new TypeOfOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'require': $expr = new RequireOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'closure': $closure = new Closure(); $closure->setReadOnly($this->isReadOnly()); $closure->setExpectReturn($this->_expecting, $this->_expectingVariable); return $closure->compile($expression, $compilationContext); case 'closure-arrow': $closure = new ClosureArrow(); $closure->setReadOnly($this->isReadOnly()); $closure->setExpectReturn($this->_expecting, $this->_expectingVariable); return $closure->compile($expression, $compilationContext); case 'reference': $reference = new Reference(); $reference->setReadOnly($this->isReadOnly()); $reference->setExpectReturn($this->_expecting, $this->_expectingVariable); return $reference->compile($expression, $compilationContext); default: throw new CompilerException("Unknown expression: " . $type, $expression); } }
/** * Produce the code to register a class constant * * @param CompilationContext $compilationContext * @throws CompilerException * @throws Exception */ public function compile(CompilationContext $compilationContext) { if ($this->value['type'] == 'static-constant-access') { $this->resolveClassConstant(); } switch ($this->value['type']) { case 'constant': $constant = new Constants(); $compiledExpression = $constant->compile($this->value, $compilationContext); $this->value['type'] = $compiledExpression->getType(); $this->value['value'] = $compiledExpression->getCode(); $this->compile($compilationContext); return; case 'long': case 'int': $compilationContext->codePrinter->output("zend_declare_class_constant_long(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'double': $compilationContext->codePrinter->output("zend_declare_class_constant_double(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'bool': if ($this->value['value'] == 'false') { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 0 TSRMLS_CC);"); } else { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 1 TSRMLS_CC);"); } break; case 'string': case 'char': $compilationContext->codePrinter->output("zend_declare_class_constant_string(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), \"" . Utils::addSlashes($this->value['value']) . "\" TSRMLS_CC);"); break; case 'null': $compilationContext->codePrinter->output("zend_declare_class_constant_null(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\") TSRMLS_CC);"); break; default: throw new CompilerException('Type "' . $this->value['type'] . '" is not supported.'); } }