Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions ext/xmlwriter/VmXmlWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,10 @@ public static function startElement(ObjectEntry $entry, string $name): bool
{
$state = self::requireOpen($entry, 'XMLWriter::startElement()');
if (!self::isValidElementName($name)) {
// php-src php_xmlwriter.c — Zend cites Argument #2 without param name (#31610).
throw new \ValueError(sprintf(
'XMLWriter::startElement(): Argument #1 ($name) must be a valid element name, %s given',
var_export($name, true)
'XMLWriter::startElement(): Argument #2 must be a valid element name, %s given',
self::zendQuotedName($name)
));
}
self::closeStartTagIfOpen($state);
Expand All @@ -371,9 +372,10 @@ public static function writeAttribute(ObjectEntry $entry, string $name, string $
return false;
}
if (!self::isValidAttributeName($name)) {
// php-src — Zend cites Argument #2 ($value) for the name check (#31610).
throw new \ValueError(sprintf(
'XMLWriter::writeAttribute(): Argument #1 ($name) must be a valid attribute name, %s given',
var_export($name, true)
'XMLWriter::writeAttribute(): Argument #2 ($value) must be a valid attribute name, %s given',
self::zendQuotedName($name)
));
}
self::endOpenAttributeIfNeeded($state);
Expand Down Expand Up @@ -494,9 +496,10 @@ public static function startAttribute(ObjectEntry $entry, string $name): bool
return false;
}
if (!self::isValidAttributeName($name)) {
// php-src — Zend cites Argument #2 without param name (#31610).
throw new \ValueError(sprintf(
'XMLWriter::startAttribute(): Argument #1 ($name) must be a valid attribute name, %s given',
var_export($name, true)
'XMLWriter::startAttribute(): Argument #2 must be a valid attribute name, %s given',
self::zendQuotedName($name)
));
}
self::endOpenAttributeIfNeeded($state);
Expand Down Expand Up @@ -524,6 +527,13 @@ public static function endAttribute(ObjectEntry $entry): bool

public static function writeElement(ObjectEntry $entry, string $name, ?string $content = null): bool
{
// Validate under writeElement's Zend message before delegating to startElement (#31610).
if (!self::isValidElementName($name)) {
throw new \ValueError(sprintf(
'XMLWriter::writeElement(): Argument #2 ($content) must be a valid element name, %s given',
self::zendQuotedName($name)
));
}
if (!self::startElement($entry, $name)) {
return false;
}
Expand Down Expand Up @@ -1418,6 +1428,12 @@ private static function isValidElementName(string $name): bool
return true;
}

/** php-src ValueError uses double quotes around the given name (unescaped; #31610). */
private static function zendQuotedName(string $name): string
{
return '"'.$name.'"';
}

private static function isValidAttributeName(string $name): bool
{
return self::isValidElementName($name);
Expand Down
46 changes: 37 additions & 9 deletions ext/xmlwriter/XmlWriterClassMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

namespace PHPCompiler\ext\xmlwriter;

use PHPCompiler\ext\standard\VmNullStringParamDeprecation;
use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\InternalStrictArg;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;

Expand Down Expand Up @@ -110,37 +112,63 @@ public static function atLeastUserArgCountMessage(string $function, int $minimum
);
}

protected function stringArg(Variable $var, string $label, int $index, string $paramName = 'value'): string
{
/**
* Z_PARAM_STR — soft-null DEP+coerce under php-src-strict (php-src php_xmlwriter.c; #31610).
*
* $label may include trailing "()" (legacy call sites); deprecation uses the bare Class::method.
*/
protected function stringArg(
Variable $var,
string $label,
int $index,
Frame $frame,
string $paramName = 'value'
): string {
$function = str_ends_with($label, '()') ? substr($label, 0, -2) : $label;
$display = str_ends_with($label, '()') ? $label : $label.'()';
if (InternalStrictArg::isCallerStrict($frame)) {
InternalStrictArg::rejectNullString($var, $function, $paramName, $index, $frame);
}
$var = $var->resolveIndirect();
if (Variable::TYPE_OBJECT === $var->type) {
throw new \TypeError(sprintf(
'%s(): Argument #%d ($%s) must be of type string, %s given',
$label,
'%s: Argument #%d ($%s) must be of type string, %s given',
$display,
$index + 1,
$paramName,
$var->toObject()->class->name
));
}
if (Variable::TYPE_ARRAY === $var->type) {
throw new \TypeError(sprintf(
'%s(): Argument #%d ($%s) must be of type string, array given',
$label,
'%s: Argument #%d ($%s) must be of type string, array given',
$display,
$index + 1,
$paramName
));
}
if (Variable::TYPE_NULL === $var->type) {
// Z_PARAM_STR weak: E_DEPRECATED then coerce to '' (#31610).
VmNullStringParamDeprecation::emit($frame, $function, $index, $paramName);

return '';
}

return $var->toString();
}

protected function nullableStringArg(Variable $var, string $label, int $index, string $paramName = 'value'): ?string
{
protected function nullableStringArg(
Variable $var,
string $label,
int $index,
Frame $frame,
string $paramName = 'value'
): ?string {
$var = $var->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}

return $this->stringArg($var, $label, $index, $paramName);
return $this->stringArg($var, $label, $index, $frame, $paramName);
}
}
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterOpenURI.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::openUri()');
$this->requireExactUserArgCount($frame, 'XMLWriter::openUri', 1);
$uri = $this->stringArg($frame->calledArgs[1], 'XMLWriter::openUri()', 0, 'uri');
$uri = $this->stringArg($frame->calledArgs[1], 'XMLWriter::openUri()', 0, $frame, 'uri');
$ok = VmXmlWriter::openURI($entry, $uri);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
17 changes: 12 additions & 5 deletions ext/xmlwriter/XmlWriterProceduralFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PHPCompiler\ext\xmlwriter;

use PHPCompiler\ext\standard\VmNullStringParamDeprecation;
use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
Expand Down Expand Up @@ -53,10 +54,11 @@ protected function writerArg(Frame $frame, string $function): ObjectEntry

/**
* Procedural string arg — 1-based index includes $writer as argument #1.
* Z_PARAM_STR soft-null DEP (#31610).
*/
protected function stringArgAt(Variable $var, string $function, int $argNum, string $paramName): string
protected function stringArgAt(Frame $frame, int $frameArgIndex, string $function, int $argNum, string $paramName): string
{
$var = $var->resolveIndirect();
$var = $frame->calledArgs[$frameArgIndex]->resolveIndirect();
if (Variable::TYPE_OBJECT === $var->type) {
throw new \TypeError(sprintf(
'%s(): Argument #%d ($%s) must be of type string, %s given',
Expand All @@ -74,18 +76,23 @@ protected function stringArgAt(Variable $var, string $function, int $argNum, str
$paramName
));
}
if (Variable::TYPE_NULL === $var->type) {
VmNullStringParamDeprecation::emit($frame, $function, $argNum - 1, $paramName);

return '';
}

return $var->toString();
}

protected function nullableStringArgAt(Variable $var, string $function, int $argNum, string $paramName): ?string
protected function nullableStringArgAt(Frame $frame, int $frameArgIndex, string $function, int $argNum, string $paramName): ?string
{
$var = $var->resolveIndirect();
$var = $frame->calledArgs[$frameArgIndex]->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}

return $this->stringArgAt($var, $function, $argNum, $paramName);
return $this->stringArgAt($frame, $frameArgIndex, $function, $argNum, $paramName);
}

protected function newWriter(Frame $frame): ObjectEntry
Expand Down
5 changes: 1 addition & 4 deletions ext/xmlwriter/XmlWriterSetIndentString.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public function execute(Frame $frame): void
$entry = $this->receiver($frame, 'XMLWriter::setIndentString()');
$this->requireExactUserArgCount($frame, 'XMLWriter::setIndentString', 1);
$indentation = $this->stringArg(
$frame->calledArgs[1],
'XMLWriter::setIndentString()',
0,
'indentation'
$frame->calledArgs[1], 'XMLWriter::setIndentString()', 0, $frame, 'indentation'
);
$ok = VmXmlWriter::setIndentString($entry, $indentation);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startAttribute()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startAttribute', 1);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startAttribute()', 0, 'name');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startAttribute()', 0, $frame, 'name');
$ok = VmXmlWriter::startAttribute($entry, $name);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
6 changes: 3 additions & 3 deletions ext/xmlwriter/XmlWriterStartAttributeNS.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startAttributeNs()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startAttributeNs', 3);
$prefix = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startAttributeNs()', 0, 'prefix');
$name = $this->stringArg($frame->calledArgs[2], 'XMLWriter::startAttributeNs()', 1, 'name');
$uri = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startAttributeNs()', 2, 'uri');
$prefix = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startAttributeNs()', 0, $frame, 'prefix');
$name = $this->stringArg($frame->calledArgs[2], 'XMLWriter::startAttributeNs()', 1, $frame, 'name');
$uri = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startAttributeNs()', 2, $frame, 'uri');
$ok = VmXmlWriter::startAttributeNS($entry, $prefix, $name, $uri);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
4 changes: 2 additions & 2 deletions ext/xmlwriter/XmlWriterStartDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ public function execute(Frame $frame): void
$this->requireAtMostUserArgCount($frame, 'XMLWriter::startDocument', 3);
$version = '1.0';
if (isset($frame->calledArgs[1])) {
$version = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startDocument()', 0, 'version');
$version = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startDocument()', 0, $frame, 'version');
}
$encoding = null;
if (isset($frame->calledArgs[2])) {
$encoding = $this->nullableStringArg($frame->calledArgs[2], 'XMLWriter::startDocument()', 1, 'encoding');
$encoding = $this->nullableStringArg($frame->calledArgs[2], 'XMLWriter::startDocument()', 1, $frame, 'encoding');
}
$ok = VmXmlWriter::startDocument($entry, $version, $encoding);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
Expand Down
6 changes: 3 additions & 3 deletions ext/xmlwriter/XmlWriterStartDtd.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public function execute(Frame $frame): void
$entry = $this->receiver($frame, 'XMLWriter::startDtd()');
$this->requireUserArgCountRange($frame, 'XMLWriter::startDtd', 1, 3);
$argc = \count($frame->calledArgs);
$qualifiedName = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtd()', 0, 'qualifiedName');
$qualifiedName = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtd()', 0, $frame, 'qualifiedName');
$publicId = null;
$systemId = null;
if ($argc >= 3) {
$publicId = $this->nullableStringArg($frame->calledArgs[2], 'XMLWriter::startDtd()', 1, 'publicId');
$publicId = $this->nullableStringArg($frame->calledArgs[2], 'XMLWriter::startDtd()', 1, $frame, 'publicId');
}
if ($argc >= 4) {
$systemId = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startDtd()', 2, 'systemId');
$systemId = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startDtd()', 2, $frame, 'systemId');
}
$ok = VmXmlWriter::startDtd($entry, $qualifiedName, $publicId, $systemId);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartDtdAttlist.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startDtdAttlist()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startDtdAttlist', 1);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdAttlist()', 0, 'name');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdAttlist()', 0, $frame, 'name');
$ok = VmXmlWriter::startDtdAttlist($entry, $name);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartDtdElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startDtdElement()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startDtdElement', 1);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdElement()', 0, 'name');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdElement()', 0, $frame, 'name');
$ok = VmXmlWriter::startDtdElement($entry, $name);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartDtdEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startDtdEntity()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startDtdEntity', 2);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdEntity()', 0, 'name');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startDtdEntity()', 0, $frame, 'name');
$isParam = $frame->calledArgs[2]->resolveIndirect()->toBool();
$ok = VmXmlWriter::startDtdEntity($entry, $name, $isParam);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startElement()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startElement', 1);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startElement()', 0, 'name');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startElement()', 0, $frame, 'name');
$ok = VmXmlWriter::startElement($entry, $name);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
6 changes: 3 additions & 3 deletions ext/xmlwriter/XmlWriterStartElementNS.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startElementNs()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startElementNs', 3);
$prefix = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startElementNs()', 0, 'prefix');
$name = $this->stringArg($frame->calledArgs[2], 'XMLWriter::startElementNs()', 1, 'name');
$uri = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startElementNs()', 2, 'uri');
$prefix = $this->nullableStringArg($frame->calledArgs[1], 'XMLWriter::startElementNs()', 0, $frame, 'prefix');
$name = $this->stringArg($frame->calledArgs[2], 'XMLWriter::startElementNs()', 1, $frame, 'name');
$uri = $this->nullableStringArg($frame->calledArgs[3], 'XMLWriter::startElementNs()', 2, $frame, 'uri');
$ok = VmXmlWriter::startElementNS($entry, $prefix, $name, $uri);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
2 changes: 1 addition & 1 deletion ext/xmlwriter/XmlWriterStartPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::startPi()');
$this->requireExactUserArgCount($frame, 'XMLWriter::startPi', 1);
$target = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startPi()', 0, 'target');
$target = $this->stringArg($frame->calledArgs[1], 'XMLWriter::startPi()', 0, $frame, 'target');
$ok = VmXmlWriter::startPI($entry, $target);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
14 changes: 2 additions & 12 deletions ext/xmlwriter/XmlWriterText.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use PHPCompiler\VM\BuiltinExecute;
use PHPCompiler\VM\Variable;

