GoogleFontsOptimizerUtils   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 279
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 279
ccs 90
cts 90
cp 1
rs 10
c 0
b 0
f 0
wmc 26

11 Methods

Rating   Name   Duplication   Size   Complexity  
A buildFontsMarkupLinks() 0 12 2
A isGoogleWebFontUrl() 0 3 1
B buildFontsMarkupScript() 0 42 4
A dedupValues() 0 21 4
A hasHtmlTag() 0 3 1
A hasHtml5Doctype() 0 3 1
A httpsify() 0 12 3
A hasXslStylesheet() 0 3 1
A encodeUnencodedAmpersands() 0 9 2
B buildGoogleFontsUrl() 0 25 4
A arrayFlattenIterative() 0 15 3
1
<?php
2
3
namespace ZWF;
4
5
use ZWF\GoogleFontsCollection as Collection;
6
7
class GoogleFontsOptimizerUtils
8
{
9
    /**
10
     * Replaces any occurrences of un-encoded ampersands in the given string
11
     * with the value given in the `$amp` parameter (`&amp;` by default).
12
     *
13
     * @param string $url
14
     * @param string $amp
15
     *
16
     * @return string
17
     */
18 7
    public static function encodeUnencodedAmpersands($url, $amp = '&amp;')
19 7
    {
20 7
        $amp = trim($amp);
21
22 7
        if (empty($amp)) {
23 3
            $amp = '&amp;';
24
        }
25
26 7
        return preg_replace('/&(?!#?\w+;)/', $amp, $url);
27
    }
28
29
    /**
30
     * Replaces protocol-relative or non-https URLs into https URLs.
31
     *
32
     * @param string $link
33
     *
34
     * @return string
35
     */
36 6
    public static function httpsify($link)
37 6
    {
38 6
        $is_protocol_relative = ('/' === $link{0} && '/' === $link{1});
39
40 6
        if ($is_protocol_relative) {
41 1
            $link = 'https:' . $link;
42
        } else {
43 5
            $link = preg_replace('/^(https?):\/\//mi', '', $link);
44 5
            $link = 'https://' . $link;
45
        }
46
47 6
        return $link;
48
    }
49
50
    /**
51
     * Returns true if a given `$url` is a Google Fonts URL.
52
     *
53
     * @param string $url
54
     *
55
     * @return bool
56
     */
57 12
    public static function isGoogleWebFontUrl($url)
58 12
    {
59 12
        return (substr_count($url, 'fonts.googleapis.com/css') > 0);
60
    }
61
62
    /**
63
     * Returns true if given `$string` contains the HTML5 doctype.
64
     *
65
     * @param string $string
66
     *
67
     * @return bool
68
     */
69 2
    public static function hasHtml5Doctype($string)
70 2
    {
71 2
        return (preg_match('/^<!DOCTYPE.+html>/i', $string) > 0);
72
    }
73
74
    /**
75
     * Returns true when given `$string` contains an XSL stylesheet element.
76
     *
77
     * @param string $string
78
     *
79
     * @return bool
80
     */
81 2
    public static function hasXslStylesheet($string)
82 2
    {
83 2
        return (false !== stripos($string, '<xsl:stylesheet'));
84
    }
85
86
    /**
87
     * Returns true when given `$string` contains the beginnings of an `<html>` tag.
88
     *
89
     * @param string $string
90
     * @return bool
91
     */
92 2
    public static function hasHtmlTag($string)
93 2
    {
94 2
        return (false !== stripos($string, '<html'));
95
    }
96
97
    /**
98
     * Given a `['key' => 'value1,value2,value3']` or a
99
     * `['key' => ['value1', 'value2', 'value3']]` map/array it returns a new
100
     * array with the same keys, but the values are now always an array of
101
     * values (and any potential duplicate values are removed).
102
     * If the `$sort` parameter is given, the list of values is sorted using
103
     * `sort()` and the `$sort` parameter is treated as a sort flag.
104
     *
105
     * @param array $data
106
     * @param bool|int $sort If false, no sorting, otherwise an integer representing
107
     *                       sort flags. See http://php.net/sort
108
     *
109
     * @return array
110
     */
111 9
    public static function dedupValues(array $data, $sort = false)
112 9
    {
113 9
        foreach ($data as $key => $values) {
114 9
            if (is_array($values)) {
115 8
                $parts = $values;
116
            } else {
117 9
                $parts = explode(',', $values);
118
            }
119
120 9
            $parts = array_unique($parts);
121
122
            // Perform sort if specified
123 9
            if (false !== $sort) {
124 6
                sort($parts, (int) $sort);
125
            }
126
127
            // Store back
128 9
            $data[$key] = $parts;
129
        }
130
131 9
        return $data;
132
    }
133
134
    /**
135
     * Given a `GoogleFontsCollection` it builds needed `<link rel="stylesheet">` markup.
136
     *
137
     * @param Collection $fonts
138
     *
139
     * @return string
140
     */
141 2
    public static function buildFontsMarkupLinks(Collection $fonts)
142 2
    {
143 2
        $font_url = $fonts->getCombinedUrl();
144 2
        $href     = self::encodeUnencodedAmpersands($font_url);
145 2
        $markup   = '<link rel="stylesheet" type="text/css" href="' . $href . '">';
146
147 2
        foreach ($fonts->getTextUrls() as $url) {
148 1
            $markup .= '<link rel="stylesheet" type="text/css"';
149 1
            $markup .= ' href="' . self::encodeUnencodedAmpersands($url) . '">';
150
        }
151
152 2
        return $markup;
153
    }
154
155
    /**
156
     * Given a `GoogleFontsCollection` it builds the WebFont loader script markup.
157
     *
158
     * @param Collection $collection
159
     *
160
     * @return string
161
     */
162 2
    public static function buildFontsMarkupScript(Collection $collection)
163 2
    {
164 2
        $families_array = [];
165
166 2
        list($names, $mapping) = $collection->getScriptData();
167
168 2
        foreach ($names as $name => $sizes) {
169 2
            $family = $name . ':' . implode(',', $sizes);
170 2
            if (isset($mapping[$name])) {
171 2
                $family .= ':' . implode(',', $mapping[$name]);
172
            }
173 2
            $families_array[] = $family;
174
        }
175 2
        $families = "'" . implode("', '", $families_array) . "'";
176
177
        // Load 'text' requests with the "custom" module
178 2
        $custom = '';
179 2
        if ($collection->hasText()) {
180 1
            $custom  = ",\n    custom: {\n";
181 1
            $custom .= "        families: [ '" . implode("', '", $collection->getTextNames()) . "' ],\n";
182 1
            $custom .= "        urls: [ '" . implode("', '", $collection->getTextUrls()) . "' ]\n";
183 1
            $custom .= '    }';
184
        }
185
186
        $markup = <<<MARKUP
187
<script type="text/javascript">
188
WebFontConfig = {
189 2
    google: { families: [ {$families} ] }{$custom}
190
};
191
(function() {
192
    var wf = document.createElement('script');
193
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
194
        '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
195
    wf.type = 'text/javascript';
196
    wf.async = 'true';
197
    var s = document.getElementsByTagName('script')[0];
198
    s.parentNode.insertBefore(wf, s);
199
})();
200
</script>
201
MARKUP;
202
203 2
        return $markup;
204
    }
205
206
    /**
207
     * Builds a combined Google Font URL for multiple font families/subsets.
208
     *
209
     * Usage examples:
210
     * ```
211
     * ZWF\GoogleFontsOptimizerUtils::buildGoogleFontsUrl(
212
     *     [
213
     *         'Open Sans' => [ '400', '400italic', '700', '700italic' ],
214
     *         'Ubuntu'    => [ '400', '400italic', '700', '700italic' ],
215
     *     ),
216
     *     [ 'latin', 'latin-ext' ]
217
     * );
218
     * ```
219
     *
220
     * or
221
     *
222
     * ```
223
     * ZWF\GoogleFontsOptimizerUtils::buildGoogleFontsUrl(
224
     *     [
225
     *         'Open Sans' => '400,400italic,700,700italic',
226
     *         'Ubuntu'    => '400,400italic,700,700italic',
227
     *     ],
228
     *     'latin,latin-ext'
229
     * );
230
     * ```
231
     *
232
     * @param array $fonts
233
     * @param array|string $subsets
234
     *
235
     * @return null|string
236
     */
237 6
    public static function buildGoogleFontsUrl(array $fonts, $subsets = [])
238 6
    {
239 6
        $base_url  = 'https://fonts.googleapis.com/css';
240 6
        $font_args = [];
241 6
        $family    = [];
242
243 6
        $fonts = self::dedupValues($fonts, false); // no sort
244 6
        foreach ($fonts as $font_name => $font_weight) {
245
            // Trimming end colon handles edge case of being given an empty $font_weight
246 6
            $family[] = trim(trim($font_name) . ':' . implode(',', $font_weight), ':');
247
        }
248
249 6
        $font_args['family'] = implode('|', $family);
250
251 6
        if (! empty($subsets)) {
252 5
            if (is_array($subsets)) {
253 4
                $subsets = array_unique($subsets);
254 4
                $subsets = implode(',', $subsets);
255
            }
256 5
            $font_args['subset'] = trim($subsets);
257
        }
258
259 6
        $url = $base_url . '?' . http_build_query($font_args);
260
261 6
        return $url;
262
    }
263
264
    /**
265
     * Flatten an array without recursion.
266
     *
267
     * @param array $arr
268
     *
269
     * @return array
270
     */
271 3
    public static function arrayFlattenIterative(array $arr)
272 3
    {
273 3
        $flat  = [];
274 3
        $stack = array_values($arr);
275
276 3
        while (! empty($stack)) {
277 3
            $value = array_shift($stack);
278 3
            if (is_array($value)) {
279 3
                $stack = array_merge(array_values($value), $stack);
280
            } else {
281 2
                $flat[] = $value;
282
            }
283
        }
284
285 3
        return $flat;
286
    }
287
}
288