feat(native): add fixed-width scalar fields

master
韩天峰 3 weeks ago
parent 821c686ab0
commit b3898c329d
  1. 5
      docs/en/NATIVE_CLASS_OBJECT.md
  2. 5
      docs/zh-cn/NATIVE_CLASS_OBJECT.md
  3. 7
      phpunit/code/native-class-uint64-property.php
  4. 9
      phpunit/code/native-stack-promotion.php
  5. 9
      phpunit/src/NativeClass/NativeClassValidationTest.php
  6. 4
      phpunit/src/NativeClass/NativeObjectStackPromotionCodegenTest.php
  7. 18
      src/CompilerBase.php
  8. 2
      src/Entity/PropertyDef.php
  9. 12
      src/NativeClass/NativeClassSupportTrait.php
  10. 13
      src/Parser/AssignOpTrait.php
  11. 7
      src/Parser/BinaryOpTrait.php
  12. 10
      src/Parser/PropertyAccessTrait.php
  13. 2
      src/Preprocessor.php
  14. 119
      src/Transform/NativePropertyTypeLowering.php
  15. 7
      src/Transform/Visitor.php
  16. 5
      src/Translator.php
  17. 99
      tests/compiler/native-class/fixed-width-properties.phpt

@ -227,6 +227,9 @@ Type declarations are used to determine the C++ field layout at compile time. Th
| `bool` | `php::Bool` | Native value field | | `bool` | `php::Bool` | Native value field |
| `int` | `php::Int` | Native value field | | `int` | `php::Int` | Native value field |
| `float` | `php::Float` | Native value field | | `float` | `php::Float` | Native value field |
| `int8` / `int16` / `int32` | `int8_t` / `int16_t` / `int32_t` | Stored at the requested width; expressions use `php::Int` |
| `uint8` / `uint16` / `uint32` | `uint8_t` / `uint16_t` / `uint32_t` | Stored at the requested width; expressions use `php::Int` |
| `float32` | `float` | Four-byte storage; expressions use `php::Float` |
| `string` | `php::Str` | The PHPX RAII string type currently used by TypePHP | | `string` | `php::Str` | The PHPX RAII string type currently used by TypePHP |
| `array` | `php::Array` | PHPX RAII array, preserving PHP COW semantics | | `array` | `php::Array` | PHPX RAII array, preserving PHP COW semantics |
| Concrete Zend class | `php::Object` | Stores a Zend Object and validates the class at the assignment entry | | Concrete Zend class | `php::Object` | Stores a Zend Object and validates the class at the assignment entry |
@ -240,6 +243,8 @@ Type declarations are used to determine the C++ field layout at compile time. Th
`string`, `array`, Zend Object, Stream, and mixed fields are still located directly at fixed offsets in the C++ `struct`. The underlying zval or zend object they hold is managed by the PHPX RAII type, but property reads do not require a property hash table, object handler, or ZendVM dispatch. `string`, `array`, Zend Object, Stream, and mixed fields are still located directly at fixed offsets in the C++ `struct`. The underlying zval or zend object they hold is managed by the PHPX RAII type, but property reads do not require a property hash table, object handler, or ZendVM dispatch.
Fixed-width scalar names apply only to Native Class properties. They select field storage rather than introducing new PHP expression types. Natural C++ alignment and tail padding still apply; TypePHP does not emit packed classes. A flat Native Class containing only scalar fields has the same payload layout and size as its generated C++ class. The Native Heap keeps its GC header immediately before that payload, outside the C++ object size.
PHP itself does not allow `resource` as a property type; stream resources in TypePHP should be declared with the existing `Stream` pseudo-type. Types that PHP itself forbids in property declarations, such as `void`, `never`, and `callable`, are likewise forbidden in Native Class. PHP itself does not allow `resource` as a property type; stream resources in TypePHP should be declared with the existing `Stream` pseudo-type. Types that PHP itself forbids in property declarations, such as `void`, `never`, and `callable`, are likewise forbidden in Native Class.
The following types are explicitly forbidden as Native Class property types: The following types are explicitly forbidden as Native Class property types:

