Passed
Push — master ( a6877f...69aabc )
by Joschi
02:36 queued 22s
created

CssRulesSerializer   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Test Coverage

Coverage 78.95%

Importance

Changes 0
Metric Value
dl 0
loc 199
ccs 45
cts 57
cp 0.7895
rs 10
c 0
b 0
f 0
wmc 17

9 Methods

Rating   Name   Duplication   Size   Complexity  
A addAndMultiplyCssMediaConditions() 0 19 3
A exportCssRule() 0 8 2
A initializeCssMediaConditions() 0 11 2
B exportCssRuleMediaConditions() 0 26 3
A exportCssRuleAtBlock() 0 7 1
A __construct() 0 3 1
A exportCssRuleDeclarationBlock() 0 7 1
A exportCssRuleRule() 0 7 1
A toCss() 0 19 3
1
<?php
2
3
/**
4
 * responsive-images-css
5
 *
6
 * @category   Jkphl
7
 * @package    Jkphl\Respimgcss
8
 * @subpackage Jkphl\Respimgcss\Infrastructure
9
 * @author     Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright  Copyright © 2018 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license    http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2018 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Respimgcss\Infrastructure;
38
39
use Jkphl\Respimgcss\Domain\Contract\CssMediaConditionInterface as DomainMediaConditionInterface;
40
use Jkphl\Respimgcss\Domain\Contract\CssRuleInterface;
41
use Jkphl\Respimgcss\Infrastructure\CssMediaConditionInterface as RenderableMediaConditionInterface;
42
use Jkphl\Respimgcss\Ports\InvalidArgumentException;
43
use Sabberworm\CSS\CSSList\AtRuleBlockList;
44
use Sabberworm\CSS\CSSList\Document;
45
use Sabberworm\CSS\Renderable;
46
use Sabberworm\CSS\Rule\Rule;
47
use Sabberworm\CSS\RuleSet\DeclarationBlock;
48
use Sabberworm\CSS\Value\CSSString;
49
use Sabberworm\CSS\Value\URL;
50
51
/**
52
 * CSS rules serializer
53
 *
54
 * @package    Jkphl\Respimgcss
55
 * @subpackage Jkphl\Respimgcss\Infrastructure
56
 */
