PHPackages                             wikimedia/dodo - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. wikimedia/dodo

ActiveLibrary

wikimedia/dodo
==============

DOm DOcument implementation

v0.7.1(4mo ago)2133.9k↑25%1MITPHPPHP &gt;=8.1

Since Jul 4Pushed 2mo ago20 watchersCompare

[ Source](https://github.com/wikimedia/mediawiki-libs-Dodo)[ Packagist](https://packagist.org/packages/wikimedia/dodo)[ Docs](https://www.mediawiki.org/wiki/Dodo)[ RSS](/packages/wikimedia-dodo/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (15)Versions (9)Used By (1)

[![Latest Stable Version](https://camo.githubusercontent.com/170ba92ce71db681e1e4af9643a3d2e1fbf5a7913ec5e8709a1b0c290f563ccd/68747470733a2f2f706f7365722e707567782e6f72672f77696b696d656469612f646f646f2f762f737461626c652e737667)](https://packagist.org/packages/wikimedia/dodo) [![License](https://camo.githubusercontent.com/de9da21ce6e4ba4f1c69a7e84234478427d75b59de6ca5233daa882ecb52685b/68747470733a2f2f706f7365722e707567782e6f72672f77696b696d656469612f646f646f2f6c6963656e73652e737667)](https://packagist.org/packages/wikimedia/dodo)

Dodo
====

[](#dodo)

Dodo is a port of [Domino.js](https://github.com/fgnass/domino) to PHP, in order to provide a more performant and spec-compliant DOM library than the DOMDocument PHP classes (`xml` extension), which is built on [libxml2](www.xmlsoft.org).

Dodo uses a PHP binding for WebIDL defined by [IDLeDOM](https://packagist.org/packages/wikimedia/idle-dom). Details of the WebIDL binding can be found in the IDLeDOM documentation.

Additional documentation about the library can be found on [MediaWiki.org](https://www.mediawiki.org/wiki/Dodo).

Report issues on [Phabricator](https://phabricator.wikimedia.org/maniphest/task/edit/form/1/?projects=Parsoid&title=Dodo:%20).

Install
-------

[](#install)

This package is [available on Packagist](https://packagist.org/packages/wikimedia/dodo):

```
$ composer require wikimedia/dodo
```

Usage
-----

[](#usage)

A better set of examples and tests is coming. For an extremely basic usage, see [tests/DodoTest.php](tests/DodoTest.php).

Tests
-----

[](#tests)

```
$ composer test
```

Status
------

[](#status)

This software is near completion, from the perspective of DOM features used by Parsoid. Completing the last missing features/fixing the last remaining bugs to allow Parsoid to run with Dodo as its DOM library is the prime objective.

After that, performance benchmarking and tuning will be in order.

We run many but not all W3C and WPT tests. Some of these depend on JavaScript-specific features and as such will probably always be skipped. The "known failures" framework we use could use some improvement in order to provide more granular results.

Background
----------

[](#background)

(taken from [this page](https://www.mediawiki.org/wiki/Parsoid/PHP/Help_wanted))

> The PHP DOM extension is a wrapper around libxml2 with a thin layer of DOM-compatibility on top ("To some extent libxml2 provides support for the following additional specifications but doesn't claim to implement them completely \[...\] Document Object Model (DOM) Level 2 Core \[...\] but it doesn't implement the API itself, gdome2 does this on top of libxml2").

> This is not really remotely close to a modern standards-compliant HTML5 DOM implementation and is barely maintained, much less kept in sync with the WHATWG's pace of change.

The Dodo library implements PHP interfaces generated directly from the WebIDL sources included in the WHATWG DOM specification by `IDLeDOM`.

Developer Notes
---------------

[](#developer-notes)

### Why you need accessors for interface properties

[](#why-you-need-accessors-for-interface-properties)

Most DOM implementations have to make a decision about adapting the specification's notion of an interface property. In many languages, the only solution is to use accessor functions, e.g. `getFoo()` and `setFoo(value)` and prevent direct access to the properties themselves.

This is not contrary to the spec's intention, as it is mostly capturing data representation, and seems to expect some level of indirection between the library that implements the specification, and the code which calls that library.

Aside from the usual arguments and reasons for preferring accessors over direct property access and vice-versa, in this case most implementations are forced down the accessor route for one reason in particular, and that is that the current [DOM Specification](https://dom.spec.whatwg.org) defines certain interface properties as being `readonly`, for example the [Attr](https://dom.spec.whatwg.org/#interface-attr) interface:

```
interface Attr : Node {
  readonly attribute DOMString? namespaceURI;
  readonly attribute DOMString? prefix;
  readonly attribute DOMString localName;
  readonly attribute DOMString name;
  [CEReactions] attribute DOMString value;

  readonly attribute Element? ownerElement;

  readonly attribute boolean specified; // useless; always returns true
};

```

This essentially means that once their value has been set once (in the constructor), it cannot be modified, but can still be accessed.

PHP currently lacks a way to implement readonly properties without incurring significant performance penalties. Although there have been several RFCs ([readonly properties](https://wiki.php.net/rfc/readonly_properties)and [property accessors syntax](https://wiki.php.net/rfc/propertygetsetsyntax-v1.2)), they have always been declined.

So, in the PHP binding for WebIDL which Dodo uses, we have explicit accessors for each WebIDL property. If a class property "foo" is not marked `readonly`, then there will be methods `getFoo()` and `setFoo($value)` defined on the class. If "foo" is marked `readonly`, then only `getFoo()` will be defined on the class.

We bridge the gap between the spec and common usage by defining special "magic methods" (`__get`, `__set`, etc) in order to support the common `$obj->foo` style of access. These will be less-performant than accessing the appropriate `getFoo` or `setFoo` method directly, and so for performance Dodo internally avoids using this style of access.

However, if you're reading this, and PHP has passed an RFC with improved JavaScript-style property accessor functions, you know what to do: replace the `__get` and `__set` magic methods with appropriate property accessors. (This can probably be done in IDLeDOM's generated `Helper` classes, and may not actually need any code change in Dodo itself.)

### Strings specified as "NULL or non-empty"

[](#strings-specified-as-null-or-non-empty)

It isn't uncommon for interface properties to have type written `DOMString?`, which is not a single type, but rather indicates that the field may take either of type `NULL`, or type `DOMString`(they are distinct types).

For example, the `namespaceURI` property from the [Attr](https://dom.spec.whatwg.org/#interface-attr) interface:

```
interface Attr : Node {
  readonly attribute DOMString? namespaceURI;
  /* ... */
};

```

However, it's common for there to be an additional constraint on the value of such properties, one which is not visible from inspection of the interface definition in IDL.

For example, [namespaceURI](https://dom.spec.whatwg.org/#dom-attr-namespaceuri)is defined to return the [namespace](https://dom.spec.whatwg.org/#concept-attribute-namespace), which is either "NULL or a non-empty string".

Well, this is a bit annoying because it's certainly possible to provide the empty string to any interface which accepts arguments of type `DOMString`.

Because of this common stipulation, you would find in the code something that looks like:

```
class Attr extends Node
{
        protected $namespaceURI = NULL;

        public function construct(string? $namespace=NULL /* ... other arguments ... */)
        {
                if ($namespace !== '') {
                        $this->$namespaceURI = $namespace;
                }

                /* ... */
        }

        /* ... */
}

```

The caller can provide either a string or NULL, but the assignment will only occur if it is NOT the empty string. In that case, `$this->$namespaceURI` will retain its default value of `NULL`.

### Strings specified as "non-empty"

[](#strings-specified-as-non-empty)

This seems simpler, but it's actually worse than "NULL or non-empty"!

Properties that must be "non-empty strings", such as [localName](https://dom.spec.whatwg.org/#concept-attribute-local-name), are usually integral to the object functioning properly. `localName`, for example, is the name of the attribute.

Unfortunately, an empty string is also a string, and even a `DOMString`. So providing the empty string is valid when the function's argument type is `DOMString` (or `string`, in PHP's type hinting). But in the case of constructors, once we find out that this argument is the empty string, the entire object is undefined.

But in PHP, it's not possible to "abort" the constructor -- an object of the specified class will always be returned to the caller. In old versions of PHP, you could actually do something like `unset($this)`inside the constructor. Pretty cool, but you haven't been able to do it for years. What a pain...

So we probably have to throw an Exception, or make a "non-empty string" class.

### Readonly does not mean immutable

[](#readonly-does-not-mean-immutable)

Read-only/read-write and mutable/immutable

These are not equivalent, though it seems at first they might be.

```
    Immutable  read-only
    Read-write => mutable

```

But

```
    mutable =/> read-write

```

For example, on an Attr object, ownerElement is a read-only property, but it can still change if we associate the Attr node with another element.

For another example, the name property of an attribute is read-only, but the prefix property is read-write, and since I can mutate the prefix property, I can mutate the name (which includes this prefix), thus making the name property mutable, even if it's read-only.

Basically, there are properties where even if you can't update them directly, you can update something that is used to compute their value.

### Methods that are somewhere between abstract and concrete...

[](#methods-that-are-somewhere-between-abstract-and-concrete)

The `Node` interface methods `isEqualNode` and `cloneNode` are two good examples of things that are annoying. Both of them first do something that is common among all `Node` objects, and then proceed to do something that is unique to whatever class has extended `Node`, for example `Attr`.

That means that if you want to implement them as `abstract`, you have to include this boilerplate `Node`-common stuff in all of the subclass implementations of the abstract method. What a pain.

So instead, we have abstract methods like `_subclass_isEqualNode`, which are called by `Node::isEqualNode` when it's time to do the subclass-specific part.

### Other readability conventions

[](#other-readability-conventions)

- If a property accessor or method is part of the spec, it is written exactly as in the spec IDL (naturally).
- If a property or method is for internal-use, it is prefixed with '\_'.

### Potential bugs in Domino.js

[](#potential-bugs-in-dominojs)

It appears that HTMLCollection will not recompute the cache when an Element's `id` or `name` attribute changes. However, these are used to index two internal caches, and so the HTMLCollection will no longer be "live".

Solution would be to update `lastModTime` when those attributes are mutated.

### Performance tips

[](#performance-tips)

- Make sure your Element ids stay unique. The spec requires that you return the first Element with that id, in document order, and it is not very performant to compute the document order.

License and Credits
-------------------

[](#license-and-credits)

The initial version of this code was written by Jason Linehan. Further improvements were made by C. Scott Ananian (IDLeDOM, bug fixes, missing features) and Tim Abdullin (test suite).

This code is (c) Copyright 2019-2025 Wikimedia Foundation. It is distributed under the MIT license; see LICENSE for more info.

---

###  Health Score

50

—

FairBetter than 96% of packages

Maintenance82

Actively maintained with recent releases

Popularity32

Limited adoption so far

Community24

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 62.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~235 days

Recently: every ~384 days

Total

8

Last Release

130d ago

PHP version history (3 changes)v0.1.0PHP &gt;=7.2.9|^8.0

v0.5.0PHP &gt;=7.4.3

v0.7.0PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/b94d9718c06ec7c3fd7a104bc44966fb7464b9ec7411582343ea35a7a6a85f08?d=identicon)[mediawiki](/maintainers/mediawiki)

![](https://www.gravatar.com/avatar/716c86d71cbf921e7912a505f89d799de398fc0a3af0bd4c8862834b2d642bd7?d=identicon)[wikimedia](/maintainers/wikimedia)

![](https://www.gravatar.com/avatar/3551c2aefb299a0c45807f7677f5b26d8a5be4a4af359b4bf4fabbdd1f2b990e?d=identicon)[cscott](/maintainers/cscott)

---

Top Contributors

[![cscott](https://avatars.githubusercontent.com/u/156080?v=4)](https://github.com/cscott "cscott (207 commits)")[![linehan](https://avatars.githubusercontent.com/u/1724663?v=4)](https://github.com/linehan "linehan (68 commits)")[![DannyS712](https://avatars.githubusercontent.com/u/46829944?v=4)](https://github.com/DannyS712 "DannyS712 (24 commits)")[![jdforrester](https://avatars.githubusercontent.com/u/881572?v=4)](https://github.com/jdforrester "jdforrester (13 commits)")[![umherirrender](https://avatars.githubusercontent.com/u/1174884?v=4)](https://github.com/umherirrender "umherirrender (11 commits)")[![subbuss](https://avatars.githubusercontent.com/u/91608?v=4)](https://github.com/subbuss "subbuss (3 commits)")[![reedy](https://avatars.githubusercontent.com/u/67615?v=4)](https://github.com/reedy "reedy (2 commits)")[![jaishsingh](https://avatars.githubusercontent.com/u/143109699?v=4)](https://github.com/jaishsingh "jaishsingh (1 commits)")[![supertassu](https://avatars.githubusercontent.com/u/9721638?v=4)](https://github.com/supertassu "supertassu (1 commits)")[![tstarling](https://avatars.githubusercontent.com/u/389141?v=4)](https://github.com/tstarling "tstarling (1 commits)")[![KleinMuci](https://avatars.githubusercontent.com/u/84108018?v=4)](https://github.com/KleinMuci "KleinMuci (1 commits)")

---

Tags

domwebidldomino

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/wikimedia-dodo/health.svg)

```
[![Health](https://phpackages.com/badges/wikimedia-dodo/health.svg)](https://phpackages.com/packages/wikimedia-dodo)
```

###  Alternatives

[masterminds/html5

An HTML5 parser and serializer.

1.8k242.8M229](/packages/masterminds-html5)[wikimedia/parsoid

Parsoid, a bidirectional parser between wikitext and HTML5

171524.3k1](/packages/wikimedia-parsoid)[paquettg/php-html-parser

An HTML DOM parser. It allows you to manipulate HTML. Find tags on an HTML page with selectors just like jQuery.

2.4k7.9M123](/packages/paquettg-php-html-parser)[sunra/php-simple-html-dom-parser

Composer adaptation of: A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way! Require PHP 5+. Supports invalid HTML. Find tags on an HTML page with selectors just like jQuery. Extract contents from HTML in a single line.

1.3k9.4M61](/packages/sunra-php-simple-html-dom-parser)[sabre/xml

sabre/xml is an XML library that you may not hate.

52832.2M131](/packages/sabre-xml)[voku/simple_html_dom

Simple HTML DOM package.

9188.3M63](/packages/voku-simple-html-dom)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
