From facd24832f46c25371914e004c99bae3bdf15aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PHP=20Compiler=20Ext=20=E2=80=94=20DOM=20=26=20XML?= Date: Fri, 14 Aug 2026 03:04:53 +0000 Subject: [PATCH] =?UTF-8?q?Stdlib:=20SimpleXML=20methods/import=20excess?= =?UTF-8?q?=20argc=20=E2=86=92=20ArgumentCountError=20(#30828)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match php-src simplexml.stub.php / php_dom.stub.php: excess user args throw ArgumentCountError instead of being silently accepted on VM and JIT/AOT. Co-authored-by: Cursor --- docs/bootstrap-inventory.md | 135 +++++++++--------- ext/dom/dom_import_simplexml.php | 5 +- ext/dom/ns_import_simplexml.php | 5 +- ext/simplexml/JitSimpleXmlAddChild.php | 4 + ext/simplexml/JitSimpleXmlAsXml.php | 4 + ext/simplexml/JitSimpleXmlAttributes.php | 4 + ext/simplexml/JitSimpleXmlChildren.php | 4 + ext/simplexml/JitSimpleXmlConstruct.php | 4 + ext/simplexml/JitSimpleXmlCount.php | 4 + ext/simplexml/JitSimpleXmlGetName.php | 4 + .../JitSimpleXmlRegisterXPathNamespace.php | 4 + ext/simplexml/JitSimpleXmlToString.php | 4 + ext/simplexml/JitSimpleXmlXpath.php | 4 + .../SimpleXmlElementAddAttribute.php | 9 +- ext/simplexml/SimpleXmlElementAddChild.php | 6 +- ext/simplexml/SimpleXmlElementAsXml.php | 9 +- ext/simplexml/SimpleXmlElementAttributes.php | 2 + ext/simplexml/SimpleXmlElementChildren.php | 2 + ext/simplexml/SimpleXmlElementConstruct.php | 6 +- ext/simplexml/SimpleXmlElementCount.php | 2 + .../SimpleXmlElementGetDocNamespaces.php | 2 + ext/simplexml/SimpleXmlElementGetName.php | 2 + .../SimpleXmlElementGetNamespaces.php | 2 + ext/simplexml/SimpleXmlElementIterator.php | 10 ++ ...SimpleXmlElementRegisterXPathNamespace.php | 6 +- ext/simplexml/SimpleXmlElementToString.php | 2 + ext/simplexml/SimpleXmlElementXpath.php | 6 +- ext/simplexml/simplexml_import_dom.php | 5 +- ext/simplexml/simplexml_load_file.php | 5 +- ext/simplexml/simplexml_load_string.php | 10 +- lib/JIT/Call/SimpleXMLElementAsXml.php | 17 ++- lib/JIT/SimpleXmlInstanceMethodJit.php | 4 +- lib/VM/Builtin/VmClassMethod.php | 66 +++++++++ phpunit.xml.dist | 2 + .../SimpleXmlExcessArgc30828JITTest.php | 26 ++++ .../SimpleXmlExcessArgc30828VMTest.php | 26 ++++ .../stdlib/excess_argc_simplexml_30828.phpt | 26 ++++ .../excess_argc_simplexml_30828_jit.phpt | 26 ++++ ...tainer_gap_simplexml_excess_argc_30828.php | 96 +++++++++++++ 39 files changed, 452 insertions(+), 108 deletions(-) create mode 100644 test/compliance/SimpleXmlExcessArgc30828JITTest.php create mode 100644 test/compliance/SimpleXmlExcessArgc30828VMTest.php create mode 100644 test/compliance/cases/stdlib/excess_argc_simplexml_30828.phpt create mode 100644 test/compliance/cases/stdlib/excess_argc_simplexml_30828_jit.phpt create mode 100644 test/repro/maintainer_gap_simplexml_excess_argc_30828.php diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md index 1e6f4370670..e438b7802ae 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) | 7430 | | Phase A ratio-deferred paths | 0 | | Source constructs flagged (blockers) | 0 | -| Source constructs flagged (warnings) | 24028 | +| Source constructs flagged (warnings) | 24021 | ## Compiler CFG gaps (`lib/Compiler.php`) @@ -575,8 +575,8 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/dom/XmlDocumentCreateEmpty.php` | 0 | 2 | | `ext/dom/XmlDocumentCreateFromFile.php` | 0 | 3 | | `ext/dom/XmlDocumentCreateFromString.php` | 0 | 3 | -| `ext/dom/dom_import_simplexml.php` | 0 | 2 | -| `ext/dom/ns_import_simplexml.php` | 0 | 2 | +| `ext/dom/dom_import_simplexml.php` | 0 | 1 | +| `ext/dom/ns_import_simplexml.php` | 0 | 1 | | `ext/ds/BuiltinClasses.php` | 0 | 19 | | `ext/ds/DsDepthClasses.php` | 0 | 43 | | `ext/ds/DsExtensionPolicy.php` | 0 | 1 | @@ -2082,12 +2082,12 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/simplexml/JitSimpleXmlUserScript.php` | 0 | 7 | | `ext/simplexml/JitSimpleXmlXpath.php` | 0 | 1 | | `ext/simplexml/Module.php` | 0 | 4 | -| `ext/simplexml/SimpleXmlElementAddAttribute.php` | 0 | 2 | +| `ext/simplexml/SimpleXmlElementAddAttribute.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementAddChild.php` | 0 | 1 | -| `ext/simplexml/SimpleXmlElementAsXml.php` | 0 | 2 | +| `ext/simplexml/SimpleXmlElementAsXml.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementAttributes.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementChildren.php` | 0 | 1 | -| `ext/simplexml/SimpleXmlElementConstruct.php` | 0 | 2 | +| `ext/simplexml/SimpleXmlElementConstruct.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementCount.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementGet.php` | 0 | 1 | | `ext/simplexml/SimpleXmlElementGetDocNamespaces.php` | 0 | 1 | @@ -2111,9 +2111,9 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/simplexml/SimpleXmlSerializeDeny.php` | 0 | 3 | | `ext/simplexml/VmSimpleXml.php` | 0 | 60 | | `ext/simplexml/VmSimpleXmlIterator.php` | 0 | 4 | -| `ext/simplexml/simplexml_import_dom.php` | 0 | 2 | -| `ext/simplexml/simplexml_load_file.php` | 0 | 2 | -| `ext/simplexml/simplexml_load_string.php` | 0 | 2 | +| `ext/simplexml/simplexml_import_dom.php` | 0 | 1 | +| `ext/simplexml/simplexml_load_file.php` | 0 | 1 | +| `ext/simplexml/simplexml_load_string.php` | 0 | 1 | | `ext/snmp/BuiltinClasses.php` | 0 | 1 | | `ext/snmp/Module.php` | 0 | 25 | | `ext/snmp/SNMPClose.php` | 0 | 2 | @@ -7080,7 +7080,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `lib/VM/Builtin/SensitiveParameterValueDebugInfo.php` | 0 | 1 | | `lib/VM/Builtin/SensitiveParameterValueGetValue.php` | 0 | 1 | | `lib/VM/Builtin/ThrowableGetMessage.php` | 0 | 1 | -| `lib/VM/Builtin/VmClassMethod.php` | 0 | 4 | +| `lib/VM/Builtin/VmClassMethod.php` | 0 | 5 | | `lib/VM/Builtin/WeakMapConstruct.php` | 0 | 1 | | `lib/VM/Builtin/WeakMapCount.php` | 0 | 1 | | `lib/VM/Builtin/WeakMapGetIterator.php` | 0 | 1 | @@ -11528,13 +11528,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/dom/dom_import_simplexml.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 24) - 2 class method(s) ### `ext/dom/ns_import_simplexml.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 30) - 2 class method(s) ### `ext/ds/BuiltinClasses.php` @@ -24403,7 +24401,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/JitSimpleXmlConstruct.php` **Warnings** (review for bootstrap subset): -- new Exception (line 26) +- new Exception (line 30) - 1 class method(s) ### `ext/simplexml/JitSimpleXmlCount.php` @@ -24435,7 +24433,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/JitSimpleXmlRegisterXPathNamespace.php` **Warnings** (review for bootstrap subset): -- new ExternalMethod (line 25) +- new ExternalMethod (line 29) - 1 class method(s) ### `ext/simplexml/JitSimpleXmlToString.php` @@ -24470,7 +24468,6 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/SimpleXmlElementAddAttribute.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 25) - 1 class method(s) ### `ext/simplexml/SimpleXmlElementAddChild.php` @@ -24481,7 +24478,6 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/SimpleXmlElementAsXml.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 34) - 3 class method(s) ### `ext/simplexml/SimpleXmlElementAttributes.php` @@ -24497,7 +24493,6 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/SimpleXmlElementConstruct.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 28) - 1 class method(s) ### `ext/simplexml/SimpleXmlElementCount.php` @@ -24703,19 +24698,16 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/simplexml/simplexml_import_dom.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 30) - 3 class method(s) ### `ext/simplexml/simplexml_load_file.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 26) - 2 class method(s) ### `ext/simplexml/simplexml_load_string.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 26) - 2 class method(s) ### `ext/snmp/BuiltinClasses.php` @@ -26878,7 +26870,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ArgumentCountError (line 240) - new ArgumentCountError (line 278) - new ObjectEntry (line 421) -- new ArgumentCountError (line 506) +- new ArgumentCountError (line 512) - 18 class method(s) ### `ext/spl/FilterIteratorBuiltin.php` @@ -27261,18 +27253,18 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ClassEntry (line 50) - new SplFileObjectConstruct (line 69) - new ArgumentCountError (line 171) -- new ArgumentCountError (line 296) -- new ArgumentCountError (line 339) -- new ArgumentCountError (line 377) -- new ArgumentCountError (line 569) -- new ArgumentCountError (line 599) -- new ArgumentCountError (line 698) -- new ArgumentCountError (line 754) -- new ArgumentCountError (line 816) -- new ArgumentCountError (line 894) -- new ArgumentCountError (line 997) -- new Variable (line 1132) -- new ArgumentCountError (line 1175) +- new ArgumentCountError (line 284) +- new ArgumentCountError (line 327) +- new ArgumentCountError (line 365) +- new ArgumentCountError (line 559) +- new ArgumentCountError (line 589) +- new ArgumentCountError (line 688) +- new ArgumentCountError (line 746) +- new ArgumentCountError (line 808) +- new ArgumentCountError (line 886) +- new ArgumentCountError (line 989) +- new Variable (line 1124) +- new ArgumentCountError (line 1167) - 35 class method(s) ### `ext/spl/SplFileObjectSnapshotJitHelper.php` @@ -36052,33 +36044,33 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new Variable (line 4018) - new Variable (line 4116) - new Variable (line 4130) -- new Variable (line 4297) -- new Variable (line 4302) -- new Error (line 4322) -- new Error (line 4326) -- new Variable (line 4406) -- new Variable (line 4412) -- new Variable (line 4418) -- new Variable (line 4464) -- new Variable (line 4469) -- new Variable (line 4498) +- new Variable (line 4308) +- new Variable (line 4313) +- new Error (line 4333) +- new Error (line 4337) +- new Variable (line 4417) +- new Variable (line 4423) +- new Variable (line 4429) +- new Variable (line 4475) +- new Variable (line 4480) - new Variable (line 4508) -- new Variable (line 4514) -- new Variable (line 4536) -- new Variable (line 4557) -- new HashTable (line 4591) -- new Variable (line 4608) -- new Variable (line 4610) -- new HashTable (line 4711) -- new Variable (line 4743) -- new Variable (line 4745) -- new HashTable (line 4758) -- new Variable (line 4765) -- new Variable (line 4776) -- new HashTable (line 4791) -- new Variable (line 4794) -- new HashTable (line 4813) -- new Variable (line 4816) +- new Variable (line 4518) +- new Variable (line 4524) +- new Variable (line 4546) +- new Variable (line 4567) +- new HashTable (line 4601) +- new Variable (line 4618) +- new Variable (line 4620) +- new HashTable (line 4721) +- new Variable (line 4753) +- new Variable (line 4755) +- new HashTable (line 4768) +- new Variable (line 4775) +- new Variable (line 4786) +- new HashTable (line 4801) +- new Variable (line 4804) +- new HashTable (line 4823) +- new Variable (line 4826) - 205 class method(s) - 2 closure(s) @@ -54548,15 +54540,15 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new Call\SimpleXMLElementConstruct (line 47) - new Call\SimpleXMLElementAddChild (line 52) - new Call\SimpleXMLElementAsXml (line 58) -- new Call\SimpleXMLElementXpath (line 63) -- new Call\SimpleXMLElementRegisterXPathNamespace (line 68) -- new Call\SimpleXMLElementGet (line 73) -- new Call\SimpleXMLElementOffsetGet (line 78) -- new Call\SimpleXMLElementCount (line 83) -- new Call\SimpleXMLElementToString (line 88) -- new Call\SimpleXMLElementChildren (line 93) -- new Call\SimpleXMLElementAttributes (line 98) -- new Call\SimpleXMLElementGetName (line 103) +- new Call\SimpleXMLElementXpath (line 65) +- new Call\SimpleXMLElementRegisterXPathNamespace (line 70) +- new Call\SimpleXMLElementGet (line 75) +- new Call\SimpleXMLElementOffsetGet (line 80) +- new Call\SimpleXMLElementCount (line 85) +- new Call\SimpleXMLElementToString (line 90) +- new Call\SimpleXMLElementChildren (line 95) +- new Call\SimpleXMLElementAttributes (line 100) +- new Call\SimpleXMLElementGetName (line 105) - 2 class method(s) ### `lib/JIT/SplAutoloadCallbackPolicy.php` @@ -57943,10 +57935,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `lib/VM/Builtin/VmClassMethod.php` **Warnings** (review for bootstrap subset): -- new ArgumentCountError (line 38) -- new ArgumentCountError (line 49) +- new ArgumentCountError (line 41) - new ArgumentCountError (line 52) -- 7 class method(s) +- new ArgumentCountError (line 63) +- new ArgumentCountError (line 66) +- 11 class method(s) ### `lib/VM/Builtin/WeakMapConstruct.php` diff --git a/ext/dom/dom_import_simplexml.php b/ext/dom/dom_import_simplexml.php index ff3579fb5ac..ad2ad219f66 100644 --- a/ext/dom/dom_import_simplexml.php +++ b/ext/dom/dom_import_simplexml.php @@ -20,9 +20,8 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 1) { - throw new \ArgumentCountError('dom_import_simplexml() expects at least 1 argument, 0 given'); - } + // php-src ext/dom/php_dom.stub.php: dom_import_simplexml(object $node) (#30828). + $this->requireExactArgCount($frame, 'dom_import_simplexml', 1); if (null === $frame->vmContext) { throw new \LogicException('dom_import_simplexml() requires VM context'); } diff --git a/ext/dom/ns_import_simplexml.php b/ext/dom/ns_import_simplexml.php index e1f0228a498..82926bac6ec 100644 --- a/ext/dom/ns_import_simplexml.php +++ b/ext/dom/ns_import_simplexml.php @@ -26,9 +26,8 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 1) { - throw new \ArgumentCountError('Dom\\import_simplexml() expects at least 1 argument, 0 given'); - } + // php-src php_dom.stub.php: Dom\import_simplexml(object $node) (#30828). + $this->requireExactArgCount($frame, 'Dom\\import_simplexml', 1); if (null === $frame->vmContext) { throw new \LogicException('Dom\\import_simplexml() requires VM context'); } diff --git a/ext/simplexml/JitSimpleXmlAddChild.php b/ext/simplexml/JitSimpleXmlAddChild.php index 49a4f9a3936..0b19ab4892d 100644 --- a/ext/simplexml/JitSimpleXmlAddChild.php +++ b/ext/simplexml/JitSimpleXmlAddChild.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::addChild() — user-script AOT (#19306). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlAddChild { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange($context, $args, 'SimpleXMLElement::addChild', 1, 3)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if (\count($args) < 2) { throw new \LogicException('SimpleXMLElement::addChild() expects receiver and name'); } diff --git a/ext/simplexml/JitSimpleXmlAsXml.php b/ext/simplexml/JitSimpleXmlAsXml.php index 6ae3f3a1a8b..a1cd2dd63f9 100644 --- a/ext/simplexml/JitSimpleXmlAsXml.php +++ b/ext/simplexml/JitSimpleXmlAsXml.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::asXML() — user-script AOT (#19306). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlAsXml { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange($context, $args, 'SimpleXMLElement::asXML', 0, 1)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if ([] === $args) { throw new \LogicException('SimpleXMLElement::asXML() expects receiver'); } diff --git a/ext/simplexml/JitSimpleXmlAttributes.php b/ext/simplexml/JitSimpleXmlAttributes.php index 5ad0a0c30d2..79dc91adf07 100644 --- a/ext/simplexml/JitSimpleXmlAttributes.php +++ b/ext/simplexml/JitSimpleXmlAttributes.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::attributes() — user-script AOT (#27535). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlAttributes { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange($context, $args, 'SimpleXMLElement::attributes', 0, 2)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if ([] === $args) { throw new \LogicException('SimpleXMLElement::attributes() expects receiver'); } diff --git a/ext/simplexml/JitSimpleXmlChildren.php b/ext/simplexml/JitSimpleXmlChildren.php index 9b04da52de7..7804488882d 100644 --- a/ext/simplexml/JitSimpleXmlChildren.php +++ b/ext/simplexml/JitSimpleXmlChildren.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::children() — user-script AOT (#27535). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlChildren { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange($context, $args, 'SimpleXMLElement::children', 0, 2)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if ([] === $args) { throw new \LogicException('SimpleXMLElement::children() expects receiver'); } diff --git a/ext/simplexml/JitSimpleXmlConstruct.php b/ext/simplexml/JitSimpleXmlConstruct.php index f2e4fc87af0..2ea15fff653 100644 --- a/ext/simplexml/JitSimpleXmlConstruct.php +++ b/ext/simplexml/JitSimpleXmlConstruct.php @@ -7,6 +7,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\JitValueBox; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::__construct() — user-script AOT (#19306). */ @@ -14,6 +15,9 @@ final class JitSimpleXmlConstruct { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange($context, $args, 'SimpleXMLElement::__construct', 1, 5)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if (\count($args) < 2) { throw new \LogicException('SimpleXMLElement::__construct() expects receiver and data'); } diff --git a/ext/simplexml/JitSimpleXmlCount.php b/ext/simplexml/JitSimpleXmlCount.php index 5051700679f..cfe6f0ee30b 100644 --- a/ext/simplexml/JitSimpleXmlCount.php +++ b/ext/simplexml/JitSimpleXmlCount.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::count() — user-script AOT (#26863). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlCount { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireExactJitUserArgCount($context, $args, 'SimpleXMLElement::count', 0)) { + return VmClassMethod::jitArgcDummyReturn($context); + } $us = JitSimpleXmlUserScript::tryCount($context, ...$args); if (null !== $us) { return $us; diff --git a/ext/simplexml/JitSimpleXmlGetName.php b/ext/simplexml/JitSimpleXmlGetName.php index e395f1faa20..8d3dee71a3b 100644 --- a/ext/simplexml/JitSimpleXmlGetName.php +++ b/ext/simplexml/JitSimpleXmlGetName.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::getName() — user-script AOT (#27535). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlGetName { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireExactJitUserArgCount($context, $args, 'SimpleXMLElement::getName', 0)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if ([] === $args) { throw new \LogicException('SimpleXMLElement::getName() expects receiver'); } diff --git a/ext/simplexml/JitSimpleXmlRegisterXPathNamespace.php b/ext/simplexml/JitSimpleXmlRegisterXPathNamespace.php index bb3e40b69a3..1eb6d4f7ba1 100644 --- a/ext/simplexml/JitSimpleXmlRegisterXPathNamespace.php +++ b/ext/simplexml/JitSimpleXmlRegisterXPathNamespace.php @@ -7,6 +7,7 @@ use PHPCompiler\JIT\Call\ExternalMethod; use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::registerXPathNamespace() — user-script AOT (#27534). */ @@ -14,6 +15,9 @@ final class JitSimpleXmlRegisterXPathNamespace { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireExactJitUserArgCount($context, $args, 'SimpleXMLElement::registerXPathNamespace', 2)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if (\count($args) < 3) { throw new \LogicException('SimpleXMLElement::registerXPathNamespace() expects receiver, prefix, and namespace'); } diff --git a/ext/simplexml/JitSimpleXmlToString.php b/ext/simplexml/JitSimpleXmlToString.php index 0f63d1f4efd..44289988bd7 100644 --- a/ext/simplexml/JitSimpleXmlToString.php +++ b/ext/simplexml/JitSimpleXmlToString.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::__toString() — user-script AOT (#26863). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlToString { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireExactJitUserArgCount($context, $args, 'SimpleXMLElement::__toString', 0)) { + return VmClassMethod::jitArgcDummyReturn($context); + } $us = JitSimpleXmlUserScript::tryToString($context, ...$args); if (null !== $us) { return $us; diff --git a/ext/simplexml/JitSimpleXmlXpath.php b/ext/simplexml/JitSimpleXmlXpath.php index dd91edbba26..a7302a369d6 100644 --- a/ext/simplexml/JitSimpleXmlXpath.php +++ b/ext/simplexml/JitSimpleXmlXpath.php @@ -6,6 +6,7 @@ use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable as JITVariable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; /** LLVM lowering for SimpleXMLElement::xpath() — user-script AOT (#22720). */ @@ -13,6 +14,9 @@ final class JitSimpleXmlXpath { public static function invoke(Context $context, JITVariable ...$args): Value { + if (!VmClassMethod::requireExactJitUserArgCount($context, $args, 'SimpleXMLElement::xpath', 1)) { + return VmClassMethod::jitArgcDummyReturn($context); + } if (\count($args) < 2) { throw new \LogicException('SimpleXMLElement::xpath() expects receiver and path'); } diff --git a/ext/simplexml/SimpleXmlElementAddAttribute.php b/ext/simplexml/SimpleXmlElementAddAttribute.php index d6177a34044..90cfacab6de 100644 --- a/ext/simplexml/SimpleXmlElementAddAttribute.php +++ b/ext/simplexml/SimpleXmlElementAddAttribute.php @@ -21,12 +21,11 @@ public function execute(Frame $frame): void if (null === $frame->vmContext) { throw new \LogicException('SimpleXMLElement::addAttribute() requires VM context'); } - if (\count($frame->calledArgs) < 3) { - throw new \ArgumentCountError( - 'SimpleXMLElement::addAttribute() expects at least 2 arguments, ' - .(\count($frame->calledArgs) - 1).' given' - ); + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::addAttribute() called without $this'); } + // php-src simplexml.stub.php: addAttribute(string $qualifiedName, string $value = "", ?string $namespace = null) (#30828). + $this->requireUserArgCountRange($frame, 'SimpleXMLElement::addAttribute', 2, 3); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::addAttribute()' diff --git a/ext/simplexml/SimpleXmlElementAddChild.php b/ext/simplexml/SimpleXmlElementAddChild.php index ce7694901a3..0675280964e 100644 --- a/ext/simplexml/SimpleXmlElementAddChild.php +++ b/ext/simplexml/SimpleXmlElementAddChild.php @@ -21,9 +21,11 @@ public function execute(Frame $frame): void if (null === $frame->vmContext) { throw new \LogicException('SimpleXMLElement::addChild() requires VM context'); } - if (\count($frame->calledArgs) < 2) { - throw new \LogicException('SimpleXMLElement::addChild() expects at least 1 argument'); + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::addChild() called without $this'); } + // php-src simplexml.stub.php: addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null) (#30828). + $this->requireUserArgCountRange($frame, 'SimpleXMLElement::addChild', 1, 3); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::addChild()' diff --git a/ext/simplexml/SimpleXmlElementAsXml.php b/ext/simplexml/SimpleXmlElementAsXml.php index af3cf3afaa9..d90b62d4829 100644 --- a/ext/simplexml/SimpleXmlElementAsXml.php +++ b/ext/simplexml/SimpleXmlElementAsXml.php @@ -30,13 +30,8 @@ public function execute(Frame $frame): void if ($argc < 1) { throw new \LogicException($label.' called without $this'); } - if ($argc > 2) { - throw new \ArgumentCountError(\sprintf( - '%s expects at most 1 argument, %d given', - $label, - $argc - 1 - )); - } + // php-src simplexml.stub.php: asXML(?string $filename = null) / saveXML FALIAS (#30828). + $this->requireAtMostUserArgCount($frame, 'SimpleXMLElement::'.$this->name, 1); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), $label diff --git a/ext/simplexml/SimpleXmlElementAttributes.php b/ext/simplexml/SimpleXmlElementAttributes.php index 94d4e9830a2..207f16f809c 100644 --- a/ext/simplexml/SimpleXmlElementAttributes.php +++ b/ext/simplexml/SimpleXmlElementAttributes.php @@ -24,6 +24,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::attributes() called without $this'); } + // php-src simplexml.stub.php: attributes(?string $namespaceOrPrefix = null, bool $isPrefix = false) (#30828). + $this->requireAtMostUserArgCount($frame, 'SimpleXMLElement::attributes', 2); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::attributes()' diff --git a/ext/simplexml/SimpleXmlElementChildren.php b/ext/simplexml/SimpleXmlElementChildren.php index ce44772cfd4..a41304919ec 100644 --- a/ext/simplexml/SimpleXmlElementChildren.php +++ b/ext/simplexml/SimpleXmlElementChildren.php @@ -24,6 +24,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::children() called without $this'); } + // php-src simplexml.stub.php: children(?string $namespaceOrPrefix = null, bool $isPrefix = false) (#30828). + $this->requireAtMostUserArgCount($frame, 'SimpleXMLElement::children', 2); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::children()' diff --git a/ext/simplexml/SimpleXmlElementConstruct.php b/ext/simplexml/SimpleXmlElementConstruct.php index 30bbf546b85..d3d287a4ed8 100644 --- a/ext/simplexml/SimpleXmlElementConstruct.php +++ b/ext/simplexml/SimpleXmlElementConstruct.php @@ -24,9 +24,11 @@ public function execute(Frame $frame): void if (null === $frame->vmContext) { throw new \LogicException('SimpleXMLElement::__construct() requires VM context'); } - if (\count($frame->calledArgs) < 2) { - throw new \ArgumentCountError('SimpleXMLElement::__construct() expects at least 1 argument, 0 given'); + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::__construct() called without $this'); } + // php-src simplexml.stub.php: __construct(string $data, int $options = 0, bool $dataIsURL = false, string $namespaceOrPrefix = "", bool $isPrefix = false) (#30828). + $this->requireUserArgCountRange($frame, 'SimpleXMLElement::__construct', 1, 5); $entry = $frame->calledArgs[0]->resolveIndirect()->toObject(); $dataVar = $frame->calledArgs[1]->resolveIndirect(); if (Variable::TYPE_STRING !== $dataVar->type) { diff --git a/ext/simplexml/SimpleXmlElementCount.php b/ext/simplexml/SimpleXmlElementCount.php index 2691f4a3384..4f34f5272bf 100644 --- a/ext/simplexml/SimpleXmlElementCount.php +++ b/ext/simplexml/SimpleXmlElementCount.php @@ -20,6 +20,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::count() called without $this'); } + // php-src simplexml.stub.php: count(): int (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::count', 0); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::count()' diff --git a/ext/simplexml/SimpleXmlElementGetDocNamespaces.php b/ext/simplexml/SimpleXmlElementGetDocNamespaces.php index 71538ede7ae..0127ae2c5e8 100644 --- a/ext/simplexml/SimpleXmlElementGetDocNamespaces.php +++ b/ext/simplexml/SimpleXmlElementGetDocNamespaces.php @@ -21,6 +21,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::getDocNamespaces() called without $this'); } + // php-src simplexml.stub.php: getDocNamespaces(bool $recursive = false, bool $fromRoot = true) (#30828). + $this->requireAtMostUserArgCount($frame, 'SimpleXMLElement::getDocNamespaces', 2); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::getDocNamespaces()' diff --git a/ext/simplexml/SimpleXmlElementGetName.php b/ext/simplexml/SimpleXmlElementGetName.php index 0c21cb7e099..89b76843344 100644 --- a/ext/simplexml/SimpleXmlElementGetName.php +++ b/ext/simplexml/SimpleXmlElementGetName.php @@ -20,6 +20,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::getName() called without $this'); } + // php-src simplexml.stub.php: getName(): string (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::getName', 0); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::getName()' diff --git a/ext/simplexml/SimpleXmlElementGetNamespaces.php b/ext/simplexml/SimpleXmlElementGetNamespaces.php index 987e325c3b8..dfc099f7a5e 100644 --- a/ext/simplexml/SimpleXmlElementGetNamespaces.php +++ b/ext/simplexml/SimpleXmlElementGetNamespaces.php @@ -21,6 +21,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::getNamespaces() called without $this'); } + // php-src simplexml.stub.php: getNamespaces(bool $recursive = false): array (#30828). + $this->requireAtMostUserArgCount($frame, 'SimpleXMLElement::getNamespaces', 1); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::getNamespaces()' diff --git a/ext/simplexml/SimpleXmlElementIterator.php b/ext/simplexml/SimpleXmlElementIterator.php index 4f4a9181363..c0ad7f19654 100644 --- a/ext/simplexml/SimpleXmlElementIterator.php +++ b/ext/simplexml/SimpleXmlElementIterator.php @@ -241,6 +241,11 @@ public function __construct() public function execute(Frame $frame): void { + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::hasChildren() called without $this'); + } + // php-src simplexml.stub.php: hasChildren(): bool (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::hasChildren', 0); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::hasChildren()' @@ -264,6 +269,11 @@ public function execute(Frame $frame): void if (null === $frame->vmContext) { throw new \LogicException('SimpleXMLElement::getChildren() requires VM context'); } + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::getChildren() called without $this'); + } + // php-src simplexml.stub.php: getChildren(): ?SimpleXMLElement (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::getChildren', 0); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::getChildren()' diff --git a/ext/simplexml/SimpleXmlElementRegisterXPathNamespace.php b/ext/simplexml/SimpleXmlElementRegisterXPathNamespace.php index 89eead22130..7ab6671bbb1 100644 --- a/ext/simplexml/SimpleXmlElementRegisterXPathNamespace.php +++ b/ext/simplexml/SimpleXmlElementRegisterXPathNamespace.php @@ -18,9 +18,11 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 3) { - throw new \LogicException('SimpleXMLElement::registerXPathNamespace() expects at least 2 arguments'); + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::registerXPathNamespace() called without $this'); } + // php-src simplexml.stub.php: registerXPathNamespace(string $prefix, string $namespace) (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::registerXPathNamespace', 2); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::registerXPathNamespace()' diff --git a/ext/simplexml/SimpleXmlElementToString.php b/ext/simplexml/SimpleXmlElementToString.php index 091b772bf37..63f9ab73620 100644 --- a/ext/simplexml/SimpleXmlElementToString.php +++ b/ext/simplexml/SimpleXmlElementToString.php @@ -20,6 +20,8 @@ public function execute(Frame $frame): void if (\count($frame->calledArgs) < 1) { throw new \LogicException('SimpleXMLElement::__toString() called without $this'); } + // php-src simplexml.stub.php: __toString(): string (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::__toString', 0); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::__toString()' diff --git a/ext/simplexml/SimpleXmlElementXpath.php b/ext/simplexml/SimpleXmlElementXpath.php index 006ae8024be..63e25335751 100644 --- a/ext/simplexml/SimpleXmlElementXpath.php +++ b/ext/simplexml/SimpleXmlElementXpath.php @@ -21,9 +21,11 @@ public function execute(Frame $frame): void if (null === $frame->vmContext) { throw new \LogicException('SimpleXMLElement::xpath() requires VM context'); } - if (\count($frame->calledArgs) < 2) { - throw new \LogicException('SimpleXMLElement::xpath() expects at least 1 argument'); + if (\count($frame->calledArgs) < 1) { + throw new \LogicException('SimpleXMLElement::xpath() called without $this'); } + // php-src simplexml.stub.php: xpath(string $expression) (#30828). + $this->requireExactUserArgCount($frame, 'SimpleXMLElement::xpath', 1); $entry = VmSimpleXml::requireElement( $frame->calledArgs[0]->resolveIndirect()->toObject(), 'SimpleXMLElement::xpath()' diff --git a/ext/simplexml/simplexml_import_dom.php b/ext/simplexml/simplexml_import_dom.php index aee27bf9d46..10c53a2efef 100644 --- a/ext/simplexml/simplexml_import_dom.php +++ b/ext/simplexml/simplexml_import_dom.php @@ -26,9 +26,8 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 1) { - throw new \ArgumentCountError('simplexml_import_dom() expects at least 1 argument, 0 given'); - } + // php-src simplexml.stub.php: simplexml_import_dom(object $node, ?string $class = SimpleXMLElement::class) (#30828). + $this->requireArgCountRange($frame, 'simplexml_import_dom', 1, 2); if (null === $frame->vmContext) { throw new \LogicException('simplexml_import_dom() requires VM context'); } diff --git a/ext/simplexml/simplexml_load_file.php b/ext/simplexml/simplexml_load_file.php index 5d12f4b3022..ca36560dc60 100644 --- a/ext/simplexml/simplexml_load_file.php +++ b/ext/simplexml/simplexml_load_file.php @@ -22,9 +22,8 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 1) { - throw new \ArgumentCountError('simplexml_load_file() expects at least 1 argument, 0 given'); - } + // php-src simplexml.stub.php: simplexml_load_file — same arity as load_string (#30828). + $this->requireArgCountRange($frame, 'simplexml_load_file', 1, 5); if (null === $frame->vmContext) { throw new \LogicException('simplexml_load_file() requires VM context'); } diff --git a/ext/simplexml/simplexml_load_string.php b/ext/simplexml/simplexml_load_string.php index 3b7c6106aae..7a5f36d833f 100644 --- a/ext/simplexml/simplexml_load_string.php +++ b/ext/simplexml/simplexml_load_string.php @@ -8,6 +8,7 @@ use PHPCompiler\Frame; use PHPCompiler\Func\Internal; use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\JitValueBox; use PHPCompiler\JIT\Variable as JITVariable; use PHPCompiler\VM\Variable; use PHPLLVM\Value; @@ -22,9 +23,8 @@ public function __construct() public function execute(Frame $frame): void { - if (\count($frame->calledArgs) < 1) { - throw new \ArgumentCountError('simplexml_load_string() expects at least 1 argument, 0 given'); - } + // php-src simplexml.stub.php: simplexml_load_string($data, $class_name = SimpleXMLElement::class, $options = 0, $namespace_or_prefix = "", $is_prefix = false) (#30828). + $this->requireArgCountRange($frame, 'simplexml_load_string', 1, 5); if (null === $frame->vmContext) { throw new \LogicException('simplexml_load_string() requires VM context'); } @@ -62,6 +62,10 @@ public function execute(Frame $frame): void public function call(Context $context, JITVariable ...$args): Value { + if (!$this->requireArgCountRangeJit($context, $args, 'simplexml_load_string', 1, 5)) { + return JitValueBox::pointer($context, JitValueBox::alloc($context)); + } + return JitSimpleXmlLoadString::invoke($context, ...$args); } } diff --git a/lib/JIT/Call/SimpleXMLElementAsXml.php b/lib/JIT/Call/SimpleXMLElementAsXml.php index a3b3ee7a0a5..1df6b7d1de7 100644 --- a/lib/JIT/Call/SimpleXMLElementAsXml.php +++ b/lib/JIT/Call/SimpleXMLElementAsXml.php @@ -8,13 +8,28 @@ use PHPCompiler\JIT\Call; use PHPCompiler\JIT\Context; use PHPCompiler\JIT\Variable; +use PHPCompiler\VM\Builtin\VmClassMethod; use PHPLLVM\Value; -/** SimpleXMLElement::asXML() — user-script AOT (#19306). */ +/** SimpleXMLElement::asXML() / saveXML() — user-script AOT (#19306, #30828). */ final class SimpleXMLElementAsXml implements Call { + public function __construct(private string $method = 'asXML') + { + } + public function call(Context $context, Variable ...$args): Value { + if (!VmClassMethod::requireJitUserArgCountRange( + $context, + $args, + 'SimpleXMLElement::'.$this->method, + 0, + 1 + )) { + return VmClassMethod::jitArgcDummyReturn($context); + } + return JitSimpleXmlAsXml::invoke($context, ...$args); } } diff --git a/lib/JIT/SimpleXmlInstanceMethodJit.php b/lib/JIT/SimpleXmlInstanceMethodJit.php index c210ad3017f..e4934af8e6f 100644 --- a/lib/JIT/SimpleXmlInstanceMethodJit.php +++ b/lib/JIT/SimpleXmlInstanceMethodJit.php @@ -55,7 +55,9 @@ public static function ensureProxy(Context $context, string $proxyName): void } // saveXML is a php-src FALIAS of asXML (#19413). if ('simplexmlelement::asxml' === $lc || 'simplexmlelement::savexml' === $lc) { - $context->functionProxies[$lc] = new Call\SimpleXMLElementAsXml(); + $context->functionProxies[$lc] = new Call\SimpleXMLElementAsXml( + 'simplexmlelement::savexml' === $lc ? 'saveXML' : 'asXML' + ); return; } diff --git a/lib/VM/Builtin/VmClassMethod.php b/lib/VM/Builtin/VmClassMethod.php index 819ebaa7e71..2af6c272565 100644 --- a/lib/VM/Builtin/VmClassMethod.php +++ b/lib/VM/Builtin/VmClassMethod.php @@ -6,7 +6,10 @@ use PHPCompiler\Frame; use PHPCompiler\Func\Internal; +use PHPCompiler\JIT\BasicBlockHelper; use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\ExceptionBridge; +use PHPCompiler\JIT\JitValueBox; use PHPCompiler\JIT\Variable as JITVariable; use PHPLLVM\Value; @@ -39,6 +42,17 @@ protected function requireExactUserArgCount(Frame $frame, string $function, int } } + /** + * At-most user arity → Zend ArgumentCountError (#30828). + */ + protected function requireAtMostUserArgCount(Frame $frame, string $function, int $maximum): void + { + $given = $this->userArgCount($frame); + if ($given > $maximum) { + throw new \ArgumentCountError(self::atMostUserArgCountMessage($function, $maximum, $given)); + } + } + /** * Inclusive user-arity range → Zend ArgumentCountError (#30834). */ @@ -85,4 +99,56 @@ public static function atLeastUserArgCountMessage(string $function, int $minimum $given ); } + + /** + * Instance-method JIT argc — $args[0] is $this (php-src ZEND_NUM_ARGS; #30828). + * + * @param JITVariable[] $args + */ + public static function requireJitUserArgCountRange( + Context $context, + array $args, + string $function, + int $minimum, + int $maximum + ): bool { + $given = max(0, \count($args) - 1); + if ($given < $minimum) { + ExceptionBridge::emitArgumentCountErrorAndAbort( + $context, + self::atLeastUserArgCountMessage($function, $minimum, $given) + ); + BasicBlockHelper::ensureOpenInsertBlock($context, $function.'_argc_cont'); + + return false; + } + if ($given > $maximum) { + ExceptionBridge::emitArgumentCountErrorAndAbort( + $context, + self::atMostUserArgCountMessage($function, $maximum, $given) + ); + BasicBlockHelper::ensureOpenInsertBlock($context, $function.'_argc_cont'); + + return false; + } + + return true; + } + + /** + * @param JITVariable[] $args + */ + public static function requireExactJitUserArgCount( + Context $context, + array $args, + string $function, + int $expected + ): bool { + return self::requireJitUserArgCountRange($context, $args, $function, $expected, $expected); + } + + public static function jitArgcDummyReturn(Context $context): Value + { + return JitValueBox::pointer($context, JitValueBox::alloc($context)); + } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index db5c02a9c10..6cf6e09bcdc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -372,6 +372,8 @@ ./test/compliance/XmlReaderExcessArgc30641JITTest.php ./test/compliance/XmlWriterExcessArgc30818VMTest.php ./test/compliance/XmlWriterExcessArgc30818JITTest.php + ./test/compliance/SimpleXmlExcessArgc30828VMTest.php + ./test/compliance/SimpleXmlExcessArgc30828JITTest.php ./test/compliance/GetCalledClassExcessArgc30648VMTest.php ./test/compliance/GetCalledClassExcessArgc30648JITTest.php ./test/compliance/ReflectionEnumExcessArgc30865VMTest.php diff --git a/test/compliance/SimpleXmlExcessArgc30828JITTest.php b/test/compliance/SimpleXmlExcessArgc30828JITTest.php new file mode 100644 index 00000000000..8a4f436a8de --- /dev/null +++ b/test/compliance/SimpleXmlExcessArgc30828JITTest.php @@ -0,0 +1,26 @@ + self::parsePHPT( + __DIR__.'/cases/stdlib/excess_argc_simplexml_30828_jit.phpt', + 'excess_argc_simplexml_30828_jit.phpt' + ); + } + + public function setUp(): void + { + $this->BIN = realpath(__DIR__.'/../../bin/jit.php'); + } +} diff --git a/test/compliance/SimpleXmlExcessArgc30828VMTest.php b/test/compliance/SimpleXmlExcessArgc30828VMTest.php new file mode 100644 index 00000000000..dd9dba4e920 --- /dev/null +++ b/test/compliance/SimpleXmlExcessArgc30828VMTest.php @@ -0,0 +1,26 @@ + self::parsePHPT( + __DIR__.'/cases/stdlib/excess_argc_simplexml_30828.phpt', + 'excess_argc_simplexml_30828.phpt' + ); + } + + public function setUp(): void + { + $this->BIN = realpath(__DIR__.'/../../bin/vm.php'); + } +} diff --git a/test/compliance/cases/stdlib/excess_argc_simplexml_30828.phpt b/test/compliance/cases/stdlib/excess_argc_simplexml_30828.phpt new file mode 100644 index 00000000000..9b24390fd25 --- /dev/null +++ b/test/compliance/cases/stdlib/excess_argc_simplexml_30828.phpt @@ -0,0 +1,26 @@ +--TEST-- +SimpleXML methods/import excess argc → ArgumentCountError (#30828) +--RUNFILE-- +../../../repro/maintainer_gap_simplexml_excess_argc_30828.php +--EXPECT-- +SimpleXMLElement::children() expects at most 2 arguments, 3 given +SimpleXMLElement::attributes() expects at most 2 arguments, 3 given +SimpleXMLElement::xpath() expects exactly 1 argument, 2 given +SimpleXMLElement::registerXPathNamespace() expects exactly 2 arguments, 3 given +SimpleXMLElement::addChild() expects at most 3 arguments, 4 given +SimpleXMLElement::addAttribute() expects at most 3 arguments, 4 given +dom_import_simplexml() expects exactly 1 argument, 2 given +simplexml_import_dom() expects at most 2 arguments, 3 given +simplexml_load_string() expects at most 5 arguments, 6 given +SimpleXMLElement::getName() expects exactly 0 arguments, 1 given +SimpleXMLElement::count() expects exactly 0 arguments, 1 given +SimpleXMLElement::getNamespaces() expects at most 1 argument, 2 given +SimpleXMLElement::getDocNamespaces() expects at most 2 arguments, 3 given +SimpleXMLElement::asXML() expects at most 1 argument, 2 given +SimpleXMLElement::saveXML() expects at most 1 argument, 2 given +simplexml_load_file() expects at most 5 arguments, 6 given +SimpleXMLElement::__construct() expects at most 5 arguments, 6 given +SimpleXMLElement::__toString() expects exactly 0 arguments, 1 given +SimpleXMLElement::hasChildren() expects exactly 0 arguments, 1 given +SimpleXMLElement::getChildren() expects exactly 0 arguments, 1 given +ok=r,3,r diff --git a/test/compliance/cases/stdlib/excess_argc_simplexml_30828_jit.phpt b/test/compliance/cases/stdlib/excess_argc_simplexml_30828_jit.phpt new file mode 100644 index 00000000000..0b02942a183 --- /dev/null +++ b/test/compliance/cases/stdlib/excess_argc_simplexml_30828_jit.phpt @@ -0,0 +1,26 @@ +--TEST-- +SimpleXML methods/import excess argc → ArgumentCountError JIT (#30828) +--RUNFILE-- +../../../repro/maintainer_gap_simplexml_excess_argc_30828.php +--EXPECT-- +SimpleXMLElement::children() expects at most 2 arguments, 3 given +SimpleXMLElement::attributes() expects at most 2 arguments, 3 given +SimpleXMLElement::xpath() expects exactly 1 argument, 2 given +SimpleXMLElement::registerXPathNamespace() expects exactly 2 arguments, 3 given +SimpleXMLElement::addChild() expects at most 3 arguments, 4 given +SimpleXMLElement::addAttribute() expects at most 3 arguments, 4 given +dom_import_simplexml() expects exactly 1 argument, 2 given +simplexml_import_dom() expects at most 2 arguments, 3 given +simplexml_load_string() expects at most 5 arguments, 6 given +SimpleXMLElement::getName() expects exactly 0 arguments, 1 given +SimpleXMLElement::count() expects exactly 0 arguments, 1 given +SimpleXMLElement::getNamespaces() expects at most 1 argument, 2 given +SimpleXMLElement::getDocNamespaces() expects at most 2 arguments, 3 given +SimpleXMLElement::asXML() expects at most 1 argument, 2 given +SimpleXMLElement::saveXML() expects at most 1 argument, 2 given +simplexml_load_file() expects at most 5 arguments, 6 given +SimpleXMLElement::__construct() expects at most 5 arguments, 6 given +SimpleXMLElement::__toString() expects exactly 0 arguments, 1 given +SimpleXMLElement::hasChildren() expects exactly 0 arguments, 1 given +SimpleXMLElement::getChildren() expects exactly 0 arguments, 1 given +ok=r,3,r diff --git a/test/repro/maintainer_gap_simplexml_excess_argc_30828.php b/test/repro/maintainer_gap_simplexml_excess_argc_30828.php new file mode 100644 index 00000000000..31aa8bf374e --- /dev/null +++ b/test/repro/maintainer_gap_simplexml_excess_argc_30828.php @@ -0,0 +1,96 @@ +getMessage(), "\n"; + } catch (Throwable $e) { + echo get_class($e), ':', $e->getMessage(), "\n"; + } +} + +$x = simplexml_load_string(''); +$d = new DOMDocument(); +$d->loadXML(''); + +msg(static function () use ($x) { + $x->children(null, false, 1); +}); +msg(static function () use ($x) { + $x->attributes(null, false, 1); +}); +msg(static function () use ($x) { + $x->xpath('//a', 1); +}); +msg(static function () use ($x) { + $x->registerXPathNamespace('a', 'urn:a', 1); +}); +msg(static function () use ($x) { + $x->addChild('a', 'b', null, 1); +}); +msg(static function () use ($x) { + $x->addAttribute('a', 'b', null, 1); +}); +msg(static function () use ($x) { + dom_import_simplexml($x, 1); +}); +msg(static function () use ($d) { + simplexml_import_dom($d, null, 1); +}); +msg(static function () { + simplexml_load_string('', 'SimpleXMLElement', 0, '', false, 1); +}); +msg(static function () use ($x) { + $x->getName(1); +}); +msg(static function () use ($x) { + $x->count(1); +}); +msg(static function () use ($x) { + $x->getNamespaces(false, 1); +}); +msg(static function () use ($x) { + $x->getDocNamespaces(false, true, 1); +}); +msg(static function () use ($x) { + $x->asXML('/tmp/sxe30828.xml', 1); +}); +msg(static function () use ($x) { + $x->saveXML('/tmp/sxe30828b.xml', 1); +}); +msg(static function () { + simplexml_load_file('/tmp/nope-30828.xml', 'SimpleXMLElement', 0, '', false, 1); +}); +msg(static function () { + new SimpleXMLElement('', 0, false, '', false, 1); +}); +msg(static function () use ($x) { + $x->__toString(1); +}); +msg(static function () use ($x) { + $x->hasChildren(1); +}); +msg(static function () use ($x) { + $x->getChildren(1); +}); + +// Legal arities still work. +$ok = simplexml_load_string(''); +$ok->children(); +$ok->children(null, false); +$ok->xpath('//a'); +$ok->registerXPathNamespace('p', 'urn:p'); +$ok->addChild('b'); +$ok->addChild('c', 'v', null); +$ok->addAttribute('k', 'v'); +$n = dom_import_simplexml($ok); +$back = simplexml_import_dom($n, null); +echo 'ok=', $ok->getName(), ',', $ok->count(), ',', $back->getName(), "\n";