PATH:
home
/
sparklp6
/
public_html
/
website_356c8563
/
wp-content
/
plugins
/
ajax-search-lite
/
src
/
server
/
Utils
<?php /* @noinspection PhpComposerExtensionStubsInspection */ /* @noinspection RegExpRedundantEscape */ namespace WPDRMS\ASL\Utils; if ( !defined('ABSPATH') ) { die("You can't access this file directly."); } class Str { /** * Performs a safe sanitation and escape for strings and numeric values in LIKE type queries. * This is not to be used on whole queries, only values. * * @param mixed $input * @param bool $remove_quotes * @param string $remove * @return array|mixed * @uses wd_mysql_escape_mimic() */ public static function escape( $input, bool $remove_quotes = false, string $remove = '' ) { // recursively go through if it is an array if ( is_array($input) ) { foreach ( $input as $k => $v ) { $input[ $k ] = self::escape($v, $remove_quotes, $remove); } return $input; } if ( is_float( $input ) || is_int( $input ) ) { return $input; } if ( $remove_quotes ) { $input = str_replace( array( chr(145), chr(146), chr(147), chr(148), chr(150), chr(151), chr(133), "'", '"', ), '', $input ); } if ( !empty($remove) ) { $input = str_replace(str_split($remove), '', $input); } if ( function_exists( 'esc_sql' ) ) { return esc_sql( $input ); } // Okay, what? Not one function is present, use the one we have return wd_mysql_escape_mimic($input); } /** * Checks if the given date format matches the pattern YYYY-MM-DD * * It does not validate the date, only the format. * * @param string $date * @return bool */ public static function checkDate( string $date ): bool { if ( MB::strlen( $date ) !== 10 ) { return false; } return (bool) preg_match('/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}\z/', $date); } /** * Converts a string to number, array of strings to array of numbers * * Since esc_like() does not escape numeric values, casting them is the easiest way to go * * @param mixed $number * @return int[]|float[]|int|float */ public static function forceNumeric( $number ) { if ( is_array($number) ) { foreach ( $number as $k => $v ) { $number[ $k ] = self::forceNumeric($v); } return $number; } else { if ( is_int($number) || is_float($number) ) { return $number; } if ( $number === null ) { return 0; } // Replace any non-numeric and decimal point character $number = preg_replace('/[^0-9\.\-]+/', '', $number); // PHP 7.x+ 'feature' - empty string, or non-numeric value throws an error // ...in this case it is either a number or an empty string at this point, so check for empty string $number = empty($number) ? 0 : $number; // Check explanation above // Still something fishy? $number = !is_numeric($number) ? strpos($number, '.') !== false ? floatval($number) : intval($number) : $number; $number = $number + 0; } return $number; } /** * Generates a string reverse, support multibite strings, plus fallback if mbstring not avail * * @param string $s * @return string */ public static function reverse( string $s ): string { /* * Not sure if using extension_loaded(...) is enough. */ if ( function_exists('mb_detect_encoding') && function_exists('mb_strlen') && function_exists('mb_substr') ) { // Using mbstring $encoding = mb_detect_encoding($s); $encoding = $encoding === false ? null : $encoding; $length = mb_strlen($s, $encoding); // @phpstan-ignore-line $reversed = ''; while ( $length-- > 0 ) { $reversed .= mb_substr($s, $length, 1, $encoding); // @phpstan-ignore-line } return $reversed; } else { // Good old regex method, still supporting fully UFT8 preg_match_all('/./us', $s, $ar); return implode(array_reverse($ar[0])); } } /** * Converts anything to string * * @param mixed $any * @param string $separator * @param int $level * @return string */ public static function anyToString( $any, string $separator = ' ', int $level = 0 ): string { $str = ''; if ( is_string($any) && $level === 0 ) { $any = maybe_unserialize($any); /** * String check is required again, as only string is accepted for json_validate * and at this point it can be an array or object. */ if ( is_string($any) && function_exists('json_validate') && json_validate($any) ) { $any = json_decode($any, true); } } if ( is_array( $any ) ) { $str_arr = array(); foreach ( $any as $sub_arr ) { $str_arr[] = self::anyToString( $sub_arr, $separator, $level + 1 ); } $str_arr = array_filter( $str_arr, fn( $value ) => $value !== '' ); $str = implode( $separator, $str_arr ); } else { // Invalid values if ( is_bool($any) || is_null($any) || ( is_float($any) && is_nan($any) ) ) { return ''; } // Check for objects, as those yield a fatal error when converted to strings if ( !is_object($any) ) { $str = (string) $any; } } return $str; } /** * Replaces the first occurrence of the $find string with $replace within the $subject. * * @param string $find * @param string $replace * @param string $subject * @return string * @since 4.11 */ public static function replaceFirst( string $find, string $replace, string $subject ): string { if ( $find === '' ) { return $subject; } // From the comments at PHP.net/str_replace // Splits $subject into an array of 2 items by $find, // and then joins the array with $replace return implode($replace, explode($find, $subject, 2)); } /** * @param string $s * @return string */ public static function fixSSLURLs( string $s ): string { if ( ASL_SITE_IS_PROBABLY_SSL ) { /** @noinspection HttpUrlsUsage */ return str_replace('http://', 'https://', $s); } return $s; } /** * Clears and trims a search phrase from extra slashes and extra space characters * * @param string $s * @return string */ public static function clear( string $s ): string { // Step 2: Remove all control (non-printable) characters except for whitespace characters // Control characters range from \x00 to \x1F and \x7F // We'll preserve whitespace characters like space, tab, newline, etc. $cleaned = preg_replace('/[\x00-\x08\x0B-\x1F\x7F]/u', '', $s); if ( $cleaned === null ) { return ''; } // Step 1: Remove all backslashes $stripped = str_replace('\\', '', $cleaned); // Step 3: Trim leading and trailing whitespace $trimmed = trim($stripped); // Step 4: Replace multiple whitespace characters with a single space $res = preg_replace('/\s+/', ' ', $trimmed); // Step 5: Ensure the result is a string return is_string($res) ? $res : ''; } /** * PHP 7.4 way of json_validate() * * @param string $str * @return bool */ public static function isJson( string $str ): bool { $json = json_decode($str); return $json && $str !== $json; } /** * Strips given HTML tags and all of their contents from a text * * Similar to strip_tags() but the contents are also removed * * @param string|string[] $text String or array of strings * @param string[] $tags * @return string|string[] Concatenated text * @noinspection PhpMissingParamTypeInspection */ public static function stripTagsWithContent( $text, $tags = array() ) { foreach ( $tags as $tag ) { $text = preg_replace('/<' . $tag . '>(.*?)<\/' . $tag . '>/s', '', $text); if ( $text === null ) { return ''; } $text = preg_replace('/<\\/?' . $tag . '(.|\\s)*?>/', '', $text); if ( $text === null ) { return ''; } // New line is required to split non-blank lines $text = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $text); if ( $text === null ) { return ''; } } return $text; } /** * Removes WordPress Gutenberg editor blocks with their contents from the given string by block names array * * @param string $content * @param string[] $block_names * @return string */ public static function removeGutenbergBlocks( string $content, array $block_names = array( 'core-embed/*' ) ): string { foreach ( $block_names as &$block_name ) { $block_name = str_replace('*', 'AAAAAAAAAAAAAAA', $block_name); $block_name = preg_quote($block_name, '~'); // Escaping with delimiter $block_name = str_replace('AAAAAAAAAAAAAAA', '.*?', $block_name); } $block_names = implode('|', $block_names); // Use '~' as the regex delimiter to avoid escaping '/' $ret = preg_replace( "~<!--\s+wp:($block_names)(\s+\{.*?\})?\s+-->(.*?)<!--\s+\/wp:($block_names)\s+-->~s", '', $content ); return $ret === null ? '' : $ret; } /** * Removes accents via transliterator and the WordPress core remove_accents function * * @param string $str * @return string */ public static function removeAccents( string $str ): string { // Hebrew transliteration does not work correctly, no vowels. if ( preg_match('/\p{Hebrew}/u', $str) ) { return $str; } $s = $str; if ( class_exists('\\Transliterator') && method_exists('\\Transliterator', 'transliterate') ) { $transliterator = \Transliterator::createFromRules( ':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;', \Transliterator::FORWARD ); if ( $transliterator !== null ) { $s = $transliterator->transliterate($s); if ( $s === false ) { $s = $str; } } } return remove_accents($s); } /** * Resolves content with bracket syntax (such as no results fields etc...) * * @param $content * @param array $fields fieldN=>contentN keypairs for replacements * @param bool $empty_on_missing if any of the field contents is empty, leave the whole expression empty * @return string */ public static function resolveBracketSyntax( $content, array $fields = array(), bool $empty_on_missing = false ): string { if ( empty($fields) ) { return $content; } // Find conditional patterns, like [prefix {field} suffix] preg_match_all( '/(\[.*?\])/', $content, $matches ); if ( isset( $matches[0] ) && isset( $matches[1] ) && is_array( $matches[1] ) ) { foreach ( $matches[1] as $fieldset ) { // Pass on each section to this function again, the code will never get here $stripped_fieldset = str_replace(array( '[', ']' ), '', $fieldset); $processed_content = self::resolveBracketSyntax($stripped_fieldset, $fields, true); // Replace the original with the processed version, first occurrence, in case of duplicates $content = self::replaceFirst($fieldset, $processed_content, $content); } } preg_match_all( '/{(.*?)}/', $content, $matches ); if ( isset( $matches[0] ) && isset( $matches[1] ) && is_array( $matches[1] ) ) { foreach ( $matches[1] as $field ) { $val = $fields[ $field ] ?? ''; // For the recursive call to break, if any of the fields is empty if ( $empty_on_missing && $val === '' ) { return ''; } $content = str_replace( '{' . $field . '}', $val, $content ); } } return $content; } /** * Checks if a string is Base64 encoded * * @param string $s * @return bool */ public static function isBase64Encoded( string $s ): bool { if ( (bool) preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s) === false ) { return false; } $decoded = base64_decode($s, true); if ( $decoded === false ) { return false; } if ( function_exists('mb_detect_encoding') ) { $encoding = mb_detect_encoding($decoded); if ( !in_array($encoding, array( 'UTF-8', 'ASCII' ), true) ) { return false; } } return $decoded !== false && base64_encode($decoded) === $s; } public static function getContext( $content, $length, $depth, $phrase, $phrase_arr ): string { // Try for an exact match if ( count( $phrase_arr ) > 0 && $phrase != '' ) { $_ex_content = self::contextFind( $content, $phrase, floor($length / 6), $length, $depth, true ); if ( $_ex_content === false ) { // No exact match, go with the first keyword $content = self::contextFind( $content, $phrase_arr[0], floor($length / 6), $length, $depth ); } else { $content = $_ex_content; } } return $content; } /** * Returns the context of a phrase within a text. * Uses preg_split method to iterate through strings. * * @param $str - string context * @param $needle - string context * @param $context - int length of the context * @param $maxlength - int maximum length of the string in characters * @param $str_length_limit - source string maximum length * @return string * @noinspection PhpMissingParamTypeInspection */ public static function contextFind( $str, $needle, $context, $maxlength, $str_length_limit = 10000, $false_on_no_match = false ) { $haystack = remove_accents(' ' . trim($str) . ' '); // To prevent memory overflow, we need to limit the hay to relatively low count $haystack = wd_substr_at_word(MB::strtolower($haystack), $str_length_limit, ''); $needle = remove_accents(MB::strtolower($needle)); if ( $needle == '' ) { if ( MB::strlen($str) > $maxlength ) { return wd_substr_at_word($str, $maxlength); } else { return $str; } } /** * This is an interesting issue. Turns out mb_substr($hay, $start, 1) is very ineffective. * the preg_split(...) method is far more efficient in terms of speed, however it needs much more * memory. In our case speed is the top priority. However, to prevent memory overflow, the haystack * is reduced to 10000 characters (roughly 1500 words) first. * * Reference ticket: https://wp-dreams.com/forums/topic/search-speed/ * Speed tests: http://stackoverflow.com/questions/3666306/how-to-iterate-utf-8-string-in-php */ $chrArray = preg_split('//u', $haystack, -1, PREG_SPLIT_NO_EMPTY); $hay_length = count($chrArray) - 1; if ( $i = MB::strpos($haystack, $needle) ) { $start =$i; $end =$i; $spaces =0; while ( $spaces < ( (int) $context /2 ) && $start > 0 ) { --$start; if ( $chrArray[ $start ] == ' ' ) { ++$spaces; } } while ( $spaces < ( $context +1 ) && $end < $hay_length ) { ++$end; if ( $chrArray[ $end ] == ' ' ) { ++$spaces; } } while ( $spaces < ( $context +1 ) && $start > 0 ) { --$start; if ( $chrArray[ $start ] == ' ' ) { ++$spaces; } } $str_start = ( $start - 1 ) < 0 ? 0 : ( $start -1 ); $str_end = ( $end - 1 ) < 0 ? 0 : ( $end -1 ); $result = trim( MB::substr($str, $str_start, ( $str_end - $str_start )) ); // Somewhere inbetween.. if ( $start != 0 && $end < $hay_length ) { return '... ' . $result . ' ...'; } // Beginning if ( $start == 0 && $end < $hay_length ) { return $result . ' ...'; } // End if ( $start != 0 && $end == $hay_length ) { return '... ' . $result; } // If it is too long, strip it if ( MB::strlen($result) > $maxlength ) { return wd_substr_at_word( $result, $maxlength ); } // Else, it is the whole return $result; } else { if ( $false_on_no_match ) { return false; } // If it is too long, strip it if ( MB::strlen($str) > $maxlength ) { return wd_substr_at_word( $str, $maxlength ); } return $str; } } }
[+]
Polylang
[-] .htaccess
[edit]
[-] Str.php
[edit]
[-] MB.php
[edit]
[-] WooCommerce.php
[edit]
[-] Post.php
[edit]
[-] User.php
[edit]
[+]
AdvancedField
[-] Html.php
[edit]
[+]
..