Completed
Push — master ( 30530b...953066 )
by Vitaly
34:35
created

ResourceManager   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 199
ccs 38
cts 38
cp 1
rs 10
c 0
b 0
f 0
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 21 3
A getAssetProcessedPath() 0 9 1
A convertType() 0 9 2
A isValid() 0 6 2
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 samsonframework\filemanager\FileManagerInterface;
9
use samsonphp\event\Event;
10
11
/**
12
 * Resource assets management class.
13
 *
14
 * @package samsonphp\resource
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
    ];
78
79
    /** @var string Full path to project web root directory */
80
    public static $webRoot;
81
82
    /** @var string Full path to project root directory */
83
    public static $projectRoot;
84
85
    /** @var string Full path to project cache root directory */
86
    public static $cacheRoot;
87
88
    /** @var array Cached assets */
89
    protected $cache = [];
90
91
    /** @var array Collection of assets */
92
    protected $assets = [];
93
94
    /** @var FileManagerInterface */
95
    protected $fileManager;
96
97
    /**
98
     * Resource constructor.
99
     *
100
     * @param FileManagerInterface $fileManager File managing class
101
     */
102 2
    public function __construct(FileManagerInterface $fileManager)
103
    {
104 2
        $this->fileManager = $fileManager;
105 2
    }
106
107
    /**
108
     * Recursively process asset
109
     * @param array $dependencies Collection of assets for compilation
110
     */
111 2
    protected function processAsset($dependencies)
112
    {
113 2
        foreach ($dependencies as $source => $nothing) {
114
            // Read asset content
115 2
            $content = $this->fileManager->read($source);
116
117 2
            $extension = pathinfo($source, PATHINFO_EXTENSION);
118
119
            // Resource dependant resources
120 2
            $innerDependencies = [];
121
122
            // Compile content
123 2
            $compiled = $content;
124 2
            Event::fire(self::E_COMPILE, [$source, &$extension, &$compiled, &$innerDependencies]);
125
126
            // Write compiled asset
127 2
            $target = $this->getAssetProcessedPath($source);
128 2
            $this->fileManager->write($target, $compiled);
129 2
            $this->fileManager->touch($target, $this->fileManager->lastModified($source));
130
131
            // Go deeper in recursion
132 2
            $this->processAsset($innerDependencies);
133 2
        }
134 2
    }
135
136
    /**
137
     * Create static assets.
138
     *
139
     * @param string[] $paths Collection of paths for gathering assets
140
     *
141
     * @return string[] Cached assets full paths collection
142
     */
143 2
    public function manage(array $paths)
144
    {
145
        // Get assets list
146 2
        $assets = $this->fileManager->scan($paths, self::TYPES, self::$excludeFolders);
147
148
        // Iterate all assets for analyzing
149 2
        foreach ($assets as $asset) {
150
            // Build path to processed asset
151 2
            $cachedAsset = $this->getAssetProcessedPath($asset);
152
153
            // If cached assets was modified or new
154 2
            if (!$this->isValid($asset, $cachedAsset)) {
155
                // Recursively process asset and possible dependencies
156 2
                $this->processAsset([$asset => []]);
157
                // Store processed asset
158 2
                $this->assets[pathinfo($cachedAsset, PATHINFO_EXTENSION)][] = $cachedAsset;
159 2
            }
160 2
        }
161
162 2
        return $this->assets;
163
    }
164
165
    /**
166
     * Get asset cached path with extension conversion.
167
     *
168
     * @param string $asset Asset full path
169
     *
170
     * @return string Full path to cached asset
171
     */
172 2
    protected function getAssetProcessedPath($asset)
173
    {
174
        // Build asset project root relative path
175 2
        $relativePath = str_replace(self::$projectRoot, '', $asset);
176
177
        // Build full cached asset path
178 2
        return dirname(self::$cacheRoot . $relativePath) . '/'
179 2
        . pathinfo($asset, PATHINFO_FILENAME) . '.' . $this->convertType($asset);
180
    }
181
182
    /**
183
     * Get asset final type.
184
     *
185
     * @param string $asset Full asset path
186
     *
187
     * @return string Asset final type
188
     */
189 2
    public function convertType($asset)
190
    {
191
        // Convert input extension
192 2
        $extension = pathinfo($asset, PATHINFO_EXTENSION);
193
194 2
        return array_key_exists($extension, self::CONVERTER)
195 2
            ? self::CONVERTER[$extension]
196 2
            : $extension;
197
    }
198
199
    /**
200
     * Define if asset is not valid.
201
     *
202
     * @param string $asset       Full path to asset
203
     *
204
     * @param string $cachedAsset Full path to cached asset
205
     *
206
     * @return bool True if cached asset is valid
207
     */
208 2
    protected function isValid($asset, $cachedAsset)
209
    {
210
        // If cached asset does not exists or is invalid
211 2
        return $this->fileManager->exists($cachedAsset) !== false
212 2
        && $this->fileManager->lastModified($cachedAsset) === $this->fileManager->lastModified($asset);
213
    }
214
}
215