Skip to content

Instantly share code, notes, and snippets.

@josephscott
Created March 16, 2026 20:21
Show Gist options
  • Select an option

  • Save josephscott/254521a43dee3300448ef288dd2ea1f2 to your computer and use it in GitHub Desktop.

Select an option

Save josephscott/254521a43dee3300448ef288dd2ea1f2 to your computer and use it in GitHub Desktop.
Gutenberg: prepend_to_selector() comparison
<?php
/**
* Benchmark: prepend_to_selector() implementations.
*
* Usage: php bench-prepend-to-selector.php
*/
$data = array(
'single class selector' => array(
'selector' => '.inner',
'to_prepend' => '.wrapper ',
'expected' => '.wrapper .inner',
),
'single element selector' => array(
'selector' => 'p',
'to_prepend' => '.wrapper ',
'expected' => '.wrapper p',
),
'single id selector' => array(
'selector' => '#main',
'to_prepend' => '.wrapper ',
'expected' => '.wrapper #main',
),
'two comma-separated selectors without spaces' => array(
'selector' => 'h1,h2',
'to_prepend' => '.wrapper ',
'expected' => '.wrapper h1,.wrapper h2',
),
'three comma-separated selectors without spaces' => array(
'selector' => 'h1,h2,h3',
'to_prepend' => '.some-class ',
'expected' => '.some-class h1,.some-class h2,.some-class h3',
),
'comma-separated class selectors without spaces' => array(
'selector' => '.foo,.bar',
'to_prepend' => '.prefix ',
'expected' => '.prefix .foo,.prefix .bar',
),
'prepend without trailing space' => array(
'selector' => '.child',
'to_prepend' => '.parent',
'expected' => '.parent.child',
),
'compound selector without trailing space no comma spaces' => array(
'selector' => '.a,.b',
'to_prepend' => '.parent',
'expected' => '.parent.a,.parent.b',
),
'descendant selector prepended' => array(
'selector' => '.block .inner',
'to_prepend' => '.scope ',
'expected' => '.scope .block .inner',
),
'descendant selectors comma-separated without spaces' => array(
'selector' => '.block .inner,.block .alt',
'to_prepend' => '.scope ',
'expected' => '.scope .block .inner,.scope .block .alt',
),
'empty selector' => array(
'selector' => '',
'to_prepend' => '.prefix ',
'expected' => '.prefix ',
),
'empty prepend' => array(
'selector' => '.child',
'to_prepend' => '',
'expected' => '.child',
),
'both empty' => array(
'selector' => '',
'to_prepend' => '',
'expected' => '',
),
'attribute selector' => array(
'selector' => '[data-type="example"]',
'to_prepend' => '.scope ',
'expected' => '.scope [data-type="example"]',
),
'pseudo-class selector' => array(
'selector' => ':where(.is-layout-flex)',
'to_prepend' => '.editor ',
'expected' => '.editor :where(.is-layout-flex)',
),
'many comma-separated selectors without spaces' => array(
'selector' => 'h1,h2,h3,h4,h5,h6',
'to_prepend' => '.content ',
'expected' => '.content h1,.content h2,.content h3,.content h4,.content h5,.content h6',
),
'real world block element selector' => array(
'selector' => 'p',
'to_prepend' => '.wp-block-group ',
'expected' => '.wp-block-group p',
),
'real world compound block element selectors' => array(
'selector' => 'a,.wp-element-button',
'to_prepend' => '.wp-block-group ',
'expected' => '.wp-block-group a,.wp-block-group .wp-element-button',
),
'spaces after commas are preserved' => array(
'selector' => 'h1, h2, h3',
'to_prepend' => '.some-class ',
'expected' => '.some-class h1,.some-class h2,.some-class h3',
),
'spaces after commas preserved with class selectors' => array(
'selector' => '.foo, .bar',
'to_prepend' => '.prefix ',
'expected' => '.prefix .foo,.prefix .bar',
),
'mixed whitespace around commas preserved' => array(
'selector' => '.a , .b , .c',
'to_prepend' => '.pre ',
'expected' => '.pre .a ,.pre .b ,.pre .c',
),
);
function prepend_current( string $selector, string $to_prepend ): string {
if ( ! str_contains( $selector, ',' ) ) {
return $to_prepend . $selector;
}
$new_selectors = array();
$selectors = explode( ',', $selector );
foreach ( $selectors as $sel ) {
$new_selectors[] = $to_prepend . $sel;
}
return implode( ',', $new_selectors );
}
function prepend_new( string $selector, string $to_prepend ): string {
if ( ! str_contains( $selector, ',' ) ) {
return $to_prepend . $selector;
}
return $to_prepend . str_replace( ',', ',' . $to_prepend, $selector );
}
$loops = 200;
$case_count = count( $data );
// --- Correctness check ---
$mismatches = 0;
foreach ( $data as $label => $case ) {
$current_result = prepend_current( $case['selector'], $case['to_prepend'] );
$new_result = prepend_new( $case['selector'], $case['to_prepend'] );
if ( $current_result !== $case['expected'] ) {
echo "FAIL [current] $label\n";
echo " expected: {$case['expected']}\n";
echo " got: $current_result\n";
++$mismatches;
}
if ( $new_result !== $case['expected'] ) {
echo "FAIL [new] $label\n";
echo " expected: {$case['expected']}\n";
echo " got: $new_result\n";
++$mismatches;
}
}
if ( $mismatches > 0 ) {
echo "\n$mismatches correctness failure(s) - aborting benchmark.\n";
exit( 1 );
}
echo "All $case_count cases produce identical results. Running benchmark...\n\n";
// --- Benchmark: current implementation ---
$start = hrtime( true );
for ( $i = 0; $i < $loops; $i++ ) {
foreach ( $data as $case ) {
prepend_current( $case['selector'], $case['to_prepend'] );
}
}
$time_current = hrtime( true ) - $start;
// --- Benchmark: new implementation ---
$start = hrtime( true );
for ( $i = 0; $i < $loops; $i++ ) {
foreach ( $data as $case ) {
prepend_new( $case['selector'], $case['to_prepend'] );
}
}
$time_new = hrtime( true ) - $start;
// --- Results ---
$total_calls = $loops * $case_count;
printf( "Total calls per implementation: %s\n", number_format( $total_calls ) );
printf( "Loops: %d x %d cases\n\n", $loops, $case_count );
$time_current_ms = $time_current / 1e6;
$time_new_ms = $time_new / 1e6;
printf( "Current : %.4f ms\n", $time_current_ms );
printf( "New : %.4f ms\n", $time_new_ms );
$diff_ms = $time_current_ms - $time_new_ms;
$diff_pct = ( $diff_ms / $time_current_ms ) * 100;
$direction = $diff_ms > 0 ? 'faster' : 'slower';
printf( "\nDifference: %.4f ms (%.2f%% %s)\n", abs( $diff_ms ), abs( $diff_pct ), $direction );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment