HeadScript   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 70
dl 0
loc 189
rs 10
c 0
b 0
f 0
wmc 25

7 Methods

Rating   Name   Duplication   Size   Complexity  
B processItems() 0 34 9
A generateScripts() 0 13 4
A isUseCdata() 0 11 2
A toString() 0 26 3
A __construct() 0 8 1
A generateMinifiedFilePath() 0 8 3
A minifyFile() 0 18 3
1
<?php
2
3
namespace Halfpastfour\HeadMinifier\View\Helper;
4
5
use MatthiasMullie\Minify;
6
use Zend\View\Renderer\PhpRenderer;
7
8
/**
9
 * Class HeadScript
10
 *
11
 * @package Halfpastfour\HeadMinifier\View\Helper
12
 *
13
 * Proxies to container methods:
14
 * @method string|int getWhitespace(string | int $indent)
15
 * @method string|int getIndent()
16
 * @method string getSeparator()
17
 */
18
class HeadScript extends \Zend\View\Helper\HeadScript
19
{
20
    /**
21
     * @var array
22
     */
23
    private $options = [];
24
25
    /**
26
     * @var string
27
     */
28
    private $publicDir = '';
29
30
    /**
31
     * @var string
32
     */
33
    private $cacheDir = '';
34
35
    /**
36
     * @var string
37
     */
38
    private $baseUrl = '';
39
40
    /**
41
     * HeadLink constructor.
42
     *
43
     * @param array  $config
44
     * @param string $baseUrl
45
     */
46
    public function __construct(array $config, string $baseUrl)
47
    {
48
        $this->options   = $config;
49
        $this->publicDir = $this->options['directories']['public'];
50
        $this->cacheDir  = $this->options['directories']['cache'];
51
        $this->baseUrl   = $baseUrl;
52
53
        parent::__construct();
54
    }
55
56
    /**
57
     * @param string $indent
58
     *
59
     * @return string
60
     */
61
    public function toString($indent = null): string
62
    {
63
        // If configuration tells us minifying is not enabled, use the default view helper.
64
        if (! $this->options['enabled']) {
65
            return parent::toString($indent);
66
        }
67
68
        $cacheItems = [];
69
        // Process all items. The items that don't require any changes will be returned in $items. The items that will
70
        // be cached will be returned in $cacheItems.
71
        $items = $this->processItems($cacheItems);
72
73
        $indent = (null !== $indent)
74
            ? $this->getWhitespace($indent)
75
            : $this->getIndent();
76
77
        $identifier   = sha1(implode($cacheItems));
78
        $minifiedFile = "/{$identifier}.min.js";
79
80
        // Create a minified file containing all cache items. Return the name of the minified file as the last item in
81
        // returned in $items.
82
        $scripts = $this->minifyFile($minifiedFile, $cacheItems, $items)
83
            // Generate the script tags.
84
                        ->generateScripts($items, $indent);
85
86
        return $indent . implode($this->escape($this->getSeparator()) . $indent, $scripts);
87
    }
88
89
    /**
90
     * @param array $cacheItems
91
     *
92
     * @return array
93
     */
94
    private function processItems(array &$cacheItems): array
95
    {
96
        $items = [];
97
        foreach ($this as $index => $item) {
98
            if ($item->type !== 'text/javascript' && (! @$item->attributes['src'] || ! $item->source)) {
99
                $items[] = $item;
100
                continue;
101
            }
102
            $localUri = str_replace(
103
                $this->baseUrl,
104
                '',
105
                preg_replace('/\?.*/', '', $this->publicDir . @$item->attributes['src'])
106
            );
107
108
            $remoteUri = @$item->attributes['src'];
109
            $handle    = curl_init($remoteUri);
110
            curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
111
112
            if (is_file($localUri)) {
113
                $cacheItems[$index] = $localUri;
114
                continue;
115
            }
116
117
            if (curl_exec($handle) !== false) {
118
                $cacheItems[$index] = $remoteUri;
119
                continue;
120
            }
121
122
            if ($remoteUri || $item->source) {
123
                $items[$index] = $item;
124
            }
125
        }
126
127
        return $items;
128
    }
129
130
    /**
131
     * @param string $minifiedFileName
132
     *
133
     * @return string
134
     */
135
    private function generateMinifiedFilePath(string $minifiedFileName): string
136
    {
137
        $minifiedFilePath = $this->cacheDir . $minifiedFileName;
138
        if (strpos($minifiedFilePath, $this->publicDir) === 0) {
139
            $minifiedFilePath = substr($minifiedFilePath, strlen($this->publicDir)) ?: $minifiedFilePath;
140
        }
141
142
        return $this->baseUrl . $minifiedFilePath;
143
    }
144
145
    /**
146
     * @param string $minifiedFileName
147
     * @param array  $cacheItems
148
     * @param array  $items
149
     *
150
     * @return $this
151
     */
152
    private function minifyFile(string $minifiedFileName, array $cacheItems, array &$items): HeadScript
153
    {
154
        if (! empty($cacheItems)) {
155
            if (! is_file($this->cacheDir . $minifiedFileName)) {
156
                $minifier = new Minify\JS();
157
                array_map(function ($uri) use ($minifier) {
158
                    $minifier->add($uri);
159
                }, $cacheItems);
160
                $minifier->minify($this->cacheDir . $minifiedFileName);
161
            }
162
163
            // Add the minified file to the list of items.
164
            $items[] = $this->createData('text/javascript', [
165
                'src' => $this->generateMinifiedFilePath($minifiedFileName),
166
            ]);
167
        }
168
169
        return $this;
170
    }
171
172
    /**
173
     * @return bool
174
     */
175
    private function isUseCdata(): bool
176
    {
177
        $view     = $this->view;
178
        $useCdata = $this->useCdata;
179
        if ($view instanceof PhpRenderer) {
180
            /** @var \Zend\View\Helper\Doctype $plugin */
181
            $plugin   = $view->plugin('doctype');
182
            $useCdata = $plugin->isXhtml();
183
        }
184
185
        return $useCdata;
186
    }
187
188
    /**
189
     * @param array  $items
190
     * @param string $indent
191
     *
192
     * @return array
193
     */
194
    private function generateScripts(array $items, $indent): array
195
    {
196
        $useCdata    = $this->isUseCdata();
197
        $escapeStart = ($useCdata) ? '//<![CDATA[' : '//<!--';
198
        $escapeEnd   = ($useCdata) ? '//]]>' : '//-->';
199
200
        $scripts = [];
201
202
        foreach ($items as $item) {
203
            $scripts[] = $this->itemToString($item, $indent, $escapeStart, $escapeEnd);
204
        }
205
206
        return $scripts;
207
    }
208
}
209