Completed
Push — master ( ac2e98...a10e1e )
by Vitaly
02:48
created

Router   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 78.26%

Importance

Changes 32
Bugs 3 Features 20
Metric Value
c 32
b 3
f 20
dl 0
loc 219
ccs 54
cts 69
cp 0.7826
rs 10
wmc 16
lcom 1
cbo 4

5 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 17 1
B createAssets() 0 52 6
A getAssetPathData() 0 15 2
B getAssets() 0 27 3
A renderTemplate() 0 19 4
1
<?php
2
namespace samsonphp\resource;
3
4
use samson\core\ExternalModule;
5
use samsonphp\event\Event;
6
7
/**
8
 * Resource router for serving static resource from unreachable web-root paths.
9
 *
10
 * TODO: Validate old files that do not exists anymore to remove them
11
 *
12
 * @author Vitaly Iegorov <[email protected]>
13
 */
14
class Router extends ExternalModule
15
{
16
    /** @deprecated Use E_MODULES */
17
    const EVENT_START_GENERATE_RESOURCES = 'resourcer.modulelist';
18
    /** Event for modifying modules */
19
    const E_MODULES = 'resourcer.modulelist';
20
    /** Event for resources preloading */
21
    const E_RESOURCE_PRELOAD = 'resourcer.preload';
22
    /** Event for resources compiling */
23
    const E_RESOURCE_COMPILE = 'resourcer.compile';
24
    /** Event when recourse management is finished */
25
    const E_FINISHED = 'resourcer.finished';
26
27
    /** Assets types */
28
    const T_CSS = 'css';
29
    const T_LESS = 'less';
30
    const T_SCSS = 'scss';
31
    const T_SASS = 'sass';
32
    const T_JS = 'js';
33
    const T_TS = 'ts';
34
    const T_COFFEE = 'coffee';
35
36
    /** Assets converter */
37
    const CONVERTER = [
38
        self::T_JS => self::T_JS,
39
        self::T_TS => self::T_JS,
40
        self::T_COFFEE => self::T_JS,
41
        self::T_CSS => self::T_CSS,
42
        self::T_LESS => self::T_CSS,
43
        self::T_SCSS => self::T_CSS,
44
        self::T_SASS => self::T_CSS,
45
    ];
46
47
    /** @deprecated Identifier */
48
    protected $id = STATIC_RESOURCE_HANDLER;
49
50
    /** Collection of registered resource types */
51
    protected $types = [
52
        self::T_CSS,
53
        self::T_JS,
54
        self::T_LESS,
55
        self::T_SCSS,
56
        self::T_SASS,
57
        self::T_COFFEE,
58
        self::T_TS
59
    ];
60
61
    /** @var array Assets cache */
62
    protected $cache = [];
63
64
    /** @var array Template markers for inserting assets */
65
    protected $templateMarkers = [
66
        'css' => '</head>',
67
        'js' => '</body>'
68
    ];
69
    /** @var array Collection of static resources */
70
    protected $resources = [];
71
72
    /** @var array Collection of static resource URLs */
73
    protected $resourceUrls = [];
74
75
    /**
76
     * @see ModuleConnector::init()
77
     *
78
     * @param array $params Initialization parameters
79
     *
80
     * @return bool True if module successfully initialized
81
     */
82 1
    public function init(array $params = array())
83
    {
84
        // Subscribe for CSS handling
85 1
        Event::subscribe(self::E_RESOURCE_COMPILE, [new CSS(), 'compile']);
86
87
        // Subscribe to core template rendering event
88 1
        Event::subscribe('core.rendered', [$this, 'renderTemplate']);
89
90
        // Create assets
91 1
        $this->createAssets($this->getAssets());
92
93
        // Fire completion event
94 1
        Event::fire(self::E_FINISHED);
95
96
        // Continue parent initialization
97 1
        return parent::init($params);
98
    }
99
100
    /**
101
     * Create static assets.
102
     *
103
     * @param array $files Collection of paths for gathering resources
104
     */
105 1
    public function createAssets(array $files)
106
    {
107 1
        $wwwRoot = getcwd();
108
109 1
        $assets = [];
110
111
        // Scan folder and gather
112 1
        foreach ($files as $file) {
113
            // Generate cached resource path with possible new extension after compiling
114 1
            $assets[$file] = $this->getAssetPathData($file);
115 1
            $extension = pathinfo($assets[$file], PATHINFO_EXTENSION);
116
117
            // If cached assets was modified or new
118 1
            if (!file_exists($assets[$file]) || filemtime($file) !== filemtime($assets[$file])) {
119
                // Read asset content
120 1
                $this->cache[$file] = file_get_contents($file);
121
122
                // Fire event for analyzing resource
123 1
                Event::fire(self::E_RESOURCE_PRELOAD, [$file, pathinfo($file, PATHINFO_EXTENSION), &$this->cache[$file]]);
124 1
            } else {
125
                // Add this resource to resource collection grouped by resource type
126
                $this->resources[$extension][] = $assets[$file];
127
                $this->resourceUrls[$extension][] = str_replace($wwwRoot, '', $assets[$file]);
128
            }
129 1
        }
130
131 1
        $wwwRoot = getcwd();
132 1
        foreach ($this->cache as $file => $content) {
133 1
            $extension = pathinfo($file, PATHINFO_EXTENSION);
134
135 1
            $compiled = $content;
136 1
            Event::fire(self::E_RESOURCE_COMPILE, [$file, &$extension, &$compiled]);
137
138
            // Create folder structure and file only if it is not empty
139 1
            $resource = $this->getAssetPathData($file, $extension);
140
141
            // Create cache path
142 1
            $path = dirname($resource);
143 1
            if (!file_exists($path)) {
144 1
                mkdir($path, 0777, true);
145 1
            }
146
147 1
            file_put_contents($resource, $compiled);
148
149
            // Sync cached file with source file
150 1
            touch($resource, filemtime($file));
151
152
            // Add this resource to resource collection grouped by resource type
153 1
            $this->resources[$extension][] = $resource;
154 1
            $this->resourceUrls[$extension][] = str_replace($wwwRoot, '', $resource);
155 1
        }
156 1
    }
157
158 1
    private function getAssetPathData($resource, $extension = null)
159
    {
160
        // Convert input extension
161 1
        $extension = self::CONVERTER[$extension === null
162 1
            ? pathinfo($resource, PATHINFO_EXTENSION)
163 1
            : $extension];
164
165 1
        $wwwRoot = getcwd();
166 1
        $projectRoot = dirname($wwwRoot) . '/';
167 1
        $relativePath = str_replace($projectRoot, '', $resource);
168
169 1
        $fileName = pathinfo($resource, PATHINFO_FILENAME);
170
171 1
        return dirname($this->cache_path . $relativePath) . '/' . $fileName . '.' . $extension;
172
    }
173
174
    /**
175
     * Get asset files
176
     */
177 1
    private function getAssets()
178
    {
179
        // Get loaded modules
180 1
        $moduleList = $this->system->module_stack;
0 ignored issues
show
Bug introduced by
Accessing module_stack on the interface samsonframework\core\SystemInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
181
182
        // Event for modification of module list
183 1
        Event::fire(self::E_MODULES, array(&$moduleList));
184
185 1
        $projectRoot = dirname(getcwd()) . '/';
186
187
        // Add module paths
188 1
        $paths = [];
189 1
        foreach ($moduleList as $module) {
190
            /**
191
             * We need to exclude project root because vendor folder will be scanned
192
             * and all assets from there would be added.
193
             */
194 1
            if ($module->path() !== $projectRoot) {
195 1
                $paths[] = $module->path();
196 1
            }
197 1
        }
198
199
        // Add web-root as last path
200 1
        $paths[] = getcwd();
201
202 1
        return Resource::scan($paths, $this->types);
203
    }
204
205
    /**
206
     * Template rendering handler by injecting static assets url
207
     * in appropriate.
208
     *
209
     * @param $view
210
     *
211
     * @return mixed
212
     */
213
    public function renderTemplate(&$view)
214
    {
215
        foreach ($this->resourceUrls as $type => $urls) {
216
            // Replace template marker by type with collection of links to resources of this type
217
            $view = str_ireplace(
218
                $this->templateMarkers[$type],
219
                implode("\n", array_map(function ($value) use ($type) {
220
                    if ($type === 'css') {
221
                        return '<link type="text/css" rel="stylesheet" property="stylesheet" href="' . $value . '">';
222
                    } elseif ($type === 'js') {
223
                        return '<script type="text/javascript" src="' . $value . '"></script>';
224
                    }
225
                }, $urls)) . "\n" . $this->templateMarkers[$type],
226
                $view
227
            );
228
        }
229
230
        return $view;
231
    }
232
}
233