public function can($action, $resource, $resourceValue = null)
{
if (is_object($resource)) {
$resourceValue = $resource;
$resource = get_classname($resourceValue);
} elseif (is_array($resource)) {
// Nested resources can be passed through an associative array, this way conditions which are
// dependent upon the association will work when using a class.
$resourceValue = head(array_values($resource));
$resource = head(array_keys($resource));
}
// The conditional callback (Closure) is only evaluated when an actual instance object is present.
// It is not evaluated when checking permissions on the class name (such as in the 'index' action).
$skipConditions = false;
if (is_string($resource) && !is_object($resourceValue) && $this->hasCondition($action, $resource)) {
$skipConditions = true;
}
$self = $this;
$rules = $this->getRulesFor($action, $resource);
if (!$rules->isEmpty()) {
$allowed = array_reduce($rules->all(), function ($result, $rule) use($self, $resourceValue, $skipConditions) {
if ($skipConditions) {
return $rule->getBehavior();
// Short circuit
} else {
if ($rule->isRestriction()) {
// 'deny' rules override prior rules.
$result = $result && $rule->isAllowed($self, $resourceValue);
} else {
// 'allow' rules do not override prior rules but instead are logically or'ed.
// Unlike Authority default behavior.
$result = $result || $rule->isAllowed($self, $resourceValue);
}
}
return $result;
}, false);
} else {
$allowed = false;
}
return $allowed;
}