Вход Регистрация
Файл: library/XenForo/Helper/String.php
Строк: 554
<?php

/**
 * Helper to do common string operations, such as word wrap and censoring.
 *
 * @package XenForo_Helper
 */
class XenForo_Helper_String
{
    
/**
     * Censor cache for default words/censor string option.
     *
     * @var array|null Null when not set up
     */
    
protected static $_censorCache null;

    
/**
     * Internal value of html encoded variable for auto-linking. Only used
     * to pass to callback.
     *
     * @var boolean
     */
    
protected static $_alptHtmlEncoded true;

    
/**
     * Private constructor. Use statically.
     */
    
private function __construct() {}

    
/**
     * Adds spaces to the given string after the given amount of
     * contiguous, non-whitespace characters.
     *
     * @param string $string
     * @param integer $breakLength Number of characters before break; if null, use option
     *
     * @return string
     */
    
public static function wordWrapString($string$breakLength)
    {
        
$breakLength intval($breakLength);
        if (
$breakLength || $breakLength strlen($string)) // strlen isn't completely accurate, but this is an optimization
        
{
            return 
$string;
        }

        return 
preg_replace('#[^s]{' $breakLength '}(?=[^s])#u''$0  '$string);
    }

    
/**
     * Helper to allow templates to pass the censor string to self::censorString()
     *
     * @param string $string
     * @param string $censorString
     *
     * @return string
     */
    
public static function censorStringTemplateHelper($string$censorString null)
    {
        return 
self::censorString($stringnull$censorString);
    }

    
/**
     * Censors the given string.
     *
     * @param string $string
     * @param array|null $words Words to censor. Null to use option value.
     * @param string|null $censorString String to censor each character with. Null to use option value.
     *
     * @return string
     */
    
public static function censorString($string, array $words null$censorString null)
    {
        
$allowCache = ($words === null && $censorString === null); // ok to use cache for default
        
$censorCache = ($allowCache self::$_censorCache null);

        if (
$censorCache === null)
        {
            if (
$words === null)
            {
                
$words XenForo_Application::get('options')->censorWords;
            }

            if (!
$words)
            {
                if (
$allowCache)
                {
                    
self::$_censorCache = array();
                }

                return 
$string;
            }

            if (
$censorString === null)
            {
                
$censorString XenForo_Application::get('options')->censorCharacter;
            }

            
$censorCache self::buildCensorArray($words$censorString);

            if (
$allowCache)
            {
                
self::$_censorCache $censorCache;
            }
        }

        
$string preg_replace(array_keys($censorCache), $censorCache$string);

        return 
$string;
    }

    
/**
     * Builds the censorship array.
     *
     * @param array $words List of words (from option format)
     * @param string $censorString String to replace each character with if no replacement map
     *
     * @return array Possible keys: exact, any with key-value search/replace pairs
     */
    
public static function buildCensorArray(array $words$censorString)
    {
        
$censorCache = array();

        foreach (
$words AS $key => $word)
        {
            if (
is_string($key) || !isset($word['regex']) || !isset($word['replace']))
            {
                
// old format or broken
                
continue;
            }

            
$regex $word['regex'];
            
$replace $word['replace'];

            
$censorCache[$regex] = is_int($replace) ? str_repeat($censorString$replace) : $replace;
        }

        return 
$censorCache;
    }

    
/**
     * Returns a string that is snipped to $maxLength characters at the nearest space,
     * Appends an elipsis if the string is snipped.
     *
     * @param string
     * @param integer Max length of returned string, excluding elipsis
     * @param integer Offset from string start - will add leading elipsis
     * @param string Elipses string (default: '...')
     *
     * @return string
     */
    
public static function wholeWordTrim($string$maxLength$offset 0$elipses '...')
    {
        
//TODO: this may need a handler for language independence and some form of error correction for bbcode

        
if ($offset)
        {
            
$string preg_replace('/^S*s+/s'''utf8_substr($string$offset));
        }

        
$strLength utf8_strlen($string);

        if (
$maxLength && $strLength $maxLength)
        {
            
$string utf8_substr($string0$maxLength);
            
$string strrev(preg_replace('/^S*s+/s'''strrev($string))) . $elipses;
        }

        if (
$offset)
        {
            
$string $elipses $string;
        }

        return 
$string;
    }

    public static function 
linkTaggedPlainText($string)
    {
        
$string preg_replace_callback(
            
'#(?<=^|s|[](,]|--|@)@[(d+):('|"|&quot;|)(.*)\2]#iU',
            array('self', '_linkTaggedPlainTextCallback'),
            
$string
        );

        return 
$string;
    }

    protected static function _linkTaggedPlainTextCallback(array 
$match)
    {
        
$userId = intval($match[1]);
        
$username = htmlspecialchars($match[3], null, 'utf-8', false);

        
$link = XenForo_Link::buildPublicLink('full:members', array('user_id' => $userId));

        return '<a href="' . htmlspecialchars($link) . '" class="
username" data-user="' . $userId . '' . $username . '">'
            . 
$username . '</a>';
    }

    /**
     * Automatically links URLs/email in the given string of BB code. Output will be original
     * BB code input with [url] or [email] tags inserted where ncessary.
     *
     * @param string 
$string BB code string
     * @param boolean 
$autoEmbedIfEnabled Do auto embed of media if enabled in options
     *
     * @return string
     */
    public static function autoLinkBbCode(
$string$autoEmbedIfEnabled = true)
    {
        
$formatter = XenForo_BbCode_Formatter_Base::create('XenForo_BbCode_Formatter_BbCode_AutoLink', false);
        
$formatter->setAutoEmbed($autoEmbedIfEnabled);
        
$parser = XenForo_BbCode_Parser::create($formatter);
        return 
$parser->render($string);
    }

    /**
     * Auto-links URLs in plain text. This text should generally already be HTML
     * escaped, because it can't be done after the linking.
     *
     * @param string 
$string
     * @param boolean 
$htmlEncoded Denotes whether the text is already encoded; if false, the URL will be encoded before being put into the link
     *
     * @return string Text with links added
     */
    public static function autoLinkPlainText(
$string$htmlEncoded = true)
    {
        self::
$_alptHtmlEncoded = $htmlEncoded;

        return preg_replace_callback(
            '#(?<=[^a-z0-9@-]|^)(https?://|ftp://|www.)[^s"
]+#i',
            
array('self''_autoLinkPlainTextCallback'),
            
$string
        
);
    }

    
/**
     * Internal handler for auto-linking regex.
     *
     * @param array $match
     *
     * @return string
     */
    
protected static function _autoLinkPlainTextCallback(array $match)
    {
        
$link self::prepareAutoLinkedUrl($match[0]);

        if (!
self::$_alptHtmlEncoded)
        {
            
$link['url'] = htmlspecialchars($link['url']);
        }

        list(
$class$target) = self::getLinkClassTarget($link['url']);
        
$class $class " class="$class"" '';
        
$target $target " target="$target"" '';

        return 
'<a href="' $link['url'] . "" rel="nofollow"$class$target>" . $link['linkText'] . '</a>' . $link['suffixText'];
    }

    /**
     * Gets the class and target to apply to a specified link URL.

     * @param string 
$url
     *
     * @return array [class, target, type (internal/external), scheme match]
     */
    public static function getLinkClassTarget(
$url)
    {
        
$target = '_blank';
        
$class = 'externalLink';
        
$type = 'external';
        
$schemeMatch = true;

        
$urlInfo = @parse_url($url);
        if (
$urlInfo)
        {
            if (empty(
$urlInfo['host']))
            {
                
$isInternal = true;
            }
            else
            {
                
$host = $urlInfo['host'] . (!empty($urlInfo['port']) ? ":$urlInfo[port]" : '');
                
$isInternal = ($host == XenForo_Application::$host);

                
$scheme = (!empty($urlInfo['scheme']) ? strtolower($urlInfo['scheme']) : 'http');
                
$schemeMatch = $scheme == (XenForo_Application::$secure ? 'https' : 'http');
            }

            if (
$isInternal)
            {
                
$target = '';
                
$class = 'internalLink';
                
$type = 'internal';
            }
        }

        return array(
$class$target$type$schemeMatch);
    }

    /**
     * Given a text that appears to be a URL, extracts the components from it,
     * possibly moving characters after the link or adding http://.
     *
     * @param string 
$url URL that may have trailing characters or missing scheme
     *
     * @return array Keys: url, linkText, suffixText
     */
    public static function prepareAutoLinkedUrl(
$url)
    {
        
$linkText = $url;
        
$suffixText = '';

        if (strpos(
$url, '://') === false)
        {
            
$url = 'http://' . $url;
        }

        do
        {
            
$matchedTrailer = false;
            
$lastChar = substr($url, -1);
            switch (
$lastChar)
            {
                case ')':
                    if (substr_count(
$url, ')') == substr_count($url, '('))
                    {
                        break;
                    }
                    // break missing intentionally

                case '.':
                case ',':
                case '!':
                case ':':
                case "'":
                    $suffixText = $lastChar . $suffixText;
                    $url = substr($url, 0, -1);
                    $linkText = substr($linkText, 0, -1);

                    $matchedTrailer = true;
                    break;
            }
        }
        while ($matchedTrailer);

        return array(
            '
url' => $url,
            '
linkText' => $linkText,
            '
suffixText' => $suffixText
        );
    }

    /**
     * Strips BB code from a string
     *
     * @param string $string
     * @param boolean $stripQuote If true, contents from within quote tags are stripped
     *
     * @return string
     */
    public static function bbCodeStrip($string, $stripQuote = false)
    {
        /*$formatter = XenForo_BbCode_Formatter_Base::create('
XenForo_BbCode_Formatter_BbCode_Strip', false);
        $formatter->setMaxQuoteDepth(0);

        $parser = XenForo_BbCode_Parser::create($formatter);
        return $parser->render($string);*/

        if ($stripQuote)
        {
            $parts = preg_split('
#([quote[^]]*]|[/quote])#i', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
            
$string '';
            
$quoteLevel 0;
            foreach (
$parts AS $i => $part)
            {
                if (
$i == 0)
                {
                    
// always text, only include if not inside quotes
                    
if ($quoteLevel == 0)
                    {
                        
$string .= rtrim($part) . "n";
                    }
                }
                else
                {
                    
// quote start/end
                    
if ($part[1] == '/')
                    {
                        
// close tag, down a level if open
                        
if ($quoteLevel)
                        {
                            
$quoteLevel--;
                        }
                    }
                    else
                    {
                        
// up a level
                        
$quoteLevel++;
                    }
                }
            }
        }

        
// replaces unviewable tags with a text representation
        
$string preg_replace('#[(attach|media|img|spoiler)[^]]*].*[/\1]#siU''[\1]'$string);

        while (
$string != ($newString preg_replace('#[([a-z0-9]+)(=[^]]*)?](.*)[/1]#siU''3'$string)))
        {
            
$string $newString;
        }

        
$string str_replace('[*]'''$string);

        return 
trim($string);
    }

    
/**
     * Creates a snippet of $length maximum characters around a search term.
     * If the term is not found, the snippet is taken from the string start.
     *
     * @param $string
     * @param $maxLength
     * @param $term
     *
     * @return string
     */
    
public static function wholeWordTrimAroundSearchTerm($string$maxLength$term)
    {
        
$stringLength utf8_strlen($string);

        if (
$stringLength $maxLength)
        {
            
$term strval($term);

            if (
$term !== '')
            {
                
// TODO: slightly more intelligent search term matching, breaking up multiple words etc.
                
$termPosition utf8_strpos(utf8_strtolower($string), utf8_strtolower($term));
            }
            else
            {
                
$termPosition false;
            }

            if (
$termPosition !== false)
            {
                
// add term length to term start position
                
$startPos $termPosition utf8_strlen($term);

                
// count back half the max characters
                
$startPos -= $maxLength 2;

                
// don't overflow the beginning
                
$startPos max(0$startPos);

                
// don't overflow the end
                
$startPos min($startPos$stringLength $maxLength);
            }
            else
            {
                
$startPos 0;
            }

            
$string self::wholeWordTrim($string$maxLength$startPos);
        }

        return 
$string;
    }

    
/**
     * Returns an HTML string with instances a search term highlighted with <em class="$emClass">...</em>
     *
     * @param string Haystack
     * @param string Needle
     * @param string Class with which to style the wrapping <em>
     *
     * @return string HTML
     */
    
public static function highlightSearchTerm($string$term$emClass 'highlight')
    {
        
$term trim(preg_replace('#((^|s)[+|-]|[/()"~^])#'' 'strval($term)));
        if (
$term !== '')
        {
            return 
preg_replace('/(' preg_replace('#s+#''|'preg_quote(htmlspecialchars($term), '/')) . ')/si''<em class="' $emClass '">1</em>'htmlspecialchars($string));
        }

        return 
htmlspecialchars($string);
    }

    
/**
     * Strips out quoted text from the specified string, allowing for quoted text
     * up to a specified depth.
     *
     * @param string $string
     * @param integer $allowedDepth -1 for unlimited depth
     * @param boolean $censorResults
     *
     * @return string Quotes stripped
     */
    
public static function stripQuotes($string$allowedDepth = -1$censorResults true)
    {
        if (
$allowedDepth == -1)
        {
            return 
$string;
        }
        else
        {
            
$formatter XenForo_BbCode_Formatter_Base::create('XenForo_BbCode_Formatter_BbCode_Strip'false);
            
$formatter->setMaxQuoteDepth($allowedDepth);
            
$formatter->setCensoring($censorResults);

            
$parser XenForo_BbCode_Parser::create($formatter);
            return 
$parser->render($string);
        }
    }
}
Онлайн: 0
Реклама