# [PHP] DOMElement insert custom HTML using Symfony DomCrawler

In JavaScript, if we want to insert a custom HTML to an element, there is a convenience way that we can set it into the `innerHTML`.

```javascript
const el = document.querySelector('.foo');

el.innerHTML = `<div>FOO</div>`;
```

But in PHP, although there has a DOM Document implementation, it is only supports HTML4 and not up-to-date with W3C standards for a long time, the `innerHTML` still not work on PHP DOMDocument extension, so we must use another way to insert custom HTML.

## Using Symfony DomCrawler for HTML5

The Symfony [DomCrawler Component](https://symfony.com/doc/current/components/dom_crawler.html) is a useful package to help us parsing and filtering DOM, and there is a [masterminds/html5](https://packagist.org/packages/masterminds/html5) package can work with DomCrawler to allow it correctly handle HTML5.

We can install both of them:

```bash
composer require symfony/dom-crawler masterminds/html5 symfony/css-selector
```

## Parse HTML

Now we can parse a HTML string to DOMDocument:

```php
use Symfony\Component\DomCrawler\Crawler;

// Some HTML5 string, maybe is an article content, or get by HTTPClient
$html = '...';

// Parse it, you must add `useHtml5Parser: true` 
// to use masterminds/html5 as the parser
$crawler = new Crawler($html, useHtml5Parser: true);

// Filter what you want by CSS selector
$cards = $crawler->filter('.card');

foreach ($cards as $card) {
    // @var $card DOMElement
}
```

## Insert or Replace HTML

Now if you want to insert a simple HTML, you can just use `createElement()`

```php
$body = $crawler->ownerDocument->createElement('div');
$body->setAttribute('class', 'card-body');
$body->setAttribute('style', '...');

// Append child
$card->appendChild($body);

// Or insertBefore
$card->firstChild->insertBefore($body);

// Or replace
$card->firstChild->replaceWith($body);
```

## Insert from Another DOM

And if you want to insert a HTML string, you must parse it into DOMElement first, here we use `Masterminds\HTML5` to parse it:

```php
use Masterminds\HTML5;

$childHTML = '<div class="card-body">...</div>';

$html5 = new HTML5();
$dom = $html5->loadHTML($childHTML);

// Get the node
$node = $dom->documentElement->firstElementChild;
```

If you directly insert this node:

```php
$card->appendChild($node);
```

you will receive an error message:

> Fatal error: Uncaught exception 'DOMException' with message 'Wrong Document Error'

That because in the DOM standards, every DOM nodes and elements are belongs to only 1 DOMDocument, if you want to insert a DOM node into another Document, you must import them first.

```php
// Now we get the ownerDocument of $card and import nodes
// Remember add the second argument TRUE to deeply import.
$root = $card->ownerDocument->importNode($dom->documentElement, true);
```

The returned `$root` is a clone of original DOM node, we can insert it to new DOM:

```php
$card->appendChild($root->firstElementChild);
```

## Save back to HTML

In the current Symfony Crawler version, save as HTML can be very convenience, just use:

```php
$crawler->html();
```

Note, if you get the child crawler instance, it may print partial of the HTML:

```php
$cards->html();
```
