From 4cb068fa94a7e72afbaa9a2b63a684412c417662 Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 9 Jun 2026 17:22:26 +0000 Subject: [PATCH] Stdlib: HashContext::__debugInfo() exposes algo name only (#7084) Register VM builtin __debugInfo on HashContext so var_dump hides digest state and matches php-src ext/hash/hash.c debug_info handler. Co-authored-by: Cursor --- ext/hash/HashContextDebugInfo.php | 42 +++++++++++++++++++ ext/hash/VmHashContext.php | 12 ++++++ ext/standard/VmHashNative.php | 16 +++++++ .../cases/stdlib/hash_context_debug_info.phpt | 18 ++++++++ test/unit/HashModuleTest.php | 16 +++++++ 5 files changed, 104 insertions(+) create mode 100644 ext/hash/HashContextDebugInfo.php create mode 100644 test/compliance/cases/stdlib/hash_context_debug_info.phpt diff --git a/ext/hash/HashContextDebugInfo.php b/ext/hash/HashContextDebugInfo.php new file mode 100644 index 00000000000..addc14f9611 --- /dev/null +++ b/ext/hash/HashContextDebugInfo.php @@ -0,0 +1,42 @@ +returnVar) { + return; + } + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('HashContext::__debugInfo() expects a HashContext receiver'); + } + $receiver = $frame->calledArgs[0]->resolveIndirect(); + if (Variable::TYPE_OBJECT !== $receiver->type) { + throw new \LogicException('HashContext::__debugInfo() expects a HashContext receiver'); + } + $object = $receiver->toObject(); + $algoName = VmHashContext::debugInfoAlgoName($object); + $ht = new HashTable(); + $algoVar = new Variable(); + $algoVar->string($algoName); + $ht->addNew('algo', $algoVar); + $frame->returnVar->array($ht); + } +} diff --git a/ext/hash/VmHashContext.php b/ext/hash/VmHashContext.php index 53dcf121da3..9e7ca99e832 100644 --- a/ext/hash/VmHashContext.php +++ b/ext/hash/VmHashContext.php @@ -31,9 +31,21 @@ public static function registerClass(Context $ctx): void } $entry = new \PHPCompiler\VM\ClassEntry('HashContext'); $entry->isInternal = true; + $entry->methods['__debuginfo'] = new HashContextDebugInfo(); + $entry->methodNames['__debuginfo'] = '__debugInfo'; $ctx->classes[self::CLASS_LC] = $entry; } + public static function debugInfoAlgoName(ObjectEntry $entry): string + { + $state = self::$store[$entry->id] ?? null; + if (null === $state || self::CLASS_LC !== strtolower($entry->class->name)) { + throw new \LogicException('HashContext::__debugInfo() expects a HashContext receiver'); + } + + return VmHashNative::resolveAlgoName($state['algo']); + } + public static function init(Context $vmCtx, string $algo): Variable { $algoId = VmHashNative::resolveAlgoId($algo); diff --git a/ext/standard/VmHashNative.php b/ext/standard/VmHashNative.php index 73a08262074..f91fc15b4be 100644 --- a/ext/standard/VmHashNative.php +++ b/ext/standard/VmHashNative.php @@ -808,6 +808,22 @@ public static function resolveAlgoId(string $algo): int return self::algoId($algo); } + /** Canonical registered algorithm name for HashContext::__debugInfo() (php-src ops->algo). */ + public static function resolveAlgoName(int $algoId): string + { + if (1 === $algoId) { + return 'sha256'; + } + if (2 === $algoId) { + return 'sha1'; + } + if (3 === $algoId) { + return 'md5'; + } + + throw new \LogicException('Unsupported incremental hash algorithm id: '.$algoId); + } + /** * @return array */ diff --git a/test/compliance/cases/stdlib/hash_context_debug_info.phpt b/test/compliance/cases/stdlib/hash_context_debug_info.phpt new file mode 100644 index 00000000000..8513d8beca7 --- /dev/null +++ b/test/compliance/cases/stdlib/hash_context_debug_info.phpt @@ -0,0 +1,18 @@ +--TEST-- +Stdlib: HashContext::__debugInfo() — var_dump hides digest state (#7084) +--FILE-- +__debugInfo()); +var_dump($ctx); +--EXPECTF-- +array(1) { + ["algo"]=> + string(6) "sha256" +} +object(HashContext)#%d (1) { + ["algo"]=> + string(6) "sha256" +} diff --git a/test/unit/HashModuleTest.php b/test/unit/HashModuleTest.php index 3c28c9d1da4..39f1a9c4e3e 100644 --- a/test/unit/HashModuleTest.php +++ b/test/unit/HashModuleTest.php @@ -75,4 +75,20 @@ public function test_hash_init_invalid_algo_value_error(): void $this->expectExceptionMessage('hash_init(): Argument #1 ($algo) must be a valid hashing algorithm'); $fn->execute($frame); } + + public function test_hash_context_debug_info(): void + { + $runtime = new Runtime(); + $code = <<<'PHP' +__debugInfo(); +echo (int) is_array($info); +echo isset($info['algo']) ? $info['algo'] : ''; +PHP; + $block = $runtime->parseAndCompile($code, 'hash_context_debug_info.php'); + ob_start(); + $runtime->run($block); + self::assertSame('1sha256', ob_get_clean()); + } }