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 ( 1d0b67...503e17 )
by Der Mundschenk
03:04
created

Settings::parse_diacritics_replacement_string()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 1
nop 1
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
9
 *  modify it under the terms of the GNU General Public License
10
 *  as published by the Free Software Foundation; either version 2
11
 *  of the License, or (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
19
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 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
class Settings implements \ArrayAccess {
39
40
	/**
41
	 * The current no-break narrow space character.
42
	 *
43
	 * @var string
44
	 */
45
	protected $no_break_narrow_space;
46
47
	/**
48
	 * Primary quote style.
49
	 *
50
	 * @var Settings\Quotes
51
	 */
52
	protected $primary_quote_style;
53
54
	/**
55
	 * Secondary quote style.
56
	 *
57
	 * @var Settings\Quotes
58
	 */
59
	protected $secondary_quote_style;
60
61
	/**
62
	 * A regex pattern for custom units (or the empty string).
63
	 *
64
	 * @var string
65
	 */
66
	protected $custom_units = '';
67
68
	/**
69
	 * A hashmap of settings for the various typographic options.
70
	 *
71
	 * @var array
72
	 */
73
	protected $data = [];
74
75
	/**
76
	 * An array containing all self-closing HTML5 tags.
77
	 *
78
	 * @var array
79
	 */
80
	protected $self_closing_tags = [];
81
82
	/**
83
	 * A array of tags we should never touch.
84
	 *
85
	 * @var array
86
	 */
87
	protected $inappropriate_tags = [];
88
89
	/**
90
	 * The current dash style.
91
	 *
92
	 * @var Settings\Dashes
93
	 */
94
	protected $dash_style;
95
96
	/**
97
	 * Sets up a new Settings object.
98
	 *
99
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
100
	 */
101
	public function __construct( $set_defaults = true ) {
102
		$this->init( $set_defaults );
103
	}
104
105
	/**
106
	 * Provides access to named settings (object syntax).
107
	 *
108
	 * @param string $key The settings key.
109
	 *
110
	 * @return mixed
111
	 */
112
	public function &__get( $key ) {
113
		return $this->data[ $key ];
114
	}
115
116
	/**
117
	 * Changes a named setting (object syntax).
118
	 *
119
	 * @param string $key   The settings key.
120
	 * @param mixed  $value The settings value.
121
	 */
122
	public function __set( $key, $value ) {
123
		$this->data[ $key ] = $value;
124
	}
125
126
	/**
127
	 * Checks if a named setting exists (object syntax).
128
	 *
129
	 * @param string $key The settings key.
130
	 */
131
	public function __isset( $key ) {
132
		return isset( $this->data[ $key ] );
133
	}
134
135
	/**
136
	 * Unsets a named setting.
137
	 *
138
	 * @param string $key The settings key.
139
	 */
140
	public function __unset( $key ) {
141
		unset( $this->data[ $key ] );
142
	}
143
144
	/**
145
	 * Changes a named setting (array syntax).
146
	 *
147
	 * @param string $offset The settings key.
148
	 * @param mixed  $value  The settings value.
149
	 */
150
	public function offsetSet( $offset, $value ) {
151
		if ( is_null( $offset ) ) {
152
			$this->data[] = $value;
153
		} else {
154
			$this->data[ $offset ] = $value;
155
		}
156
	}
157
158
	/**
159
	 * Checks if a named setting exists (array syntax).
160
	 *
161
	 * @param string $offset The settings key.
162
	 */
163
	public function offsetExists( $offset ) {
164
		return isset( $this->data[ $offset ] );
165
	}
166
167
	/**
168
	 * Unsets a named setting (array syntax).
169
	 *
170
	 * @param string $offset The settings key.
171
	 */
172
	public function offsetUnset( $offset ) {
173
		unset( $this->data[ $offset ] );
174
	}
175
176
	/**
177
	 * Provides access to named settings (array syntax).
178
	 *
179
	 * @param string $offset The settings key.
180
	 *
181
	 * @return mixed
182
	 */
183
	public function offsetGet( $offset ) {
184
		return isset( $this->data[ $offset ] ) ? $this->data[ $offset ] : null;
185
	}
186
187
	/**
188
	 * Retrieves the current non-breaking narrow space character (either the
189
	 * regular non-breaking space &nbsp; or the the true non-breaking narrow space &#8239;).
190
	 *
191
	 * @return string
192
	 */
193
	public function no_break_narrow_space() {
194
		return $this->no_break_narrow_space;
195
	}
196
197
	/**
198
	 * Retrieves the primary (double) quote style.
199
	 *
200
	 * @return Settings\Quotes
201
	 */
202
	public function primary_quote_style() {
203
		return $this->primary_quote_style;
204
	}
205
206
	/**
207
	 * Retrieves the secondary (single) quote style.
208
	 *
209
	 * @return Settings\Quotes
210
	 */
211
	public function secondary_quote_style() {
212
		return $this->secondary_quote_style;
213
	}
214
215
	/**
216
	 * Retrieves the dash style.
217
	 *
218
	 * @return Settings\Dashes
219
	 */
220
	public function dash_style() {
221
		return $this->dash_style;
222
	}
223
224
	/**
225
	 * Retrieves the custom units pattern.
226
	 *
227
	 * @return string The pattern is suitable for inclusion into a regular expression.
228
	 */
229
	public function custom_units() {
230
		return $this->custom_units;
231
	}
232
233
	/**
234
	 * Initialize the PHP_Typography object.
235
	 *
236
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
237
	 */
238
	private function init( $set_defaults = true ) {
239
		$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.
240
241
		$this->dash_style = new Settings\Simple_Dashes( U::EM_DASH, U::THIN_SPACE, U::EN_DASH, U::THIN_SPACE );
242
243
		$this->primary_quote_style   = new Settings\Simple_Quotes( U::DOUBLE_QUOTE_OPEN, U::DOUBLE_QUOTE_CLOSE );
244
		$this->secondary_quote_style = new Settings\Simple_Quotes( U::SINGLE_QUOTE_OPEN, U::SINGLE_QUOTE_CLOSE );
245
246
		// Set up some arrays for quick HTML5 introspection.
247
		$this->self_closing_tags = array_filter( array_keys( \Masterminds\HTML5\Elements::$html5 ), function( $tag ) {
248
			return \Masterminds\HTML5\Elements::isA( $tag, \Masterminds\HTML5\Elements::VOID_TAG );
249
		} );
250
		$this->inappropriate_tags = [ 'iframe', 'textarea', 'button', 'select', 'optgroup', 'option', 'map', 'style', 'head', 'title', 'script', 'applet', 'object', 'param' ];
251
252
		if ( $set_defaults ) {
253
			$this->set_defaults();
254
		}
255
	}
256
257
	/**
258
	 * (Re)set various options to their default values.
259
	 */
260
	public function set_defaults() {
261
		// General attributes.
262
		$this->set_tags_to_ignore();
263
		$this->set_classes_to_ignore();
264
		$this->set_ids_to_ignore();
265
266
		// Smart characters.
267
		$this->set_smart_quotes();
268
		$this->set_smart_quotes_primary();
269
		$this->set_smart_quotes_secondary();
270
		$this->set_smart_dashes();
271
		$this->set_smart_dashes_style();
272
		$this->set_smart_ellipses();
273
		$this->set_smart_diacritics();
274
		$this->set_diacritic_language();
275
		$this->set_diacritic_custom_replacements();
276
		$this->set_smart_marks();
277
		$this->set_smart_ordinal_suffix();
278
		$this->set_smart_math();
279
		$this->set_smart_fractions();
280
		$this->set_smart_exponents();
281
282
		// Smart spacing.
283
		$this->set_single_character_word_spacing();
284
		$this->set_fraction_spacing();
285
		$this->set_unit_spacing();
286
		$this->set_french_punctuation_spacing();
287
		$this->set_units();
288
		$this->set_dash_spacing();
289
		$this->set_dewidow();
290
		$this->set_max_dewidow_length();
291
		$this->set_max_dewidow_pull();
292
		$this->set_wrap_hard_hyphens();
293
		$this->set_url_wrap();
294
		$this->set_email_wrap();
295
		$this->set_min_after_url_wrap();
296
		$this->set_space_collapse();
297
		$this->set_true_no_break_narrow_space();
298
299
		// Character styling.
300
		$this->set_style_ampersands();
301
		$this->set_style_caps();
302
		$this->set_style_initial_quotes();
303
		$this->set_style_numbers();
304
		$this->set_style_hanging_punctuation();
305
		$this->set_initial_quote_tags();
306
307
		// Hyphenation.
308
		$this->set_hyphenation();
309
		$this->set_hyphenation_language();
310
		$this->set_min_length_hyphenation();
311
		$this->set_min_before_hyphenation();
312
		$this->set_min_after_hyphenation();
313
		$this->set_hyphenate_headings();
314
		$this->set_hyphenate_all_caps();
315
		$this->set_hyphenate_title_case();
316
		$this->set_hyphenate_compounds();
317
		$this->set_hyphenation_exceptions();
318
319
		// Parser error handling.
320
		$this->set_ignore_parser_errors();
321
	}
322
323
	/**
324
	 * Enable lenient parser error handling (HTML is "best guess" if enabled).
325
	 *
326
	 * @param bool $on Optional. Default false.
327
	 */
328
	public function set_ignore_parser_errors( $on = false ) {
329
		$this->data['parserErrorsIgnore'] = $on;
330
	}
331
332
	/**
333
	 * Sets an optional handler for parser errors. Invalid callbacks will be silently ignored.
334
	 *
335
	 * @param callable|null $handler Optional. A callable that takes an array of error strings as its parameter. Default null.
336
	 */
337
	public function set_parser_errors_handler( $handler = null ) {
338
		if ( ! empty( $handler ) && ! is_callable( $handler ) ) {
339
			return; // Invalid handler, abort.
340
		}
341
342
		$this->data['parserErrorsHandler'] = $handler;
343
	}
344
345
	/**
346
	 * Enable usage of true "no-break narrow space" (&#8239;) instead of the normal no-break space (&nbsp;).
347
	 *
348
	 * @param bool $on Optional. Default false.
349
	 */
350
	public function set_true_no_break_narrow_space( $on = false ) {
351
352
		if ( $on ) {
353
			$this->no_break_narrow_space = U::NO_BREAK_NARROW_SPACE;
354
		} else {
355
			$this->no_break_narrow_space = U::NO_BREAK_SPACE;
356
		}
357
	}
358
359
	/**
360
	 * Sets tags for which the typography of their children will be left untouched.
361
	 *
362
	 * @param string|array $tags A comma separated list or an array of tag names.
363
	 */
364
	public function set_tags_to_ignore( $tags = [ 'code', 'head', 'kbd', 'object', 'option', 'pre', 'samp', 'script', 'noscript', 'noembed', 'select', 'style', 'textarea', 'title', 'var', 'math' ] ) {
365
		// Ensure that we pass only lower-case tag names to XPath.
366
		$tags = array_filter( array_map( 'strtolower', Strings::maybe_split_parameters( $tags ) ), 'ctype_alnum' );
367
368
		// Self closing tags shouldn't be in $tags.
369
		$this->data['ignoreTags'] = array_unique( array_merge( array_diff( $tags, $this->self_closing_tags ), $this->inappropriate_tags ) );
370
	}
371
372
	/**
373
	 * Sets classes for which the typography of their children will be left untouched.
374
	 *
375
	 * @param string|array $classes A comma separated list or an array of class names.
376
	 */
377
	 function set_classes_to_ignore( $classes = [ 'vcard', 'noTypo' ] ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
378
		$this->data['ignoreClasses'] = Strings::maybe_split_parameters( $classes );
379
	}
380
381
	/**
382
	 * Sets IDs for which the typography of their children will be left untouched.
383
	 *
384
	 * @param string|array $ids A comma separated list or an array of tag names.
385
	 */
386
	public function set_ids_to_ignore( $ids = [] ) {
387
		$this->data['ignoreIDs'] = Strings::maybe_split_parameters( $ids );
388
	}
389
390
	/**
391
	 * Enables/disables typographic quotes.
392
	 *
393
	 * @param bool $on Optional. Default true.
394
	 */
395
	public function set_smart_quotes( $on = true ) {
396
		$this->data['smartQuotes'] = $on;
397
	}
398
399
	/**
400
	 * Sets the style for primary ('double') quotemarks.
401
	 *
402
	 * Allowed values for $style:
403
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
404
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
405
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
406
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
407
	 * "singleCurled" => "&lsquo;foo&rsquo;",
408
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
409
	 * "singleLow9" => "&sbquo;foo&rsquo;",
410
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
411
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
412
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
413
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
414
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
415
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
416
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
417
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
418
	 *
419
	 * @param string $style Defaults to 'doubleCurled.
420
	 */
421 View Code Duplication
	public function set_smart_quotes_primary( $style = Quote_Style::DOUBLE_CURLED ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
422
		if ( $style instanceof Settings\Quotes ) {
423
			$quotes = $style;
424
		} else {
425
			$quotes = Quote_Style::get_styled_quotes( $style, $this );
426
		}
427
428
		if ( ! empty( $quotes ) ) {
429
			$this->primary_quote_style = $quotes;
430
		} else {
431
			trigger_error( "Invalid quote style $style.", E_USER_WARNING ); // @codingStandardsIgnoreLine.
432
		}
433
	}
434
435
	/**
436
	 * Sets the style for secondary ('single') quotemarks.
437
	 *
438
	 * Allowed values for $style:
439
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
440
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
441
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
442
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
443
	 * "singleCurled" => "&lsquo;foo&rsquo;",
444
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
445
	 * "singleLow9" => "&sbquo;foo&rsquo;",
446
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
447
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
448
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
449
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
450
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
451
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
452
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
453
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
454
	 *
455
	 * @param string $style Defaults to 'singleCurled'.
456
	 */
457 View Code Duplication
	public function set_smart_quotes_secondary( $style = Quote_Style::SINGLE_CURLED ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
458
		if ( $style instanceof Settings\Quotes ) {
459
			$quotes = $style;
460
		} else {
461
			$quotes = Quote_Style::get_styled_quotes( $style, $this );
462
		}
463
464
		if ( ! empty( $quotes ) ) {
465
			$this->secondary_quote_style = $quotes;
466
		} else {
467
			trigger_error( "Invalid quote style $style.", E_USER_WARNING ); // @codingStandardsIgnoreLine.
468
		}
469
	}
470
471
	/**
472
	 * Enables/disables replacement of "a--a" with En Dash " -- " and "---" with Em Dash.
473
	 *
474
	 * @param bool $on Optional. Default true.
475
	 */
476
	public function set_smart_dashes( $on = true ) {
477
		$this->data['smartDashes'] = $on;
478
	}
479
480
	/**
481
	 * Sets the typographical conventions used by smart_dashes.
482
	 *
483
	 * Allowed values for $style:
484
	 * - "traditionalUS"
485
	 * - "international"
486
	 *
487
	 * @param string|Settings\Dashes $style Optional. Default Dash_Style::TRADITIONAL_US.
488
	 */
489 View Code Duplication
	public function set_smart_dashes_style( $style = Dash_Style::TRADITIONAL_US ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
490
		if ( $style instanceof Settings\Dashes ) {
491
			$dashes = $style;
492
		} else {
493
			$dashes = Dash_Style::get_styled_dashes( $style, $this );
494
		}
495
496
		if ( ! empty( $dashes ) ) {
497
			$this->dash_style = $dashes;
498
		} else {
499
			trigger_error( "Invalid dash style $style.", E_USER_WARNING ); // @codingStandardsIgnoreLine.
500
		}
501
	}
502
503
	/**
504
	 * Enables/disables replacement of "..." with "…".
505
	 *
506
	 * @param bool $on Optional. Default true.
507
	 */
508
	public function set_smart_ellipses( $on = true ) {
509
		$this->data['smartEllipses'] = $on;
510
	}
511
512
	/**
513
	 * Enables/disables replacement "creme brulee" with "crème brûlée".
514
	 *
515
	 * @param bool $on Optional. Default true.
516
	 */
517
	public function set_smart_diacritics( $on = true ) {
518
		$this->data['smartDiacritics'] = $on;
519
	}
520
521
	/**
522
	 * Sets the language used for diacritics replacements.
523
	 *
524
	 * @param string $lang Has to correspond to a filename in 'diacritics'. Optional. Default 'en-US'.
525
	 */
526
	public function set_diacritic_language( $lang = 'en-US' ) {
527
		if ( isset( $this->data['diacriticLanguage'] ) && $this->data['diacriticLanguage'] === $lang ) {
528
			return;
529
		}
530
531
		$this->data['diacriticLanguage'] = $lang;
532
		$language_file_name = dirname( __FILE__ ) . '/diacritics/' . $lang . '.json';
533
534
		if ( file_exists( $language_file_name ) ) {
535
			$diacritics_file = json_decode( file_get_contents( $language_file_name ), true );
536
			$this->data['diacriticWords'] = $diacritics_file['diacritic_words'];
537
		} else {
538
			unset( $this->data['diacriticWords'] );
539
		}
540
541
		$this->update_diacritics_replacement_arrays();
542
	}
543
544
	/**
545
	 * Sets up custom diacritics replacements.
546
	 *
547
	 * @param string|array $custom_replacements An array formatted [needle=>replacement, needle=>replacement...],
548
	 *                                          or a string formatted `"needle"=>"replacement","needle"=>"replacement",...
549
	 */
550
	public function set_diacritic_custom_replacements( $custom_replacements = [] ) {
551
		if ( ! is_array( $custom_replacements ) ) {
552
			$custom_replacements = $this->parse_diacritics_replacement_string( $custom_replacements );
553
		}
554
555
		$this->data['diacriticCustomReplacements'] = Arrays::array_map_assoc( function( $key, $replacement ) {
556
			$key         = strip_tags( trim( $key ) );
557
			$replacement = strip_tags( trim( $replacement ) );
558
559
			if ( ! empty( $key ) && ! empty( $replacement ) ) {
560
				return [ $key, $replacement ];
561
			} else {
562
				return [];
563
			}
564
		}, $custom_replacements );
565
566
		$this->update_diacritics_replacement_arrays();
567
	}
568
569
	/**
570
	 * Parses a custom diacritics replacement string into an array.
571
	 *
572
	 * @param string $custom_replacements A string formatted `"needle"=>"replacement","needle"=>"replacement",...
573
	 *
574
	 * @return array
575
	 */
576
	private function parse_diacritics_replacement_string( $custom_replacements ) {
577
		return Arrays::array_map_assoc( function( $key, $replacement ) {
578
579
			// Account for single and double quotes in keys ...
580
			if ( preg_match( '/("|\')((?:(?!\1).)+)(?:\1\s*=>)/', $replacement, $match ) ) {
581
				$key = $match[2];
582
			}
583
584
			// ... and values.
585
			if ( preg_match( '/(?:=>\s*("|\'))((?:(?!\1).)+)(?:\1)/', $replacement, $match ) ) {
586
				$replacement = $match[2];
587
			}
588
589
			return [ $key, $replacement ];
590
		}, preg_split( '/,/', $custom_replacements, -1, PREG_SPLIT_NO_EMPTY ) );
591
	}
592
593
	/**
594
	 * Update the pattern and replacement arrays in $settings['diacriticReplacement'].
595
	 *
596
	 * Should be called whenever a new diacritics replacement language is selected or
597
	 * when the custom replacements are updated.
598
	 */
599
	private function update_diacritics_replacement_arrays() {
600
		$patterns = [];
601
		$replacements = [];
602
603
		if ( ! empty( $this->data['diacriticCustomReplacements'] ) ) {
604
			$this->parse_diacritics_rules( $this->data['diacriticCustomReplacements'], $patterns, $replacements );
605
		}
606
		if ( ! empty( $this->data['diacriticWords'] ) ) {
607
			$this->parse_diacritics_rules( $this->data['diacriticWords'], $patterns, $replacements );
608
		}
609
610
		$this->data['diacriticReplacement'] = [
611
			'patterns'     => $patterns,
612
			'replacements' => $replacements,
613
		];
614
	}
615
616
	/**
617
	 * Parse an array of diacritics rules.
618
	 *
619
	 * @param array $diacritics_rules The rules ( $word => $replacement ).
620
	 * @param array $patterns         Resulting patterns. Passed by reference.
621
	 * @param array $replacements     Resulting replacements. Passed by reference.
622
	 */
623
	private function parse_diacritics_rules( array $diacritics_rules, array &$patterns, array &$replacements ) {
624
625
		foreach ( $diacritics_rules as $needle => $replacement ) {
626
			$patterns[] = '/\b(?<!\w[' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . '])' . $needle . '\b(?![' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . ']\w)/u';
627
			$replacements[ $needle ] = $replacement;
628
		}
629
	}
630
631
	/**
632
	 * Enables/disables replacement of (r) (c) (tm) (sm) (p) (R) (C) (TM) (SM) (P) with ® © ™ ℠ ℗.
633
	 *
634
	 * @param bool $on Optional. Default true.
635
	 */
636
	public function set_smart_marks( $on = true ) {
637
		$this->data['smartMarks'] = $on;
638
	}
639
640
	/**
641
	 * Enables/disables proper mathematical symbols.
642
	 *
643
	 * @param bool $on Optional. Default true.
644
	 */
645
	public function set_smart_math( $on = true ) {
646
		$this->data['smartMath'] = $on;
647
	}
648
649
	/**
650
	 * Enables/disables replacement of 2^2 with 2<sup>2</sup>
651
	 *
652
	 * @param bool $on Optional. Default true.
653
	 */
654
	public function set_smart_exponents( $on = true ) {
655
		$this->data['smartExponents'] = $on;
656
	}
657
658
	/**
659
	 * Enables/disables replacement of 1/4 with <sup>1</sup>&#8260;<sub>4</sub>.
660
	 *
661
	 * @param bool $on Optional. Default true.
662
	 */
663
	public function set_smart_fractions( $on = true ) {
664
		$this->data['smartFractions'] = $on;
665
	}
666
667
	/**
668
	 * Enables/disables replacement of 1st with 1<sup>st</sup>.
669
	 *
670
	 * @param bool $on Optional. Default true.
671
	 */
672
	public function set_smart_ordinal_suffix( $on = true ) {
673
		$this->data['smartOrdinalSuffix'] = $on;
674
	}
675
676
	/**
677
	 * Enables/disables forcing single character words to next line with the insertion of &nbsp;.
678
	 *
679
	 * @param bool $on Optional. Default true.
680
	 */
681
	public function set_single_character_word_spacing( $on = true ) {
682
		$this->data['singleCharacterWordSpacing'] = $on;
683
	}
684
685
	/**
686
	 * Enables/disables fraction spacing.
687
	 *
688
	 * @param bool $on Optional. Default true.
689
	 */
690
	public function set_fraction_spacing( $on = true ) {
691
		$this->data['fractionSpacing'] = $on;
692
	}
693
694
	/**
695
	 * Enables/disables keeping units and values together with the insertion of &nbsp;.
696
	 *
697
	 * @param bool $on Optional. Default true.
698
	 */
699
	public function set_unit_spacing( $on = true ) {
700
		$this->data['unitSpacing'] = $on;
701
	}
702
703
	/**
704
	 * Enables/disables numbered abbreviations like "ISO 9000" together with the insertion of &nbsp;.
705
	 *
706
	 * @param bool $on Optional. Default true.
707
	 */
708
	public function set_numbered_abbreviation_spacing( $on = true ) {
709
		$this->data['numberedAbbreviationSpacing'] = $on;
710
	}
711
712
	/**
713
	 * Enables/disables extra whitespace before certain punction marks, as is the French custom.
714
	 *
715
	 * @param bool $on Optional. Default true.
716
	 */
717
	public function set_french_punctuation_spacing( $on = true ) {
718
		$this->data['frenchPunctuationSpacing'] = $on;
719
	}
720
721
	/**
722
	 * Sets the list of units to keep together with their values.
723
	 *
724
	 * @param string|array $units A comma separated list or an array of units.
725
	 */
726
	public function set_units( $units = [] ) {
727
		$this->data['units'] = Strings::maybe_split_parameters( $units );
728
		$this->update_unit_pattern( $this->data['units'] );
729
	}
730
731
	/**
732
	 * Update components and pattern for matching both standard and custom units.
733
	 *
734
	 * @param array $units An array of unit names.
735
	 */
736
	private function update_unit_pattern( array $units ) {
737
		// Update components & regex pattern.
738
		foreach ( $units as $index => $unit ) {
739
			// Escape special chars.
740
			$units[ $index ] = preg_replace( '#([\[\\\^\$\.\|\?\*\+\(\)\{\}])#', '\\\\$1', $unit );
741
		}
742
		$this->custom_units = implode( '|', $units );
743
		$this->custom_units .= ( $this->custom_units ) ? '|' : '';
744
	}
745
746
	/**
747
	 * Enables/disables wrapping of Em and En dashes are in thin spaces.
748
	 *
749
	 * @param bool $on Optional. Default true.
750
	 */
751
	public function set_dash_spacing( $on = true ) {
752
		$this->data['dashSpacing'] = $on;
753
	}
754
755
	/**
756
	 * Enables/disables removal of extra whitespace characters.
757
	 *
758
	 * @param bool $on Optional. Default true.
759
	 */
760
	public function set_space_collapse( $on = true ) {
761
		$this->data['spaceCollapse'] = $on;
762
	}
763
764
	/**
765
	 * Enables/disables widow handling.
766
	 *
767
	 * @param bool $on Optional. Default true.
768
	 */
769
	public function set_dewidow( $on = true ) {
770
		$this->data['dewidow'] = $on;
771
	}
772
773
	/**
774
	 * Sets the maximum length of widows that will be protected.
775
	 *
776
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
777
	 */
778
	public function set_max_dewidow_length( $length = 5 ) {
779
		$length = ( $length > 1 ) ? $length : 5;
780
781
		$this->data['dewidowMaxLength'] = $length;
782
	}
783
784
	/**
785
	 * Sets the maximum length of pulled text to keep widows company.
786
	 *
787
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
788
	 */
789
	public function set_max_dewidow_pull( $length = 5 ) {
790
		$length = ( $length > 1 ) ? $length : 5;
791
792
		$this->data['dewidowMaxPull'] = $length;
793
	}
794
795
	/**
796
	 * Enables/disables wrapping at internal hard hyphens with the insertion of a zero-width-space.
797
	 *
798
	 * @param bool $on Optional. Default true.
799
	 */
800
	public function set_wrap_hard_hyphens( $on = true ) {
801
		$this->data['hyphenHardWrap'] = $on;
802
	}
803
804
	/**
805
	 * Enables/disables wrapping of urls.
806
	 *
807
	 * @param bool $on Optional. Default true.
808
	 */
809
	public function set_url_wrap( $on = true ) {
810
		$this->data['urlWrap'] = $on;
811
	}
812
813
	/**
814
	 * Enables/disables wrapping of email addresses.
815
	 *
816
	 * @param bool $on Optional. Default true.
817
	 */
818
	public function set_email_wrap( $on = true ) {
819
		$this->data['emailWrap'] = $on;
820
	}
821
822
	/**
823
	 * Sets the minimum character requirement after an URL wrapping point.
824
	 *
825
	 * @param int $length Defaults to 5. Trying to set the value to less than 1 resets the length to the default.
826
	 */
827
	public function set_min_after_url_wrap( $length = 5 ) {
828
		$length = ( $length > 0 ) ? $length : 5;
829
830
		$this->data['urlMinAfterWrap'] = $length;
831
	}
832
833
	/**
834
	 * Enables/disables wrapping of ampersands in <span class="amp">.
835
	 *
836
	 * @param bool $on Optional. Default true.
837
	 */
838
	public function set_style_ampersands( $on = true ) {
839
		$this->data['styleAmpersands'] = $on;
840
	}
841
842
	/**
843
	 * Enables/disables wrapping caps in <span class="caps">.
844
	 *
845
	 * @param bool $on Optional. Default true.
846
	 */
847
	public function set_style_caps( $on = true ) {
848
		$this->data['styleCaps'] = $on;
849
	}
850
851
	/**
852
	 * Enables/disables wrapping of initial quotes in <span class="quo"> or <span class="dquo">.
853
	 *
854
	 * @param bool $on Optional. Default true.
855
	 */
856
	public function set_style_initial_quotes( $on = true ) {
857
		$this->data['styleInitialQuotes'] = $on;
858
	}
859
860
	/**
861
	 * Enables/disables wrapping of numbers in <span class="numbers">.
862
	 *
863
	 * @param bool $on Optional. Default true.
864
	 */
865
	public function set_style_numbers( $on = true ) {
866
		$this->data['styleNumbers'] = $on;
867
	}
868
869
	/**
870
	 * Enables/disables wrapping of punctiation and wide characters in <span class="pull-*">.
871
	 *
872
	 * @param bool $on Optional. Default true.
873
	 */
874
	public function set_style_hanging_punctuation( $on = true ) {
875
		$this->data['styleHangingPunctuation'] = $on;
876
	}
877
878
	/**
879
	 * Sets the list of tags where initial quotes and guillemets should be styled.
880
	 *
881
	 * @param string|array $tags A comma separated list or an array of tag names.
882
	 */
883
	public function set_initial_quote_tags( $tags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'li', 'dd', 'dt' ] ) {
884
		// Make array if handed a list of tags as a string.
885
		if ( ! is_array( $tags ) ) {
886
			$tags = preg_split( '/[^a-z0-9]+/', $tags, -1, PREG_SPLIT_NO_EMPTY );
887
		}
888
889
		// Store the tag array inverted (with the tagName as its index for faster lookup).
890
		$this->data['initialQuoteTags'] = array_change_key_case( array_flip( $tags ), CASE_LOWER );
891
	}
892
893
	/**
894
	 * Enables/disables hyphenation.
895
	 *
896
	 * @param bool $on Optional. Default true.
897
	 */
898
	public function set_hyphenation( $on = true ) {
899
		$this->data['hyphenation'] = $on;
900
	}
901
902
	/**
903
	 * Sets the hyphenation pattern language.
904
	 *
905
	 * @param string $lang Has to correspond to a filename in 'lang'. Optional. Default 'en-US'.
906
	 */
907
	public function set_hyphenation_language( $lang = 'en-US' ) {
908
		if ( isset( $this->data['hyphenLanguage'] ) && $this->data['hyphenLanguage'] === $lang ) {
909
			return; // Bail out, no need to do anything.
910
		}
911
912
		$this->data['hyphenLanguage'] = $lang;
913
	}
914
915
	/**
916
	 * Sets the minimum length of a word that may be hyphenated.
917
	 *
918
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
919
	 */
920
	public function set_min_length_hyphenation( $length = 5 ) {
921
		$length = ( $length > 1 ) ? $length : 5;
922
923
		$this->data['hyphenMinLength'] = $length;
924
	}
925
926
	/**
927
	 * Sets the minimum character requirement before a hyphenation point.
928
	 *
929
	 * @param int $length Defaults to 3. Trying to set the value to less than 1 resets the length to the default.
930
	 */
931
	public function set_min_before_hyphenation( $length = 3 ) {
932
		$length = ( $length > 0 ) ? $length : 3;
933
934
		$this->data['hyphenMinBefore'] = $length;
935
	}
936
937
	/**
938
	 * Sets the minimum character requirement after a hyphenation point.
939
	 *
940
	 * @param int $length Defaults to 2. Trying to set the value to less than 1 resets the length to the default.
941
	 */
942
	public function set_min_after_hyphenation( $length = 2 ) {
943
		$length = ( $length > 0 ) ? $length : 2;
944
945
		$this->data['hyphenMinAfter'] = $length;
946
	}
947
948
	/**
949
	 * Enables/disables hyphenation of titles and headings.
950
	 *
951
	 * @param bool $on Optional. Default true.
952
	 */
953
	public function set_hyphenate_headings( $on = true ) {
954
		$this->data['hyphenateTitle'] = $on;
955
	}
956
957
	/**
958
	 * Enables/disables hyphenation of words set completely in capital letters.
959
	 *
960
	 * @param bool $on Optional. Default true.
961
	 */
962
	public function set_hyphenate_all_caps( $on = true ) {
963
		$this->data['hyphenateAllCaps'] = $on;
964
	}
965
966
	/**
967
	 * Enables/disables hyphenation of words starting with a capital letter.
968
	 *
969
	 * @param bool $on Optional. Default true.
970
	 */
971
	public function set_hyphenate_title_case( $on = true ) {
972
		$this->data['hyphenateTitleCase'] = $on;
973
	}
974
975
	/**
976
	 * Enables/disables hyphenation of compound words (e.g. "editor-in-chief").
977
	 *
978
	 * @param bool $on Optional. Default true.
979
	 */
980
	public function set_hyphenate_compounds( $on = true ) {
981
		$this->data['hyphenateCompounds'] = $on;
982
	}
983
984
	/**
985
	 * Sets custom word hyphenations.
986
	 *
987
	 * @param string|array $exceptions An array of words with all hyphenation points marked with a hard hyphen (or a string list of such words).
988
	 *        In the latter case, only alphanumeric characters and hyphens are recognized. The default is empty.
989
	 */
990
	public function set_hyphenation_exceptions( $exceptions = [] ) {
991
		$this->data['hyphenationCustomExceptions'] = Strings::maybe_split_parameters( $exceptions );
992
	}
993
994
	/**
995
	 * Retrieves a unique hash value for the current settings.
996
	 *
997
	 * @param int $max_length The maximum number of bytes returned. Optional. Default 16.
998
	 *
999
	 * @return string A binary hash value for the current settings limited to $max_length.
1000
	 */
1001
	public function get_hash( $max_length = 16 ) {
1002
		$hash = md5( json_encode( $this->data ), true );
1003
1004
		if ( $max_length < strlen( $hash ) ) {
1005
			$hash = substr( $hash, 0, $max_length );
1006
		}
1007
1008
		return $hash;
1009
	}
1010
}
1011