403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/burlakastudio.realcommenter/lib/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/burlakastudio.realcommenter/lib/STOP_FILTER.php
<?php
/**
 * Модуль "Полноценные комментарии 2.0" под Битрикс
 * Официальный сайт модуля: www.realcommenter.com
 * Официальный сайт разработчика: burlaka.studio
 * Автор и разработчик: Алексей Бурлака (AlexeyGfi) -> alexeygfi@gmail.com
 */

namespace Burlakastudio\Realcommenter;

class STOP_FILTER
{

    public static function trim_html(&$text = '', $editor_type = 'html')
    {

        if ($editor_type != 'html') {
            self::stripTags($text);
            self::replace_new_string_to_html($text);
        } else {
            self::tag_attributes_sanitarium($text);
            $text = self::normalizeTextInParagraph($text);
            $text = preg_replace('~^\s*(<br\s*\/*>\s*)+~', '<br>', $text);
            $text = preg_replace('~\s*(<br\s*\/*>\s*)+$~', '<br>', $text);
        }

        $text = trim($text);

    }

    /**
     * Мы принимаем на вход указатель!
     * Чтобы если мы нашли что-то плохое, но настройки модуля говорят о том,
     * чтобы мы пропустили, предварительно обезопасив данные - делаем это напрямую в переменную
     *
     * !!! Исходя из этого, заходить на проверку нужно только тогда, когда это действительно необходимо.
     * То есть если комментирует / редактирует модератор — не нужно зря текст потрошить
     *
     * Плюс, учитывая, что мы на вход получаем как раз указатель,
     * можно провести с текстом санитарные действия: почистить двойные enter-ы, пустые символы в начале,
     * замену символов
     *
     * @param string $text
     * @param bool $skip_snitarium
     *
     * @return array|bool
     */
    public static function text_must_be_stopped(&$text = '', $skip_snitarium = false)
    {

        /**
         * ...если для текущего пользователя отключен визуальный редактор (== skip any html-formatting)
         */
        if (!$skip_snitarium || !GRANT_AND_ACCESS::is_use_text_editor()) {
            self::clear_up($text);
        }

        /**
         * Встречаются ли в очищенном тексте плохие слова
         */
        $bad_words = self::has_bad_words($text);
        if ($bad_words) {
            return ['STOP_FILTER_WORDS' => $bad_words['WORD_FILTER']];
        }

        /**
         * Встречаются ли запрещённые теги
         */
        $bad_tags = self::has_bad_tags($text);
        if ($bad_tags) {
            /**
             * Если стоит опция, касающаяся ТЕГОВ - чистить и пропускать
             */
            $clear_not_allowed_and_go = OPTIONS::get('clear_not_allowed_and_go');
            if ($clear_not_allowed_and_go == 'Y') {

                $text = $bad_tags['filtered_text'];
                return false;

            } else {
                return ['STOP_TAGS' => $bad_tags['garbage_tags']];
            }
        }

        return false;
    }

    public static function has_bad_words($text = '')
    {

        $_text = $text;
        $_text = preg_replace('~<\/?([a-z]+)[^>]*?>~is' . BX_UTF_PCRE_MODIFIER, '', $_text);

        $_text = self::replace_danger_symbols($_text);
        $_text = self::replace_smiles($_text);

        $_text = preg_replace('~[\r\n]~', '', $_text);

        if (self::censored_text($_text)) {
            global $censored_info;
            return $censored_info;
        }

        return false;

    }

