Completed
Push — master ( 3cc9f7...230e2b )
by Vitaly
07:31
created

Router::replaceUrlCallback()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 36
Code Lines 15

Duplication

Lines 6
Ratio 16.67 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
cc 7
eloc 15
c 2
b 0
f 2
nc 10
nop 1
dl 6
loc 36
rs 6.7272
1
<?php
2
namespace samsonphp\resource;
3
4
use Aws\CloudFront\Exception\Exception;
5
use samson\core\ExternalModule;
6
use samson\core\Module;
7
use samsonframework\resource\ResourceMap;
8
use samsonphp\event\Event;
9
use samsonphp\resource\exception\ResourceNotFound;
10
11
/**
12
 * Resource router for serving static resource from unreachable web-root paths.
13
 *
14
 * @author Vitaly Iegorov <[email protected]>
15
 * @author Nikita Kotenko <[email protected]>
16
 */
17
class Router extends ExternalModule
18
{
19
    /** @deprecated Use E_MODULES */
20
    const EVENT_START_GENERATE_RESOURCES = 'resourcer.modulelist';
21
    /** Event for modifying modules */
22
    const E_MODULES = 'resourcer.modulelist';
23
    /** Event for resources preloading */
24
    const E_RESOURCE_PRELOAD = 'resourcer.preload';
25
    /** Event for resources compiling */
26
    const E_RESOURCE_COMPILE = 'resourcer.compile';
27
28
    /** Collection of excluding scanning folder patterns */
29
    const EXCLUDING_FOLDERS = [
30
        '*/cache/*',
31
        '*/vendor/*/vendor/*'
32
    ];
33
34
    /** Collection of registered resource types */
35
    public $types = ['css', 'less', 'js', 'coffee', 'ts'];
36
37
    /** @var array Template markers for inserting assets */
38
    protected $templateMarkers = [
39
        'css' => '</head>',
40
        'js' => '</body>'
41
    ];
42
43
    /** @var array Collection of static resources */
44
    protected $resources = [];
45
    /** @var array Collection of static resource URLs */
46
    protected $resourceUrls = [];
47
48
    /** Identifier */
49
    protected $id = STATIC_RESOURCE_HANDLER;
50
51
    /** @see ModuleConnector::init() */
52
    public function init(array $params = array())
53
    {
54
        // Subscribe for CSS handling
55
        Event::subscribe(self::E_RESOURCE_COMPILE, [new CSS(), 'compile']);
56
57
        $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...
58
        $projectRoot = dirname(getcwd()).'/';
59
        $paths = [$projectRoot.'www/'];
60
61
        // Event for modification of module list
62
        Event::fire(self::E_MODULES, array(&$moduleList));
63
64
        // Add module paths
65
        foreach ($moduleList as $module) {
66
            if ($module->path() !== $projectRoot) {
67
                $paths[] = $module->path();
68
            }
69
        }
70
71
        // Iterate all types of assets
72
        foreach ($this->types as $type) {
73
            $this->createAssets($paths, $type);
74
        }
75
76
        // Subscribe to core template rendering event
77
        Event::subscribe('core.rendered', [$this, 'renderTemplate']);
78
    }
79
80
    /**
81
     * Get path static resources list filtered by extensions.
82
     *
83
     * @param string $path Path for static resources scanning
84
     * @param string $extension Resource type
85
     *
86
     * @return array Matched static resources collection with full paths
87
     */
88
    protected function scanFolderRecursively($path, $extension)
89
    {
90
        // TODO: Handle not supported cmd command(Windows)
91
        // TODO: Handle not supported exec()
92
93
        // Generate LINUX command to gather resources as this is 20 times faster
94
        $files = [];
95
96
        // Scan path excluding folder patterns
97
        exec(
98
            'find ' . $path . ' -type f -name "*.' . $extension . '" '.implode(' ', array_map(function ($value) {
99
                return '-not -path ' . $value;
100
            }, self::EXCLUDING_FOLDERS)),
101
            $files
102
        );
103
104
        // TODO: Why some paths have double slashes? Investigate speed of realpath, maybe // changing if quicker
105
        return array_map('realpath', $files);
106
    }
107
108
    /**
109
     * Create static assets.
110
     *
111
     * @param array  $paths Collection of paths for gatherin resources
112
     * @param string $type Resource extension
113
     */
114
    public function createAssets(array $paths, $type)
115
    {
116
        // Gather all resource files for this type
117
        $files = [];
118
        foreach ($paths as $path) {
119
            $files = array_filter(array_merge($this->scanFolderRecursively($path, $type), $files));
120
        }
121
122
        // Create resources timestamps
123
        $timeStamps = [];
124
        foreach ($files as $file) {
125
            $timeStamps[$file] = filemtime($file);
126
        }
127
128
        // Generate resources cache stamp by hashing combined files modification timestamp
129
        $cacheStamp = md5(implode('', $timeStamps));
0 ignored issues
show
Unused Code introduced by
$cacheStamp is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
130
131
        // TODO: We need cache for list of files to check if we need to preload them by storing modified date
132
133
        // Here we need to prepare resource - gather LESS variables for example
134
        foreach ($files as $file) {
135
            // Fire event for preloading resource
136
            Event::fire(self::E_RESOURCE_PRELOAD, [$file, pathinfo($file, PATHINFO_EXTENSION)]);
137
        }
138
139
        $wwwRoot = getcwd();
140
        $projectRoot = dirname($wwwRoot).'/';
141
142
        // Here we can compile resources
143
        foreach ($files as $file) {
144
            $extension = pathinfo($file, PATHINFO_EXTENSION);
145
            $fileName = pathinfo($file, PATHINFO_FILENAME);
146
            $relativePath = str_replace($projectRoot, '', $file);
147
148
            // Compiled resource
149
            $compiled = '';
150
            Event::fire(self::E_RESOURCE_COMPILE, [$file, &$extension, &$compiled]);
151
152
            // Generate cached resource path with possible new extension after compiling
153
            $resource = dirname($this->cache_path.$relativePath).'/'.$fileName.'.'.$extension;
154
155
            // Create folder structure and file only if it is not empty
156
            if (strlen($compiled)) {
157
                // Create cache path
158
                $path = dirname($resource);
159
                if (!file_exists($path)) {
160
                    mkdir($path, 0777, true);
161
                }
162
                file_put_contents($resource, $compiled);
163
164
                // Add this resource to resource collection grouped by resource type
165
                $this->resources[$extension][] = $resource;
166
                $this->resourceUrls[$extension][] = str_replace($wwwRoot, '', $resource);
167
            }
168
        }
169
    }
170
171
    /**
172
     * Template rendering handler by injecting static assets url
173
     * in appropriate.
174
     *
175
     * @param $view
176
     *
177
     * @return mixed
178
     */
179
    public function renderTemplate(&$view)
180
    {
181
        foreach ($this->resourceUrls as $type => $urls) {
182
            // Replace template marker by type with collection of links to resources of this type
183
            $view = str_ireplace(
184
                $this->templateMarkers[$type],
185
                implode("\n", array_map(function($value) use ($type) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
186
                    if ($type === 'css') {
187
                        return '<link type="text/css" rel="stylesheet" property="stylesheet" href="' . $value . '">';
188
                    } elseif ($type === 'js') {
189
                        return '<script async type="text/javascript" src="' . $value . '"></script>';
190
                    }
191
                }, $urls)) . "\n" . $this->templateMarkers[$type],
192
                $view
193
            );
194
        }
195
196
        return $view;
197
    }
198
}
199