Pure regex-based DOM manipulation library with React/JSX Template Literal Support. No DOMDocument, no XML parsing - just regex magic!
Perfect for preserving exact HTML structure, React/Vue components, and template variables.
composer require devoashim/regxdomdocument- Quick Start
- Loading HTML
- Finding Elements
- Getting Content
- Setting Content
- Attributes & Classes
- Template Literals (React/JSX)
- Removing Elements
- Iteration & Filtering
- Saving & Output
- Advanced Usage - RegexHelper
- Method Reference
- Use Cases
use devoashim\regxdomdocument\XDOM;
$dom = XDOM::load($html);
// Find and modify elements
$dom->find('#app')->setInnerHTML('<Component />');
$dom->find('.header')->addClass('active');
echo $dom->html();Convert static className to dynamic React template literal:
$dom = XDOM::loadFromFile('index.html');
// Convert static to dynamic
$dom->find('.stricky-header')
->setDynamicClass('isStick', 'stricky-fixed');
// Output HTML
echo $dom->html();Before:
<div className="stricky-header stricked-menu main-menu">After:
<div className={`stricky-header stricked-menu main-menu ${isStick ? 'stricky-fixed' : ''}`}>// From string
$dom = XDOM::load($htmlString);
// From file
$dom = XDOM::loadFromFile('/path/to/file.html');
// From Laravel Storage
$path = Storage::disk('public')->path('index.html');
$dom = XDOM::loadFromFile($path);// By ID
$element = $dom->find('#header');
$element = $dom->find('#main-content');
// By Class
$elements = $dom->find('.container');
$elements = $dom->find('.nav-item');
// By Tag
$elements = $dom->find('div');
$elements = $dom->find('nav');
$elements = $dom->find('section');
// Combined Selectors
$element = $dom->find('div#main'); // Tag + ID
$element = $dom->find('section#about');
$elements = $dom->find('div.container'); // Tag + Class
$elements = $dom->find('nav.main-nav');
// Descendant (parent child)
$elements = $dom->find('header nav');
$elements = $dom->find('div .content');
$elements = $dom->find('main section');// Get inner HTML (content between tags)
$content = $dom->find('#app')->innerHTML();
// <div id="app">THIS CONTENT</div>
// Returns: "THIS CONTENT"
// Get outer HTML (full element with tags)
$html = $dom->find('#app')->outerHTML();
// Returns: "<div id="app">Content</div>"
// Get text content (strip all HTML)
$text = $dom->find('#app')->text();
// <div id="app"><p>Hello <b>World</b></p></div>
// Returns: "Hello World"
// Get complete HTML document
$fullHtml = $dom->html();
// Get attribute value
$value = $dom->find('#app')->attr('data-value');
$id = $dom->find('div')->attr('id');
$class = $dom->find('nav')->attr('class');// Get first element
$first = $dom->find('.item')->first();
// Returns: ['html' => '...', 'position' => ..., 'tag' => '...']
// Get last element
$last = $dom->find('.item')->last();
// Get element by index
$third = $dom->find('.item')->get(2); // 0-based index
// Get all matches as array
$matches = $dom->find('.item')->getMatches();
// Returns: [
// ['html' => '<div class="item">1</div>', 'position' => 0, 'tag' => 'div', 'type' => 'full'],
// ['html' => '<div class="item">2</div>', 'position' => 50, 'tag' => 'div', 'type' => 'full'],
// ]// Count elements
$count = $dom->find('.item')->count();
echo "Found $count items";
// Check if has attribute
if ($dom->find('#app')->hasAttr('data-loaded')) {
echo "Has data-loaded attribute";
}
// Check if has class
if ($dom->find('#app')->hasClass('active')) {
echo "Has active class";
}
// Check if empty
if ($dom->find('.item')->isEmpty()) {
echo "No items found";
}
// Check if not empty
if ($dom->find('.item')->isNotEmpty()) {
echo "Items found!";
}// Set inner HTML (PRESERVES EXACT CONTENT - no encoding)
$dom->find('#app')->setInnerHTML('<Menu /> <Dashboard />');
// Perfect for React/Vue components!
// Set outer HTML (replace entire element)
$dom->find('#old')->setOuterHTML('<section id="new">Content</section>');
// Before: <div id="old">...</div>
// After: <section id="new">Content</section>
// Set text (HTML encoded for safety)
$dom->find('#title')->setText('Hello & Welcome');
// Output: Hello & Welcome
// Append content (add at end, inside element)
$dom->find('#list')->append('<li>New Item</li>');
// Prepend content (add at start, inside element)
$dom->find('#list')->prepend('<li>First Item</li>');
// Insert before element
$dom->find('#main')->before('<header>Header</header>');
// Insert after element
$dom->find('#main')->after('<footer>Footer</footer>');// Set single attribute
$dom->find('#app')
->attr('data-component', 'main')
->attr('data-version', '2.0')
->attr('data-loaded', 'true');
// Remove attribute
$dom->find('#app')->removeAttr('data-old');
// Add single class
$dom->find('#app')
->addClass('active')
->addClass('loaded')
->addClass('ready');
// Add multiple classes
$dom->find('.header')->addClasses('active loaded ready');
$dom->find('.header')->addClasses(['active', 'loaded', 'ready']);
// Remove class
$dom->find('#app')
->removeClass('old')
->removeClass('hidden');
// Toggle class
$dom->find('#app')->toggleClass('active');
// If has 'active' → removes it
// If no 'active' → adds it
// Set all classes (replace existing)
$dom->find('.header')->setClasses('new-class another-class');
// Conditional class (PHP-side)
$dom->find('.header')->conditionalClass('active', $isActive);$dom->find('.header')->setDynamicClass('isStick', 'sticky-fixed');Result:
<div className={`header ${isStick ? 'sticky-fixed' : ''}`}>$dom->find('.header')
->setMultipleConditionalClasses([
'isStick' => 'sticky-fixed',
'isTransparent' => 'transparent',
'isScrolled' => 'scrolled'
]);Result:
<div className={`header ${isStick ? 'sticky-fixed' : ''} ${isTransparent ? 'transparent' : ''} ${isScrolled ? 'scrolled' : ''}`}>$dom->find('.button')
->setTemplateExpression('className', 'btn ${type === "primary" ? "btn-primary" : "btn-secondary"}');Result:
<button className={`btn ${type === "primary" ? "btn-primary" : "btn-secondary"}`}>$dom->find('div')->convertToClassName();Before: <div class="header">
After: <div className="header">
// Remove elements from DOM
$dom->find('.old-widget')->remove();
$dom->find('#deprecated')->remove();// Iterate each element
$dom->find('.item')->each(function($element, $index) {
echo "Item $index: " . $element['html'] . "\n";
});
// Map elements (transform)
$texts = $dom->find('.item')->map(function($element, $index) {
return strip_tags($element['html']);
});
// Returns: Collection or array
// Filter elements
$filtered = $dom->find('.item')->filter(function($element, $index) {
return strpos($element['html'], 'active') !== false;
});
// Returns: RegexNodeCollection with filtered elements// Get HTML as string
$html = $dom->html();
// Set HTML
$dom->setHtml($newHtmlString);
// Returns: XDOM (chainable)
// Save to file
$dom->saveToFile('/path/to/output.html');
// Returns: XDOM (chainable)
// Save to Laravel Storage
$dom->saveToStorage('public/output.html');
// Returns: XDOM (chainable)
// Return as Laravel Response
return $dom->toResponse();
// Returns: Response
// Automatically sets Content-Type: text/htmlFor direct regex operations without the fluent interface:
use devoashim\regxdomdocument\RegexHelper;Returns: string or null
// Extract full element (with tags)
$content = RegexHelper::extractTagContent($html, 'div', 'header', 'class');
// From: <div class="header">Content</div>
// Returns: "<div class="header">Content</div>"
$content = RegexHelper::extractTagContent($html, 'section', 'main', 'id');
// From: <section id="main">Content</section>
// Returns: "<section id="main">Content</section>"
// Extract inner content (without tags)
$inner = RegexHelper::extractInnerContent($html, 'div', 'container', 'class');
// From: <div class="container"><p>Text</p></div>
// Returns: "<p>Text</p>"Returns: string
// Replace full element
$newHtml = RegexHelper::replaceTagContent(
$html,
'div',
'header',
'<div class="header"><Header /></div>'
);
// Replace inner content only
$newHtml = RegexHelper::replaceInnerContent(
$html,
'div',
'content',
'<Component />'
);Returns: array
// Find all matching elements
$widgets = RegexHelper::findAllTags($html, 'div', 'widget', 'class');
// Returns: ['<div class="widget">1</div>', '<div class="widget">2</div>', ...]
foreach ($widgets as $i => $widget) {
echo "Widget $i: $widget\n";
}Returns: string
// Remove all matching elements
$cleanHtml = RegexHelper::removeAllTags($html, 'div', 'ads', 'class');Returns: bool
// Check if element exists
if (RegexHelper::hasTag($html, 'div', 'header', 'class')) {
echo "Header exists!";
}Returns: array or string
// Extract all attributes from tag
$tag = '<div class="box" id="main" data-value="123">';
$attrs = RegexHelper::extractAttributes($tag);
// Returns: ['class' => 'box', 'id' => 'main', 'data-value' => '123']
// Replace attribute value
$newTag = RegexHelper::replaceAttribute($tag, 'class', 'new-box active');
// Returns: '<div class="new-box active" id="main" data-value="123">'Returns: string
// Convert to template literal
$reactHtml = RegexHelper::setDynamicClassName(
$html,
'div',
'header',
'isStick',
'sticky-fixed'
);| Method | Returns | Description |
|---|---|---|
load($html) |
XDOM | Load HTML string |
loadFromFile($path) |
XDOM | Load from file |
find($selector) |
RegexNodeCollection | Find elements |
html() |
string | Get full HTML |
setHtml($html) |
XDOM | Set HTML (chainable) |
saveToFile($path) |
XDOM | Save to file (chainable) |
saveToStorage($path) |
XDOM | Save to Laravel Storage |
toResponse() |
Response | Return as Laravel response |
| Method | Returns | Chainable | Description |
|---|---|---|---|
innerHTML() |
string | No | Get inner HTML |
outerHTML() |
string | No | Get outer HTML |
text() |
string | No | Get text content |
attr($name) |
string/null | No | Get attribute value |
attr($name, $value) |
RegexNodeCollection | Yes | Set attribute |
hasAttr($name) |
bool | No | Check if has attribute |
removeAttr($name) |
RegexNodeCollection | Yes | Remove attribute |
addClass($class) |
RegexNodeCollection | Yes | Add single class |
addClasses($classes) |
RegexNodeCollection | Yes | Add multiple classes |
removeClass($class) |
RegexNodeCollection | Yes | Remove class |
toggleClass($class) |
RegexNodeCollection | Yes | Toggle class |
setClasses($classes) |
RegexNodeCollection | Yes | Set all classes |
hasClass($class) |
bool | No | Check if has class |
conditionalClass($class, $condition) |
RegexNodeCollection | Yes | Add class if condition true |
setInnerHTML($html) |
RegexNodeCollection | Yes | Set inner HTML |
setOuterHTML($html) |
RegexNodeCollection | Yes | Replace entire element |
setText($text) |
RegexNodeCollection | Yes | Set text (encoded) |
append($html) |
RegexNodeCollection | Yes | Append inside element |
prepend($html) |
RegexNodeCollection | Yes | Prepend inside element |
before($html) |
RegexNodeCollection | Yes | Insert before element |
after($html) |
RegexNodeCollection | Yes | Insert after element |
remove() |
RegexNodeCollection | Yes | Remove element |
setDynamicClass($var, $class) |
RegexNodeCollection | Yes | Add template literal |
setMultipleConditionalClasses($array) |
RegexNodeCollection | Yes | Multiple conditionals |
setTemplateExpression($attr, $expr) |
RegexNodeCollection | Yes | Custom template |
convertToClassName() |
RegexNodeCollection | Yes | class → className |
each($callback) |
RegexNodeCollection | Yes | Iterate elements |
map($callback) |
Collection/array | No | Transform elements |
filter($callback) |
RegexNodeCollection | No | Filter elements |
first() |
array/null | No | Get first element |
last() |
array/null | No | Get last element |
get($index) |
array/null | No | Get by index |
getMatches() |
array | No | Get all matches |
count() |
int | No | Count elements |
isEmpty() |
bool | No | Check if empty |
isNotEmpty() |
bool | No | Check if not empty |
| Method | Returns | Description |
|---|---|---|
extractTagContent($html, $tag, $id, $type) |
string/null | Extract full element |
extractInnerContent($html, $tag, $id, $type) |
string/null | Extract inner content |
replaceTagContent($html, $tag, $id, $new, $type) |
string | Replace full element |
replaceInnerContent($html, $tag, $id, $new, $type) |
string | Replace inner content |
findAllTags($html, $tag, $id, $type) |
array | Find all matching |
removeAllTags($html, $tag, $id, $type) |
string | Remove all matching |
hasTag($html, $tag, $id, $type) |
bool | Check if exists |
extractAttributes($tag) |
array | Get all attributes |
replaceAttribute($tag, $name, $value) |
string | Replace attribute value |
setDynamicClassName($html, $tag, $id, $var, $class) |
string | Create template literal |
Note: $type parameter can be 'class' (default) or 'id'
$dom = XDOM::loadFromFile('template.html');
$dom->find('.header')->setInnerHTML('<Header />');
$dom->find('.navigation')->setInnerHTML('<Navigation />');
$dom->find('.content')->setInnerHTML('<Content />');
$dom->find('.footer')->setInnerHTML('<Footer />');
echo $dom->html();$dom = XDOM::loadFromFile('index.html');
$dom->find('.stricky-header')
->setDynamicClass('isStick', 'stricky-fixed')
->attr('data-component', 'header');
echo $dom->html();$dom->find('.nav-item')
->setDynamicClass('isActive', 'active');Result:
<li className={`nav-item ${isActive ? 'active' : ''}`}> $dom->find('.main-header')->addRef('headerRef');Before:
<header className="main-header">After:
<header ref={headerRef} className="main-header">$dom = XDOM::load($template);
$dom->find('#username')->setInnerHTML('{{user.name}}');
$dom->find('#email')->setInnerHTML('{{user.email}}');
$dom->find('#role')->setInnerHTML('{{user.role}}');
return $dom->html();$dom->find('.widget')
->addClass('active')
->addClass('featured')
->attr('data-loaded', 'true');$dom->find('#app')
->setInnerHTML('<App />')
->addClass('loaded')
->addClass('active')
->attr('data-component', 'root')
->attr('data-version', '2.0')
->prepend('<!-- App Start -->')
->append('<!-- App End -->');$element = $dom->find('#app');
if ($element->hasClass('old')) {
$element->removeClass('old')->addClass('new');
}
if (!$element->hasAttr('data-version')) {
$element->attr('data-version', '2.0');
}// Extract
$header = RegexHelper::extractTagContent($html, 'div', 'header', 'class');
// Replace
$newHtml = RegexHelper::replaceTagContent(
$html,
'div',
'header',
'<header><HeaderComponent /></header>'
);// Find nav inside header only
$nav = $dom->find('header nav');
if ($nav->isNotEmpty()) {
$nav->setInnerHTML('<Navigation items={menuItems} />');
}$dom->find('#content')
->before('<div class="wrapper">')
->after('</div>')
->addClass('inner-content');All modification methods return RegexNodeCollection, enabling powerful chaining:
// Example 1: Complete component setup
$dom->find('#app')
->setInnerHTML('<App />')
->addClass('loaded')
->addClass('active')
->attr('data-component', 'root')
->attr('data-version', '2.0')
->prepend('<!-- App Start -->')
->append('<!-- App End -->');
// Example 2: Style and configure widget
$dom->find('.widget')
->addClass('active')
->addClass('featured')
->attr('data-priority', 'high')
->setInnerHTML('<WidgetComponent />');
// Example 3: Update card styling
$dom->find('.card')
->removeClass('old-style')
->addClass('new-style')
->attr('data-updated', 'true')
->setInnerHTML('<CardComponent />');Exact Content Preservation - No HTML encoding or modification
React/Vue/JSX Support - Perfect for component templates
Template Literals - Generate dynamic className with conditionals
Template Variables - Works with {{mustache}}, {blade}, etc.
Pure Regex - Fast, lightweight, no dependencies
Method Chaining - Clean, readable code
Laravel Integration - Storage and Response helpers
No DOM Parser - Preserves exact formatting and structure
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License.
Created by devoashim
- GitHub Issues: Create an issue
- Documentation: Full API reference and examples included
If you find this package useful, please consider giving it a star on GitHub!
Remember:
- All content is preserved EXACTLY (no encoding)
- Perfect for React/Vue/Angular components
- Works seamlessly with template variables
- Pure regex - fast and lightweight
- Method chaining for clean, maintainable code
Last Updated: 2024 Version: Complete Merged Guide