/
url.php
290 lines (264 loc) · 8.44 KB
/
url.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
<?php
/**
* @package Habari
*
*/
/**
* URL class which handles creation of URLs based on the rewrite
* rules in the database. Uses rules to construct pretty URLs for use
* by the system and especially the theme's template engine
*
*/
class URL extends Singleton
{
// static collection of rules ( pulled from RewriteController )
private $rules = null;
private $matched_rule = null;
private static $stub = null;
/**
* Enables singleton working properly
*
* @see singleton.php
*/
protected static function instance()
{
return self::getInstanceOf( __CLASS__ );
}
/**
* A simple caching mechanism to avoid reloading rule array
*/
private function load_rules()
{
if ( URL::instance()->rules != null ) {
return;
}
URL::instance()->rules = RewriteRules::get_active();
}
/**
* Get the matched RewriteRule that was matched in parse().
*
* @return RewriteRule matched rule, or null
*/
public static function get_matched_rule()
{
return URL::instance()->matched_rule;
}
/**
* Get the active RewriteRules that are cached in self::load_rules().
*
* @return array RewriteRules active rules, or null
*/
public static function get_active_rules()
{
return URL::instance()->rules;
}
/**
* Cause the matched rule to be unset in the case of a 404
*
* @return RewriteRule A rewrite rule that represents a 404 error - no match on the URL requested
*/
public static function set_404()
{
if ( empty( URL::instance()->matched_rule ) || ( URL::instance()->matched_rule->name != 'display_404' ) ) {
$rule = RewriteRules::by_name( 'display_404' );
URL::instance()->matched_rule = reset( $rule );
URL::instance()->matched_rule->match( self::$stub );
}
return URL::instance()->matched_rule;
}
/**
* Match a URL/URI against the rewrite rules stored in the DB.
* This method is used by the Controller class for parsing
* requests, and by other classes, such as Pingback, which
* uses it to determine the post slug for a given URL.
*
* Returns the matched RewriteRule object, or false.
*
* @param string $from_url URL string to parse
* @return RewriteRule matched rule, or false
*/
public static function parse( $from_url )
{
$base_url = Site::get_path( 'base', true );
/*
* Strip out the base URL from the requested URL
* but only if the base URL isn't /
*/
if ( strpos( $from_url, $base_url ) === 0 ) {
$from_url = MultiByte::substr( $from_url, MultiByte::strlen( $base_url ) );
}
/* Trim off any leading or trailing slashes */
$from_url = trim( $from_url, '/' );
/* Remove the querystring from the URL */
if ( MultiByte::strpos( $from_url, '?' ) !== false ) {
list( $from_url, )= explode( '?', $from_url );
}
$url = URL::instance();
$url->load_rules(); // Cached in singleton
/*
* Run the stub through the regex matcher
*/
$pattern_matches = array();
self::$stub = $from_url;
foreach ( $url->rules as $rule ) {
if ( $rule->match( $from_url ) ) {
$url->matched_rule = $rule;
/* Stop processing at first matched rule... */
return $rule;
}
}
return false;
}
/**
* Builds the required pretty URL given a supplied
* rule name and a set of placeholder replacement
* values and returns the built URL.
*
* <code>
* URL::get( 'display_entries_by_date', array(
* 'year' => '2000',
* 'month' => '05',
* 'day' => '01',
* ) );
* </code>
*
* @param mixed $rule_names string name of the rule or array of rules which would build the URL
* @param mixed $args (optional) array or object of placeholder replacement values
* @param boolean $useall If true (default), then all passed parameters that are not part of the built URL are tacked onto the URL as querystring
* @param boolean $prepend_site If true (default), a full URL is returned, if false, only the path part of the URL is returned
*/
public static function get( $rule_names = '', $args = array(), $useall = true, $noamp = false, $prepend_site = true )
{
$args = self::extract_args( $args );
$url = URL::instance();
if ( $rule_names == '' ) {
// Retrieve current matched RewriteRule
$selectedrule = $url->get_matched_rule();
// Retrieve arguments name the RewriteRule can use to build a URL.
$rr_named_args = $selectedrule->named_args;
$rr_args = array_merge( $rr_named_args['required'], $rr_named_args['optional'] );
// For each argument, check if the handler_vars array has that argument and if it does, use it.
$rr_args_values = array();
foreach ( $rr_args as $rr_arg ) {
if ( !isset( $args[$rr_arg] ) ) {
$rr_arg_value = Controller::get_var( $rr_arg );
if ( $rr_arg_value != '' ) {
$args[$rr_arg] = $rr_arg_value;
}
}
}
}
else {
$url->load_rules();
$selectedrule = null;
if ( !is_array( $rule_names ) ) {
$rule_names = array( $rule_names );
}
foreach ( $rule_names as $rule_name ) {
if ( $rules = $url->rules->by_name( $rule_name ) ) {
$rating = null;
foreach ( $rules as $rule ) {
$newrating = $rule->arg_match( $args );
// Is the rating perfect?
if ( $rating == 0 ) {
$selectedrule = $rule;
break;
}
if ( empty( $rating ) || ( $newrating < $rating ) ) {
$rating = $newrating;
$selectedrule = $rule;
}
}
if ( isset( $selectedrule ) ) {
break;
}
}
}
}
if ( $selectedrule instanceOf RewriteRule ) {
$return_url = $selectedrule->build( $args, $useall, $noamp );
if ( $prepend_site ) {
return Site::get_url( 'habari', true ) . $return_url;
}
else {
return $return_url;
}
}
else {
$error = new Exception();
$error_trace = $error->getTrace();
// Since URL::out() calls this function, the index 0 is URL::get() which is not the proper failing call.
if ( isset( $error_trace[1]['class'] ) && isset( $error_trace[1]['function'] ) && ( $error_trace[1]['class'] == 'URL' ) && ( $error_trace[1]['function'] == 'out' ) ) {
$error_args = $error_trace[1];
}
// When calling URL::get() directly, the index 0 is the proper file and line of the failing call.
else {
$error_args = $error_trace[0];
}
EventLog::log( _t( 'Could not find a rule matching the following names: %s. File: %s (line %s)', array( implode( ', ', $rule_names ), $error_args['file'], $error_args['line'] ) ), 'notice', 'rewriterules', 'habari' );
}
}
/**
* Helper wrapper function. Outputs the URL via echo.
* @param string $rule_name name of the rule which would build the URL
* @param array $args (optional) array of placeholder replacement values
* @param boolean $useall If true (default), then all passed parameters that are not part of the built URL are tacked onto the URL as querystring
* @param boolean $prepend_site If true (default), a full URL is returned, if false, only the path part of the URL is returned
*/
public static function out( $rule_name = null, $args = array(), $useall = true, $noamp = true, $prepend_site = true )
{
echo URL::get( $rule_name, $args, $useall, $noamp, $prepend_site );
}
/**
* Get a fully-qualified URL from a filesystem path
*
* @param string $path The filesystem path
* @param bool whether to include a trailing slash. Default: No
* @param bool whether to leave a filename on the URL. Default: No
* @return string URL
*/
public static function get_from_filesystem( $path, $trail = false, $preserve_file = false )
{
if ( !$preserve_file ) {
$path = dirname( $path );
}
$url = Site::get_url( 'habari' ) . MultiByte::substr( $path, MultiByte::strlen( HABARI_PATH ) );
// Replace windows paths with forward slashes
$url = str_replace( '\\', '/', $url );
$url .= Utils::trail($trail);
return $url;
}
/**
* Extract the possible arguments to use in the URL from the passed variable
* @param mixed $args An array of values or a URLProperties object with properties to use in the construction of a URL
* @return array Properties to use to construct a URL
*/
public static function extract_args( $args, $prefix = '' )
{
if ( is_object( $args ) ) {
if ( $args instanceof URLProperties ) {
$args = $args->get_url_args();
}
else {
$args_ob = array();
foreach ( $args as $key => $value ) {
$args_ob[$key] = $value;
}
$args = $args_ob;
}
}
else {
$args = Utils::get_params( $args );
}
// can this be done with array_walk?
if ( $prefix && $args ) {
$args_out = array();
foreach ( $args as $key => $value ) {
$args_out[$prefix.$key] = $value;
}
$args = $args_out;
}
return $args;
}
}
?>