FlagManager.php 7.98 KB
<?php
namespace BTFlags;

class FlagManager {
    
    private $flagsFile;
    private $errors;

    public function __construct(){
        $this->errors = [];
        $this->flagsFile = FLAGS_DIR . DIRECTORY_SEPARATOR . 'flags.json';
    }

    private function checkDir(){
        if(!defined("FLAGS_DIR")) throw new \Exception("constant FLAGS_DIR is undefined. Make sure to add it in your constants.php file.");
    }
    
    public function create($flagData){
        $this->checkDir();
        $created = true;
        $flags = $this->all();
        if($flags && count(array_filter($flags, function($flag) use ($flagData){
            return $flag['name'] == $flagData['name'] || $flag['encrypted_name'] == $flagData['encrypted_name'];
        }))){
            $this->errors[] = 'Flag already exists.';
            return false;
        }
        
        try {
            $flags[] = $flagData;
            file_put_contents($this->flagsFile, json_encode($flags));
        } catch(\Exception $e){
            $created = false;
            $this->errors[] = 'Creating a flag failed. Please try again.';
        }

        return $created;
    }
    
    public function delete($flagData){
        $this->checkDir();
        $deleted = true;
        $flags = $this->all();
        if($flags && !count(array_filter($flags, function($flag) use ($flagData){
            return $flag['name'] == $flagData['name'] || $flag['encrypted_name'] == $flagData['encrypted_name'];
        }))){
            $this->errors[] = 'Unable to find flag: ' . $flagData['name'];
            return false;
        }

        $filteredFlags = array_filter($flags, function($flag) use ($flagData){
            return $flag['name'] != $flagData['name'] || $flag['encrypted_name'] != $flagData['encrypted_name'];
        });
        
        try {
            file_put_contents($this->flagsFile, json_encode($filteredFlags));
        } catch(\Exception $e){
            $deleted = false;
            $this->errors[] = 'Deleting the flag failed. Please try again.';
        }

        return $deleted;
    }
    
    public function update($flagData){
        $this->checkDir();
        $updated = true;
        $flags = $this->all();
        if($flags && !count(array_filter($flags, function($flag) use ($flagData){
            return $flag['name'] == $flagData['old_name'] && $flag['encrypted_name'] == $flagData['old_encrypted_name'];
        }))){
            $this->errors[] = 'Unable to find flag: ' . $flagData['old_name'];
            return false;
        }

        $filteredFlags = array_filter($flags, function($flag) use ($flagData){
            return $flag['name'] == $flagData['old_name'] && $flag['encrypted_name'] == $flagData['old_encrypted_name'];
        });

        if(!isset(array_keys($filteredFlags)[0])) return false;

        unset($flagData['old_name']);
        unset($flagData['old_encrypted_name']);
        
        try {
            $flags[array_keys($filteredFlags)[0]] = $flagData;
            file_put_contents($this->flagsFile, json_encode($flags));
        } catch(\Exception $e){
            $updated = false;
            $this->errors[] = 'Updating the flag failed. Please try again.';
        }

        return $updated;
    }

    public function getAllForTable($tableOptions){
        $this->checkDir();
        $flags = $this->all();
        if(!$flags) return [];

        foreach($flags as $index => $flag){
            $flags[$index]['combined_values'] = !$flags[$index]['is_filtered'] ? 'Anything' : '';
            if(!$flags[$index]['is_filtered']) continue;
            foreach($flag['accepted_values'] as $acceptedValue){
                if(is_null($acceptedValue['value']) || !trim($acceptedValue['value'])) continue;
                $flags[$index]['combined_values'] .= $acceptedValue['value'] . ' -> ' . $acceptedValue['encrypted_value'] . '<br>';
            }
        }

        $flags = $this->filterFlags($flags, $tableOptions);

        return array_slice($flags, (($tableOptions['page'] - 1) * $tableOptions['perPage']), $tableOptions['perPage']);
    }

