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 (#118)
by Der Mundschenk
01:44
created

Settings::parse_diacritics_replacement_string()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2.0054

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 15
rs 10
ccs 8
cts 9
cp 0.8889
cc 2
nc 1
nop 1
crap 2.0054
1
<?php
2
/**
3
 *  This file is part of PHP-Typography.
4
 *
5
 *  Copyright 2014-2019 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
use PHP_Typography\Settings\Quotes;
33
34
/**
35
 * Store settings for the PHP_Typography class.
36
 *
37
 * @author Peter Putzer <[email protected]>
38
 *
39
 * @since 4.0.0
40
 * @since 6.5.0 The protected property $no_break_narrow_space has been deprecated.
41
 */
42
class Settings implements \ArrayAccess, \JsonSerializable {
43
44
	/**
45
	 * The current no-break narrow space character.
46
	 *
47
	 * @deprecated 6.5.0
48
	 *
49
	 * @var string
50
	 */
51
	protected $no_break_narrow_space;
52
53
	/**
54
	 * Primary quote style.
55
	 *
56
	 * @var Quotes
57
	 */
58
	protected $primary_quote_style;
59
60
	/**
61
	 * Secondary quote style.
62
	 *
63
	 * @var Quotes
64
	 */
65
	protected $secondary_quote_style;
66
67
	/**
68
	 * A regex pattern for custom units (or the empty string).
69
	 *
70
	 * @var string
71
	 */
72
	protected $custom_units = '';
73
74
	/**
75
	 * A hashmap of settings for the various typographic options.
76
	 *
77
	 * @var array
78
	 */
79
	protected $data = [];
80
81
	/**
82
	 * The current dash style.
83
	 *
84
	 * @var Settings\Dashes
85
	 */
86
	protected $dash_style;
87
88
	/**
89
	 * The Unicode character mapping (some characters still have compatibility issues).
90
	 *
91
	 * @since 6.5.0
92
	 *
93
	 * @var string[]
94
	 */
95
	protected $unicode_mapping;
96
97
	/**
98
	 * Sets up a new Settings object.
99
	 *
100
	 * @since 6.0.0 If $set_defaults is `false`, the settings object is not fully
101
	 *              initialized unless `set_smart_quotes_primary`,
102
	 *              `set_smart_quotes_secondary`, `set_smart_dashes_style` and
103
	 *              `set_true_no_break_narrow_space` are called explicitly.
104
	 * @since 6.5.0 A (partial) character mapping can be given to remap certain
105
	 *              characters.
106
	 *
107
	 * @param bool     $set_defaults If true, set default values for various properties. Defaults to true.
108
	 * @param string[] $mapping      A (partial) Unicode character remapping.
109
	 */
110 1
	public function __construct( $set_defaults = true, array $mapping = [] ) {
111 1
		if ( $set_defaults ) {
112 1
			$this->set_defaults();
113
		}
114
115
		// Merge default character mapping with given mapping.
116 1
		$this->unicode_mapping = \array_merge( U::DEFAULT_MAPPING, $mapping );
117
118
		// Keep backwards compatibility.
119 1
		$this->no_break_narrow_space = $this->unicode_mapping[ U::NO_BREAK_NARROW_SPACE ];
0 ignored issues
show
Deprecated Code introduced by
The property PHP_Typography\Settings::$no_break_narrow_space has been deprecated: 6.5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

119
		/** @scrutinizer ignore-deprecated */ $this->no_break_narrow_space = $this->unicode_mapping[ U::NO_BREAK_NARROW_SPACE ];

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...
120 1
	}
121
122
	/**
123
	 * Provides access to named settings (object syntax).
124
	 *
125
	 * @param string $key The settings key.
126
	 *
127
	 * @return mixed
128
	 */
129 1
	public function &__get( $key ) {
130 1
		return $this->data[ $key ];
131
	}
132
133
	/**
134
	 * Changes a named setting (object syntax).
135
	 *
136
	 * @param string $key   The settings key.
137
	 * @param mixed  $value The settings value.
138
	 */
139 1
	public function __set( $key, $value ) {
140 1
		$this->data[ $key ] = $value;
141 1
	}
142
143
	/**
144
	 * Checks if a named setting exists (object syntax).
145
	 *
146
	 * @param string $key The settings key.
147
	 */
148 1
	public function __isset( $key ) {
149 1
		return isset( $this->data[ $key ] );
150
	}
151
152
	/**
153
	 * Unsets a named setting.
154
	 *
155
	 * @param string $key The settings key.
156
	 */
157 1
	public function __unset( $key ) {
158 1
		unset( $this->data[ $key ] );
159 1
	}
160
161
	/**
162
	 * Changes a named setting (array syntax).
163
	 *
164
	 * @param string $offset The settings key.
165
	 * @param mixed  $value  The settings value.
166
	 */
167 1
	public function offsetSet( $offset, $value ) {
168 1
		if ( ! empty( $offset ) ) {
169 1
			$this->data[ $offset ] = $value;
170
		}
171 1
	}
172
173
	/**
174
	 * Checks if a named setting exists (array syntax).
175
	 *
176
	 * @param string $offset The settings key.
177
	 */
178 1
	public function offsetExists( $offset ) {
179 1
		return isset( $this->data[ $offset ] );
180
	}
181
182
	/**
183
	 * Unsets a named setting (array syntax).
184
	 *
185
	 * @param string $offset The settings key.
186
	 */
187 1
	public function offsetUnset( $offset ) {
188 1
		unset( $this->data[ $offset ] );
189 1
	}
190
191
	/**
192
	 * Provides access to named settings (array syntax).
193
	 *
194
	 * @param string $offset The settings key.
195
	 *
196
	 * @return mixed
197
	 */
198 1
	public function offsetGet( $offset ) {
199 1
		return isset( $this->data[ $offset ] ) ? $this->data[ $offset ] : null;
200
	}
201
202
	/**
203
	 * Provides a JSON serialization of the settings.
204
	 *
205
	 * @return mixed
206
	 */
207 1
	public function jsonSerialize() {
208 1
		return \array_merge(
209 1
			$this->data,
210
			[
211 1
				'unicode_mapping'       => $this->unicode_mapping,
212 1
				'primary_quotes'        => "{$this->primary_quote_style->open()}|{$this->primary_quote_style->close()}",
213 1
				'secondary_quotes'      => "{$this->secondary_quote_style->open()}|{$this->secondary_quote_style->close()}",
214 1
				'dash_style'            => "{$this->dash_style->interval_dash()}|{$this->dash_style->interval_space()}|{$this->dash_style->parenthetical_dash()}|{$this->dash_style->parenthetical_space()}",
215 1
				'custom_units'          => $this->custom_units,
216
			]
217
		);
218
	}
219
220
	/**
221
	 * Retrieves the unicode character array (including any remappings).
222
	 *
223
	 * @since 6.5.0
224
	 *
225
	 * @return string[]
226
	 */
227
	public function character_mapping() {
228
		return $this->unicode_mapping;
229
	}
230
231
	/**
232
	 * Remaps a unicode character to another one.
233
	 *
234
	 * @since 6.5.0
235
	 *
236
	 * @param  string $char     The remapped character.
237
	 * @param  string $new_char The character to actually use.
238
	 *
239
	 * @return bool             Returns true on success, false otherwise.
240
	 */
241
	public function remap_character( $char, $new_char ) {
242
		if ( isset( $this->unicode_mapping[ $char ] ) ) {
243
			$this->unicode_mapping[ $char ] = $new_char;
244
245
			// Compatibility with the old way of setting the no-break narrow space.
246
			if ( U::NO_BREAK_NARROW_SPACE === $char ) {
247
				$this->no_break_narrow_space = $new_char;
248
			}
249
250
			return true;
251
		}
252
253
		return false;
254
	}
255
256
	/**
257
	 * Retrieves the current non-breaking narrow space character (either the
258
	 * regular non-breaking space &nbsp; or the the true non-breaking narrow space &#8239;).
259
	 *
260
	 * @deprecated 6.5.0 Use ::character_mapping() instead.
261
	 *
262
	 * @return string
263
	 */
264 1
	public function no_break_narrow_space() {
265 1
		return $this->no_break_narrow_space;
0 ignored issues
show
Deprecated Code introduced by
The property PHP_Typography\Settings::$no_break_narrow_space has been deprecated: 6.5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

265
		return /** @scrutinizer ignore-deprecated */ $this->no_break_narrow_space;

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...
266
	}
267
268
	/**
269
	 * Retrieves the primary (double) quote style.
270
	 *
271
	 * @return Quotes
272
	 */
273 1
	public function primary_quote_style() {
274 1
		return $this->primary_quote_style;
275
	}
276
277
	/**
278
	 * Retrieves the secondary (single) quote style.
279
	 *
280
	 * @return Quotes
281
	 */
282 1
	public function secondary_quote_style() {
283 1
		return $this->secondary_quote_style;
284
	}
285
286
	/**
287
	 * Retrieves the dash style.
288
	 *
289
	 * @return Settings\Dashes
290
	 */
291 1
	public function dash_style() {
292 1
		return $this->dash_style;
293
	}
294
295
	/**
296
	 * Retrieves the custom units pattern.
297
	 *
298
	 * @return string The pattern is suitable for inclusion into a regular expression.
299
	 */
300 1
	public function custom_units() {
301 1
		return $this->custom_units;
302
	}
303
304
	/**
305
	 * (Re)set various options to their default values.
306
	 */
307 1
	public function set_defaults() {
308
		// General attributes.
309 1
		$this->set_tags_to_ignore();
310 1
		$this->set_classes_to_ignore();
311 1
		$this->set_ids_to_ignore();
312
313
		// Smart characters.
314 1
		$this->set_smart_quotes();
315 1
		$this->set_smart_quotes_primary();
316 1
		$this->set_smart_quotes_secondary();
317 1
		$this->set_smart_quotes_exceptions();
318 1
		$this->set_smart_dashes();
319 1
		$this->set_smart_dashes_style();
320 1
		$this->set_smart_ellipses();
321 1
		$this->set_smart_diacritics();
322 1
		$this->set_diacritic_language();
323 1
		$this->set_diacritic_custom_replacements();
324 1
		$this->set_smart_marks();
325 1
		$this->set_smart_ordinal_suffix();
326 1
		$this->set_smart_ordinal_suffix_match_roman_numerals();
327 1
		$this->set_smart_math();
328 1
		$this->set_smart_fractions();
329 1
		$this->set_smart_exponents();
330 1
		$this->set_smart_area_units();
331
332
		// Smart spacing.
333 1
		$this->set_single_character_word_spacing();
334 1
		$this->set_fraction_spacing();
335 1
		$this->set_unit_spacing();
336 1
		$this->set_french_punctuation_spacing();
337 1
		$this->set_units();
338 1
		$this->set_dash_spacing();
339 1
		$this->set_dewidow();
340 1
		$this->set_max_dewidow_length();
341 1
		$this->set_max_dewidow_pull();
342 1
		$this->set_dewidow_word_number();
343 1
		$this->set_wrap_hard_hyphens();
344 1
		$this->set_url_wrap();
345 1
		$this->set_email_wrap();
346 1
		$this->set_min_after_url_wrap();
347 1
		$this->set_space_collapse();
348 1
		$this->set_true_no_break_narrow_space();
0 ignored issues
show
Deprecated Code introduced by
The function PHP_Typography\Settings:...no_break_narrow_space() has been deprecated: 6.5.0 Use ::remap_character() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

348
		/** @scrutinizer ignore-deprecated */ $this->set_true_no_break_narrow_space();

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

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

Loading history...
349
350
		// Character styling.
351 1
		$this->set_style_ampersands();
352 1
		$this->set_style_caps();
353 1
		$this->set_style_initial_quotes();
354 1
		$this->set_style_numbers();
355 1
		$this->set_style_hanging_punctuation();
356 1
		$this->set_initial_quote_tags();
357
358
		// Hyphenation.
359 1
		$this->set_hyphenation();
360 1
		$this->set_hyphenation_language();
361 1
		$this->set_min_length_hyphenation();
362 1
		$this->set_min_before_hyphenation();
363 1
		$this->set_min_after_hyphenation();
364 1
		$this->set_hyphenate_headings();
365 1
		$this->set_hyphenate_all_caps();
366 1
		$this->set_hyphenate_title_case();
367 1
		$this->set_hyphenate_compounds();
368 1
		$this->set_hyphenation_exceptions();
369
370
		// Parser error handling.
371 1
		$this->set_ignore_parser_errors();
372 1
	}
373
374
	/**
375
	 * Enable lenient parser error handling (HTML is "best guess" if enabled).
376
	 *
377
	 * @param bool $on Optional. Default false.
378
	 */
379 1
	public function set_ignore_parser_errors( $on = false ) {
380 1
		$this->data['parserErrorsIgnore'] = $on;
381 1
	}
382
383
	/**
384
	 * Sets an optional handler for parser errors. Invalid callbacks will be silently ignored.
385
	 *
386
	 * @since 6.0.0. callable type is enforced via typehinting.
387
	 *
388
	 * @param callable|null $handler Optional. A callable that takes an array of error strings as its parameter. Default null.
389
	 */
390 2
	public function set_parser_errors_handler( callable $handler = null ) {
391 2
		$this->data['parserErrorsHandler'] = $handler;
392 2
	}
393
394
	/**
395
	 * Enable usage of true "no-break narrow space" (&#8239;) instead of the normal no-break space (&nbsp;).
396
	 *
397
	 * @deprecated 6.5.0 Use ::remap_character() instead.
398
	 *
399
	 * @param bool $on Optional. Default false.
400
	 */
401 1
	public function set_true_no_break_narrow_space( $on = false ) {
402
403 1
		if ( $on ) {
404 1
			$this->remap_character( U::NO_BREAK_NARROW_SPACE, U::NO_BREAK_NARROW_SPACE );
405
		} else {
406 1
			$this->remap_character( U::NO_BREAK_NARROW_SPACE, U::NO_BREAK_SPACE );
407
		}
408 1
	}
409
410
	/**
411
	 * Sets tags for which the typography of their children will be left untouched.
412
	 *
413
	 * @param string|array $tags A comma separated list or an array of tag names.
414
	 */
415 1
	public function set_tags_to_ignore( $tags = [ 'code', 'head', 'kbd', 'object', 'option', 'pre', 'samp', 'script', 'noscript', 'noembed', 'select', 'style', 'textarea', 'title', 'var', 'math' ] ) {
416
		// Ensure that we pass only lower-case tag names to XPath.
417 1
		$tags = array_filter( array_map( 'strtolower', Strings::maybe_split_parameters( $tags ) ), 'ctype_alnum' );
418
419 1
		$this->data['ignoreTags'] = array_unique( array_merge( $tags, array_flip( DOM::inappropriate_tags() ) ) );
420 1
	}
421
422
	/**
423
	 * Sets classes for which the typography of their children will be left untouched.
424
	 *
425
	 * @param string|array $classes A comma separated list or an array of class names.
426
	 */
427 1
	public function set_classes_to_ignore( $classes = [ 'vcard', 'noTypo' ] ) {
428 1
		$this->data['ignoreClasses'] = Strings::maybe_split_parameters( $classes );
429 1
	}
430
431
	/**
432
	 * Sets IDs for which the typography of their children will be left untouched.
433
	 *
434
	 * @param string|array $ids A comma separated list or an array of tag names.
435
	 */
436 1
	public function set_ids_to_ignore( $ids = [] ) {
437 1
		$this->data['ignoreIDs'] = Strings::maybe_split_parameters( $ids );
438 1
	}
439
440
	/**
441
	 * Enables/disables typographic quotes.
442
	 *
443
	 * @param bool $on Optional. Default true.
444
	 */
445 1
	public function set_smart_quotes( $on = true ) {
446 1
		$this->data['smartQuotes'] = $on;
447 1
	}
448
449
	/**
450
	 * Sets the style for primary ('double') quotemarks.
451
	 *
452
	 * Allowed values for $style:
453
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
454
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
455
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
456
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
457
	 * "singleCurled" => "&lsquo;foo&rsquo;",
458
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
459
	 * "singleLow9" => "&sbquo;foo&rsquo;",
460
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
461
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
462
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
463
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
464
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
465
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
466
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
467
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
468
	 *
469
	 * @param  Quotes|string $style Optional. A Quotes instance or a quote style constant. Defaults to 'doubleCurled'.
470
	 *
471
	 * @throws \DomainException Thrown if $style constant is invalid.
472
	 */
473 3
	public function set_smart_quotes_primary( $style = Quote_Style::DOUBLE_CURLED ) {
474 3
		$this->primary_quote_style = $this->get_quote_style( $style );
475 2
	}
476
477
	/**
478
	 * Sets the style for secondary ('single') quotemarks.
479
	 *
480
	 * Allowed values for $style:
481
	 * "doubleCurled" => "&ldquo;foo&rdquo;",
482
	 * "doubleCurledReversed" => "&rdquo;foo&rdquo;",
483
	 * "doubleLow9" => "&bdquo;foo&rdquo;",
484
	 * "doubleLow9Reversed" => "&bdquo;foo&ldquo;",
485
	 * "singleCurled" => "&lsquo;foo&rsquo;",
486
	 * "singleCurledReversed" => "&rsquo;foo&rsquo;",
487
	 * "singleLow9" => "&sbquo;foo&rsquo;",
488
	 * "singleLow9Reversed" => "&sbquo;foo&lsquo;",
489
	 * "doubleGuillemetsFrench" => "&laquo;&nbsp;foo&nbsp;&raquo;",
490
	 * "doubleGuillemets" => "&laquo;foo&raquo;",
491
	 * "doubleGuillemetsReversed" => "&raquo;foo&laquo;",
492
	 * "singleGuillemets" => "&lsaquo;foo&rsaquo;",
493
	 * "singleGuillemetsReversed" => "&rsaquo;foo&lsaquo;",
494
	 * "cornerBrackets" => "&#x300c;foo&#x300d;",
495
	 * "whiteCornerBracket" => "&#x300e;foo&#x300f;"
496
	 *
497
	 * @param  Quotes|string $style Optional. A Quotes instance or a quote style constant. Defaults to 'singleCurled'.
498
	 *
499
	 * @throws \DomainException Thrown if $style constant is invalid.
500
	 */
501 3
	public function set_smart_quotes_secondary( $style = Quote_Style::SINGLE_CURLED ) {
502 3
		$this->secondary_quote_style = $this->get_quote_style( $style );
503 2
	}
504
505
	/**
506
	 * Retrieves a Quotes instance from a given style.
507
	 *
508
	 * @param  Quotes|string $style A Quotes instance or a quote style constant.
509
	 *
510
	 * @throws \DomainException Thrown if $style constant is invalid.
511
	 *
512
	 * @return Quotes
513
	 */
514 6
	protected function get_quote_style( $style ) {
515 6
		return $this->get_style( $style, Quotes::class, [ Quote_Style::class, 'get_styled_quotes' ], 'quote' );
516
	}
517
518
	/**
519
	 * Sets the list of exceptional words for smart quotes replacement. Mainly,
520
	 * this is used for contractions beginning with an apostrophe.
521
	 *
522
	 * @param string[] $exceptions Optional. An array of replacements indexed by the ”non-smart" form.
523
	 *                             Default a list of English words beginning with an apostrophy.
524
	 */
525 1
	public function set_smart_quotes_exceptions( $exceptions = [
526
		"'tain't"   => U::APOSTROPHE . 'tain' . U::APOSTROPHE . 't',
527
		"'twere"    => U::APOSTROPHE . 'twere',
528
		"'twas"     => U::APOSTROPHE . 'twas',
529
		"'tis"      => U::APOSTROPHE . 'tis',
530
		"'til"      => U::APOSTROPHE . 'til',
531
		"'bout"     => U::APOSTROPHE . 'bout',
532
		"'nuff"     => U::APOSTROPHE . 'nuff',
533
		"'round"    => U::APOSTROPHE . 'round',
534
		"'cause"    => U::APOSTROPHE . 'cause',
535
		"'splainin" => U::APOSTROPHE . 'splainin',
536
		"'em'"      => U::APOSTROPHE . 'em',
537
	] ) {
538 1
		$this->data['smartQuotesExceptions'] = [
539 1
			'patterns'     => \array_keys( $exceptions ),
540 1
			'replacements' => \array_values( $exceptions ),
541
		];
542 1
	}
543
544
	/**
545
	 * Retrieves an object from a given style.
546
	 *
547
	 * @param  object|string $style          A style object instance or a style constant.
548
	 * @param  string        $expected_class A class name.
549
	 * @param  callable      $get_style      A function that returns a style object from a given style constant.
550
	 * @param  string        $description    Style description for the exception message.
551
	 *
552
	 * @throws \DomainException Thrown if $style constant is invalid.
553
	 *
554
	 * @return object An instance of $expected_class.
555
	 */
556 9
	protected function get_style( $style, $expected_class, callable $get_style, $description ) {
557 9
		if ( $style instanceof $expected_class ) {
558 3
			$object = $style;
559
		} else {
560 6
			$object = $get_style( $style, $this );
561
		}
562
563 9
		if ( ! \is_object( $object ) || ! $object instanceof $expected_class ) {
564 3
			throw new \DomainException( "Invalid $description style $style." );
565
		}
566
567 6
		return $object;
568
	}
569
570
	/**
571
	 * Enables/disables replacement of "a--a" with En Dash " -- " and "---" with Em Dash.
572
	 *
573
	 * @param bool $on Optional. Default true.
574
	 */
575 1
	public function set_smart_dashes( $on = true ) {
576 1
		$this->data['smartDashes'] = $on;
577 1
	}
578
579
	/**
580
	 * Sets the typographical conventions used by smart_dashes.
581
	 *
582
	 * Allowed values for $style:
583
	 * - "traditionalUS"
584
	 * - "international"
585
	 *
586
	 * @param string|Settings\Dashes $style Optional. Default Dash_Style::TRADITIONAL_US.
587
	 *
588
	 * @throws \DomainException Thrown if $style constant is invalid.
589
	 */
590 3
	public function set_smart_dashes_style( $style = Dash_Style::TRADITIONAL_US ) {
591 3
		$this->dash_style = $this->get_style( $style, Settings\Dashes::class, [ Dash_Style::class, 'get_styled_dashes' ], 'dash' );
592 2
	}
593
594
	/**
595
	 * Enables/disables replacement of "..." with "…".
596
	 *
597
	 * @param bool $on Optional. Default true.
598
	 */
599 1
	public function set_smart_ellipses( $on = true ) {
600 1
		$this->data['smartEllipses'] = $on;
601 1
	}
602
603
	/**
604
	 * Enables/disables replacement "creme brulee" with "crème brûlée".
605
	 *
606
	 * @param bool $on Optional. Default true.
607
	 */
608 1
	public function set_smart_diacritics( $on = true ) {
609 1
		$this->data['smartDiacritics'] = $on;
610 1
	}
611
612
	/**
613
	 * Sets the language used for diacritics replacements.
614
	 *
615
	 * @param string $lang Has to correspond to a filename in 'diacritics'. Optional. Default 'en-US'.
616
	 */
617 1
	public function set_diacritic_language( $lang = 'en-US' ) {
618 1
		if ( isset( $this->data['diacriticLanguage'] ) && $this->data['diacriticLanguage'] === $lang ) {
619 1
			return;
620
		}
621
622 1
		$this->data['diacriticLanguage'] = $lang;
623 1
		$language_file_name              = \dirname( __FILE__ ) . '/diacritics/' . $lang . '.json';
624
625 1
		if ( \file_exists( $language_file_name ) ) {
626 1
			$diacritics_file              = \json_decode( \file_get_contents( $language_file_name ), true );
627 1
			$this->data['diacriticWords'] = $diacritics_file['diacritic_words'];
628
		} else {
629 1
			unset( $this->data['diacriticWords'] );
630
		}
631
632 1
		$this->update_diacritics_replacement_arrays();
633 1
	}
634
635
	/**
636
	 * Sets up custom diacritics replacements.
637
	 *
638
	 * @param string|array $custom_replacements An array formatted [needle=>replacement, needle=>replacement...],
639
	 *                                          or a string formatted `"needle"=>"replacement","needle"=>"replacement",...
640
	 */
641 5
	public function set_diacritic_custom_replacements( $custom_replacements = [] ) {
642 5
		if ( ! \is_array( $custom_replacements ) ) {
643 2
			$custom_replacements = $this->parse_diacritics_replacement_string( $custom_replacements );
644
		}
645
646 5
		$this->data['diacriticCustomReplacements'] = self::array_map_assoc(
647
			function( $key, $replacement ) {
648 5
				$key         = \strip_tags( \trim( $key ) );
649 5
				$replacement = \strip_tags( \trim( $replacement ) );
650
651 5
				if ( ! empty( $key ) && ! empty( $replacement ) ) {
652 3
					return [ $key => $replacement ];
653
				} else {
654 2
					return [];
655
				}
656 5
			},
657
			$custom_replacements
658
		);
659
660 5
		$this->update_diacritics_replacement_arrays();
661 5
	}
662
663
	/**
664
	 * Parses a custom diacritics replacement string into an array.
665
	 *
666
	 * @param string $custom_replacements A string formatted `"needle"=>"replacement","needle"=>"replacement",...
667
	 *
668
	 * @return array
669
	 */
670 2
	private function parse_diacritics_replacement_string( $custom_replacements ) {
671 2
		return self::array_map_assoc(
672
			function( $key, $replacement ) {
673
				// Account for single and double quotes in keys in and values, discard everything else.
674 2
				if ( \preg_match( '/(?<kquo>"|\')(?<key>(?:(?!\k<kquo>).)+)\k<kquo>\s*=>\s*(?<rquo>"|\')(?<replacement>(?:(?!\k<rquo>).)+)\k<rquo>/', $replacement, $match ) ) {
675 2
					$key         = $match['key'];
676 2
					$replacement = $match['replacement'];
677
678 2
					return [ $key => $replacement ];
679
				}
680
681
				return [];
682 2
			},
683
			/** RE correct. @scrutinizer ignore-type */
684 2
			\preg_split( '/,/', $custom_replacements, -1, PREG_SPLIT_NO_EMPTY )
685
		);
686
	}
687
688
	/**
689
	 * Provides an array_map implementation with control over resulting array's keys.
690
	 *
691
	 * Based on https://gist.github.com/jasand-pereza/84ecec7907f003564584.
692
	 *
693
	 * @since 6.0.0
694
	 *
695
	 * @param  callable $callback A callback function that needs to return [ $key => $value ] pairs.
696
	 * @param  array    $array    The array.
697
	 *
698
	 * @return array
699
	 */
700 2
	protected static function array_map_assoc( callable $callback, array $array ) {
701 2
		$new = [];
702
703 2
		foreach ( $array as $k => $v ) {
704 2
			$u = $callback( $k, $v );
705
706 2
			if ( ! empty( $u ) ) {
707 1
				$new[ \key( $u ) ] = \current( $u );
708
			}
709
		}
710
711 2
		return $new;
712
	}
713
714
	/**
715
	 * Update the pattern and replacement arrays in $settings['diacriticReplacement'].
716
	 *
717
	 * Should be called whenever a new diacritics replacement language is selected or
718
	 * when the custom replacements are updated.
719
	 */
720 6
	private function update_diacritics_replacement_arrays() {
721 6
		$patterns     = [];
722 6
		$replacements = [];
723
724 6
		if ( ! empty( $this->data['diacriticCustomReplacements'] ) ) {
725 3
			$this->parse_diacritics_rules( $this->data['diacriticCustomReplacements'], $patterns, $replacements );
726
		}
727 6
		if ( ! empty( $this->data['diacriticWords'] ) ) {
728 1
			$this->parse_diacritics_rules( $this->data['diacriticWords'], $patterns, $replacements );
729
		}
730
731 6
		$this->data['diacriticReplacement'] = [
732 6
			'patterns'     => $patterns,
733 6
			'replacements' => $replacements,
734
		];
735 6
	}
736
737
	/**
738
	 * Parse an array of diacritics rules.
739
	 *
740
	 * @param array $diacritics_rules The rules ( $word => $replacement ).
741
	 * @param array $patterns         Resulting patterns. Passed by reference.
742
	 * @param array $replacements     Resulting replacements. Passed by reference.
743
	 */
744 4
	private function parse_diacritics_rules( array $diacritics_rules, array &$patterns, array &$replacements ) {
745
746 4
		foreach ( $diacritics_rules as $needle => $replacement ) {
747 4
			$patterns[]              = '/\b(?<!\w[' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . '])' . $needle . '\b(?![' . U::NO_BREAK_SPACE . U::SOFT_HYPHEN . ']\w)/u';
748 4
			$replacements[ $needle ] = $replacement;
749
		}
750 4
	}
751
752
	/**
753
	 * Enables/disables replacement of (r) (c) (tm) (sm) (p) (R) (C) (TM) (SM) (P) with ® © ™ ℠ ℗.
754
	 *
755
	 * @param bool $on Optional. Default true.
756
	 */
757 1
	public function set_smart_marks( $on = true ) {
758 1
		$this->data['smartMarks'] = $on;
759 1
	}
760
761
	/**
762
	 * Enables/disables proper mathematical symbols.
763
	 *
764
	 * @param bool $on Optional. Default true.
765
	 */
766 1
	public function set_smart_math( $on = true ) {
767 1
		$this->data['smartMath'] = $on;
768 1
	}
769
770
	/**
771
	 * Enables/disables replacement of 2^2 with 2<sup>2</sup>
772
	 *
773
	 * @param bool $on Optional. Default true.
774
	 */
775 1
	public function set_smart_exponents( $on = true ) {
776 1
		$this->data['smartExponents'] = $on;
777 1
	}
778
779
	/**
780
	 * Enables/disables replacement of 1/4 with <sup>1</sup>&#8260;<sub>4</sub>.
781
	 *
782
	 * @param bool $on Optional. Default true.
783
	 */
784 1
	public function set_smart_fractions( $on = true ) {
785 1
		$this->data['smartFractions'] = $on;
786 1
	}
787
788
	/**
789
	 * Enables/disables replacement of 1st with 1<sup>st</sup>.
790
	 *
791
	 * @param bool $on Optional. Default true.
792
	 */
793 1
	public function set_smart_ordinal_suffix( $on = true ) {
794 1
		$this->data['smartOrdinalSuffix'] = $on;
795 1
	}
796
797
	/**
798
	 * Enables/disables replacement of XXe with XX<sup>e</sup>.
799
	 *
800
	 * @since 6.5.0
801
	 *
802
	 * @param bool $on Optional. Default false.
803
	 */
804 1
	public function set_smart_ordinal_suffix_match_roman_numerals( $on = false ) {
805 1
		$this->data['smartOrdinalSuffixRomanNumerals'] = $on;
806 1
	}
807
808
	/**
809
	 * Enables/disables replacement of m2 with m³ and m3 with m³.
810
	 *
811
	 * @param bool $on Optional. Default true.
812
	 */
813 1
	public function set_smart_area_units( $on = true ) {
814 1
		$this->data['smartAreaVolumeUnits'] = $on;
815 1
	}
816
817
	/**
818
	 * Enables/disables forcing single character words to next line with the insertion of &nbsp;.
819
	 *
820
	 * @param bool $on Optional. Default true.
821
	 */
822 1
	public function set_single_character_word_spacing( $on = true ) {
823 1
		$this->data['singleCharacterWordSpacing'] = $on;
824 1
	}
825
826
	/**
827
	 * Enables/disables fraction spacing.
828
	 *
829
	 * @param bool $on Optional. Default true.
830
	 */
831 1
	public function set_fraction_spacing( $on = true ) {
832 1
		$this->data['fractionSpacing'] = $on;
833 1
	}
834
835
	/**
836
	 * Enables/disables keeping units and values together with the insertion of &nbsp;.
837
	 *
838
	 * @param bool $on Optional. Default true.
839
	 */
840 1
	public function set_unit_spacing( $on = true ) {
841 1
		$this->data['unitSpacing'] = $on;
842 1
	}
843
844
	/**
845
	 * Enables/disables numbered abbreviations like "ISO 9000" together with the insertion of &nbsp;.
846
	 *
847
	 * @param bool $on Optional. Default true.
848
	 */
849 1
	public function set_numbered_abbreviation_spacing( $on = true ) {
850 1
		$this->data['numberedAbbreviationSpacing'] = $on;
851 1
	}
852
853
	/**
854
	 * Enables/disables extra whitespace before certain punction marks, as is the French custom.
855
	 *
856
	 * @since 6.0.0 The default value is now `false`.`
857
	 *
858
	 * @param bool $on Optional. Default false.
859
	 */
860 1
	public function set_french_punctuation_spacing( $on = false ) {
861 1
		$this->data['frenchPunctuationSpacing'] = $on;
862 1
	}
863
864
	/**
865
	 * Sets the list of units to keep together with their values.
866
	 *
867
	 * @param string|array $units A comma separated list or an array of units.
868
	 */
869 1
	public function set_units( $units = [] ) {
870 1
		$this->data['units'] = Strings::maybe_split_parameters( $units );
871 1
		$this->custom_units  = $this->update_unit_pattern( $this->data['units'] );
872 1
	}
873
874
	/**
875
	 * Update pattern for matching custom units.
876
	 *
877
	 * @since 6.4.0 Visibility changed to protected, return value added.
878
	 *
879
	 * @param array $units An array of unit names.
880
	 *
881
	 * @return string
882
	 */
883 2
	protected function update_unit_pattern( array $units ) {
884
		// Update unit regex pattern.
885 2
		foreach ( $units as $index => $unit ) {
886 2
			$units[ $index ] = \preg_quote( $unit, '/' );
887
		}
888
889 2
		$custom_units  = \implode( '|', $units );
890 2
		$custom_units .= ! empty( $custom_units ) ? '|' : '';
891
892 2
		return $custom_units;
893
	}
894
895
	/**
896
	 * Enables/disables wrapping of Em and En dashes are in thin spaces.
897
	 *
898
	 * @param bool $on Optional. Default true.
899
	 */
900 1
	public function set_dash_spacing( $on = true ) {
901 1
		$this->data['dashSpacing'] = $on;
902 1
	}
903
904
	/**
905
	 * Enables/disables removal of extra whitespace characters.
906
	 *
907
	 * @param bool $on Optional. Default true.
908
	 */
909 1
	public function set_space_collapse( $on = true ) {
910 1
		$this->data['spaceCollapse'] = $on;
911 1
	}
912
913
	/**
914
	 * Enables/disables widow handling.
915
	 *
916
	 * @param bool $on Optional. Default true.
917
	 */
918 1
	public function set_dewidow( $on = true ) {
919 1
		$this->data['dewidow'] = $on;
920 1
	}
921
922
	/**
923
	 * Sets the maximum length of widows that will be protected.
924
	 *
925
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
926
	 */
927 1
	public function set_max_dewidow_length( $length = 5 ) {
928 1
		$length = ( $length > 1 ) ? $length : 5;
929
930 1
		$this->data['dewidowMaxLength'] = $length;
931 1
	}
932
933
	/**
934
	 * Sets the maximum number of words considered for dewidowing.
935
	 *
936
	 * @param int $number Defaults to 1. Only 1, 2 and 3 are valid.
937
	 */
938 1
	public function set_dewidow_word_number( $number = 1 ) {
939 1
		$number = ( $number > 3 || $number < 1 ) ? 1 : $number;
940
941 1
		$this->data['dewidowWordNumber'] = $number;
942 1
	}
943
944
	/**
945
	 * Sets the maximum length of pulled text to keep widows company.
946
	 *
947
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
948
	 */
949 1
	public function set_max_dewidow_pull( $length = 5 ) {
950 1
		$length = ( $length > 1 ) ? $length : 5;
951
952 1
		$this->data['dewidowMaxPull'] = $length;
953 1
	}
954
955
	/**
956
	 * Enables/disables wrapping at internal hard hyphens with the insertion of a zero-width-space.
957
	 *
958
	 * @param bool $on Optional. Default true.
959
	 */
960 1
	public function set_wrap_hard_hyphens( $on = true ) {
961 1
		$this->data['hyphenHardWrap'] = $on;
962 1
	}
963
964
	/**
965
	 * Enables/disables wrapping of urls.
966
	 *
967
	 * @param bool $on Optional. Default true.
968
	 */
969 1
	public function set_url_wrap( $on = true ) {
970 1
		$this->data['urlWrap'] = $on;
971 1
	}
972
973
	/**
974
	 * Enables/disables wrapping of email addresses.
975
	 *
976
	 * @param bool $on Optional. Default true.
977
	 */
978 1
	public function set_email_wrap( $on = true ) {
979 1
		$this->data['emailWrap'] = $on;
980 1
	}
981
982
	/**
983
	 * Sets the minimum character requirement after an URL wrapping point.
984
	 *
985
	 * @param int $length Defaults to 5. Trying to set the value to less than 1 resets the length to the default.
986
	 */
987 1
	public function set_min_after_url_wrap( $length = 5 ) {
988 1
		$length = ( $length > 0 ) ? $length : 5;
989
990 1
		$this->data['urlMinAfterWrap'] = $length;
991 1
	}
992
993
	/**
994
	 * Enables/disables wrapping of ampersands in <span class="amp">.
995
	 *
996
	 * @param bool $on Optional. Default true.
997
	 */
998 1
	public function set_style_ampersands( $on = true ) {
999 1
		$this->data['styleAmpersands'] = $on;
1000 1
	}
1001
1002
	/**
1003
	 * Enables/disables wrapping caps in <span class="caps">.
1004
	 *
1005
	 * @param bool $on Optional. Default true.
1006
	 */
1007 1
	public function set_style_caps( $on = true ) {
1008 1
		$this->data['styleCaps'] = $on;
1009 1
	}
1010
1011
	/**
1012
	 * Enables/disables wrapping of initial quotes in <span class="quo"> or <span class="dquo">.
1013
	 *
1014
	 * @param bool $on Optional. Default true.
1015
	 */
1016 1
	public function set_style_initial_quotes( $on = true ) {
1017 1
		$this->data['styleInitialQuotes'] = $on;
1018 1
	}
1019
1020
	/**
1021
	 * Enables/disables wrapping of numbers in <span class="numbers">.
1022
	 *
1023
	 * @param bool $on Optional. Default true.
1024
	 */
1025 1
	public function set_style_numbers( $on = true ) {
1026 1
		$this->data['styleNumbers'] = $on;
1027 1
	}
1028
1029
	/**
1030
	 * Enables/disables wrapping of punctiation and wide characters in <span class="pull-*">.
1031
	 *
1032
	 * @param bool $on Optional. Default true.
1033
	 */
1034 1
	public function set_style_hanging_punctuation( $on = true ) {
1035 1
		$this->data['styleHangingPunctuation'] = $on;
1036 1
	}
1037
1038
	/**
1039
	 * Sets the list of tags where initial quotes and guillemets should be styled.
1040
	 *
1041
	 * @param string|array $tags A comma separated list or an array of tag names.
1042
	 */
1043 1
	public function set_initial_quote_tags( $tags = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'li', 'dd', 'dt' ] ) {
1044
		// Make array if handed a list of tags as a string.
1045 1
		if ( ! \is_array( $tags ) ) {
1046 1
			$tags = \preg_split( '/[^a-z0-9]+/', $tags, -1, PREG_SPLIT_NO_EMPTY );
1047
		}
1048
1049
		// Store the tag array inverted (with the tagName as its index for faster lookup).
1050 1
		$this->data['initialQuoteTags'] = \array_change_key_case( \array_flip( /** Array. @scrutinizer ignore-type */ $tags ), CASE_LOWER );
1051 1
	}
1052
1053
	/**
1054
	 * Enables/disables hyphenation.
1055
	 *
1056
	 * @param bool $on Optional. Default true.
1057
	 */
1058 1
	public function set_hyphenation( $on = true ) {
1059 1
		$this->data['hyphenation'] = $on;
1060 1
	}
1061
1062
	/**
1063
	 * Sets the hyphenation pattern language.
1064
	 *
1065
	 * @param string $lang Has to correspond to a filename in 'lang'. Optional. Default 'en-US'.
1066
	 */
1067 8
	public function set_hyphenation_language( $lang = 'en-US' ) {
1068 8
		if ( isset( $this->data['hyphenLanguage'] ) && $this->data['hyphenLanguage'] === $lang ) {
1069 3
			return; // Bail out, no need to do anything.
1070
		}
1071
1072 8
		$this->data['hyphenLanguage'] = $lang;
1073 8
	}
1074
1075
	/**
1076
	 * Sets the minimum length of a word that may be hyphenated.
1077
	 *
1078
	 * @param int $length Defaults to 5. Trying to set the value to less than 2 resets the length to the default.
1079
	 */
1080 1
	public function set_min_length_hyphenation( $length = 5 ) {
1081 1
		$length = ( $length > 1 ) ? $length : 5;
1082
1083 1
		$this->data['hyphenMinLength'] = $length;
1084 1
	}
1085
1086
	/**
1087
	 * Sets the minimum character requirement before a hyphenation point.
1088
	 *
1089
	 * @param int $length Defaults to 3. Trying to set the value to less than 1 resets the length to the default.
1090
	 */
1091 1
	public function set_min_before_hyphenation( $length = 3 ) {
1092 1
		$length = ( $length > 0 ) ? $length : 3;
1093
1094 1
		$this->data['hyphenMinBefore'] = $length;
1095 1
	}
1096
1097
	/**
1098
	 * Sets the minimum character requirement after a hyphenation point.
1099
	 *
1100
	 * @param int $length Defaults to 2. Trying to set the value to less than 1 resets the length to the default.
1101
	 */
1102 1
	public function set_min_after_hyphenation( $length = 2 ) {
1103 1
		$length = ( $length > 0 ) ? $length : 2;
1104
1105 1
		$this->data['hyphenMinAfter'] = $length;
1106 1
	}
1107
1108
	/**
1109
	 * Enables/disables hyphenation of titles and headings.
1110
	 *
1111
	 * @param bool $on Optional. Default true.
1112
	 */
1113 1
	public function set_hyphenate_headings( $on = true ) {
1114 1
		$this->data['hyphenateTitle'] = $on;
1115 1
	}
1116
1117
	/**
1118
	 * Enables/disables hyphenation of words set completely in capital letters.
1119
	 *
1120
	 * @param bool $on Optional. Default true.
1121
	 */
1122 1
	public function set_hyphenate_all_caps( $on = true ) {
1123 1
		$this->data['hyphenateAllCaps'] = $on;
1124 1
	}
1125
1126
	/**
1127
	 * Enables/disables hyphenation of words starting with a capital letter.
1128
	 *
1129
	 * @param bool $on Optional. Default true.
1130
	 */
1131 1
	public function set_hyphenate_title_case( $on = true ) {
1132 1
		$this->data['hyphenateTitleCase'] = $on;
1133 1
	}
1134
1135
	/**
1136
	 * Enables/disables hyphenation of compound words (e.g. "editor-in-chief").
1137
	 *
1138
	 * @param bool $on Optional. Default true.
1139
	 */
1140 1
	public function set_hyphenate_compounds( $on = true ) {
1141 1
		$this->data['hyphenateCompounds'] = $on;
1142 1
	}
1143
1144
	/**
1145
	 * Sets custom word hyphenations.
1146
	 *
1147
	 * @param string|array $exceptions An array of words with all hyphenation points marked with a hard hyphen (or a string list of such words).
1148
	 *        In the latter case, only alphanumeric characters and hyphens are recognized. The default is empty.
1149
	 */
1150 2
	public function set_hyphenation_exceptions( $exceptions = [] ) {
1151 2
		$this->data['hyphenationCustomExceptions'] = Strings::maybe_split_parameters( $exceptions );
1152 2
	}
1153
1154
	/**
1155
	 * Retrieves a unique hash value for the current settings.
1156
	 *
1157
	 * @since 5.2.0 The new parameter $raw_output has been added.
1158
	 *
1159
	 * @param int  $max_length Optional. The maximum number of bytes returned (0 for unlimited). Default 16.
1160
	 * @param bool $raw_output Optional. Wether to return raw binary data for the hash. Default true.
1161
	 *
1162
	 * @return string A binary hash value for the current settings limited to $max_length.
1163
	 */
1164 1
	public function get_hash( $max_length = 16, $raw_output = true ) {
1165 1
		$hash = \md5( \json_encode( $this ), $raw_output );
1166
1167 1
		if ( $max_length < \strlen( $hash ) && $max_length > 0 ) {
1168 1
			$hash = \substr( $hash, 0, $max_length );
1169
		}
1170
1171 1
		return $hash;
1172
	}
1173
}
1174