    /**
     * Список разрешённых и запрещённых тегов - может быть оба окошка пустых
     * Разрешённые, если заполнены, имеют приоритет перед запрещёнными.
     * То есть если заполнен список разрешённых тегов,
     * на запрещённые уже просто не смотрим - действуют только они (и встречаться в тексте должны только они).
     *
     * @param string $text
     *
     * @return array|bool
     */
    public static function has_bad_tags($text = '')
    {
        $tag_allowed = TOOLS::strip_option_value(OPTIONS::get('tag_allowed'));
        $tag_rejected = TOOLS::strip_option_value(OPTIONS::get('tag_rejected'));

        // Нет ограничений == нет плохих тегов
        if (empty($tag_allowed) && empty($tag_rejected)) {
            return false;
        }

        if (!empty($tag_allowed)) {
            /**
             * Заменяем все разрешённые теги в тексте и смотрим,
             * встречается ли в тексте ещё какой-то тег
             */

            $_text = preg_replace(
                '~<\/?(' . implode('|', $tag_allowed) . ')(>|\s[^>]*?>)~is' . BX_UTF_PCRE_MODIFIER,
                '',
                $text
            );

            if (preg_match_all(
                '~<\/?([a-z]+)[^>]*?>~is' . BX_UTF_PCRE_MODIFIER,
                $_text,
                $matches
            )
            ) {

                $garbage_tags = array_values(array_filter(array_unique($matches[1])));

                $filtered_text = preg_replace(
                    '~<\/?(' . implode('|', $garbage_tags) . ')(>|\s[^>]*?>)~is' . BX_UTF_PCRE_MODIFIER,
                    '',
                    $text
                );

                return [
                    'garbage_tags' => $garbage_tags,
                    'filtered_text' => $filtered_text
                ];

            }

        } else {
            /**
             * Есть ли в тексте запрещённые теги?
             */

            if (preg_match_all('~<\/?(' . implode('|', $tag_rejected) . ')(>|\s[^>]*?>)~is' . BX_UTF_PCRE_MODIFIER, $text, $matches)) {

                $garbage_tags = array_values(array_filter(array_unique($matches[1])));

                $filtered_text = preg_replace('~<\/?(' . implode('|', $garbage_tags) . ')(>|\s[^>]*?>)~is' . BX_UTF_PCRE_MODIFIER, '', $text);

                return [
                    'garbage_tags' => $garbage_tags,
                    'filtered_text' => $filtered_text
                ];

            }

        }

        return false;

    }

    public static function clear_up(&$text = '')
    {

        $text = self::replace_danger_symbols($text);
        $text = self::replace_smiles($text);
        /**
         * Перенёс это после удаления опасных символов,
         * потому что мы новые строки меняем на <br>,
         * ...и тут же их портим
         */
        self::trim_html($text, 'text');

    }

    private static function replace_danger_symbols($text)
    {
        return htmlspecialchars(
            $text,
            ENT_COMPAT | ENT_HTML5,
            \Bitrix\Main\Context::getCurrent()->getCulture()->getCharset()
        );
    }

    private static function getRegisteredSmiles()
    {
        return ['ag', 'ab', 'ac', 'ai', 'af', 'ax', 'ah', 'ak', 'aq', 'ad', 'ar', 'bw', 'bl', 'bs', 'aa', 'ae', 'bt', 'az', 'bq', 'at', 'bb', 'bu', 'bm', 'br'];
    }

    private static function replace_smiles($text, $mode = 1)
    {

        if (!$_REQUEST['smile_allow']) {
            return $text;
        }

        //Сюда же - замена смайликов.
        $use_smiles = OPTIONS::get('use_smiles');
        if ($use_smiles == 'Y') {

            $registered_smiles = self::getRegisteredSmiles();

            if ($mode == 1) {
                $map_of_replace = [];

                //Читаем смайлы
                foreach ($registered_smiles as $rs) {
                    $tmp_smile = OPTIONS::get('smile_' . $rs);
                    if ($tmp_smile) {
                        //У нас список, указанный через пробел. Нам нужно только первое значение
                        $smile_variants = explode(' ', $tmp_smile);
                        foreach ($smile_variants as $sv)
                            $map_of_replace[strlen($sv)][] = ['s' => $sv, 'r' => $rs];
                    }
                }

                krsort($map_of_replace);

                if (count($map_of_replace)) {
                    foreach ($map_of_replace as $mmv) {
                        foreach ($mmv as $mv)
                            $text = str_replace(
                                $mv['s'],
                                '<img src= "/bitrix/modules/' . TOOLS::getModuleName() . '/images/' . $mv['r'] . '.gif" alt= "" />',
                                $text
                            );
                    }
                }

            } else {

                $smiles_list = [];

                //Читаем смайлы
                foreach ($registered_smiles as $rs) {
                    $tmp_smile = OPTIONS::get('smile_' . $rs);
                    if ($tmp_smile) {
                        //У нас список, указанный через пробел. Нам нужно только первое значение
                        $smile_variants = explode(' ', $tmp_smile);
                        $smiles_list['[ /bitrix/modules/' . TOOLS::getModuleName() . '/images/' . $rs . '.gif ]'] = $smile_variants[0];
                        $smiles_list['<img src= "/bitrix/modules/' . TOOLS::getModuleName() . '/images/' . $rs . '.gif" alt= "" />'] = $smile_variants[0];
                    }
                }

                if (count($smiles_list)) {
                    foreach ($smiles_list as $s_search => $s_replace) {
                        $text = str_replace($s_search, $s_replace, $text);
                    }
                }

            }

        }

        return $text;

    }

