使用PHP内置的宏来检查参数数量

NathanFreeman 2 weeks ago
parent bd6335ec3b
commit 79e9928999
  1. 18
      src/Generator/ClosureGenerator.php
  2. 34
      src/Generator/ParamCountCheckMarco.php
  3. 30
      src/Translator.php
  4. 59
      tests/compiler/functions/args-count.phpt

@ -150,19 +150,11 @@ trait ClosureGenerator
}
$requiredArgCount++;
}
if ($requiredArgCount > 0) {
$expected = $requiredArgCount === count($params) ? 'exactly' : 'at least';
$message = $this->genCharPtr(
'Too few arguments to function {closure}(), %u passed and ' . $expected . ' ' . $requiredArgCount . ' expected',
true
);
$code .= $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $requiredArgCount . ')) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_argument_count_error, 0, ' . $message . ', php::getCallArgNum());' . PHP_EOL;
$code .= $this->getIndent() . 'return php::null;' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
// Use PHP's built-in macros to check whether the number of parameters is correct.
$maxNumArgs = count($params);
$hasVariadic = $maxNumArgs > 0 ? $params[$maxNumArgs - 1]->variadic : false;
$code .= ParamCountCheckMarco::genParamCountMacroDefinition($maxNumArgs, $requiredArgCount, $hasVariadic);
foreach ($params as $i => $param) {
if ($param->byRef) {

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace TypePhp\Generator;
class ParamCountCheckMarco
{
/**
* ZEND_PARSE_PARAMETERS_NONE cannot be used here, because it hardcodes a return statement,
* which would result in a type mismatch.
*/
private const string NONE_PARAMS_TEMPLATE = <<<EOL
ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 0)
ZEND_PARSE_PARAMETERS_END_EX(php::throwErrorIfOccurred());
EOL;
/**
* @param int $maxNumArgs
* @param int $minNumArgs
* @param bool $hasVariadic
* @return string
*/
public static function genParamCountMacroDefinition(int $maxNumArgs, int $minNumArgs, bool $hasVariadic = false): string
{
if ($maxNumArgs == 0) {
return self::NONE_PARAMS_TEMPLATE;
}
return sprintf(<<<EOL
ZEND_PARSE_PARAMETERS_START(%d, %d)
ZEND_PARSE_PARAMETERS_END_EX(php::throwErrorIfOccurred());
EOL, $minNumArgs, ($hasVariadic ? -1 : $maxNumArgs));
}
}

@ -35,6 +35,7 @@ use TypePhp\Exception\Skip;
use TypePhp\Exception\SyntaxError;
use TypePhp\Generator\DefaultArgumentGenerator;
use TypePhp\Generator\LibraryImportStubGenerator;
use TypePhp\Generator\ParamCountCheckMarco;
use TypePhp\Generator\Symbol;
use TypePhp\Metadata\Constants;
use TypePhp\Platform\PlatformFactory;
@ -3366,11 +3367,13 @@ CODE;
// returns with EG(exception) set. Convert back to normal Zend exception
// propagation at the outermost wrapper.
$cppCode = 'try {' . PHP_EOL;
$callParams = '';
if ($functionDef->argCountRequired > 0) {
$cppCode .= $this->genWrapperRequiredArgCountCheck($functionDef, $displayName);
}
// Use PHP's built-in macros to check whether the number of parameters is correct.
$maxNumArgs = count($functionDef->argInfoList);
$hasVariadic = $maxNumArgs > 0 ? $functionDef->argInfoList[$maxNumArgs - 1]->variadic : false;
$cppCode .= ParamCountCheckMarco::genParamCountMacroDefinition($maxNumArgs, $functionDef->argCountRequired, $hasVariadic);
$callParams = '';
foreach ($functionDef->argInfoList as $k => $argInfo) {
$var = 'arg_' . $argInfo->name;
if ($argInfo->variadic) {
@ -3498,25 +3501,6 @@ CODE;
], true);
}
private function genWrapperRequiredArgCountCheck(FunctionDef $functionDef, string $displayName): string
{
$required = $functionDef->argCountRequired;
$expected = $required === count($functionDef->argInfoList) ? 'exactly' : 'at least';
$message = $this->genCharPtr(
'Too few arguments to function ' . $displayName . '(), %u passed and ' . $expected . ' ' . $required . ' expected',
true
);
$code = $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $required . ')) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_argument_count_error, 0, ' . $message . ', php::getCallArgNum());' . PHP_EOL;
$code .= $this->getIndent() . 'return;' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
return $code;
}
protected function genMethodWrapper(ClassDef $classDef, MethodDef $methodDef): string
{
$name = $classDef->getNamespacedName();

@ -0,0 +1,59 @@
--TEST--
args count check
--FILE--
<?php
declare(strict_types=1);
function hello(string $value) {}
function world(callable $callback) {
$callback('a');
}
function variadic(string $value, ...$results) {
var_dump($value, $results);
}
function main()
{
try {
$callback = 'hello';
$callback();
} catch (ArgumentCountError $e) {
var_dump($e->getMessage());
}
try {
world(function(string $value, string $value1) {
});
} catch (ArgumentCountError $e) {
var_dump($e->getMessage());
}
try {
$callable = "variadic";
$callable();
} catch (ArgumentCountError $e) {
var_dump($e->getMessage());
}
$callable('1', 1, 2, 3, 4, 5);
}
?>
--EXPECT--
string(43) "hello() expects exactly 1 argument, 0 given"
string(58) "stdClass::{closure}() expects exactly 2 arguments, 1 given"
string(47) "variadic() expects at least 1 argument, 0 given"
string(1) "1"
array(5) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
[3]=>
int(4)
[4]=>
int(5)
}
Loading…
Cancel
Save