Route::getPattern()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Route
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Route;
16
17
use Phossa2\Route\Message\Message;
18
use Phossa2\Route\Exception\LogicException;
19
use Phossa2\Route\Traits\HandlerAwareTrait;
20
use Phossa2\Route\Interfaces\RouteInterface;
21
use Phossa2\Route\Interfaces\HandlerAwareInterface;
22
use Phossa2\Event\EventableExtensionCapableAbstract;
23
24
/**
25
 * Route
26
 *
27
 * @package Phossa2\Route
28
 * @author  Hong Zhang <[email protected]>
29
 * @see     EventableExtensionCapableAbstract
30
 * @see     RouteInterface
31
 * @see     HandlerAwareInterface
32
 * @version 2.0.0
33
 * @since   2.0.0 added
34
 */
35
class Route extends EventableExtensionCapableAbstract implements RouteInterface, HandlerAwareInterface
36
{
37
    use HandlerAwareTrait;
38
39
    /**#@+
40
     * Route related events
41
     *
42
     * @const
43
     */
44
45
    // before executing handler on this route
46
    const EVENT_BEFORE_HANDLER = 'route.handler.before';
47
48
    // after executing handler on this route
49
    const EVENT_AFTER_HANDLER = 'route.handler.before';
50
51
    /**#@-*/
52
53
    /**
54
     * pattern to match against
55
     *
56
     * @var    string
57
     * @access protected
58
     */
59
    protected $pattern;
60
61
    /**
62
     * allowed http methods
63
     *
64
     * @var    string[]
65
     * @access protected
66
     */
67
    protected $methods;
68
69
    /**
70
     * default values for placeholders in the pattern
71
     *
72
     * @var    array
73
     * @access protected
74
     */
75
    protected $defaults = [];
76
77
    /**
78
     * Constructor
79
     *
80
     * @param  string|string[] $httpMethod 'GET|POST' allowed for this route.
81
     * @param  string $pattern matching pattern
82
     * @param  mixed $handler for Status::OK status
83
     * @param  array $defaultValues default value for placeholders
84
     * @throws LogicException if pattern malformed
85
     * @access public
86
     */
87
    public function __construct(
88
        $httpMethod,
89
        /*# string */ $pattern,
90
        $handler,
91
        array $defaultValues = []
92
    ) {
93
        $this->setMethods($httpMethod)
94
             ->setPattern($pattern)
95
             ->addHandler($handler)
96
             ->setDefault($defaultValues);
97
    }
98
99
    /**
100
     * {@inheritDoc}
101
     */
102
    public function setPattern(/*# string */ $pattern)
103
    {
104
        // pattern checking
105
        $this->validatePattern($pattern);
106
107
        // check default values in the pattern
108
        if (false !== strpos($pattern, '=')) {
109
            $pattern = $this->extractDefaultValues($pattern);
110
        }
111
112
        $this->pattern = $pattern;
113
        return $this;
114
    }
115
116
    /**
117
     * {@inheritDoc}
118
     */
119
    public function getPattern()/*# : string */
120
    {
121
        return $this->pattern;
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127
    public function setMethods($methods)
128
    {
129
        $this->methods = is_string($methods) ?
130
            preg_split('~[^A-Z]+~', strtoupper($methods), -1, PREG_SPLIT_NO_EMPTY) :
131
            array_map('strtoupper', $methods);
132
        return $this;
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138
    public function getMethods()/*# : array */
139
    {
140
        return $this->methods;
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146
    public function setDefault(array $values)
147
    {
148
        $this->defaults = array_replace($this->defaults, $values);
149
        return $this;
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155
    public function getDefault()/*# : array */
156
    {
157
        return $this->defaults;
158
    }
159
160
    /**
161
     * Validate the pattern
162
     *
163
     * @param  string $pattern
164
     * @throws LogicException
165
     * @access protected
166
     */
167
    protected function validatePattern(/*# string */ $pattern)
168
    {
169
        if (!is_string($pattern) ||
170
            substr_count($pattern, '[') !== substr_count($pattern, ']') ||
171
            substr_count($pattern, '{') !== substr_count($pattern, '}')
172
        ) {
173
            throw new LogicException(
174
                Message::get(Message::RTE_PATTERN_MALFORM, $pattern),
175
                Message::RTE_PATTERN_MALFORM
176
            );
177
        }
178
    }
179
180
    /**
181
     * Extract default values from the pattern
182
     *
183
     * @param  string $pattern
184
     * @return string
185
     * @access protected
186
     */
187
    protected function extractDefaultValues(
188
        /*# string */ $pattern
189
    )/*# : string */ {
190
        $regex = '~\{([a-zA-Z][a-zA-Z0-9_]*+)[^\}]*(=[a-zA-Z0-9._]++)\}~';
191
        if (preg_match_all($regex, $pattern, $matches, \PREG_SET_ORDER)) {
192
            $srch = $repl = $vals = [];
193
            foreach ($matches as $m) {
194
                $srch[] = $m[0];
195
                $repl[] = str_replace($m[2], '', $m[0]);
196
                $vals[$m[1]] = substr($m[2], 1);
197
            }
198
            $this->setDefault($vals);
199
            return str_replace($srch, $repl, $pattern);
200
        }
201
        return $pattern;
202
    }
203
}
204