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 (#32)
by Der Mundschenk
03:28
created

Settings::jsonSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 0
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
	 * An array containing all self-closing HTML5 tags.
79
	 *
80
	 * @deprecated 5.2.0 Not used anymore, will be removed in 6.0.0.
81
	 *
82
	 * @var array
83
	 */
84
	protected $self_closing_tags = [];
85
86
	/**
87
	 * A array of tags we should never touch.
88
	 *
89
	 * @deprecated 5.2.0 Use DOM::inappropriate_tags() if necessary. Will be removed in 6.0.0.
90
	 *
91
	 * @var array
92
	 */
93
	protected $inappropriate_tags = [];
94
95
	/**
96
	 * The current dash style.
97
	 *
98
	 * @var Settings\Dashes
99
	 */
100
	protected $dash_style;
101
102
	/**
103
	 * Sets up a new Settings object.
104
	 *
105
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
106
	 */
107
	public function __construct( $set_defaults = true ) {
108
		$this->init( $set_defaults );
109
	}
110
111
	/**
112
	 * Provides access to named settings (object syntax).
113
	 *
114
	 * @param string $key The settings key.
115
	 *
116
	 * @return mixed
117
	 */
118
	public function &__get( $key ) {
119
		return $this->data[ $key ];
120
	}
121
122
	/**
123
	 * Changes a named setting (object syntax).
124
	 *
125
	 * @param string $key   The settings key.
126
	 * @param mixed  $value The settings value.
127
	 */
128
	public function __set( $key, $value ) {
129
		$this->data[ $key ] = $value;
130
	}
131
132
	/**
133
	 * Checks if a named setting exists (object syntax).
134
	 *
135
	 * @param string $key The settings key.
136
	 */
137
	public function __isset( $key ) {
138
		return isset( $this->data[ $key ] );
139
	}
140
141
	/**
142
	 * Unsets a named setting.
143
	 *
144
	 * @param string $key The settings key.
145
	 */
146
	public function __unset( $key ) {
147
		unset( $this->data[ $key ] );
148
	}
149
150
	/**
151
	 * Changes a named setting (array syntax).
152
	 *
153
	 * @param string $offset The settings key.
154
	 * @param mixed  $value  The settings value.
155
	 */
156
	public function offsetSet( $offset, $value ) {
157
		if ( is_null( $offset ) ) {
158
			$this->data[] = $value;
159
		} else {
160
			$this->data[ $offset ] = $value;
161
		}
162
	}
163
164
	/**
165
	 * Checks if a named setting exists (array syntax).
166
	 *
167
	 * @param string $offset The settings key.
168
	 */
169
	public function offsetExists( $offset ) {
170
		return isset( $this->data[ $offset ] );
171
	}
172
173
	/**
174
	 * Unsets a named setting (array syntax).
175
	 *
176
	 * @param string $offset The settings key.
177
	 */
178
	public function offsetUnset( $offset ) {
179
		unset( $this->data[ $offset ] );
180
	}
181
182
	/**
183
	 * Provides access to named settings (array syntax).
184
	 *
185
	 * @param string $offset The settings key.
186
	 *
187
	 * @return mixed
188
	 */
189
	public function offsetGet( $offset ) {
190
		return isset( $this->data[ $offset ] ) ? $this->data[ $offset ] : null;
191
	}
192
193
	/**
194
	 * Provides a JSON serialization of the settings.
195
	 *
196
	 * @return mixed
197
	 */
198
	public function jsonSerialize() {
199
		return array_merge(
200
			$this->data,
201
			[
202
				'no_break_narrow_space'  => $this->no_break_narrow_space,
203
				'primary_quotes'         => "{$this->primary_quote_style->open()}|{$this->primary_quote_style->close()}",
204
				'secondary_quotes'       => "{$this->secondary_quote_style->open()}|{$this->secondary_quote_style->close()}",
205
				'dash_style'             => "{$this->dash_style->interval_dash()}|{$this->dash_style->interval_space()}|{$this->dash_style->parenthetical_dash()}|{$this->dash_style->parenthetical_space()}",
206
				'custom_units'           => $this->custom_units,
207
			]
208
		);
209
	}
210
211
	/**
212
	 * Retrieves the current non-breaking narrow space character (either the
213
	 * regular non-breaking space &nbsp; or the the true non-breaking narrow space &#8239;).
214
	 *
215
	 * @return string
216
	 */
217
	public function no_break_narrow_space() {
218
		return $this->no_break_narrow_space;
219
	}
220
221
	/**
222
	 * Retrieves the primary (double) quote style.
223
	 *
224
	 * @return Settings\Quotes
225
	 */
226
	public function primary_quote_style() {
227
		return $this->primary_quote_style;
228
	}
229
230
	/**
231
	 * Retrieves the secondary (single) quote style.
232
	 *
233
	 * @return Settings\Quotes
234
	 */
235
	public function secondary_quote_style() {
236
		return $this->secondary_quote_style;
237
	}
238
239
	/**
240
	 * Retrieves the dash style.
241
	 *
242
	 * @return Settings\Dashes
243
	 */
244
	public function dash_style() {
245
		return $this->dash_style;
246
	}
247
248
	/**
249
	 * Retrieves the custom units pattern.
250
	 *
251
	 * @return string The pattern is suitable for inclusion into a regular expression.
252
	 */
253
	public function custom_units() {
254
		return $this->custom_units;
255
	}
256
257
	/**
258
	 * Initialize the PHP_Typography object.
259
	 *
260
	 * @param bool $set_defaults If true, set default values for various properties. Defaults to true.
261
	 */
262
	private function init( $set_defaults = true ) {
263
		$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.
264
265
		$this->dash_style = new Settings\Simple_Dashes( U::EM_DASH, U::THIN_SPACE, U::EN_DASH, U::THIN_SPACE );
266
267
		$this->primary_quote_style   = new Settings\Simple_Quotes( U::DOUBLE_QUOTE_OPEN, U::DOUBLE_QUOTE_CLOSE );
268
		$this->secondary_quote_style = new Settings\Simple_Quotes( U::SINGLE_QUOTE_OPEN, U::SINGLE_QUOTE_CLOSE );
269
270
		// Set up some arrays for quick HTML5 introspection.
271
		$this->self_closing_tags  = array_filter( array_keys( \Masterminds\HTML5\Elements::$html5 ), function( $tag ) {
0 ignored issues
show
Deprecated Code introduced by
The property PHP_Typography\Settings::$self_closing_tags has been deprecated with message: 5.2.0 Not used anymore, will be removed in 6.0.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
272
				return \Masterminds\HTML5\Elements::isA( $tag, \Masterminds\HTML5\Elements::VOID_TAG );
273
			} );
274
		$this->inappropriate_tags = array_flip( DOM::inappropriate_tags() );
0 ignored issues
show
Deprecated Code introduced by
The property PHP_Typography\Settings::$inappropriate_tags has been deprecated with message: 5.2.0 Use DOM::inappropriate_tags() if necessary. Will be removed in 6.0.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
275
276
		if ( $set_defaults ) {
277
			$this->set_defaults();
278
		}
279
	}
280
281
	/**
282
	 * (Re)set various options to their default values.
283
	 */
284
	public function set_defaults() {
285
		// General attributes.
286
		$this->set_tags_to_ignore();
287
		$this->set_classes_to_ignore();
288
		$this->set_ids_to_ignore();
289
290
		// Smart characters.
291
		$this->set_smart_quotes();
292
		$this->set_smart_quotes_primary();
293
		$this->set_smart_quotes_secondary();
294
		$this->set_smart_dashes();
295
		$this->set_smart_dashes_style();
296
		$this->set_smart_ellipses();
297
		$this->set_smart_diacritics();
298
		$this->set_diacritic_language();
299
		$this->set_diacritic_custom_replacements();
300
		$this->set_smart_marks();
301
		$this->set_smart_ordinal_suffix();
302
		$this->set_smart_math();
303
		$this->set_smart_fractions();
304
		$this->set_smart_exponents();
305
306
		// Smart spacing.
307
		$this->set_single_character_word_spacing();
308
		$this->set_fraction_spacing();
309
		$this->set_unit_spacing();
310
		$this->set_french_punctuation_spacing();
311
		$this->set_units();
312
		$this->set_dash_spacing();
313
		$this->set_dewidow();
314
		$this->set_max_dewidow_length();
315
		$this->set_max_dewidow_pull();
316
		$this->set_dewidow_word_number();
317
		$this->set_wrap_hard_hyphens();
318
		$this->set_url_wrap();
319
		$this->set_email_wrap();
320
		$this->set_min_after_url_wrap();
321
		$this->set_space_collapse();
322
		$this->set_true_no_break_narrow_space();
323
324
		// Character styling.
325
		$this->set_style_ampersands();
326
		$this->set_style_caps();
327
		$this->set_style_initial_quotes();
328
		$this->set_style_numbers();
329
		$this->set_style_hanging_punctuation();
330
		$this->set_initial_quote_tags();
331
332
		// Hyphenation.
333
		$this->set_hyphenation();
334
		$this->set_hyphenation_language();
335
		$this->set_min_length_hyphenation();
336
		$this->set_min_before_hyphenation();
337
		$this->set_min_after_hyphenation();
338
		$this->set_hyphenate_headings();
339
		$this->set_hyphenate_all_caps();
340
		$this->set_hyphenate_title_case();
341
		$this->set_hyphenate_compounds();
342
		$this->set_hyphenation_exceptions();
343
344
		// Parser error handling.
345
		$this->set_ignore_parser_errors();
346
	}
347
348
	/**
349
	 * Enable lenient parser error handling (HTML is "best guess" if enabled).
350
	 *
351
	 * @param bool $on Optional. Default false.
352
	 */
353
	public function set_ignore_parser_errors( $on = false ) {
354
		$this->data['parserErrorsIgnore'] = $on;
355
	}
356
357
	/**
358
	 * Sets an optional handler for parser errors. Invalid callbacks will be silently ignored.
359
	 *
360
	 * @param callable|null $handler Optional. A callable that takes an array of error strings as its parameter. Default null.
361
	 */
362
	public function set_parser_errors_handler( $handler = null ) {
363
		if ( ! empty( $handler ) && ! is_callable( $handler ) ) {
364
			return; // Invalid handler, abort.
365
		}
366
367
		$this->data['parserErrorsHandler'] = $handler;
368
	}
369
370
	/**
371
	 * Enable usage of true "no-break narrow space" (&#8239;) instead of the normal no-break space (&nbsp;).
372
	 *
373
	 * @param bool $on Optional. Default false.
374
	 */
375
	public function set_true_no_break_narrow_space( $on = false ) {
376
377
		if ( $on ) {
378
			$this->no_break_narrow_space = U::NO_BREAK_NARROW_SPACE;
379
		} else {
380
			$this->no_break_narrow_space = U::NO_BREAK_SPACE;
381
		}
382
	}
383
384
	/**
385
	 * Sets tags for which the typography of their children will be left untouched.
386
	 *
387
	 * @param string|array $tags A comma separated list or an array of tag names.
388
	 */
389
	public function set_tags_to_ignore( $tags = [ 'code', 'head', 'kbd', 'object', 'option', 'pre', 'samp', 'script', 'noscript', 'noembed', 'select', 'style', 'textarea', 'title', 'var', 'math' ] ) {
390
		// Ensure that we pass only lower-case tag names to XPath.
391
		$tags = array_filter( array_map( 'strtolower', Strings::maybe_split_parameters( $tags ) ), 'ctype_alnum' );
392
393
		$this->data['ignoreTags'] = array_unique( array_merge( $tags, $this->inappropriate_tags ) );
0 ignored issues
show
Deprecated Code introduced by
The property PHP_Typography\Settings::$inappropriate_tags has been deprecated with message: 5.2.0 Use DOM::inappropriate_tags() if necessary. Will be removed in 6.0.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
394
	}
395
396
	/**
397
	 * Sets classes for which the typography of their children will be left untouched.
398
	 *
399
	 * @param string|array $classes A comma separated list or an array of class names.
400
	 */
401
	public function set_classes_to_ignore( $classes = [ 'vcard', 'noTypo' ] ) {
402
		$this->data['ignoreClasses'] = Strings::maybe_split_parameters( $classes );
403
	}
404
405
	/**
406
	 * Sets IDs for which the typography of their children will be left untouched.
407
	 *
408
	 * @param string|array $ids A comma separated list or an array of tag names.
409
	 */
410
	public function set_ids_to_ignore( $ids = [] ) {
411
		$this->data['ignoreIDs'] = Strings::maybe_split_parameters( $ids );
412
	}
413
414
	/**
415
	 * Enables/disables typographic quotes.
416
	 *
417
	 * @param bool $on Optional. Default true.
418
	 */
419
	public function set_smart_quotes( $on = true ) {
420
		$this->data['smartQuotes'] = $on;
421
	}
422
423
	/**
424
	 * Sets the style for primary ('double') 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 'doubleCurled.
444
	 *
445
	 * @throws \DomainException Thrown if $style constant is invalid.
446
	 */
447
	public function set_smart_quotes_primary( $style = Quote_Style::DOUBLE_CURLED ) {
448
		$this->primary_quote_style = $this->get_quote_style( $style );
449
	}
450
451
	/**
452
	 * Sets the style for secondary ('single') quotemarks.
453
	 *
454
	 * Allowed values for $style:
455
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
456
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
457
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
458
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
459
	 * "singleCurled" => "&lsquo;foo&rsquo;",
460
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
461
	 * "singleLow9" => "&sbquo;foo&rsquo;",
462
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
463
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
464
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
465
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
466
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
467
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
468
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
469
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
470
	 *
471
	 * @param string $style Defaults to 'singleCurled'.
472
	 *
473
	 * @throws \DomainException Thrown if $style constant is invalid.
474
	 */
475
	public function set_smart_quotes_secondary( $style = Quote_Style::SINGLE_CURLED ) {
476
		$this->secondary_quote_style = $this->get_quote_style( $style );
477
	}
478
479
	/**
480
	 * Retrieves a Quotes instance from a given style.
481
	 *
482
	 * @param  Settings\Quotes|string $style A Quotes instance or a quote style constant.
483
	 *
484
	 * @throws \DomainException Thrown if $style constant is invalid.
485
	 *
486
	 * @return Settings\Quotes
487
	 */
488
	protected function get_quote_style( $style ) {
489
		return $this->get_style( $style, Settings\Quotes::class, [ Quote_Style::class, 'get_styled_quotes' ], 'quote' );
490
	}
491
492
	/**
493
	 * Retrieves an object from a given style.
494
	 *
495
	 * @param  object|string $style          A style object instance or a style constant.
496
	 * @param  string        $expected_class A class name.
497
	 * @param  callable      $get_style      A function that returns a style object from a given style constant.
498
	 * @param  string        $description    Style description for the exception message.
499
	 *
500
	 * @throws \DomainException Thrown if $style constant is invalid.
501
	 *
502
	 * @return object An instance of $expected_class.
503
	 */
504
	protected function get_style( $style, $expected_class, callable $get_style, $description ) {
505
		if ( $style instanceof $expected_class ) {
506
			$object = $style;
507
		} else {
508
			$object = $get_style( $style, $this );
509
		}
510
511
		if ( ! $object instanceof $expected_class ) {
512
			throw new \DomainException( "Invalid $description style $style." );
513
		}
514
515
		return $object;
516
	}
517
518
	/**
519
	 * Enables/disables replacement of "a--a" with En Dash " -- " and "---" with Em Dash.
520
	 *
521
	 * @param bool $on Optional. Default true.
522
	 */
523
	public function set_smart_dashes( $on = true ) {
524
		$this->data['smartDashes'] = $on;
525
	}
526
527
	/**
528
	 * Sets the typographical conventions used by smart_dashes.
529
	 *
530
	 * Allowed values for $style:
531
	 * - "traditionalUS"
532
	 * - "international"
533
	 *
534
	 * @param string|Settings\Dashes $style Optional. Default Dash_Style::TRADITIONAL_US.
535
	 *
536
	 * @throws \DomainException Thrown if $style constant is invalid.
537
	 */
538
	public function set_smart_dashes_style( $style = Dash_Style::TRADITIONAL_US ) {
539
		$this->dash_style = $this->get_style( $style, Settings\Dashes::class, [ Dash_Style::class, 'get_styled_dashes' ], 'dash' );
540
	}
541
542
	/**
543
	 * Enables/disables replacement of "..." with "…".
544
	 *
545
	 * @param bool $on Optional. Default true.
546
	 */
547
	public function set_smart_ellipses( $on = true ) {
548
		$this->data['smartEllipses'] = $on;
549
	}
550
551
	/**
552
	 * Enables/disables replacement "creme brulee" with "crème brûlée".
553
	 *
554
	 * @param bool $on Optional. Default true.
555
	 */
556
	public function set_smart_diacritics( $on = true ) {
557
		$this->data['smartDiacritics'] = $on;
558
	}
559
560
	/**
561
	 * Sets the language used for diacritics replacements.
562
	 *
563
	 * @param string $lang Has to correspond to a filename in 'diacritics'. Optional. Default 'en-US'.
564
	 */
565
	public function set_diacritic_language( $lang = 'en-US' ) {
566
		if ( isset( $this->data['diacriticLanguage'] ) && $this->data['diacriticLanguage'] === $lang ) {
567
			return;
568
		}
569
570
		$this->data['diacriticLanguage'] = $lang;
571
		$language_file_name = dirname( __FILE__ ) . '/diacritics/' . $lang . '.json';
572
573
		if ( file_exists( $language_file_name ) ) {
574
			$diacritics_file = json_decode( file_get_contents( $language_file_name ), true );
575
			$this->data['diacriticWords'] = $diacritics_file['diacritic_words'];
576
		} else {
577
			unset( $this->data['diacriticWords'] );
578
		}
579
580
		$this->update_diacritics_replacement_arrays();
581
	}
582
583
	/**
584
	 * Sets up custom diacritics replacements.
585
	 *
586
	 * @param string|array $custom_replacements An array formatted [needle=>replacement, needle=>replacement...],
587
	 *                                          or a string formatted `"needle"=>"replacement","needle"=>"replacement",...
588
	 */
589
	public function set_diacritic_custom_replacements( $custom_replacements = [] ) {
590
		if ( ! is_array( $custom_replacements ) ) {
591
			$custom_replacements = $this->parse_diacritics_replacement_string( $custom_replacements );
592
		}
593
594
		$this->data['diacriticCustomReplacements'] = Arrays::array_map_assoc( function( $key, $replacement ) {
595
			$key         = strip_tags( trim( $key ) );
596
			$replacement = strip_tags( trim( $replacement ) );
597
598
			if ( ! empty( $key ) && ! empty( $replacement ) ) {
599
				return [ $key, $replacement ];
600
			} else {
601
				return [];
602
			}
603
		}, $custom_replacements );
604
605
		$this->update_diacritics_replacement_arrays();
606
	}
607
608
	/**
609
	 * Parses a custom diacritics replacement string into an array.
610
	 *
611
	 * @param string $custom_replacements A string formatted `"needle"=>"replacement","needle"=>"replacement",...
612
	 *
613
	 * @return array
614
	 */
615
	private function parse_diacritics_replacement_string( $custom_replacements ) {
616
		return Arrays::array_map_assoc( function( $key, $replacement ) {
617
618
			// Account for single and double quotes in keys ...
619
			if ( preg_match( '/("|\')((?:(?!\1).)+)(?:\1\s*=>)/', $replacement, $match ) ) {
620
				$key = $match[2];
621
			}
622
623
			// ... and values.
624
			if ( preg_match( '/(?:=>\s*("|\'))((?:(?!\1).)+)(?:\1)/', $replacement, $match ) ) {
625
				$replacement = $match[2];
626
			}
627
628
			return [ $key, $replacement ];
629
		}, preg_split( '/,/', $custom_replacements, -1, PREG_SPLIT_NO_EMPTY ) );
630
	}
631
632
	/**
633
	 * Update the pattern and replacement arrays in $settings['diacriticReplacement'].
634
	 *
635
	 * Should be called whenever a new diacritics replacement language is selected or
636
	 * when the custom replacements are updated.
637
	 */
638
	private function update_diacritics_replacement_arrays() {
639
		$patterns = [];
640
		$replacements = [];
641
642
		if ( ! empty( $this->data['diacriticCustomReplacements'] ) ) {
643
			$this->parse_diacritics_rules( $this->data['diacriticCustomReplacements'], $patterns, $replacements );
644
		}
645
		if ( ! empty( $this->data['diacriticWords'] ) ) {
646
			$this->parse_diacritics_rules( $this->data['diacriticWords'], $patterns, $replacements );
647
		}
648
649
		$this->data['diacriticReplacement'] = [
650
			'patterns'     => $patterns,
651
			'replacements' => $replacements,
652
		];
653
	}
654
655
	/**
656
	 * Parse an array of diacritics rules.
657
	 *
658
	 * @param array $diacritics_rules The rules ( $word => $replacement ).
659
	 * @param array $patterns         Resulting patterns. Passed by reference.
660
	 * @param array $replacements     Resulting replacements. Passed by reference.
661
	 */
662
	private function parse_diacritics_rules( array $diacritics_rules, array &$patterns, array &$replacements ) {
663
664
		foreach ( $diacritics_rules as $needle => $replacement ) {
665
			$patterns[] = '/\b(?<!\w[' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . '])' . $needle . '\b(?![' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . ']\w)/u';
666
			$replacements[ $needle ] = $replacement;
667
		}
668
	}
669
670
	/**
671
	 * Enables/disables replacement of (r) (c) (tm) (sm) (p) (R) (C) (TM) (SM) (P) with ® © ™ ℠ ℗.
672
	 *
673
	 * @param bool $on Optional. Default true.
674
	 */
675
	public function set_smart_marks( $on = true ) {
676
		$this->data['smartMarks'] = $on;
677
	}
678
679
	/**
680
	 * Enables/disables proper mathematical symbols.
681
	 *
682
	 * @param bool $on Optional. Default true.
683
	 */
684
	public function set_smart_math( $on = true ) {
685
		$this->data['smartMath'] = $on;
686
	}
687
688
	/**
689
	 * Enables/disables replacement of 2^2 with 2<sup>2</sup>
690
	 *
691
	 * @param bool $on Optional. Default true.
692
	 */
693
	public function set_smart_exponents( $on = true ) {
694
		$this->data['smartExponents'] = $on;
695
	}
696
697
	/**
698
	 * Enables/disables replacement of 1/4 with <sup>1</sup>&#8260;<sub>4</sub>.
699
	 *
700
	 * @param bool $on Optional. Default true.
701
	 */
702
	public function set_smart_fractions( $on = true ) {
703
		$this->data['smartFractions'] = $on;
704
	}
705
706
	/**
707
	 * Enables/disables replacement of 1st with 1<sup>st</sup>.
708
	 *
709
	 * @param bool $on Optional. Default true.
710
	 */
711
	public function set_smart_ordinal_suffix( $on = true ) {
712
		$this->data['smartOrdinalSuffix'] = $on;
713
	}
714
715
	/**
716
	 * Enables/disables forcing single character words to next line with the insertion of &nbsp;.
717
	 *
718
	 * @param bool $on Optional. Default true.
719
	 */
720
	public function set_single_character_word_spacing( $on = true ) {
721
		$this->data['singleCharacterWordSpacing'] = $on;
722
	}
723
724
	/**
725
	 * Enables/disables fraction spacing.
726
	 *
727
	 * @param bool $on Optional. Default true.
728
	 */
729
	public function set_fraction_spacing( $on = true ) {
730
		$this->data['fractionSpacing'] = $on;
731
	}
732
733
	/**
734
	 * Enables/disables keeping units and values together with the insertion of &nbsp;.
735
	 *
736
	 * @param bool $on Optional. Default true.
737
	 */
738
	public function set_unit_spacing( $on = true ) {
739
		$this->data['unitSpacing'] = $on;
740
	}
741
742
	/**
743
	 * Enables/disables numbered abbreviations like "ISO 9000" together with the insertion of &nbsp;.
744
	 *
745
	 * @param bool $on Optional. Default true.
746
	 */
747
	public function set_numbered_abbreviation_spacing( $on = true ) {
748
		$this->data['numberedAbbreviationSpacing'] = $on;
749
	}
750
751
	/**
752
	 * Enables/disables extra whitespace before certain punction marks, as is the French custom.
753
	 *
754
	 * @param bool $on Optional. Default true.
755
	 */
756
	public function set_french_punctuation_spacing( $on = true ) {
757
		$this->data['frenchPunctuationSpacing'] = $on;
758
	}
759
760
	/**
761
	 * Sets the list of units to keep together with their values.
762
	 *
763
	 * @param string|array $units A comma separated list or an array of units.
764
	 */
765
	public function set_units( $units = [] ) {
766
		$this->data['units'] = Strings::maybe_split_parameters( $units );
767
		$this->update_unit_pattern( $this->data['units'] );
768
	}
769
770
	/**
771
	 * Update components and pattern for matching both standard and custom units.
772
	 *
773
	 * @param array $units An array of unit names.
774
	 */
775
	private function update_unit_pattern( array $units ) {
776
		// Update components & regex pattern.
777
		foreach ( $units as $index => $unit ) {
778
			// Escape special chars.
779
			$units[ $index ] = preg_replace( '#([\[\\\^\$\.\|\?\*\+\(\)\{\}])#', '\\\\$1', $unit );
780
		}
781
		$this->custom_units = implode( '|', $units );
782
		$this->custom_units .= ( $this->custom_units ) ? '|' : '';
783
	}
784
785
	/**
786
	 * Enables/disables wrapping of Em and En dashes are in thin spaces.
787
	 *
788
	 * @param bool $on Optional. Default true.
789
	 */
790
	public function set_dash_spacing( $on = true ) {
791
		$this->data['dashSpacing'] = $on;
792
	}
793
794
	/**
795
	 * Enables/disables removal of extra whitespace characters.
796
	 *
797
	 * @param bool $on Optional. Default true.
798
	 */
799
	public function set_space_collapse( $on = true ) {
800
		$this->data['spaceCollapse'] = $on;
801
	}
802
803
	/**
804
	 * Enables/disables widow handling.
805
	 *
806
	 * @param bool $on Optional. Default true.
807
	 */
808
	public function set_dewidow( $on = true ) {
809
		$this->data['dewidow'] = $on;
810
	}
811
812
	/**
813
	 * Sets the maximum length of widows that will be protected.
814
	 *
815
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
816
	 */
817
	public function set_max_dewidow_length( $length = 5 ) {
818
		$length = ( $length > 1 ) ? $length : 5;
819
820
		$this->data['dewidowMaxLength'] = $length;
821
	}
822
823
	/**
824
	 * Sets the maximum number of words considered for dewidowing.
825
	 *
826
	 * @param int $number Defaults to 1. Only 1, 2 and 3 are valid.
827
	 */
828
	public function set_dewidow_word_number( $number = 1 ) {
829
		$number = ( $number > 3 || $number < 1 ) ? 1 : $number;
830
831
		$this->data['dewidowWordNumber'] = $number;
832
	}
833
834
	/**
835
	 * Sets the maximum length of pulled text to keep widows company.
836
	 *
837
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
838
	 */
839
	public function set_max_dewidow_pull( $length = 5 ) {
840
		$length = ( $length > 1 ) ? $length : 5;
841
842
		$this->data['dewidowMaxPull'] = $length;
843
	}
844
845
	/**
846
	 * Enables/disables wrapping at internal hard hyphens with the insertion of a zero-width-space.
847
	 *
848
	 * @param bool $on Optional. Default true.
849
	 */
850
	public function set_wrap_hard_hyphens( $on = true ) {
851
		$this->data['hyphenHardWrap'] = $on;
852
	}
853
854
	/**
855
	 * Enables/disables wrapping of urls.
856
	 *
857
	 * @param bool $on Optional. Default true.
858
	 */
859
	public function set_url_wrap( $on = true ) {
860
		$this->data['urlWrap'] = $on;
861
	}
862
863
	/**
864
	 * Enables/disables wrapping of email addresses.
865
	 *
866
	 * @param bool $on Optional. Default true.
867
	 */
868
	public function set_email_wrap( $on = true ) {
869
		$this->data['emailWrap'] = $on;
870
	}
871
872
	/**
873
	 * Sets the minimum character requirement after an URL wrapping point.
874
	 *
875
	 * @param int $length Defaults to 5. Trying to set the value to less than 1 resets the length to the default.
876
	 */
877
	public function set_min_after_url_wrap( $length = 5 ) {
878
		$length = ( $length > 0 ) ? $length : 5;
879
880
		$this->data['urlMinAfterWrap'] = $length;
881
	}
882
883
	/**
884
	 * Enables/disables wrapping of ampersands in <span class="amp">.
885
	 *
886
	 * @param bool $on Optional. Default true.
887
	 */
888
	public function set_style_ampersands( $on = true ) {
889
		$this->data['styleAmpersands'] = $on;
890
	}
891
892
	/**
893
	 * Enables/disables wrapping caps in <span class="caps">.
894
	 *
895
	 * @param bool $on Optional. Default true.
896
	 */
897
	public function set_style_caps( $on = true ) {
898
		$this->data['styleCaps'] = $on;
899
	}
900
901
	/**
902
	 * Enables/disables wrapping of initial quotes in <span class="quo"> or <span class="dquo">.
903
	 *
904
	 * @param bool $on Optional. Default true.
905
	 */
906
	public function set_style_initial_quotes( $on = true ) {
907
		$this->data['styleInitialQuotes'] = $on;
908
	}
909
910
	/**
911
	 * Enables/disables wrapping of numbers in <span class="numbers">.
912
	 *
913
	 * @param bool $on Optional. Default true.
914
	 */
915
	public function set_style_numbers( $on = true ) {
916
		$this->data['styleNumbers'] = $on;
917
	}
918
919
	/**
920
	 * Enables/disables wrapping of punctiation and wide characters in <span class="pull-*">.
921
	 *
922
	 * @param bool $on Optional. Default true.
923
	 */
924
	public function set_style_hanging_punctuation( $on = true ) {
925
		$this->data['styleHangingPunctuation'] = $on;
926
	}
927
928
	/**
929
	 * Sets the list of tags where initial quotes and guillemets should be styled.
930
	 *
931
	 * @param string|array $tags A comma separated list or an array of tag names.
932
	 */
933
	public function set_initial_quote_tags( $tags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'li', 'dd', 'dt' ] ) {
934
		// Make array if handed a list of tags as a string.
935
		if ( ! is_array( $tags ) ) {
936
			$tags = preg_split( '/[^a-z0-9]+/', $tags, -1, PREG_SPLIT_NO_EMPTY );
937
		}
938
939
		// Store the tag array inverted (with the tagName as its index for faster lookup).
940
		$this->data['initialQuoteTags'] = array_change_key_case( array_flip( $tags ), CASE_LOWER );
941
	}
