Статьи

Освоение ролей и возможностей WordPress

Управление пользователями в WordPress основано на ролях и возможностях.

Роль — это объект, состоящий из уникального имени и набора возможностей. Каждая возможность используется для определения, имеет ли роль доступ к определенной функции платформы.

Давайте подробнее рассмотрим, как работают роли и возможности WordPress.

Под капотом

Хранение ролей

Список ролей и возможностей по умолчанию доступен в Кодексе WordPress .

База данных хранит этот список в таблице wp_options .

Он использует сериализованный ключ wp_user_roles .

таблица wp_options

Несериализованные данные выглядят так:

 array( 'administrator' => array( 'name' => 'Administrator', 'capabilities' => array( 'switch_themes' => true, 'edit_themes' => true, 'activate_plugins' => true, 'edit_plugins' => true, 'edit_users' => true, // [...] ) ), 'contributor' => array( 'name' => 'Contributor', 'capabilities' => array( 'delete_pages' => true, 'delete_others_pages' => true, 'delete_published_pages' => true, 'delete_posts' => true, // [...] ) ), // [...] ); 

Эта мета автоматически устанавливается при установке нового сайта WordPress.

Когда WordPress запускается, класс WP_Roles загружает список из базы данных.

Это происходит между plugins_loaded и init hooks.

Связывание пользователей с ролями

WordPress использует meta_key , хранящийся в таблице wp_usermeta , чтобы связать пользователя с его ролью.

таблица wp_usermeta

После несериализации мета выглядит так:

 array( 'administrator' => true ) 

Обратите внимание, что WordPress использует массив, хотя пользователь может иметь только одну роль за раз, позже мы увидим, почему.

Кроме того, имейте в виду, что часть wp_ является префиксом текущего блога.

(Мы можем получить его, используя $GLOBALS['wpdb']->get_blog_prefix() ).

В многосайтовой установке это позволит пользователю использовать разные роли в разных экземплярах:

  • wp_capabilities => a:1:{s:13:"administrator";b:1;}
  • wp_10_capabilities => a:1:{s:11:"contributor";b:1;}
  • wp_15_capabilities => a:1:{s:10:"subscriber";b:1;}
  • [...]

Это правило также применяется к записи wp_user_roles которую мы видели ранее, в таблице wp_options .

Наконец, мы можем увидеть метаданные wp_user_level вместе с ролью.

Он использовался для обработки ролей в старых версиях WordPress и теперь устарел .

Работа с возможностями в ядре

Мы видели, как роли загружаются и связаны с пользователями; Оттуда WordPress может получить возможности данного пользователя, когда это необходимо.

Несколько возможностей по умолчанию жестко запрограммированы в ядре WordPress.

Например, при загрузке экрана плагина он проверяет, может ли текущий пользователь управлять плагинами, выполнив следующий код:

 if (!current_user_can('activate_plugins')) { wp_die(__('You do not have sufficient permissions to manage plugins for this site.')); } 

Роли никогда не жестко закодированы; роль — это только оболочка возможностей, она существует только в базе данных.

Работа с ролями и возможностями: API WordPress

Доступ к API

WordPress предоставляет следующие глобальные функции, помогающие нам работать с ролями.

current_user_can ()

Проверяет, обладает ли текущий пользователь необходимой возможностью.

 add_action('init', function() { if (current_user_can('install_plugins')) { echo 'you can install plugins'; } else { echo 'You cannot install plugins'; } }); 

WP_User :: has_cap

Проверяет, принадлежит ли конкретный пользователь возможности.

 add_action('init', function() { $user = get_user_by('slug', 'admin'); if ($user->has_cap('install_plugins')) { echo 'Admin can install plugins'; } else { echo 'Admin cannot install plugins'; } }); 

Мы можем заметить, что current_user_can использует эту функцию.

get_editable_roles ()

Возвращает доступные роли.

 add_action('admin_init', function() { $roles = get_editable_roles(); var_dump($roles); }); 

Список может быть переопределен фильтром editable_roles , поэтому мы не должны полагаться на эту функцию для получения полного списка ролей на веб-сайте.

Обратите внимание на использование admin_init , так как функция еще не загружена в init .

get_role ()

Получает объект WP_Role из его слаг.

 add_action('init', function() { $role = get_role('administrator'); var_dump($role); }); // This will print: // WP_Role Object // ( // [name] => administrator // [capabilities] => Array // ( // [switch_themes] => 1 // [edit_themes] => 1 // [activate_plugins] => 1 // [edit_plugins] => 1 // [...] 

WP_Role :: has_cap ()

Проверяет, обладает ли роль необходимыми возможностями.

 add_action('init', function() { $role = get_role('administrator'); var_dump($role->has_cap('install_plugins')); // Prints TRUE }); 

Настройка API

WordPress также предлагает полный API для настройки ролей и их возможностей.

add_role ()

Регистрирует новую роль в базе данных.

 add_action('init', function() { add_role('plugins_manager', 'Plugins Manager', array( 'install_plugins', 'activate_plugins', 'edit_plugins' )); }); 

remove_role ()

Удаляет требуемую роль из базы данных, если она существует.

 add_action('init', function() { remove_role('plugins_manager'); }); 

WP_Role :: add_cap ()

Добавляет возможность к роли.

 add_action('init', function() { $role = get_role('contributor'); $role->add_cap('install_plugins'); }); 

Это может быть базовая возможность ( install_plugins , edit_posts ,…) или любая пользовательская строка ( my_awesome_plugin_cap ).

Это позволяет нам регистрировать столько пользовательских возможностей, сколько нам нужно для наших плагинов.

WP_Role :: remove_cap ()

Удаляет возможность из роли, если она существует.

 add_action('init', function() { $role = get_role('contributor'); $role->remove_cap('install_plugins'); }); 

WP_User :: add_role ()

Добавляет роль данному пользователю.

 add_action('init', function() { $user = get_user_by('slug', 'admin'); $user->add_role('contributor'); }); 

Эта функция позволяет теоретически установить много ролей для одного и того же пользователя.

Поскольку бэкэнд WordPress отображает только одну роль для каждого пользователя и управляет WP_User::remove_role() , нам не следует добавлять несколько ролей для пользователя и всегда использовать WP_User::remove_role() перед добавлением новой.

WP_User :: remove_role ()

Удаляет роль из данного пользователя.

 add_action('init', function() { $user = get_user_by('slug', 'admin'); $user->remove_role('administrator'); }); 

WP_User :: add_cap ()

Добавляет возможность данному пользователю.

 add_action('init', function() { $user = get_user_by('slug', 'admin'); $user->add_cap('my_custom_cap'); }); 

Таблица wp_usermeta с пользовательскими заглавными буквами

Это может быть полезно, если мы хотим добавить одну возможность пользователю без необходимости создавать полную роль.

WP_User :: remove_cap ()

Удаляет возможность у данного пользователя.

 add_action('init', function() { $user = get_user_by('slug', 'admin'); $user->remove_cap('my_custom_cap'); }); 

Несколько проблем с API WordPress

С функциями, которые мы видели, все выглядит хорошо, кроме одного: доступа к базе данных и производительности.

Основная проблема, с которой мы сталкиваемся при работе с ролями и возможностями, — когда мы должны запускать наш код?

Чтобы объяснить это, давайте посмотрим на код ядра WordPress.

Сначала мы хотим добавить новую пустую роль:

 add_action('init', function() { add_role('plugins_manager', 'Plugins Manager', array()); }); 

Вот первые строки функции add_role (которая фактически перенаправляет на WP_Roles::add_role ):

 public function add_role( $role, $display_name, $capabilities = array() ) { if ( isset( $this->roles[$role] ) ) return; 

Если мы добавляем новую роль, функция add_role запускается один раз, а затем ничего не делает.

Далее, скажем, мы хотим добавить возможность в нашу недавно созданную роль:

 add_action('init', function() { $role = get_role('plugins_manager'); $role->add_cap('install_plugins'); }); 

Функция WP_Role::add_cap() в WordPress 4.2.2 выглядит следующим образом:

 public function add_cap( $role, $cap, $grant = true ) { if ( ! isset( $this->roles[$role] ) ) return; $this->roles[$role]['capabilities'][$cap] = $grant; if ( $this->use_db ) update_option( $this->role_key, $this->roles ); } 

Он обновляет объект $this->roles , но мы также видим, что база данных будет обновляться при каждом запуске нашего кода, даже если наша новая возможность уже зарегистрирована!

Это означает, что если мы заботимся о производительности, весь код, который мы создаем для настройки ролей и возможностей, не должен запускаться при каждой загрузке страницы.

обходные

Есть несколько вариантов, чтобы избежать этих проблем с базой данных.

Работа с активацией плагина

WordPress позволяет авторам плагинов запускать код при включении плагина из бэкэнда с помощью функции register_activation_hook() .

Давайте создадим пример плагина:

 /* Plugin Name: Our sample role plugin */ register_activation_hook(__FILE__, function() { $role = add_role('plugins_manager', 'Plugins Manager', array()); $role->add_cap('install_plugins'); }); 

Этот код будет запускаться только один раз при включении плагина на сайте.

Теперь нужно помнить, что это решение зависит от активации и деактивации плагина.

Что произойдет, если плагин уже работает, или его повторная активация пропущена при обновлении?

На самом деле, это решение зависит также от базы данных и требует дополнительного шага при загрузке кода.

В обход базы данных WordPress

Существует второе недокументированное решение, которое в некоторых случаях может хорошо работать.

Давайте в последний раз посмотрим на ядро ​​WordPress, когда объект WP_Roles загружает роли из базы данных при загрузке WordPress:

 protected function _init() { global $wpdb, $wp_user_roles; $this->role_key = $wpdb->get_blog_prefix() . 'user_roles'; if ( ! empty( $wp_user_roles ) ) { $this->roles = $wp_user_roles; $this->use_db = false; } else { $this->roles = get_option( $this->role_key ); } 

Прежде чем получать данные из базы данных, WordPress проверяет глобальную переменную $wp_user_roles .

Если установлено, WordPress будет использовать его содержимое и заблокировать использование базы данных, установив для переменной $this->use_db значение false .

Давайте попробуем это, сохранив только новую, ограниченную роль administrator :

 /* Plugin Name: Our sample role plugin */ $GLOBALS['wp_user_roles'] = array( 'administrator' => array( 'name' => 'Administrator', 'capabilities' => array( 'activate_plugins' => true, 'read' => true, ) ) ); 

При загрузке серверной части мы видим, что она сохранила определение нашей пользовательской роли:

пользовательский бэкэнд

Это решение решает проблему с базой данных, но может вводить некоторые другие:

  • Плагины, которые используют собственный API, могут работать неправильно.
  • Мы должны вручную установить определение каждой роли, даже той, которую мы не хотим менять.

Тем не менее, при создании пользовательского приложения WordPress, которому необходим пользовательский статический список ролей, это может быть возможным решением:

  • Определение ролей может быть версионировано с помощью кода.
  • Выдвижение нового кода в среду автоматически обновит определение.
  • Больше нет вопросов о базе данных.

Вывод

В этой статье я представил обзор использования ролей и возможностей в WordPress.

Хотя его полный API позволяет нам делать почти все, что мы хотим, отношение к базе данных остается главной задачей.

Мы должны помнить об этом при разработке наших плагинов и тем.

Что вы думаете о том, как WordPress управляет ролями? Я с нетерпением жду ваших отзывов!