Completed
Push — master ( cc0315...505aa7 )
by Vitaly
02:04
created

Router::src_replace_callback()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 31
Code Lines 13

Duplication

Lines 6
Ratio 19.35 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
cc 6
eloc 13
c 3
b 0
f 2
nc 6
nop 1
dl 6
loc 31
rs 8.439
1
<?php
2
namespace samsonphp\resource;
3
4
use samson\core\ExternalModule;
5
use samson\core\iModule;
6
use samsonphp\event\Event;
7
use samsonphp\resource\exception\ResourceNotFound;
8
9
/**
10
 * Класс для определения, построения и поиска путей к ресурсам
11
 * системы. Класс предназначен для формирования УНИКАЛЬНЫХ URL
12
 * описывающих путь к ресурсу веб-приложения/модуля независимо
13
 * от его расположения на HDD.
14
 *
15
 * Создавая возможность один рас описать путь вида:
16
 *    ИМЯ_РЕСУРСА - ИМЯ_ВЕБПРИЛОЖЕНИЯ - ИМЯ_МОДУЛЯ
17
 *
18
 * И больше не задумываться об реальном(физическом) местоположении
19
 * ресурса
20
 *
21
 * @package SamsonPHP
22
 * @author Vitaly Iegorov <[email protected]>
23
 * @author Nikita Kotenko <[email protected]>
24
 * @version 1.0
25
 */
26
class Router extends ExternalModule
27
{
28
    /** Event showing that new gather resource file was created */
29
    const EVENT_CREATED = 'resource.created';
30
31
    /** Event showing that new gather resource file was created */
32
    const EVENT_START_GENERATE_RESOURCES = 'resource.start.generate.resources';
33
34
    /** Identifier */
35
    protected $id = STATIC_RESOURCE_HANDLER;
36
37
    /** @var string Marker for inserting generated javascript link */
38
    public $javascriptMarker = '</body>';
39
40
    /** Cached resources path collection */
41
    public $cached = array();
42
43
    /** Collection of updated cached resources for notification of changes */
44
    public $updated = array();
45
46
    /** Pointer to processing module */
47
    private $currentModule;
48
49
    /** @var string Current processed resource */
50
    private $currentResource;
51
52
53
    /** @see ModuleConnector::init() */
54
    public function init(array $params = array())
55
    {
56
        parent::init($params);
57
58
        $moduleList = $this->system->module_stack;
59
60
        Event::fire(self::EVENT_START_GENERATE_RESOURCES, array(&$moduleList));
61
62
        $this->generateResources($moduleList);
63
64
        // Subscribe to core rendered event
65
        $this->system->subscribe('core.rendered', array($this, 'renderer'));
66
    }
67
68
    public function generateResources($moduleList, $templatePath = 'default')
69
    {
70
        // Cache main web resources
71
        foreach (array(array('js'), array('css', 'less'), array('coffee')) as $rts) {
72
            // Get first resource type as extension
73
            $rt = $rts[0];
74
75
            $hash_name = '';
76
77
            // Iterate gathered namespaces for their resources
78
            /** @var Module $module */
79
            foreach ($moduleList as $id => $module) {
80
                // If necessary resources has been collected
81
                foreach ($rts as $_rt) {
82
                    if (isset($module->resourceMap->$_rt)) {
83
                        foreach ($module->resourceMap->$_rt as $resource) {
84
                            // Created string with last resource modification time
85
                            $hash_name .= filemtime($resource);
86
                        }
87
                    }
88
                }
89
            }
90
91
            // Get hash that's describes resource status
92
            $hash_name = md5($hash_name) . '.' . $rt;
93
94
            $file = $hash_name;
95
96
            $dir = str_replace(array('/', '.'), '_', $templatePath);
97
98
            // If cached file does not exists
99
            if ($this->cache_refresh($file, true, $dir)) {
0 ignored issues
show
Unused Code introduced by
The call to Router::cache_refresh() has too many arguments starting with $dir.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
100
                // Read content of resource files
101
                $content = '';
102
                foreach ($moduleList as $id => $module) {
103
                    $this->currentModule = $module;
104
                    // If this ns has resources of specified type
105
                    foreach ($rts as $_rt) {
106
                        if (isset($module->resourceMap->$_rt)) {
107
                            //TODO: If you will remove & from iterator - system will fail at last element
108
                            foreach ($module->resourceMap->$_rt as $resource) {
109
                                // Store current processing resource
110
                                $this->currentResource = $resource;
111
                                // Read resource file
112
                                $c = file_get_contents($resource);
113
                                // Rewrite url in css
114
                                if ($rt == 'css') {
115
                                    $c = preg_replace_callback('/url\s*\(\s*(\'|\")?([^\)\s\'\"]+)(\'|\")?\s*\)/i',
116
                                        array($this, 'src_replace_callback'), $c);
117
                                }
118
                                // Gather processed resource text together
119
                                $content .= "\n\r" . $c;
120
                            }
121
                        }
122
                    }
123
                }
124
125
                // Fire event that new resource has been generated
126
                Event::fire(self::EVENT_CREATED, array($rt, &$content, &$file, &$this));
127
128
                // Fix updated resource file with new path to it
129
                $this->updated[$rt] = $file;
130
131
                // Запишем содержание нового "собранного" ресурса
132
                file_put_contents($file, $content);
133
            }
134
135
            // Save path to resource cache
136
            $this->cached[$rt][$templatePath] = __SAMSON_CACHE_PATH . $this->id . '/' . $dir . '/' . $hash_name;
137
        }
138
    }
139
140
    /**
141
     * Core render handler for including CSS and JS resources to html
142
     *
143
     * @param sting $view View content
144
     * @param array $data View data
145
     *
146
     * @return string Processed view content
147
     */
148
    public function renderer(&$view, $data = array(), iModule $m = null)
149
    {
150
        $tempateId = isset($this->cached['css'][$this->system->template()]) ? $this->system->template() : 'default';
151
152
        // Define resource urls
153
        $css = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['css'][$tempateId]);
154
        $js = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['js'][$tempateId]);
