Completed
Push — master ( c547d6...31d97f )
by Vitaly
07:05
created

Module::title()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php declare(strict_types=1);
2
namespace samsonphp\core;
3
4
use samsonframework\core\ResourcesInterface;
5
use samsonframework\core\SystemInterface;
6
use samsonframework\core\ViewInterface;
7
use samsonphp\core\deprecated\iModule;
8
use samsonphp\core\exception\ControllerActionNotFound;
9
use samsonphp\core\exception\ViewPathNotFound;
10
use samsonphp\core\exception\ViewVariableNotFound;
11
12
/**
13
 * Модуль системы
14
 *
15
 * @author Vitaly Iegorov <[email protected]>
16
 * @deprecated Will be merged with ExternalModule
17
 */
18
class Module implements \ArrayAccess, iModule
19
{
20
    /** Static module instances collection */
21
    public static $instances = array();
22
23
    /** Uniquer identifier to check pointers */
24
    public $uid;
25
26
    /** @var ResourcesInterface Pointer to module resource map */
27
    public $resourceMap;
28
29
    /** @var array TODO: WTF? */
30
    public $composerParameters = array();
31
32
    /** Module views collection */
33
    protected $views = array();
34
35
    /** Module location */
36
    protected $path = '';
37
38
    /** Unique module identifier */
39
    protected $id;
40
41
    /** Path to view for rendering */
42
    protected $view_path = self::VD_POINTER_DEF;
43
44
    /** Pointer to view data entry */
45
    protected $data = array(self::VD_POINTER_DEF => array(self::VD_HTML => ''));
46
47
    /** Collection of data for view rendering, filled with default pointer */
48
    protected $view_data = array(self::VD_POINTER_DEF => array(self::VD_HTML => ''));
49
50
    /** Name of current view context entry */
51
    protected $view_context = self::VD_POINTER_DEF;
52
53
    /** Unique module cache path in local web-application */
54
    protected $cache_path;
55
56
    /** @var SystemInterface Instance for interaction with framework */
57
    protected $system;
58
59
    /**
60
     * Constructor
61
     *
62
     * @param string $id Module unique identifier
63
     * @param string $path Module location
64
     * @param ResourcesInterface $resourceMap Pointer to module resource map
65
     * @param SystemInterface $system
66 7
     */
67
    public function __construct($id, $path, ResourcesInterface $resourceMap, SystemInterface $system)
68
    {
69 7
        // Inject generic module dependencies
70
        $this->system = $system;
71
72 7
        $resourceMap->build($path);
73
74 7
        // Store pointer to module resource map
75
        $this->resourceMap = &$resourceMap;
76
        // Save views list
77 7
        $this->views = $resourceMap->views;
0 ignored issues
show
Bug introduced by
Accessing views on the interface samsonframework\core\ResourcesInterface 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...
78
79
        // Set default view context name
80 7
        $this->view_context = self::VD_POINTER_DEF;
81
82
        // Set up default view data pointer
83 7
        $this->data = &$this->view_data[$this->view_context];
84
85
        // Set module identifier
86 7
        $this->id = $id;
87
88
        // Set path to module
89 7
        $this->path(realpath($path));
90
91
        // Generate unique module identifier
92 7
        $this->uid = rand(0, 9999999) . '_' . microtime(true);
93
94
        // Add to module identifier to view data stack
95 7
        $this->data['id'] = $this->id;
96
97
        // Generate unique module cache path in local web-application
98
        $this->cache_path = __SAMSON_CWD__ . __SAMSON_CACHE_PATH . $this->id . '/';
99 7
100
        // Save ONLY ONE copy of this instance in static instances collection,
101
        // avoiding rewriting by cloned modules
102 7
        !isset(self::$instances[$this->id]) ? self::$instances[$this->id] = &$this : '';
103
104
        // Make view path relative to module - remove module path from view path
105 7
        $this->views = str_replace($this->path, '', $this->views);
106
107
        //elapsed('Registering module: '.$this->id.'('.$path.')' );
108 7
    }
109
110
    /** @see iModule::path() */
111 7
    public function path($value = null)
112 7
    {
113
        // Если передан параметр - установим его
114 7
        if (func_num_args()) {
115
            $this->path = isset($value{0}) ? rtrim(normalizepath($value), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : '';
116 1
117
            return $this;
118
        } // Вернем относительный путь к файлам модуля
119
        else return $this->path;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
120
    }
121
122
    /**    @see iModule::title() */
123
    public function title($title = null)
124
    {
125
        return $this->set($title, 'title');
126 1
    }
127
128 1
    /** @see iModule::set() */
129
    public function set($value, $field = null)
130 1
    {
131
        $this->__set($value, $field);
132
133
        return $this;
134 1
    }
135
136 1
    /**    @see iModule::id() */
137
    public function id()
138
    {
139
        return $this->id;
140
    }
141
142
    /** @see iModuleViewable::toView() */
143
    public function toView($prefix = null, array $restricted = array())
144
    {
145
        // Get all module data variables
146
        $view_data = array_merge($this->data, get_object_vars($this));
147
148
        // Remove plain HTML from view data
149
        unset($view_data[self::VD_HTML]);
150
151
        return $view_data;
152 1
    }
153
154 1
    /** @see iModule::html() */
155
    public function html($value)
156 1
    {
157
        $this->data[self::VD_HTML] = $value;
158
159 3
        return $this;
160
    }
161
162 3
    public function view($viewPath)
163
    {
164
        if (is_a($viewPath, ViewInterface::class)) {
165 3
            $this->view_path = $viewPath;
166
        } elseif (is_string($viewPath)) {
167 3
            // Find full path to view file
168
            $foundViewPath = $this->findView($viewPath);
169
170 3
            // We could not find view
171 3
            if ($foundViewPath !== false) {
172 1
                // Switch view context to founded module view
173
                $this->viewContext($foundViewPath);
174
175
                // Set current view path
176 3
                $this->view_path = $foundViewPath;
177
            } else {
178
                throw(new ViewPathNotFound($viewPath));
179
            }
180
        }
181
182
        // Продолжим цепирование
183
        return $this;
184
    }
185 3
186
    /**
187
     * Find view file by its part in module view resources and return full path to it.
188 3
     *
189
     * @param string $viewPath Part of path to module view file
190
     * @return string Full path to view file
191 3
     */
192
    public function findView($viewPath)
193 3
    {
194
        // Remove file extension for correct array searching
195
        $viewPath = str_replace(array('.php', '.vphp'), '', $viewPath);
196 3
197
        // Try to find passed view_path in  resources views collection
198
        if (sizeof($view = preg_grep('/' . addcslashes($viewPath, '/\\') . '(\.php|\.vphp)/ui', $this->views))) {
199 1
            // Sort view paths to get the shortest path
200
            usort($view, array($this, 'sortStrings'));
201
202
            // Set current full view path as last found view
203
            return end($view);
204
        }
205
206 4
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by samsonphp\core\Module::findView of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
207
    }
208
209 4
    /**
210
     * Perform module view context switching
211
     * @param string $view_path New view context name
212 4
     */
213
    protected function viewContext($view_path)
214
    {
215 4
        // Pointer to NEW view data context
216
        $new = &$this->view_data[$view_path];
217
218
        // Pointer to OLD view data context
219 3
        $old = &$this->view_data[$this->view_context];
220
221 3
        // If we are trying to switch to NEW view context
222
        if ($this->view_context !== $view_path) {
223
            //elapsed( $this->id.' - Switching view context from '.$this->view_context.' to '.$view_path );
224 3
225
            // Create new entry in view data collection if it does not exists
226
            if (!isset($this->view_data[$view_path])) {
227
                // Create new view data record
228
                $new = array();
229 3
230 3
                // If current view data context has view data
231
                if (isset($old)) {
232
                    //elapsed($old);
233 3
                    //elapsed( $this->id.' - Copying previous view context view data '.$this->view_context.' to new view context '.$view_path.'('.sizeof($old).')');
234 3
235
                    // Copy default view context view data to new view context
236
                    $new = array_merge($new, $old);
237 3
                }
238
239
                // Clear plain HTML for new view context
240 3
                $new[self::VD_HTML] = '';
241 3
            }
242
243 4
            // Change view data pointer to appropriate view data entry
244
            $this->data = &$new;
245
246 1
            // Save current context name
247
            $this->view_context = $view_path;
248
        }
249 1
        //else elapsed( $this->id.' - NO need to switch view context from '.$this->view_context.' to '.$view_path );
250
    }
251
252 1
    /**
253
     * @param null $controller
254
     *
255
     * @throws ControllerActionNotFound
256
     */
257 1
    public function render($controller = null)
258 1
    {
259 1
        // Switch current system active module
260
        $old = &$this->system->active($this);
261
262 1
        // If specific controller action should be run
263
        if (isset($controller)) {
264
            /**
265 1
             * TODO: This would be removed in next major version, here should be
266
             * passed real controller method for execution.
267 1
             */
268
            $controller = method_exists($this, iModule::OBJ_PREFIX . $controller)
269 1
                ? iModule::OBJ_PREFIX . $controller
270
                : $controller;
271 1
272 1
            // Define if this is a procedural controller or OOP
273 1
            $callback = array($this, $controller);
274
275 1
            // If this controller action is present
276
            if (method_exists($this, $controller)) {
277
                // Get passed arguments
278 1
                $parameters = func_get_args();
279
                // Remove first as its a controller action name
280
                array_shift($parameters);
281 1
                // Perform controller action with passed parameters
282 1
                call_user_func_array($callback, $parameters);
283
            } else {
284
                throw new ControllerActionNotFound($this->id . '#' . $controller);
285 3
            }
286
        }
287
288 3
        // Output view
289
        echo $this->output();
290
291
        // Restore previous active module
292
        $this->system->active($old);
293 3
    }
294
295
    /**    @see iModule::output() */
296
    public function output()
297
    {
298
        // If view path not specified - use current correct view path
299 3
        $viewPath = $this->view_path;
300
301
        if (is_string($viewPath)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
302 3
303
            //elapsed('['.$this->id.'] Rendering view context: ['.$viewPath.'] with ['.$renderer->id.']');
304 2
305
            // Switch view context to new module view
306
            $this->viewContext($viewPath);
307 2
308
            //elapsed($this->id.' - Outputing '.$view_path.'-'.sizeof($this->data));
309
            //elapsed(array_keys($this->view_data));
310 2
311 2
            // Get current view context plain HTML
312
            $out = $this->data[self::VD_HTML];
313
314 3
            // If view path specified
315
            if (isset($viewPath{0})) {
316
                // Временно изменим текущий модуль системы
317 3
                $old = $this->system->active($this);
318
319
                // Прорисуем представление модуля
320 3
                $out .= $this->system->render($this->path . $viewPath, $this->data);
321
322
                // Вернем на место текущий модуль системы
323 3
                $this->system->active($old);
324
            }
325
326 3
            // Clear currently outputted view context from VCS
327
            unset($this->view_data[$viewPath]);
328
329 3
            // Get last element from VCS
330
            end($this->view_data);
331
332
            // Get last element from VCS name
333 1
            $this->view_context = key($this->view_data);
334
335
            // Set internal view data pointer to last VCS entry
336
            $this->data = &$this->view_data[$this->view_context];
337
338 1
            // Return view path to previous state
339
            $this->view_path = $this->view_context;
340 1
341 1
            // Вернем результат прорисовки
342 1
            return $out;
343 1
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
344
        } elseif (is_a($viewPath, ViewInterface::class)) {
345 1
            /** @var ViewInterface $viewPath */
346
            return $viewPath->output();
347
        }
348 1
    }
349
350
    /** Magic method for calling un existing object methods */
351
    public function __call($method, $arguments)
352
    {
353
        //elapsed($this->id.' - __Call '.$method);
354 1
355
        // If value is passed - set it
356 1
        if (count($arguments)) {
357
            // If first argument is object or array - pass method name as second parameter
358
            if (is_object($arguments[0]) || is_array($arguments[0])) {
359
                $this->__set($arguments[0], $method);
360 1
            } else { // Standard logic
361
                $this->__set($arguments[0], $method);
362
            }
363 1
        }
364
365
        // Chaining
366 1
        return $this;
367
    }
368
369 1
    // Магический метод для получения переменных представления модуля
370 1
371
    /** Обработчик сериализации объекта */
372
    public function __sleep()
373
    {
374
        return array('id', 'path', 'data', 'views');
375 1
    }
376
377 1
    /** Обработчик десериализации объекта */
378 1
    public function __wakeup()
379
    {
380 2
        // Fill global instances
381
        self::$instances[$this->id] = &$this;
382 2
383
        // Set up default view data pointer
384
        $this->view_data[self::VD_POINTER_DEF] = $this->data;
385
386
        // Set reference to view context entry
387 3
        $this->data = &$this->view_data[self::VD_POINTER_DEF];
388
    }
389
390 3
    // TODO: Переделать обработчик в одинаковый вид для объектов и простых
391
392
    /** Группа методов для доступа к аттрибутам в виде массива */
393 3
    public function offsetSet($value, $offset)
394 2
    {
395 2
        $this->__set($offset, $value);
396 1
    }
397
398
    public function offsetGet($offset)
399
    {
400 2
        return $this->__get($offset);
401
    }
402
403 3
    // Магический метод для установки переменных представления модуля
404
405
    public function __get($field)
406 3
    {
407 1
        // Установим пустышку как значение переменной
408
        $result = null;
409
410
        // Если указанная переменная представления существует - получим её значение
411 1
        if (isset($this->data[$field])) {
412 1
            $result = &$this->data[$field];
413 1
        } else {
414 1
            throw(new ViewVariableNotFound($field));
415 1
        }
416 1
417 1
        // Иначе вернем пустышку
418 3
        return $result;
419
    }
420 3
421 3
    public function __set($value, $field = null)
422
    {
423 1
        if (is_object($field) || is_array($field)) {
424
            $tempValue = $field;
425 1
            $field = $value;
426 1
            $value = $tempValue;
427
428 2
            // TODO: Will be added in next major version
429
            //throw new \Exception('ViewInterface::set($value, $name) has changed, first arg is variable second is name or prefix');
430 2
        }
431
432
		// This is object
433
		if (is_object($value) && is_a($value, 'samsonframework\core\RenderInterface')) {
434 1
			$this->_setObject($value, $field);
435
		} elseif (is_array($value)) { // If array is passed
436 1
			$this->_setArray($value, $field);
437
		} else { // Set view variable
438
            $this->data[$field] = $value;
439
		}
440
    }
441
442
    public function offsetUnset($offset)
443
    {
444
        unset($this->data[$offset]);
445
    }
446 1
447
    public function offsetExists($offset)
448
    {
449 1
        return isset($this->data[$offset]);
450 1
    }
451 1
452
    /** Sort array by string length */
453
    protected function sortStrings($a, $b)
454 1
    {
455
        return strlen($b) - strlen($a);
456
    }
457 1
458
    /**
459 1
     * Create unique module cache folder structure in local web-application.
460 1
     *
461 1
     * @param string $file Path to file relative to module cache location
462
     * @param boolean $clear Flag to perform generic cache folder clearence
463
     * @return boolean TRUE if cache file has to be regenerated
464 1
     */
465
    protected function cache_refresh(&$file, $clear = true, $folder = null)
0 ignored issues
show
Coding Style introduced by
Method name "Module::cache_refresh" is not in camel caps format
Loading history...
466
    {
467
        // Add slash to end
468
        $folder = isset($folder) ? rtrim($folder, '/') . '/' : '';
469
470
        $path = $this->cache_path . $folder;
471
472
        // If module cache folder does not exists - create it
473
        if (!file_exists($path)) {
474
            mkdir($path, 0777, true);
475 1
        }
476
477
        // Build full path to cached file
478 1
        $file = $path . $file;
479
480
        // If cached file does not exsits
481 1
        if (!file_exists($file)) {
482
            // If clearence flag set to true - clear all files in module cache directory with same extension
483
            if ($clear) {
484 1
                File::clear($path, pathinfo($file, PATHINFO_EXTENSION));
485
            }
486
487 1
            // Signal for cache file regeneration
488 1
            return true;
489
        }
490
491
        return false;
492
    }
493
494
    /**
495 1
     *
496
     * @param unknown $object
497
     * @param string $viewprefix
498 1
     */
499
    private function _setObject($object, $viewprefix = null)
500
    {
501 1
        // Generate viewprefix as only lowercase classname without NS if it is not specified
502 1
        $class_name = is_string($viewprefix) ? $viewprefix : '' . mb_strtolower(ns_classname(get_class($object)), 'UTF-8');
0 ignored issues
show
Deprecated Code introduced by
The function ns_classname() has been deprecated with message: use \samson\core\AutoLoader::className() and pass full class name to it without splitting into class name and namespace

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
503
504
        // Save object to view data
505
        $this->data[$class_name] = $object;
506
507
        // Add separator
508
        $class_name .= '_';
509
510
        // Generate objects view array data and merge it with view data
511
        $this->data = array_merge($this->data, $object->toView($class_name));
512
    }
513
514
    /**
515
     *
516
     * @param unknown $array
517
     * @param string $viewprefix
518
     */
519
    private function _setArray($array, $viewprefix = null)
520
    {
521
        // Save array to view data
522
        $this->data[$viewprefix] = $array;
523
524
        // Add array values to view data
525
        $this->data = array_merge($this->data, $array);
526
    }
527
}
528