942
943
	/**
944
	 * Enables/disables hyphenation.
945
	 *
946
	 * @param bool $on Optional. Default true.
947
	 */
948
	public function set_hyphenation( $on = true ) {
949
		$this->data['hyphenation'] = $on;
950
	}
951
952
	/**
953
	 * Sets the hyphenation pattern language.
954
	 *
955
	 * @param string $lang Has to correspond to a filename in 'lang'. Optional. Default 'en-US'.
956
	 */
957
	public function set_hyphenation_language( $lang = 'en-US' ) {
958
		if ( isset( $this->data['hyphenLanguage'] ) && $this->data['hyphenLanguage'] === $lang ) {
959
			return; // Bail out, no need to do anything.
960
		}
961
962
		$this->data['hyphenLanguage'] = $lang;
963
	}
964
965
	/**
966
	 * Sets the minimum length of a word that may be hyphenated.
967
	 *
968
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
969
	 */
970
	public function set_min_length_hyphenation( $length = 5 ) {
971
		$length = ( $length > 1 ) ? $length : 5;
972
973
		$this->data['hyphenMinLength'] = $length;
974
	}
975
976
	/**
977
	 * Sets the minimum character requirement before a hyphenation point.
978
	 *
979
	 * @param int $length Defaults to 3. Trying to set the value to less than 1 resets the length to the default.
980
	 */
981
	public function set_min_before_hyphenation( $length = 3 ) {
982
		$length = ( $length > 0 ) ? $length : 3;
983
984
		$this->data['hyphenMinBefore'] = $length;
985
	}
986
987
	/**
988
	 * Sets the minimum character requirement after a hyphenation point.
989
	 *
990
	 * @param int $length Defaults to 2. Trying to set the value to less than 1 resets the length to the default.
991
	 */
992
	public function set_min_after_hyphenation( $length = 2 ) {
993
		$length = ( $length > 0 ) ? $length : 2;
994
995
		$this->data['hyphenMinAfter'] = $length;
996
	}
997
998
	/**
999
	 * Enables/disables hyphenation of titles and headings.
1000
	 *
1001
	 * @param bool $on Optional. Default true.
1002
	 */
1003
	public function set_hyphenate_headings( $on = true ) {
1004
		$this->data['hyphenateTitle'] = $on;
1005
	}
1006
1007
	/**
1008
	 * Enables/disables hyphenation of words set completely in capital letters.
1009
	 *
1010
	 * @param bool $on Optional. Default true.
1011
	 */
1012
	public function set_hyphenate_all_caps( $on = true ) {
1013
		$this->data['hyphenateAllCaps'] = $on;
1014
	}
1015
1016
	/**
1017
	 * Enables/disables hyphenation of words starting with a capital letter.
1018
	 *
1019
	 * @param bool $on Optional. Default true.
1020
	 */
1021
	public function set_hyphenate_title_case( $on = true ) {
1022
		$this->data['hyphenateTitleCase'] = $on;
1023
	}
1024
1025
	/**
1026
	 * Enables/disables hyphenation of compound words (e.g. "editor-in-chief").
1027
	 *
1028
	 * @param bool $on Optional. Default true.
1029
	 */
1030
	public function set_hyphenate_compounds( $on = true ) {
1031
		$this->data['hyphenateCompounds'] = $on;
1032
	}
1033
1034
	/**
1035
	 * Sets custom word hyphenations.
1036
	 *
1037
	 * @param string|array $exceptions An array of words with all hyphenation points marked with a hard hyphen (or a string list of such words).
1038
	 *        In the latter case, only alphanumeric characters and hyphens are recognized. The default is empty.
1039
	 */
1040
	public function set_hyphenation_exceptions( $exceptions = [] ) {
1041
		$this->data['hyphenationCustomExceptions'] = Strings::maybe_split_parameters( $exceptions );
1042
	}
1043
1044
	/**
1045
	 * Retrieves a unique hash value for the current settings.
1046
	 *
1047
	 * @since 5.2.0 The new parameter $raw_output has been added.
1048
	 *
1049
	 * @param int  $max_length Optional. The maximum number of bytes returned (0 for unlimited). Default 16.
1050
	 * @param bool $raw_output Optional. Wether to return raw binary data for the hash. Default true.
1051
	 *
1052
	 * @return string A binary hash value for the current settings limited to $max_length.
1053
	 */
1054
	public function get_hash( $max_length = 16, $raw_output = true ) {
1055
		$hash = md5( json_encode( $this ), $raw_output );
1056
1057
		if ( $max_length < strlen( $hash ) && $max_length > 0 ) {
1058
			$hash = substr( $hash, 0, $max_length );
1059
		}
1060
1061
		return $hash;
1062
	}
1063
}
1064