1: <?php namespace Laravel; use Laravel\Routing\Router, Laravel\Routing\Route;
2:
3: class URL {
4:
5: /**
6: * The cached base URL.
7: *
8: * @var string
9: */
10: public static $base;
11:
12: /**
13: * Get the full URI including the query string.
14: *
15: * @return string
16: */
17: public static function full()
18: {
19: return static::to(URI::full());
20: }
21:
22: /**
23: * Get the full URL for the current request.
24: *
25: * @return string
26: */
27: public static function current()
28: {
29: return static::to(URI::current(), null, false, false);
30: }
31:
32: /**
33: * Get the URL for the application root.
34: *
35: * @param bool $https
36: * @return string
37: */
38: public static function home($https = null)
39: {
40: $route = Router::find('home');
41:
42: // If a route named "home" exists, we'll route to that instead of using
43: // the single slash root URI. This allows the HTTPS attribute to be
44: // respected instead of being hard-coded in the redirect.
45: if ( ! is_null($route))
46: {
47: return static::to_route('home');
48: }
49:
50: return static::to('/', $https);
51: }
52:
53: /**
54: * Get the base URL of the application.
55: *
56: * @return string
57: */
58: public static function base()
59: {
60: if (isset(static::$base)) return static::$base;
61:
62: $base = 'http://localhost';
63:
64: // If the application's URL configuration is set, we will just use that
65: // instead of trying to guess the URL from the $_SERVER array's host
66: // and script variables as this is a more reliable method.
67: if (($url = Config::get('application.url')) !== '')
68: {
69: $base = $url;
70: }
71: else
72: {
73: $base = Request::foundation()->getRootUrl();
74: }
75:
76: return static::$base = $base;
77: }
78:
79: /**
80: * Generate an application URL.
81: *
82: * <code>
83: * // Create a URL to a location within the application
84: * $url = URL::to('user/profile');
85: *
86: * // Create a HTTPS URL to a location within the application
87: * $url = URL::to('user/profile', true);
88: * </code>
89: *
90: * @param string $url
91: * @param bool $https
92: * @param bool $asset
93: * @param bool $locale
94: * @return string
95: */
96: public static function to($url = '', $https = null, $asset = false, $locale = true)
97: {
98: // If the given URL is already valid or begins with a hash, we'll just return
99: // the URL unchanged since it is already well formed. Otherwise we will add
100: // the base URL of the application and return the full URL.
101: if (static::valid($url) or starts_with($url, '#'))
102: {
103: return $url;
104: }
105:
106: // Unless $https is specified (true or false), we maintain the current request
107: // security for any new links generated. So https for all secure links.
108: if (is_null($https)) $https = Request::secure();
109:
110: $root = static::base();
111:
112: if ( ! $asset)
113: {
114: $root .= '/'.Config::get('application.index');
115: }
116:
117: $languages = Config::get('application.languages');
118:
119: if ( ! $asset and $locale and count($languages) > 0)
120: {
121: if (in_array($default = Config::get('application.language'), $languages))
122: {
123: $root = rtrim($root, '/').'/'.$default;
124: }
125: }
126:
127: // Since SSL is not often used while developing the application, we allow the
128: // developer to disable SSL on all framework generated links to make it more
129: // convenient to work with the site while developing locally.
130: if ($https and Config::get('application.ssl'))
131: {
132: $root = preg_replace('~http://~', 'https://', $root, 1);
133: }
134: else
135: {
136: $root = preg_replace('~https://~', 'http://', $root, 1);
137: }
138:
139: return rtrim($root, '/').'/'.ltrim($url, '/');
140: }
141:
142: /**
143: * Generate an application URL with HTTPS.
144: *
145: * @param string $url
146: * @return string
147: */
148: public static function to_secure($url = '')
149: {
150: return static::to($url, true);
151: }
152:
153: /**
154: * Generate a URL to a controller action.
155: *
156: * <code>
157: * // Generate a URL to the "index" method of the "user" controller
158: * $url = URL::to_action('user@index');
159: *
160: * // Generate a URL to http://example.com/user/profile/taylor
161: * $url = URL::to_action('user@profile', array('taylor'));
162: * </code>
163: *
164: * @param string $action
165: * @param array $parameters
166: * @return string
167: */
168: public static function to_action($action, $parameters = array())
169: {
170: // This allows us to use true reverse routing to controllers, since
171: // URIs may be setup to handle the action that do not follow the
172: // typical Laravel controller URI conventions.
173: $route = Router::uses($action);
174:
175: if ( ! is_null($route))
176: {
177: return static::explicit($route, $action, $parameters);
178: }
179: // If no route was found that handled the given action, we'll just
180: // generate the URL using the typical controller routing setup
181: // for URIs and turn SSL to false by default.
182: else
183: {
184: return static::convention($action, $parameters);
185: }
186: }
187:
188: /**
189: * Generate an action URL from a route definition
190: *
191: * @param array $route
192: * @param string $action
193: * @param array $parameters
194: * @return string
195: */
196: protected static function explicit($route, $action, $parameters)
197: {
198: $https = array_get(current($route), 'https', null);
199:
200: return static::to(static::transpose(key($route), $parameters), $https);
201: }
202:
203: /**
204: * Generate an action URI by convention.
205: *
206: * @param string $action
207: * @param array $parameters
208: * @return string
209: */
210: protected static function convention($action, $parameters)
211: {
212: list($bundle, $action) = Bundle::parse($action);
213:
214: $bundle = Bundle::get($bundle);
215:
216: // If a bundle exists for the action, we will attempt to use its "handles"
217: // clause as the root of the generated URL, as the bundle can only handle
218: // URIs that begin with that string and no others.
219: $root = $bundle['handles'] ?: '';
220:
221: $parameters = implode('/', $parameters);
222:
223: // We'll replace both dots and @ signs in the URI since both are used
224: // to specify the controller and action, and by convention should be
225: // translated into URI slashes for the URL.
226: $uri = $root.'/'.str_replace(array('.', '@'), '/', $action);
227:
228: $uri = static::to(str_finish($uri, '/').$parameters);
229:
230: return trim($uri, '/');
231: }
232:
233: /**
234: * Generate an application URL to an asset.
235: *
236: * @param string $url
237: * @param bool $https
238: * @return string
239: */
240: public static function to_asset($url, $https = null)
241: {
242: if (static::valid($url) or static::valid('http:'.$url)) return $url;
243:
244: // If a base asset URL is defined in the configuration, use that and don't
245: // try and change the HTTP protocol. This allows the delivery of assets
246: // through a different server or third-party content delivery network.
247: if ($root = Config::get('application.asset_url', false))
248: {
249: return rtrim($root, '/').'/'.ltrim($url, '/');
250: }
251:
252: $url = static::to($url, $https, true);
253:
254: // Since assets are not served by Laravel, we do not need to come through
255: // the front controller. So, we'll remove the application index specified
256: // in the application config from the generated URL.
257: if (($index = Config::get('application.index')) !== '')
258: {
259: $url = str_replace($index.'/', '', $url);
260: }
261:
262: return $url;
263: }
264:
265: /**
266: * Generate a URL from a route name.
267: *
268: * <code>
269: * // Create a URL to the "profile" named route
270: * $url = URL::to_route('profile');
271: *
272: * // Create a URL to the "profile" named route with wildcard parameters
273: * $url = URL::to_route('profile', array($username));
274: * </code>
275: *
276: * @param string $name
277: * @param array $parameters
278: * @return string
279: */
280: public static function to_route($name, $parameters = array())
281: {
282: if (is_null($route = Routing\Router::find($name)))
283: {
284: throw new \Exception("Error creating URL for undefined route [$name].");
285: }
286:
287: // To determine whether the URL should be HTTPS or not, we look for the "https"
288: // value on the route action array. The route has control over whether the URL
289: // should be generated with an HTTPS protocol string or just HTTP.
290: $https = array_get(current($route), 'https', null);
291:
292: $uri = trim(static::transpose(key($route), $parameters), '/');
293:
294: return static::to($uri, $https);
295: }
296:
297: /**
298: * Get the URL to switch language, keeping the current page or not
299: *
300: * @param string $language The new language
301: * @param boolean $reset Whether navigation should be reset
302: * @return string An URL
303: */
304: public static function to_language($language, $reset = false)
305: {
306: // Get the url to use as base
307: $url = $reset ? URL::home() : URL::to(URI::current());
308:
309: // Validate the language
310: if (!in_array($language, Config::get('application.languages')))
311: {
312: return $url;
313: }
314:
315: // Get the language we're switching from and the one we're going to
316: $from = '/'.Config::get('application.language').'/';
317: $to = '/'.$language.'/';
318:
319: return str_replace($from, $to, $url);
320: }
321:
322: /**
323: * Substitute the parameters in a given URI.
324: *
325: * @param string $uri
326: * @param array $parameters
327: * @return string
328: */
329: public static function transpose($uri, $parameters)
330: {
331: // Spin through each route parameter and replace the route wildcard segment
332: // with the corresponding parameter passed to the method. Afterwards, we'll
333: // replace all of the remaining optional URI segments.
334: foreach ((array) $parameters as $parameter)
335: {
336: if ( ! is_null($parameter))
337: {
338: $uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1);
339: }
340: }
341:
342: // If there are any remaining optional place-holders, we'll just replace
343: // them with empty strings since not every optional parameter has to be
344: // in the array of parameters that were passed to us.
345: $uri = preg_replace('/\(.+?\)/', '', $uri);
346:
347: return trim($uri, '/');
348: }
349:
350: /**
351: * Determine if the given URL is valid.
352: *
353: * @param string $url
354: * @return bool
355: */
356: public static function valid($url)
357: {
358: if (starts_with($url, '//')) return true;
359:
360: return filter_var($url, FILTER_VALIDATE_URL) !== false;
361: }
362:
363: }
364: