Skip to content

Instantly share code, notes, and snippets.

@barryvdh
Created August 15, 2017 07:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barryvdh/4356322e471d069e1c2a66bfac8f48e7 to your computer and use it in GitHub Desktop.
Save barryvdh/4356322e471d069e1c2a66bfac8f48e7 to your computer and use it in GitHub Desktop.
<?php
namespace App\Libraries;
use ReflectionClass;
use ReflectionMethod;
use Illuminate\Support\Str;
/**
* @deprecated since version 5.2.
*/
class ControllerInspector
{
/**
* An array of HTTP verbs.
*
* @var array
*/
protected $verbs = [
'any', 'get', 'post', 'put', 'patch',
'delete', 'head', 'options',
];
/**
* Get the routable methods for a controller.
*
* @param string $controller
* @param string $prefix
* @return array
*/
public function getRoutable($controller, $prefix)
{
$routable = [];
$reflection = new ReflectionClass($controller);
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
// 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 ($methods as $method) {
if ($this->isRoutable($method)) {
$data = $this->getMethodData($method, $prefix);
$routable[$method->name][] = $data;
// 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][] = $this->getIndexData($data, $prefix);
}
}
}
return $routable;
}
/**
* Determine if the given controller method is routable.
*
* @param \ReflectionMethod $method
* @return bool
*/
public function isRoutable(ReflectionMethod $method)
{
if ($method->class == 'Illuminate\Routing\Controller') {
return false;
}
return Str::startsWith($method->name, $this->verbs);
}
/**
* Get the method data for a given method.
*
* @param \ReflectionMethod $method
* @param string $prefix
* @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 ['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('_', Str::snake($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('_', Str::snake($name)), 1));
}
/**
* Add wildcards to the given URI.
*
* @param string $uri
* @return string
*/
public function addUriWildcards($uri)
{
return $uri.'/{one?}/{two?}/{three?}/{four?}/{five?}';
}
}
<?php
use App\Libraries\ControllerInspector;
Route::macro('controller', function($uri, $controller, $names = []){
$prepended = $controller;
// First, we will check to see if a controller prefix has been registered in
// the route group. If it has, we will need to prefix it before trying to
// reflect into the class instance and pull out the method for routing.
$groupStack = Route::getGroupStack();
if (! empty($groupStack)) {
$group = end($groupStack);
$prepended = isset($group['namespace']) && strpos($controller, '\\') !== 0 ? $group['namespace'].'\\'.$controller : $controller;
}
$routable = (new ControllerInspector())
->getRoutable($prepended, $uri);
// When a controller is routed using this method, we use Reflection to parse
// out all of the routable methods for the controller, then register each
// route explicitly for the developers, so reverse routing is possible.
foreach ($routable as $method => $routes) {
foreach ($routes as $route) {
$action = ['uses' => $controller.'@'.$method];
// If a given controller method has been named, we will assign the name to the
// controller action array, which provides for a short-cut to method naming
// so you don't have to define an individual route for these controllers.
$action['as'] = Arr::get($names, $method);
Route::{$route['verb']}($route['uri'], $action);
}
}
$missing = Route::any($uri.'/{_missing}', $controller.'@missingMethod');
$missing->where('_missing', '(.*)');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment