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

HeadLink::generateMinifiedFilePath()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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