WhitespacePlaceholder::replaceElementContents()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 25
ccs 6
cts 6
cp 1
rs 8.8571
cc 1
eloc 7
nc 1
nop 2
crap 1
1
<?php
2
3
namespace ArjanSchouten\HtmlMinifier\Placeholders;
4
5
use ArjanSchouten\HtmlMinifier\PlaceholderContainer;
6
7
class WhitespacePlaceholder implements PlaceholderInterface
8
{
9
    /**
10
     * @var array
11
     */
12
    protected $htmlPlaceholderTags = [
13
        'plaintext',
14
        'textarea',
15
        'listing',
16
        'script',
17
        'style',
18
        'code',
19
        'cite',
20
        'pre',
21
        'xmp',
22
    ];
23
24
    /**
25
     * Replace critical content with a temporary placeholder.
26
     *
27
     * @param \ArjanSchouten\HtmlMinifier\MinifyContext $context
28
     *
29
     * @return \ArjanSchouten\HtmlMinifier\MinifyContext
30
     */
31 5
    public function process($context)
32
    {
33 5
        $contents = $context->getContents();
34
35 5
        $contents = $this->whitespaceBetweenInlineElements($contents, $context->getPlaceholderContainer());
36 5
        $contents = $this->whitespaceInInlineElements($contents, $context->getPlaceholderContainer());
37 5
        $contents = $this->replaceElementContents($contents, $context->getPlaceholderContainer());
38
39 5
        return $context->setContents($contents);
40
    }
41
42
    /**
43
     * Whitespaces between inline html elements must be replaced with a placeholder because
44
     * a browser is showing that whitespace.
45
     *
46
     * @param string $contents
47
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
48
     *
49
     * @return string
50
     */
51 5 View Code Duplication
    protected function whitespaceBetweenInlineElements($contents, PlaceholderContainer $placeholderContainer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
    {
53 5
        $elementsRegex = $this->getInlineElementsRegex();
54
55 5
        return preg_replace_callback(
56
            '/
57
                (
58 5
                    <('.$elementsRegex.')       # Match the start tag and capture it
59
                    (?:(?!<\/\2>).*)            # Match everything without the end tag
60
                    <\/\2>                      # Match the captured elements end tag
61
                )
62
                \s+                             # Match minimal 1 whitespace between the elements
63 5
                <('.$elementsRegex.')           # Match the start of the next inline element
64
            /xi',
65
            function ($match) use ($placeholderContainer) {
66
                // Where going to respect one space between the inline elements.
67 1
                $placeholder = $placeholderContainer->createPlaceholder(' ');
68
69 1
                return $match[1].$placeholder.'<'.$match[3];
70 5
        }, $contents);
71
    }
72
73
    /**
74
     * Whitespaces in an inline element have a function so we replace it.
75
     *
76
     * @param string $contents
77
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
78
     *
79
     * @return string
80
     */
81 5 View Code Duplication
    protected function whitespaceInInlineElements($contents, PlaceholderContainer $placeholderContainer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
82
    {
83 5
        $elementsRegex = $this->getInlineElementsRegex();
84
85 5
        return preg_replace_callback(
86
            '/
87
                (
88 5
                    <('.$elementsRegex.')   # Match an inline element
89
                    (?:(?!<\/\2>).*)        # Match everything except its end tag
90
                    <\/\2>                  # Match the end tag
91
                    \s+
92
                )
93 5
                <('.$elementsRegex.')       # Match starting tag
94
            /xis',
95
            function ($match) use ($placeholderContainer) {
96 1
                return $this->replaceWhitespacesInInlineElements($match[1], $placeholderContainer).'<'.$match[3];
97 5
            }, $contents);
98
    }
99
100
    /**
101
     * Replace the whitespaces in inline elements with a placeholder.
102
     *
103
     * @param string $element
104
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
105
     *
106
     * @return string
107
     */
108 1
    private function replaceWhitespacesInInlineElements($element, PlaceholderContainer $placeholderContainer)
109
    {
110
        return preg_replace_callback('/>\s/', function ($match) use ($placeholderContainer) {
0 ignored issues
show
Unused Code introduced by
The parameter $match 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...
111 1
            return '>'.$placeholderContainer->createPlaceholder(' ');
112 1
        }, $element);
113
    }
114
115
    /**
116
     * @param string $contents
117
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
118
     *
119
     * @return string
120
     */
121 5
    protected function replaceElementContents($contents, PlaceholderContainer $placeholderContainer)
122
    {
123 5
        $htmlTags = implode('|', $this->htmlPlaceholderTags);
124
125
        $pattern = '/
126
            (
127 5
                <(' . $htmlTags . ')                    # Match html start tag and capture
128
                    (?:
129
                        [^"\'>]*|"[^"]*"|\'[^\']*\'
130
                    )*                                  # Match all attributes
131
                >                                       # Match end of tag
132
            )
133
            (
134
                (?:                                     # Negotiate this part
135
                    (?!<\/\2>)                          # Negative look ahead for the end tag
136
                    .                                   # Match everything if not the end tag 
137
                )*+                                     # Possessive quantifier which will prevent backtracking
138
            )
139
            (<\/\2>)                                    # Match end tag by back referencing the start tag
140
            /xis';
141
142 5
        return preg_replace_callback($pattern, function ($match) use ($placeholderContainer) {
143 1
            return $match[1].$placeholderContainer->createPlaceholder($match[3]).$match[4];
144 5
        }, $contents);
145
    }
146
147
    /**
148
     * Get the regular expression for matching inline elements.
149
     *
150
     * @return string
151
     */
152 5
    private function getInlineElementsRegex()
153
    {
154 5
        return 'a(?:bbr|cronym)?|b(?:utton|r|ig|do)?|c(?:ite|ode)|dfn|em|i(?:mg|nput)|kbd|label|map|object|q|s(?:amp|elect|mall|pan|trong|u[bp])|t(?:extarea|t)|var';
155
    }
156
}
157