Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 80a5ff...96a6de )
by Der Mundschenk
20s
created

Settings::array_map_assoc()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 2
1
<?php
2
/**
3
 *  This file is part of PHP-Typography.
4
 *
5
 *  Copyright 2014-2017 Peter Putzer.
6
 *  Copyright 2009-2011 KINGdesk, LLC.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License along
19
 *  with this program; if not, write to the Free Software Foundation, Inc.,
20
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
 *
22
 *  ***
23
 *
24
 *  @package mundschenk-at/php-typography
25
 *  @license http://www.gnu.org/licenses/gpl-2.0.html
26
 */
27
28
namespace PHP_Typography;
29
30
use PHP_Typography\Settings\Dash_Style;
31
use PHP_Typography\Settings\Quote_Style;
32
33
/**
34
 * Store settings for the PHP_Typography class.
35
 *
36
 * @author Peter Putzer <[email protected]>
37
 *
38
 * @since 4.0.0
39
 */
40
class Settings implements \ArrayAccess, \JsonSerializable {
41
42
	/**
43
	 * The current no-break narrow space character.
44
	 *
45
	 * @var string
46
	 */
47
	protected $no_break_narrow_space;
48
49
	/**
50
	 * Primary quote style.
51
	 *
52
	 * @var Settings\Quotes
53
	 */
54
	protected $primary_quote_style;
55
56
	/**
57
	 * Secondary quote style.
58
	 *
59
	 * @var Settings\Quotes
60
	 */
61
	protected $secondary_quote_style;
62
63
	/**
64
	 * A regex pattern for custom units (or the empty string).
65
	 *
66
	 * @var string
67
	 */
68
	protected $custom_units = '';
69
70
	/**
71
	 * A hashmap of settings for the various typographic options.
72
	 *
73
	 * @var array
74
	 */
75
	protected $data = [];
76
77
	/**
78
	 * The current dash style.
79
	 *
80
	 * @var Settings\Dashes
81
	 */
82
	protected $dash_style;
83
84
	/**
85
	 * Sets up a new Settings object.
86
	 *
87
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
88
	 */
89
	public function __construct( $set_defaults = true ) {
90
		$this->init( $set_defaults );
91
	}
92
93
	/**
94
	 * Provides access to named settings (object syntax).
95
	 *
96
	 * @param string $key The settings key.
97
	 *
98
	 * @return mixed
99
	 */
100
	public function &__get( $key ) {
101
		return $this->data[ $key ];
102
	}
103
104
	/**
105
	 * Changes a named setting (object syntax).
106
	 *
107
	 * @param string $key   The settings key.
108
	 * @param mixed  $value The settings value.
109
	 */
110
	public function __set( $key, $value ) {
111
		$this->data[ $key ] = $value;
112
	}
113
114
	/**
115
	 * Checks if a named setting exists (object syntax).
116
	 *
117
	 * @param string $key The settings key.
118
	 */
119
	public function __isset( $key ) {
120
		return isset( $this->data[ $key ] );
121
	}
122
123
	/**
124
	 * Unsets a named setting.
125
	 *
126
	 * @param string $key The settings key.
127
	 */
128
	public function __unset( $key ) {
129
		unset( $this->data[ $key ] );
130
	}
131
132
	/**
133
	 * Changes a named setting (array syntax).
134
	 *
135
	 * @param string $offset The settings key.
136
	 * @param mixed  $value  The settings value.
137
	 */
138
	public function offsetSet( $offset, $value ) {
139
		if ( ! empty( $offset ) ) {
140
			$this->data[ $offset ] = $value;
141
		}
142
	}
143
144
	/**
145
	 * Checks if a named setting exists (array syntax).
146
	 *
147
	 * @param string $offset The settings key.
148
	 */
149
	public function offsetExists( $offset ) {
150
		return isset( $this->data[ $offset ] );
151
	}
152
153
	/**
154
	 * Unsets a named setting (array syntax).
155
	 *
156
	 * @param string $offset The settings key.
157
	 */
158
	public function offsetUnset( $offset ) {
159
		unset( $this->data[ $offset ] );
160
	}
161
162
	/**
163
	 * Provides access to named settings (array syntax).
164
	 *
165
	 * @param string $offset The settings key.
166
	 *
167
	 * @return mixed
168
	 */
169
	public function offsetGet( $offset ) {
170
		return isset( $this->data[ $offset ] ) ? $this->data[ $offset ] : null;
171
	}
172
173
	/**
174
	 * Provides a JSON serialization of the settings.
175
	 *
176
	 * @return mixed
177
	 */
178
	public function jsonSerialize() {
179
		return \array_merge(
180
			$this->data,
181
			[
182
				'no_break_narrow_space' => $this->no_break_narrow_space,
183
				'primary_quotes'        => "{$this->primary_quote_style->open()}|{$this->primary_quote_style->close()}",
184
				'secondary_quotes'      => "{$this->secondary_quote_style->open()}|{$this->secondary_quote_style->close()}",
185
				'dash_style'            => "{$this->dash_style->interval_dash()}|{$this->dash_style->interval_space()}|{$this->dash_style->parenthetical_dash()}|{$this->dash_style->parenthetical_space()}",
186
				'custom_units'          => $this->custom_units,
187
			]
188
		);
189
	}
190
191
	/**
192
	 * Retrieves the current non-breaking narrow space character (either the
193
	 * regular non-breaking space &nbsp; or the the true non-breaking narrow space &#8239;).
194
	 *
195
	 * @return string
196
	 */
197
	public function no_break_narrow_space() {
198
		return $this->no_break_narrow_space;
199
	}
200
201
	/**
202
	 * Retrieves the primary (double) quote style.
203
	 *
204
	 * @return Settings\Quotes
205
	 */
206
	public function primary_quote_style() {
207
		return $this->primary_quote_style;
208
	}
209
210
	/**
211
	 * Retrieves the secondary (single) quote style.
212
	 *
213
	 * @return Settings\Quotes
214
	 */
215
	public function secondary_quote_style() {
216
		return $this->secondary_quote_style;
217
	}
218
219
	/**
220
	 * Retrieves the dash style.
221
	 *
222
	 * @return Settings\Dashes
223
	 */
224
	public function dash_style() {
225
		return $this->dash_style;
226
	}
227
228
	/**
229
	 * Retrieves the custom units pattern.
230
	 *
231
	 * @return string The pattern is suitable for inclusion into a regular expression.
232
	 */
233
	public function custom_units() {
234
		return $this->custom_units;
235
	}
236
237
	/**
238
	 * Initialize the PHP_Typography object.
239
	 *
240
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
241
	 */
242
	private function init( $set_defaults = true ) {
243
		$this->no_break_narrow_space = U::NO_BREAK_SPACE;  // used in unit spacing - can be changed to 8239 via set_true_no_break_narrow_space.
244
245
		$this->dash_style = $this->get_dash_style( Dash_Style::TRADITIONAL_US );
246
247
		$this->primary_quote_style   = $this->get_quote_style( Quote_Style::DOUBLE_CURLED );
248
		$this->secondary_quote_style = $this->get_quote_style( Quote_Style::SINGLE_CURLED );
249
250
		if ( $set_defaults ) {
251
			$this->set_defaults();
252
		}
253
	}
254
255
	/**
256
	 * (Re)set various options to their default values.
257
	 */
258
	public function set_defaults() {
259
		// General attributes.
260
		$this->set_tags_to_ignore();
261
		$this->set_classes_to_ignore();
262
		$this->set_ids_to_ignore();
263
264
		// Smart characters.
265
		$this->set_smart_quotes();
266
		$this->set_smart_quotes_primary();
267
		$this->set_smart_quotes_secondary();
268
		$this->set_smart_dashes();
269
		$this->set_smart_dashes_style();
270
		$this->set_smart_ellipses();
271
		$this->set_smart_diacritics();
272
		$this->set_diacritic_language();
273
		$this->set_diacritic_custom_replacements();
274
		$this->set_smart_marks();
275
		$this->set_smart_ordinal_suffix();
276
		$this->set_smart_math();
277
		$this->set_smart_fractions();
278
		$this->set_smart_exponents();
279
280
		// Smart spacing.
281
		$this->set_single_character_word_spacing();
282
		$this->set_fraction_spacing();
283
		$this->set_unit_spacing();
284
		$this->set_french_punctuation_spacing();
285
		$this->set_units();
286
		$this->set_dash_spacing();
287
		$this->set_dewidow();
288
		$this->set_max_dewidow_length();
289
		$this->set_max_dewidow_pull();
290
		$this->set_dewidow_word_number();
291
		$this->set_wrap_hard_hyphens();
292
		$this->set_url_wrap();
293
		$this->set_email_wrap();
294
		$this->set_min_after_url_wrap();
295
		$this->set_space_collapse();
296
		$this->set_true_no_break_narrow_space();
297
298
		// Character styling.
299
		$this->set_style_ampersands();
300
		$this->set_style_caps();
301
		$this->set_style_initial_quotes();
302
		$this->set_style_numbers();
303
		$this->set_style_hanging_punctuation();
304
		$this->set_initial_quote_tags();
305
306
		// Hyphenation.
307
		$this->set_hyphenation();
308
		$this->set_hyphenation_language();
309
		$this->set_min_length_hyphenation();
310
		$this->set_min_before_hyphenation();
311
		$this->set_min_after_hyphenation();
312
		$this->set_hyphenate_headings();
313
		$this->set_hyphenate_all_caps();
314
		$this->set_hyphenate_title_case();
315
		$this->set_hyphenate_compounds();
316
		$this->set_hyphenation_exceptions();
317
318
		// Parser error handling.
319
		$this->set_ignore_parser_errors();
320
	}
321
322
	/**
323
	 * Enable lenient parser error handling (HTML is "best guess" if enabled).
324
	 *
325
	 * @param bool $on Optional. Default false.
326
	 */
327
	public function set_ignore_parser_errors( $on = false ) {
328
		$this->data['parserErrorsIgnore'] = $on;
329
	}
330
331
	/**
332
	 * Sets an optional handler for parser errors. Invalid callbacks will be silently ignored.
333
	 *
334
	 * @since 6.0.0. callable type is enforced via typehinting.
335
	 *
336
	 * @param callable|null $handler Optional. A callable that takes an array of error strings as its parameter. Default null.
337
	 */
338
	public function set_parser_errors_handler( callable $handler = null ) {
339
		$this->data['parserErrorsHandler'] = $handler;
340
	}
341
342
	/**
343
	 * Enable usage of true "no-break narrow space" (&#8239;) instead of the normal no-break space (&nbsp;).
344
	 *
345
	 * @param bool $on Optional. Default false.
346
	 */
347
	public function set_true_no_break_narrow_space( $on = false ) {
348
349
		if ( $on ) {
350
			$this->no_break_narrow_space = U::NO_BREAK_NARROW_SPACE;
351
		} else {
352
			$this->no_break_narrow_space = U::NO_BREAK_SPACE;
353
		}
354
	}
355
356
	/**
357
	 * Sets tags for which the typography of their children will be left untouched.
358
	 *
359
	 * @param string|array $tags A comma separated list or an array of tag names.
360
	 */
361
	public function set_tags_to_ignore( $tags = [ 'code', 'head', 'kbd', 'object', 'option', 'pre', 'samp', 'script', 'noscript', 'noembed', 'select', 'style', 'textarea', 'title', 'var', 'math' ] ) {
362
		// Ensure that we pass only lower-case tag names to XPath.
363
		$tags = array_filter( array_map( 'strtolower', Strings::maybe_split_parameters( $tags ) ), 'ctype_alnum' );
364
365
		$this->data['ignoreTags'] = array_unique( array_merge( $tags, array_flip( DOM::inappropriate_tags() ) ) );
366
	}
367
368
	/**
369
	 * Sets classes for which the typography of their children will be left untouched.
370
	 *
371
	 * @param string|array $classes A comma separated list or an array of class names.
372
	 */
373
	public function set_classes_to_ignore( $classes = [ 'vcard', 'noTypo' ] ) {
374
		$this->data['ignoreClasses'] = Strings::maybe_split_parameters( $classes );
375
	}
376
377
	/**
378
	 * Sets IDs for which the typography of their children will be left untouched.
379
	 *
380
	 * @param string|array $ids A comma separated list or an array of tag names.
381
	 */
382
	public function set_ids_to_ignore( $ids = [] ) {
383
		$this->data['ignoreIDs'] = Strings::maybe_split_parameters( $ids );
384
	}
385
386
	/**
387
	 * Enables/disables typographic quotes.
388
	 *
389
	 * @param bool $on Optional. Default true.
390
	 */
391
	public function set_smart_quotes( $on = true ) {
392
		$this->data['smartQuotes'] = $on;
393
	}
394
395
	/**
396
	 * Sets the style for primary ('double') quotemarks.
397
	 *
398
	 * Allowed values for $style:
399
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
400
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
401
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
402
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
403
	 * "singleCurled" => "&lsquo;foo&rsquo;",
404
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
405
	 * "singleLow9" => "&sbquo;foo&rsquo;",
406
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
407
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
408
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
409
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
410
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
411
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
412
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
413
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
414
	 *
415
	 * @param string $style Defaults to 'doubleCurled.
416
	 *
417
	 * @throws \DomainException Thrown if $style constant is invalid.
418
	 */
419
	public function set_smart_quotes_primary( $style = Quote_Style::DOUBLE_CURLED ) {
420
		$this->primary_quote_style = $this->get_quote_style( $style );
421
	}
422
423
	/**
424
	 * Sets the style for secondary ('single') quotemarks.
425
	 *
426
	 * Allowed values for $style:
427
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
428
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
429
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
430
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
431
	 * "singleCurled" => "&lsquo;foo&rsquo;",
432
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
433
	 * "singleLow9" => "&sbquo;foo&rsquo;",
434
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
435
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
436
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
437
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
438
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
439
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
440
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
441
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
442
	 *
443
	 * @param string $style Defaults to 'singleCurled'.
444
	 *
445
	 * @throws \DomainException Thrown if $style constant is invalid.
446
	 */
447
	public function set_smart_quotes_secondary( $style = Quote_Style::SINGLE_CURLED ) {
448
		$this->secondary_quote_style = $this->get_quote_style( $style );
449
	}
450
451
	/**
452
	 * Retrieves a Quotes instance from a given style.
453
	 *
454
	 * @param  Settings\Quotes|string $style A Quotes instance or a quote style constant.
455
	 *
456
	 * @throws \DomainException Thrown if $style constant is invalid.
457
	 *
458
	 * @return Settings\Quotes
459
	 */
460
	protected function get_quote_style( $style ) {
461
		return $this->get_style( $style, Settings\Quotes::class, [ Quote_Style::class, 'get_styled_quotes' ], 'quote' );
462
	}
463
464
	/**
465
	 * Retrieves a Quotes instance from a given style.
466
	 *
467
	 * @since 6.0.0
468
	 *
469
	 * @param  Settings\Dashes|string $style A Dashes instance or a dash style constant.
470
	 *
471
	 * @throws \DomainException Thrown if $style constant is invalid.
472
	 *
473
	 * @return Settings\Dashes
474
	 */
475
	protected function get_dash_style( $style ) {
476
		return $this->get_style( $style, Settings\Dashes::class, [ Dash_Style::class, 'get_styled_dashes' ], 'dash' );
477
	}
478
479
	/**
480
	 * Retrieves an object from a given style.
481
	 *
482
	 * @param  object|string $style          A style object instance or a style constant.
483
	 * @param  string        $expected_class A class name.
484
	 * @param  callable      $get_style      A function that returns a style object from a given style constant.
485
	 * @param  string        $description    Style description for the exception message.
486
	 *
487
	 * @throws \DomainException Thrown if $style constant is invalid.
488
	 *
489
	 * @return mixed An instance of $expected_class.
490
	 */
491
	protected function get_style( $style, $expected_class, callable $get_style, $description ) {
492
		if ( $style instanceof $expected_class ) {
493
			$object = $style;
494
		} else {
495
			$object = $get_style( $style, $this );
496
		}
497
498
		if ( ! $object instanceof $expected_class ) {
499
			throw new \DomainException( "Invalid $description style $style." );
500
		}
501
502
		return $object;
503
	}
504
505
	/**
506
	 * Enables/disables replacement of "a--a" with En Dash " -- " and "---" with Em Dash.
507
	 *
508
	 * @param bool $on Optional. Default true.
509
	 */
510
	public function set_smart_dashes( $on = true ) {
511
		$this->data['smartDashes'] = $on;
512
	}
513
514
	/**
515
	 * Sets the typographical conventions used by smart_dashes.
516
	 *
517
	 * Allowed values for $style:
518
	 * - "traditionalUS"
519
	 * - "international"
520
	 *
521
	 * @param string|Settings\Dashes $style Optional. Default Dash_Style::TRADITIONAL_US.
522
	 *
523
	 * @throws \DomainException Thrown if $style constant is invalid.
524
	 */
525
	public function set_smart_dashes_style( $style = Dash_Style::TRADITIONAL_US ) {
526
		$this->dash_style = $this->get_dash_style( $style );
527
	}
528
529
	/**
530
	 * Enables/disables replacement of "..." with "…".
531
	 *
532
	 * @param bool $on Optional. Default true.
533
	 */
534
	public function set_smart_ellipses( $on = true ) {
535
		$this->data['smartEllipses'] = $on;
536
	}
537
538
	/**
539
	 * Enables/disables replacement "creme brulee" with "crème brûlée".
540
	 *
541
	 * @param bool $on Optional. Default true.
542
	 */
543
	public function set_smart_diacritics( $on = true ) {
544
		$this->data['smartDiacritics'] = $on;
545
	}
546
547
	/**
548
	 * Sets the language used for diacritics replacements.
549
	 *
550
	 * @param string $lang Has to correspond to a filename in 'diacritics'. Optional. Default 'en-US'.
551
	 */
552
	public function set_diacritic_language( $lang = 'en-US' ) {
553
		if ( isset( $this->data['diacriticLanguage'] ) && $this->data['diacriticLanguage'] === $lang ) {
554
			return;
555
		}
556
557
		$this->data['diacriticLanguage'] = $lang;
558
		$language_file_name              = dirname( __FILE__ ) . '/diacritics/' . $lang . '.json';
559
560
		if ( file_exists( $language_file_name ) ) {
561
			$diacritics_file              = json_decode( file_get_contents( $language_file_name ), true );
562
			$this->data['diacriticWords'] = $diacritics_file['diacritic_words'];
563
		} else {
564
			unset( $this->data['diacriticWords'] );
565
		}
566
567
		$this->update_diacritics_replacement_arrays();
568
	}
569
570
	/**
571
	 * Sets up custom diacritics replacements.
572
	 *
573
	 * @param string|array $custom_replacements An array formatted [needle=>replacement, needle=>replacement...],
574
	 *                                          or a string formatted `"needle"=>"replacement","needle"=>"replacement",...
575
	 */
576
	public function set_diacritic_custom_replacements( $custom_replacements = [] ) {
577
		if ( ! is_array( $custom_replacements ) ) {
578
			$custom_replacements = $this->parse_diacritics_replacement_string( $custom_replacements );
579
		}
580
581
		$this->data['diacriticCustomReplacements'] = self::array_map_assoc( function( $key, $replacement ) {
582
			$key         = strip_tags( trim( $key ) );
583
			$replacement = strip_tags( trim( $replacement ) );
584
585
			if ( ! empty( $key ) && ! empty( $replacement ) ) {
586
				return [ $key => $replacement ];
587
			} else {
588
				return [];
589
			}
590
		}, $custom_replacements );
591
592
		$this->update_diacritics_replacement_arrays();
593
	}
594
595
	/**
596
	 * Parses a custom diacritics replacement string into an array.
597
	 *
598
	 * @param string $custom_replacements A string formatted `"needle"=>"replacement","needle"=>"replacement",...
599
	 *
600
	 * @return array
601
	 */
602
	private function parse_diacritics_replacement_string( $custom_replacements ) {
603
		return self::array_map_assoc( function( $key, $replacement ) {
604
605
			// Account for single and double quotes in keys ...
606
			if ( preg_match( '/("|\')((?:(?!\1).)+)(?:\1\s*=>)/', $replacement, $match ) ) {
607
				$key = $match[2];
608
			}
609
610
			// ... and values.
611
			if ( preg_match( '/(?:=>\s*("|\'))((?:(?!\1).)+)(?:\1)/', $replacement, $match ) ) {
612
				$replacement = $match[2];
613
			}
614
615
			return [ $key => $replacement ];
616
		}, preg_split( '/,/', $custom_replacements, -1, PREG_SPLIT_NO_EMPTY ) );
617
	}
618
619
	/**
620
	 * Provides an array_map implementation with control over resulting array's keys.
621
	 *
622
	 * Based on https://gist.github.com/jasand-pereza/84ecec7907f003564584.
623
	 *
624
	 * @since 6.0.0
625
	 *
626
	 * @param  callable $callback A callback function that needs to return [ $key => $value ] pairs.
627
	 * @param  array    $array    The array.
628
	 *
629
	 * @return array
630
	 */
631
	protected static function array_map_assoc( callable $callback, array $array ) {
632
		$new = [];
633
634
		foreach ( $array as $k => $v ) {
635
			$u = $callback( $k, $v );
636
637
			if ( ! empty( $u ) ) {
638
				$new[ \key( $u ) ] = \current( $u );
639
			}
640
		}
641
642
		return $new;
643
	}
644
645
	/**
646
	 * Update the pattern and replacement arrays in $settings['diacriticReplacement'].
647
	 *
648
	 * Should be called whenever a new diacritics replacement language is selected or
649
	 * when the custom replacements are updated.
650
	 */
651
	private function update_diacritics_replacement_arrays() {
652
		$patterns     = [];
653
		$replacements = [];
654
655
		if ( ! empty( $this->data['diacriticCustomReplacements'] ) ) {
656
			$this->parse_diacritics_rules( $this->data['diacriticCustomReplacements'], $patterns, $replacements );
657
		}
658
		if ( ! empty( $this->data['diacriticWords'] ) ) {
659
			$this->parse_diacritics_rules( $this->data['diacriticWords'], $patterns, $replacements );
660
		}
661
662
		$this->data['diacriticReplacement'] = [
663
			'patterns'     => $patterns,
664
			'replacements' => $replacements,
665
		];
666
	}
667
668
	/**
669
	 * Parse an array of diacritics rules.
670
	 *
671
	 * @param array $diacritics_rules The rules ( $word => $replacement ).
672
	 * @param array $patterns         Resulting patterns. Passed by reference.
673
	 * @param array $replacements     Resulting replacements. Passed by reference.
674
	 */
675
	private function parse_diacritics_rules( array $diacritics_rules, array &$patterns, array &$replacements ) {
676
677
		foreach ( $diacritics_rules as $needle => $replacement ) {
678
			$patterns[]              = '/\b(?<!\w[' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . '])' . $needle . '\b(?![' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . ']\w)/u';
679
			$replacements[ $needle ] = $replacement;
680
		}
681
	}
682
683
	/**
684
	 * Enables/disables replacement of (r) (c) (tm) (sm) (p) (R) (C) (TM) (SM) (P) with ® © ™ ℠ ℗.
685
	 *
686
	 * @param bool $on Optional. Default true.
687
	 */
688
	public function set_smart_marks( $on = true ) {
689
		$this->data['smartMarks'] = $on;
690
	}
691
692
	/**
693
	 * Enables/disables proper mathematical symbols.
694
	 *
695
	 * @param bool $on Optional. Default true.
696
	 */
697
	public function set_smart_math( $on = true ) {
698
		$this->data['smartMath'] = $on;
699
	}
700
701
	/**
702
	 * Enables/disables replacement of 2^2 with 2<sup>2</sup>
703
	 *
704
	 * @param bool $on Optional. Default true.
705
	 */
706
	public function set_smart_exponents( $on = true ) {
707
		$this->data['smartExponents'] = $on;
708
	}
709
710
	/**
711
	 * Enables/disables replacement of 1/4 with <sup>1</sup>&#8260;<sub>4</sub>.
712
	 *
713
	 * @param bool $on Optional. Default true.
714
	 */
715
	public function set_smart_fractions( $on = true ) {
716
		$this->data['smartFractions'] = $on;
717
	}
718
719
	/**
720
	 * Enables/disables replacement of 1st with 1<sup>st</sup>.
721
	 *
722
	 * @param bool $on Optional. Default true.
723
	 */
724
	public function set_smart_ordinal_suffix( $on = true ) {
725
		$this->data['smartOrdinalSuffix'] = $on;
726
	}
727
728
	/**
729
	 * Enables/disables forcing single character words to next line with the insertion of &nbsp;.
730
	 *
731
	 * @param bool $on Optional. Default true.
732
	 */
733
	public function set_single_character_word_spacing( $on = true ) {
734
		$this->data['singleCharacterWordSpacing'] = $on;
735
	}
736
737
	/**
738
	 * Enables/disables fraction spacing.
739
	 *
740
	 * @param bool $on Optional. Default true.
741
	 */
742
	public function set_fraction_spacing( $on = true ) {
743
		$this->data['fractionSpacing'] = $on;
744
	}
745
746
	/**
747
	 * Enables/disables keeping units and values together with the insertion of &nbsp;.
748
	 *
749
	 * @param bool $on Optional. Default true.
750
	 */
751
	public function set_unit_spacing( $on = true ) {
752
		$this->data['unitSpacing'] = $on;
753
	}
754
755
	/**
756
	 * Enables/disables numbered abbreviations like "ISO 9000" together with the insertion of &nbsp;.
757
	 *
758
	 * @param bool $on Optional. Default true.
759
	 */
760
	public function set_numbered_abbreviation_spacing( $on = true ) {
761
		$this->data['numberedAbbreviationSpacing'] = $on;
762
	}
763
764
	/**
765
	 * Enables/disables extra whitespace before certain punction marks, as is the French custom.
766
	 *
767
	 * @param bool $on Optional. Default true.
768
	 */
769
	public function set_french_punctuation_spacing( $on = true ) {
770
		$this->data['frenchPunctuationSpacing'] = $on;
771
	}
772
773
	/**
774
	 * Sets the list of units to keep together with their values.
775
	 *
776
	 * @param string|array $units A comma separated list or an array of units.
777
	 */
778
	public function set_units( $units = [] ) {
779
		$this->data['units'] = Strings::maybe_split_parameters( $units );
780
		$this->update_unit_pattern( $this->data['units'] );
781
	}
782
783
	/**
784
	 * Update components and pattern for matching both standard and custom units.
785
	 *
786
	 * @param array $units An array of unit names.
787
	 */
788
	private function update_unit_pattern( array $units ) {
789
		// Update components & regex pattern.
790
		foreach ( $units as $index => $unit ) {
791
			// Escape special chars.
792
			$units[ $index ] = preg_replace( '#([\[\\\^\$\.\|\?\*\+\(\)\{\}])#', '\\\\$1', $unit );
793
		}
794
		$this->custom_units  = implode( '|', $units );
795
		$this->custom_units .= ( $this->custom_units ) ? '|' : '';
796
	}
797
798
	/**
799
	 * Enables/disables wrapping of Em and En dashes are in thin spaces.
800
	 *
801
	 * @param bool $on Optional. Default true.
802
	 */
803
	public function set_dash_spacing( $on = true ) {
804
		$this->data['dashSpacing'] = $on;
805
	}
806
807
	/**
808
	 * Enables/disables removal of extra whitespace characters.
809
	 *
810
	 * @param bool $on Optional. Default true.
811
	 */
812
	public function set_space_collapse( $on = true ) {
813
		$this->data['spaceCollapse'] = $on;
814
	}
815
816
	/**
817
	 * Enables/disables widow handling.
818
	 *
819
	 * @param bool $on Optional. Default true.
820
	 */
821
	public function set_dewidow( $on = true ) {
822
		$this->data['dewidow'] = $on;
823
	}
824
825
	/**
826
	 * Sets the maximum length of widows that will be protected.
827
	 *
828
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
829
	 */
830
	public function set_max_dewidow_length( $length = 5 ) {
831
		$length = ( $length > 1 ) ? $length : 5;
832
833
		$this->data['dewidowMaxLength'] = $length;
834
	}
835
836
	/**
837
	 * Sets the maximum number of words considered for dewidowing.
838
	 *
839
	 * @param int $number Defaults to 1. Only 1, 2 and 3 are valid.
840
	 */
841
	public function set_dewidow_word_number( $number = 1 ) {
842
		$number = ( $number > 3 || $number < 1 ) ? 1 : $number;
843
844
		$this->data['dewidowWordNumber'] = $number;
845
	}
846
847
	/**
848
	 * Sets the maximum length of pulled text to keep widows company.
849
	 *
850
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
851
	 */
852
	public function set_max_dewidow_pull( $length = 5 ) {
853
		$length = ( $length > 1 ) ? $length : 5;
854
855
		$this->data['dewidowMaxPull'] = $length;
856
	}
857
858
	/**
859
	 * Enables/disables wrapping at internal hard hyphens with the insertion of a zero-width-space.
860
	 *
861
	 * @param bool $on Optional. Default true.
862
	 */
863
	public function set_wrap_hard_hyphens( $on = true ) {
864
		$this->data['hyphenHardWrap'] = $on;
865
	}
866
867
	/**
868
	 * Enables/disables wrapping of urls.
869
	 *
870
	 * @param bool $on Optional. Default true.
871
	 */
872
	public function set_url_wrap( $on = true ) {
873
		$this->data['urlWrap'] = $on;
874
	}
875
876
	/**
877
	 * Enables/disables wrapping of email addresses.
878
	 *
879
	 * @param bool $on Optional. Default true.
880
	 */
881
	public function set_email_wrap( $on = true ) {
882
		$this->data['emailWrap'] = $on;
883
	}
884
885
	/**
886
	 * Sets the minimum character requirement after an URL wrapping point.
887
	 *
888
	 * @param int $length Defaults to 5. Trying to set the value to less than 1 resets the length to the default.
889
	 */
890
	public function set_min_after_url_wrap( $length = 5 ) {
891
		$length = ( $length > 0 ) ? $length : 5;
892
893
		$this->data['urlMinAfterWrap'] = $length;
894
	}
895
896
	/**
897
	 * Enables/disables wrapping of ampersands in <span class="amp">.
898
	 *
899
	 * @param bool $on Optional. Default true.
900
	 */
901
	public function set_style_ampersands( $on = true ) {
902
		$this->data['styleAmpersands'] = $on;
903
	}
904
905
	/**
906
	 * Enables/disables wrapping caps in <span class="caps">.
907
	 *
908
	 * @param bool $on Optional. Default true.
909
	 */
910
	public function set_style_caps( $on = true ) {
911
		$this->data['styleCaps'] = $on;
912
	}
913
914
	/**
915
	 * Enables/disables wrapping of initial quotes in <span class="quo"> or <span class="dquo">.
916
	 *
917
	 * @param bool $on Optional. Default true.
918
	 */
919
	public function set_style_initial_quotes( $on = true ) {
920
		$this->data['styleInitialQuotes'] = $on;
921
	}
922
923
	/**
924
	 * Enables/disables wrapping of numbers in <span class="numbers">.
925
	 *
926
	 * @param bool $on Optional. Default true.
927
	 */
928
	public function set_style_numbers( $on = true ) {
929
		$this->data['styleNumbers'] = $on;
930
	}
931
932
	/**
933
	 * Enables/disables wrapping of punctiation and wide characters in <span class="pull-*">.
934
	 *
935
	 * @param bool $on Optional. Default true.
936
	 */
937
	public function set_style_hanging_punctuation( $on = true ) {
938
		$this->data['styleHangingPunctuation'] = $on;
939
	}
940
941
	/**
942
	 * Sets the list of tags where initial quotes and guillemets should be styled.
943
	 *
944
	 * @param string|array $tags A comma separated list or an array of tag names.
945
	 */
946
	public function set_initial_quote_tags( $tags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'li', 'dd', 'dt' ] ) {
947
		// Make array if handed a list of tags as a string.
948
		if ( ! is_array( $tags ) ) {
949
			$tags = preg_split( '/[^a-z0-9]+/', $tags, -1, PREG_SPLIT_NO_EMPTY );
950
		}
951
952
		// Store the tag array inverted (with the tagName as its index for faster lookup).
953
		$this->data['initialQuoteTags'] = array_change_key_case( array_flip( $tags ), CASE_LOWER );
954
	}
