diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md index 2a50a749769..1c400056154 100644 --- a/docs/bootstrap-inventory.md +++ b/docs/bootstrap-inventory.md @@ -12,7 +12,7 @@ Regenerate: `php script/bootstrap-inventory.php` | Phase A inventory files (M2 ratio SSOT) | 4482 | | Phase A ratio-deferred paths | 0 | | Source constructs flagged (blockers) | 0 | -| Source constructs flagged (warnings) | 12170 | +| Source constructs flagged (warnings) | 12184 | ## Compiler CFG gaps (`lib/Compiler.php`) @@ -486,8 +486,10 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/mbstring/mb_substr_count.php` | 0 | 2 | | `ext/mbstring/mb_trim.php` | 0 | 1 | | `ext/mbstring/mb_ucfirst.php` | 0 | 3 | -| `ext/msgpack/Module.php` | 0 | 1 | -| `ext/msgpack/MsgpackFunction.php` | 0 | 3 | +| `ext/msgpack/Module.php` | 0 | 3 | +| `ext/msgpack/VmMsgpack.php` | 0 | 11 | +| `ext/msgpack/msgpack_pack.php` | 0 | 3 | +| `ext/msgpack/msgpack_unpack.php` | 0 | 2 | | `ext/openssl/BuiltinClasses.php` | 0 | 1 | | `ext/openssl/JitOpensslCipherIvLength.php` | 0 | 1 | | `ext/openssl/JitOpensslCipherKeyLength.php` | 0 | 1 | @@ -2956,7 +2958,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `lib/JIT/Analyzer.php` | 0 | 1 | | `lib/JIT/AotDebugSymbols.php` | 0 | 1 | | `lib/JIT/ArrayAccessHelper.php` | 0 | 1 | -| `lib/JIT/ArrayBuiltinHelper.php` | 0 | 45 | +| `lib/JIT/ArrayBuiltinHelper.php` | 0 | 44 | | `lib/JIT/ArrayFilterCallbackPolicy.php` | 0 | 1 | | `lib/JIT/ArrayFindCallbackPolicy.php` | 0 | 1 | | `lib/JIT/ArrayFindHelper.php` | 0 | 1 | @@ -7618,13 +7620,36 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/msgpack/Module.php` **Warnings** (review for bootstrap subset): +- new msgpack_pack (line 17) +- new msgpack_unpack (line 18) +- 1 class method(s) + +### `ext/msgpack/VmMsgpack.php` + +**Warnings** (review for bootstrap subset): +- new MsgpackUnpackException (line 32) +- new MsgpackUnpackException (line 35) +- new Exception (line 63) +- new Exception (line 74) +- new Exception (line 170) +- new MsgpackUnpackException (line 180) +- new MsgpackUnpackException (line 246) +- new MsgpackUnpackException (line 327) +- new MsgpackUnpackException (line 404) +- new MsgpackUnpackException (line 428) +- 28 class method(s) + +### `ext/msgpack/msgpack_pack.php` + +**Warnings** (review for bootstrap subset): +- new Exception (line 30) +- new Error (line 37) - 2 class method(s) -### `ext/msgpack/MsgpackFunction.php` +### `ext/msgpack/msgpack_unpack.php` **Warnings** (review for bootstrap subset): -- new Error (line 22) -- new Error (line 27) +- new Error (line 58) - 2 class method(s) ### `ext/openssl/BuiltinClasses.php` @@ -24334,50 +24359,49 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag **Warnings** (review for bootstrap subset): - new Variable (line 120) -- new Variable (line 859) -- new Variable (line 1160) -- new Variable (line 1218) -- new Variable (line 1220) -- new Variable (line 1232) -- new Variable (line 1337) -- new Variable (line 1339) -- new Variable (line 2490) -- new Variable (line 3289) -- new Variable (line 3338) -- new Variable (line 3341) -- new Variable (line 4275) -- new Variable (line 4311) -- new Variable (line 4319) -- new Variable (line 4329) -- new Variable (line 4340) -- new Variable (line 4348) -- new Variable (line 4398) -- new Variable (line 4419) -- new Variable (line 4440) -- new Variable (line 4464) -- new Variable (line 4493) -- new Variable (line 4513) -- new Variable (line 4564) -- new Variable (line 4572) -- new Variable (line 6902) -- new Variable (line 6933) -- new Variable (line 6950) -- new Variable (line 6966) -- new strval (line 6991) -- new Variable (line 7240) -- new Variable (line 7256) -- new Variable (line 7429) -- new Variable (line 7445) -- new Variable (line 7493) -- new Variable (line 7798) -- new Variable (line 7807) -- new Variable (line 8293) -- new Variable (line 8309) -- new Variable (line 8359) -- new Variable (line 8865) -- new Variable (line 8871) -- 188 class method(s) -- 8 closure(s) +- new Variable (line 632) +- new Variable (line 933) +- new Variable (line 991) +- new Variable (line 993) +- new Variable (line 1005) +- new Variable (line 1110) +- new Variable (line 1112) +- new Variable (line 2263) +- new Variable (line 3062) +- new Variable (line 3111) +- new Variable (line 3114) +- new Variable (line 4048) +- new Variable (line 4084) +- new Variable (line 4092) +- new Variable (line 4102) +- new Variable (line 4113) +- new Variable (line 4121) +- new Variable (line 4171) +- new Variable (line 4192) +- new Variable (line 4213) +- new Variable (line 4237) +- new Variable (line 4266) +- new Variable (line 4286) +- new Variable (line 4337) +- new Variable (line 4345) +- new Variable (line 6675) +- new Variable (line 6706) +- new Variable (line 6723) +- new Variable (line 6739) +- new strval (line 6764) +- new Variable (line 7013) +- new Variable (line 7029) +- new Variable (line 7202) +- new Variable (line 7218) +- new Variable (line 7266) +- new Variable (line 7571) +- new Variable (line 7580) +- new Variable (line 8066) +- new Variable (line 8082) +- new Variable (line 8132) +- new Variable (line 8638) +- new Variable (line 8644) +- 179 class method(s) ### `lib/JIT/ArrayFilterCallbackPolicy.php` diff --git a/ext/msgpack/Module.php b/ext/msgpack/Module.php index e1c3bf8b1c6..f535775ba23 100644 --- a/ext/msgpack/Module.php +++ b/ext/msgpack/Module.php @@ -7,21 +7,15 @@ use PHPCompiler\ModuleAbstract; /** - * msgpack extension module entry (php-src ext/msgpack/msgpack.c; issue #7032). - * - * MessagePack encode/decode tracked in #6551. Register under {@see standard} so - * extension_loaded('msgpack') and function_exists('msgpack_*') stay false until - * pack/unpack are implemented (#11986), matching ZipArchive/gd phantom pattern. + * msgpack extension module entry (php-src ext/msgpack/msgpack.c; #6551). */ class Module extends ModuleAbstract { - public function getExtensionName(): string - { - return 'standard'; - } - public function getFunctions(): array { - return []; + return [ + new msgpack_pack(), + new msgpack_unpack(), + ]; } } diff --git a/ext/msgpack/MsgpackFunction.php b/ext/msgpack/MsgpackFunction.php deleted file mode 100644 index 3e2dee08a06..00000000000 --- a/ext/msgpack/MsgpackFunction.php +++ /dev/null @@ -1,29 +0,0 @@ -getName().'() is not implemented in this compiler build (issue #6551)'); - } - - public function call(Context $context, JITVariable ...$args): Value - { - throw new \Error($this->getName().'() is not implemented for JIT in this compiler build (issue #6551)'); - } -} diff --git a/ext/msgpack/VmMsgpack.php b/ext/msgpack/VmMsgpack.php new file mode 100644 index 00000000000..b9715f6ac11 --- /dev/null +++ b/ext/msgpack/VmMsgpack.php @@ -0,0 +1,451 @@ +resolveIndirect()); + } + + /** + * @return array{0: mixed, 1: int} + */ + public static function unpackWithOffset(string $data, int $offset = 0): array + { + if ($offset < 0 || $offset > \strlen($data)) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + if ($offset === \strlen($data)) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + + return self::decodeAt($data, $offset); + } + + public static function unpack(string $data, int $offset, ?Frame $frame) + { + try { + [$value] = self::unpackWithOffset($data, $offset); + + return $value; + } catch (MsgpackUnpackException) { + self::emitWarning($frame, self::INVALID_DATA); + + return false; + } + } + + private static function encodeValue(Variable $value): string + { + switch ($value->type) { + case Variable::TYPE_NULL: + return "\xc0"; + case Variable::TYPE_BOOLEAN: + return $value->toBool(null) ? "\xc3" : "\xc2"; + case Variable::TYPE_INTEGER: + if ($value->isVmResource()) { + throw new \Exception('Cannot pack resource in msgpack'); + } + + return self::encodeInt($value->toInt(null)); + case Variable::TYPE_FLOAT: + return "\xcb".pack('E', $value->toFloat(null)); + case Variable::TYPE_STRING: + return self::encodeString($value->toString(null)); + case Variable::TYPE_ARRAY: + return self::encodeArray($value->toArray()); + default: + throw new \Exception('Cannot pack type '.$value->type.' in msgpack'); + } + } + + private static function encodeInt(int $n): string + { + if ($n >= 0) { + if ($n <= 0x7f) { + return \chr($n); + } + if ($n <= 0xff) { + return "\xcc".\chr($n); + } + if ($n <= 0xffff) { + return "\xcd".pack('n', $n); + } + if ($n <= 0xffffffff) { + return "\xce".pack('N', $n); + } + + return "\xcf".pack('J', $n); + } + if ($n >= -0x20) { + return \chr($n & 0xff); + } + if ($n >= -0x80) { + return "\xd0".pack('c', $n); + } + if ($n >= -0x8000) { + return "\xd1".pack('n', $n & 0xffff); + } + if ($n >= -0x80000000) { + return "\xd2".pack('N', $n & 0xffffffff); + } + + return "\xd3".pack('J', $n & 0xffffffffffffffff); + } + + private static function encodeString(string $s): string + { + $len = \strlen($s); + if ($len <= 0x1f) { + return \chr(0xa0 | $len).$s; + } + if ($len <= 0xff) { + return "\xd9".\chr($len).$s; + } + if ($len <= 0xffff) { + return "\xda".pack('n', $len).$s; + } + + return "\xdb".pack('N', $len).$s; + } + + private static function encodeArray(HashTable $table): string + { + $pairs = iterator_to_array($table->iterateKeyed(true), false); + $count = \count($pairs); + if ($table->isPackedList()) { + $out = self::encodeContainerHeader(0x90, 0xdc, 0xdd, $count); + foreach ($pairs as [, $element]) { + $out .= self::encodeValue($element); + } + + return $out; + } + $out = self::encodeContainerHeader(0x80, 0xde, 0xdf, $count); + foreach ($pairs as [$key, $element]) { + $out .= self::encodeMapKey($key->resolveIndirect()); + $out .= self::encodeValue($element); + } + + return $out; + } + + private static function encodeContainerHeader(int $fixBase, int $tag16, int $tag32, int $count): string + { + if ($count <= 0x0f) { + return \chr($fixBase | $count); + } + if ($count <= 0xffff) { + return \chr($tag16).pack('n', $count); + } + + return \chr($tag32).pack('N', $count); + } + + private static function encodeMapKey(Variable $key): string + { + if (Variable::TYPE_INTEGER === $key->type && !$key->isVmResource()) { + return self::encodeInt($key->toInt(null)); + } + if (Variable::TYPE_STRING === $key->type) { + return self::encodeString($key->toString(null)); + } + + throw new \Exception('Cannot pack illegal array key type in msgpack'); + } + + /** + * @return array{0: mixed, 1: int} + */ + private static function decodeAt(string $data, int $offset): array + { + $len = \strlen($data); + if ($offset >= $len) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + $byte = \ord($data[$offset]); + ++$offset; + + if ($byte <= 0x7f) { + return [$byte, $offset]; + } + if ($byte >= 0xe0) { + return [$byte - 0x100, $offset]; + } + if (0xc0 === $byte) { + return [null, $offset]; + } + if (0xc2 === $byte) { + return [false, $offset]; + } + if (0xc3 === $byte) { + return [true, $offset]; + } + if ($byte >= 0xa0 && $byte <= 0xbf) { + return self::readString($data, $offset, $byte & 0x1f); + } + if ($byte >= 0x90 && $byte <= 0x9f) { + return self::readArray($data, $offset, $byte & 0x0f); + } + if ($byte >= 0x80 && $byte <= 0x8f) { + return self::readMap($data, $offset, $byte & 0x0f); + } + + switch ($byte) { + case 0xcc: + return self::readUInt8($data, $offset); + case 0xcd: + return self::readUInt16($data, $offset); + case 0xce: + return self::readUInt32($data, $offset); + case 0xcf: + return self::readUInt64($data, $offset); + case 0xd0: + return self::readInt8($data, $offset); + case 0xd1: + return self::readInt16($data, $offset); + case 0xd2: + return self::readInt32($data, $offset); + case 0xd3: + return self::readInt64($data, $offset); + case 0xca: + return self::readFloat32($data, $offset); + case 0xcb: + return self::readFloat64($data, $offset); + case 0xd9: + return self::readStringWithLen($data, $offset, 1); + case 0xda: + return self::readStringWithLen($data, $offset, 2); + case 0xdb: + return self::readStringWithLen($data, $offset, 4); + case 0xdc: + return self::readArrayWithLen($data, $offset, 2); + case 0xdd: + return self::readArrayWithLen($data, $offset, 4); + case 0xde: + return self::readMapWithLen($data, $offset, 2); + case 0xdf: + return self::readMapWithLen($data, $offset, 4); + default: + throw new MsgpackUnpackException(self::INVALID_DATA); + } + } + + /** @return array{0: int, 1: int} */ + private static function readUInt8(string $data, int $offset): array + { + self::requireBytes($data, $offset, 1); + + return [\ord($data[$offset]), $offset + 1]; + } + + /** @return array{0: int, 1: int} */ + private static function readUInt16(string $data, int $offset): array + { + self::requireBytes($data, $offset, 2); + $unpacked = unpack('n', $data, $offset); + + return [(int) $unpacked[1], $offset + 2]; + } + + /** @return array{0: int, 1: int} */ + private static function readUInt32(string $data, int $offset): array + { + self::requireBytes($data, $offset, 4); + $unpacked = unpack('N', $data, $offset); + + return [(int) $unpacked[1], $offset + 4]; + } + + /** @return array{0: int, 1: int} */ + private static function readUInt64(string $data, int $offset): array + { + self::requireBytes($data, $offset, 8); + $unpacked = unpack('J', $data, $offset); + + return [(int) $unpacked[1], $offset + 8]; + } + + /** @return array{0: int, 1: int} */ + private static function readInt8(string $data, int $offset): array + { + self::requireBytes($data, $offset, 1); + $unpacked = unpack('c', $data, $offset); + + return [(int) $unpacked[1], $offset + 1]; + } + + /** @return array{0: int, 1: int} */ + private static function readInt16(string $data, int $offset): array + { + self::requireBytes($data, $offset, 2); + $unpacked = unpack('n', $data, $offset); + $value = (int) $unpacked[1]; + if ($value >= 0x8000) { + $value -= 0x10000; + } + + return [$value, $offset + 2]; + } + + /** @return array{0: int, 1: int} */ + private static function readInt32(string $data, int $offset): array + { + self::requireBytes($data, $offset, 4); + $unpacked = unpack('N', $data, $offset); + $value = (int) $unpacked[1]; + if ($value >= 0x80000000) { + $value -= 0x100000000; + } + + return [$value, $offset + 4]; + } + + /** @return array{0: int, 1: int} */ + private static function readInt64(string $data, int $offset): array + { + self::requireBytes($data, $offset, 8); + $unpacked = unpack('J', $data, $offset); + $value = (int) $unpacked[1]; + if (\PHP_INT_SIZE < 8 && $value < 0) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + + return [$value, $offset + 8]; + } + + /** @return array{0: float, 1: int} */ + private static function readFloat32(string $data, int $offset): array + { + self::requireBytes($data, $offset, 4); + $unpacked = unpack('G', $data, $offset); + + return [(float) $unpacked[1], $offset + 4]; + } + + /** @return array{0: float, 1: int} */ + private static function readFloat64(string $data, int $offset): array + { + self::requireBytes($data, $offset, 8); + $unpacked = unpack('E', $data, $offset); + + return [(float) $unpacked[1], $offset + 8]; + } + + /** @return array{0: string, 1: int} */ + private static function readString(string $data, int $offset, int $length): array + { + self::requireBytes($data, $offset, $length); + + return [substr($data, $offset, $length), $offset + $length]; + } + + /** @return array{0: string, 1: int} */ + private static function readStringWithLen(string $data, int $offset, int $lenBytes): array + { + if (1 === $lenBytes) { + [$length, $offset] = self::readUInt8($data, $offset); + } elseif (2 === $lenBytes) { + [$length, $offset] = self::readUInt16($data, $offset); + } else { + [$length, $offset] = self::readUInt32($data, $offset); + } + + return self::readString($data, $offset, $length); + } + + /** @return array{0: list, 1: int} */ + private static function readArray(string $data, int $offset, int $count): array + { + $items = []; + for ($i = 0; $i < $count; ++$i) { + [$item, $offset] = self::decodeAt($data, $offset); + $items[] = $item; + } + + return [$items, $offset]; + } + + /** @return array{0: list, 1: int} */ + private static function readArrayWithLen(string $data, int $offset, int $lenBytes): array + { + if (2 === $lenBytes) { + [$count, $offset] = self::readUInt16($data, $offset); + } else { + [$count, $offset] = self::readUInt32($data, $offset); + } + + return self::readArray($data, $offset, $count); + } + + /** @return array{0: array, 1: int} */ + private static function readMap(string $data, int $offset, int $count): array + { + $map = []; + for ($i = 0; $i < $count; ++$i) { + [$key, $offset] = self::decodeAt($data, $offset); + if (!\is_int($key) && !\is_string($key)) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + [$value, $offset] = self::decodeAt($data, $offset); + $map[$key] = $value; + } + + return [$map, $offset]; + } + + /** @return array{0: array, 1: int} */ + private static function readMapWithLen(string $data, int $offset, int $lenBytes): array + { + if (2 === $lenBytes) { + [$count, $offset] = self::readUInt16($data, $offset); + } else { + [$count, $offset] = self::readUInt32($data, $offset); + } + + return self::readMap($data, $offset, $count); + } + + private static function requireBytes(string $data, int $offset, int $need): void + { + if ($offset + $need > \strlen($data)) { + throw new MsgpackUnpackException(self::INVALID_DATA); + } + } + + private static function emitWarning(?Frame $frame, string $message): void + { + if (null === $frame?->vmContext) { + @\trigger_error($message, \E_WARNING); + + return; + } + $frame->vmContext->errors->triggerError( + $message, + ErrorReporter::E_WARNING, + '' !== $frame->scriptPath ? $frame->scriptPath : null, + $frame->vmContext, + $frame + ); + } +} + +final class MsgpackUnpackException extends \RuntimeException +{ +} diff --git a/ext/msgpack/msgpack_pack.php b/ext/msgpack/msgpack_pack.php index ad02c6dc690..f8ed603034f 100644 --- a/ext/msgpack/msgpack_pack.php +++ b/ext/msgpack/msgpack_pack.php @@ -4,9 +4,36 @@ namespace PHPCompiler\ext\msgpack; -/** - * msgpack_pack() stub (php-src ext/msgpack/msgpack.c; issue #7032, #6551). - */ -final class msgpack_pack extends MsgpackFunction +use PHPCompiler\Frame; +use PHPCompiler\Func\Internal; +use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\Variable as JITVariable; +use PHPLLVM\Value; + +/** msgpack_pack() — MessagePack encode (php-src ext/msgpack/msgpack.c; #6551). */ +final class msgpack_pack extends Internal { + public function __construct() + { + parent::__construct('msgpack_pack'); + } + + public function execute(Frame $frame): void + { + $this->requireExactArgCount($frame, 'msgpack_pack', 1); + if (null === $frame->returnVar) { + return; + } + try { + $packed = VmMsgpack::pack($frame->calledArgs[0]); + } catch (\Throwable $e) { + throw new \Exception($e->getMessage(), 0, $e); + } + $frame->returnVar->string($packed); + } + + public function call(Context $context, JITVariable ...$args): Value + { + throw new \Error('msgpack_pack() is not implemented for JIT in this compiler build (issue #6551)'); + } } diff --git a/ext/msgpack/msgpack_unpack.php b/ext/msgpack/msgpack_unpack.php index 7bf812ef562..9a28f42c961 100644 --- a/ext/msgpack/msgpack_unpack.php +++ b/ext/msgpack/msgpack_unpack.php @@ -4,9 +4,57 @@ namespace PHPCompiler\ext\msgpack; -/** - * msgpack_unpack() stub (php-src ext/msgpack/msgpack.c; issue #7032, #6551). - */ -final class msgpack_unpack extends MsgpackFunction +use PHPCompiler\ext\standard\VmJson; +use PHPCompiler\ext\standard\VmMath; +use PHPCompiler\ext\standard\VmString; +use PHPCompiler\Frame; +use PHPCompiler\Func\Internal; +use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\Variable as JITVariable; +use PHPLLVM\Value; + +/** msgpack_unpack() — MessagePack decode (php-src ext/msgpack/msgpack.c; #6551). */ +final class msgpack_unpack extends Internal { + public function __construct() + { + parent::__construct('msgpack_unpack'); + } + + public function execute(Frame $frame): void + { + $this->requireAtLeastArgCount($frame, 'msgpack_unpack', 1); + $this->requireAtMostArgCount($frame, 'msgpack_unpack', 2); + if (null === $frame->returnVar) { + return; + } + $data = VmString::coerceStringBuiltinArg( + $frame->calledArgs[0], + 'msgpack_unpack', + 0, + 'data' + ); + $offset = 0; + if (isset($frame->calledArgs[1])) { + $offset = VmMath::parseIntBuiltinArgForFrame( + $frame, + 1, + 'msgpack_unpack', + 2, + 'offset' + ); + } + $decoded = VmMsgpack::unpack($data, $offset, $frame); + if (false === $decoded) { + $frame->returnVar->bool(false); + + return; + } + $frame->returnVar->copyFrom(VmJson::import($decoded)); + } + + public function call(Context $context, JITVariable ...$args): Value + { + throw new \Error('msgpack_unpack() is not implemented for JIT in this compiler build (issue #6551)'); + } } diff --git a/test/compliance/cases/stdlib/extension_loaded_msgpack.phpt b/test/compliance/cases/stdlib/extension_loaded_msgpack.phpt new file mode 100644 index 00000000000..ec45e5492fa --- /dev/null +++ b/test/compliance/cases/stdlib/extension_loaded_msgpack.phpt @@ -0,0 +1,15 @@ +--TEST-- +stdlib extension_loaded('msgpack') true when pack/unpack implemented (#6551, ext/msgpack/msgpack.c) +--FILE-- + 1, 'b' => [2, 3], 'c' => 'hello', 'd' => true, 'e' => null, 'f' => 1.5]; +$packed = msgpack_pack($data); +$unpacked = msgpack_unpack($packed); +echo is_string($packed) ? '1' : '0'; +echo $unpacked === $data ? '1' : '0'; +echo function_exists('msgpack_pack') ? '1' : '0'; +echo function_exists('msgpack_unpack') ? '1' : '0'; +echo extension_loaded('msgpack') ? '1' : '0'; +echo "\n"; +?> +--EXPECT-- +11111 diff --git a/test/compliance/cases/stdlib/msgpack_unpack_invalid.phpt b/test/compliance/cases/stdlib/msgpack_unpack_invalid.phpt new file mode 100644 index 00000000000..9d73c5f4fce --- /dev/null +++ b/test/compliance/cases/stdlib/msgpack_unpack_invalid.phpt @@ -0,0 +1,12 @@ +--TEST-- +stdlib msgpack_unpack invalid payload returns false with warning (#6551, ext/msgpack/msgpack.c) +--FILE-- + +--EXPECT-- +1 diff --git a/test/repro-maintainer/parity_msgpack_roundtrip.php b/test/repro-maintainer/parity_msgpack_roundtrip.php new file mode 100644 index 00000000000..8d5cd8fe2a0 --- /dev/null +++ b/test/repro-maintainer/parity_msgpack_roundtrip.php @@ -0,0 +1,25 @@ + 1, 'b' => [2, 3], 'c' => 'hello', 'd' => true, 'e' => null, 'f' => 1.5]; +$packed = msgpack_pack($data); +$unpacked = msgpack_unpack($packed); +if ($unpacked !== $data) { + echo "fail: round-trip mismatch\n"; + exit(1); +} + +echo "ok\n"; diff --git a/test/repro/maintainer_gap_msgpack_phantom_registration.php b/test/repro/maintainer_gap_msgpack_phantom_registration.php index 87f0ba4d1e9..2ef2d30b6ee 100644 --- a/test/repro/maintainer_gap_msgpack_phantom_registration.php +++ b/test/repro/maintainer_gap_msgpack_phantom_registration.php @@ -2,16 +2,23 @@ declare(strict_types=1); -if (extension_loaded('msgpack')) { - echo "fail: extension_loaded('msgpack') true on reference profile\n"; +if (!extension_loaded('msgpack')) { + echo "fail: extension_loaded('msgpack') false on reference profile\n"; exit(1); } foreach (['msgpack_pack', 'msgpack_unpack'] as $fn) { - if (function_exists($fn)) { - echo "fail: function_exists('{$fn}') true on reference profile\n"; + if (!function_exists($fn)) { + echo "fail: function_exists('{$fn}') false on reference profile\n"; exit(1); } } +$data = ['a' => 1, 'b' => [2, 3], 'c' => 'hello']; +$packed = msgpack_pack($data); +if (!is_string($packed) || msgpack_unpack($packed) !== $data) { + echo "fail: msgpack round-trip\n"; + exit(1); +} + echo "ok\n"; diff --git a/test/selfhost/compiler_lib_spine_smoke/main.php b/test/selfhost/compiler_lib_spine_smoke/main.php index bb2e31e533d..882b17fce34 100644 --- a/test/selfhost/compiler_lib_spine_smoke/main.php +++ b/test/selfhost/compiler_lib_spine_smoke/main.php @@ -484,7 +484,7 @@ require_once __DIR__.'/../../../ext/mbstring/mb_trim.php'; require_once __DIR__.'/../../../ext/mbstring/mb_ucfirst.php'; require_once __DIR__.'/../../../ext/msgpack/Module.php'; -require_once __DIR__.'/../../../ext/msgpack/MsgpackFunction.php'; +require_once __DIR__.'/../../../ext/msgpack/VmMsgpack.php'; require_once __DIR__.'/../../../ext/msgpack/msgpack_pack.php'; require_once __DIR__.'/../../../ext/msgpack/msgpack_unpack.php'; require_once __DIR__.'/../../../ext/openssl/BuiltinClasses.php'; diff --git a/test/unit/MsgpackModuleTest.php b/test/unit/MsgpackModuleTest.php new file mode 100644 index 00000000000..14726f7b828 --- /dev/null +++ b/test/unit/MsgpackModuleTest.php @@ -0,0 +1,55 @@ +vmContext; + + foreach (['msgpack_pack', 'msgpack_unpack'] as $fn) { + self::assertTrue(VmReflection::functionExists($ctx, $fn), $fn); + } + + $code = <<<'PHP' +parseAndCompile($code, 'msgpack_module.php'); + ob_start(); + $runtime->run($block); + self::assertSame('111', ob_get_clean()); + } + + public function test_msgpack_scalar_and_array_roundtrip(): void + { + $runtime = new Runtime(); + $code = <<<'PHP' + 1, 'b' => [2, 3], 'c' => 'hello', 'd' => true, 'e' => null, 'f' => 1.5]; +$packed = msgpack_pack($data); +$unpacked = msgpack_unpack($packed); +echo (int) is_string($packed); +echo (int) ($unpacked === $data); +echo msgpack_unpack("\xff") === false ? '1' : '0'; +PHP; + $block = $runtime->parseAndCompile($code, 'msgpack_roundtrip.php'); + ob_start(); + $runtime->run($block); + self::assertSame('110', ob_get_clean()); + } +}