@ -242,6 +242,9 @@ final class InvalidContext
| `bool` | `php::Bool` | 原生值字段 | | `bool` | `php::Bool` | 原生值字段 |
| `int` | `php::Int` | 原生值字段 | | `int` | `php::Int` | 原生值字段 |
| `float` | `php::Float` | 原生值字段 | | `float` | `php::Float` | 原生值字段 |
| `int8` / `int16` / `int32` | `int8_t` / `int16_t` / `int32_t` | 按指定位宽存储;表达式类型仍为 `php::Int` |
| `uint8` / `uint16` / `uint32` | `uint8_t` / `uint16_t` / `uint32_t` | 按指定位宽存储;表达式类型仍为 `php::Int` |
| `float32` | `float` | 4 字节存储;表达式类型仍为 `php::Float` |
| `string` | `php::Str` | TypePHP 当前使用的 PHPX RAII 字符串类型 | | `string` | `php::Str` | TypePHP 当前使用的 PHPX RAII 字符串类型 |
| `array` | `php::Array` | PHPX RAII 数组,保留 PHP COW 语义 | | `array` | `php::Array` | PHPX RAII 数组,保留 PHP COW 语义 |
| 确定的 Zend class | `php::Object` | 保存 Zend Object,并在赋值入口验证 class | | 确定的 Zend class | `php::Object` | 保存 Zend Object,并在赋值入口验证 class |
@ -255,6 +258,8 @@ final class InvalidContext
`string`、`array`、Zend Object、Stream 和 mixed 字段仍然直接位于 C++ `struct` 的固定偏移处。它们持有的底层 zval 或 zend 对象由 PHPX RAII 类型管理,但属性读取不需要属性哈希表、object handler 或 ZendVM 分派。 `string`、`array`、Zend Object、Stream 和 mixed 字段仍然直接位于 C++ `struct` 的固定偏移处。它们持有的底层 zval 或 zend 对象由 PHPX RAII 类型管理,但属性读取不需要属性哈希表、object handler 或 ZendVM 分派。
定宽标量名称只适用于 Native Class 属性,用于选择字段存储,不会引入新的 PHP 表达式类型。布局仍遵循 C++ 的自然对齐和尾部填充,TypePHP 不生成 packed class。仅包含标量字段且没有多态机制的 Native Class,其 payload 布局和尺寸与生成的 C++ class 一致;Native Heap 的 GC header 位于 payload 之前,不计入 C++ 对象尺寸。
PHP 本身不允许将 `resource` 写成属性类型;TypePHP 中的 stream resource 应使用已有的 `Stream` 伪类型声明。`void`、`never`、`callable` 等 PHP 本身禁止用于属性声明的类型,在 Native Class 中同样禁止。 PHP 本身不允许将 `resource` 写成属性类型;TypePHP 中的 stream resource 应使用已有的 `Stream` 伪类型声明。`void`、`never`、`callable` 等 PHP 本身禁止用于属性声明的类型,在 Native Class 中同样禁止。
以下类型明确禁止作为 Native Class 属性类型: 以下类型明确禁止作为 Native Class 属性类型:

@ -0,0 +1,7 @@
<?php
#[Native]
class NativeUint64Property
{
public uint64 $value;
}

@ -74,3 +74,12 @@ function inheritedNativeObject(): int
$value = new NativeStackPromotionChild(); $value = new NativeStackPromotionChild();
return $value->readFromBase(); return $value->readFromBase();
} }
#[Native]
final class NativeCompactScalarFixture
{
public int32 $number = 0;
public int8 $tag = 0;
public uint16 $flags = 0;
public float32 $ratio = 0.0;
}

@ -140,6 +140,15 @@ final class NativeClassValidationTest extends \BaseTest
$this->compile('native-class-static-property.php'); $this->compile('native-class-static-property.php');
} }
public function testRejectsUint64Property(): void
{
$this->expectException(\TypePhp\Exception\SyntaxError::class);
$this->expectExceptionMessage(
'Native property type `uint64` is not supported because it cannot be represented by php::Int',
);
$this->compile('native-class-uint64-property.php');
}
public function testRejectsInheritanceAcrossObjectModels(): void public function testRejectsInheritanceAcrossObjectModels(): void
{ {
$this->expectException(TestError::class); $this->expectException(TestError::class);

@ -57,6 +57,10 @@ final class NativeObjectStackPromotionCodegenTest extends TestCase
'virtual ~php_nativestackpromotionchild() noexcept override;', 'virtual ~php_nativestackpromotionchild() noexcept override;',
$header, $header,
); );
self::assertStringContainsString('int32_t number = 0;', $header);
self::assertStringContainsString('int8_t tag = 0;', $header);
self::assertStringContainsString('uint16_t flags = 0;', $header);
self::assertStringContainsString('float ratio = 0;', $header);
self::assertStringContainsString( self::assertStringContainsString(
'virtual php::Int __typephp_virtual_php_nativestackpromotionbase__readfrombase() override;', 'virtual php::Int __typephp_virtual_php_nativestackpromotionbase__readfrombase() override;',
$header, $header,

@ -3768,7 +3768,11 @@ class CompilerBase implements PropertyAccessContext
if ($this->isVarExpr($expr->var) && !$this->hasVar($var)) { if ($this->isVarExpr($expr->var) && !$this->hasVar($var)) {
$this->errorUndefinedVariable($expr->var); $this->errorUndefinedVariable($expr->var);
} }
return '++' . $var; $result = '++' . $var;
$property = $this->getNativePropertyDef($expr->var);
return $property === null
? $result
: $this->promoteNativeObjectPropertyValue($property, $result);
} }
/** /**
@ -4184,7 +4188,11 @@ class CompilerBase implements PropertyAccessContext
$opName = $op === '+' ? '++' : '--'; $opName = $op === '+' ? '++' : '--';
$this->fatalError($expr, "Cannot use {$opName} on {$type}. Use " . ($op === '+' ? '+= 1' : '-= 1') . ' instead (Big* types are immutable).'); $this->fatalError($expr, "Cannot use {$opName} on {$type}. Use " . ($op === '+' ? '+= 1' : '-= 1') . ' instead (Big* types are immutable).');
} }
return $var . str_repeat($op, 2); $result = $var . str_repeat($op, 2);
$property = $this->getNativePropertyDef($expr->var);
return $property === null
? $result
: $this->promoteNativeObjectPropertyValue($property, $result);
} }
if ($this->isStaticPropertyFetch($expr->var)) { if ($this->isStaticPropertyFetch($expr->var)) {
$native = $this->parseNativeStaticPropertyFetch($expr->var); $native = $this->parseNativeStaticPropertyFetch($expr->var);
@ -4237,7 +4245,11 @@ class CompilerBase implements PropertyAccessContext
if ($this->isVarExpr($expr->var) && !$this->hasVar($var)) { if ($this->isVarExpr($expr->var) && !$this->hasVar($var)) {
$this->errorUndefinedVariable($expr->var); $this->errorUndefinedVariable($expr->var);
} }
return '--' . $var; $result = '--' . $var;
$property = $this->getNativePropertyDef($expr->var);
return $property === null
? $result
: $this->promoteNativeObjectPropertyValue($property, $result);
} }
protected function parsePrint(Expr\Print_ $expr): string protected function parsePrint(Expr\Print_ $expr): string

@ -28,6 +28,8 @@ class PropertyDef
public string $class = ''; public string $class = '';
public array $typeCheck = []; public array $typeCheck = [];
public string $typeStr = ''; public string $typeStr = '';
/** Optional fixed-width C++ storage used only by Native Class fields. */
public string $nativeStorageType = '';
public bool $promoted = false; public bool $promoted = false;
public bool $readonly = false; public bool $readonly = false;
/** The generated Zend property table cannot represent this default exactly. */ /** The generated Zend property table cannot represent this default exactly. */