955
956
	/**
957
	 * Enables/disables hyphenation.
958
	 *
959
	 * @param bool $on Optional. Default true.
960
	 */
961
	public function set_hyphenation( $on = true ) {
962
		$this->data['hyphenation'] = $on;
963
	}
964
965
	/**
966
	 * Sets the hyphenation pattern language.
967
	 *
968
	 * @param string $lang Has to correspond to a filename in 'lang'. Optional. Default 'en-US'.
969
	 */
970
	public function set_hyphenation_language( $lang = 'en-US' ) {
971
		if ( isset( $this->data['hyphenLanguage'] ) && $this->data['hyphenLanguage'] === $lang ) {
972
			return; // Bail out, no need to do anything.
973
		}
974
975
		$this->data['hyphenLanguage'] = $lang;
976
	}
977
978
	/**
979
	 * Sets the minimum length of a word that may be hyphenated.
980
	 *
981
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
982
	 */
983
	public function set_min_length_hyphenation( $length = 5 ) {
984
		$length = ( $length > 1 ) ? $length : 5;
985
986
		$this->data['hyphenMinLength'] = $length;
987
	}
988
989
	/**
990
	 * Sets the minimum character requirement before a hyphenation point.
991
	 *
992
	 * @param int $length Defaults to 3. Trying to set the value to less than 1 resets the length to the default.
993
	 */
