Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
NameFormatModifiers
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
4 / 4
9
100.00% covered (success)
100.00%
1 / 1
 apply
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 stripSpanWrapper
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 modifierHandlers
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 applySingleModifier
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\name\Utility;
6
7use Drupal\Component\Utility\Html;
8use Drupal\Component\Utility\Unicode;
9
10/**
11 * Applies string-transformation modifiers to a single token value.
12 *
13 * Modifier characters are L, U, F, G, T, S, B, and b.
14 * When the value is wrapped in a single outer <span>, the span is
15 * temporarily removed, the modifiers are applied to the inner text, and
16 * the span is re-applied to preserve markup-mode wrappers.
17 *
18 * @internal
19 */
20final class NameFormatModifiers {
21
22  /**
23   * Default word-boundary pattern used by the B/b modifiers.
24   */
25  const BOUNDARY_REGEXP = '/[\b,\s]/';
26
27  /**
28   * Applies case, trim, escape, and word-split modifiers to a string.
29   *
30   * @param string $string
31   *   The value to modify.
32   * @param string $modifiers
33   *   A sequence of modifier characters (L, U, F, G, T, S, B, b).
34   * @param string $boundary_regexp
35   *   Word-boundary pattern used by B and b.
36   *
37   * @return string
38   *   The modified string.
39   */
40  public static function apply(
41    string $string,
42    string $modifiers,
43    string $boundary_regexp = self::BOUNDARY_REGEXP,
44  ): string {
45    $string_is_empty = (!strlen($string) || !$modifiers);
46    if ($string_is_empty) {
47      return $string;
48    }
49
50    [$prefix, $string, $suffix] = self::stripSpanWrapper($string);
51    $handlers                   = self::modifierHandlers($boundary_regexp);
52
53    for ($j = 0; $j < strlen($modifiers); $j += 1) {
54      $string = self::applySingleModifier($string, $modifiers[$j], $handlers);
55    }
56
57    return $prefix . $string . $suffix;
58  }
59
60  /**
61   * Extracts a single wrapping span and returns wrapper + core string.
62   *
63   * @return array{0: string, 1: string, 2: string}
64   *   Prefix, core value, and suffix.
65   */
66  private static function stripSpanWrapper(string $string): array {
67    if (preg_match('/^(<span[^>]*>)(.*)(<\/span>)$/i', $string, $matches)) {
68      return [$matches[1], $matches[2], $matches[3]];
69    }
70
71    return ['', $string, ''];
72  }
73
74  /**
75   * Returns handlers for each supported modifier character.
76   *
77   * @param string $boundary_regexp
78   *   Word-boundary pattern used by B and b.
79   *
80   * @return array<string, \Closure>
81   *   Modifier handlers keyed by modifier character.
82   */
83  private static function modifierHandlers(string $boundary_regexp): array {
84    return [
85      'L' => static fn (string $value): string => mb_strtolower($value),
86      'U' => static fn (string $value): string => mb_strtoupper($value),
87      'F' => static fn (string $value): string => Unicode::ucfirst($value),
88      'G' => static fn (string $value): string => Unicode::ucwords($value),
89      'T' => static fn (string $value): string => trim(preg_replace('/\s+/', ' ', $value)),
90      'S' => static fn (string $value): string => Html::escape($value),
91      'B' => static function (string $value) use ($boundary_regexp): string {
92        $parts = preg_split($boundary_regexp, $value);
93        return (string) array_shift($parts);
94      },
95      'b' => static function (string $value) use ($boundary_regexp): string {
96        $parts = preg_split($boundary_regexp, $value);
97        return (string) array_pop($parts);
98      },
99    ];
100  }
101
102  /**
103   * Applies one modifier character to the provided string.
104   *
105   * @param string $string
106   *   The value to modify.
107   * @param string $modifier
108   *   A single modifier character (L, U, F, G, T, S, B, b).
109   * @param array<string, \Closure> $handlers
110   *   Modifier handlers keyed by modifier character.
111   *
112   * @return string
113   *   The modified string, or the original when the modifier is unknown.
114   */
115  private static function applySingleModifier(
116    string $string,
117    string $modifier,
118    array $handlers,
119  ): string {
120    if (!isset($handlers[$modifier])) {
121      return $string;
122    }
123
124    return $handlers[$modifier]($string);
125  }
126
127}