@ -1204,6 +1204,7 @@ trait NativeClassSupportTrait
$definition = $resolution->propertyDef; $definition = $resolution->propertyDef;
if ($definition->nullable if ($definition->nullable
|| $definition->nativeStorageType !== ''
|| $definition->getter !== null || $definition->getter !== null
|| $definition->setter !== null || $definition->setter !== null
|| Type::getReferenceType($definition->type) !== $referenceType || Type::getReferenceType($definition->type) !== $referenceType
@ -1871,6 +1872,9 @@ trait NativeClassSupportTrait
protected function getNativeObjectPropertyType(PropertyDef $property): string protected function getNativeObjectPropertyType(PropertyDef $property): string
{ {
if ($property->nativeStorageType !== '') {
return $property->nativeStorageType;
}
if ($property->type === Type::OBJECT && $this->isNativeObjectClass($property->class)) { if ($property->type === Type::OBJECT && $this->isNativeObjectClass($property->class)) {
return $this->getNativeObjectPointerType($property->class); return $this->getNativeObjectPointerType($property->class);
} }
@ -1884,6 +1888,14 @@ trait NativeClassSupportTrait
}; };
} }
protected function promoteNativeObjectPropertyValue(PropertyDef $property, string $expression): string
{
if ($property->nativeStorageType === '') {
return $expression;
}
return 'static_cast<' . $property->type . '>(' . $expression . ')';
}
protected function getNativeObjectInitializerName(string|ClassDef $class): string protected function getNativeObjectInitializerName(string|ClassDef $class): string
{ {
// Keep compiler-owned helpers outside the php_* user symbol namespace. // Keep compiler-owned helpers outside the php_* user symbol namespace.

@ -517,7 +517,7 @@ trait AssignOpTrait
// Parse and materialize the receiver before the right-hand // Parse and materialize the receiver before the right-hand
// expression. PHP evaluates an object/property target before // expression. PHP evaluates an object/property target before
// its assigned value, and C++ operand order must not decide it. // its assigned value, and C++ operand order must not decide it.
$leftExpr = $this->parsePropertyFetch($left); $leftExpr = $this->parsePropertyFetchUpdate($left);
if ($def->type === Type::OBJECT && $this->isNativeObjectClass($def->class)) { if ($def->type === Type::OBJECT && $this->isNativeObjectClass($def->class)) {
if ($this->isNull($right)) { if ($this->isNull($right)) {
if (!$def->nullable) { if (!$def->nullable) {
@ -538,7 +538,10 @@ trait AssignOpTrait
if ($def->type !== Type::VAR) { if ($def->type !== Type::VAR) {
$rightExpr = $this->convertExprFromType($def->type, $rightExpr); $rightExpr = $this->convertExprFromType($def->type, $rightExpr);
} }
return $leftExpr . ' = ' . $rightExpr; return $this->promoteNativeObjectPropertyValue(
$def,
$leftExpr . ' = ' . $rightExpr,
);
} }
} }
@ -1308,7 +1311,11 @@ trait AssignOpTrait
? $def->type ? $def->type
: $rightType; : $rightType;
return $var . ' ' . $op . ' (' . $this->convertNativePropertyWriteExpr($def->type, $effectiveRightType, $rightExpr) . ')'; return $this->promoteNativeObjectPropertyValue(
$def,
$var . ' ' . $op . ' ('
. $this->convertNativePropertyWriteExpr($def->type, $effectiveRightType, $rightExpr) . ')',
);
} }
protected function convertNativePropertyWriteExpr(string $propertyType, string $rightType, string $rightExpr): string protected function convertNativePropertyWriteExpr(string $propertyType, string $rightType, string $rightExpr): string

@ -759,6 +759,13 @@ trait BinaryOpTrait
return $def->type; return $def->type;
} }
} }
$def = $this->getNativePropertyDef($expr);
if ($def !== null
&& in_array($def->type, [Type::INT, Type::FLOAT, Type::BOOL], true)
&& $this->isNativePropertyTypedValue($expr)
) {
return $def->type;
}
return Type::VAR; return Type::VAR;
} }

@ -1256,8 +1256,11 @@ trait PropertyAccessTrait
if ($this->isNativeObjectClass($nativeExpressionClass)) { if ($this->isNativeObjectClass($nativeExpressionClass)) {
$objectName = $this->materializeNativeObjectReceiver($object, $nativeExpressionClass); $objectName = $this->materializeNativeObjectReceiver($object, $nativeExpressionClass);
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $this->getNativeObjectMemberReceiver($objectName) $field = $this->getNativeObjectMemberReceiver($objectName)
. $this->getNativeObjectPropertyCppName($resolution->propertyDef, $resolution->classDef); . $this->getNativeObjectPropertyCppName($resolution->propertyDef, $resolution->classDef);
return $this->isPropertyFetchUpdate($expr)
? $field
: $this->promoteNativeObjectPropertyValue($resolution->propertyDef, $field);
} }
$update = $this->isPropertyFetchUpdate($expr); $update = $this->isPropertyFetchUpdate($expr);
@ -1276,8 +1279,11 @@ trait PropertyAccessTrait
$this->fatalError($expr, "Native class `{$class}` has no property `\${$propertyName}`"); $this->fatalError($expr, "Native class `{$class}` has no property `\${$propertyName}`");
} }
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR); $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_VAR);
return $this->getNativeObjectMemberReceiver($objectName) $field = $this->getNativeObjectMemberReceiver($objectName)
. $this->getNativeObjectPropertyCppName($resolution->propertyDef, $resolution->classDef); . $this->getNativeObjectPropertyCppName($resolution->propertyDef, $resolution->classDef);
return $this->isPropertyFetchUpdate($expr)
? $field
: $this->promoteNativeObjectPropertyValue($resolution->propertyDef, $field);
} }
$objectVar = $this->parenthesizeOpenOperand($objectName); $objectVar = $this->parenthesizeOpenOperand($objectName);
$directMagic = !$update && !$this->isNativePropertyAccess($expr) $directMagic = !$update && !$this->isNativePropertyAccess($expr)

