Completed
Push — master ( b86345...d632d5 )
by Maxim
02:34
created

AssetManager::applyJs()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 5
nop 0
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace WebComplete\mvc\assets;
4
5
use Symfony\Component\Filesystem\Filesystem;
6
use WebComplete\mvc\exception\Exception;
7
8
class AssetManager
9
{
10
11
    const PRODUCTION_JS = 'asset.min.js';
12
    const PRODUCTION_CSS = 'asset.min.css';
13
14
    /**
15
     * @var AbstractAsset[]
16
     */
17
    protected $assets = [];
18
    /**
19
     * @var string
20
     */
21
    protected $webRoot;
22
    /**
23
     * @var string
24
     */
25
    protected $assetDirName;
26
    /**
27
     * @var Filesystem
28
     */
29
    protected $filesystem;
30
    protected $isProduction = false;
31
32
    /**
33
     * @param Filesystem $filesystem
34
     * @param string $webRoot
35
     * @param string $assetDirName
36
     * @param bool $isProduction
37
     */
38
    public function __construct(
39
        Filesystem $filesystem,
40
        string $webRoot,
41
        string $assetDirName,
42
        bool $isProduction = false
43
    ) {
44
        $this->filesystem = $filesystem;
45
        $this->webRoot = \rtrim($webRoot, '/');
46
        $this->assetDirName = $assetDirName;
47
        $this->isProduction = $isProduction;
48
    }
49
50
    /**
51
     * @param AbstractAsset $asset
52
     *
53
     * @throws \Symfony\Component\Filesystem\Exception\IOException
54
     */
55
    public function registerAsset(AbstractAsset $asset)
56
    {
57
        $assetClass = \get_class($asset);
58
        if (!isset($this->assets[$assetClass])) {
59
            foreach ($asset->getAssetsBefore() as $assetBefore) {
60
                $this->registerAsset($assetBefore);
61
            }
62
63
            if ($asset->publish) {
64
                $this->publishAsset($asset);
65
            }
66
            $this->assets[$assetClass] = $asset;
67
            foreach ($asset->getAssetsAfter() as $assetAfter) {
68
                $this->registerAsset($assetAfter);
69
            }
70
        }
71
    }
72
73
    /**
74
     * @param string $assetClass
75
     * @param $file
76
     *
77
     * @param bool $absolute
78
     *
79
     * @return string
80
     * @throws Exception
81
     */
82
    public function getPath(string $assetClass, $file, bool $absolute = false): string
83
    {
84
        if (!isset($this->assets[$assetClass])) {
85
            throw new Exception('Asset ' . $assetClass . ' is not registered');
86
        }
87
88
        return $absolute
89
            ? $this->webRoot . $this->getWebDir($this->assets[$assetClass]) . \ltrim($file, '/')
90
            : $this->getWebDir($this->assets[$assetClass]) . \ltrim($file, '/');
91
    }
92
93
    /**
94
     * @return string
95
     */
96
    public function applyCss(): string
97
    {
98
        $result = [];
99
        foreach ($this->assets as $asset) {
100
            $links = $this->getLinks($asset, $asset->css());
101
            foreach ($links as $link) {
102
                $result[] = '<link rel="stylesheet" href="' . $link . '">';
103
            }
104
        }
105
        return \implode('', $result);
106
    }
107
108
    /**
109
     * @return string
110
     */
111
    public function applyJs(): string
112
    {
113
        $result = [];
114
        foreach ($this->assets as $asset) {
115
            $productionJs = $this->getWebDir($asset) . self::PRODUCTION_JS;
116
            if ($this->isProduction() && \file_exists($this->webRoot . $productionJs)) {
117
                $links = $this->getLinks($asset, $asset->externalJs());
118
                $links[] = $productionJs;
119
            } else {
120
                $links = $this->getLinks($asset, $asset->js());
121
            }
122
            foreach ($links as $link) {
123
                $result[] = '<script src="' . $link . '"></script>';
124
            }
125
        }
126
        return \implode('', $result);
127
    }
128
129
    /**
130
     * @return bool
131
     */
132
    public function isProduction(): bool
133
    {
134
        return $this->isProduction;
135
    }
136
137
    /**
138
     * @param bool $isProduction
139
     */
140
    public function setIsProduction(bool $isProduction)
141
    {
142
        $this->isProduction = $isProduction;
143
    }
144
145
    /**
146
     * @param AbstractAsset $asset
147
     *
148
     * @throws \Symfony\Component\Filesystem\Exception\IOException
149
     */
150
    protected function publishAsset(AbstractAsset $asset)
151
    {
152
        $hash = $asset->getHash();
153
        $dir = $this->webRoot . '/' . $this->assetDirName . '/' . $hash;
154
        if ($asset->useLinks) {
155
            if (!$this->filesystem->exists($dir)) {
156
                $this->filesystem->symlink($asset->getBasePath(), $dir, true);
157
            }
158
        } else {
159
            if ($this->filesystem->exists($dir)) {
160
                $this->filesystem->remove($dir);
161
            }
162
            $this->filesystem->mirror($asset->getBasePath(), $dir);
163
            $this->filesystem->chmod($dir, 0755, 000, true);
164
        }
165
    }
166
167
    /**
168
     * @param AbstractAsset $asset
169
     *
170
     * @return string
171
     */
172
    private function getWebDir(AbstractAsset $asset): string
173
    {
174
        return $asset->publish
175
            ? '/' . $this->assetDirName . '/' . $asset->getHash() . '/'
176
            : '/';
177
    }
178
179
    /**
180
     * @param AbstractAsset $asset
181
     * @param array $files
182
     *
183
     * @return array
184
     */
185
    private function getLinks(AbstractAsset $asset, array $files): array
186
    {
187
        $result = [];
188
        $dir = $this->getWebDir($asset);
189
        foreach ($files as $file) {
190
            $result[] = $this->getLink($file, $dir);
191
        }
192
        return $result;
193
    }
194
195
    /**
196
     * @param $file
197
     * @param $dir
198
     *
199
     * @return string
200
     */
201
    private function getLink($file, $dir): string
202
    {
203
        $link = $file;
204
        if (false === \strpos($file, 'http') && false === \strpos($file, '//')) {
205
            $filePath = $dir . \ltrim($file, '/');
206
            $timestamp = \filemtime($this->webRoot . $filePath);
207
            $link = $filePath . '?' . $timestamp;
0 ignored issues
show
Bug introduced by
Are you sure $timestamp of type integer|false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
            $link = $filePath . '?' . /** @scrutinizer ignore-type */ $timestamp;
Loading history...
208
        }
209
210
        return $link;
211
    }
212
}
213