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
Pull Request — master (#46)
by Der Mundschenk
02:32
created

Settings::get_dash_style()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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