Html::getAutoId()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Plaisio\Helper;
5
6
use SetBased\Helper\Cast;
7
8
/**
9
 * A utility class for generating HTML elements, tags, and attributes.
10
 */
11
class Html
12
{
13
  //--------------------------------------------------------------------------------------------------------------------
14
  /**
15
   * The encoding of the generated HTML code.
16
   *
17
   * @var string
18
   *
19
   * @since 1.0.0
20
   * @api
21
   */
22
  public static $encoding = 'UTF-8';
23
24
  /**
25
   * Counter for generating unique element IDs.
26
   *
27
   * @var int
28
   */
29
  private static $autoId = 0;
30
31
  /**
32
   * Map from (some) unicode characters to ASCII characters.
33
   *
34
   * @var array
35
   */
36
  private static $trans = ['ß' => 'sz',
37
                           'à' => 'a',
38
                           'á' => 'a',
39
                           'â' => 'a',
40
                           'ã' => 'a',
41
                           'ä' => 'a',
42
                           'å' => 'a',
43
                           'æ' => 'ae',
44
                           'ç' => 'c',
45
                           'è' => 'e',
46
                           'é' => 'e',
47
                           'ê' => 'e',
48
                           'ë' => 'e',
49
                           'ì' => 'i',
50
                           'í' => 'i',
51
                           'î' => 'i',
52
                           'ï' => 'i',
53
                           'ð' => 'e',
54
                           'ñ' => 'n',
55
                           'ò' => 'o',
56
                           'ó' => 'o',
57
                           'ô' => 'o',
58
                           'õ' => 'o',
59
                           'ö' => 'o',
60
                           '÷' => 'x',
61
                           'ø' => 'o',
62
                           'ù' => 'u',
63
                           'ú' => 'u',
64
                           'û' => 'u',
65
                           'ü' => 'u',
66
                           'ý' => 'y',
67
                           'þ' => 'b',
68
                           'ÿ' => 'y',
69
                           'č' => 'c',
70
                           'ł' => 'l',
71
                           'š' => 's',
72
                           'ů' => 'u',
73
                           'ž' => 'z',
74
                           'а' => 'a',
75
                           'б' => 'b',
76
                           'в' => 'v',
77
                           'г' => 'g',
78
                           'д' => 'd',
79
                           'е' => 'e',
80
                           'ж' => 'zh',
81
                           'з' => 'z',
82
                           'и' => 'i',
83
                           'й' => 'i',
84
                           'к' => 'k',
85
                           'л' => 'l',
86
                           'м' => 'm',
87
                           'н' => 'n',
88
                           'о' => 'o',
89
                           'п' => 'p',
90
                           'р' => 'r',
91
                           'с' => 's',
92
                           'т' => 't',
93
                           'у' => 'u',
94
                           'ф' => 'f',
95
                           'х' => 'kh',
96
                           'ц' => 'ts',
97
                           'ч' => 'ch',
98
                           'ш' => 'sh',
99
                           'щ' => 'shch',
100
                           'ъ' => '',
101
                           'ы' => 'y',
102
                           'ь' => '',
103
                           'э' => 'e',
104
                           'ю' => 'iu',
105
                           'я' => 'ia',
106
                           'ё' => 'e'];
107
108
  //--------------------------------------------------------------------------------------------------------------------
109
  /**
110
   * Returns a string with proper conversion of special characters to HTML entities of an attribute of a HTML tag.
111
   *
112
   * Boolean attributes (e.g. checked, disabled and draggable, autocomplete also) are set when the value is none empty.
113
   *
114
   * @param string $name  The name of the attribute.
115
   * @param mixed  $value The value of the attribute.
116
   *
117
   * @return string
118
   *
119
   * @since 1.0.0
120
   * @api
121
   */
122 27
  public static function generateAttribute(string $name, $value): string
123
  {
124 27
    $html = '';
125
126
    switch ($name)
127
    {
128
      // Boolean attributes.
129 27
      case 'autofocus':
130 27
      case 'checked':
131 27
      case 'disabled':
132 27
      case 'hidden':
133 27
      case 'ismap':
134 27
      case 'multiple':
135 27
      case 'novalidate':
136 27
      case 'readonly':
137 27
      case 'required':
138 27
      case 'selected':
139 27
      case 'spellcheck':
140 3
        if (!empty($value))
141
        {
142 2
          $html = ' ';
143 2
          $html .= $name;
144 2
          $html .= '="';
145 2
          $html .= $name;
146 2
          $html .= '"';
147
        }
148 3
        break;
149
150
      // Annoying boolean attribute exceptions.
151 25
      case 'draggable':
152 25
      case 'contenteditable':
153 3
        if ($value!==null)
154
        {
155 3
          $html = ' ';
156 3
          $html .= $name;
157 3
          $html .= (!empty($value)) ? '="true"' : '="false"';
158
        }
159 3
        break;
160
161 23
      case 'autocomplete':
162 2
        if ($value!==null)
163
        {
164 2
          $html = ' ';
165 2
          $html .= $name;
166 2
          $html .= (!empty($value)) ? '="on"' : '="off"';
167
        }
168 2
        break;
169
170 21
      case 'translate':
171 3
        if ($value!==null)
172
        {
173 3
          $html = ' ';
174 3
          $html .= $name;
175 3
          $html .= (!empty($value)) ? '="yes"' : '="no"';
176
        }
177 3
        break;
178
179 19
      case 'class' and is_array($value):
180 3
        $classes = implode(' ', self::cleanClasses($value));
181 3
        if ($classes!=='')
182
        {
183 2
          $html = ' class="';
184 2
          $html .= htmlspecialchars($classes, ENT_QUOTES, self::$encoding);
185 2
          $html .= '"';
186
        }
187 3
        break;
188
189
      default:
190 16
        if ($value!==null && $value!=='')
191
        {
192 13
          $html = ' ';
193 13
          $html .= htmlspecialchars($name, ENT_QUOTES, self::$encoding);
194 13
          $html .= '="';
195 13
          $html .= htmlspecialchars(Cast::toManString($value), ENT_QUOTES, self::$encoding);
196 13
          $html .= '"';
197
        }
198 16
        break;
199
    }
200
201 27
    return $html;
202
  }
203
204
  //--------------------------------------------------------------------------------------------------------------------
205
  /**
206
   * Generates HTML code for an element.
207
   *
208
   * Note: tags for void elements such as '<br/>' are not supported.
209
   *
210
   * @param string      $tagName    The name of the tag, e.g. a, form.
211
   * @param array       $attributes The attributes of the tag. Special characters in the attributes will be replaced
212
   *                                with HTML entities.
213
   * @param string|null $innerText  The inner text of the tag.
214
   * @param bool        $isHtml     If set the inner text is a HTML snippet, otherwise special characters in the inner
215
   *                                text will be replaced with HTML entities.
216
   *
217
   * @return string
218
   *
219
   * @since 1.0.0
220
   * @api
221
   */
222 12
  public static function generateElement(string $tagName,
223
                                         array $attributes = [],
224
                                         ?string $innerText = '',
225
                                         bool $isHtml = false): string
226
  {
227 12
    $html = self::generateTag($tagName, $attributes);
228 12
    $html .= ($isHtml) ? $innerText : self::txt2Html($innerText);
229 12
    $html .= '</';
230 12
    $html .= $tagName;
231 12
    $html .= '>';
232
233 12
    return $html;
234
  }
235
236
  //--------------------------------------------------------------------------------------------------------------------
237
  /**
238
   * Generates HTML code for a start tag of an element.
239
   *
240
   * @param string $tagName    The name of the tag, e.g. a, form.
241
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
242
   *                           HTML entities.
243
   *
244
   * @return string
245
   *
246
   * @since 1.0.0
247
   * @api
248
   */
249 12
  public static function generateTag(string $tagName, array $attributes = []): string
250
  {
251 12
    $html = '<';
252 12
    $html .= $tagName;
253 12
    foreach ($attributes as $name => $value)
254
    {
255
      // Ignore attributes with leading underscore.
256 10
      if (strpos($name, '_')!==0) $html .= self::generateAttribute($name, $value);
257
    }
258 12
    $html .= '>';
259
260 12
    return $html;
261
  }
262
263
  //--------------------------------------------------------------------------------------------------------------------
264
  /**
265
   * Generates HTML code for a void element.
266
   *
267
   * Void elements are: area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track,
268
   * wbr. See <http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements>
269
   *
270
   * @param string $tagName    The name of the tag, e.g. img, link.
271
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
272
   *                           HTML entities.
273
   *
274
   * @return string
275
   *
276
   * @since 1.0.0
277
   * @api
278
   */
279 2
  public static function generateVoidElement(string $tagName, array $attributes = []): string
280
  {
281 2
    $html = '<';
282 2
    $html .= $tagName;
283 2
    foreach ($attributes as $name => $value)
284
    {
285
      // Ignore attributes with leading underscore.
286 2
      if (strpos($name, '_')!==0) $html .= self::generateAttribute($name, $value);
287
    }
288 2
    $html .= '/>';
289
290 2
    return $html;
291
  }
292
293
  //--------------------------------------------------------------------------------------------------------------------
294
  /**
295
   * Returns a string that can be safely used as an ID for an element. The format of the id is 'abc_<n>' where n is
296
   * incremented with each call of this method.
297
   *
298
   * @return string
299
   *
300
   * @since 1.0.0
301
   * @api
302
   */
303 1
  public static function getAutoId(): string
304
  {
305 1
    self::$autoId++;
306
307 1
    return 'abc_'.self::$autoId;
308
  }
309
310
  //--------------------------------------------------------------------------------------------------------------------
311
  /**
312
   * Returns a string with special characters converted to HTML entities.
313
   * This method is a wrapper around [htmlspecialchars](http://php.net/manual/en/function.htmlspecialchars.php).
314
   *
315
   * @param string|null $string The string with optionally special characters.
316
   *
317
   * @return string
318
   *
319
   * @since 1.0.0
320
   * @api
321
   */
322 12
  public static function txt2Html(?string $string): string
323
  {
324 12
    if ($string===null) return '';
325
326 12
    return htmlspecialchars($string, ENT_QUOTES, self::$encoding);
327
  }
328
329
  //--------------------------------------------------------------------------------------------------------------------
330
  /**
331
   * Returns the slug of a string that can be safely used in an URL.
332
   *
333
   * @param string|null $string The string.
334
   *
335
   * @return string
336
   *
337
   * @since 1.1.0
338
   * @api
339
   */
340 1
  public static function txt2Slug(?string $string): string
341
  {
342 1
    if ($string===null) return '';
343
344 1
    return trim(preg_replace('/[^0-9a-z]+/', '-', strtr(mb_strtolower($string), self::$trans)), '-');
345
  }
346
347
  //--------------------------------------------------------------------------------------------------------------------
348
  /**
349
   * Removes empty and duplicate classes from an array with classes.
350
   *
351
   * @param array $classes The classes.
352
   *
353
   * @return array
354
   */
355 3
  private static function cleanClasses(array $classes): array
356
  {
357 3
    $ret = [];
358
359 3
    foreach ($classes as $class)
360
    {
361 2
      $tmp = Cast::toManString($class, '');
362 2
      if ($tmp!=='') $ret[] = $tmp;
363
    }
364
365 3
    return array_unique($ret);
366
  }
367
368
  //--------------------------------------------------------------------------------------------------------------------
369
}
370
371
//----------------------------------------------------------------------------------------------------------------------
372