Skip to content

Latest commit

 

History

History
1062 lines (837 loc) · 34.4 KB

novos-recursos-da-ext-dom-no-php-8.4.md

File metadata and controls

1062 lines (837 loc) · 34.4 KB
source_url revision status license
1718313707
wip

Novos Recursos da ext-dom no PHP 8.4

  • Versão: 0.1
  • Data: 12/04/2024
  • Pessoas autoras: Niels Dossche
  • Situação: Em votação
  • Primeira publicação: Wiki do PHP

Introdução

O ciclo de desenvolvimento do PHP 8.4 já viu duas melhorias importantes na extensão ext-dom: suporte ao HTML 5 e conformidade opcional com a especificação DOM. Esta RFC é (provavelmente) a última melhoria da ext-dom para o PHP 8.4: ela propõe adicionar novos recursos à extensão. Em particular, focaremos no suporte ao seletor CSS, preenchendo recursos ausentes, mas comuns, e adicionando novas propriedades. Essas melhorias e alterações se aplicam apenas às novas classes no namespace Dom.

Proposta

A RFC consiste em várias subpropostas agrupadas em uma RFC para minimizar a sobrecarga. Nesta seção, discutiremos cada proposta separadamente.

Seletores CSS

Existem várias maneiras de consultar nós em um documento XML ou HTML. Aquela já implementada desde a existência da extensão DOM está usando XPath. Outra forma popular, com a qual as pessoas provavelmente estão mais familiarizadas, são os seletores CSS. Em teoria, toda consulta que você pode escrever com seletores CSS também pode ser escrita com XPath. No entanto, em muitos casos, o XPath é muito mais complicado de usar. Por exemplo, uma consulta simples como p:contains("hello") + span é equivalente a .//p[contains(normalize-space(),"hello")]/following-sibling: :*[1]/self::span. As coisas podem ficar muito mais complicadas quando você usa pseudofunções como :disabled, que são muito difíceis de expressar em XPath.

A especificação DOM define as seguintes funções de seletor CSS:

namespace Dom {
    interface ParentNode {
        public function querySelector(string $selectors): ?Element {}
        public function querySelectorAll(string $selectors): NodeList {}
    }

    class Element extends Node implements ParentNode {
        public function closest(string $selectors): ?Element {}
        public function matches(string $selectors): bool {}
    }
}

Isso é o que os métodos fazem:

  • querySelector: Retorna o primeiro elemento descendente que corresponde aos seletores CSS.
  • querySelectorAll: Retorna uma NodeList contendo todos os elementos descendentes que correspondem aos seletores CSS.
  • closest: Encontra o ancestral mais próximo do elemento que corresponde aos seletores CSS.
  • matches: Retorna true se o elemento corresponder aos seletores CSS, ou false, caso contrário.

Vale ressaltar que os seletores CSS podem conter pseudoclasses que só fazem sentido quando algo é renderizado na tela. Como, por exemplo, :hover, que corresponde quando uma pessoa usuária passa o mouse sobre um elemento. Como no contexto do PHP isso não faz sentido, um seletor CSS que usa tal pseudoclasse não corresponderá a nada.

Embora seja possível implementar closest e matches usando XPath, isso não pode ser feito com um bom desempenho (até onde eu sei).

A biblioteca fundamental que usamos para análise do HTML5 também contém a funcionalidade CSS necessária para implementar esses métodos. Portanto, podemos obter a funcionalidade com relativa facilidade. Eu só tive que adaptar as estruturas de dados dos nós para corresponder às estruturas de dados do PHP.

Existem soluções alternativas criadas por pessoas desenvolvedoras que implementam uma tradução de seletores CSS para XPath, mas com base no que vi:

  • Elas sofrerão um impacto no desempenho porque a tradução não é "gratuita".
  • Elas não fornecem uma forma eficiente de implementar closest e matches.
  • Como elas são implementadas em torno das antigas classes DOM, e as antigas classes DOM não consideram os diferentes namespaces HTML adequadamente, elas também não consideram os namespaces adequadamente.

Exemplos

Exemplo 1: querySelector

$dom = Dom\XMLDocument::createFromString(<<<XML
<root>
  <span>1</span>
  <p>oi</p>
  <span>2</span>
</root>
XML);

var_dump($dom->querySelector('p ~ span')->textContent); // string(1) "2"

Exemplo 2: closest

$dom = Dom\XMLDocument::createFromString(<<<XML
<root>
  <div class="foo" xml:id="div1">
    <div xml:id="div2">
      <div class="bar" xml:id="div3"/>
    </div>
  </div>
</root>
XML);

var_dump($dom->getElementById('div3')->closest('div')->getAttribute("xml:id")); // string(4) "div3"
var_dump($dom->getElementById('div3')->closest(':not(div[class])')->getAttribute("xml:id")); // string(4) "div2"

Exemplo 3: matches

$dom = Dom\XMLDocument::createFromString(<<<XML
<root>
  <div xml:id="div1">
    <div xml:id="div2">
      <div xml:id="div3"/>
    </div>
  </div>
</root>
XML);

var_dump($dom->getElementById('div3')->matches('div > div')); // bool(true)
var_dump($dom->getElementById('div3')->matches('root > div')); // bool(false)

Element::$innerHTML

Esta é uma propriedade da classe Element definida na especificação DOM:

namespace Dom {
    class Element /* ... */ {
        public string $innerHTML;
    }
}

A leitura deste campo obterá a serialização do conteúdo interno do elemento, a gravação nele transformará uma string em uma subárvore e substituirá o conteúdo do elemento pela nova subárvore. Se o documento for um documento HTML, o analisador/serializador HTML será usado. Se o documento for um documento XML, o analisador/serializador XML será usado. Sim, isso significa que innerHTML pode definir conteúdo XML, e isso foi definido pelas especificações. Esse erro na nomenclatura é uma bagagem herdada da especificação que decorre do fato de que, por questões de interoperabilidade, a classe Element é compartilhada entre documentos XML e HTML.

Se a serialização não estiver bem formada para XML, uma DOMException com $code DOM_SYNTAX_ERR será lançada, conforme definido pela especificação.

A análise de documentos (ou fragmentos) pode causar erros hard/soft. Os erros soft são relatados por meio de alertas ou, se o mecanismo interno de tratamento de erros for usado, os erros serão armazenados em um array. A menos que LIBXML_NOERROR seja fornecida, nesse caso esses erros soft serão silenciados. Observe que não temos como fornecer uma opção de análise para a propriedade innerHTML e, portanto, não podemos fornecer uma maneira de silenciar os erros de maneira limpa. Perguntei sobre isso na lista de discussão, mas não obtive resposta. Isso provavelmente significa que as pessoas estão inseguras ou não se importam e, por isso, optei por não implementar o relatório de erros porque é mais fácil omitir algo e adicioná-lo mais tarde do que remover algo mais tarde.

Novas propriedades para Document

Proponho a adição de várias novas propriedades à classe Document para tornar o desenvolvimento um pouco mais fácil:

namespace Dom {
    class HTMLElement extends Element {
        /* Há uma oportunidade de adicionar propriedades úteis da especificação
           HTML aqui no futuro. */
    }

    class Document /* ... */ {
        public ?HTMLElement $body;
        /** @readonly */
        public ?HTMLElement $head;
        public string $title;
    }
}

Essas adições são descritas no adendo da especificação HTML para o DOM.

As propriedades devem ser relativamente autoexplicativas. $body refere-se ao elemento body (se houver), $head ao elemento head (se houver) e $title ao texto dentro do elemento title (que por sua vez está dentro do elemento head). Você pode ler sobre todos os detalhes usando o link acima, porque é um pouco mais complicado quando SVG está envolvido, por exemplo, mas você deve estar familiarizado com essas propriedades do Javascript.

Como você pode ver, isso também requer a adição da classe HTMLElement. Esta classe estende a classe Element. No futuro, poderemos adicionar propriedades a elas também, mas isso será deixado de fora desta RFC por enquanto. Os elementos que estão dentro do namespace HTML agora retornarão uma instância de HTMLElement em vez de Element. Por exemplo, $documentElement é uma propriedade na classe Document do tipo Element. Se este for um elemento HTML, obteremos uma instância de HTMLElement em vez de Element. Tudo isso está definido nas especificações.

TokenList

Proponho adicionar a classe TokenList da especificação DOM ao PHP:

namespace Dom {
    /**
    * @not-serializable
    * @strict-properties
    */
    final class TokenList implements \IteratorAggregate, \Countable {
        private function __construct() {}

        /** @readonly */
        public int $length;
        public function item(int $index): ?string {}
        public function contains(string $token): bool {}
        public function add(string ...$tokens): void {}
        public function remove(string ...$tokens): void {}
        public function toggle(string $token, ?bool $force = null): bool {}
        public function replace(string $token, string $newToken): bool {}
        public function supports(string $token): bool {}
        public string $value;

        public function count(): int {}

        public function getIterator(): \Iterator {}
    }
}

Uma instância de TokenList pode ser obtida através da propriedade Element::$classList. Por enquanto, seu propósito está limitado a gerenciar os nomes das classes de um elemento, mas a classe é construída para representar um conjunto de tokens. Superficialmente, pode parecer trivial gerenciar os nomes de classes em documentos, mas isso não é bem verdade. TokenList considerará as classes como um conjunto, lidará com normalização de espaços em branco, iteração, manipulações fáceis como alternância, etc., tudo disponível para você em uma API fácil de usar.

Exemplos

Exemplo 1: Operações básicas

$dom = Dom\XMLDocument::createFromString("<root class='primeiro segundo\tterceiro'/>");
$root = $dom->documentElement;
$list = $root->classList;

var_dump($list);
/*
object(Dom\TokenList)#3 (2) {
["length"]=>
int(3)
["value"]=>
string(25) "primeiro segundo terceiro"
}
*/

var_dump($list->contains("segundo")); // bool(true)
var_dump($list->toggle("segundo")); // bool(false)
var_dump($root->className); // string(17) "primeiro terceiro"

$list->replace("terceiro", "outra-classe");
var_dump($list->item(1)); // string(12) "outra-classe"

Adições Específicas do PHP

A extensão DOM já implementa algumas extensões específicas do PHP para as classes DOM, como suporte à normalização e canonização. Para melhor suportar algumas cargas de trabalho, proponho as seguintes adições específicas do PHP:

namespace Dom {
    /**
    * @not-serializable
    * @strict-properties
    */
    final class NamespaceInfo
    {
        public readonly ?string $prefix;
        public readonly ?string $namespaceURI;
        public readonly Element $element;

        private function __construct() {}
    }

class Attr /* ... */ {
    public function rename(?string $namespace, string $qualifiedName): void {}
}

class Element /* ... */ {
    public string $substitutedNodeValue;

        /** @return list<NamespaceInfo> */
        public function getInScopeNamespaces(): array {}

        /** @return list<NamespaceInfo> */
        public function getDescendantNamespaces(): array {}

        public function rename(?string $namespace, string $qualifiedName): void {}
    }
}

Vamos examiná-las uma por uma.

NamespaceInfo

Esta classe é a substituta moderna da classe DOMNamespaceNode. DOMNamespaceNode foi mal projetada porque tenta ser um Node, mas, na verdade, não é um nó porque não está na árvore. Por exemplo, no "DOM antigo", ao usar getAttributeNode("xmlns"), ele pode retornar um DOMNamespaceNode para a declaração de namespace, mesmo que não exista necessariamente tal atributo. A outra maneira de obter uma instância DOMNamespaceNode é via XPath usando o eixo namespace::*.

A razão pela qual retornamos a instância DOMNamespaceNode para o XPath é devido a algumas regras peculiares estabelecidas pela Recomendação do XPath. Em particular, o eixo de namespace precisa retornar todos os namespaces em escopo de um elemento. No entanto, esse link da especificação também afirma:

Os elementos nunca compartilham nós de namespace: se um nó de elemento não for o mesmo nó que outro nó de elemento, então nenhum dos nós de namespace de um nó de elemento será o mesmo nó que os nós de namespace de outro nó de elemento.

Por isso, não podemos retornar o nó de atributo correspondente à declaração do namespace (se houver) porque teríamos que retornar o mesmo nó de atributo para elementos diferentes. Consequentemente, o DOMNamespaceNode é retornado no "DOM antigo". No entanto, implementar isso no "novo DOM" é um problema porque estaríamos retornando algo de uma consulta XPath que não é um nó. Isso é confuso para as pessoas usuárias e também para as ferramentas de análise estática.

Como essas APIs também são implementadas por navegadores, vale a pena ver como eles resolvem esse problema e o que diz a especificação. Acontece que tudo isso não está documentado nas especificações e os navegadores não implementam o eixo de namespace.

Proponho adicionar dois métodos ao "novo DOM" que substituem a funcionalidade do eixo de namespace: getInScopeNamespaces, que substitui ./namespace::* e getDescendantNamespaces, que substitui .//namespace::*. Quando as pessoas usuárias tentarem consultar nós de namespace a partir do eixo de namespace em Dom\XPath, lançaremos uma DOMException com $code DOM_NOT_SUPPORTED_ERR, redirecionando as pessoas usuárias para usar um desses dois métodos.

Para identificar um namespace, precisamos apenas saber o prefixo, o URI e o elemento com escopo definido. Portanto, ele possui esses três campos. Só pode ser construído pela extensão DOM, não pelas pessoas usuárias. Nenhuma propriedade específica do nó será implementada em NamespaceInfo.

As principais vantagens são:

  • A garantia de que as consultas XPath para nós sempre retornarão nós.
  • Melhores resultados de análise estática.
  • Menos confusão para as pessoas usuárias.
Exemplos
$dom = Dom\XMLDocument::createFromString(<<<XML
<root xmlns="urn:a">
    <b:sibling xmlns:b="urn:b" xmlns:d="urn:d" d:foo="bar">
        <d:child xmlns:d="urn:d2"/>
    </b:sibling>
</root>
XML);

$sibling = $dom->documentElement->firstElementChild;
var_dump($sibling->getInScopeNamespaces());
var_dump($sibling->getDescendantNamespaces());

Exemplo: saída de getInscopeNamespaces()

array(3) {
    [0]=>
    object(Dom\NamespaceInfo)#2 (3) {
        ["prefix"]=>
        NULL
        ["namespaceURI"]=>
        string(5) "urn:a"
        ["element"]=> ...
        (<b:sibling>)
    }
    [1]=>
    object(Dom\NamespaceInfo)#4 (3) {
        ["prefix"]=>
        string(1) "b"
        ["namespaceURI"]=>
        string(5) "urn:b"
        ["element"]=> ...
        (<b:sibling>)
    }
    [2]=>
    object(Dom\NamespaceInfo)#5 (3) {
        ["prefix"]=>
        string(1) "d"
        ["namespaceURI"]=>
        string(5) "urn:d"
        ["element"]=> ...
        (<b:sibling>)
    }
}

Exemplo: saída de getInScopeNamespaces()

array(6) {
    [0]=>
    object(Dom\NamespaceInfo)#5 (3) {
        ["prefix"]=>
        NULL
        ["namespaceURI"]=>
        string(5) "urn:a"
        ["element"]=> ...
        (<b:sibling>)
    }
    [1]=>
    object(Dom\NamespaceInfo)#4 (3) {
        ["prefix"]=>
        string(1) "b"
        ["namespaceURI"]=>
        string(5) "urn:b"
        ["element"]=> ...
        (<b:sibling>)
    }
    [2]=>
    object(Dom\NamespaceInfo)#2 (3) {
        ["prefix"]=>
        string(1) "d"
        ["namespaceURI"]=>
        string(5) "urn:d"
        ["element"]=> ...
        (<b:sibling>)
    }
    [3]=>
    object(Dom\NamespaceInfo)#6 (3) {
        ["prefix"]=>
        NULL
        ["namespaceURI"]=>
        string(5) "urn:a"
        ["element"]=> ...
        (<d:child>)
    }
    [4]=>
    object(Dom\NamespaceInfo)#8 (3) {
        ["prefix"]=>
        string(1) "b"
        ["namespaceURI"]=>
        string(5) "urn:b"
        ["element"]=> ...
        (<d:child>)
    }
    [5]=>
    object(Dom\NamespaceInfo)#9 (3) {
        ["prefix"]=>
        string(1) "d"
        ["namespaceURI"]=>
        string(6) "urn:d2"
        ["element"]=> ...
        (<d:child>)
    }
}

$substitutedNodeValue

No "DOM antigo", a propriedade $nodeValue realizava a substituição de entidade, o que vai contra as especificações e pode causar problemas de segurança. No "novo DOM", $nodeValue não substitui entidades (conforme pretendido pelas especificações). No entanto, isso significa que não podemos mais substituir entidades de propósito. Este não é o caso de uso mais comum, mas às vezes é necessário ao lidar com XML que você confia. A propriedade $substitutedNodeValue será o valor do nó, mas com a substituição de entidade explicitamente habilitada.

Exemplos

Exemplo 1: Definir o valor substituído em uma entidade nativa

$dom = Dom\XMLDocument::createFromString('<root/>');
$root = $dom->documentElement;

$root->substitutedNodeValue = "&amp;";

var_dump($root->textContent); // string(1) "&"

// Nota: isso irá escapar a entidade conforme as regras de serialização do XML.
echo $dom->saveXml(); // <root>&amp;</root>

Método rename

Esse método é apenas parcialmente específico do PHP. Ele existia no DOM Core Level 3, mas nunca foi implementado no PHP. Ele não existe mais no padrão atual: os autores o removeram para simplificar a API e acho que também porque a especificação DOM é mais centrada em HTML hoje em dia do que em XML. Propomos algo muito semelhante ao que existia nas especificações, mas ligeiramente melhorado.

Às vezes é necessário alterar um prefixo de namespace para um elemento/atributo, alterar o nome de um elemento/atributo ou alterar seu URI de namespace. Este caso de uso ocorre ao combinar diferentes documentos ou ao corrigir documentos, como, por exemplo, com implementações SOAP criadas por pessoas usuárias. Isso pode ser feito hoje recriando toda a subárvore sob um elemento com o novo nome, prefixo e namespace; mas isso é extremamente irritante e difícil de acertar. Essa abordagem também não funcionará se você tiver referências à mesma instância de Element, pois agora um trecho de código está funcionando em um novo nó enquanto outros trechos de código funcionam no nó antigo.

Acontece que alterar essas propriedades é realmente muito fácil de fazer internamente, então faz sentido apenas expor essa funcionalidade à pessoa usuária.

Você verá que o método rename segue a mesma assinatura do método createElementNS e também executa as mesmas verificações de integridade relacionadas ao namespace. Essas verificações de integridade garantem que as regras relacionadas ao namespace sejam atendidas e, se não forem, o método lançará uma DOMException do tipo NAMESPACE_ERR (ou INVALID_CHARACTER_ERR).

public function createElementNS(?string $namespace, string $qualifiedName): Element {} // Em Dom\Document
public function rename(?string $namespace, string $qualifiedName): void {} // Em Dom\Element e Dom\Attr

O primeiro argumento do método rename permite alterar o URI do namespace do elemento/atributo, enquanto o segundo permite alterar o nome qualificado. O nome qualificado é a combinação do prefixo e do nome local; ou apenas o nome local se não houver nenhum prefixo. Você pode estar se perguntando: "Por que não dividir esse método em vários métodos diferentes?". A resposta é que isso não é possível: o namespace escolhido tem implicações sobre quais nomes qualificados são permitidos. Portanto, em alguns casos você terá que alterar esses dois simultaneamente. É claro que é possível alterar apenas um dos dois enquanto mantém o outro intacto, mas isso deve acontecer conforme as regras relacionadas ao namespace.

Vimos anteriormente como os elementos no namespace HTML criarão uma instância de HTMLElement em vez de Element. Isso impõe uma restrição à API rename porque, caso contrário, será possível criar um Element no namespace HTML ou um HTMLElement que não esteja no namespace HTML. Portanto, se o elemento estiver no namespace HTML, ele deverá permanecer nesse namespace; e se não estiver no namespace HTML, não poderá entrar no namespace HTML. Se você tentar fazer isso, uma DOMException com $code DOM_INVALID_MODIFICATION_ERR será lançada.

Exemplos

Exemplo 1: Operação básica em um elemento

$dom = Dom\XMLDocument::createFromString('<root/>');
$root = $dom->documentElement;
$root->rename(NULL, 'documento');

echo $dom->saveXml(); // <documento/>

$root->rename('urn:teste', 'documento');

echo $root->namespaceURI; // urn:teste
var_dump($root->prefix); // NULL
echo $dom->saveXml(); // <documento xmlns="urn:teste"/>

$root->rename('urn:teste', 'prefixo:documento');

echo $root->namespaceURI; // urn:teste
var_dump($root->prefix); // prefixo
echo $dom->saveXml(); // <prefixo:documento xmlns:prefixo="urn:teste"/>

Exemplo 2: Alterando o nome de um elemento HTML

$dom = Dom\HTMLDocument::createFromString('<p>olá</p>', LIBXML_NOERROR);
$p = $dom->getElementsByTagName('p')[0];

$p->rename($p->namespaceURI, 'span');

echo $dom->saveHTML(); // <html><head></head><body><span>olá</span></body></html>

Exemplo 3: Alterando o nome de um atributo

$dom = Dom\HTMLDocument::createFromString('<p align="center"></p>', LIBXML_NOERROR);
$p = $dom->getElementsByTagName('p')[0];
$attr = $p->getAttributeNode('align');

$attr->rename($attr->namespaceURI, 'title');

echo $dom->saveHTML(); // <html><head></head><body><p title="center"></p></body></html>

Exemplo 4: Alterar o prefixo de um elemento, mantendo o restante intacto (exemplo de caso especial)

$dom = Dom\XMLDocument::createFromString('<prefixo:root xmlns:prefixo="urn:x"/>');
$root = $dom->documentElement;
$root->rename($root->namespaceURI, 'foo:' . $root->localName);

// Prefixo alterado, mas não na serialização devido ao namespace urn:x estar
// vinculado ao "prefixo" pelo atributo.
var_dump($root->prefix); // string(3) "foo"
echo $dom->saveXML(); // <prefixo:root xmlns:prefixo="urn:x"/>

// Corrigimos isso renomeando o atributo ou removendo-o.
$root->removeAttribute('xmlns:prefixo');
echo $dom->saveXML(); // <foo:root xmlns:foo="urn:x"/>

Permitindo Melhorias Específicas do PHP na Experiência da Pessoa Desenvolvedora

Funções DOM como Element::insertAdjacentElement(string $where, Element $element) e Element::insertAdjacentText(string $where, string $data) têm um primeiro argumento $where. Existem apenas quatro valores válidos para $where: "beforebegin", "afterbegin", "foreend", "afterend". Então, na verdade, isso é uma enum disfarçada. Proponho usar o recurso enum do PHP. Isso evitaria erros de programação e tornaria as dicas do IDE muito mais agradáveis, contribuindo para uma melhor experiência da pessoa desenvolvedora. Estritamente falando, isso se desvia das especificações do DOM, mas, de qualquer forma, já modelamos as classes DOM de uma forma que se ajusta ao modelo OOP do PHP. Na verdade, eu proporia permitir o uso de enums onde fizer sentido na extensão para novas APIs. Como a classe Element não existia antes da RFC de conformidade opcional com a especificação DOM, podemos alterar a assinatura sem afetar os usuários, já que nenhuma versão do PHP 8.4 foi feita até agora.

Em particular, isso resultará nas seguintes assinaturas de função e enum:

namespace Dom {
    enum AdjacentPosition : string {
        case BeforeBegin = "beforebegin";
        case AfterBegin = "afterbegin";
        case BeforeEnd = "beforeend";
        case AfterEnd = "afterend";
    }

    class Element /* ... */ {
        public function insertAdjacentElement(AdjacentPosition $where, Element $element): ?Element {}
        public function insertAdjacentText(AdjacentPosition $where, string $data): void {}
    }
}

A enum AdjacentPosition é apoiada de forma que os valores literais da especificação DOM ainda possam ser usados usando AdjacentPosition::from("beforebegin"), etc.

Emendas de API

Inicialmente, a RFC de conformidade opcional com a especificação DOM copiou as APIs existentes das antigas classes DOM sem mudanças para a maioria das APIs. Alguém relatou que (DOM)Document::xinclude() tem um comportamento estranho de valor de retorno. Em particular, citando a documentação:

Retorna o número de XIncludes no documento, -1 se algum processamento falhou ou false se não houve substituições.

Isso parece ser causado por um erro de implementação. O comportamento mais sensato seria lançar um erro em caso de falha (para evitar confusão com 0/false) e retornar o número de substituições em caso de sucesso. Se não houve substituições o número 0 deverá ser retornado.

A nova assinatura desta função ficaria assim:

final class XMLDocument extends Document {
    public function xinclude(int $options = 0): int {}
}

A exceção lançada será DOMException com $code definido como DOM_INVALID_MODIFICATION_ERR.

Alterações Incompatíveis com Versões Anteriores

Nenhum porque esta RFC afeta apenas as classes adicionadas na versão 8.4.

Versões Propostas do PHP

PHP 8.4.

Impacto da RFC

Para Extensões Existentes

Apenas a ext-dom é afetada.

Issues Abertas

Nenhuma ainda.

Funcionalidade PHP Não Afetada

Tudo fora da ext-dom.

Escopo Futuro

Inicialmente planejei incluir a propriedade outerHTML também. Isso é muito viável com todo o trabalho interno do DOM que aconteceu durante o ciclo de desenvolvimento do PHP 8.4. Porém, como não tenho visto demanda por isso, acho que meu tempo será melhor gasto com outros recursos. Se alguém realmente quiser isso no PHP 8.4, sinta-se à vontade para fazer uma implementação de PoC, deve ser bastante viável usando Lexbor e as atuais APIs internas da ext-dom.

A classe HTMLElement pode oferecer algumas propriedades úteis, mas isso foi deixado de fora porque ninguém realmente solicitou esse recurso até agora, então o tempo de desenvolvimento é melhor gasto em outro lugar.

Escolhas de Votação Propostas

Uma votação primária sim/não com maioria de 2/3 para aceitar esta proposta na totalidade.

A votação começou em 10/06/2024 e terminará em 24/06/2024.

Aceita a RFC de Adições ao DOM no PHP 8.4?

Nome Sim Não
adiel (adiel) X
ashnazg (ashnazg) X
beberlei (beberlei) X
crell (crell) X
devnexen (devnexen) X
galvao (galvao) X
girgias (girgias) X
heiglandreas (heiglandreas) X
jimw (jimw) X
josh (josh) X
kguest (kguest) X
kocsismate (kocsismate) X
levim (levim) X
mauricio (mauricio) X
mbeccati (mbeccati) X
nicolasgrekas (nicolasgrekas) X
nielsdos (nielsdos) X
petk (petk) X
pierrick (pierrick) X
ramsey (ramsey) X
sebastian (sebastian) X
sergey (sergey) X
theodorejb (theodorejb) X
timwolla (timwolla) X
weierophinney (weierophinney) X
Total 25 0

Patches e Testes

Implementação

Depois que o projeto for implementado, esta seção deverá conter:

  • as versões nas quais foi feito o merge
  • um link para os commits do git
  • um link para a entrada no manual do PHP para o recurso
  • um link para a seção de especificação da linguagem (se houver)

Referências

Changelog

  • 0.1: Primeira versão colocada em discussão e votação

Agradecimentos

Gostaria de agradecer a Toon Verwerft por seus comentários e testes iniciais.