haphpiness

These are things in PHP which make me genuinely_happy();

Named Capture Groups in preg_match

PHP's preg_match supports PCRE named capture groups: (?P<name>...). Instead of juggling numeric match indices, your captures become a named map.

// Parse an ISO 8601 date without counting brackets
$pattern = '/^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$/';

preg_match($pattern, '2024-03-15', $matches);

echo $matches['year'];   // "2024"
echo $matches['month'];  // "03"
echo $matches['day'];    // "15"

// Numeric indices still work too — your choice
echo $matches[1];  // "2024"

Named groups shine in complex patterns where positional counting becomes error-prone:

// Parse a log line
$log = '[2024-03-15 14:32:01] production.ERROR: Connection refused {"host":"db1"}';

preg_match(
    '/^\[(?P<date>[^\]]+)\]\s+(?P<channel>\w+)\.(?P<level>\w+):\s+(?P<message>[^{]+)/',
    $log,
    $m
);

echo $m['date'];     // "2024-03-15 14:32:01"
echo $m['channel'];  // "production"
echo $m['level'];    // "ERROR"
echo trim($m['message']); // "Connection refused"

Named groups also work with preg_replace_callback, preg_match_all, and preg_replace using ${name} syntax in replacement strings.

Significance: Maintainability

Numbered capture groups are an implementation detail that leaks into your calling code. Named groups make regex patterns self-documenting — the name describes what the group captures, and your code reads the intent rather than $m[3].