ResourceManager   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 196
rs 10
c 0
b 0
f 0
ccs 35
cts 35
cp 1
wmc 11
lcom 1
cbo 2

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B processAsset() 0 24 2
A manage() 0 20 3
A getAssetProcessedPath() 0 9 1
A convertType() 0 9 2
A isValid() 0 6 2
1
<?php declare(strict_types=1);
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 15.05.16 at 10:52
5
 */
6
namespace samsonphp\resource;
7
8
use samsonframework\filemanager\FileManagerInterface;
9
use samsonphp\event\Event;
10
11
/**
12
 * Resource assets management class.
13
 *
14
 * @author Vitaly Egorov <[email protected]>
15
 */
16
class ResourceManager
17
{
18
    /** Event for resources analyzing */
19
    const E_ANALYZE = 'resource.analyze';
20
    /** Event for resources compiling */
21
    const E_COMPILE = 'resource.compile';
22
23
    /** Assets types */
24
    const T_CSS = 'css';
25
    const T_LESS = 'less';
26
    const T_SCSS = 'scss';
27
    const T_SASS = 'sass';
28
    const T_JS = 'js';
29
    const T_TS = 'ts';
30
    const T_COFFEE = 'coffee';
31
    const T_JPG = 'jpg';
32
    const T_JPEG = 'jpeg';
33
    const T_PNG = 'png';
34
    const T_GIF = 'gif';
35
    const T_SVG = 'svg';
36
    const T_TTF = 'ttf';
37
    const T_WOFF = 'woff';
38
    const T_WOFF2 = 'woff2';
39
    const T_EOT = 'eot';
40
41
    /** Assets types collection */
42
    const TYPES = [
43
        self::T_CSS,
44
        self::T_LESS,
45
        self::T_SCSS,
46
        self::T_SASS,
47
        self::T_JS,
48
        self::T_TS,
49
        self::T_COFFEE,
50
        /*self::T_JPG,
51
        self::T_JPEG,
52
        self::T_PNG,
53
        self::T_GIF,
54
        self::T_SVG,
55
        self::T_TTF,
56
        self::T_WOFF,
57
        self::T_WOFF2,
58
        self::T_EOT,*/
59
    ];
60
61
    /** Assets converter */
62
    const CONVERTER = [
63
        self::T_JS => self::T_JS,
64
        self::T_TS => self::T_JS,
65
        self::T_COFFEE => self::T_JS,
66
        self::T_CSS => self::T_CSS,
67
        self::T_LESS => self::T_CSS,
68
        self::T_SCSS => self::T_CSS,
69
        self::T_SASS => self::T_CSS,
70
    ];
71
72
    /** Collection of excluding scanning folder patterns */
73
    public static $excludeFolders = [
74
        '*/cache/*',
75
        '*/tests/*',
76
        '*/vendor/*/vendor/*',
77
        '*/node_modules/*'
78
    ];
79
80
    /** @var string Full path to project web root directory */
81
    public static $webRoot;
82
83
    /** @var string Full path to project root directory */
84
    public static $projectRoot;
85
86
    /** @var string Full path to project cache root directory */
87
    public static $cacheRoot;
88
89
    /** @var array Collection of assets */
90
    protected $assets = [];
91
92
    /** @var FileManagerInterface */
93
    protected $fileManager;
94
95
    /**
96
     * Resource constructor.
97
     *
98
     * @param FileManagerInterface $fileManager File managing class
99
     */
100 2
    public function __construct(FileManagerInterface $fileManager)
101
    {
102 2
        $this->fileManager = $fileManager;
103 2
    }
104
105
    /**
106
     * Recursively process asset
107
     * @param array $dependencies Collection of assets for compilation
108
     */
109 2
    protected function processAsset($dependencies)
110
    {
111 2
        foreach ($dependencies as $source => $nothing) {
112
            // Read asset content
113 2
            $content = $this->fileManager->read($source);
114
115 2
            $extension = pathinfo($source, PATHINFO_EXTENSION);
116
117
            // Resource dependant resources
118 2
            $innerDependencies = [];
119
120
            // Compile content
121 2
            $compiled = $content;
122 2
            Event::fire(self::E_COMPILE, [$source, &$extension, &$compiled, &$innerDependencies]);
123
124
            // Write compiled asset
125 2
            $target = $this->getAssetProcessedPath($source);
126 2
            $this->fileManager->write($target, $compiled);
127 2
            $this->fileManager->touch($target, $this->fileManager->lastModified($source));
128
129
            // Go deeper in recursion
130 2
            $this->processAsset($innerDependencies);
131
        }
132 2
    }
133
134
    /**
135
     * Create static assets.
136
     *
137
     * @param string[] $paths Collection of paths for gathering assets
138
     *
139
     * @return string[] Cached assets full paths collection
140
     */
141 2
    public function manage(array $paths)
142
    {
143 2
        $this->assets = [];
144
        // Iterate all assets for analyzing
145 2
        foreach ($this->fileManager->scan($paths, self::TYPES, self::$excludeFolders) as $asset) {
146
            // Build path to processed asset
147 2
            $cachedAsset = $this->getAssetProcessedPath($asset);
148
149
            // If cached assets was modified or new
150 2
            if (!$this->isValid($asset, $cachedAsset)) {
151
                // Recursively process asset and possible dependencies
152 2
                $this->processAsset([$asset => []]);
153
            }
154
155
            // Store processed asset
156 2
            $this->assets[pathinfo($cachedAsset, PATHINFO_EXTENSION)][] = $cachedAsset;
157
        }
158
159 2
        return $this->assets;
160
    }
161
162
    /**
163
     * Get asset cached path with extension conversion.
164
     *
165
     * @param string $asset Asset full path
166
     *
167
     * @return string Full path to cached asset
168
     */
169 2
    protected function getAssetProcessedPath($asset)
170
    {
171
        // Build asset project root relative path
172 2
        $relativePath = str_replace(self::$projectRoot, '', $asset);
173
174
        // Build full cached asset path
175 2
        return dirname(self::$cacheRoot . $relativePath) . '/'
176 2
        . pathinfo($asset, PATHINFO_FILENAME) . '.' . $this->convertType($asset);
177
    }
178
179
    /**
180
     * Get asset final type.
181
     *
182
     * @param string $asset Full asset path
183
     *
184
     * @return string Asset final type
185
     */
186 2
    public function convertType($asset)
187
    {
188
        // Convert input extension
189 2
        $extension = pathinfo($asset, PATHINFO_EXTENSION);
190
191 2
        return array_key_exists($extension, self::CONVERTER)
192 2
            ? self::CONVERTER[$extension]
193 2
            : $extension;
194
    }
195
196
    /**
197
     * Define if asset is not valid.
198
     *
199
     * @param string $asset       Full path to asset
200
     *
201
     * @param string $cachedAsset Full path to cached asset
202
     *
203
     * @return bool True if cached asset is valid
204
     */
205 2
    protected function isValid($asset, $cachedAsset)
206
    {
207
        // If cached asset does not exists or is invalid
208 2
        return $this->fileManager->exists($cachedAsset) !== false
209 2
        && $this->fileManager->lastModified($cachedAsset) === $this->fileManager->lastModified($asset);
210
    }
211
}
212