Skip to content

Commit

Permalink
Merge pull request #1 from utopia-php/middelwares
Browse files Browse the repository at this point in the history
Added support for running hooks only for specific groups of routes
  • Loading branch information
eldadfux authored Jun 25, 2020
2 parents e696c0f + dc6558e commit 30aeb2a
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 79 deletions.
213 changes: 149 additions & 64 deletions src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ class App
protected $mode = '';

/**
* Error
* Errors
*
* An error callback
* Errors callbacks
*
* @var callback
*/
protected $error = null;
protected $errors = [
'*' => [],
];

/**
* Init
Expand All @@ -67,7 +69,9 @@ class App
*
* @var callback[]
*/
protected $init = [];
protected $init = [
'*' => [],
];

/**
* Shutdown
Expand All @@ -76,7 +80,9 @@ class App
*
* @var callback[]
*/
protected $shutdown = [];
protected $shutdown = [
'*' => [],
];

/**
* Options
Expand All @@ -85,7 +91,9 @@ class App
*
* @var callback[]
*/
protected $options = [];
protected $options = [
'*' => [],
];

/**
* Route
Expand Down Expand Up @@ -133,7 +141,7 @@ public function __construct($timezone, $mode = self::MODE_TYPE_PRODUCTION)
* @param string $url
* @return Route
*/
public function get($url)
public function get($url): Route
{
return $this->addRoute(self::REQUEST_METHOD_GET, $url);
}
Expand All @@ -146,7 +154,7 @@ public function get($url)
* @param string $url
* @return Route
*/
public function post($url)
public function post($url): Route
{
return $this->addRoute(self::REQUEST_METHOD_POST, $url);
}
Expand All @@ -159,7 +167,7 @@ public function post($url)
* @param string $url
* @return Route
*/
public function put($url)
public function put($url): Route
{
return $this->addRoute(self::REQUEST_METHOD_PUT, $url);
}
Expand All @@ -172,7 +180,7 @@ public function put($url)
* @param string $url
* @return Route
*/
public function patch($url)
public function patch($url): Route
{
return $this->addRoute(self::REQUEST_METHOD_PATCH, $url);
}
Expand All @@ -185,7 +193,7 @@ public function patch($url)
* @param string $url
* @return Route
*/
public function delete($url)
public function delete($url): Route
{
return $this->addRoute(self::REQUEST_METHOD_DELETE, $url);
}
Expand All @@ -195,12 +203,18 @@ public function delete($url)
*
* Set a callback function that will be initialized on application start
*
* @param $callback
* @param callable $callback
* @param string $group Pass "*" for all
* @return $this
*/
public function init($callback)
public function init(callable $callback, string $group = '*'): self
{
$this->init[] = $callback;
if(!isset($this->init[$group])) {
$this->init[$group] = [];
}

$this->init[$group][] = $callback;

return $this;
}

Expand All @@ -209,12 +223,18 @@ public function init($callback)
*
* Set a callback function that will be initialized on application end
*
* @param $callback
* @param callable $callback
* @param string $group Use "*" for all
* @return $this
*/
public function shutdown($callback)
public function shutdown(callable $callback, string $group = '*'): self
{
$this->shutdown[] = $callback;
if(!isset($this->shutdown[$group])) {
$this->shutdown[$group] = [];
}

$this->shutdown[$group][] = $callback;

return $this;
}

Expand All @@ -223,12 +243,18 @@ public function shutdown($callback)
*
* Set a callback function for all request with options method
*
* @param $callback
* @param callable $callback
* @param string $group Use "*" for all
* @return $this
*/
public function options($callback)
public function options(callable $callback, string $group = '*'): self
{
$this->options[] = $callback;
if(!isset($this->options[$group])) {
$this->options[$group] = [];
}

$this->options[$group][] = $callback;

return $this;
}

Expand All @@ -237,12 +263,18 @@ public function options($callback)
*
* An error callback for failed or no matched requests
*
* @param $callback
* @param callbale $callback
* @param string $group Use "*" for all
* @return $this
*/
public function error($callback)
public function error(callable $callback, string $group = '*'): self
{
$this->error = $callback;
if(!isset($this->errors[$group])) {
$this->errors[$group] = [];
}

$this->errors[$group][] = $callback;

return $this;
}

Expand Down Expand Up @@ -401,6 +433,83 @@ public function match(Request $request)
return $this->route;
}

/**
* Execute a given route with middlewares and error handling
*
* @param Route $route
* @return self
*/
public function execute(Route $route, array $args = []): self
{
$keys = [];
$params = [];
$groups = $route->getGroups();

// Extract keys from URL
$keyRegex = '@^' . \preg_replace('@:[^/]+@', ':([^/]+)', $route->getURL()) . '$@';
\preg_match($keyRegex, $route->getURL(), $keys);

// Remove the first key and value ( corresponding to full regex match )
\array_shift($keys);

// combine keys and values to one array
$values = \array_combine($keys, $this->matches);

try {
foreach ($this->init['*'] as $init) { // Global init hooks
\call_user_func_array($init, []);
}

foreach ($groups as $group) {
if(isset($this->init[$group])) {
foreach ($this->init[$group] as $init) { // Group init hooks
\call_user_func_array($init, []);
}
}
}

foreach ($route->getParams() as $key => $param) {
// Get value from route or request object
$arg = (isset($args[$key])) ? $args[$key] : $param['default'];
$value = isset($values[$key]) ? $values[$key] : $arg;
$value = ($value === '') ? $param['default'] : $value;

$this->validate($key, $param, $value);

$params[$key] = $value;
}

// Call the callback with the matched positions as params
\call_user_func_array($route->getAction(), $params);

foreach ($groups as $group) {
if(isset($this->shutdown[$group])) {
foreach ($this->shutdown[$group] as $shutdown) { // Group shutdown hooks
\call_user_func_array($shutdown, []);
}
}
}

foreach ($this->shutdown['*'] as $shutdown) { // Global shutdown hooks
\call_user_func_array($shutdown, []);
}
} catch (\Exception $e) {
foreach ($groups as $group) {
if(isset($this->errors[$group])) {
foreach ($this->errors[$group] as $error) { // Group shutdown hooks
\call_user_func_array($error, [$e]);
}
}
}

foreach ($this->errors['*'] as $error) { // Global error hooks
\call_user_func_array($error, [$e]);
}
}

return $this;
}

/**
* Run
*
Expand All @@ -409,66 +518,42 @@ public function match(Request $request)
*
* @param Request $request
* @param Response $response
* @return mixed
* @return self
*/
public function run(Request $request, Response $response)
{
$keys = [];
$params = [];
$method = $request->getServer('REQUEST_METHOD', '');
$route = $this->match($request);
$groups = $route->getGroups();

if (self::REQUEST_METHOD_HEAD == $method) {
$response->disablePayload();
}

if (null !== $route) {
// Extract keys from URL
$keyRegex = '@^' . \preg_replace('@:[^/]+@', ':([^/]+)', $route->getURL()) . '$@';
\preg_match($keyRegex, $route->getURL(), $keys);

// Remove the first key and value ( corresponding to full regex match )
\array_shift($keys);

// combine keys and values to one array
$values = \array_combine($keys, $this->matches);

return $this->execute($route, $request->getParams());
} elseif (self::REQUEST_METHOD_OPTIONS == $method) {
try {
foreach ($this->init as $init) {
\call_user_func_array($init, []);
}

foreach ($route->getParams() as $key => $param) {
// Get value from route or request object
$value = isset($values[$key]) ? $values[$key] : $request->getParam($key, $param['default']);
$value = ($value === '') ? $param['default'] : $value;

$this->validate($key, $param, $value);

$params[$key] = $value;
}

// Call the callback with the matched positions as params
\call_user_func_array($route->getAction(), $params);

foreach ($this->shutdown as $shutdown) {
\call_user_func_array($shutdown, []);
foreach ($groups as $group) {
if(isset($this->options[$group])) {
foreach ($this->options[$group] as $option) { // Group options hooks
\call_user_func_array($option, []);
}
}
}
} catch (\Exception $e) {
\call_user_func_array($this->error, [$e]);
}

return $this;
} elseif (self::REQUEST_METHOD_OPTIONS == $method) {
try {
foreach ($this->options as $option) {
foreach ($this->options['*'] as $option) { // Global options hooks
\call_user_func_array($option, []);
}
} catch (\Exception $e) {
\call_user_func_array($this->error, [$e]);
foreach ($this->errors['*'] as $error) { // Global error hooks
\call_user_func_array($error, [$e]);
}
}
} else {
\call_user_func_array($this->error, [new Exception('Not Found', 404)]);
foreach ($this->errors['*'] as $error) { // Global error hooks
\call_user_func_array($error, [new Exception('Not Found', 404)]);
}
}

return $this;
Expand Down
1 change: 1 addition & 0 deletions src/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public function getParams(): array
case self::METHOD_POST:
case self::METHOD_PUT:
case self::METHOD_PATCH:
case self::METHOD_DELETE:
return $this->generateInput();
break;
default:
Expand Down
Loading

0 comments on commit 30aeb2a

Please sign in to comment.