/** XMLWriter::text() — text node content (php-src ext/xmlwriter/php_xmlwriter.c; #30818; #6065). */
/** XMLWriter::text() — text node content (php-src ext/xmlwriter/php_xmlwriter.c; #30818; #6065 / #31610). */
final class XmlWriterText extends XmlWriterClassMethod
{
public function __construct()
Expand All @@ -20,17 +20,7 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::text()');
$this->requireExactUserArgCount($frame, 'XMLWriter::text', 1);
$var = $frame->calledArgs[1]->resolveIndirect();
if (Variable::TYPE_OBJECT === $var->type) {
throw new \TypeError(sprintf(
'XMLWriter::text(): Argument #1 ($content) must be of type string, %s given',
$var->toObject()->class->name
));
}
if (Variable::TYPE_ARRAY === $var->type) {
throw new \TypeError('XMLWriter::text(): Argument #1 ($content) must be of type string, array given');
}
$content = $var->toString();
$content = $this->stringArg($frame->calledArgs[1], 'XMLWriter::text()', 0, $frame, 'content');
$ok = VmXmlWriter::text($entry, $content);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
4 changes: 2 additions & 2 deletions ext/xmlwriter/XmlWriterWriteAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public function execute(Frame $frame): void
{
$entry = $this->receiver($frame, 'XMLWriter::writeAttribute()');
$this->requireExactUserArgCount($frame, 'XMLWriter::writeAttribute', 2);
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::writeAttribute()', 0, 'name');
$value = $this->stringArg($frame->calledArgs[2], 'XMLWriter::writeAttribute()', 1, 'value');
$name = $this->stringArg($frame->calledArgs[1], 'XMLWriter::writeAttribute()', 0, $frame, 'name');
$value = $this->stringArg($frame->calledArgs[2], 'XMLWriter::writeAttribute()', 1, $frame, 'value');
$ok = VmXmlWriter::writeAttribute($entry, $name, $value);
BuiltinExecute::writeReturn($frame, static function (Variable $ret) use ($ok): void {
$ret->bool($ok);
Expand Down
Loading
Loading