Completed
Push — master ( 174ea9...d0877c )
by Vitaly
05:06
created

ResourceManager::manage()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 15
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 28
ccs 17
cts 17
cp 1
crap 3
rs 8.8571
1
<?php
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 15.05.16 at 10:52
5
 */
6
namespace samsonphp\resource;
7
8
use samsonphp\event\Event;
9
10
/**
11
 * Resource assets management class.
12
 *
13
 * @package samsonphp\resource
14
 */
15
class ResourceManager
16
{
17
    /** Event for resources analyzing */
18
    const E_ANALYZE = 'resource.analyze';
19
    /** Event for resources compiling */
20
    const E_COMPILE = 'resource.compile';
21
22
    /** Assets types */
23
    const T_CSS = 'css';
24
    const T_LESS = 'less';
25
    const T_SCSS = 'scss';
26
    const T_SASS = 'sass';
27
    const T_JS = 'js';
28
    const T_TS = 'ts';
29
    const T_COFFEE = 'coffee';
30
    const T_JPG = 'jpg';
31
    const T_JPEG = 'jpeg';
32
    const T_PNG = 'png';
33
    const T_GIF = 'gif';
34
    const T_SVG = 'svg';
35
    const T_TTF = 'ttf';
36
    const T_WOFF = 'woff';
37
    const T_WOFF2 = 'woff2';
38
    const T_EOT = 'eot';
39
40
    /** Assets types collection */
41
    const TYPES = [
42
        self::T_CSS,
43
        self::T_LESS,
44
        self::T_SCSS,
45
        self::T_SASS,
46
        self::T_JS,
47
        self::T_TS,
48
        self::T_COFFEE,
49
        /*self::T_JPG,
50
        self::T_JPEG,
51
        self::T_PNG,
52
        self::T_GIF,
53
        self::T_SVG,
54
        self::T_TTF,
55
        self::T_WOFF,
56
        self::T_WOFF2,
57
        self::T_EOT,*/
58
    ];
59
60
    /** Assets converter */
61
    const CONVERTER = [
62
        self::T_JS => self::T_JS,
63
        self::T_TS => self::T_JS,
64
        self::T_COFFEE => self::T_JS,
65
        self::T_CSS => self::T_CSS,
66
        self::T_LESS => self::T_CSS,
67
        self::T_SCSS => self::T_CSS,
68
        self::T_SASS => self::T_CSS,
69
    ];
70
71
    /** Collection of excluding scanning folder patterns */
72
    public static $excludeFolders = [
73
        '*/cache/*',
74
        '*/tests/*',
75
        '*/vendor/*/vendor/*'
76
    ];
77
78
    /** @var string Full path to project web root directory */
79
    public static $webRoot;
80
81
    /** @var string Full path to project root directory */
82
    public static $projectRoot;
83
84
    /** @var string Full path to project cache root directory */
85
    public static $cacheRoot;
86
87
    /** @var array Cached assets */
88
    protected $cache = [];
89
90
    /** @var array Collection of assets */
91
    protected $assets = [];
92
93
    /**
94
     * Resource constructor.
95
     *
96
     * @param FileManagerInterface $fileManager File managing class
97
     */
98 1
    public function __construct(FileManagerInterface $fileManager)
99
    {
100 1
        $this->fileManager = $fileManager;
0 ignored issues
show
Bug introduced by
The property fileManager does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
101 1
    }
102
103
    /**
104
     * Create static assets.
105
     *
106
     * @param array $paths Collection of paths for gathering assets
107
     *
108
     * @return array Cached assets full paths collection
109
     */
110 1
    public function manage(array $paths)
111
    {
112 1
        $assets = $this->fileManager->scan($paths, self::TYPES, self::$excludeFolders);
113
114
        // Iterate all assets for analyzing
115 1
        $cache = [];
116 1
        foreach ($assets as $asset) {
117 1
            $cache[$asset] = $this->analyzeAsset($asset);
118 1
        }
119 1
        $cache = array_filter($cache);
120
121
        // Iterate invalid assets
122 1
        foreach ($cache as $file => $content) {
123 1
            $extension = pathinfo($file, PATHINFO_EXTENSION);
124
125
            // Compile content
126 1
            $compiled = $content;
127 1
            Event::fire(self::E_COMPILE, [$file, &$extension, &$compiled]);
128
129 1
            $asset = $this->getAssetCachedPath($file);
130 1
            $this->fileManager->write($asset, $compiled);
131 1
            $this->fileManager->touch($asset, $this->fileManager->lastModified($file));
132
133 1
            $this->assets[$extension][] = $asset;
134 1
        }
135
136 1
        return $this->assets;
137
    }
138
139
    /**
140
     * Analyze asset.
141
     *
142
     * @param string $asset Full path to asset
143
     *
144
     * @return string Analyzed asset content
145
     */
146 1
    protected function analyzeAsset($asset)
147
    {
148
        // Generate cached resource path with possible new extension after compiling
149 1
        $cachedAsset = $this->getAssetCachedPath($asset);
150
151 1
        $extension = pathinfo($asset, PATHINFO_EXTENSION);
152
153
        // If cached assets was modified or new
154 1
        if (!$this->isValid($asset, $cachedAsset)) {
155
            // Read asset content
156 1
            $content = $this->fileManager->read($asset);
157
158
            // Fire event for analyzing resource
159 1
            Event::fire(self::E_ANALYZE, [
160 1
                $asset,
161 1
                $extension,
162
                &$content
163 1
            ]);
164
165 1
            return $content;
166
        } else {
167
            // Add this resource to resource collection grouped by resource type
168 1
            $this->assets[$this->convertType($asset)][] = $cachedAsset;
169
        }
170
171 1
        return '';
172
    }
173
174
    /**
175
     * Get asset cached path with extension conversion.
176
     *
177
     * @param string $asset Asset full path
178
     *
179
     * @return string Full path to cached asset
180
     */
181 1
    protected function getAssetCachedPath($asset)
182
    {
183
        // Build asset project root relative path
184 1
        $relativePath = str_replace(self::$projectRoot, '', $asset);
185
186
        // Build full cached asset path
187 1
        return dirname(self::$cacheRoot . $relativePath) . '/'
188 1
        . pathinfo($asset, PATHINFO_FILENAME) . '.' . $this->convertType($asset);
189
    }
190
191
    /**
192
     * Get asset final type.
193
     *
194
     * @param string $asset Full asset path
195
     *
196
     * @return string Asset final type
197
     */
198 1
    public function convertType($asset)
199
    {
200
        // Convert input extension
201 1
        $extension = pathinfo($asset, PATHINFO_EXTENSION);
202
203 1
        return array_key_exists($extension, self::CONVERTER)
204 1
            ? self::CONVERTER[$extension]
205 1
            : $extension;
206
    }
207
208
    /**
209
     * Define if asset is not valid.
210
     *
211
     * @param string $asset       Full path to asset
212
     *
213
     * @param string $cachedAsset Full path to cached asset
214
     *
215
     * @return bool True if cached asset is valid
216
     */
217 1
    protected function isValid($asset, $cachedAsset)
218
    {
219
        // If cached asset does not exists or is invalid
220 1
        return $this->fileManager->exists($cachedAsset) !== false
221 1
        && $this->fileManager->lastModified($cachedAsset) === $this->fileManager->lastModified($asset);
222
    }
223
}
224