Skip to content
Closed
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
7 changes: 7 additions & 0 deletions phpunit/code/re-assign-numeric-without-using-native-types.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

function main()
{
$x = 1;
$x = [];
};
5 changes: 5 additions & 0 deletions phpunit/src/AssignTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ public function testReAssign()
$this->exec('Cannot re-assign `$obj` from `php::Str` to `php::Object`', 're-assign.php');
}

public function testReAssignNumericValueWithoutUsingNativeTypes()
{
$this->exec('Cannot re-assign `$x` from `php::Array` to `php::Int', "re-assign-numeric-without-using-native-types.php");
}

public function testAssignClass()
{
$this->exec('Cannot re-assign typed object `$obj1` from `stdClass` to `ArrayObject`', 're-assign-2.php');
Expand Down
98 changes: 62 additions & 36 deletions src/CompilerBase.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/**
* This file is part of TypePHP.
*
Expand Down Expand Up @@ -512,7 +513,7 @@ protected function getBoolValue(Expr\ConstFetch $expr): string
// Windows platform: store the detected PHP lib file paths.
protected string $windowsPhpEmbedLib = ''; // Path to php8embed.lib
protected string $windowsPhpCoreLib = ''; // Path to php8ts.lib or php8.lib

// New platform and compiler abstraction layers (optional to use).
protected ?PlatformBase $platform = null;
protected ?CompilerBackend $compilerBackend = null;
Expand Down Expand Up @@ -1616,7 +1617,8 @@ protected function parseIdentifier(Node $expr): string
if ($expr instanceof Node\Name || $expr instanceof Node\VarLikeIdentifier || $expr instanceof Node\Identifier) {
return $expr->toString();
}
if ($expr instanceof Node\Scalar\Int_
if (
$expr instanceof Node\Scalar\Int_
|| $expr instanceof Node\Scalar\Float_
|| $expr instanceof Node\Scalar\String_
) {
Expand Down Expand Up @@ -1976,8 +1978,10 @@ protected function resolveCalledFunctionDef(NodeAbstract $expr): ?FunctionDef
}
return $class === '' ? null : $this->findAotMethodFunctionDef($class, $expr->name->toString());
}
if ($expr instanceof Expr\StaticCall && $expr->class instanceof Node\Name
&& $expr->name instanceof Node\Identifier) {
if (
$expr instanceof Expr\StaticCall && $expr->class instanceof Node\Name
&& $expr->name instanceof Node\Identifier
) {
$class = $this->parseIdentifier($expr->class);
if ($class === 'self' || $class === 'static') {
$class = $this->getFullClassName();
Expand Down Expand Up @@ -2059,7 +2063,7 @@ protected function detectClassOfExpr(NodeAbstract $expr): string
}
if ($expr instanceof Expr\Match_) {
return $this->getCommonNativeObjectExpressionClass(array_map(
static fn (Node\MatchArm $arm): Expr => $arm->body,
static fn(Node\MatchArm $arm): Expr => $arm->body,
$expr->arms,
));
}
Expand Down Expand Up @@ -2113,7 +2117,8 @@ protected function detectClassOfExpr(NodeAbstract $expr): string
$receiverClass = $this->detectClassOfExpr($expr->var);
if ($this->isNativeObjectClass($receiverClass)) {
$property = $this->findNativeObjectProperty($receiverClass, $expr->name->toString());
if ($property !== null
if (
$property !== null
&& $property->type === Type::OBJECT
&& $this->isNativeObjectClass($property->class)
) {
Expand Down Expand Up @@ -2230,7 +2235,8 @@ protected function isObjectClassStaticallyAssignableTo(string $class, string $ex
return true;
}

if (!$this->hasClass($class)
if (
!$this->hasClass($class)
&& !$this->hasInterface($class)
&& !$this->isInternalClass($class)
&& !$this->isInternalInterface($class)
Expand Down Expand Up @@ -2382,10 +2388,12 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
return 'return php::toReferenceExact(' . $this->parseExpr($v->expr) . ');';
}
}
if (!$this->isVarExpr($v->expr)
if (
!$this->isVarExpr($v->expr)
&& !$this->isPropertyFetch($v->expr)
&& !$this->isStaticPropertyFetch($v->expr)
&& !$this->isArrayDimFetch($v->expr)) {
&& !$this->isArrayDimFetch($v->expr)
) {
$this->fatalError($v, 'A function returning by reference must return a variable');
}
if ($this->isVarExpr($v->expr)) {
Expand Down Expand Up @@ -2419,7 +2427,8 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
return 'return ' . $this->parseChainedExpr($v->expr, self::OP_REFVAL) . ';';
}
if ($v->expr === null) {
if (!$this->context->inClosure
if (
!$this->context->inClosure
&& $this->getNativeObjectReturnType($this->functionDef) !== null
) {
$this->fatalError(
Expand Down Expand Up @@ -2540,7 +2549,8 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
'return value'
);
}
if (!$this->context->inClosure
if (
!$this->context->inClosure
&& ($nativeReturnClass = $this->getReturnClass()) !== ''
&& $this->isNativeObjectClass($nativeReturnClass)
) {
Expand All @@ -2551,7 +2561,8 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
return 'return nullptr;';
}
$objectClass = $this->detectClassOfExpr($v->expr);
if ($objectClass === '' || !$this->isNativeObjectClass($objectClass)
if (
$objectClass === '' || !$this->isNativeObjectClass($objectClass)
|| !$this->isObjectClassStaticallyAssignableTo($objectClass, $nativeReturnClass)
) {
$this->fatalError(
Expand All @@ -2564,7 +2575,7 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
$returnCode = $this->functionDef->returnNullable
? $returnExpr
: 'php::nativeRequireObject(' . $returnExpr . ', "'
. addslashes($nativeReturnClass) . '")';
. addslashes($nativeReturnClass) . '")';

if (count($this->context->afterStmtLines) === $afterStmtCount) {
return 'return ' . $returnCode . ';';
Expand All @@ -2581,7 +2592,7 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
$finalReturn = $this->functionDef->returnNullable
? $tmpVar
: 'php::nativeRequireObject(' . $tmpVar . ', "'
. addslashes($nativeReturnClass) . '")';
. addslashes($nativeReturnClass) . '")';
$this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $finalReturn . ';';
return $tmpVar . ' = ' . $returnExpr . ';';
}
Expand All @@ -2597,9 +2608,11 @@ protected function parseReturn(Node\Stmt\Return_ $v): string
$returnType = Type::VAR;
}

if (!$this->context->inClosure
if (
!$this->context->inClosure
&& ($type === Type::VAR || $type === Type::REF)
&& $this->isStrictScalarType($returnType)) {
&& $this->isStrictScalarType($returnType)
) {
// Keep the zval type until the declared return boundary has been
// checked. Converting first would silently coerce invalid values.
$tmpVar = $this->addTmpVar(Type::VAR);
Expand Down Expand Up @@ -2863,8 +2876,7 @@ protected function findNativeClassConst(
string $class,
string $const,
?string $accessingClass = null
): string|false
{
): string|false {
if (!$this->hasClass($class)) {
return false;
}
Expand Down Expand Up @@ -2911,12 +2923,14 @@ protected function findNativeClassConst(
if ($constDef === null) {
return false;
}
if ($classDef instanceof ClassDef
if (
$classDef instanceof ClassDef
&& !$this->checkAccessibleByClassName(
$classDef->getNamespacedName(false),
$constDef->flags,
$accessingClass,
)) {
)
) {
$this->fatalError($expr, 'Constant `' . $classDef->getNamespacedName() . '::' . $const . '` is not accessible');
}
if ($constDef->type === Type::ARRAY) {
Expand Down Expand Up @@ -3279,7 +3293,8 @@ protected function detectTypeOfExpr($expr): string
}
break;
case 'Expr_ClassConstFetch':
if ($this->isIdExpr($expr->name)
if (
$this->isIdExpr($expr->name)
&& strtolower($this->parseIdentifier($expr->name)) === 'class'
) {
return Type::STR;
Expand Down Expand Up @@ -3947,12 +3962,14 @@ protected function parseNew(Expr\New_ $expr): string
$this->fatalError($expr, "abstract class `{$className}` cannot be instantiated");
}
$constructor = $this->findConstructor($className);
if ($constructor !== null
&& !$this->checkAccessibleByClassName($constructor['className'], $constructor['flags'])) {
if (
$constructor !== null
&& !$this->checkAccessibleByClassName($constructor['className'], $constructor['flags'])
) {
$this->fatalError(
$expr,
'Cannot call ' . $this->visibilityLabel($constructor['flags']) . ' '
. $constructor['className'] . '::__construct()'
. $constructor['className'] . '::__construct()'
);
}
if ($this->isNativeObjectClass($className)) {
Expand Down Expand Up @@ -4060,7 +4077,8 @@ protected function parseInstanceof(Expr\Instanceof_ $expr): string
$result = $valueIsNative
&& ($targetIsNative || $targetIsInterface)
&& $this->isObjectClassStaticallyAssignableTo($valueClass, $targetClass);
if (!$result
if (
!$result
&& $valueIsNative
&& $targetIsNative
&& $this->isObjectClassStaticallyAssignableTo($targetClass, $valueClass)
Expand Down Expand Up @@ -4393,7 +4411,8 @@ protected function parseChainedExpr(NodeAbstract $node, string $op, bool $getVal
$this->assertNativeArrayAccessReferenceForbidden($node);
$this->assertNativeObjectReferenceForbidden($node, $node);
}
if ($node instanceof Expr\ArrayDimFetch
if (
$node instanceof Expr\ArrayDimFetch
&& $this->isNativeObjectClass($this->detectClassOfExpr($node->var))
) {
return $this->parseNativeArrayAccessPresence($node, $op, $getValue);
Expand All @@ -4404,7 +4423,8 @@ protected function parseChainedExpr(NodeAbstract $node, string $op, bool $getVal
return $nativePresence;
}
}
if ($op === self::OP_ISSET
if (
$op === self::OP_ISSET
&& $node instanceof Expr\ArrayDimFetch
&& $node->dim !== null
&& $node->var instanceof Expr\StaticPropertyFetch
Expand Down Expand Up @@ -5040,18 +5060,19 @@ protected function checkAccessibleByClassName(
string $declaringClass,
int $flags,
?string $accessingClass = null
): bool
{
): bool {
if ($accessingClass !== null) {
$accessingClass = ltrim($accessingClass, '\\');
$scopeClassDef = $this->hasClass($accessingClass)
? $this->getClass($accessingClass)
: null;
} else {
$scopeClassDef = $this->classDef;
if ($this->functionDef !== null
if (
$this->functionDef !== null
&& $this->functionDef->attributeFactoryScope !== ''
&& $this->hasClass($this->functionDef->attributeFactoryScope)) {
&& $this->hasClass($this->functionDef->attributeFactoryScope)
) {
$scopeClassDef = $this->getClass($this->functionDef->attributeFactoryScope);
}
}
Expand Down Expand Up @@ -5180,8 +5201,10 @@ protected function genLocalVarDecl(array $localVars): string
$code .= 'php::Var ' . $name . ' = php::Var(' . $boxCtor . ');' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
}
if (!isset($info['boxExpr']) && $info['size'] !== null
&& ($defaultValue = $this->getStdContainerDefaultValueExpr($info['type'])) !== null) {
if (
!isset($info['boxExpr']) && $info['size'] !== null
&& ($defaultValue = $this->getStdContainerDefaultValueExpr($info['type'])) !== null
) {
$code .= PHP_EOL . $this->getIndent() . 'php::initializeStdContainer(' . $name . '_ref, ' . $defaultValue . ');';
}
} elseif ($type === Type::STD_MAP || $type === Type::STD_ORDERED_MAP) {
Expand All @@ -5208,7 +5231,8 @@ protected function genLocalVarDecl(array $localVars): string
$code .= ';';
}
$code .= PHP_EOL;
if ($stdContainerInfo !== null
if (
$stdContainerInfo !== null
&& isset($stdContainerInfo['class'])
&& $this->isNativeObjectClass($stdContainerInfo['class'])
) {
Expand Down Expand Up @@ -5321,9 +5345,11 @@ protected function genReturnCode(): string
if ($this->functionDef->returnTypeCheck && !$this->context->inClosure) {
return $this->genUnionCheckedReturn(self::VALUE_NULL);
}
if ($this->functionDef->returnType === Type::INT
if (
$this->functionDef->returnType === Type::INT
or $this->functionDef->returnType === Type::FLOAT
or $this->functionDef->returnType === Type::BOOL) {
or $this->functionDef->returnType === Type::BOOL
) {
return $this->getIndent() . 'return 0;';
} else {
return $this->getIndent() . 'return ' . self::VALUE_NULL . ';';
Expand Down
1 change: 1 addition & 0 deletions src/NativeClass/NativeClassSupportTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,7 @@ protected function assertNativeClassNotUsedWithReflection(
}

$target = $expr->args[0]->value;

$nativeClass = '';
if ($this->isScalarString($target)) {
// Reflection string arguments are runtime names, not names relative
Expand Down
1 change: 0 additions & 1 deletion src/Parser/AssignOpTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ private function isHoistSafeDeclarationInitializer(Expr $expr): bool
|| ($expr instanceof Expr\ClassConstFetch && $this->isHoistSafeClassConstFetch($expr))
|| (($expr instanceof Expr\UnaryPlus || $expr instanceof Expr\UnaryMinus)
&& ($expr->expr instanceof Node\Scalar\Int_ || $expr->expr instanceof Node\Scalar\Float_));

return $literal && in_array(
$this->detectTypeOfExpr($expr),
[Type::INT, Type::FLOAT, Type::BOOL, Type::STR, Type::VAR],
Expand Down
4 changes: 2 additions & 2 deletions src/Parser/SwitchTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ protected function parseSwitch(Node\Stmt\Switch_ $v): string
) {
$this->fatalError($case, 'switch case must end with return/break/continue/exit/throw, ' . $lastExpr->getType() . ' given');
}
$target = count($caseGroups);
$count = count($caseGroups);
if ($hasDefault) {
$defaultTarget = $target;
$defaultTarget = $count;
}
$caseGroups[] = [$caseConds, $hasDefault, $stmts];
$caseConds = [];
Expand Down
2 changes: 1 addition & 1 deletion src/Parser/TypeConversionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ protected function getNativeType(string $type): string
if ($type === Type::FLOAT && $this->decimalTypes) {
return Type::DECIMAL;
}
return $this->nativeTypes ? $type : Type::VAR;
return $type;
}

protected function convertExprFromType(string $type, string $expr): string
Expand Down
Loading
Loading