@ -22,6 +22,7 @@ use TypePhp\Exception\SyntaxError;
use TypePhp\Transform\PropertyHookLowering; use TypePhp\Transform\PropertyHookLowering;
use TypePhp\Transform\CompileTimeAttribute; use TypePhp\Transform\CompileTimeAttribute;
use TypePhp\Transform\NativeClassAttributeLowering; use TypePhp\Transform\NativeClassAttributeLowering;
use TypePhp\Transform\NativePropertyTypeLowering;
use TypePhp\Transform\NanoSyntaxValidationVisitor; use TypePhp\Transform\NanoSyntaxValidationVisitor;
use TypePhp\Transform\PrinterLowering; use TypePhp\Transform\PrinterLowering;
use TypePhp\Transform\ArrayableLowering; use TypePhp\Transform\ArrayableLowering;
@ -2439,6 +2440,7 @@ class Preprocessor extends CompilerBase
false, false,
); );
$propDef->node = $errorNode; $propDef->node = $errorNode;
$propDef->nativeStorageType = NativePropertyTypeLowering::getStorageType($errorNode);
if ($typeNode !== null if ($typeNode !== null
&& !$typeNode instanceof NullableType && !$typeNode instanceof NullableType
&& !$typeNode instanceof UnionType && !$typeNode instanceof UnionType

@ -0,0 +1,119 @@
<?php
/**
* This file is part of TypePHP.
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace TypePhp\Transform;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\NodeAbstract;
use TypePhp\Exception\SyntaxError;
/**
* Preserve PHP's scalar type system while selecting a narrower C++ field for
* explicitly sized Native Class properties.
*/
final class NativePropertyTypeLowering
{
public const string STORAGE_TYPE_ATTRIBUTE = 'typephpNativePropertyStorageType';
/** @var array<string, array{php: string, cpp: string}> */
private const array TYPES = [
'int8' => ['php' => 'int', 'cpp' => 'int8_t'],
'int16' => ['php' => 'int', 'cpp' => 'int16_t'],
'int32' => ['php' => 'int', 'cpp' => 'int32_t'],
'uint8' => ['php' => 'int', 'cpp' => 'uint8_t'],
'uint16' => ['php' => 'int', 'cpp' => 'uint16_t'],
'uint32' => ['php' => 'int', 'cpp' => 'uint32_t'],
'float32' => ['php' => 'float', 'cpp' => 'float'],
];
private const array UNSUPPORTED_TYPES = [
'uint64' => 'it cannot be represented by php::Int',
];
public static function lowerClass(Stmt\Class_ $class): void
{
foreach ($class->stmts as $stmt) {
if ($stmt instanceof Stmt\Property) {
$stmt->type = self::lowerDeclaration($stmt, $stmt->type);
continue;
}
if (!$stmt instanceof Stmt\ClassMethod || $stmt->name->toLowerString() !== '__construct') {
continue;
}
foreach ($stmt->params as $param) {
if ($param->isPromoted()) {
$param->type = self::lowerDeclaration($param, $param->type);
}
}
}
}
public static function getStorageType(Node $declaration): string
{
$type = $declaration->getAttribute(self::STORAGE_TYPE_ATTRIBUTE, '');
return is_string($type) ? $type : '';
}
private static function lowerDeclaration(
Node $declaration,
Node\ComplexType|Node\Identifier|Node\Name|null $type,
): Node\ComplexType|Node\Identifier|Node\Name|null {
$unsupportedType = self::findType($type, self::UNSUPPORTED_TYPES);
if ($unsupportedType !== null) {
throw new SyntaxError(
"Native property type `{$unsupportedType}` is not supported because "
. self::UNSUPPORTED_TYPES[$unsupportedType],
);
}
$fixedType = self::findFixedType($type);
if ($fixedType === null) {
return $type;
}
if (!$type instanceof Node\Name && !$type instanceof Node\Identifier) {
throw new SyntaxError(
"Native property type `{$fixedType}` must be declared directly and cannot be nullable or composite",
);
}
$mapping = self::TYPES[$fixedType];
$declaration->setAttribute(self::STORAGE_TYPE_ATTRIBUTE, $mapping['cpp']);
return new Node\Identifier($mapping['php'], $type->getAttributes());
}
private static function findFixedType(?NodeAbstract $type): ?string
{
return self::findType($type, self::TYPES);
}
/** @param array<string, mixed> $types */
private static function findType(?NodeAbstract $type, array $types): ?string
{
if ($type === null) {
return null;
}
if ($type instanceof Node\NullableType) {
return self::findType($type->type, $types);
}
if ($type instanceof Node\UnionType || $type instanceof Node\IntersectionType) {
foreach ($type->types as $member) {
$matchedType = self::findType($member, $types);
if ($matchedType !== null) {
return $matchedType;
}
}
return null;
}
if (!$type instanceof Node\Name && !$type instanceof Node\Identifier) {
return null;
}
$name = strtolower($type->toString());
return isset($types[$name]) ? $name : null;
}
}

@ -132,6 +132,13 @@ class Visitor extends NodeVisitorAbstract
$methods = []; $methods = [];
$classReadonly = $node instanceof Stmt\Class_ && $node->isReadonly(); $classReadonly = $node instanceof Stmt\Class_ && $node->isReadonly();
if ($node instanceof Stmt\Class_ && NativeClassAttributeLowering::isNative($node)) {
$this->guard(
$node,
static fn () => NativePropertyTypeLowering::lowerClass($node),
'Native',
);
}
foreach ($node->stmts as $stmt) { foreach ($node->stmts as $stmt) {
if ($stmt instanceof Stmt\Property) { if ($stmt instanceof Stmt\Property) {
foreach (PropertyHookLowering::lowerProperty($stmt) as $method) { foreach (PropertyHookLowering::lowerProperty($stmt) as $method) {

@ -7900,7 +7900,10 @@ CODE;
"Cannot override final property {$parentClass}::\${$name}" "Cannot override final property {$parentClass}::\${$name}"
); );
} }
if ($childProp->type !== $parentProp->type || $childProp->class !== $parentProp->class) { if ($childProp->type !== $parentProp->type
|| $childProp->class !== $parentProp->class
|| $childProp->nativeStorageType !== $parentProp->nativeStorageType
) {
$this->fatalError($classStmt, $this->fatalError($classStmt,
"Declaration of `{$className}::\${$name}` must be compatible " . "Declaration of `{$className}::\${$name}` must be compatible " .
"with `{$parentClass}::\${$name}`"); "with `{$parentClass}::\${$name}`");

@ -0,0 +1,99 @@
--TEST--
Native class: fixed-width scalar fields retain PHP scalar expression types
--FILE--
<?php
#[Native]
final class CompactScalars
{
public int8 $i8 = -8;
public int16 $i16 = -1600;
public int32 $i32 = -320000;
public uint8 $u8 = 8;
public uint16 $u16 = 1600;
public uint32 $u32 = 4294967295;
public float32 $f32 = 16777216.0;
public float32 $f32unit = 1.0;
public int32 $negative = -1;
public int32 $one = 1;
public function update(): void
{
$this->i8 += 2;
$this->u16 *= 2;
}
public function intSum(): int
{
return $this->u32 + $this->one;
}
public function intCompare(): bool
{
return $this->u32 > $this->negative;
}
public function floatSum(): float
{
return $this->f32 + $this->f32unit;
}
}
#[Native]
final class MixedScalars
{
public int $integer = 3;
public float $floating = 0.5;
public int8 $smallInteger = 2;
public float32 $smallFloat = 0.25;
public function nativeSum(): float
{
return $this->integer + $this->floating;
}
public function fixedIntSum(): float
{
return $this->smallInteger + $this->floating;
}
public function fixedFloatSum(): float
{
return $this->integer + $this->smallFloat;
}
}
function main(): void
{
$value = new CompactScalars();
$value->update();
var_dump(
$value->i8,
$value->i16,
$value->i32,
$value->u8,
$value->u16,
$value->u32,
$value->intSum(),
$value->intCompare(),
$value->floatSum(),
);
$mixed = new MixedScalars();
var_dump($mixed->nativeSum(), $mixed->fixedIntSum(), $mixed->fixedFloatSum());
}
?>
--EXPECT--
int(-6)
int(-1600)
int(-320000)
int(8)
int(3200)
int(4294967295)
int(4294967296)
bool(true)
float(16777217)
float(3.5)
float(2.5)
float(3.25)
Loading…
Cancel
Save