994
	public function set_min_before_hyphenation( $length = 3 ) {
995
		$length = ( $length > 0 ) ? $length : 3;
996
997
		$this->data['hyphenMinBefore'] = $length;
998
	}
999
1000
	/**
1001
	 * Sets the minimum character requirement after a hyphenation point.
1002
	 *
1003
	 * @param int $length Defaults to 2. Trying to set the value to less than 1 resets the length to the default.
1004
	 */
1005
	public function set_min_after_hyphenation( $length = 2 ) {
1006
		$length = ( $length > 0 ) ? $length : 2;
1007
1008
		$this->data['hyphenMinAfter'] = $length;
1009
	}
1010
1011
	/**
1012
	 * Enables/disables hyphenation of titles and headings.
1013
	 *
1014
	 * @param bool $on Optional. Default true.
1015
	 */
1016
	public function set_hyphenate_headings( $on = true ) {
1017
		$this->data['hyphenateTitle'] = $on;
1018
	}
1019
1020
	/**
1021
	 * Enables/disables hyphenation of words set completely in capital letters.
1022
	 *
1023
	 * @param bool $on Optional. Default true.
1024
	 */
1025
	public function set_hyphenate_all_caps( $on = true ) {
1026
		$this->data['hyphenateAllCaps'] = $on;
1027
	}
1028
1029
	/**
1030
	 * Enables/disables hyphenation of words starting with a capital letter.
1031
	 *
1032
	 * @param bool $on Optional. Default true.
1033
	 */
1034
	public function set_hyphenate_title_case( $on = true ) {
1035
		$this->data['hyphenateTitleCase'] = $on;
1036
	}
1037
1038
	/**
1039
	 * Enables/disables hyphenation of compound words (e.g. "editor-in-chief").
1040
	 *
1041
	 * @param bool $on Optional. Default true.
1042
	 */
1043
	public function set_hyphenate_compounds( $on = true ) {
1044
		$this->data['hyphenateCompounds'] = $on;
1045
	}
1046
1047
	/**
1048
	 * Sets custom word hyphenations.
1049
	 *
1050
	 * @param string|array $exceptions An array of words with all hyphenation points marked with a hard hyphen (or a string list of such words).
1051
	 *        In the latter case, only alphanumeric characters and hyphens are recognized. The default is empty.
1052
	 */
1053
	public function set_hyphenation_exceptions( $exceptions = [] ) {
1054
		$this->data['hyphenationCustomExceptions'] = Strings::maybe_split_parameters( $exceptions );
1055
	}
1056
1057
	/**
1058
	 * Retrieves a unique hash value for the current settings.
1059
	 *
1060
	 * @since 5.2.0 The new parameter $raw_output has been added.
1061
	 *
1062
	 * @param int  $max_length Optional. The maximum number of bytes returned (0 for unlimited). Default 16.
1063
	 * @param bool $raw_output Optional. Wether to return raw binary data for the hash. Default true.
1064
	 *
1065
	 * @return string A binary hash value for the current settings limited to $max_length.
1066
	 */
1067
	public function get_hash( $max_length = 16, $raw_output = true ) {
1068
		$hash = md5( json_encode( $this ), $raw_output );
1069
1070
		if ( $max_length < strlen( $hash ) && $max_length > 0 ) {
1071
			$hash = substr( $hash, 0, $max_length );
1072
		}
1073
1074
		return $hash;
1075
	}
1076
}
1077