Completed
Push — master ( 7285a1...bcbc0e )
by Vitaly
06:04
created

Module::isAsynchronousRequest()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4286
cc 3
eloc 4
nc 3
nop 0
1
<?php
2
namespace samsonphp\router;
3
4
use samsonframework\core\SystemInterface;
5
use samsonframework\routing\Core;
6
use samsonframework\routing\generator\Structure;
7
use samsonframework\routing\Route;
8
9
/**
10
 * SamsonPHP Routing module implementation.
11
 *
12
 * @package samsonphp\router
13
 */
14
class Module extends \samson\core\CompressableExternalModule
15
{
16
    /** @var string Module identifier */
17
    public $id = 'router';
18
19
    /** @var string Default controller module identifier */
20
    public $defaultModule = 'main';
21
22
    /** @var RouteCollection Routes collection */
23
    protected $routes;
24
25
    /**
26
     * Old generic "main_page" route callback searcher to match old logic.
27
     *
28
     * @return Route Default application route "/"
29
     */
30
    protected function findGenericDefaultAction()
31
    {
32
        $callback = null;
33
        // If callback is passed  - function name
34
        if (is_callable($this->defaultModule)) {
35
            // Use it as main controller callback
36
            $callback = $this->defaultModule;
37
            // Consider as module identifier is passed
38
        } elseif (isset($this->system->module_stack[$this->defaultModule])) {
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...
39
            // Try to find module universal controller action
40
            $callback = $this->system->module_stack[$this->defaultModule]->id.'#'.self::CTR_UNI;
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...
41
        }
42
43
        return new Route('/', $callback, 'main_page');
0 ignored issues
show
Bug introduced by
It seems like $callback defined by null on line 32 can also be of type null; however, samsonframework\routing\Route::__construct() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
44
    }
45
46
    /**
47
     * Module initialization.
48
     *
49
     * @param array $params Initialization parameters collection
50
     * @return bool Initialization result
51
     */
52
    public function init(array $params = array())
53
    {
54
        // Create SamsonPHP routing table from loaded modules
55
        $rg = new GenericRouteGenerator($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...
56
57
        // Generate web-application routes
58
        $this->routes = $rg->generate();
0 ignored issues
show
Documentation Bug introduced by
It seems like $rg->generate() of type object<samsonframework\routing\RouteCollection> is incompatible with the declared type object<samsonphp\router\RouteCollection> of property $routes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
59
        $this->routes->add($this->findGenericDefaultAction());
60
61
        // Create cache marker
62
        $cacheFile = $this->routes->hash().'.php';
63
64
        // If we need to refresh cache
65
        if ($this->cache_refresh($cacheFile)) {
66
            $generator = new Structure($this->routes, new \samsonphp\generator\Generator());
67
            // Generate routing logic function
68
            $routerLogic = $generator->generate();
69
70
            // Store router logic in cache
71
            file_put_contents($cacheFile, '<?php '."\n".$routerLogic);
72
        }
73
74
        require($cacheFile);
75
76
        // Subscribe to samsonphp\core routing event
77
        \samsonphp\event\Event::subscribe('core.routing', array($this, 'router'));
78
79
        // Continue initialization
80
        return parent::init($params);
81
    }
82
83
    /**
84
     * Define if HTTP request is asynchronous.
85
     *
86
     * @return bool True if request is asynchronous
87
     */
88
    public function isAsynchronousRequest()
89
    {
90
        return $_SERVER['HTTP_ACCEPT'] == '*/*'
91
        || isset($_SERVER['HTTP_SJSASYNC'])
92
        || isset($_POST['SJSASYNC']);
93
    }
94
95
    /**
96
     * Parse route parameters received from router logic function.
97
     *
98
     * @param callable $callback Route instance
99
     * @param array $receivedParameters Collection of parsed parameters
100
     * @return array Collection of route callback needed parameters
101
     */
102
    protected function parseParameters($callback, array $receivedParameters)
103
    {
104
        $parameters = array();
105
        // Parse callback signature and get parameters list
106
        if (is_callable($callback)) {
107
            $reflectionMethod = is_array($callback)
108
                ? new \ReflectionMethod($callback[0], $callback[1])
109
                : new \ReflectionFunction($callback);
110
            foreach ($reflectionMethod->getParameters() as $parameter) {
111
                $parameters[] = $parameter->getName();
112
            }
113
        }
114
115
        // Gather parsed route parameters in correct order
116
        $foundParameters = array();
117
        foreach ($parameters as $name) {
118
            // Add to parameters collection
119
            $foundParameters[] = &$receivedParameters[$name];
120
        }
121
        return $foundParameters;
122
    }
123
124
    /**
125
     * SamsonPHP core.routing event handler
126
     *
127
     * @param SystemInterface $core Pointer to core object
128
     * @param mixed $result Return value as routing result
129
     * @return bool Routing result
130
     */
131
    public function router(SystemInterface &$core, &$result)
132
    {
133
        //elapsed('Start routing');
134
        // Flag for matching SamsonPHP asynchronous requests
135
        $async = $this->isAsynchronousRequest();
136
        // Get HTTP request path
137
        $path = $_SERVER['REQUEST_URI'];
138
        // Get HTTP request method
139
        $method = $_SERVER['REQUEST_METHOD'];
140
        // Prepend HTTP request type, true - asynchronous
141
        $method = ($async ? GenericRouteGenerator::ASYNC_PREFIX : '').$method;
142
143
        $result = false;
144
145
        /** @var mixed $routeMetadata Dispatching result route metadata */
146
        if (is_array($routeMetadata = call_user_func(Core::ROUTING_LOGIC_FUNCTION, $path, $method))) {
147
            // Get callback info
148
            list($module, $method) = explode("#", $this->routes[$routeMetadata[0]]->callback);
149
            // Get module
150
            $module = $core->module($module);
151
            // Create callback
152
            $callback = array($module, $method);
153
154
            // Check if we have vaild callback
155
            if (is_callable($callback)) {
156
                // Routing result
157
                $result = call_user_func_array(
158
                    $callback,
159
                    $this->parseParameters($callback, $routeMetadata[1])
160
                );
161
162
                // Get object from callback and set it as current active core module
163
                $core->active($module);
164
165
                // If this route is asynchronous
166
                if ($async) {
167
                    // Set async response
168
                    $core->async(true);
169
170
                    // If controller action has failed
171
                    if (!isset($result['status']) || !$result['status']) {
172
                        $result['message'] = "\n" . 'Event failed: ' . $routeMetadata[0];
173
                        $result['status'] = 0;
174
                    }
175
176
                    // Encode event result as json object
177
                    echo json_encode($result, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
178
179
                    // Mark as successful
180
                    $result = true;
181
                }
182
            }
183
184
            // If no result is passed - consider success
185
            $result = $result !== false ? true : $result;
186
        }
187
188
        //elapsed('Finished routing');
189
        // Return true or false depending on $result
190
        return $result;
191
    }
192
}
193