Completed
Pull Request — master (#7)
by halfpastfour
02:11
created

HeadScript   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 188
rs 10
c 0
b 0
f 0
wmc 25

7 Methods

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