/www/slight

To get this branch, use:
bzr branch http://bzr.ed.am/www/slight
1 by Tim Marston
initial commit
1
<?php
2
/**
3
 * Flight: An extensible micro-framework.
4
 *
5
 * @copyright   Copyright (c) 2011, Mike Cao <mike@mikecao.com>
6
 * @license     MIT, http://flightphp.com/license
7
 */
8
9
namespace flight\net;
10
11
/**
12
 * The Route class is responsible for routing an HTTP request to
13
 * an assigned callback function. The Router tries to match the
14
 * requested URL against a series of URL patterns.
15
 */
16
class Route {
17
    /**
18
     * @var string URL pattern
19
     */
20
    public $pattern;
21
22
    /**
23
     * @var mixed Callback function
24
     */
25
    public $callback;
26
27
    /**
28
     * @var array HTTP methods
29
     */
30
    public $methods = array();
31
32
    /**
33
     * @var array Route parameters
34
     */
35
    public $params = array();
36
37
    /**
38
     * @var string Matching regular expression
39
     */
40
    public $regex;
41
42
    /**
43
     * @var string URL splat content
44
     */
45
    public $splat = '';
46
47
    /**
48
     * @var boolean Pass self in callback parameters
49
     */
50
    public $pass = false;
51
52
    /**
53
     * Constructor.
54
     *
55
     * @param string $pattern URL pattern
56
     * @param mixed $callback Callback function
57
     * @param array $methods HTTP methods
58
     * @param boolean $pass Pass self in callback parameters
59
     */
60
    public function __construct($pattern, $callback, $methods, $pass) {
61
        $this->pattern = $pattern;
62
        $this->callback = $callback;
63
        $this->methods = $methods;
64
        $this->pass = $pass;
65
    }
66
67
    /**
68
     * Checks if a URL matches the route pattern. Also parses named parameters in the URL.
69
     *
70
     * @param string $url Requested URL
71
     * @return boolean Match status
72
     */
73
    public function matchUrl($url) {
74
        // Wildcard or exact match
75
        if ($this->pattern === '*' || $this->pattern === $url) {
76
            if ($this->pass) {
77
                $this->params[] = $this;
78
            }
79
            return true;
80
        }
81
82
        $ids = array();
83
        $last_char = substr($this->pattern, -1);
84
85
        // Get splat
86
        if ($last_char === '*') {
87
            $n = 0;
88
            $len = strlen($url);
89
            $count = substr_count($this->pattern, '/');
90
91
            for ($i = 0; $i < $len; $i++) {
92
                if ($url[$i] == '/') $n++;
93
                if ($n == $count) break;
94
            }
95
96
            $this->splat = (string)substr($url, $i+1);
97
        }
98
99
        // Build the regex for matching
100
        $regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern);
101
102
        $regex = preg_replace_callback(
103
            '#@([\w]+)(:([^/\(\)]*))?#',
104
            function($matches) use (&$ids) {
105
                $ids[$matches[1]] = null;
106
                if (isset($matches[3])) {
107
                    return '(?P<'.$matches[1].'>'.$matches[3].')';
108
                }
109
                return '(?P<'.$matches[1].'>[^/\?]+)';
110
            },
111
            $regex
112
        );
113
114
        // Fix trailing slash
115
        if ($last_char === '/') {
116
            $regex .= '?';
117
        }
118
        // Allow trailing slash
119
        else {
120
            $regex .= '/?';
121
        }
122
123
        // Attempt to match route and named parameters
124
        if (preg_match('#^'.$regex.'(?:\?.*)?$#i', $url, $matches)) {
125
            foreach ($ids as $k => $v) {
126
                $this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
127
            }
128
129
            if ($this->pass) {
130
                $this->params[] = $this;
131
            }
132
133
            $this->regex = $regex;
134
135
            return true;
136
        }
137
138
        return false;
139
    }
140
141
    /**
142
     * Checks if an HTTP method matches the route methods.
143
     *
144
     * @param string $method HTTP method
145
     * @return bool Match status
146
     */
147
    public function matchMethod($method) {
148
        return count(array_intersect(array($method, '*'), $this->methods)) > 0;
149
    }
150
}