Skip to content

Commit 5af664d

Browse files
committed
refactor: optimize swatch injection method using DOM manipulation
- Replaced regex-based label processing with DOMDocument for improved reliability and maintainability. - Enhanced the handling of color swatches by ensuring valid color data and preserving original label text. - Improved overall code structure for better readability and future modifications.
1 parent 6df047c commit 5af664d

File tree

1 file changed

+107
-76
lines changed

1 file changed

+107
-76
lines changed

includes/WooCommerce/class-color-block-swatch-manager.php

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -103,85 +103,116 @@ public function inject_color_swatches_in_block( string $block_content, array $bl
103103
* @return string Modified block content.
104104
*/
105105
private function inject_swatches_with_html_processor( string $block_content ): string {
106-
// Use regex to find and replace label elements with color swatches.
107-
$pattern = '/<label[^>]*class="[^"]*wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill[^"]*"[^>]*>(.*?)<\/label>/s';
108-
109-
$result = preg_replace_callback(
110-
$pattern,
111-
function ( $matches ) {
112-
$label_content = $matches[0];
113-
$inner_content = $matches[1];
114-
115-
// Extract the color value from the input.
116-
if ( preg_match( '/name="attribute_pa_color"\s+value="([^"]*)"/', $inner_content, $input_matches ) ) {
117-
$color_slug = $input_matches[1];
118-
119-
// Get color term and value.
120-
$color_term = get_term_by( 'slug', $color_slug, 'pa_color' );
121-
122-
if ( $color_term ) {
123-
$color_data = $this->get_color_display_data_for_term( $color_term );
124-
125-
if ( $color_data['is_valid'] && ! empty( $color_data['value'] ) ) {
126-
// Create accessible swatch HTML.
127-
$aria_label = sprintf(
128-
/* translators: %s: color name */
129-
__( 'Color option: %s', 'aggressive-apparel' ),
130-
$color_term->name
131-
);
132-
133-
$classes = 'aggressive-apparel-color-swatch aggressive-apparel-color-swatch--interactive';
134-
if ( $this->show_label ) {
135-
$classes .= ' aggressive-apparel-color-swatch--with-label';
136-
}
137-
138-
// Different styling based on color type.
139-
if ( 'pattern' === $color_data['type'] ) {
140-
$classes .= ' aggressive-apparel-color-swatch--pattern';
141-
$background_style = 'background-image: url(\'' . esc_url( $color_data['value'] ) . '\');';
142-
$data_attributes = 'data-pattern-url="' . esc_attr( $color_data['value'] ) . '"';
143-
} else {
144-
$background_style = 'background-color: ' . esc_attr( $color_data['value'] ) . ';';
145-
$data_attributes = 'data-color="' . esc_attr( $color_data['value'] ) . '"';
146-
}
147-
148-
$swatch_html = '<span class="' . esc_attr( $classes ) . ' aggressive-apparel-color-swatch__circle" ' .
149-
'style="' . $background_style . '" ' .
150-
'aria-label="' . esc_attr( $aria_label ) . '" ' .
151-
'role="img" ' .
152-
'tabindex="0" ' .
153-
'title="' . esc_attr( $color_term->name ) . '" ' .
154-
$data_attributes . ' ' .
155-
'data-color-name="' . esc_attr( $color_term->name ) . '">' .
156-
'</span>';
157-
158-
// Modify the label content based on show_label setting.
159-
if ( ! $this->show_label ) {
160-
// Replace content after input tag with swatch.
161-
$modified_content = preg_replace( '/(<input[^>]*>)(.*?)<\/label>$/s', '$1' . $swatch_html . '</label>', $label_content );
162-
} else {
163-
// Insert swatch after input tag, keep text.
164-
$modified_content = preg_replace( '/(<input[^>]*>)/', '$1' . $swatch_html, $label_content, 1 );
165-
}
166-
167-
// Ensure we have valid content.
168-
if ( null === $modified_content ) {
169-
$modified_content = $label_content;
170-
}
171-
172-
return $modified_content;
173-
}
174-
}
106+
$dom = new \DOMDocument();
107+
$loaded = $dom->loadHTML(
108+
$block_content,
109+
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
110+
);
111+
112+
if ( ! $loaded ) {
113+
return $block_content;
114+
}
115+
116+
$labels = $dom->getElementsByTagName( 'label' );
117+
$modified = false;
118+
119+
// NodeList is live, so copy to array before mutations.
120+
$label_nodes = array();
121+
foreach ( $labels as $label ) {
122+
$label_nodes[] = $label;
123+
}
124+
125+
foreach ( $label_nodes as $label ) {
126+
$class_attr = $label->getAttribute( 'class' );
127+
if ( false === strpos( $class_attr, 'wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill' ) ) {
128+
continue;
129+
}
130+
131+
$inputs = $label->getElementsByTagName( 'input' );
132+
if ( 0 === $inputs->length ) {
133+
continue;
134+
}
135+
136+
$target_input = null;
137+
foreach ( $inputs as $input ) {
138+
if ( 'attribute_pa_color' === $input->getAttribute( 'name' ) ) {
139+
$target_input = $input;
140+
break;
175141
}
142+
}
176143

177-
// Return unchanged if no color processing needed.
178-
return $label_content;
179-
},
180-
$block_content
181-
);
144+
if ( ! $target_input ) {
145+
continue;
146+
}
147+
148+
$color_slug = $target_input->getAttribute( 'value' );
149+
if ( ! $color_slug ) {
150+
continue;
151+
}
152+
153+
$color_term = get_term_by( 'slug', $color_slug, 'pa_color' );
154+
if ( ! $color_term ) {
155+
continue;
156+
}
157+
158+
$color_data = $this->get_color_display_data_for_term( $color_term );
159+
if ( ! $color_data['is_valid'] || empty( $color_data['value'] ) ) {
160+
continue;
161+
}
162+
163+
$aria_label = sprintf(
164+
/* translators: %s: color name */
165+
__( 'Color option: %s', 'aggressive-apparel' ),
166+
$color_term->name
167+
);
168+
169+
$classes = 'aggressive-apparel-color-swatch aggressive-apparel-color-swatch--interactive';
170+
if ( $this->show_label ) {
171+
$classes .= ' aggressive-apparel-color-swatch--with-label';
172+
}
173+
174+
$swatch = $dom->createElement( 'span' );
175+
$swatch->setAttribute( 'class', $classes . ' aggressive-apparel-color-swatch__circle' );
176+
$swatch->setAttribute( 'aria-label', $aria_label );
177+
$swatch->setAttribute( 'role', 'img' );
178+
$swatch->setAttribute( 'tabindex', '0' );
179+
$swatch->setAttribute( 'title', $color_term->name );
180+
$swatch->setAttribute( 'data-color-name', $color_term->name );
181+
182+
if ( 'pattern' === $color_data['type'] ) {
183+
$swatch->setAttribute( 'data-pattern-url', $color_data['value'] );
184+
$swatch->setAttribute( 'style', 'background-image: url(' . esc_url( $color_data['value'] ) . ');' );
185+
$swatch->setAttribute( 'class', $swatch->getAttribute( 'class' ) . ' aggressive-apparel-color-swatch--pattern' );
186+
} else {
187+
$swatch->setAttribute( 'data-color', $color_data['value'] );
188+
$swatch->setAttribute( 'style', 'background-color: ' . esc_attr( $color_data['value'] ) . ';' );
189+
}
190+
191+
// Preserve original label text (trimmed) to re-append when show_label is true.
192+
$label_text = trim( $label->textContent ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
193+
194+
// Rebuild label content: input + swatch (+ text when configured).
195+
while ( $label->firstChild ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
196+
$label->removeChild( $label->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
197+
}
198+
199+
$label->appendChild( $target_input->cloneNode( true ) );
200+
$label->appendChild( $swatch );
201+
202+
if ( $this->show_label && '' !== $label_text ) {
203+
$label->appendChild( $dom->createTextNode( $label_text ) );
204+
}
205+
206+
$modified = true;
207+
}
208+
209+
if ( ! $modified ) {
210+
return $block_content;
211+
}
212+
213+
$output = $dom->saveHTML();
182214

183-
// Ensure we always return a string, even if preg_replace_callback fails.
184-
return ( null !== $result ) ? $result : $block_content;
215+
return $output ? $output : $block_content;
185216
}
186217

187218
/**

0 commit comments

Comments
 (0)