Passed
Push — master ( 24c63b...09b8ef )
by Joschi
01:57
created

addAndMultiplyCssMediaConditions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 19
ccs 0
cts 8
cp 0
crap 12
rs 9.4285
c 0
b 0
f 0
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;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Jkphl\Respimgcss\Infrast...MediaConditionInterface. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
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 1
    public function __construct(array $rules)
72
    {
73 1
        $this->rules = $rules;
74 1
    }
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 1
    public function toCss(string $selector): string
85
    {
86
        // If the CSS selector is invalid
87 1
        $selector = trim($selector);
88 1
        if (!strlen($selector)) {
89
            throw new InvalidArgumentException(
90
                sprintf(InvalidArgumentException::INVALID_CSS_SELECTOR_STR, $selector),
91
                InvalidArgumentException::INVALID_CSS_SELECTOR
92
            );
93
        }
94
95 1
        $cssDocument = new Document();
96
97
        /** @var CssRuleInterface $rule */
98 1
        foreach ($this->rules as $rule) {
99 1
            $cssDocument->append($this->exportCssRule($rule, $selector));
100
        }
101
102 1
        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 1
    protected function exportCssRule(CssRuleInterface $rule, string $selector): Renderable
114
    {
115
        // If the rule has conditions: Render as an @-rule block
116 1
        if (count($rule)) {
117 1
            return $this->exportCssRuleAtBlock($rule, $selector);
118
        }
119
120 1
        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 1
    protected function exportCssRuleAtBlock(CssRuleInterface $rule, string $selector): AtRuleBlockList
132
    {
133 1
        $atRuleMediaConditions = $this->exportCssRuleMediaConditions($rule);
134 1
        $atRuleBlockList       = new AtRuleBlockList('media', $atRuleMediaConditions);
135 1
        $atRuleBlockList->append($this->exportCssRuleDeclarationBlock($rule, $selector));
136
137 1
        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 1
    protected function exportCssRuleMediaConditions(CssRuleInterface $rule): string
148
    {
149 1
        $alternativeMediaConditions = new CssMediaConditionAlternatives();
150
151
        // Run through all conditions of this rule
152
        /** @var CssMediaConditionInterface $condition */
153 1
        foreach ($rule as $condition) {
154 1
            $addMediaConditionAlternatives = CssMediaConditionFactory::createFromMediaCondition($condition);
155
156
            // If there are already registered media conditions: Add and multiply
157 1
            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 1
            $alternativeMediaConditions = $this->initializeCssMediaConditions(
167 1
                $alternativeMediaConditions,
168 1
                $addMediaConditionAlternatives
169
            );
170
        }
171
172 1
        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 1
    protected function initializeCssMediaConditions(
213
        CssMediaConditionAlternatives $alternativeMediaConditions,
214
        array $mediaConditionAlternatives
215
    ): CssMediaConditionAlternatives {
216
        // Run through all generated media condition alternatives
217
        /** @var RenderableMediaConditionInterface $mediaConditionAlternative */
218 1
        foreach ($mediaConditionAlternatives as $mediaConditionAlternative) {
219 1
            $alternativeMediaConditions->appendCondition(new LogicalAndCssMediaCondition([$mediaConditionAlternative]));
220
        }
221
222 1
        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
    protected
0 ignored issues
show
Coding Style introduced by
Scope keyword "protected" must be followed by a single space
Loading history...
234 1
    function exportCssRuleDeclarationBlock(
0 ignored issues
show
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for exportCssRuleDeclarationBlock.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
235
        CssRuleInterface $rule,
236
        string $selector
237
    ): DeclarationBlock {
238 1
        $declarationBlock = new DeclarationBlock();
239 1
        $declarationBlock->setSelectors([$selector]);
240 1
        $declarationBlock->addRule($this->exportCssRuleRule($rule));
241
242 1
        return $declarationBlock;
243
    }
244
245
    /**
246
     * Export the CSS rule property and value
247
     *
248
     * @param CssRuleInterface $rule CSS rule
249
     *
250
     * @return Rule Export CSS rule
251
     */
252
    protected
0 ignored issues
show
Coding Style introduced by
Scope keyword "protected" must be followed by a single space
Loading history...
253 1
    function exportCssRuleRule(
0 ignored issues
show
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for exportCssRuleRule.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
254
        CssRuleInterface $rule
255
    ): Rule {
256 1
        $imageCandidateFile = $rule->getImageCandidate()->getFile();
257 1
        $cssRule            = new Rule('background-image');
258 1
        $cssRule->setValue(new URL(new CSSString($imageCandidateFile)));
259
260 1
        return $cssRule;
261
    }
262
}