1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Core\Utility; 17 18/** 19 * Class with helper functions for string handling 20 */ 21class StringUtility 22{ 23 /** 24 * Returns TRUE if $haystack begins with $needle. 25 * The input string is not trimmed before and search is done case sensitive. 26 * 27 * @param string $haystack Full string to check 28 * @param string $needle Reference string which must be found as the "first part" of the full string 29 * @throws \InvalidArgumentException 30 * @return bool TRUE if $needle was found to be equal to the first part of $haystack 31 * @deprecated will be removed in TYPO3 v12.0. Use PHP's native str_starts_with() function instead. 32 */ 33 public static function beginsWith($haystack, $needle) 34 { 35 trigger_error('StringUtility::beginsWith() will be removed in TYPO3 v12.0. Use PHPs str_starts_with() function instead.', E_USER_DEPRECATED); 36 // Sanitize $haystack and $needle 37 if (is_array($haystack) || is_object($haystack) || $haystack === null || (string)$haystack != $haystack) { 38 throw new \InvalidArgumentException( 39 '$haystack can not be interpreted as string', 40 1347135546 41 ); 42 } 43 if (is_array($needle) || is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) { 44 throw new \InvalidArgumentException( 45 '$needle can not be interpreted as string or has zero length', 46 1347135547 47 ); 48 } 49 $haystack = (string)$haystack; 50 $needle = (string)$needle; 51 return $needle !== '' && strpos($haystack, $needle) === 0; 52 } 53 54 /** 55 * Returns TRUE if $haystack ends with $needle. 56 * The input string is not trimmed before and search is done case sensitive. 57 * 58 * @param string $haystack Full string to check 59 * @param string $needle Reference string which must be found as the "last part" of the full string 60 * @throws \InvalidArgumentException 61 * @return bool TRUE if $needle was found to be equal to the last part of $haystack 62 * @deprecated will be removed in TYPO3 v12.0. Use PHP's native str_ends_with() function instead. 63 */ 64 public static function endsWith($haystack, $needle) 65 { 66 trigger_error('StringUtility::endsWith() will be removed in TYPO3 v12.0. Use PHPs str_ends_with() function instead.', E_USER_DEPRECATED); 67 // Sanitize $haystack and $needle 68 if (is_array($haystack) || is_object($haystack) || $haystack === null || (string)$haystack != $haystack) { 69 throw new \InvalidArgumentException( 70 '$haystack can not be interpreted as string', 71 1347135544 72 ); 73 } 74 if (is_array($needle) || is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) { 75 throw new \InvalidArgumentException( 76 '$needle can not be interpreted as string or has no length', 77 1347135545 78 ); 79 } 80 $haystackLength = strlen($haystack); 81 $needleLength = strlen($needle); 82 if (!$haystackLength || $needleLength > $haystackLength) { 83 return false; 84 } 85 $position = strrpos((string)$haystack, (string)$needle); 86 return $position !== false && $position === $haystackLength - $needleLength; 87 } 88 89 /** 90 * This function generates a unique id by using the more entropy parameter. 91 * Furthermore the dots are removed so the id can be used inside HTML attributes e.g. id. 92 * 93 * @param string $prefix 94 * @return string 95 */ 96 public static function getUniqueId($prefix = '') 97 { 98 $uniqueId = uniqid($prefix, true); 99 return str_replace('.', '', $uniqueId); 100 } 101 102 /** 103 * Escape a CSS selector to be used for DOM queries 104 * 105 * This method takes care to escape any CSS selector meta character. 106 * The result may be used to query the DOM like $('#' + escapedSelector) 107 * 108 * @param string $selector 109 * @return string 110 */ 111 public static function escapeCssSelector(string $selector): string 112 { 113 return preg_replace('/([#:.\\[\\],=@])/', '\\\\$1', $selector); 114 } 115 116 /** 117 * Removes the Byte Order Mark (BOM) from the input string. This method supports UTF-8 encoded strings only! 118 * 119 * @param string $input 120 * @return string 121 */ 122 public static function removeByteOrderMark(string $input): string 123 { 124 if (strpos($input, "\xef\xbb\xbf") === 0) { 125 $input = substr($input, 3); 126 } 127 128 return $input; 129 } 130 131 /** 132 * Matching two strings against each other, supporting a "*" wildcard (match many) or a "?" wildcard (match one= or (if wrapped in "/") PCRE regular expressions 133 * 134 * @param string $haystack The string in which to find $needle. 135 * @param string $needle The string to find in $haystack 136 * @return bool Returns TRUE if $needle matches or is found in (according to wildcards) $haystack. E.g. if $haystack is "Netscape 6.5" and $needle is "Net*" or "Net*ape" then it returns TRUE. 137 */ 138 public static function searchStringWildcard($haystack, $needle): bool 139 { 140 $result = false; 141 if ($haystack === $needle) { 142 $result = true; 143 } elseif ($needle) { 144 if (preg_match('/^\\/.+\\/$/', $needle)) { 145 // Regular expression, only "//" is allowed as delimiter 146 $regex = $needle; 147 } else { 148 $needle = str_replace(['*', '?'], ['%%%MANY%%%', '%%%ONE%%%'], $needle); 149 $regex = '/^' . preg_quote($needle, '/') . '$/'; 150 // Replace the marker with .* to match anything (wildcard) 151 $regex = str_replace(['%%%MANY%%%', '%%%ONE%%%'], ['.*', '.'], $regex); 152 } 153 $result = (bool)preg_match($regex, $haystack); 154 } 155 return $result; 156 } 157 158 /** 159 * Takes a comma-separated list and removes all duplicates. 160 * If a value in the list is trim(empty), the value is ignored. 161 * 162 * @param string $list Accept a comma-separated list of values. 163 * @return string Returns the list without any duplicates of values, space around values are trimmed. 164 */ 165 public static function uniqueList(string $list): string 166 { 167 return implode(',', array_unique(GeneralUtility::trimExplode(',', $list, true))); 168 } 169 170 /** 171 * Works the same as str_pad() except that it correctly handles strings with multibyte characters 172 * and takes an additional optional argument $encoding. 173 * 174 * @param string $string 175 * @param int $length 176 * @param string $pad_string 177 * @param int $pad_type 178 * @param string $encoding 179 * @return string 180 */ 181 public static function multibyteStringPad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, string $encoding = 'UTF-8'): string 182 { 183 $len = mb_strlen($string, $encoding); 184 $pad_string_len = mb_strlen($pad_string, $encoding); 185 if ($len >= $length || $pad_string_len === 0) { 186 return $string; 187 } 188 189 switch ($pad_type) { 190 case STR_PAD_RIGHT: 191 $string .= str_repeat($pad_string, (int)(($length - $len)/$pad_string_len)); 192 $string .= mb_substr($pad_string, 0, ($length - $len) % $pad_string_len); 193 return $string; 194 195 case STR_PAD_LEFT: 196 $leftPad = str_repeat($pad_string, (int)(($length - $len)/$pad_string_len)); 197 $leftPad .= mb_substr($pad_string, 0, ($length - $len) % $pad_string_len); 198 return $leftPad . $string; 199 200 case STR_PAD_BOTH: 201 $leftPadCount = (int)(($length - $len)/2); 202 $len += $leftPadCount; 203 $padded = ((int)($leftPadCount / $pad_string_len)) * $pad_string_len; 204 $leftPad = str_repeat($pad_string, (int)($leftPadCount / $pad_string_len)); 205 $leftPad .= mb_substr($pad_string, 0, $leftPadCount - $padded); 206 $string = $leftPad . $string . str_repeat($pad_string, (int)(($length - $len)/$pad_string_len)); 207 $string .= mb_substr($pad_string, 0, ($length - $len) % $pad_string_len); 208 return $string; 209 } 210 return $string; 211 } 212} 213