route.php 5.6 KB
Newer Older
1 2 3 4 5
<?php namespace Laravel\Routing;

use Closure;
use Laravel\Arr;
use Laravel\Response;
6

7
class Route {
8 9

	/**
10 11 12 13 14 15 16
	 * The route key, including request method and URI.
	 *
	 * @var string
	 */
	public $key;

	/**
17 18 19 20 21 22 23
	 * The URIs the route responds to.
	 *
	 * @var array
	 */
	public $uris;

	/**
24 25 26 27
	 * The route callback or array.
	 *
	 * @var mixed
	 */
28
	public $callback;
29 30

	/**
31
	 * The parameters that will passed to the route callback.
32 33 34 35 36 37 38 39
	 *
	 * @var array
	 */
	public $parameters;

	/**
	 * Create a new Route instance.
	 *
40 41 42
	 * @param  string   $key
	 * @param  mixed    $callback
	 * @param  array    $parameters
43 44
	 * @return void
	 */
45
	public function __construct($key, $callback, $parameters = array())
46
	{
47 48
		$this->key = $key;
		$this->callback = $callback;
49
		$this->parameters = $parameters;
50

51 52 53 54
		// Extract each URI from the route key. Since the route key has the
		// request method, we will extract that from the string. If the URI
		// points to the root of the application, a single forward slash
		// will be returned.
55 56
		if (strpos($key, ', ') === false)
		{
57
			$this->uris = array($this->extract($this->key));
58 59
		}
		else
60
		{
61
			$this->uris = array_map(array($this, 'extract'), explode(', ', $key));
62 63
		}

64
		if ( ! $callback instanceof Closure and ! is_array($callback) and ! is_string($callback))
65 66 67
		{
			throw new \Exception('Invalid route defined for URI ['.$this->key.']');
		}
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
	 * Retrieve the URI from a given route destination.
	 *
	 * If the request is to the root of the application, a single slash
	 * will be returned, otherwise the leading slash will be removed.
	 *
	 * @param  string  $segment
	 * @return string
	 */
	protected function extract($segment)
	{
		$segment = substr($segment, strpos($segment, ' ') + 1);

		return ($segment !== '/') ? trim($segment, '/') : $segment;
	}

	/**
	 * Call a given route and return the route's response.
	 *
	 * @return Response
	 */
	public function call()
	{
		// Since "before" filters can halt the request cycle, we will return
		// any response from the before filters. Allowing filters to halt the
		// request cycle makes tasks like authorization convenient.
96 97 98 99 100 101
		//
		// The route is responsible for running the global filters, and any
		// filters defined on the route itself. Since all incoming requests
		// come through a route (either defined or ad-hoc), it makes sense
		// to let the route handle the global filters. If the route uses
		// a controller, the controller will only call its own filters.
102 103
		$before = array_merge(array('before'), $this->filters('before'));

104
		$response = Filter::run($before, array(), true);
105

106
		if (is_null($response) and ! is_null($response = $this->response()))
107 108 109
		{
			if ($response instanceof Delegate)
			{
110
				$response = Controller::call($response->destination, $this->parameters);
111
			}
112
		}
113

114 115 116 117 118 119 120 121
		// If we still don't have a response, we're out of options and
		// will use the 404 response. We will still let the response
		// hit the after filter in case the developer is doing any
		// logging or other work where every response is needed.
		if (is_null($response))
		{
			$response = Response::error('404');
		}
122

123 124 125 126 127 128 129
		// The after filter and the framework expects all responses to
		// be instances of the Response class. If the route did not
		// return an instsance of Response, we will make on now.
		if ( ! $response instanceof Response)
		{
			$response = new Response($response);
		}
130

131
		$filters = array_merge($this->filters('after'), array('after'));
132

133
		Filter::run($filters, array($response));
134

135
		return $response;
136 137 138
	}

	/**
139
	 * Call the closure defined for the route, or get the route delegator.
140
	 *
141 142 143 144
	 * Note that this method differs from the "call" method in that it does
	 * not resolve the controller or prepare the response. Delegating to
	 * controller's is handled by the "call" method.
	 *
145 146
	 * @return mixed
	 */
147
	protected function response()
148
	{
149 150 151 152
		if ($this->callback instanceof Closure)
		{
			return call_user_func_array($this->callback, $this->parameters);
		}
153 154 155
		// If the route is an array, we will return the first value with a
		// key of "uses", or the first instance of a Closure. If the value
		// is a string, the route is delegating the responsibility for
156
		// for handling the request to a controller.
157 158
		elseif (is_array($this->callback))
		{
159 160
			$callback = Arr::first($this->callback, function($key, $value)
			{
161
				return $key == 'uses' or $value instanceof Closure;
162
			});
Taylor Otwell committed
163

164 165 166 167 168 169 170 171
			if ($callback instanceof Closure)
			{
				return call_user_func_array($callback, $this->parameters);
			}
			else
			{
				return new Delegate($callback);
			}
172 173 174
		}
		elseif (is_string($this->callback))
		{
Taylor Otwell committed
175
			return new Delegate($this->callback);
176
		}
177
	}
178

179
	/**
180
	 * Get an array of filter names defined for the route.
181
	 *
182
	 * @param  string  $name
183 184
	 * @return array
	 */
185
	public function filters($name)
186
	{
187 188
		if (is_array($this->callback) and isset($this->callback[$name]))
		{
189 190 191
			$filters = $this->callback[$name];

			return (is_string($filters)) ? explode('|', $filters) : (array) $filters;
192 193 194
		}

		return array();
195 196
	}

197
	/**
198
	 * Determine if the route has a given name.
199
	 *
200
	 * @param  string  $name
201 202 203 204
	 * @return bool
	 */
	public function is($name)
	{
205
		return is_array($this->callback) and Arr::get($this->callback, 'name') === $name;
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	}

	/**
	 * Determine if the route handles a given URI.
	 *
	 * @param  string  $uri
	 * @return bool
	 */
	public function handles($uri)
	{
		return in_array($uri, $this->uris);
	}

	/**
	 * Magic Method to handle dynamic method calls to determine the name of the route.
	 */
	public function __call($method, $parameters)
	{
224
		if (strpos($method, 'is_') === 0) return $this->is(substr($method, 3));
225 226

		throw new \Exception("Call to undefined method [$method] on Route class.");
227 228
	}

229
}