    private function filterFlags($flags, $tableOptions){
        $this->checkDir();
        if(!empty($tableOptions['filter'])){
            $filter = $tableOptions['filter'];
            $flags = array_filter($flags, function($flag) use ($filter) {
                return trim($flag['name']) == trim($filter) || trim($flag['encrypted_name']) == trim($filter);
            });
        }
        return $flags;
    }

    public function getAllCountForTable($tableOptions){
        $this->checkDir();
        $flags = $this->all();
        if(!$flags) return 0;

        $flags = $this->filterFlags($flags, $tableOptions);
        
        return count($flags);
    }
    
    public function all(){
        $this->checkDir();
        $flags = [];
        $retrieved = true;
        try {
            $flags = json_decode(@file_get_contents($this->flagsFile), true);
        } catch(\Exception $e){
            $retrieved = false;
            $this->errors[] = 'Cannot retrieve flags. Please try again';
        }
        return $retrieved ? $flags : false;
    }

    public function errors(){
        return $this->errors;
    }

    public function register(){
        $this->checkDir();
        $flags = $this->all();
        if(!$flags) return [];
        foreach($flags as $flag){
            $value = $this->checkFlag($flag, !empty($flag['default']) ? $flag['default'] : null);
            setcookie($flag['encrypted_name'], $value['cookie_value'], time() + (86400 * 30), "/");
        }
    }

    private function filterFlag($flag, $needle, $defaultValue){
        $this->checkDir();
        if(!$flag['is_filtered']) return array('value' => $needle, 'cookie_value' => $needle );

        $acceptedValues = $flag['accepted_values'];
        if(isset($flag['both_usable'])){
            if($flag['both_usable']){
                $flag_value = '';
                foreach($acceptedValues as $value) {
                    if($value['encrypted_value'] == $needle || $value['value'] == $needle)
                        $flag_value = $value['value'];
                }
                return $flag_value != '' ? array('value' => $flag_value, 'cookie_value' => $needle ) : array('value' => $defaultValue, 'cookie_value' => $defaultValue );
            }
        }
        $flag_value = '';
        foreach($acceptedValues as $value) {
            if($value['encrypted_value'] == $needle)
                $flag_value = $value['value'];
        }
        return $flag_value != '' ? array('value' => $flag_value, 'cookie_value' => $needle ) : array('value' => $defaultValue, 'cookie_value' => $defaultValue );
    }

    public function checkFlag($flag, $defaultValue, $driver = 'cookie'){
        $this->checkDir();
        if((isset($flag['both_usable']) && $flag['both_usable'])){
            if(!empty($_GET[$flag['name']])) return $this->filterFlag($flag, $_GET[$flag['name']], $defaultValue);
            if(!empty($_GET[$flag['encrypted_name']])) return $this->filterFlag($flag, $_GET[$flag['encrypted_name']], $defaultValue);
            if(!empty($_COOKIE[$flag['encrypted_name']])) return $this->filterFlag($flag, $_COOKIE[$flag['encrypted_name']], $defaultValue);
        }
        $flagName = $flag['is_encrypted'] ? $flag['encrypted_name'] : $flag['name'];
        return !empty($_GET[$flagName]) ? $this->filterFlag($flag, $_GET[$flagName], $defaultValue) : (!empty($_COOKIE[$flag['encrypted_name']]) ? $this->filterFlag($flag, $_COOKIE[$flag['encrypted_name']], $defaultValue) : $defaultValue);
    }
    
    public function getFlag($flag, $encrypted){
        $this->checkDir();
        $flags = $this->all();
        if(!$flags) return [];
        $filteredFlags = array_values(array_filter($flags, function($f) use ($flag, $encrypted){
            if(isset($f['both_usable']) && $f['both_usable']) return $flag == $f['name'] || $flag == $f['encrypted_name'];
            return (($encrypted && $f['encrypted_name'] == $flag) || (!$encrypted && $f['name'] == $flag));
        }));
        return count($filteredFlags) ? $filteredFlags[0] : null;
    }
    
}