Passed
Push — master ( eb92d0...acd567 )
by Arjan
03:26
created

WhitespacePlaceholder::replaceElementContents()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 7
cts 7
cp 1
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 2
crap 1
1
<?php
2
3
namespace ArjanSchouten\HtmlMinifier\Placeholders;
4
5
use ArjanSchouten\HtmlMinifier\PlaceholderContainer;
6
use ArjanSchouten\HtmlMinifier\Repositories\HtmlInlineElementsRepository;
7
8
class WhitespacePlaceholder implements PlaceholderInterface
9
{
10
    /**
11
     * @var array
12
     */
13
    protected $htmlPlaceholderTags = [
14
        'plaintext',
15
        'textarea',
16
        'listing',
17
        'script',
18
        'style',
19
        'code',
20
        'cite',
21
        'pre',
22
        'xmp',
23
    ];
24
25
    /**
26
     * Replace critical content with a temporary placeholder.
27
     *
28
     * @param \ArjanSchouten\HtmlMinifier\MinifyContext $context
29
     *
30
     * @return \ArjanSchouten\HtmlMinifier\MinifyContext
31
     */
32 5
    public function process($context)
33
    {
34 5
        $contents = $context->getContents();
35
36 5
        $contents = $this->whitespaceBetweenInlineElements($contents, $context->getPlaceholderContainer());
37 5
        $contents = $this->whitespaceInInlineElements($contents, $context->getPlaceholderContainer());
38 5
        $contents = $this->replaceElementContents($contents, $context->getPlaceholderContainer());
39
40 5
        return $context->setContents($contents);
41
    }
42
43
    /**
44
     * Whitespaces between inline html elements must be replaced with a placeholder because
45
     * a browser is showing that whitespace.
46
     *
47
     * @param string $contents
48
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
49
     *
50
     * @return string
51
     */
52 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...
53
    {
54 5
        $elementsRegex = $this->getInlineElementsRegex();
55
56 5
        return preg_replace_callback(
57
            '/
58
                (
59 5
                    <('.$elementsRegex.')       # Match the start tag and capture it
60
                    (?:(?!<\/\2>).*)            # Match everything without the end tag
61
                    <\/\2>                      # Match the captured elements end tag
62
                )
63
                \s+                             # Match minimal 1 whitespace between the elements
64 5
                <('.$elementsRegex.')           # Match the start of the next inline element
65
            /xi',
66
            function ($match) use ($placeholderContainer) {
67
                // Where going to respect one space between the inline elements.
68 1
                $placeholder = $placeholderContainer->addPlaceholder(' ');
69
70 1
                return $match[1].$placeholder.'<'.$match[3];
71 5
        }, $contents);
72
    }
73
74
    /**
75
     * Whitespaces in an inline element have a function so we replace it.
76
     *
77
     * @param string $contents
78
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
79
     *
80
     * @return string
81
     */
82 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...
83
    {
84 5
        $elementsRegex = $this->getInlineElementsRegex();
85
86 5
        return preg_replace_callback(
87
            '/
88
                (
89 5
                    <('.$elementsRegex.')   # Match an inline element
90
                    (?:(?!<\/\2>).*)        # Match everything except its end tag
91
                    <\/\2>                  # Match the end tag
92
                    \s+
93
                )
94 5
                <('.$elementsRegex.')       # Match starting tag
95
            /xis',
96
            function ($match) use ($placeholderContainer) {
97 1
                return $this->replaceWhitespacesInInlineElements($match[1], $placeholderContainer).'<'.$match[3];
98 5
            }, $contents);
99
    }
100
101
    /**
102
     * Replace the whitespaces in inline elements with a placeholder.
103
     *
104
     * @param string $element
105
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
106
     *
107
     * @return string
108
     */
109 1
    private function replaceWhitespacesInInlineElements($element, PlaceholderContainer $placeholderContainer)
110
    {
111
        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...
112 1
            return '>'.$placeholderContainer->addPlaceholder(' ');
113 1
        }, $element);
114
    }
115
116
    /**
117
     * @param string $contents
118
     * @param \ArjanSchouten\HtmlMinifier\PlaceholderContainer $placeholderContainer
119
     *
120
     * @return string
121
     */
122 5
    protected function replaceElementContents($contents, PlaceholderContainer $placeholderContainer)
123
    {
124 5
        $htmlTags = implode('|', $this->htmlPlaceholderTags);
125
126
        $pattern = '/
127
            (
128 5
                <(' . $htmlTags . ')                    # Match html start tag and capture
129
                    (?:
130
                        [^"\'>]*|"[^"]*"|\'[^\']*\'
131
                    )*                                  # Match all attributes
132
                >                                       # Match end of tag
133
            )
134
            (
135
                (?:                                     # Negotiate this part
136
                    (?!<\/\2>)                          # Negative look ahead for the end tag
137
                    .                                   # Match everything if not the end tag 
138
                )*+                                     # Possessive quantifier which will prevent backtracking
139
            )
140
            (<\/\2>)                                    # Match end tag by back referencing the start tag
141
            /xis';
142
143 5
        $contents = preg_replace_callback($pattern, function ($match) use ($placeholderContainer) {
144 1
            return $match[1] . $placeholderContainer->addPlaceholder($match[3]) . $match[4];
145 5
        }, $contents);
146
147 5
        return $contents;
148
    }
149
150
    /**
151
     * Get the regular expression for matching the inline element names.
152
     *
153
     * @return string
154
     */
155 5
    private function getInlineElementsRegex()
156
    {
157 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';
158
    }
159
}
160