57
class CssRulesSerializer
58
{
59
    /**
60
     * CSS rules
61
     *
62
     * @var CssRuleInterface[]
63
     */
64
    protected $rules;
65
66
    /**
67
     * CSS rules serializer constructor
68
     *
69
     * @param CssRuleInterface[] $rules CSS Rules
70
     */
71 4
    public function __construct(array $rules)
72
    {
73 4
        $this->rules = $rules;
74 4
    }
75
76
    /**
77
     * Return the registered rules as CSS
78
     *
79
     * @param string $selector CSS selector
80
     *
81
     * @return string Serialized CSS rules
82
     * @throws InvalidArgumentException If the CSS selector is invalid
83
     */
84 4
    public function toCss(string $selector): string
85
    {
86
        // If the CSS selector is invalid
87 4
        $selector = trim($selector);
88 4
        if (!strlen($selector)) {
89 1
            throw new InvalidArgumentException(
90 1
                sprintf(InvalidArgumentException::INVALID_CSS_SELECTOR_STR, $selector),
91 1
                InvalidArgumentException::INVALID_CSS_SELECTOR
92
            );
93
        }
94
95 3
        $cssDocument = new Document();
96
97
        /** @var CssRuleInterface $rule */
98 3
        foreach ($this->rules as $rule) {
99 3
            $cssDocument->append($this->exportCssRule($rule, $selector));
100
        }
101
102 3
        return $cssDocument->render();
103
    }
104
105
    /**
106
     * Export a single CSS rule
107
     *
108
     * @param CssRuleInterface $rule CSS rule
109
     * @param string $selector       CSS selector
110
     *
111
     * @return Renderable
112
     */
113 3
    protected function exportCssRule(CssRuleInterface $rule, string $selector): Renderable
114
    {
115
        // If the rule has conditions: Render as an @-rule block
116 3
        if (count($rule)) {
117 3
            return $this->exportCssRuleAtBlock($rule, $selector);
118
        }
119
120 3
        return $this->exportCssRuleDeclarationBlock($rule, $selector);
121
    }
122
123
    /**
124
     * Export a CSS rule as @-media block
125
     *
126
     * @param CssRuleInterface $rule CSS rule
127
     * @param string $selector       CSS selector
128
     *
129
     * @return AtRuleBlockList @-media block
130
     */
131 3
    protected function exportCssRuleAtBlock(CssRuleInterface $rule, string $selector): AtRuleBlockList
132
    {
133 3
        $atRuleMediaConditions = $this->exportCssRuleMediaConditions($rule);
134 3
        $atRuleBlockList       = new AtRuleBlockList('media', $atRuleMediaConditions);
135 3
        $atRuleBlockList->append($this->exportCssRuleDeclarationBlock($rule, $selector));
136
137 3
        return $atRuleBlockList;
138
    }
139
140
    /**
141
     * Export the media conditions associated with a CSS rule
142
     *
143
     * @param CssRuleInterface $rule CSS rule
144
     *
145
     * @return string Media conditions
146
     */
147 3
    protected function exportCssRuleMediaConditions(CssRuleInterface $rule): string
148
    {
149 3
        $alternativeMediaConditions = new CssMediaConditionAlternatives();
150
151
        // Run through all conditions of this rule
152
        /** @var DomainMediaConditionInterface $condition */
153 3
        foreach ($rule as $condition) {
154 3
            $addMediaConditionAlternatives = CssMediaConditionFactory::createFromMediaCondition($condition);
155
156
            // If there are already registered media conditions: Add and multiply
157 3
            if (count($alternativeMediaConditions)) {
158
                $alternativeMediaConditions = $this->addAndMultiplyCssMediaConditions(
159
                    $alternativeMediaConditions,
160
                    $addMediaConditionAlternatives
161
                );
162
                continue;
163
            }
164
165
            // Else: Add and wrap with logical and media conditions
166 3
            $alternativeMediaConditions = $this->initializeCssMediaConditions(
167 3
                $alternativeMediaConditions,
168 3
                $addMediaConditionAlternatives
169
            );
170
        }
171
172 3
        return strval($alternativeMediaConditions);
173
    }
174
175
    /**
176
     * Add and multiply media condition alternatives
177
     *
178
     * @param CssMediaConditionAlternatives $alternativeMediaConditions       Base set of alternative media conditions
179
     * @param RenderableMediaConditionInterface[] $mediaConditionAlternatives Media condition alternatives to add
180
     *
181
     * @return CssMediaConditionAlternatives Multiplied set of media condition alternatives
182
     */
183
    protected function addAndMultiplyCssMediaConditions(
184
        CssMediaConditionAlternatives $alternativeMediaConditions,
185
        array $mediaConditionAlternatives
186
    ): CssMediaConditionAlternatives {
187
        $multipliedAlternativeMediaConditions = new CssMediaConditionAlternatives();
188
189
        // Run through all generated media condition alternatives
190
        /** @var RenderableMediaConditionInterface $mediaConditionAlternative */
191
        foreach ($mediaConditionAlternatives as $mediaConditionAlternative) {
192
            // Run through all registered media condition alternatives
193
            /** @var LogicalCssMediaConditionInterface $registeredMediaConditionAlternative */
194
            foreach ($alternativeMediaConditions as $registeredMediaConditionAlternative) {
195
                $clonedMediaConditionAlternative = clone $registeredMediaConditionAlternative;
196
                $clonedMediaConditionAlternative->appendCondition($mediaConditionAlternative);
197
                $multipliedAlternativeMediaConditions->appendCondition($clonedMediaConditionAlternative);
198
            }
199
        }
200
201
        return $multipliedAlternativeMediaConditions;
202
    }
203
204
    /**
205
     * Initialize media condition alternatives
206
     *
207
     * @param CssMediaConditionAlternatives $alternativeMediaConditions       Base set of alternative media conditions
208
     * @param RenderableMediaConditionInterface[] $mediaConditionAlternatives Media condition alternatives to add
209
     *
210
     * @return CssMediaConditionAlternatives Multiplied set of media condition alternatives
211
     */
212 3
    protected function initializeCssMediaConditions(
213
        CssMediaConditionAlternatives $alternativeMediaConditions,
214
        array $mediaConditionAlternatives
215
    ): CssMediaConditionAlternatives {
216
        // Run through all generated media condition alternatives
217
        /** @var RenderableMediaConditionInterface $mediaConditionAlternative */
218 3
        foreach ($mediaConditionAlternatives as $mediaConditionAlternative) {
219 3
            $alternativeMediaConditions->appendCondition(new LogicalAndCssMediaCondition([$mediaConditionAlternative]));
220
        }
221
222 3
        return $alternativeMediaConditions;
223
    }
224
225
    /**
226
     * Export a CSS rule as declaration block (without media query)
227
     *
228
     * @param CssRuleInterface $rule CSS rule
229
     * @param string $selector       CSS selector
230
     *
231
     * @return DeclarationBlock Declaration block
232
     */
233 3
    protected function exportCssRuleDeclarationBlock(CssRuleInterface $rule, string $selector): DeclarationBlock
234
    {
235 3
        $declarationBlock = new DeclarationBlock();
236 3
        $declarationBlock->setSelectors([$selector]);
237 3
        $declarationBlock->addRule($this->exportCssRuleRule($rule));
238
239 3
        return $declarationBlock;
240
    }
241
242
    /**
243
     * Export the CSS rule property and value
244
     *
245
     * @param CssRuleInterface $rule CSS rule
246
     *
247
     * @return Rule Export CSS rule
248
     */
249 3
    protected function exportCssRuleRule(CssRuleInterface $rule): Rule
250
    {
251 3
        $imageCandidateFile = $rule->getImageCandidate()->getFile();
252 3
        $cssRule            = new Rule('background-image');
253 3
        $cssRule->setValue(new URL(new CSSString($imageCandidateFile)));
254
255 3
        return $cssRule;
256
    }
257
}