    private static function tag_attributes_sanitarium(&$text = '')
    {

        $text = preg_replace_callback(
            '~(<[a-z]+\s+)([^>]+)(>)~is' . BX_UTF_PCRE_MODIFIER,
            static function ($matches) {
                $matches[2] = preg_replace('~([a-z-]+?)(?<!class|id|href|title)\s*=\s*([\'"])[^\2]+?\2~is' . BX_UTF_PCRE_MODIFIER, '', $matches[2]);

                return $matches[1] . $matches[2] . $matches[3];
            },
            $text
        );

    }

    private static function tag_find_and_strip(&$text = '')
    {
        $text = strip_tags($text);
    }

    private static function censored_text($text)
    {

        if (!$text) {
            return false;
        }

        global $censored_info;

        if (!is_array($censored_info)) {
            $censored_info = [];
        }

        $stop_words = [];

        //Читаем правила для стоп-фильтра
        $stop_words_simple = array_filter(TOOLS::strip_option_value(OPTIONS::get("stop_words_simple")));
        $stop_words_rexp = array_filter(TOOLS::strip_option_value(OPTIONS::get("stop_words_rexp")));

        if (!empty($stop_words_simple) || !empty($stop_words_rexp)) {

            $stop_words = array_merge($stop_words_simple, $stop_words_rexp);

            foreach ($stop_words as $w_pos => $w) {
                $stop_words[$w_pos] = "~" . $w . "~i" . BX_UTF_PCRE_MODIFIER;
            }
        }


        $return_with_alert = false;

        if (count($stop_words)) {

            if (strtolower(LANG_CHARSET) != 'utf-8') {
                setlocale(LC_CTYPE, 'ru_RU.CP1251');
            }

            //Для тех умников, которые пишут х-у-й, например
            // Ситуация: Вашего внимания превращается в вашеговнимания и внутри обнаруживается "говн" o_O
//            $text_tinted = preg_replace("![\s\.\,\-\_\=\d]+!", '', $text);
            $text_tinted = preg_replace("![\.\,\-\_\=\d]+!", '', $text);

            foreach ($stop_words as $sw) {
                if (
                    preg_match($sw, $text, $matches)
                    || preg_match($sw, $text_tinted, $matches)
                ) {

                    $censored_info['WORD_FILTER'][] = $matches[0];
                    $return_with_alert = true;

                }
            }

        }

        return $return_with_alert;

    }

    /**
     * Все тексты сразу переконвертируются в HTML режим
     *
     * Текст от HTML различается разве что переносами строки.
     * Сами расставляем в тексте <br />
     *
     * @param $text
     */
    public static function replace_new_string_to_html(&$text)
    {
        $text = preg_replace("!\n!", '<br>', $text);
    }

    /**
     * @param string $text
     * @param string|array $allowed_tags
     * @return mixed|string|string[]|null
     * @throws \Bitrix\Main\ArgumentNullException
     * @throws \Bitrix\Main\ArgumentOutOfRangeException
     */
    public static function normalizeTextInParagraph($text = '', $allowed_tags = '')
    {

        /**
         * Манипуляции, которые проводим с текстом:
         *
         * === чистим неразрывные пробелы
         * ~\x{00a0}~siu
         *
         * 0 === Получаем разрешённые теги, все теги, которые не входят в этот набор отмечаем как запрещённые.
         * Все запрещённые закрывающиеся теги (например </div>) заменяем на:
         * него + "<br />" (</div><br />)
         *
         * !!! У нас к тегу <br /> максимально дружественное отношение (внутри рабочей единицы — абзаца)
         * По-этому, если пользователь нафигачил <br /><br /><br />, предполагается, что сделал это сознательно (нужна ему отбивка, он Энтерами нашлёпал)
         * А значит, мы вместо технического <br /> (который требуется нам только чтобы провести нормализацию), используем
         * /_/ (помним, что == <br />)
         *Техническое задвоение, которое может образоваться в результате чистки не разрешённых тегов, мы сведём к одному
         * /_/ и его уже заменим на <br />
         *
         * Чтобы СТРУКТУРНЫЙ АБЗАЦНЫЙ текст, который вычистится следующим пунктом не слился в сплошную строку.
         * Ниже мы дубликаты br почистим
         *
         * 1 === Чистим внутри те теги, которые нам не разрешены.
         * То есть при запрещённых тегах div, b структура
         *  <div>
         *      <div>
         *          <div><b>
         *              Первое предложение
         *          </b></div><br />
         *      </div>
         *      <div>
         *          Второе предложение
         *      </div><br />
         *  </div>
         *
         * ...вернётся как:
         *      Первое предложение<br />
         *      Второе предложение
         *
         * === Чистим пустышки типа <p> </p>. Вместо таких абзацев останется <br />
         *
         * === Получаем теги на замену. То есть например мы можем не чистить <div>-ы, а просто заменить их все на <p>
         * Аналогично можно приводить <b> к <strong>
         *
         * === (перекрывается пунктом выше)
         * Распаковываем вложенности в линейное следование. Наша идеология предполагает последовательность абзацев, а не их вложенность
         *
         * === Чистим левые class-ы, стили, неймы, id
         *
         * === Чистим множестенное повторение <br /><br /> на одинарный сброс
         *
         */

        if (!$text) {
            return $text;
        }

        $text = self::replace_difficult_symbols($text);

        $inner_text = $text;

        //Имеем возможность переопределить под тестирование
        if ($allowed_tags) {
            $allowed_tags = explode(' ', $allowed_tags);
        } else {
            $allowed_tags = TOOLS::strip_option_value(OPTIONS::get('tag_allowed'));
            // Принудительно
            $allowed_tags[] = 'div';
        }

        //Неразрывные пробелы, переносы строк
        $inner_text = preg_replace('~\x{00a0}~si' . BX_UTF_PCRE_MODIFIER, " ", $inner_text);
        $inner_text = preg_replace("~[\n\r]~si" . BX_UTF_PCRE_MODIFIER, '', $inner_text);

        /**
         * фиксим такие конструкции: <b><br></b> <em><br></em>... br нужно достать наружу.
         * Делать нужно в этом месте, чтобы например <div><br ></div> который выдаёт редактор преобразовалось просто в <br />
         * Потому что ниже код из запрещённых тегов достаёт контент и обрамляет его ещё BR-ами
         */
        $inner_text = preg_replace('~<([^>]+)><br\s*\/?></\1>~is' . BX_UTF_PCRE_MODIFIER, '<br>', $inner_text);
        $inner_text = preg_replace('~<br\s*\/?>~is' . BX_UTF_PCRE_MODIFIER, '<br>', $inner_text);

        // http://php.net/manual/ru/function.strip-tags.php - comment by mariusz.tarnaski at wp dot pl
        /**
         * работает:
         * preg_replace('@(<(?!/?(?:b|em))[^>]*>)@si', '$1xxx', $inner_text);
         * это просто на заметку.
         *
         * Потому что на данном конкретном шаге нам нужны закрывающиеся теги
         */
        $inner_text = preg_replace(
            '@(</(?!(?:' . implode('|', $allowed_tags) . '))[^>]*>)@si' . BX_UTF_PCRE_MODIFIER,
            //"$1/_/", // Потому что так генерятся лишние переносы строк
            "$1",
            $inner_text
        );

        /**
         * Фиксим такое:
         * <p><br>ТЕКСТ<br></p>
         *
         * Должно стать:
         * <p>Текст</p>
         */

        /**
         * неправильно - рубит нах всё подряд
         * $inner_text= preg_replace( '~(\<br\>)*\<(\/?)(' . implode( '|', $allowed_tags ) . ')\>(\<br\>)*~si', "<$2$3>", $inner_text );
         *
         * То есть текст:
         * <b>Объект:</b><br />
         * Раменский район, снт Смородинки;<br />
         * <b>Проект:</b><br>
         * Усманов<br>
         * <b>Исполнители:</b><br>
         * Давыдовский, Калинин;
         * ...должен сохраниться
         *
         */
        $inner_text = preg_replace('~<(' . implode('|', $allowed_tags) . ')>(<br>)*~si' . BX_UTF_PCRE_MODIFIER, "<$1>", $inner_text);
        $inner_text = preg_replace('~(<br>)*<\/(' . implode('|', $allowed_tags) . ')>~si' . BX_UTF_PCRE_MODIFIER, "$1</$2>", $inner_text);

        /**
         * Одни только закрывающиеся теги не катят, поскольку редактор делает так:
         *
         * текст
         * <div>
         *      кууууууча текста и всякой хуйни
         *      <b>текст</b>
         *      кууууууча текста и всякой хуйни
         *      <b>текст</b>
         *      кууууууча текста и всякой хуйни
         *      <b>текст</b>
         * </div>
         *
         * === в результате мы добавляем наш BR после </div>,
         * но бля это не даёт эффекта,
         * поскольку <div> не обработан и нужен разрыв именно перед ним
         *
         */
        /**
         * Расшифровка
         * Такой запрос:
         * (<(?!(?:\/|b|p))[^>]*>)
         *
         * значит, что мы ищем теги, внутри которых НЕ содержатся допустимые теги
         * https://regex101.com/
         * Negative Lookahead ----->  (?!(?:\/|b|p))
         */
        $inner_text = preg_replace('@(<(?!(?:/|' . implode('|', $allowed_tags) . '))[^>]*>)@si' . BX_UTF_PCRE_MODIFIER, "/_/$1", $inner_text);

        // 1 ===
        // Чистим все, кроме разрешённых тегов
        //$inner_text = strip_tags($inner_text, '<' . implode('><', $allowed_tags) . '>');
        $inner_text = self::stripTags($allowed_tags, $inner_text);

        /**
         * В случае:
         * <b> текст <img src= "" alt= ""> <br> текст </b>
         *
         * br должен разрезать тег:
         * <b> текст <img src= "" alt= ""></b>
         * <br>
         * <b>текст </b>
         *
         */

//		$inner_text= preg_replace( '@<(' . implode( '|', $allowed_tags ) . ')>([\w\W\s]*)(<br>)+([\w\W\s]*)(<\/\1>)@si', "<$1>$2$4$5$3/_/", $inner_text );

        $inner_text = preg_replace('@<(' . implode('|', $allowed_tags) . ')([^>]*?)>([^(<\/\1>)]*?)(<br>)([^(<\/\1>)]*?)<\/\1>@si' . BX_UTF_PCRE_MODIFIER, "<$1$2>$3</$1>/_/<$1>$5</$1>", $inner_text);

        /**
         * Ситуация: список захуярен таким образом
         * <ul><li></li></ul><ul><li></li></ul><ul><li></li></ul>
         * или
         * <ul><li>/_/</li></ul><ul><li>/_/</li></ul><ul><li>/_/</li></ul>
         * Нужно это всё склеить в общий <ul>...</ul>
         */
        $inner_text = preg_replace("~</ul>(/_/|\s|<[\s/]*br>)*<ul>~si" . BX_UTF_PCRE_MODIFIER, '', $inner_text);

        // === Возвращаем <br />ы и расставляем там, где они были добавлены взамен структурных закрывающихся тегов
        $inner_text = preg_replace("~(/_/\s?)+~is" . BX_UTF_PCRE_MODIFIER, "<br>", $inner_text);

        // ====
        // Чистим левые class-ы, стили, неймы, id
        $inner_text = preg_replace('~\s(style|class|name|id)\s*=[\'\"][^\'\"]*[\'\"]~is' . BX_UTF_PCRE_MODIFIER, '', $inner_text);
        /**
         * Также фиксим такое:
         * <br>Текст <br>
         * === убираем <br> вначале и вконце
         */
        $inner_text = preg_replace('~^([\s\n\t\r]*\<br\>[\s\n\t\r]*)+~is' . BX_UTF_PCRE_MODIFIER, '', $inner_text);
        $inner_text = preg_replace('~([\s\n\t\r]*\<br\>[\s\n\t\r]*)+$~is' . BX_UTF_PCRE_MODIFIER, '', $inner_text);
        $inner_text = preg_replace('~\s\s~is' . BX_UTF_PCRE_MODIFIER, ' ', $inner_text);

        /**
         * Чистим теги с пустотой внутри
         *
         * <b> <br> </b>
         *
         */
        $inner_text = preg_replace('~<(' . implode('|', $allowed_tags) . ')>(\s|<br>)*<\/\1>~is' . BX_UTF_PCRE_MODIFIER, '', $inner_text);

        return $inner_text;

    }

    protected static function stripTags($tags = [], $text = null)
    {
        if (empty($tags) || empty($text)) {
            return '';
        }

        if (!is_array($tags)) {
            $tags = [$tags];
        }

        /**
         * Сразу и открывающиеся и закрывающиеся
         * https://regex101.com/r/rroSjX/1
         */
        $regExp = sprintf(
            "~<(?!\/?(%s))[^>]*?>~is" . BX_UTF_PCRE_MODIFIER,
            implode('|', $tags)
        );

        return preg_replace($regExp, '', $text);

    }

    /**
     * Спецсимволы
     *
     * @param $text
     * @param string $mode
     *
     * @return mixed
     */
    private static function replace_difficult_symbols($text, $mode = 'html')
    {

        $charset_D7 = strtolower(\Bitrix\Main\Context::getCurrent()->getCulture()->getCharset());
        if ($charset_D7 == 'utf-8') {

            /**
             * http://qaru.site/questions/433490/converting-microsoft-word-special-characters-with-php
             *  "\xC2\xAB",     // « (U+00AB) in UTF-8
             * "\xC2\xBB",     // » (U+00BB) in UTF-8
             * "\xE2\x80\x98", // ‘ (U+2018) in UTF-8
             * "\xE2\x80\x99", // ’ (U+2019) in UTF-8
             * "\xE2\x80\x9A", // ‚ (U+201A) in UTF-8
             * "\xE2\x80\x9B", // ‛ (U+201B) in UTF-8
             * "\xE2\x80\x9C", // " (U+201C) in UTF-8
             * "\xE2\x80\x9D", // " (U+201D) in UTF-8
             * "\xE2\x80\x9E", // „ (U+201E) in UTF-8
             * "\xE2\x80\x9F", // ‟ (U+201F) in UTF-8
             * "\xE2\x80\xB9", // ‹ (U+2039) in UTF-8
             * "\xE2\x80\xBA", // › (U+203A) in UTF-8
             * "\xE2\x80\x93", // – (U+2013) in UTF-8
             * "\xE2\x80\x94", // — (U+2014) in UTF-8
             * "\xE2\x80\xA6"  // … (U+2026) in UTF-8
             */
            $replace_map = [
                "\xC2\xAB" => '&laquo;',     // « (U+00AB) in UTF-8
                "\xC2\xBB" => '&raquo;',     // » (U+00BB) in UTF-8
                "\xe2\x80\x98" => '\'',
                "\xe2\x80\x99" => '\'',
                "\xE2\x80\x9A" => ',', // ‚ (U+201A) in UTF-8
                "\xE2\x80\x9B" => '\'', // ‛ (U+201B) in UTF-8
                "\xe2\x80\x9C" => '"',
                "\xe2\x80\x9D" => '"',
                "\xE2\x80\x9E" => '"', // „ (U+201E) in UTF-8
                "\xE2\x80\x9F" => '"', // ‟ (U+201F) in UTF-8
                "\xE2\x80\xB9" => '&lt;', // ‹ (U+2039) in UTF-8
                "\xE2\x80\xBA" => '&gt;', // › (U+203A) in UTF-8
                "\xe2\x80\x93" => '&ndash;',
                "\xe2\x80\x94" => '&mdash;',
                "\xe2\x80\xa6" => '...',
            ];

        } else {

            // https://www.php.net/manual/ru/function.chr.php
            $replace_map = [
                chr('130') => ',',  // ',', $str);    // baseline single quote
                chr('132') => '"',  // '"', $str);    // baseline double quote
                chr('133') => '...',  // '...', $str);  // ellipsis
                chr('136') => '^',  // '^', $str);    // circumflex accent
                chr('139') => '&lt;',  // '<', $str);    // left single guillemet
                chr('155') => '&gt;',  // '>', $str);    // right single guillemet
                chr('145') => '\'',  // "'", $str);    // left single quote
                chr('146') => '\'',  // "'", $str);    // right single quote
                chr('147') => '"',  // '"', $str);    // left double quote
                chr('148') => '"',  // '"', $str);    // right double quote
                chr('149') => '-',  // '-', $str);    // bullet
                chr('150') => '&ndash;',  // '-', $str);    // endash
                chr('151') => '&mdash;',  // '--', $str);   // emdash
                chr('152') => '~',  // '~', $str);    // tilde accent
                chr('153') => '(tm)',  // '(TM)', $str); // trademark ligature
            ];
        }

        $text = str_replace(array_keys($replace_map), array_values($replace_map), $text);
        return $text;

    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit