Skip to content

Commit a9f4970

Browse files
committed
Add warning for arguments in stubs that doesn't have any type info
1 parent ca35800 commit a9f4970

11 files changed

+88
-42
lines changed

Zend/tests/010.phpt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,16 @@ var_dump(get_parent_class(""));
3636
var_dump(get_parent_class("[[[["));
3737
var_dump(get_parent_class(" "));
3838
var_dump(get_parent_class(new stdclass));
39-
var_dump(get_parent_class(array()));
40-
var_dump(get_parent_class(1));
39+
try {
40+
get_parent_class([]);
41+
} catch (TypeError $exception) {
42+
echo $exception->getMessage() . "\n";
43+
}
44+
try {
45+
get_parent_class(1);
46+
} catch (TypeError $exception) {
47+
echo $exception->getMessage() . "\n";
48+
}
4149

4250
echo "Done\n";
4351
?>
@@ -54,6 +62,6 @@ bool(false)
5462
bool(false)
5563
bool(false)
5664
bool(false)
57-
bool(false)
58-
bool(false)
65+
get_parent_class(): Argument #1 ($object) must be of type object|string|null, array given
66+
get_parent_class(): Argument #1 ($object) must be of type object|string|null, int given
5967
Done

Zend/tests/bug72162.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ $var11 = new StdClass();
77
$var16 = error_reporting($var11);
88
?>
99
--EXPECTF--
10-
Fatal error: Uncaught Error: Object of class stdClass could not be converted to string in %s:%d
10+
Fatal error: Uncaught TypeError: error_reporting(): Argument #1 ($new_error_level) must be of type ?int, object given in %s:%d
1111
Stack trace:
1212
#0 %s(%d): error_reporting(Object(stdClass))
1313
#1 {main}

Zend/zend_builtin_functions.c

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -383,21 +383,17 @@ ZEND_FUNCTION(strncasecmp)
383383
Return the current error_reporting level, and if an argument was passed - change to the new level */
384384
ZEND_FUNCTION(error_reporting)
385385
{
386-
zval *err = NULL;
387386
int old_error_reporting;
387+
zend_long err;
388+
zend_bool err_is_null = 1;
388389

389390
ZEND_PARSE_PARAMETERS_START(0, 1)
390391
Z_PARAM_OPTIONAL
391-
Z_PARAM_ZVAL(err)
392+
Z_PARAM_LONG_OR_NULL(err, err_is_null)
392393
ZEND_PARSE_PARAMETERS_END();
393394

394395
old_error_reporting = EG(error_reporting);
395-
if (ZEND_NUM_ARGS() != 0) {
396-
zend_string *new_val = zval_try_get_string(err);
397-
if (UNEXPECTED(!new_val)) {
398-
RETURN_THROWS();
399-
}
400-
396+
if (!err_is_null) {
401397
do {
402398
zend_ini_entry *p = EG(error_reporting_ini_entry);
403399

@@ -423,12 +419,8 @@ ZEND_FUNCTION(error_reporting)
423419
zend_string_release_ex(p->value, 0);
424420
}
425421

426-
p->value = new_val;
427-
if (Z_TYPE_P(err) == IS_LONG) {
428-
EG(error_reporting) = Z_LVAL_P(err);
429-
} else {
430-
EG(error_reporting) = atoi(ZSTR_VAL(p->value));
431-
}
422+
p->value = zend_long_to_str(err);
423+
EG(error_reporting) = err;
432424
} while (0);
433425
}
434426

@@ -635,14 +627,14 @@ ZEND_FUNCTION(get_called_class)
635627
Retrieves the parent class name for object or class or current scope or false if not in a scope. */
636628
ZEND_FUNCTION(get_parent_class)
637629
{
638-
zval *arg;
630+
zval *arg = NULL;
639631
zend_class_entry *ce = NULL;
640632

641-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg) == FAILURE) {
633+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &arg) == FAILURE) {
642634
RETURN_THROWS();
643635
}
644636

645-
if (!ZEND_NUM_ARGS()) {
637+
if (!arg) {
646638
ce = zend_get_executed_scope();
647639
if (ce && ce->parent) {
648640
RETURN_STR_COPY(ce->parent->name);
@@ -655,6 +647,8 @@ ZEND_FUNCTION(get_parent_class)
655647
ce = Z_OBJ_P(arg)->ce;
656648
} else if (Z_TYPE_P(arg) == IS_STRING) {
657649
ce = zend_lookup_class(Z_STR_P(arg));
650+
} else {
651+
zend_argument_type_error(1, "must be of type object|string|null, %s given", zend_zval_type_name(arg));
658652
}
659653

660654
if (ce && ce->parent) {
@@ -695,7 +689,7 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) /*
695689
} else if (Z_TYPE_P(obj) == IS_OBJECT) {
696690
instance_ce = Z_OBJCE_P(obj);
697691
} else {
698-
RETURN_FALSE;
692+
zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_type_name(obj));
699693
}
700694

701695
if (!only_subclass && EXPECTED(zend_string_equals(instance_ce->name, class_name))) {
@@ -940,6 +934,8 @@ ZEND_FUNCTION(get_class_methods)
940934
ce = Z_OBJCE_P(klass);
941935
} else if (Z_TYPE_P(klass) == IS_STRING) {
942936
ce = zend_lookup_class(Z_STR_P(klass));
937+
} else {
938+
zend_argument_type_error(1, "must be of type object|string. %s given", zend_zval_type_name(klass));
943939
}
944940

945941
if (!ce) {

Zend/zend_builtin_functions.stub.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ function strcasecmp(string $str1, string $str2): int {}
2020

2121
function strncasecmp(string $str1, string $str2, int $len): int {}
2222

23-
function error_reporting($new_error_level = UNKNOWN): int {}
23+
function error_reporting(?int $new_error_level = null): int {}
2424

25+
/** @param bool|int|float|string|array|resource|object|null $value */
2526
function define(string $constant_name, $value, bool $case_insensitive = false): bool {}
2627

2728
function defined(string $constant_name): bool {}
@@ -30,10 +31,13 @@ function get_class(object $object = UNKNOWN): string {}
3031

3132
function get_called_class(): string {}
3233

33-
function get_parent_class($object = UNKNOWN): string|false {}
34+
/** @param object|string|null $object */
35+
function get_parent_class($object = null): string|false {}
3436

37+
/** @param object|string $object */
3538
function is_subclass_of($object, string $class_name, bool $allow_string = true): bool {}
3639

40+
/** @param object|string $object */
3741
function is_a($object, string $class_name, bool $allow_string = false): bool {}
3842

3943
function get_class_vars(string $class_name): array|false {}
@@ -42,6 +46,7 @@ function get_object_vars(object $obj): array {}
4246

4347
function get_mangled_object_vars(object $obj): array {}
4448

49+
/** @param object|string $class */
4550
function get_class_methods($class): ?array {}
4651

4752
function method_exists($object_or_class, string $method): bool {}

Zend/zend_builtin_functions_arginfo.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ZEND_END_ARG_INFO()
3333
#define arginfo_strncasecmp arginfo_strncmp
3434

3535
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_error_reporting, 0, 0, IS_LONG, 0)
36-
ZEND_ARG_INFO(0, new_error_level)
36+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, new_error_level, IS_LONG, 1, "null")
3737
ZEND_END_ARG_INFO()
3838

3939
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_define, 0, 2, _IS_BOOL, 0)
@@ -53,7 +53,7 @@ ZEND_END_ARG_INFO()
5353
#define arginfo_get_called_class arginfo_zend_version
5454

5555
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_get_parent_class, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)
56-
ZEND_ARG_INFO(0, object)
56+
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, object, "null")
5757
ZEND_END_ARG_INFO()
5858

5959
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_is_subclass_of, 0, 2, _IS_BOOL, 0)

Zend/zend_interfaces.stub.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,30 @@ public function rewind();
3030

3131
interface ArrayAccess
3232
{
33-
/** @return bool */
33+
/**
34+
* @param mixed $offset
35+
* @return bool
36+
*/
3437
public function offsetExists($offset);
3538

36-
/* actually this should be return by ref but atm cannot be */
37-
/** @return mixed */
39+
/**
40+
* Actually this should be return by ref but atm cannot be.
41+
* @param mixed $offset
42+
* @return mixed
43+
*/
3844
public function offsetGet($offset);
3945

40-
/** @return void */
46+
/**
47+
* @param mixed $offset
48+
* @param mixed $value
49+
* @return void
50+
*/
4151
public function offsetSet($offset, $value);
4252

43-
/** @return void */
53+
/**
54+
* @param mixed $offset
55+
* @return void
56+
*/
4457
public function offsetUnset($offset);
4558
}
4659

build/gen_stub.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,15 @@ public function getDeclaration(): string
321321
return "ZEND_FUNCTION($this->name);\n";
322322
}
323323

