We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
| 1 | <?php |
||
| 43 | class Dewidow_Fix extends Abstract_Node_Fix { |
||
| 44 | const SPACE_BETWEEN = '[\s]+'; // \s includes all special spaces (but not ZWSP) with the u flag. |
||
| 45 | const WIDOW = '[\w\p{M}\-' . U::ZERO_WIDTH_SPACE . U::SOFT_HYPHEN . ']+?'; // \w includes all alphanumeric Unicode characters but not composed characters. |
||
| 46 | |||
| 47 | // Mandatory UTF-8 modifer. |
||
| 48 | const REGEX_START = '/ |
||
| 49 | (?: |
||
| 50 | \A |
||
| 51 | | |
||
| 52 | (?: |
||
| 53 | (?<space_before> # subpattern 1: space before (note: ZWSP is not a space) |
||
| 54 | [\s' . U::ZERO_WIDTH_SPACE . U::SOFT_HYPHEN . ']+ |
||
| 55 | ) |
||
| 56 | (?<neighbor> # subpattern 2: neighbors widow (short as possible) |
||
| 57 | [^\s' . U::ZERO_WIDTH_SPACE . U::SOFT_HYPHEN . ']+? |
||
| 58 | ) |
||
| 59 | ) |
||
| 60 | ) |
||
| 61 | (?<space_between> # subpattern 3: space between |
||
| 62 | ' . self::SPACE_BETWEEN . ' |
||
| 63 | ) |
||
| 64 | (?<widow> # subpattern 4: widow |
||
| 65 | ' . self::WIDOW . ' |
||
| 66 | (?: |
||
| 67 | ' . self::SPACE_BETWEEN . self::WIDOW . ' |
||
| 68 | ){0,'; // The maximum number of repetitions is missing. |
||
| 69 | |||
| 70 | const REGEX_END = |
||
| 71 | '}) |
||
| 72 | (?<trailing> # subpattern 5: any trailing punctuation or spaces |
||
| 73 | [^\w\p{M}]* |
||
| 74 | ) |
||
| 75 | \Z |
||
| 76 | /Sxu'; |
||
| 77 | |||
| 78 | const MASKED_NARROW_SPACE = '__NO_BREAK_NARROW_SPACE__'; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Apply the fix to a given textnode. |
||
| 82 | * |
||
| 83 | * @param \DOMText $textnode Required. |
||
| 84 | * @param Settings $settings Required. |
||
| 85 | * @param bool $is_title Optional. Default false. |
||
| 86 | */ |
||
| 87 | 56 | public function apply( \DOMText $textnode, Settings $settings, $is_title = false ) { |
|
| 99 | |||
| 100 | /** |
||
| 101 | * Dewidow a given text fragment. |
||
| 102 | * |
||
| 103 | * @param string $text The text fragment to dewidow. |
||
| 104 | * @param array $func An array of string functions. |
||
| 105 | * @param int $max_pull Maximum number of characters pulled from previous line. |
||
| 106 | * @param int $max_length Maximum widow length. |
||
| 107 | * @param int $word_number Maximum number of words allowed in widow. |
||
| 108 | * @param string $narrow_space The narrow no-break space character. |
||
| 109 | * |
||
| 110 | * @return string |
||
| 111 | */ |
||
| 112 | 28 | protected function dewidow( $text, array $func, $max_pull, $max_length, $word_number, $narrow_space ) { |
|
| 113 | 28 | if ( $word_number < 1 ) { |
|
| 114 | 5 | return $text; // We are done. |
|
| 115 | } |
||
| 116 | |||
| 117 | // Do what we have to do. |
||
| 118 | 28 | return preg_replace_callback( self::REGEX_START . ( $word_number - 1 ) . self::REGEX_END, function( array $widow ) use ( $func, $max_pull, $max_length, $word_number, $narrow_space ) { |
|
| 119 | |||
| 120 | // If we are here, we know that widows are being protected in some fashion |
||
| 121 | // with that, we will assert that widows should never be hyphenated or wrapped |
||
| 122 | // as such, we will strip soft hyphens and zero-width-spaces. |
||
| 123 | 27 | $widow['widow'] = self::strip_breaking_characters( $widow['widow'] ); |
|
| 124 | 27 | $widow['trailing'] = self::strip_breaking_characters( self::make_space_nonbreaking( $widow['trailing'], $narrow_space, $func['u'] ) ); |
|
| 125 | |||
| 126 | if ( |
||
| 127 | // Eject if widows neighbor is proceeded by a no break space (the pulled text would be too long). |
||
| 128 | 27 | '' === $widow['space_before'] || false !== \strpos( $widow['space_before'], U::NO_BREAK_SPACE ) || |
|
| 129 | |||
| 130 | // Eject if widows neighbor length exceeds the max allowed or widow length exceeds max allowed. |
||
| 131 | 26 | $func['strlen']( $widow['neighbor'] ) > $max_pull || $func['strlen']( $widow['widow'] ) > $max_length || |
|
| 132 | |||
| 133 | // Never replace thin and hair spaces with . |
||
| 134 | 27 | self::is_narrow_space( $widow['space_between'] ) |
|
| 135 | ) { |
||
| 136 | 7 | return $widow['space_before'] . $widow['neighbor'] . $this->dewidow( $widow['space_between'] . $widow['widow'] . $widow['trailing'], $func, $max_pull, $max_length, $word_number - 1, $narrow_space ); |
|
| 137 | } |
||
| 138 | |||
| 139 | // Let's protect some widows! |
||
| 140 | 22 | return $widow['space_before'] . $widow['neighbor'] . U::NO_BREAK_SPACE . self::make_space_nonbreaking( $widow['widow'], $narrow_space, $func['u'] ) . $widow['trailing']; |
|
| 141 | 28 | }, $text ); |
|
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Strip zero-width space and soft hyphens from the given string. |
||
| 146 | * |
||
| 147 | * @param string $string Required. |
||
| 148 | * |
||
| 149 | * @return string |
||
| 150 | */ |
||
| 151 | 1 | protected static function strip_breaking_characters( $string ) { |
|
| 154 | |||
| 155 | /** |
||
| 156 | * Is the given string one of the narrow space characters? |
||
| 157 | * |
||
| 158 | * @since 6.0.0 |
||
| 159 | * |
||
| 160 | * @param string $string The whitespace to test. |
||
| 161 | * |
||
| 162 | * @return bool |
||
| 163 | */ |
||
| 164 | 1 | protected static function is_narrow_space( $string ) { |
|
| 167 | |||
| 168 | /** |
||
| 169 | * Strip zero-width space and soft hyphens from the given string. |
||
| 170 | * |
||
| 171 | * @param string $string Required. |
||
| 172 | * @param string $narrow_space The narrow no-break space character. |
||
| 173 | * @param string $u Either 'u' or the empty string. |
||
| 174 | * |
||
| 175 | * @return string |
||
| 176 | */ |
||
| 177 | 1 | protected static function make_space_nonbreaking( $string, $narrow_space, $u ) { |
|
| 192 | } |
||
| 193 |