Skip to content

Commit c5782d2

Browse files
committed
Store default parameter values of internal functions in arg info
1 parent 21cfa03 commit c5782d2

File tree

10 files changed

+279
-91
lines changed

10 files changed

+279
-91
lines changed

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ PHP 8.0 UPGRADE NOTES
319319
result will include a nullability indicator for nullable types. The format
320320
of the return value is not stable and may change between PHP versions.
321321
. Reflection export() methods have been removed.
322+
. The following methods can now return information about default values of
323+
parameters of internal functions:
324+
ReflectionParameter::isDefaultValueAvailable()
325+
ReflectionParameter::getDefaultValue()
326+
ReflectionParameter::isDefaultValueConstant()
327+
ReflectionParameter::getDefaultValueConstantName()
322328

323329
- Socket:
324330
. The deprecated AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES

Zend/zend_API.h

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,50 +105,61 @@ typedef struct _zend_fcall_info_cache {
105105
#define _ZEND_ARG_INFO_FLAGS(pass_by_ref, is_variadic) \
106106
(((pass_by_ref) << _ZEND_SEND_MODE_SHIFT) | ((is_variadic) ? _ZEND_IS_VARIADIC_BIT : 0))
107107

108+
/* Arginfo structures without type information */
108109
#define ZEND_ARG_INFO(pass_by_ref, name) \
109-
{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0))},
110-
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) \
111-
{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)) },
112-
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) \
113-
{ #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)) },
114-
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) \
115-
{ #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)) },
116-
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \
117-
{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)) },
118-
#define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask) \
119-
{ #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)) },
110+
{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
111+
#define ZEND_ARG_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, default_value) \
112+
{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
120113
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) \
121-
{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)) },
114+
{ #name, ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
115+
/* Arginfo structures with simple type information */
116+
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \
117+
{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
118+
#define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
119+
{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
122120
#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) \
123-
{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)) },
121+
{ #name, ZEND_TYPE_INIT_CODE(type_hint, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
122+
/* Arginfo structures with complex type information */
123+
#define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) \
124+
{ #name, ZEND_TYPE_INIT_MASK(type_mask | _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
125+
/* Arginfo structures with object type information */
126+
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) \
127+
{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
128+
#define ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, classname, allow_null, default_value) \
129+
{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), default_value },
124130
#define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) \
125-
{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)) },
131+
{ #name, ZEND_TYPE_INIT_CLASS_CONST(#classname, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 1)), NULL },
132+
/* Legacy arginfo structures */
133+
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) \
134+
{ #name, ZEND_TYPE_INIT_CODE(IS_ARRAY, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
135+
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) \
136+
{ #name, ZEND_TYPE_INIT_CODE(IS_CALLABLE, allow_null, _ZEND_ARG_INFO_FLAGS(pass_by_ref, 0)), NULL },
126137

127138
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
128139
static const zend_internal_arg_info name[] = { \
129140
{ (const char*)(zend_uintptr_t)(required_num_args), \
130-
ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
141+
ZEND_TYPE_INIT_CLASS_CONST(#class_name, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
131142

132143
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \
133144
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, 0, -1, class_name, allow_null)
134145

135146
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, required_num_args, type) \
136147
static const zend_internal_arg_info name[] = { \
137-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
148+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_MASK(type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
138149

139150
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(name, return_reference, required_num_args, class_name, type) \
140151
static const zend_internal_arg_info name[] = { \
141-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
152+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CLASS_CONST_MASK(#class_name, type | _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
142153

143154
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
144155
static const zend_internal_arg_info name[] = { \
145-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
156+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_CODE(type, allow_null, _ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
146157
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \
147158
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null)
148159

149160
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
150161
static const zend_internal_arg_info name[] = { \
151-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0)) },
162+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS(return_reference, 0)), NULL },
152163
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
153164
ZEND_BEGIN_ARG_INFO_EX(name, {}, ZEND_RETURN_VALUE, -1)
154165
#define ZEND_END_ARG_INFO() };

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,14 @@ typedef struct _zend_class_constant {
374374
typedef struct _zend_internal_arg_info {
375375
const char *name;
376376
zend_type type;
377+
const char *default_value;
377378
} zend_internal_arg_info;
378379

379380
/* arg_info for user functions */
380381
typedef struct _zend_arg_info {
381382
zend_string *name;
382383
zend_type type;
384+
zend_string *default_value;
383385
} zend_arg_info;
384386

385387
/* the following structure repeats the layout of zend_internal_arg_info,
@@ -390,6 +392,7 @@ typedef struct _zend_arg_info {
390392
typedef struct _zend_internal_function_info {
391393
zend_uintptr_t required_num_args;
392394
zend_type type;
395+
const char *default_value;
393396
} zend_internal_function_info;
394397

395398
struct _zend_op_array {

Zend/zend_exceptions_arginfo.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ ZEND_END_ARG_INFO()
1919

2020
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Exception___construct, 0, 0, 0)
2121
ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
22-
ZEND_ARG_TYPE_INFO(0, code, IS_LONG, 0)
23-
ZEND_ARG_OBJ_INFO(0, previous, Throwable, 1)
22+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, "0")
23+
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, previous, Throwable, 1, "null")
2424
ZEND_END_ARG_INFO()
2525

2626
#define arginfo_class_Exception___wakeup arginfo_class_Throwable_getMessage
@@ -44,11 +44,11 @@ ZEND_END_ARG_INFO()
4444

4545
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ErrorException___construct, 0, 0, 0)
4646
ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
47-
ZEND_ARG_TYPE_INFO(0, code, IS_LONG, 0)
48-
ZEND_ARG_TYPE_INFO(0, severity, IS_LONG, 0)
47+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, "0")
48+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, severity, IS_LONG, 0, "E_ERROR")
4949
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
50-
ZEND_ARG_TYPE_INFO(0, lineno, IS_LONG, 0)
51-
ZEND_ARG_OBJ_INFO(0, previous, Throwable, 1)
50+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, lineno, IS_LONG, 0, "0")
51+
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, previous, Throwable, 1, "null")
5252
ZEND_END_ARG_INFO()
5353

5454
#define arginfo_class_ErrorException_getSeverity arginfo_class_Throwable_getMessage

Zend/zend_inheritance.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,14 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
684684

685685
if (i >= required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
686686
smart_str_appends(&str, " = ");
687-
if (fptr->type == ZEND_USER_FUNCTION) {
687+
688+
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
689+
if (((zend_internal_arg_info*)arg_info)->default_value) {
690+
smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->default_value);
691+
} else {
692+
smart_str_appends(&str, "<default>");
693+
}
694+
} else {
688695
zend_op *precv = NULL;
689696
{
690697
uint32_t idx = i;
@@ -733,8 +740,6 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
733740
zend_tmp_string_release(tmp_zv_str);
734741
}
735742
}
736-
} else {
737-
smart_str_appends(&str, "NULL");
738743
}
739744
}
740745

build/gen_stub.php

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
use PhpParser\Comment\Doc as DocComment;
55
use PhpParser\Node;
66
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Name;
78
use PhpParser\Node\Stmt;
9+
use PhpParser\PrettyPrinter\Standard;
10+
use PhpParser\PrettyPrinterAbstract;
811

912
error_reporting(E_ALL);
1013

@@ -15,6 +18,13 @@
1518
exit(1);
1619
}
1720

21+
class CustomPrettyPrinter extends Standard
22+
{
23+
protected function pName_FullyQualified(Name\FullyQualified $node) {
24+
return implode('\\', $node->parts);
25+
}
26+
}
27+
1828
if ($argc >= 2) {
1929
if (is_file($argv[1])) {
2030
// Generate single file.
@@ -252,19 +262,23 @@ class ArgInfo {
252262
public $isVariadic;
253263
/** @var Type|null */
254264
public $type;
265+
/** @var string|null */
266+
public $defaultValue;
255267

256-
public function __construct(string $name, int $sendBy, bool $isVariadic, ?Type $type) {
268+
public function __construct(string $name, int $sendBy, bool $isVariadic, ?Type $type, ?string $defaultValue) {
257269
$this->name = $name;
258270
$this->sendBy = $sendBy;
259271
$this->isVariadic = $isVariadic;
260272
$this->type = $type;
273+
$this->defaultValue = $defaultValue;
261274
}
262275

263276
public function equals(ArgInfo $other): bool {
264277
return $this->name === $other->name
265278
&& $this->sendBy === $other->sendBy
266279
&& $this->isVariadic === $other->isVariadic
267-
&& Type::equals($this->type, $other->type);
280+
&& Type::equals($this->type, $other->type)
281+
&& $this->defaultValue === $other->defaultValue;
268282
}
269283

270284
public function getSendByString(): string {
@@ -278,6 +292,18 @@ public function getSendByString(): string {
278292
}
279293
throw new Exception("Invalid sendBy value");
280294
}
295+
296+
public function hasDefaultValue(): bool {
297+
return $this->defaultValue !== null && $this->defaultValue !== "UNKNOWN";
298+
}
299+
300+
public function getDefaultValueString(): string {
301+
if ($this->hasDefaultValue()) {
302+
return '"' . addslashes($this->defaultValue) . '"';
303+
}
304+
305+
return "NULL";
306+
}
281307
}
282308

283309
class ReturnInfo {
@@ -432,7 +458,7 @@ function parseDocComment(DocComment $comment): array {
432458
}
433459

434460
function parseFunctionLike(
435-
string $name, ?string $className, Node\FunctionLike $func, ?string $cond
461+
PrettyPrinterAbstract $prettyPinter, string $name, ?string $className, Node\FunctionLike $func, ?string $cond
436462
): FuncInfo {
437463
$comment = $func->getDocComment();
438464
$paramMeta = [];
@@ -494,7 +520,8 @@ function parseFunctionLike(
494520
$varName,
495521
$sendBy,
496522
$param->variadic,
497-
$type
523+
$type,
524+
$param->default ? $prettyPinter->prettyPrintExpr($param->default) : null
498525
);
499526
if (!$param->default && !$param->variadic) {
500527
$numRequiredArgs = $i + 1;
@@ -572,6 +599,7 @@ function parseStubFile(string $fileName): FileInfo {
572599
$parser = new PhpParser\Parser\Php7($lexer);
573600
$nodeTraverser = new PhpParser\NodeTraverser;
574601
$nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
602+
$prettyPrinter = new CustomPrettyPrinter();
575603

576604
$stmts = $parser->parse($code);
577605
$nodeTraverser->traverse($stmts);
@@ -594,7 +622,7 @@ function parseStubFile(string $fileName): FileInfo {
594622
}
595623

596624
if ($stmt instanceof Stmt\Function_) {
597-
$funcInfos[] = parseFunctionLike($stmt->name->toString(), null, $stmt, $cond);
625+
$funcInfos[] = parseFunctionLike($prettyPrinter, $stmt->name->toString(), null, $stmt, $cond);
598626
continue;
599627
}
600628

@@ -612,7 +640,7 @@ function parseStubFile(string $fileName): FileInfo {
612640
}
613641

614642
$methodInfos[] = parseFunctionLike(
615-
$classStmt->name->toString(), $className, $classStmt, $cond);
643+
$prettyPrinter, $classStmt->name->toString(), $className, $classStmt, $cond);
616644
}
617645

618646
$classInfos[] = new ClassInfo($className, $methodInfos);
@@ -673,37 +701,44 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
673701

674702
foreach ($funcInfo->args as $argInfo) {
675703
$argKind = $argInfo->isVariadic ? "ARG_VARIADIC" : "ARG";
704+
$argDefaultKind = $argInfo->hasDefaultValue() ? "_WITH_DEFAULT_VALUE" : "";
676705
$argType = $argInfo->type;
677706
if ($argType !== null) {
678707
if (null !== $simpleArgType = $argType->tryToSimpleType()) {
679708
if ($simpleArgType->isBuiltin) {
680709
$code .= sprintf(
681-
"\tZEND_%s_TYPE_INFO(%s, %s, %s, %d)\n",
682-
$argKind, $argInfo->getSendByString(), $argInfo->name,
683-
$simpleArgType->toTypeCode(), $argType->isNullable()
710+
"\tZEND_%s_TYPE_INFO%s(%s, %s, %s, %d%s)\n",
711+
$argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
712+
$simpleArgType->toTypeCode(), $argType->isNullable(),
713+
$argInfo->hasDefaultValue() ? ", " . $argInfo->getDefaultValueString() : ""
684714
);
685715
} else {
686716
$code .= sprintf(
687-
"\tZEND_%s_OBJ_INFO(%s, %s, %s, %d)\n",
688-
$argKind, $argInfo->getSendByString(), $argInfo->name,
689-
$simpleArgType->toEscapedName(), $argType->isNullable()
717+
"\tZEND_%s_OBJ_INFO%s(%s, %s, %s, %d%s)\n",
718+
$argKind,$argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
719+
$simpleArgType->toEscapedName(), $argType->isNullable(),
720+
$argInfo->hasDefaultValue() ? ", " . $argInfo->getDefaultValueString() : ""
690721
);
691722
}
692723
} else if (null !== $representableType = $argType->tryToRepresentableType()) {
693724
if ($representableType->classType !== null) {
694725
throw new Exception('Unimplemented');
695726
}
696727
$code .= sprintf(
697-
"\tZEND_%s_TYPE_MASK(%s, %s, %s)\n",
728+
"\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n",
698729
$argKind, $argInfo->getSendByString(), $argInfo->name,
699-
$representableType->toTypeMask()
730+
$representableType->toTypeMask(),
731+
$argInfo->getDefaultValueString()
700732
);
701733
} else {
702734
throw new Exception('Unimplemented');
703735
}
704736
} else {
705737
$code .= sprintf(
706-
"\tZEND_%s_INFO(%s, %s)\n", $argKind, $argInfo->getSendByString(), $argInfo->name);
738+
"\tZEND_%s_INFO%s(%s, %s%s)\n",
739+
$argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
740+
$argInfo->hasDefaultValue() ? ", " . $argInfo->getDefaultValueString() : ""
741+
);
707742
}
708743
}
709744

ext/reflection/Makefile.frag

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
$(top_srcdir)/Zend/zend_language_parser.c:
2+
$(top_srcdir)/Zend/zend_language_scanner.c:
3+
$(top_srcdir)/ext/reflection/php_reflection.c: $(top_srcdir)/Zend/zend_language_parser.h
4+
$(builddir)/reflection.lo: $(top_srcdir)/Zend/zend_language_parser.c $(top_srcdir)/Zend/zend_language_scanner.c

ext/reflection/config.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
PHP_NEW_EXTENSION(reflection, php_reflection.c, no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
2+
PHP_ADD_MAKEFILE_FRAGMENT

0 commit comments

Comments
 (0)