Completed
Push — 2.1 ( 646cd7...866c70 )
by Dmitry
09:49
created

BaseHtmlPurifier::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 1
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
13
/**
14
 * BaseHtmlPurifier provides concrete implementation for [[HtmlPurifier]].
15
 *
16
 * Do not use BaseHtmlPurifier. Use [[HtmlPurifier]] instead.
17
 *
18
 * This helper requires `ezyang/htmlpurifier` library to be installed. This can be done via composer:
19
 *
20
 * ```
21
 * composer require --prefer-dist "ezyang/htmlpurifier:~4.6"
22
 * ```
23
 *
24
 * @see http://htmlpurifier.org/
25
 *
26
 * @author Alexander Makarov <[email protected]>
27
 * @since 2.0
28
 */
29
class BaseHtmlPurifier
30
{
31
    /**
32
     * Passes markup through HTMLPurifier making it safe to output to end user.
33
     *
34
     * @param string $content The HTML content to purify
35
     * @param array|\Closure|null $config The config to use for HtmlPurifier.
36
     * If not specified or `null` the default config will be used.
37
     * You can use an array or an anonymous function to provide configuration options:
38
     *
39
     * - An array will be passed to the `HTMLPurifier_Config::create()` method.
40
     * - An anonymous function will be called after the config was created.
41
     *   The signature should be: `function($config)` where `$config` will be an
42
     *   instance of `HTMLPurifier_Config`.
43
     *
44
     *   Here is a usage example of such a function:
45
     *
46
     *   ```php
47
     *   // Allow the HTML5 data attribute `data-type` on `img` elements.
48
     *   $content = HtmlPurifier::process($content, function ($config) {
49
     *     $config->getHTMLDefinition(true)
50
     *            ->addAttribute('img', 'data-type', 'Text');
51
     *   });
52
     * ```
53
     * @return string the purified HTML content.
54
     */
55 2
    public static function process($content, $config = null)
56
    {
57 2
        $configInstance = static::createConfig($config);
58 2
        $configInstance->autoFinalize = false;
59
60 2
        $purifier = \HTMLPurifier::instance($configInstance);
61
62 2
        return $purifier->purify($content);
63
    }
64
65
    /**
66
     * Truncate a HTML string.
67
     *
68
     * @param string $html The HTML string to be truncated.
69
     * @param int $count
70
     * @param string $suffix String to append to the end of the truncated string.
71
     * @param string|bool $encoding
72
     * @return string
73
     * @since 2.1.0
74
     */
75
    public static function truncate($html, $count, $suffix, $encoding = false)
76
    {
77
        $config = static::createConfig();
78
79
        $lexer = \HTMLPurifier_Lexer::create($config);
80
        $tokens = $lexer->tokenizeHTML($html, $config, new \HTMLPurifier_Context());
81
        $openTokens = [];
82
        $totalCount = 0;
83
        $depth = 0;
84
        $truncated = [];
85
        foreach ($tokens as $token) {
0 ignored issues
show
Bug introduced by
The expression $tokens of type array<integer,object<HTMLPurifier_Token>>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
86
            if ($token instanceof \HTMLPurifier_Token_Start) { //Tag begins
87
                $openTokens[$depth] = $token->name;
88
                $truncated[] = $token;
89
                ++$depth;
90
            } elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text
91
                if ($encoding === false) {
92
                    preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['', ''];
93
                    $token->data = $prefixSpace[1] . StringHelper::truncateWords(ltrim($token->data), $count - $totalCount, '');
94
                    $currentCount = StringHelper::countWords($token->data);
95
                } else {
96
                    $token->data = StringHelper::truncate($token->data, $count - $totalCount, '', $encoding);
0 ignored issues
show
Bug introduced by
It seems like $encoding defined by parameter $encoding on line 75 can also be of type boolean; however, yii\helpers\BaseStringHelper::truncate() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
97
                    $currentCount = mb_strlen($token->data, $encoding);
98
                }
99
                $totalCount += $currentCount;
100
                $truncated[] = $token;
101
            } elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends
102
                if ($token->name === $openTokens[$depth - 1]) {
103
                    --$depth;
104
                    unset($openTokens[$depth]);
105
                    $truncated[] = $token;
106
                }
107
            } elseif ($token instanceof \HTMLPurifier_Token_Empty) { //Self contained tags, i.e. <img/> etc.
108
                $truncated[] = $token;
109
            }
110
            if ($totalCount >= $count) {
111
                if (0 < count($openTokens)) {
112
                    krsort($openTokens);
113
                    foreach ($openTokens as $name) {
114
                        $truncated[] = new \HTMLPurifier_Token_End($name);
115
                    }
116
                }
117
                break;
118
            }
119
        }
120
        $context = new \HTMLPurifier_Context();
121
        $generator = new \HTMLPurifier_Generator($config, $context);
122
123
        return $generator->generateFromTokens($truncated) . ($totalCount >= $count ? $suffix : '');
124
    }
125
126
    /**
127
     * Creates a HtmlPurifier configuration instance.
128
     * @see \HTMLPurifier_Config::create()
129
     * @param array|\Closure|null $config The config to use for HtmlPurifier.
130
     * If not specified or `null` the default config will be used.
131
     * You can use an array or an anonymous function to provide configuration options:
132
     *
133
     * - An array will be passed to the `HTMLPurifier_Config::create()` method.
134
     * - An anonymous function will be called after the config was created.
135
     *   The signature should be: `function($config)` where `$config` will be an
136
     *   instance of `HTMLPurifier_Config`.
137
     *
138
     *   Here is a usage example of such a function:
139
     *
140
     *   ```php
141
     *   // Allow the HTML5 data attribute `data-type` on `img` elements.
142
     *   $content = HtmlPurifier::process($content, function ($config) {
143
     *     $config->getHTMLDefinition(true)
144
     *            ->addAttribute('img', 'data-type', 'Text');
145
     *   });
146
     *   ```
147
     *
148
     * @return \HTMLPurifier_Config HTMLPurifier config instance.
149
     * @throws InvalidConfigException in case "ezyang/htmlpurifier" package is not available.
150
     * @since 2.1.0
151
     */
152 4
    public static function createConfig($config = null)
153
    {
154 4
        if (!class_exists(\HTMLPurifier_Config::class)) {
155
            throw new InvalidConfigException('Unable to load "' . \HTMLPurifier_Config::class . '" class. Make sure you have installed "ezyang/htmlpurifier:~4.6" composer package.');
156
        }
157
158 4
        $configInstance = \HTMLPurifier_Config::create($config instanceof \Closure ? null : $config);
159 4
        if (Yii::$app !== null) {
160 2
            $configInstance->set('Cache.SerializerPath', Yii::$app->getRuntimePath());
161 2
            $configInstance->set('Cache.SerializerPermissions', 0775);
162
        }
163
164 4
        static::configure($configInstance);
165 4
        if ($config instanceof \Closure) {
166
            call_user_func($config, $configInstance);
167
        }
168
169 4
        return $configInstance;
170
    }
171
172
    /**
173
     * Allow the extended HtmlPurifier class to set some default config options.
174
     * @param \HTMLPurifier_Config $config HTMLPurifier config instance.
175
     * @since 2.0.3
176
     */
177 4
    protected static function configure($config)
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
178
    {
179 4
    }
180
}
181