324-
public function __toString()
324+
public function getName(): string
325325
{
326326
return $this->className ? "$this->className::$this->name" : $this->name;
327327
}
328+
329+
public function __toString()
330+
{
331+
return $this->getName();
332+
}
328333
}
329334

330335
class ReturnInfo {
@@ -561,8 +566,19 @@ public function getValue(): string {
561566

562567
public function getVariableName(): string {
563568
$value = $this->getValue();
564-
if ($value === null || strlen($value) === 0 || $value[0] !== '$') {
565-
throw new Exception("@$this->name not followed by variable name");
569+
if ($value === null || strlen($value) === 0) {
570+
throw new Exception("@$this->name doesn't have any value");
571+
}
572+
573+
if ($this->name === "param") {
574+
$pos = strpos($value, " ");
575+
if ($pos !== false) {
576+
$value = substr($value, $pos + 1);
577+
}
578+
}
579+
580+
if ($value[0] !== '$') {
581+
throw new Exception("@$this->name doesn't contain variable name");
566582
}
567583

568584
return substr($value, 1);
@@ -595,6 +611,7 @@ function parseFunctionLike(
595611
$alias = null;
596612
$isDeprecated = false;
597613
$haveDocReturnType = false;
614+
$docParamTypes = [];
598615

599616
if ($comment) {
600617
$tags = parseDocComment($comment);
@@ -616,13 +633,16 @@ function parseFunctionLike(
616633
$isDeprecated = true;
617634
} else if ($tag->name === 'return') {
618635
$haveDocReturnType = true;
636+
} else if ($tag->name === 'param') {
637+
$docParamTypes[$tag->getVariableName()] = true;
619638
}
620639
}
621640
}
622641

623642
$args = [];
624643
$numRequiredArgs = 0;
625644
$foundVariadic = false;
645+
static $warnings = [];
626646
foreach ($func->getParams() as $i => $param) {
627647
$varName = $param->var->name;
628648
$preferRef = !empty($paramMeta[$varName]['preferRef']);
@@ -641,6 +661,12 @@ function parseFunctionLike(
641661
}
642662

643663
$type = $param->type ? Type::fromNode($param->type) : null;
664+
if ($type === null && !isset($docParamTypes[$varName]) && !isset($warnings[$name->getName()])) {
665+
$warnings[$name->getName()] = true;
666+
//throw new Exception("Missing argument type for function $name()");
667+
echo "Warning: Missing argument type for function $name()\n";
668+
}
669+
644670
if ($param->default instanceof Expr\ConstFetch &&
645671
$param->default->name->toLowerString() === "null" &&
646672
$type && !$type->isNullable()

ext/hash/hash.stub.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ function hash_hmac_algos(): array {}
3131
function hash_pbkdf2(string $algo, string $password, string $salt, int $iterations, int $length = 0, bool $raw_output = false): string {}
3232

3333
/**
34-
* @param $known_string no type juggling is performed
35-
* @param $user_string no type juggling is performed
34+
* @param string $known_string no type juggling is performed
35+
* @param string $user_string no type juggling is performed
3636
*/
3737
function hash_equals(string $known_string, string $user_string): bool {}
3838

ext/intl/php_intl.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ function transliterator_list_ids(): array|false {}
414414

415415
function transliterator_create_inverse(Transliterator $orig_trans): ?Transliterator {}
416416

417-
/** @param Transliterator|string */
417+
/** @param Transliterator|string $transliterator */
418418
function transliterator_transliterate($transliterator, string $subject, int $start = 0, int $end = -1): string|false {}
419419

420420
function transliterator_get_error_code(Transliterator $trans): int|false {}

ext/mysqli/mysqli.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ function mysqli_stmt_attr_set(mysqli_stmt $mysql_stmt, int $attr, int $mode_in):
683683

684684
function mysqli_stmt_bind_param(mysqli_stmt $mysql_stmt, string $types, mixed &...$vars): bool {}
685685

686-
/** @param mixed &...$vars */
686+
/** @param mixed $vars */
687687
function mysqli_stmt_bind_result(mysqli_stmt $mysql_stmt, &...$vars): bool {}
688688

689689
function mysqli_stmt_close(mysqli_stmt $mysql_stmt): bool {}

ext/reflection/php_reflection.stub.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,7 @@ class ReflectionParameter implements Reflector
471471
/** @alias ReflectionClass::__clone */
472472
final private function __clone() {}
473473

474-
/**
475-
* @param $function string|array|object
476-
*/
474+
/** @param string|array|object $function */
477475
public function __construct($function, int|string $parameter) {}
478476

479477
public function __toString(): string {}

0 commit comments

Comments
 (0)