Toro   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 25
c 1
b 0
f 0
lcom 0
cbo 1
dl 0
loc 125
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
F serve() 0 117 23
A is_xhr_request() 0 4 2
1
<?php
2
/**
3
 * Toro (router)
4
 * 
5
 * We needed to hack Toro and decided to copy it into the framework 
6
 * instead of extending it.  It seems to be abandoned so we made a good choice.
7
 * Taken from https://github.com/anandkunal/ToroPHP
8
 * @package     erdiko/core
9
 */
10
namespace erdiko\core;
11
12
class Toro
13
{
14
    public static function serve($routes)
0 ignored issues
show
Coding Style introduced by
serve uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
15
    {
16
        ToroHook::fire('before_request', compact('routes'));
17
        
18
        // Determine action (and set default)
19
        if (empty($_SERVER['REQUEST_METHOD'])) {
20
            $_SERVER['REQUEST_METHOD'] = 'GET';
21
        }
22
        $action = strtolower($_SERVER['REQUEST_METHOD']); // e.g. get, put, post, delete
23
24
        $path_info = '/';
25
        if (!empty($_SERVER['PATH_INFO'])) {
26
            $path_info = $_SERVER['PATH_INFO'];
27
        } elseif (!empty($_SERVER['ORIG_PATH_INFO']) && $_SERVER['ORIG_PATH_INFO'] !== '/index.php') {
28
            $path_info = $_SERVER['ORIG_PATH_INFO'];
29
        } else {
30
            if (!empty($_SERVER['REQUEST_URI'])) {
31
                $path_info = (strpos($_SERVER['REQUEST_URI'], '?') > 0) ?
32
                    strstr($_SERVER['REQUEST_URI'], '?', true) : $_SERVER['REQUEST_URI'];
33
            }
34
        }
35
        
36
        $discovered_handler = null;
37
        $arguments = array();
38
        
39
        if (isset($routes[$path_info])) {
40
            $discovered_handler = $routes[$path_info];
41
        } elseif ($routes) {
42
            $tokens = array(
43
                ':string' => '([a-zA-Z]+)',
44
                ':number' => '([0-9]+)',
45
                ':alpha'  => '([a-zA-Z0-9-_]+)',
46
                ':action'  => '([a-zA-Z0-9-_/]+)'
47
            );
48
49
            // Search through routes and find first match
50
            foreach ($routes as $pattern => $handler_name) {
51
                $patternRep = strtr($pattern, $tokens);
52
                if (preg_match('#^/?' . $patternRep . '/?$#', $path_info, $matches)) {
53
                    $discovered_handler = $handler_name;
54
                    $regex_matches = $matches;
55
                    $params = isset($regex_matches[1]) ? explode("/", $regex_matches[1]) : array();
56
57
                    // See if it is an action
58
                    $isAction = preg_match("/:action/", $pattern);
59
60
                    // Determine action and arguments
61
                    if (count($params) > 1 && !$isAction) {
62
                    // @todo add different parsers here...possibly pass route function in routes.json
63
                        $action .= ucfirst($params[0]);
64
                        unset($params[0]);
65
66
                        foreach ($params as $param) {
67
                            $arguments[] = $param;
68
                        }
69
                    } else {
70
                        unset($regex_matches[0]);
71
                        $arguments = $regex_matches; // Toro compatible
72
                    }
73
74
                    break;
75
                }
76
            }
77
        }
78
79
        $result = null;
80
        $handler_instance = null;
81
82
        if ($discovered_handler) {
83
            if (is_string($discovered_handler)) {
84
                $handler_instance = new $discovered_handler();
85
            } elseif (is_callable($discovered_handler)) {
86
                $handler_instance = $discovered_handler();
87
            }
88
        }
89
90
        if ($handler_instance) {
91
            if (static::is_xhr_request() && method_exists($handler_instance, $action . '_xhr')) {
0 ignored issues
show
Bug introduced by
Since is_xhr_request() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of is_xhr_request() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
92
                header('Content-type: application/json'); // @todo support xml
93
                header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
94
                header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
95
                header('Cache-Control: no-store, no-cache, must-revalidate');
96
                header('Cache-Control: post-check=0, pre-check=0', false);
97
                header("Access-Control-Allow-Origin: *"); // @todo make this a parameter?
98
                header('Pragma: no-cache');
99
                $action .= '_xhr';
100
                $handler_instance->setIsXhrRequest(1);
101
            }
102
103
            if (is_callable(array($handler_instance, $action))) {
104
                try {
105
                    $handler_instance->setPathInfo($path_info);
106
                    ToroHook::fire('before_handler', compact('routes', 'discovered_handler', 'action', 'arguments'));
107
                    $handler_instance->_before();
108
109
                    // Action call
110
                    call_user_func_array(array($handler_instance, $action), $arguments);
111
112
                    $handler_instance->_after();
113
                    $handler_instance->send(); // render the response and return the data
114
                    ToroHook::fire(
115
                        'after_handler',
116
                        compact('routes', 'discovered_handler', 'action', 'arguments', 'result')
117
                    );
118
119
                } catch (\Exception $e) {
120
                    ToroHook::fire('500', array('error' => $e->getMessage(), 'path_info' => $path_info));
121
                }
122
            } else {
123
                ToroHook::fire('404', compact('discovered_handler', 'action', 'arguments', 'path_info'));
124
            }
125
        } else {
126
            ToroHook::fire('404', compact('discovered_handler', 'action', 'arguments', 'path_info'));
127
        }
128
129
        ToroHook::fire('after_request', compact('routes', 'discovered_handler', 'action', 'arguments', 'result'));
130
    }
131
132
    private static function is_xhr_request()
0 ignored issues
show
Coding Style introduced by
is_xhr_request uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
133
    {
134
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
135
    }
136
}