the whole shebang

This commit is contained in:
2014-11-25 16:42:40 +01:00
parent 7f74c0613e
commit ab1334c0cf
3686 changed files with 496409 additions and 1 deletions

View File

@@ -0,0 +1,158 @@
<?php namespace Illuminate\Routing\Console;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Illuminate\Routing\Generators\ControllerGenerator;
class MakeControllerCommand extends Command {
/**
* The console command name.
*
* @var string
*/
protected $name = 'controller:make';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new resourceful controller';
/**
* The controller generator instance.
*
* @var \Illuminate\Routing\Generators\ControllerGenerator
*/
protected $generator;
/**
* The path to the controller directory.
*
* @var string
*/
protected $path;
/**
* Create a new make controller command instance.
*
* @param \Illuminate\Routing\Generators\ControllerGenerator $generator
* @param string $path
* @return void
*/
public function __construct(ControllerGenerator $generator, $path)
{
parent::__construct();
$this->path = $path;
$this->generator = $generator;
}
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$this->generateController();
}
/**
* Generate a new resourceful controller file.
*
* @return void
*/
protected function generateController()
{
$controller = $this->input->getArgument('name');
// Once we have the controller and resource that we are going to be generating
// we will grab the path and options. We allow the developers to include or
// exclude given methods from the resourceful controllers we're building.
$path = $this->getPath();
$options = $this->getBuildOptions();
// Finally, we're ready to generate the actual controller file on disk and let
// the developer start using it. The controller will be stored in the right
// place based on the naemspace of this controller specified by commands.
$this->generator->make($controller, $path, $options);
$this->info('Controller created successfully!');
}
/**
* Get the path in which to store the controller.
*
* @return string
*/
protected function getPath()
{
if ( ! is_null($this->input->getOption('path')))
{
return $this->laravel['path.base'].'/'.$this->input->getOption('path');
}
return $this->path;
}
/**
* Get the options for controller generation.
*
* @return array
*/
protected function getBuildOptions()
{
$only = $this->explodeOption('only');
$except = $this->explodeOption('except');
return compact('only', 'except');
}
/**
* Get and explode a given input option.
*
* @param string $name
* @return array
*/
protected function explodeOption($name)
{
$option = $this->input->getOption($name);
return is_null($option) ? array() : explode(',', $option);
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return array(
array('name', InputArgument::REQUIRED, 'The name of the controller class'),
);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return array(
array('only', null, InputOption::VALUE_OPTIONAL, 'The methods that should be included'),
array('except', null, InputOption::VALUE_OPTIONAL, 'The methods that should be excluded'),
array('path', null, InputOption::VALUE_OPTIONAL, 'Where to place the controller'),
);
}
}

View File

@@ -0,0 +1,77 @@
<?php namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\Controllers\FilterParser;
use Illuminate\Routing\Console\MakeControllerCommand;
use Illuminate\Routing\Generators\ControllerGenerator;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
class ControllerServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerParser();
$this->registerGenerator();
$this->commands('command.controller.make');
}
/**
* Register the filter parser instance.
*
* @return void
*/
protected function registerParser()
{
$this->app['filter.parser'] = $this->app->share(function($app)
{
return new FilterParser;
});
}
/**
* Register the controller generator command.
*
* @return void
*/
protected function registerGenerator()
{
$this->app['command.controller.make'] = $this->app->share(function($app)
{
// The controller generator is responsible for building resourceful controllers
// quickly and easily for the developers via the Artisan CLI. We'll go ahead
// and register this command instances in this container for registration.
$path = $app['path'].'/controllers';
$generator = new ControllerGenerator($app['files']);
return new MakeControllerCommand($generator, $path);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array(
'filter.parser', 'command.controller.make'
);
}
}

View File

@@ -0,0 +1,3 @@
<?php namespace Illuminate\Routing\Controllers;
class After extends Filter {}

View File

@@ -0,0 +1,3 @@
<?php namespace Illuminate\Routing\Controllers;
class Before extends Filter {}

View File

@@ -0,0 +1,305 @@
<?php namespace Illuminate\Routing\Controllers;
use Closure;
use ReflectionClass;
use Illuminate\Routing\Router;
use Illuminate\Container\Container;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class Controller {
/**
* The controller filter parser.
*
* @var \Illuminate\Routing\FilterParser
*/
protected $filterParser;
/**
* The filters that have been specified.
*
* @var array
*/
protected $filters = array();
/**
* The inline Closure defined filters.
*
* @var array
*/
protected $callbackFilters = array();
/**
* The layout used by the controller.
*
* @var \Illuminate\View\View
*/
protected $layout;
/**
* Register a new "before" filter on the controller.
*
* @param string $filter
* @param array $options
* @return void
*/
public function beforeFilter($filter, array $options = array())
{
$options = $this->prepareFilter($filter, $options);
$this->filters[] = new Before($options);
}
/**
* Register a new "after" filter on the controller.
*
* @param string $filter
* @param array $options
* @return void
*/
public function afterFilter($filter, array $options = array())
{
$options = $this->prepareFilter($filter, $options);
$this->filters[] = new After($options);
}
/**
* Prepare a filter and return the options.
*
* @param string $filter
* @param array $options
* @return array
*/
protected function prepareFilter($filter, $options)
{
// When the given filter is a Closure, we will store it off in an array of the
// callback filters, keyed off the object hash of these Closures and we can
// easily retrieve it if a filter is determined to apply to this request.
if ($filter instanceof Closure)
{
$options['run'] = $hash = spl_object_hash($filter);
$this->callbackFilters[$hash] = $filter;
}
else
{
$options['run'] = $filter;
}
return $options;
}
/**
* Execute an action on the controller.
*
* @param \Illuminate\Container\Container $container
* @param \Illuminate\Routing\Router $router
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction(Container $container, Router $router, $method, $parameters)
{
$this->filterParser = $container['filter.parser'];
// If no response was returned from the before filters, we'll call the regular
// action on the controller and prepare the response. Then we will call the
// after filters on the controller to wrap up any last minute processing.
$response = $this->callBeforeFilters($router, $method);
$this->setupLayout();
if (is_null($response))
{
$response = $this->callMethod($method, $parameters);
}
// If no response is returned from the controller action and a layout is being
// used we will assume we want to just return the layout view as any nested
// views were probably bound on this view during this controller actions.
if (is_null($response) and ! is_null($this->layout))
{
$response = $this->layout;
}
return $this->processResponse($router, $method, $response);
}
/**
* Call the given action with the given parameters.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
protected function callMethod($method, $parameters)
{
return call_user_func_array(array($this, $method), $parameters);
}
/**
* Process a controller action response.
*
* @param \Illuminate\Routing\Router $router
* @param string $method
* @param mixed $response
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function processResponse($router, $method, $response)
{
$request = $router->getRequest();
// The after filters give the developers one last chance to do any last minute
// processing on the response. The response has already been converted to a
// full Response object and will also be handed off to the after filters.
$response = $router->prepare($response, $request);
$this->callAfterFilters($router, $method, $response);
return $response;
}
/**
* Call the before filters on the controller.
*
* @param \Illuminate\Routing\Router $router
* @param string $method
* @return mixed
*/
protected function callBeforeFilters($router, $method)
{
$response = null;
$route = $router->getCurrentRoute();
// When running the before filters we will simply spin through the list of the
// filters and call each one on the current route objects, which will place
// the proper parameters on the filter call, including the requests data.
$request = $router->getRequest();
$filters = $this->getBeforeFilters($request, $method);
foreach ($filters as $filter)
{
$response = $this->callFilter($route, $filter, $request);
if ( ! is_null($response)) return $response;
}
}
/**
* Get the before filters for the controller.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return array
*/
protected function getBeforeFilters($request, $method)
{
$class = 'Illuminate\Routing\Controllers\Before';
return $this->filterParser->parse($this, $request, $method, $class);
}
/**
* Call the after filters on the controller.
*
* @param \Illuminate\Routing\Router $router
* @param string $method
* @param \Symfony\Component\HttpFoundation\Response $response
* @return mixed
*/
protected function callAfterFilters($router, $method, $response)
{
$route = $router->getCurrentRoute();
// When running the before filters we will simply spin through the list of the
// filters and call each one on the current route objects, which will place
// the proper parameters on the filter call, including the requests data.
$request = $router->getRequest();
$filters = $this->getAfterFilters($request, $method);
foreach ($filters as $filter)
{
$this->callFilter($route, $filter, $request, array($response));
}
}
/**
* Get the after filters for the controller.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return array
*/
protected function getAfterFilters($request, $method)
{
$class = 'Illuminate\Routing\Controllers\After';
return $this->filterParser->parse($this, $request, $method, $class);
}
/**
* Call the given route filter.
*
* @param \Illuminate\Routing\Route $route
* @param string $filter
* @param \Symfony\Component\HttpFoundation\Request $request
* @param array $parameters
* @return mixed
*/
protected function callFilter($route, $filter, $request, $parameters = array())
{
if (isset($this->callbackFilters[$filter]))
{
$callback = $this->callbackFilters[$filter];
return call_user_func_array($callback, array_merge(array($route, $request), $parameters));
}
return $route->callFilter($filter, $request, $parameters);
}
/**
* Setup the layout used by the controller.
*
* @return void
*/
protected function setupLayout() {}
/**
* Get the code registered filters.
*
* @return array
*/
public function getControllerFilters()
{
return $this->filters;
}
/**
* Handle calls to missing methods on the controller.
*
* @param array $parameters
* @return mixed
*/
public function missingMethod($parameters)
{
throw new NotFoundHttpException("Controller method not found.");
}
/**
* Handle calls to missing methods on the controller.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->missingMethod($parameters);
}
}

View File

@@ -0,0 +1,133 @@
<?php namespace Illuminate\Routing\Controllers;
use Symfony\Component\HttpFoundation\Request;
class Filter {
/**
* The name of the filter to be applied.
*
* @var string
*/
public $run;
/**
* The HTTP methods the filter applies to.
*
* @var array
*/
public $on;
/**
* The controller methods the filter applies to.
*
* @var array
*/
public $only;
/**
* The controller methods the filter doesn't apply to.
*
* @var array
*/
public $except;
/**
* Create a new annotation instance.
*
* @param array $values
* @return void
*/
public function __construct(array $values)
{
foreach ($this->prepareValues($values) as $key => $value)
{
$this->$key = $value;
}
}
/**
* Prepare the values for setting.
*
* @param array $values
* @return void
*/
protected function prepareValues($values)
{
if (isset($values['on']))
{
$values['on'] = (array) $values['on'];
// If the "get" method is present in an "on" constraint for the annotation we
// will add the "head" method as well, since the "head" method is supposed
// to function basically identically to the get methods on the back-end.
if (in_array('get', $values['on']))
{
$values['on'][] = 'head';
}
}
return $values;
}
/**
* Determine if the filter applies to a request and method.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return bool
*/
public function applicable(Request $request, $method)
{
foreach (array('Request', 'OnlyMethod', 'ExceptMethod') as $excluder)
{
// We'll simply check the excluder method and see if the annotation does
// not apply based on that rule. If it does not, we will go ahead and
// return false since we know an annotation is not even applicable.
$excluder = "excludedBy{$excluder}";
if ($this->$excluder($request, $method)) return false;
}
return true;
}
/**
* Determine if the filter applies based on the "on" rule.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return bool
*/
protected function excludedByRequest($request, $method)
{
$http = strtolower($request->getMethod());
return isset($this->on) and ! in_array($http, (array) $this->on);
}
/**
* Determine if the filter applies based on the "only" rule.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return bool
*/
protected function excludedByOnlyMethod($request, $method)
{
return isset($this->only) and ! in_array($method, (array) $this->only);
}
/**
* Determine if the filter applies based on the "except" rule.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return bool
*/
protected function excludedByExceptMethod($request, $method)
{
return isset($this->except) and in_array($method, (array) $this->except);
}
}

View File

@@ -0,0 +1,81 @@
<?php namespace Illuminate\Routing\Controllers;
use Symfony\Component\HttpFoundation\Request;
class FilterParser {
/**
* Parse the given filters from the controller.
*
* @param \Illuminate\Routing\Controllers\Controller $controller
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @param string $filter
* @return array
*/
public function parse(Controller $controller, Request $request, $method, $filter)
{
return $this->getCodeFilters($controller, $request, $method, $filter);
}
/**
* Get the filters that were specified in code.
*
* @param \Illuminate\Routing\Controllers\Controller $controller
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @param string $filter
* @return array
*/
protected function getCodeFilters($controller, $request, $method, $filter)
{
$filters = $this->filterByClass($controller->getControllerFilters(), $filter);
return $this->getNames($this->filter($filters, $request, $method));
}
/**
* Filter the annotation instances by class name.
*
* @param array $filters
* @param string $filter
* @return array
*/
protected function filterByClass($filters, $filter)
{
return array_filter($filters, function($annotation) use ($filter)
{
return $annotation instanceof $filter;
});
}
/**
* Filter the annotation instances by request and method.
*
* @param array $filters
* @param \Symfony\Component\HttpFoundation\Request $request
* @param string $method
* @return array
*/
protected function filter($filters, $request, $method)
{
$filtered = array_filter($filters, function($annotation) use ($request, $method)
{
return $annotation->applicable($request, $method);
});
return array_values($filtered);
}
/**
* Get the filter names from an array of filter objects.
*
* @param array $filters
* @return array
*/
protected function getNames(array $filters)
{
return array_map(function($filter) { return $filter->run; }, $filters);
}
}

View File

@@ -0,0 +1,138 @@
<?php namespace Illuminate\Routing\Controllers;
use ReflectionClass;
use ReflectionMethod;
class Inspector {
/**
* An array of HTTP verbs.
*
* @var array
*/
protected $verbs = array(
'any', 'get', 'post', 'put',
'delete', 'head', 'options'
);
/**
* Get the routable methods for a controller.
*
* @param string $controller
* @param string $prefix
* @return array
*/
public function getRoutable($controller, $prefix)
{
$routable = array();
$reflection = new ReflectionClass($controller);
// To get the routable methods, we will simply spin through all methods on the
// controller instance checking to see if it belongs to the given class and
// is a publicly routable method. If so, we will add it to this listings.
foreach ($reflection->getMethods() as $method)
{
if ($this->isRoutable($method, $reflection->name))
{
$data = $this->getMethodData($method, $prefix);
// If the routable method is an index method, we will create a special index
// route which is simply the prefix and the verb and does not contain any
// the wildcard place-holders that each "typical" routes would contain.
if ($data['plain'] == $prefix.'/index')
{
$routable[$method->name][] = $data;
$routable[$method->name][] = $this->getIndexData($data, $prefix);
}
// If the routable method is not a special index method, we will just add in
// the data to the returned results straight away. We do not need to make
// any special routes for this scenario but only just add these routes.
else
{
$routable[$method->name][] = $data;
}
}
}
return $routable;
}
/**
* Determine if the given controller method is routable.
*
* @param ReflectionMethod $method
* @param string $controller
* @return bool
*/
public function isRoutable(ReflectionMethod $method, $controller)
{
if ($method->class == 'Illuminate\Routing\Controllers\Controller') return false;
return $method->isPublic() and starts_with($method->name, $this->verbs);
}
/**
* Get the method data for a given method.
*
* @param ReflectionMethod $method
* @return array
*/
public function getMethodData(ReflectionMethod $method, $prefix)
{
$verb = $this->getVerb($name = $method->name);
$uri = $this->addUriWildcards($plain = $this->getPlainUri($name, $prefix));
return compact('verb', 'plain', 'uri');
}
/**
* Get the routable data for an index method.
*
* @param array $data
* @param string $prefix
* @return array
*/
protected function getIndexData($data, $prefix)
{
return array('verb' => $data['verb'], 'plain' => $prefix, 'uri' => $prefix);
}
/**
* Extract the verb from a controller action.
*
* @param string $name
* @return string
*/
public function getVerb($name)
{
return head(explode('_', snake_case($name)));
}
/**
* Determine the URI from the given method name.
*
* @param string $name
* @param string $prefix
* @return string
*/
public function getPlainUri($name, $prefix)
{
return $prefix.'/'.implode('-', array_slice(explode('_', snake_case($name)), 1));
}
/**
* Add wildcards to the given URI.
*
* @param string $uri
* @return string
*/
public function addUriWildcards($uri)
{
return $uri.'/{v1?}/{v2?}/{v3?}/{v4?}/{v5?}';
}
}

View File

@@ -0,0 +1,209 @@
<?php namespace Illuminate\Routing\Generators;
use Illuminate\Filesystem\Filesystem;
class ControllerGenerator {
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* The default resource controller methods.
*
* @var array
*/
protected $defaults = array(
'index',
'create',
'store',
'show',
'edit',
'update',
'destroy'
);
/**
* Create a new controller generator instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
public function __construct(Filesystem $files)
{
$this->files = $files;
}
/**
* Create a new resourceful controller file.
*
* @param string $controller
* @param string $path
* @param array $options
* @return void
*/
public function make($controller, $path, array $options = array())
{
$stub = $this->addMethods($this->getController($controller), $options);
$this->writeFile($stub, $controller, $path);
return false;
}
/**
* Write the completed stub to disk.
*
* @param string $stub
* @param string $controller
* @param string $path
* @return void
*/
protected function writeFile($stub, $controller, $path)
{
if (str_contains($controller, '\\'))
{
$this->makeDirectory($controller, $path);
}
$controller = str_replace('\\', DIRECTORY_SEPARATOR, $controller);
if ( ! $this->files->exists($fullPath = $path."/{$controller}.php"))
{
return $this->files->put($fullPath, $stub);
}
}
/**
* Create the directory for the controller.
*
* @param string $controller
* @param string $path
* @return void
*/
protected function makeDirectory($controller, $path)
{
$directory = $this->getDirectory($controller);
if ( ! $this->files->isDirectory($full = $path.'/'.$directory))
{
$this->files->makeDirectory($full, 0777, true);
}
}
/**
* Get the directory the controller should live in.
*
* @param string $controller
* @return string
*/
protected function getDirectory($controller)
{
return implode('/', array_slice(explode('\\', $controller), 0, -1));
}
/**
* Get the controller class stub.
*
* @param string $controller
* @return string
*/
protected function getController($controller)
{
$stub = $this->files->get(__DIR__.'/stubs/controller.stub');
// We will explode out the controller name on the naemspace delimiter so we
// are able to replace a namespace in this stub file. If no namespace is
// provided we'll just clear out the namespace place-holder locations.
$segments = explode('\\', $controller);
$stub = $this->replaceNamespace($segments, $stub);
return str_replace('{{class}}', last($segments), $stub);
}
/**
* Replace the namespace on the controller.
*
* @param array $segments
* @param string $stub
* @return string
*/
protected function replaceNamespace(array $segments, $stub)
{
if (count($segments) > 1)
{
$namespace = implode('\\', array_slice($segments, 0, -1));
return str_replace('{{namespace}}', ' namespace '.$namespace.';', $stub);
}
else
{
return str_replace('{{namespace}}', '', $stub);
}
}
/**
* Add the method stubs to the controller.
*
* @param string $stub
* @param array $options
* @return string
*/
protected function addMethods($stub, array $options)
{
// Once we have the applicable methods, we can just spin through those methods
// and add each one to our array of method stubs. Then we will implode them
// them all with end-of-line characters and return the final joined list.
$stubs = $this->getMethodStubs($options);
$methods = implode(PHP_EOL.PHP_EOL, $stubs);
return str_replace('{{methods}}', $methods, $stub);
}
/**
* Get all of the method stubs for the given options.
*
* @param array $options
* @return array
*/
protected function getMethodStubs($options)
{
$stubs = array();
// Each stub is conveniently kept in its own file so we can just grab the ones
// we need from disk to build the controller file. Once we have them all in
// an array we will return this list of methods so they can be joined up.
foreach ($this->getMethods($options) as $method)
{
$stubs[] = $this->files->get(__DIR__."/stubs/{$method}.stub");
}
return $stubs;
}
/**
* Get the applicable methods based on the options.
*
* @param array $options
* @return array
*/
protected function getMethods($options)
{
if (isset($options['only']) and count($options['only']) > 0)
{
return $options['only'];
}
elseif (isset($options['except']) and count($options['except']) > 0)
{
return array_diff($this->defaults, $options['except']);
}
return $this->defaults;
}
}

View File

@@ -0,0 +1,7 @@
<?php{{namespace}}
class {{class}} extends \BaseController {
{{methods}}
}

View File

@@ -0,0 +1,9 @@
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
//
}

View File

@@ -0,0 +1,10 @@
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
//
}

View File

@@ -0,0 +1,10 @@
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return Response
*/
public function edit($id)
{
//
}

View File

@@ -0,0 +1,9 @@
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
//
}

View File

@@ -0,0 +1,10 @@
/**
* Display the specified resource.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}

View File

@@ -0,0 +1,9 @@
/**
* Store a newly created resource in storage.
*
* @return Response
*/
public function store()
{
//
}

View File

@@ -0,0 +1,10 @@
/**
* Update the specified resource in storage.
*
* @param int $id
* @return Response
*/
public function update($id)
{
//
}

View File

@@ -0,0 +1,208 @@
<?php namespace Illuminate\Routing;
use Illuminate\Http\RedirectResponse;
use Illuminate\Session\Store as SessionStore;
class Redirector {
/**
* The URL generator instance.
*
* @var \Illuminate\Routing\UrlGenerator
*/
protected $generator;
/**
* The session store instance.
*
* @var \Illuminate\Session\Store
*/
protected $session;
/**
* Create a new Redirector instance.
*
* @param \Illuminate\Routing\UrlGenerator $generator
* @return void
*/
public function __construct(UrlGenerator $generator)
{
$this->generator = $generator;
}
/**
* Create a new redirect response to the "home" route.
*
* @param int $status
* @return \Illuminate\Http\RedirectResponse
*/
public function home($status = 302)
{
return $this->to($this->generator->route('home'), $status);
}
/**
* Create a new redirect response to the previous location.
*
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function back($status = 302, $headers = array())
{
$back = $this->generator->getRequest()->headers->get('referer');
return $this->createRedirect($back, $status, $headers);
}
/**
* Create a new redirect response to the current URI.
*
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function refresh($status = 302, $headers = array())
{
return $this->to($this->generator->getRequest()->path(), $status, $headers);
}
/**
* Create a new redirect response, while putting the current URL in the session.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function guest($path, $status = 302, $headers = array(), $secure = null)
{
$this->session->put('url.intended', $this->generator->full());
return $this->to($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to the previously intended location.
*
* @param string $default
* @param int $status
* @param array $headers
* @param bool $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function intended($default, $status = 302, $headers = array(), $secure = null)
{
$path = $this->session->get('url.intended', $default);
$this->session->forget('url.intended');
return $this->to($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to the given path.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function to($path, $status = 302, $headers = array(), $secure = null)
{
$path = $this->generator->to($path, array(), $secure);
return $this->createRedirect($path, $status, $headers);
}
/**
* Create a new redirect response to the given HTTPS path.
*
* @param string $path
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function secure($path, $status = 302, $headers = array())
{
return $this->to($path, $status, $headers, true);
}
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param array $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function route($route, $parameters = array(), $status = 302, $headers = array())
{
$path = $this->generator->route($route, $parameters);
return $this->to($path, $status, $headers);
}
/**
* Create a new redirect response to a controller action.
*
* @param string $action
* @param array $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function action($action, $parameters = array(), $status = 302, $headers = array())
{
$path = $this->generator->action($action, $parameters);
return $this->to($path, $status, $headers);
}
/**
* Create a new redirect response.
*
* @param string $path
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
protected function createRedirect($path, $status, $headers)
{
$redirect = new RedirectResponse($path, $status, $headers);
if (isset($this->session))
{
$redirect->setSession($this->session);
}
$redirect->setRequest($this->generator->getRequest());
return $redirect;
}
/**
* Get the URL generator instance.
*
* @return \Illuminate\Routing\UrlGenerator
*/
public function getUrlGenerator()
{
return $this->generator;
}
/**
* Set the active session store.
*
* @param \Illuminate\Session\Store $session
* @return void
*/
public function setSession(SessionStore $session)
{
$this->session = $session;
}
}

View File

@@ -0,0 +1,481 @@
<?php namespace Illuminate\Routing;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route as BaseRoute;
class Route extends BaseRoute {
/**
* The router instance.
*
* @var \Illuminate\Routing\Router
*/
protected $router;
/**
* The matching parameter array.
*
* @var array
*/
protected $parameters;
/**
* The parsed parameter array.
*
* @var array
*/
protected $parsedParameters;
/**
* Execute the route and return the response.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @return mixed
*/
public function run(Request $request)
{
$this->parsedParameters = null;
// We will only call the router callable if no "before" middlewares returned
// a response. If they do, we will consider that the response to requests
// so that the request "lifecycle" will be easily halted for filtering.
$response = $this->callBeforeFilters($request);
if ( ! isset($response))
{
$response = $this->callCallable();
}
// If the response is from a filter we want to note that so that we can skip
// the "after" filters which should only run when the route method is run
// for the incoming request. Otherwise only app level filters will run.
else
{
$fromFilter = true;
}
$response = $this->router->prepare($response, $request);
// Once we have the "prepared" response, we will iterate through every after
// filter and call each of them with the request and the response so they
// can perform any final work that needs to be done after a route call.
if ( ! isset($fromFilter))
{
$this->callAfterFilters($request, $response);
}
return $response;
}
/**
* Call the callable Closure attached to the route.
*
* @return mixed
*/
protected function callCallable()
{
$variables = array_values($this->getParametersWithoutDefaults());
return call_user_func_array($this->getOption('_call'), $variables);
}
/**
* Call all of the before filters on the route.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @return mixed
*/
protected function callBeforeFilters(Request $request)
{
$before = $this->getAllBeforeFilters($request);
$response = null;
// Once we have each middlewares, we will simply iterate through them and call
// each one of them with the request. We will set the response variable to
// whatever it may return so that it may override the request processes.
foreach ($before as $filter)
{
$response = $this->callFilter($filter, $request);
if ( ! is_null($response)) return $response;
}
}
/**
* Get all of the before filters to run on the route.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @return array
*/
protected function getAllBeforeFilters(Request $request)
{
$before = $this->getBeforeFilters();
$patterns = $this->router->findPatternFilters($request->getMethod(), $request->getPathInfo());
return array_merge($before, $patterns);
}
/**
* Call all of the "after" filters for a route.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Symfony\Component\HttpFoundation\Response $response
* @return void
*/
protected function callAfterFilters(Request $request, $response)
{
foreach ($this->getAfterFilters() as $filter)
{
$this->callFilter($filter, $request, array($response));
}
}
/**
* Call a given filter with the parameters.
*
* @param string $name
* @param \Symfony\Component\HttpFoundation\Request $request
* @param array $params
* @return mixed
*/
public function callFilter($name, Request $request, array $params = array())
{
if ( ! $this->router->filtersEnabled()) return;
$merge = array($this->router->getCurrentRoute(), $request);
$params = array_merge($merge, $params);
// Next we will parse the filter name to extract out any parameters and adding
// any parameters specified in a filter name to the end of the lists of our
// parameters, since the ones at the beginning are typically very static.
list($name, $params) = $this->parseFilter($name, $params);
if ( ! is_null($callable = $this->router->getFilter($name)))
{
return call_user_func_array($callable, $params);
}
}
/**
* Parse a filter name and add any parameters to the array.
*
* @param string $name
* @param array $parameters
* @return array
*/
protected function parseFilter($name, $parameters = array())
{
if (str_contains($name, ':'))
{
// If the filter name contains a colon, we will assume that the developer
// is passing along some parameters with the name, and we will explode
// out the name and paramters, merging the parameters onto the list.
$segments = explode(':', $name);
$name = $segments[0];
// We will merge the arguments specified in the filter name into the list
// of existing parameters. We'll send them at the end since any values
// at the front are usually static such as request, response, route.
$arguments = explode(',', $segments[1]);
$parameters = array_merge($parameters, $arguments);
}
return array($name, $parameters);
}
/**
* Get a parameter by name from the route.
*
* @param string $name
* @param mixed $default
* @return string
*/
public function getParameter($name, $default = null)
{
return array_get($this->getParameters(), $name, $default);
}
/**
* Get the parameters to the callback.
*
* @return array
*/
public function getParameters()
{
// If we have already parsed the parameters, we will just return the listing
// that we already parsed as some of these may have been resolved through
// a binder that uses a database repository and shouldn't be run again.
if (isset($this->parsedParameters))
{
return $this->parsedParameters;
}
$variables = $this->compile()->getVariables();
// To get the parameter array, we need to spin the names of the variables on
// the compiled route and match them to the parameters that we got when a
// route is matched by the router, as routes instances don't have them.
$parameters = array();
foreach ($variables as $variable)
{
$parameters[$variable] = $this->resolveParameter($variable);
}
return $this->parsedParameters = $parameters;
}
/**
* Resolve a parameter value for the route.
*
* @param string $key
* @return mixed
*/
protected function resolveParameter($key)
{
$value = $this->parameters[$key];
// If the parameter has a binder, we will call the binder to resolve the real
// value for the parameters. The binders could make a database call to get
// a User object for example or may transform the input in some fashion.
if ($this->router->hasBinder($key))
{
return $this->router->performBinding($key, $value, $this);
}
return $value;
}
/**
* Get the route parameters without missing defaults.
*
* @return array
*/
public function getParametersWithoutDefaults()
{
$parameters = $this->getParameters();
foreach ($parameters as $key => $value)
{
// When calling functions using call_user_func_array, we don't want to write
// over any existing default parameters, so we will remove every optional
// parameter from the list that did not get a specified value on route.
if ($this->isMissingDefault($key, $value))
{
unset($parameters[$key]);
}
}
return $parameters;
}
/**
* Determine if a route parameter is really a missing default.
*
* @param string $key
* @param mixed $value
* @return bool
*/
protected function isMissingDefault($key, $value)
{
return $this->isOptional($key) and is_null($value);
}
/**
* Determine if a given key is optional.
*
* @param string $key
* @return bool
*/
public function isOptional($key)
{
return array_key_exists($key, $this->getDefaults());
}
/**
* Get the keys of the variables on the route.
*
* @return array
*/
public function getParameterKeys()
{
return $this->compile()->getVariables();
}
/**
* Force a given parameter to match a regular expression.
*
* @param string $name
* @param string $expression
* @return \Illuminate\Routing\Route
*/
public function where($name, $expression = null)
{
if (is_array($name)) return $this->setArrayOfWheres($name);
$this->setRequirement($name, $expression);
return $this;
}
/**
* Force a given parameters to match the expressions.
*
* @param array $wheres
* @return \Illuminate\Routing\Route
*/
protected function setArrayOfWheres(array $wheres)
{
foreach ($wheres as $name => $expression)
{
$this->where($name, $expression);
}
return $this;
}
/**
* Set the default value for a parameter.
*
* @param string $key
* @param mixed $value
* @return \Illuminate\Routing\Route
*/
public function defaults($key, $value)
{
$this->setDefault($key, $value);
return $this;
}
/**
* Set the before filters on the route.
*
* @param dynamic
* @return \Illuminate\Routing\Route
*/
public function before()
{
$this->setBeforeFilters(func_get_args());
return $this;
}
/**
* Set the after filters on the route.
*
* @param dynamic
* @return \Illuminate\Routing\Route
*/
public function after()
{
$this->setAfterFilters(func_get_args());
return $this;
}
/**
* Get the name of the action (if any) used by the route.
*
* @return string
*/
public function getAction()
{
return $this->getOption('_uses');
}
/**
* Get the before filters on the route.
*
* @return array
*/
public function getBeforeFilters()
{
return $this->getOption('_before') ?: array();
}
/**
* Set the before filters on the route.
*
* @param string $value
* @return void
*/
public function setBeforeFilters($value)
{
$filters = $this->parseFilterValue($value);
$this->setOption('_before', array_merge($this->getBeforeFilters(), $filters));
}
/**
* Get the after filters on the route.
*
* @return array
*/
public function getAfterFilters()
{
return $this->getOption('_after') ?: array();
}
/**
* Set the after filters on the route.
*
* @param string $value
* @return void
*/
public function setAfterFilters($value)
{
$filters = $this->parseFilterValue($value);
$this->setOption('_after', array_merge($this->getAfterFilters(), $filters));
}
/**
* Parse the given filters for setting.
*
* @param array|string $value
* @return array
*/
protected function parseFilterValue($value)
{
$results = array();
foreach ((array) $value as $filters)
{
$results = array_merge($results, explode('|', $filters));
}
return $results;
}
/**
* Set the matching parameter array on the route.
*
* @param array $parameters
* @return void
*/
public function setParameters($parameters)
{
$this->parameters = $parameters;
}
/**
* Set the Router instance on the route.
*
* @param \Illuminate\Routing\Router $router
* @return \Illuminate\Routing\Route
*/
public function setRouter(Router $router)
{
$this->router = $router;
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
<?php namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
class RoutingServiceProvider extends ServiceProvider {
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerRouter();
$this->registerUrlGenerator();
$this->registerRedirector();
}
/**
* Register the router instance.
*
* @return void
*/
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function($app)
{
$router = new Router($app);
// If the current application environment is "testing", we will disable the
// routing filters, since they can be tested independently of the routes
// and just get in the way of our typical controller testing concerns.
if ($app['env'] == 'testing')
{
$router->disableFilters();
}
return $router;
});
}
/**
* Register the URL generator service.
*
* @return void
*/
protected function registerUrlGenerator()
{
$this->app['url'] = $this->app->share(function($app)
{
// The URL generator needs the route collection that exists on the router.
// Keep in mind this is an object, so we're passing by references here
// and all the registered routes will be available to the generator.
$routes = $app['router']->getRoutes();
return new UrlGenerator($routes, $app['request']);
});
}
/**
* Register the Redirector service.
*
* @return void
*/
protected function registerRedirector()
{
$this->app['redirect'] = $this->app->share(function($app)
{
$redirector = new Redirector($app['url']);
// If the session is set on the application instance, we'll inject it into
// the redirector instance. This allows the redirect responses to allow
// for the quite convenient "with" methods that flash to the session.
if (isset($app['session']))
{
$redirector->setSession($app['session']);
}
return $redirector;
});
}
}

View File

@@ -0,0 +1,342 @@
<?php namespace Illuminate\Routing;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Generator\UrlGenerator as SymfonyGenerator;
class UrlGenerator {
/**
* The route collection.
*
* @var \Symfony\Component\Routing\RouteCollection
*/
protected $routes;
/**
* The request instance.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* The Symfony routing URL generator.
*
* @var \Symfony\Component\Routing\Generator\UrlGenerator
*/
protected $generator;
/**
* Create a new URL Generator instance.
*
* @param \Symfony\Component\Routing\RouteCollection $routes
* @param \Symfony\Component\HttpFoundation\Request $request
* @return void
*/
public function __construct(RouteCollection $routes, Request $request)
{
$this->routes = $routes;
$this->setRequest($request);
}
/**
* Get the full URL for the current request.
*
* @return string
*/
public function full()
{
return $this->request->fullUrl();
}
/**
* Get the current URL for the request.
*
* @return string
*/
public function current()
{
return $this->to($this->request->getPathInfo());
}
/**
* Get the URL for the previous request.
*
* @return string
*/
public function previous()
{
return $this->to($this->request->headers->get('referer'));
}
/**
* Generate a absolute URL to the given path.
*
* @param string $path
* @param mixed $parameters
* @param bool $secure
* @return string
*/
public function to($path, $parameters = array(), $secure = null)
{
if ($this->isValidUrl($path)) return $path;
$scheme = $this->getScheme($secure);
// Once we have the scheme we will compile the "tail" by collapsing the values
// into a single string delimited by slashes. This just makes it convenient
// for passing the array of parameters to this URL as a list of segments.
$tail = implode('/', (array) $parameters);
$root = $this->getRootUrl($scheme);
return trim($root.'/'.trim($path.'/'.$tail, '/'), '/');
}
/**
* Generate a secure, absolute URL to the given path.
*
* @param string $path
* @param array $parameters
* @return string
*/
public function secure($path, $parameters = array())
{
return $this->to($path, $parameters, true);
}
/**
* Generate a URL to an application asset.
*
* @param string $path
* @param bool $secure
* @return string
*/
public function asset($path, $secure = null)
{
if ($this->isValidUrl($path)) return $path;
// Once we get the root URL, we will check to see if it contains an index.php
// file in the paths. If it does, we will remove it since it is not needed
// for asset paths, but only for routes to endpoints in the application.
$root = $this->getRootUrl($this->getScheme($secure));
return $this->removeIndex($root).'/'.trim($path, '/');
}
/**
* Remove the index.php file from a path.
*
* @param string $root
* @return string
*/
protected function removeIndex($root)
{
$i = 'index.php';
return str_contains($root, $i) ? str_replace('/'.$i, '', $root) : $root;
}
/**
* Generate a URL to a secure asset.
*
* @param string $path
* @return string
*/
public function secureAsset($path)
{
return $this->asset($path, true);
}
/**
* Get the scheme for a raw URL.
*
* @param bool $secure
* @return string
*/
protected function getScheme($secure)
{
if (is_null($secure))
{
return $this->request->getScheme().'://';
}
else
{
return $secure ? 'https://' : 'http://';
}
}
/**
* Get the URL to a named route.
*
* @param string $name
* @param mixed $parameters
* @param bool $absolute
* @return string
*/
public function route($name, $parameters = array(), $absolute = true)
{
$route = $this->routes->get($name);
$parameters = (array) $parameters;
if (isset($route) and $this->usingQuickParameters($parameters))
{
$parameters = $this->buildParameterList($route, $parameters);
}
return $this->generator->generate($name, $parameters, $absolute);
}
/**
* Determine if we're short circuiting the parameter list.
*
* @param array $parameters
* @return bool
*/
protected function usingQuickParameters(array $parameters)
{
return count($parameters) > 0 and is_numeric(head(array_keys($parameters)));
}
/**
* Build the parameter list for short circuit parameters.
*
* @param \Illuminate\Routing\Route $route
* @param array $params
* @return array
*/
protected function buildParameterList($route, array $params)
{
$keys = $route->getParameterKeys();
// If the number of keys is less than the number of parameters on a route
// we'll fill out the parameter arrays with empty bindings on the rest
// of the spots until they are equal so we can run an array combine.
if (count($params) < count($keys))
{
$difference = count($keys) - count($params);
$params += array_fill(count($params), $difference, null);
}
return array_combine($keys, $params);
}
/**
* Get the URL to a controller action.
*
* @param string $action
* @param mixed $parameters
* @param bool $absolute
* @return string
*/
public function action($action, $parameters = array(), $absolute = true)
{
// First we'll check to see if we have already rendered a URL for an action
// so that we don't have to loop through all of the routes again on each
// iteration through the loop. If we have it, we can just return that.
if (isset($this->actionMap[$action]))
{
$name = $this->actionMap[$action];
return $this->route($name, $parameters, $absolute);
}
// If haven't already mapped this action to a URI yet, we will need to spin
// through all of the routes looking for routes that routes to the given
// controller's action, then we will cache them off and build the URL.
foreach ($this->routes as $name => $route)
{
if ($action == $route->getOption('_uses'))
{
$this->actionMap[$action] = $name;
return $this->route($name, $parameters, $absolute);
}
}
throw new InvalidArgumentException("Unknown action [$action].");
}
/**
* Get the base URL for the request.
*
* @param string $scheme
* @return string
*/
protected function getRootUrl($scheme)
{
$root = $this->request->root();
$start = starts_with($root, 'http://') ? 'http://' : 'https://';
return preg_replace('~'.$start.'~', $scheme, $root, 1);
}
/**
* Determine if the given path is a valid URL.
*
* @param string $path
* @return bool
*/
public function isValidUrl($path)
{
if (starts_with($path, array('#', '//', 'mailto:', 'tel:'))) return true;
return filter_var($path, FILTER_VALIDATE_URL) !== false;
}
/**
* Get the request instance.
*
* @return \Symfony\Component\HttpFoundation\Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Set the current request instance.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @return void
*/
public function setRequest(Request $request)
{
$this->request = $request;
$context = new RequestContext;
$context->fromRequest($this->request);
$this->generator = new SymfonyGenerator($this->routes, $context);
}
/**
* Get the Symfony URL generator instance.
*
* @return \Symfony\Component\Routing\Generator\UrlGenerator
*/
public function getGenerator()
{
return $this->generator;
}
/**
* Set the Symfony URL generator instance.
*
* @param \Symfony\Component\Routing\Generator\UrlGenerator $generator
* @return void
*/
public function setGenerator(SymfonyGenerator $generator)
{
$this->generator = $generator;
}
}

View File

@@ -0,0 +1,37 @@
{
"name": "illuminate/routing",
"license": "MIT",
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
}
],
"require": {
"illuminate/container": "4.0.x",
"illuminate/http": "4.0.x",
"illuminate/session": "4.0.x",
"illuminate/support": "4.0.x",
"symfony/http-foundation": "2.3.*",
"symfony/http-kernel": "2.3.*",
"symfony/routing": "2.3.*"
},
"require-dev": {
"illuminate/console": "4.0.x",
"illuminate/filesystem": "4.0.x",
"mockery/mockery": "0.7.2",
"phpunit/phpunit": "3.7.*"
},
"autoload": {
"psr-0": {
"Illuminate\\Routing": ""
}
},
"target-dir": "Illuminate/Routing",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"minimum-stability": "dev"
}