简体   繁体   中英

PHP: How to check if user can access a certain group recursively?

I'm trying to create a simple access check for my users. Basically I have an array of groups like trial , paid , admin , etc defined like this:

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

Take a look at the paid group. paid group has trial group in it's array meaning paid group users can access trial group too.

Similarly admin group can access paid , editor and trial group because admin has paid group in its array and paid group has trial group in its array (needs recursion I guess).

My function is like this (but I can't figure out the recursion part ):

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

This works when the level is directly present in array but fails to make the associations, eg when levels are connected like admin => paid => trial

How to modify this function so it can check this recursively in simplest way possible?

PS The order in which they are stored is also not fixed.

This should do it:

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;
}

I hope the comments clear it up enough for you.

I also advise you to pass $levels in as an argument instead of using a global variable as it allows for more flexibility, and globals are generally a bad practice.

The only thing you should watch out for are circular references, where you would end up with an endless loop. To solve this, simply pass the array of already visited levels as an optional argument:

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM