PHP: Как проверить, может ли пользователь получить доступ к определенной группе рекурсивно?

106
11

Я пытаюсь создать простую проверку доступа для моих пользователей. В основном у меня есть массив групп, таких как trial, paid, admin и т.д., Который определяется следующим образом:

$levels = ['trial' => [], 'enterprise' => ['admin'], 'paid' => ['trial'], 
'admin' => ['paid', 'editor'], 'editor' => []];

Взгляните на paid группу. paid группы есть trial группа в этом массиве, означающая, что paid пользователи группы могут также получить доступ к trial группе.

Аналогично, группа admin может получить доступ к paid, editor и trial группе, поскольку admin paid группе в своем массиве, а paid группа имеет trial группу в своем массиве (мне кажется, требуется рекурсия).

Моя функция подобна (но я не могу определить часть рекурсии):

<?php
public function hasAccess($requiredLevel, $myLevel) {
global $levels;
return (($myLevel == $requiredLevel) || (in_array($requiredLevel, $levels[$myLevel]));
}

Это работает, когда уровень непосредственно присутствует в массиве, но не удается создать ассоциации, например, когда уровни связаны как admin => paid => trial

Как изменить эту функцию, чтобы она могла проверить это рекурсивно самым простым способом?

PS Порядок, в котором они хранятся, также не фиксирован.

спросил(а) 2021-01-19T18:40:30+03:00 2 месяца, 3 недели назад
1
Решение
63

Это должно сделать это:

public function hasAccess($requiredLevel, $levels, $myLevel) {
// Check if $myLevel matches $requiredLevel
if ($requiredLevel === $myLevel) {
return true;
}

// Iterate through all levels under $myLevel
foreach ($levels[$myLevel] as $level) {
// If any of those levels has access, return true
if ($this->hasAccess($requiredLevel, $levels, $level)) {
return true;
}
}

return false;
}

Надеюсь, что комментарии прояснят вам достаточно.

Я также советую вам передать $levels в качестве аргумента вместо использования глобальной переменной, поскольку он обеспечивает большую гибкость, а глобальные переменные - это, как правило, плохая практика.

Единственное, на что вы должны обратить внимание, - это круговые ссылки, где вы получите бесконечный цикл. Чтобы решить эту проблему, просто передайте массив уже посещенных уровней в качестве необязательного аргумента:

public function hasAccess($requiredLevel, $levels, $myLevel, $visited = []) {
// Check if $myLevel matches $requiredLevel
if ($requiredLevel === $myLevel) {
return true;
}

// Add the current level to the visited array
$visited[] = $myLevel;

// Iterate through all levels under $myLevel
foreach ($levels[$myLevel] as $level) {
// Return true if any of those levels has access,
// and hasn't been visited before
if (
!in_array($level, $visited) &&
$this->hasAccess($requiredLevel, $levels, $level, $visited)
) {
return true;
}
}

return false;
}

ответил(а) 2021-01-19T18:40:30+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема