Code

< 40 %
40-60 %
> 60 %
1
<?php
2
/**
3
 * HtmlCompressor.php
4
 * @author Revin Roman
5
 * @link https://processfast.ru
6
 */
7
8
namespace processfast\yii\minify;
9
10
/**
11
 * Class HtmlCompressor
12
 * @package processfast\yii\minify
13
 */
14
class HtmlCompressor
15
{
16
17
    /**
18
     * @param string $data is either a handle to an open file, or an HTML string
19
     * @param null|array $options key => value array of execute options
20
     * The possible keys are:
21
     *
22
     *  - `c` or `no-comments` - removes HTML comments
23
     *  - `s` or `stats` - output filesize savings calculation
24
     *  - `x` or `extra` - perform extra (possibly unsafe) compression operations
25
     *
26
     * Example: HtmlCompressor::compress($HtmlCode, $options = ['no-comments' => true])
27
     *
28
     * @return string
29
     */
30 1
    public static function compress($data, $options = null)
31
    {
32 1
        return (new static)
33 1
            ->htmlCompress($data, $options);
34
    }
35
36
37
    /**
38
     * HTML Compressor 1.0.1
39
     * Original Author: Tyler Hall <[email protected]>
40
     * Edited by: Revin Roman <[email protected]>
41
     * Latest Source and Bug Tracker: http://github.com/tylerhall/html-compressor
42
     *
43
     * Attemps to reduce the filesize of an HTML document by removing unnecessary
44
     * whitespace at the beginning and end of lines, inside closing tags, and
45
     * stripping blank lines completely. <pre> tags are respected and their contents
46
     * are left alone. Warning, nested <pre> tags may exhibit unexpected behaviour.
47
     *
48
     * This code is licensed under the MIT Open Source License.
49
     * Copyright (c) 2010 [email protected]
50
     * Permission is hereby granted, free of charge, to any person obtaining a copy
51
     * of this software and associated documentation files (the "Software"), to deal
52
     * in the Software without restriction, including without limitation the rights
53
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
54
     * copies of the Software, and to permit persons to whom the Software is
55
     * furnished to do so, subject to the following conditions:
56
     *
57
     * The above copyright notice and this permission notice shall be included in
58
     * all copies or substantial portions of the Software.
59
     *
60
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
61
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
62
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
63
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
64
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
65
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
66
     * THE SOFTWARE.
67
     *
68
     * @param $data
69
     * @param null|array $options
70
     * @return bool|mixed|string
71
     */
72 1
    private function htmlCompress($data, $options = null)
73
    {
74 1
        if (!isset($options)) {
75 1
            $options = [];
76 1
        }
77
78 1
        $data .= "\n";
79 1
        $out = '';
80 1
        $inside_pre = false;
81 1
        $inside_textarea = false;
82 1
        $bytecount = 0;
83
84 1
        while ($line = $this->getLine($data)) {
85 1
            $bytecount += strlen($line);
86
87 1
            if ($inside_pre) {
88 1
                list($line, $inside_pre) = $this->checkInsidePre($line);
89 1
            } elseif ($inside_textarea) {
90
                list($line, $inside_textarea) = $this->checkInsideTextarea($line);
91
            } else {
92 1
                if (strpos($line, '<pre') !== false) {
93
                    // Only trim the beginning since we just entered a <pre> block...
94 1
                    $line = ltrim($line);
95
96
                    // If the <pre> ends on the same line, don't turn on $inside_pre...
97 1
                    list($line, $inside_pre) = $this->checkInsidePre($line);
98 1
                } elseif (strpos($line, '<textarea') !== false) {
99
                    // Only trim the beginning since we just entered a <textarea> block...
100
                    $line = ltrim($line);
101
102
                    // If the <textarea> ends on the same line, don't turn on $inside_textarea...
103
                    list($line, $inside_textarea) = $this->checkInsideTextarea($line);
104
                } else {
105
                    // Since we're not inside a <pre> block, we can trim both ends of the line
106 1
                    $line = trim($line);
107
108
                    // And condense multiple spaces down to one
109 1
                    $line = preg_replace('/\s\s+/', ' ', $line);
110
                }
111
            }
112
113
            // Filter out any blank lines that aren't inside a <pre> block...
114 1
            if ($inside_pre || $inside_textarea) {
115 1
                $out .= $line;
116 1
            } elseif ($line != '') {
117 1
                $out .= $line . "\n";
118 1
            }
119 1
        }
120
121
        // Perform any extra (unsafe) compression techniques...
122 1
        if (array_key_exists('x', $options) || array_key_exists('extra', $options)) {
123
            // Can break layouts that are dependent on whitespace between tags
124 1
            $out = str_replace(">\n<", '><', $out);
125 1
        }
126
127
        // Remove HTML comments...
128 1
        if (array_key_exists('c', $options) || array_key_exists('no-comments', $options)) {
129 1
            $out = preg_replace('/(<!--.*?-->)/ms', '', $out);
130 1
            $out = str_replace('<!>', '', $out);
131 1
        }
132
133
        // Remove the trailing \n
134 1
        $out = trim($out);
135
136
        // Output either our stats or the compressed data...
137 1
        if (array_key_exists('s', $options) || array_key_exists('stats', $options)) {
138 1
            $echo = '';
139 1
            $echo .= "Original Size: $bytecount\n";
140 1
            $echo .= "Compressed Size: " . strlen($out) . "\n";
141 1
            $echo .= "Savings: " . round((1 - strlen($out) / $bytecount) * 100, 2) . "%\n";
142 1
            echo $echo;
143 1
        } else {
144 1
            return $out;
145
        }
146
147 1
        return false;
148
    }
149
150
    /**
151
     * @param $line
152
     * @return array
153
     * @codeCoverageIgnore
154
     */
155 View Code Duplication
    private function checkInsidePre($line)
156
    {
157
        $inside_pre = true;
158
159
        if ((strpos($line, '</pre') !== false) && (strripos($line, '</pre') >= strripos($line, '<pre'))) {
160
            $line = rtrim($line);
161
            $inside_pre = false;
162
        }
163
164
        return [$line, $inside_pre];
165
    }
166
167
    /**
168
     * @param $line
169
     * @return array
170
     * @codeCoverageIgnore
171
     */
172 View Code Duplication
    private function checkInsideTextarea($line)
173
    {
174
        $inside_textarea = true;
175
176
        if ((strpos($line, '</textarea') !== false) && (strripos($line, '</textarea') >= strripos($line, '<textarea'))) {
177
            $line = rtrim($line);
178
            $inside_textarea = false;
179
        }
180
181
        return [$line, $inside_textarea];
182
    }
183
184
    /**
185
     * Returns the next line from an open file handle or a string
186
     * @param $data
187
     * @return bool|string
188
     * @codeCoverageIgnore
189
     */
190
    private function getLine(&$data)
191
    {
192
        if (is_resource($data)) {
193
            return fgets($data);
194
        }
195
196
        if (is_string($data)) {
197
            if (strlen($data) > 0) {
198
                $pos = strpos($data, "\n");
199
                $return = substr($data, 0, $pos) . "\n";
200
                $data = substr($data, $pos + 1);
201
202
                return $return;
203
            } else {
204
                return false;
205
            }
206
        }
207
208
        return false;
209
    }
210
211
    /**
212
     * @param $data
213
     * @param null|array $options
214
     * @return bool|mixed|string
215
     * @deprecated
216
     * @codeCoverageIgnore
217
     */
218
    private function html_compress($data, $options = null)
219
    {
220
        \Yii::warning(sprintf('You are using an deprecated method `%s`.', 'html_compress'));
221
222
        return $this->htmlCompress($data, $options);
223
    }
224
225
    /**
226
     * Returns the next line from an open file handle or a string
227
     * @param $data
228
     * @return bool|string
229
     * @deprecated
230
     * @codeCoverageIgnore
231
     */
232
    private function get_line(&$data)
233
    {
234
        \Yii::warning(sprintf('You are using an deprecated method `%s`.', 'get_line'));
235
236
        return $this->getLine($data);
237
    }
238
}