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

Router::createAssets()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 52
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 6.0118

Importance

Changes 6
Bugs 0 Features 2
Metric Value
cc 6
eloc 25
c 6
b 0
f 2
nc 9
nop 1
dl 0
loc 52
ccs 27
cts 29
cp 0.931
crap 6.0118
rs 8.6868

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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