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
16 changes: 16 additions & 0 deletions ext/dom/BuiltinClasses.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\VM\Context;

/** Register dom extension builtin classes (php-src ext/dom/php_dom.c; issue #6140). */
final class BuiltinClasses
{
public static function register(Context $ctx): void
{
VmDom::registerClasses($ctx);
}
}
24 changes: 24 additions & 0 deletions ext/dom/DocumentSaveXML.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\Frame;

/** DOMDocument::saveXML() — VM (#6140). */
final class DocumentSaveXML extends DomClassMethod
{
public function __construct()
{
parent::__construct('saveXML');
}

public function execute(Frame $frame): void
{
$receiver = $this->receiver($frame, VmDom::CLASS_DOCUMENT, 'DOMDocument::saveXML()');
if (null !== $frame->returnVar) {
$frame->returnVar->string(VmDom::saveXML($receiver));
}
}
}
59 changes: 59 additions & 0 deletions ext/dom/DomClassMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\Frame;
use PHPCompiler\VM\Builtin\VmClassMethod;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;

/** Shared VM wiring for ext/dom class methods (issue #6140). */
abstract class DomClassMethod extends VmClassMethod
{
protected function receiver(Frame $frame, string $classLc, string $label): ObjectEntry
{
if (\count($frame->calledArgs) < 1) {
throw new \LogicException($label.' called without $this');
}

return VmDom::requireReceiver($frame->calledArgs[0], $classLc, $label);
}

protected function stringArg(Variable $var, string $label, int $index): string
{
$var = $var->resolveIndirect();
if (Variable::TYPE_STRING !== $var->type && Variable::TYPE_NULL !== $var->type) {
throw new \TypeError(sprintf(
'%s expects argument #%d to be of type string, %s given',
$label,
$index + 1,
VmDom::typeLabel($var)
));
}
if (Variable::TYPE_NULL === $var->type) {
return '';
}

return $var->toString();
}

protected function nullableStringArg(Variable $var, string $label, int $index): ?string
{
$var = $var->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}
if (Variable::TYPE_STRING !== $var->type) {
throw new \TypeError(sprintf(
'%s expects argument #%d to be of type ?string, %s given',
$label,
$index + 1,
VmDom::typeLabel($var)
));
}

return $var->toString();
}
}
15 changes: 15 additions & 0 deletions ext/dom/DomConstants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

/** DOM node type constants (php-src ext/dom/dom_ce.h; issue #6140). */
final class DomConstants
{
public const XML_ELEMENT_NODE = 1;

public const XML_DOCUMENT_NODE = 9;

public const XML_DOCUMENT_TYPE_NODE = 10;
}
31 changes: 31 additions & 0 deletions ext/dom/DomNodeState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

/**
* Internal DOM node state keyed by VM object id (php-src ext/dom/php_dom.c; issue #6140).
*/
final class DomNodeState
{
public int $nodeType;

public string $nodeName;

public ?string $namespaceUri = null;

public ?string $publicId = null;

public ?string $systemId = null;

/** Root element local name for documents. */
public ?string $documentElementName = null;

/** Doctype fields copied into documents at createDocument() time. */
public ?string $doctypeName = null;

public ?string $doctypePublicId = null;

public ?string $doctypeSystemId = null;
}
39 changes: 39 additions & 0 deletions ext/dom/DomRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\VM\ObjectEntry;

/** Object-id keyed DOM node state (issue #6140). */
final class DomRegistry
{
/** @var array<int, DomNodeState> */
private static array $states = [];

public static function reset(): void
{
self::$states = [];
}

public static function attach(ObjectEntry $entry, DomNodeState $state): void
{
self::$states[$entry->id] = $state;
}

public static function state(ObjectEntry $entry): DomNodeState
{
$state = self::$states[$entry->id] ?? null;
if (null === $state) {
throw new \LogicException('DOM object has no registered node state in this compiler build');
}

return $state;
}

public static function has(ObjectEntry $entry): bool
{
return isset(self::$states[$entry->id]);
}
}
60 changes: 60 additions & 0 deletions ext/dom/ImplementationCreateDocument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\Frame;
use PHPCompiler\VM\Variable;

/** DOMImplementation::createDocument() — VM (#6140). */
final class ImplementationCreateDocument extends DomClassMethod
{
public function __construct()
{
parent::__construct('createDocument');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 4) {
throw new \LogicException('DOMImplementation::createDocument() expects exactly 3 arguments');
}
if (null === $frame->vmContext) {
throw new \LogicException('DOMImplementation::createDocument() requires VM context');
}
self::receiver($frame, VmDom::CLASS_IMPLEMENTATION, 'DOMImplementation::createDocument()');
$namespace = $this->nullableStringArg($frame->calledArgs[1], 'DOMImplementation::createDocument()', 0);
$qualifiedName = $this->stringArg($frame->calledArgs[2], 'DOMImplementation::createDocument()', 1);
$doctype = self::optionalDocumentType($frame->calledArgs[3]);
if (null !== $frame->returnVar) {
$frame->returnVar->copyFrom(VmDom::createDocument(
$frame->vmContext,
$namespace,
$qualifiedName,
$doctype
));
}
}

private static function optionalDocumentType(Variable $var): ?\PHPCompiler\VM\ObjectEntry
{
$var = $var->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
return null;
}
if (Variable::TYPE_OBJECT !== $var->type) {
throw new \TypeError(
'DOMImplementation::createDocument(): Argument #3 ($doctype) must be of type DOMDocumentType or null'
);
}
$entry = $var->toObject();
if (!VmDom::isDocumentType($entry)) {
throw new \TypeError(
'DOMImplementation::createDocument(): Argument #3 ($doctype) must be of type DOMDocumentType or null'
);
}

return $entry;
}
}
38 changes: 38 additions & 0 deletions ext/dom/ImplementationCreateDocumentType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\Frame;

/** DOMImplementation::createDocumentType() — VM (#6140). */
final class ImplementationCreateDocumentType extends DomClassMethod
{
public function __construct()
{
parent::__construct('createDocumentType');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 4) {
throw new \LogicException('DOMImplementation::createDocumentType() expects exactly 3 arguments');
}
if (null === $frame->vmContext) {
throw new \LogicException('DOMImplementation::createDocumentType() requires VM context');
}
self::receiver($frame, VmDom::CLASS_IMPLEMENTATION, 'DOMImplementation::createDocumentType()');
$qualifiedName = $this->stringArg($frame->calledArgs[1], 'DOMImplementation::createDocumentType()', 0);
$publicId = $this->stringArg($frame->calledArgs[2], 'DOMImplementation::createDocumentType()', 1);
$systemId = $this->stringArg($frame->calledArgs[3], 'DOMImplementation::createDocumentType()', 2);
if (null !== $frame->returnVar) {
$frame->returnVar->copyFrom(VmDom::createDocumentType(
$frame->vmContext,
$qualifiedName,
$publicId,
$systemId
));
}
}
}
29 changes: 29 additions & 0 deletions ext/dom/ImplementationHasFeature.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\Frame;

/** DOMImplementation::hasFeature() — VM (#6140). */
final class ImplementationHasFeature extends DomClassMethod
{
public function __construct()
{
parent::__construct('hasFeature');
}

public function execute(Frame $frame): void
{
if (\count($frame->calledArgs) < 3) {
throw new \LogicException('DOMImplementation::hasFeature() expects exactly 2 arguments');
}
self::receiver($frame, VmDom::CLASS_IMPLEMENTATION, 'DOMImplementation::hasFeature()');
$feature = $this->stringArg($frame->calledArgs[1], 'DOMImplementation::hasFeature()', 0);
$version = $this->stringArg($frame->calledArgs[2], 'DOMImplementation::hasFeature()', 1);
if (null !== $frame->returnVar) {
$frame->returnVar->bool(VmDom::hasFeature($feature, $version));
}
}
}
22 changes: 22 additions & 0 deletions ext/dom/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\dom;

use PHPCompiler\ModuleAbstract;
use PHPCompiler\Runtime;

/**
* dom extension module entry (php-src ext/dom/php_dom.c; issue #6140).
*
* PHP-in-PHP DOM factory — no runtime/*.c growth.
*/
class Module extends ModuleAbstract
{
public function init(Runtime $runtime): void
{
parent::init($runtime);
BuiltinClasses::register($runtime->vmContext);
}
}
Loading