155
156
        // TODO: Прорисовка зависит от текущего модуля, сделать єто через параметр прорисовщика
157
        // If called from compressor
158
        if ($m->id() == 'compressor') {
0 ignored issues
show
Bug introduced by
It seems like $m is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
159
            $tempateId = isset($this->cached['css'][$data['file']]) ? $data['file'] : 'default';
160
            $css = url()->base() . basename($this->cached['css'][$tempateId]);
161
            $js = url()->base() . basename($this->cached['js'][$tempateId]);
162
        }
163
164
        // Put css link at the end of <head> page block
165
        $view = str_ireplace('</head>',
166
            "\n" . '<link type="text/css" rel="stylesheet" href="' . $css . '">' . "\n" . '</head>', $view);
167
168
        // Put javascript link in the end of the document
169
        $view = str_ireplace($this->javascriptMarker,
170
            "\n" . '<script type="text/javascript" src="' . $js . '"></script>' . "\n" . $this->javascriptMarker,
171
            $view);
172
173
        //elapsed('Rendering view =)');
174
175
        return $view;
176
    }
177
178
    /**
179
     * Callback for CSS url(...) rewriting.
180
     *
181
     * @param array $matches Regular expression matches collection
182
     *
183
     * @return string Rewritten url(..) with static resource handler url
184
     * @throws ResourceNotFound
185
     */
186
    public function src_replace_callback($matches)
0 ignored issues
show
Coding Style introduced by
Method name "Router::src_replace_callback" is not in camel caps format
Loading history...
187
    {
188
        // If we have found static resource path definition and its not inline
189
        if (array_key_exists(2, $matches) && strpos($matches[2], 'data:') === false) {
190
            // Store static resource path
191
            $url = $matches[2];
192
193
            // Ignore preprocessor vars
194
            // TODO: This is totally wrong need to come up with decision
195
            if (strpos($url, '@') !== false) {
196
                return $matches[0];
197
            }
198
199
            // Remove possible GET parameters from resource path
200 View Code Duplication
            if (($getStart = stripos($url, '?')) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
                $url = substr($url, 0, $getStart);
202
            }
203
204
            // Remove possible HASH parameters from resource path
205 View Code Duplication
            if (($getStart = stripos($url, '#')) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
                $url = substr($url, 0, $getStart);
207
            }
208
209
            // Build path to static resource handler
210
            return 'url("/' . $this->id . '/?p='
211
                .Resource::getRelativePath($url, dirname($this->currentResource))
212
                . '")';
213
        }
214
215
        return $matches[0];
216
    }
217
}
218