devoashim / regxdomdocument
Pure regex-based DOM manipulation library with React/JSX template literal support. No DOMDocument, no XML parsing - just regex magic!
Requires
- php: ^8.0|^8.1|^8.2|^8.3
Requires (Dev)
- phpunit/phpunit: ^9.0|^10.0
README
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.
Installation
composer require devoashim/regxdomdocument
Table of Contents
- 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
Quick Start
Basic HTML Manipulation
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();
Template Literal Generation
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' : ''}`}>
Loading HTML
Returns: XDOM instance
// 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);
Finding Elements
Returns: RegexNodeCollection
// 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');
Getting Content
String Returns
// 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');
Array Returns
// 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'], // ]
Boolean & Integer Returns
// 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!"; }
Setting Content
Returns: RegexNodeCollection (chainable)
// 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>');
Attributes & Classes
Returns: RegexNodeCollection (chainable)
// 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);
Template Literals (React/JSX)
Single Conditional Class
$dom->find('.header')->setDynamicClass('isStick', 'sticky-fixed');
Result:
<div className={`header ${isStick ? 'sticky-fixed' : ''}`}>
Multiple Conditional Classes
$dom->find('.header') ->setMultipleConditionalClasses([ 'isStick' => 'sticky-fixed', 'isTransparent' => 'transparent', 'isScrolled' => 'scrolled' ]);
Result:
<div className={`header ${isStick ? 'sticky-fixed' : ''} ${isTransparent ? 'transparent' : ''} ${isScrolled ? 'scrolled' : ''}`}>
Custom Template Expression
$dom->find('.button') ->setTemplateExpression('className', 'btn ${type === "primary" ? "btn-primary" : "btn-secondary"}');
Result:
<button className={`btn ${type === "primary" ? "btn-primary" : "btn-secondary"}`}>
Convert class to className
$dom->find('div')->convertToClassName();
Before: <div class="header">
After: <div className="header">
Removing Elements
Returns: RegexNodeCollection (chainable)
// Remove elements from DOM $dom->find('.old-widget')->remove(); $dom->find('#deprecated')->remove();
Iteration & Filtering
Returns: RegexNodeCollection (chainable) or transformed data
// 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
Saving & Output
Various Return Types
// 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/html
Advanced Usage - RegexHelper
For direct regex operations without the fluent interface:
use devoashim\regxdomdocument\RegexHelper;
Extract Content
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>"
Replace Content
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 />' );
Find Multiple Elements
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"; }
Remove Elements
Returns: string
// Remove all matching elements $cleanHtml = RegexHelper::removeAllTags($html, 'div', 'ads', 'class');
Check Existence
Returns: bool
// Check if element exists if (RegexHelper::hasTag($html, 'div', 'header', 'class')) { echo "Header exists!"; }
Attribute Helpers
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">'
Template Literal Generation
Returns: string
// Convert to template literal $reactHtml = RegexHelper::setDynamicClassName( $html, 'div', 'header', 'isStick', 'sticky-fixed' );
Method Reference
XDOM Methods
| 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 |
RegexNodeCollection Methods
| 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 |
RegexHelper Static Methods
| 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'
Use Cases
1. React Component Conversion
$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();
2. Sticky Header with Template Literal
$dom = XDOM::loadFromFile('index.html'); $dom->find('.stricky-header') ->setDynamicClass('isStick', 'stricky-fixed') ->attr('data-component', 'header'); echo $dom->html();
3. Dynamic Navigation
$dom->find('.nav-item') ->setDynamicClass('isActive', 'active');
Result:
<li className={`nav-item ${isActive ? 'active' : ''}`}>
Add ref attribute to element's opening tag
$dom->find('.main-header')->addRef('headerRef');
Before:
<header className="main-header">
After:
<header ref={headerRef} className="main-header">
4. Template Variable Injection
$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();
5. Batch Widget Processing
$dom->find('.widget') ->addClass('active') ->addClass('featured') ->attr('data-loaded', 'true');
6. Complex Component Building
$dom->find('#app') ->setInnerHTML('<App />') ->addClass('loaded') ->addClass('active') ->attr('data-component', 'root') ->attr('data-version', '2.0') ->prepend('<!-- App Start -->') ->append('<!-- App End -->');
7. Conditional Modifications
$element = $dom->find('#app'); if ($element->hasClass('old')) { $element->removeClass('old')->addClass('new'); } if (!$element->hasAttr('data-version')) { $element->attr('data-version', '2.0'); }
8. Extract and Replace Sections
// Extract $header = RegexHelper::extractTagContent($html, 'div', 'header', 'class'); // Replace $newHtml = RegexHelper::replaceTagContent( $html, 'div', 'header', '<header><HeaderComponent /></header>' );
9. Navigation Menu Processing
// Find nav inside header only $nav = $dom->find('header nav'); if ($nav->isNotEmpty()) { $nav->setInnerHTML('<Navigation items={menuItems} />'); }
10. Multi-Step Content Wrapping
$dom->find('#content') ->before('<div class="wrapper">') ->after('</div>') ->addClass('inner-content');
Method Chaining Examples
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 />');
Key Features
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
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License.
Credits
Created by devoashim
Support
- GitHub Issues: Create an issue
- Documentation: Full API reference and examples included
⭐ Star Us!
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