Статьи

Конфиденциальность и оптимизация виджета WordPress Dashboard

Конечный продукт
Что вы будете создавать

В первых двух частях этой серии мы завершили полнофункциональный плагин, который показывает нам статус сервера в виде виджета на приборной панели. Как таковой, он доступен каждому зарегистрированному пользователю. Некоторая информация может быть конфиденциальной, и мы не хотим, чтобы они ее видели, поэтому лучше проверить роль пользователя, чтобы определить, следует ли нам сделать виджет доступным для них.

WordPress использует концепцию ролей , разработанную, чтобы дать владельцу сайта возможность контролировать то, что пользователи могут и не могут делать на сайте. Каждой роли разрешено выполнять набор задач под названием « Возможности» . Мы можем настроить роль и ее возможности с помощью функций add_roles и add_cap .

Наш плагин создаст новую возможность вызова servermetric . Только пользователь, обладающий такой возможностью, может загрузить наши виджеты панели мониторинга. Мы добавим эту возможность для роли администратора, чтобы все пользователи администратора видели ее по умолчанию.

Для других пользователей вы можете использовать плагин User Role Editor для управления возможностями любого конкретного пользователя, а также назначить для servermetic возможность servermetic .

Мы используем add_cap для добавления новой возможности, однако, эта функция записывает в базу данных, поэтому мы должны делать это только при активации плагина. После деактивации мы должны очистить базу данных, удалив роль с помощью remove_cap .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Dashboard {
  //…other code
  const CAP_METRIC = ‘server_metric’;
   
  /**
   * Start to setup hook
   */
  public function run() {
    add_action( ‘wp_dashboard_setup’, array( $this, ‘add_dashboard_widgets’ ) );
    add_action( ‘admin_enqueue_scripts’, array($this, ‘add_asset’));
    add_action( ‘admin_footer’, array($this, ‘footer’));
 
    register_activation_hook(__FILE__, array($this, ‘add_servermetric_caps’));
    register_deactivation_hook(__FILE__, array($this, ‘remove_servermetric_caps’));
  }
   
  /**
   * Add severmetric capability for admin by default
   */
  function add_servermetric_caps() {
    // gets the author role
    $role = get_role( ‘administrator’ );
    // This only works, because it accesses the class instance.
    // would allow the author to edit others’ posts for current theme only
    $role->add_cap( self::CAP_METRIC );
  }
 
  function remove_servermetric_caps() {
    // get_role returns an instance of WP_Role.
      $role = get_role( ‘administrator’ );
    $role->remove_cap( self::CAP_METRIC );
  }
   
  //…
}

Мы создаем новый константный вызов CAP_METRIC и устанавливаем его значение равным server_metric чтобы впоследствии легко было легко изменить имя возможности. Мы модифицируем наш метод run для добавления двух хуков.

Register_activation_hook запускается при активации плагина. Он принимает два параметра:

  1. (строка) имя файла: путь к основному файлу плагина
  2. ( обратный вызов ) ( обязательно ) Функция, запускаемая при активации плагина

register_deactivation_hook запускается при деактивации плагина. Он принимает тот же параметр, что и register_activation_hook.

Внутри каждой подключенной функции мы будем загружать administrator ролей и вызывать add_cap или remove_cap для объекта ролей .
Далее мы add_dashboard_widgets наш метод add_dashboard_widgets чтобы зарегистрировать виджеты только в том случае, если текущий пользователь имеет servermetric .

01
02
03
04
05
06
07
08
09
10
11
12
/**
  * Register dashboard widget proider to show up on dashboard
  */
 function add_dashboard_widgets() {
   if (!current_user_can(self::CAP_METRIC)) {
     return false;
   }
   $widget = Widget::instance();
   foreach ($widget->get_provider() as $name=>$provider) {
     $widget->register($name);
   }
 }

Затем мы используем current_user_can, чтобы проверить, имеет ли текущий пользователь возможность запроса.

Теперь только администратор увидит виджет при загрузке панели инструментов. Если вы хотите включить виджет состояния сервера для других пользователей, вы можете установить плагин User Role Editor для управления ролями и возможностями для любых пользователей.

После установки и активации перейдите в меню «Пользователи», выберите « Пользователь»> «Возможности»:

Затем на экране возможностей мы можем назначить ограничение server_metric .

Редактирование пользовательских возможностей с помощью плагина User Role Editor

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

WordPress использует API-интерфейс Transient в качестве API-интерфейса кеша. Данные сериализуются и сохраняются в таблицу wp_option WordPress с указанием срока действия кэша.

Вместо того, чтобы получать метрические данные для каждого HTTP-запроса, мы можем получить данные один раз и кэшировать их. Однако мы не можем просто поместить все в кеш или использовать то же время истечения. Например, дисковое пространство можно кэшировать в течение 15 минут, а информацию о сервере можно кэшировать в течение 60 минут, поскольку они редко меняются. Точно так же установленное программное обеспечение может быть кэшировано на день, так как оно редко изменяется после настройки сервера и подготовки к работе.

В основном мы используем get_transient и set_transient при работе с API. Согласно документации WordPress:

  1. get_transient( $transient ) : получить временное имя в виде строки и вернуть его данные. Если срок действия данных истек, возвращается false. Мы должны использовать оператор === для проверки, потому что мы можем хранить пустое значение для переходного процесса.
  2. set_transient( $transient, $value, $expiration ) : извлекает три параметра: имя переходного процесса, его значение и время его истечения в секундах. Обратите внимание, что временное имя не должно быть длиннее 45 символов.

Наши два варианта — рассмотреть возможность кэширования данных метрики или кэширования сгенерированных данных HTML. Кэширование данных HTML может сделать наш сайт очень быстрым, но это создает нагрузку на базу данных. С этой целью мы могли бы сделать тест, чтобы решить, какой из них лучше.

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

Мы можем напрямую использовать функцию get_transient или set_transient для работы с Transient API. Однако, если мы решим изменить способ, которым мы использовали Transient API, мы должны перейти к каждому месту, где мы его используем, и изменить его для каждого виджета.

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

  1. set : установить данные кеша для виджета
  2. get : получить данные кеша для виджета
  3. load : попытаться загрузить из кеша, если не существует, рассчитать данные, установить кеш и вернуть

Давайте widget/cache.php файл widget/cache.php следующим образом. Обратите внимание, что в соответствии с соглашением об автоматической загрузке имя класса будет Cache а его пространство имен — AX\StatBoard\Widget

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
namespace AX\StatBoard\Widget;
use AX\StatBoard\Widget\Provider;
 
class Cache {
  /**
   * Get cache for a particular widget
   */
  static function get(Provider $provider) {
    $cache_id = get_class($provider);
    if (false !== $data = get_transient($cache_id)) {
      return $data;
    }
    return false;
  }
     
  /**
   * Default we cached 5 minutes
   */
  static function set(Provider $provider, $value, $cache_time = 300) {
    $cache_id = get_class($provider);
    set_transient($cache_id, $value, $cache_time);
  }
   
  /**
   * Load data from cache.
   * put into cache.
   *
   */
  static function load(Provider $provider, $cache_time = 300) {
    if (false !== $data = static::get($provider)) {
      return $data;
    }
    //no data yet, let’s pull it and put it into cache
    $data = $provider->get_metric();
    static::set($provider, $data, $cache_time);
    return $data;
  }
 
}

Во-первых, обратите внимание, что мы пометили наши методы кэширования как статические. Наши методы set и get являются просто обертками для get_transient и set_transient . Метод load находится поверх set и get . Все эти методы рассчитывают получить объект провайдера виджета; поэтому внутри метода load мы можем вызвать метод get_metric для получения реальных данных.

Здесь самое важное — это временное имя. Поскольку имя класса уникально в нашем приложении, мы считаем его достаточно уникальным для временного имени. Функция get_class возвращает имя класса объекта.

Время использовать наш класс Cache . Мы постараемся реализовать Cache для widget/software.php . Измените наш оригинальный метод get_content на:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<?php
namespace AX\StatBoard\Widget;
 
class Software implements Provider {
  //…
  public function get_content() {
    $cmds = Cache::load($this, 3600 * 24);
 
    $content = »;
    foreach ($cmds as $cmd=>$info) {
      $content .= «<p><strong>$cmd</strong>&nbsp; $info</p>»;
    }
    echo $content;
  }
  //…
}

Вы можете видеть, что мы избавились от $cmds = $this->get_metric() и просто заменили его на Cache::load который будет загружать данные из кеша или загружать их из системы, если кеша не было.

Обязательно передайте второй параметр, как долго вы хотите, чтобы данные кэшировались; в противном случае данные кэшируются только в течение пяти минут. Поскольку информация о программном обеспечении на сервере редко меняется в течение короткого времени, мы установили время кеширования на 24 часа.

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

1
Cache::load($this, $time_in_second);

чтобы он позаботился о своем кешировании.

Данные об использовании диска могут кэшироваться часами, а интерфейс Ethernet может кэшироваться в течение дня или около того. Вам решать, как долго вы хотите его кешировать. Мы также можем создать страницу параметров для плагина, чтобы управлять этими значениями времени жизни кэша. Это может быть упражнение для вас, чтобы продолжить работать после того, как мы закончили эту статью.

У нас есть один последний пример с widget/ethernet.php . Мы можем добавить кеш-способность следующим образом:

1
2
3
4
5
6
<?php
namespace AX\StatBoard\Widget;
 
class Ethernet implements Provider {
  //…
}

Опять же, нам нужно только заменить get_metric на Cache::load . Информация о Ethernet и его IP-адрес, вероятно, никогда не изменятся, поэтому я установил очень длительное время жизни кэша в одну неделю: 3600 секунд * 24 часа * 7 дней.

После того, как мы добавили возможность кэширования, мы должны поддерживать механизм, позволяющий администратору извлекать виджет без его кэширования. Самый простой способ сделать это — использовать специальный параметр запроса, чтобы указать, что нам нужны реальные данные.

Как насчет небольшого параметра, такого как nocache для этого? Таким образом, вместо стандартного URL панели WordPress с domain.com/wp-admin/ мы можем использовать domain.com/wp-admin/?nocache .

Звучит легко? Давай сделаем это.

Отредактируйте наш метод get в widget / cache.php

01
02
03
04
05
06
07
08
09
10
static function get(Provider $provider) {
   if (isset($_GET[‘nocache’])) {
     return false;
   }
   $cache_id = get_class($provider);
   if (false !== $data = get_transient($cache_id)) {
     return $data;
   }
   return false;
 }

Пока nocache параметр запроса nocache , мы мгновенно возвращаем false и, следовательно, заставляем nocache реальные данные вместо кэшированных данных.

Теперь давайте подумаем о добавлении этой функции без класса Cache. Возможно, нам придется перейти к каждой строке get_transient и проверить там параметр запроса. Поэтому, рассмотрите возможность разбить вещи на несколько слоев при разработке вашего плагина. Не помещайте все в один файл и не копируйте код вставки снова и снова.

Теперь давайте попробуем посетить domain.com/wp-admin and domain.com/wp-admin?nocache и заметить разную скорость.

Загрузка 987мс с включенным кешем

Вот результат с ?nocache=1 добавленным к URL.

3.01 секундная загрузка без кеша

Несмотря на то, что мы реализовали и использовали кеш, если кеш отсутствует, страница все еще работает медленно. Все еще нужно время, чтобы получить данные с сервера. У нас еще есть возможность улучшить cronjob. Мы можем запланировать запуск нашего плагина через определенный интервал. WordPress позволяет нам делать это через wp_schedule_event . В идеале мы можем использовать wp_schedule_event для планирования перехвата, который будет выполняться через определенный интервал.

Глядя на этот пример , наш плагин может запланировать перехват вызова каждые три минуты, перехватчик, в свою очередь, вызовет другую функцию для извлечения метрических данных. Данные всегда доступны в кеше и достаточно свежие.

Откройте наш основной файл плагина serverdashboard.php и обновите метод run, включив в него новый обработчик и новый обработчик обработчиков.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
 
 /**
   * Start to setup hook
   */
  public function run() {
    add_action( ‘wp_dashboard_setup’, array( $this, ‘add_dashboard_widgets’ ) );
    add_action( ‘admin_enqueue_scripts’, array($this, ‘add_asset’));
    add_action( ‘admin_footer’, array($this, ‘footer’));
 
    register_activation_hook(__FILE__, array($this, ‘add_servermetric_caps’));
    register_deactivation_hook(__FILE__, array($this, ‘remove_servermetric_caps’));
 
    //New code to handle cronjob
    add_filter( ‘cron_schedules’, array($this, ‘cron_3min’) );
    add_action( ‘metric_generate_every_3min’, array($this, ‘generate_metric’) );
    add_action( ‘init’, array($this, ‘setup_schedule’) );
 
  }
   
  /**
   * Define a new kind of interval
   * https://codex.wordpress.org/Function_Reference/wp_get_schedules
   */
  function cron_3min($schedules) {
    $schedules[‘3min’] = array(
        ‘interval’ => 3 * 60,
        ‘display’ => __( ‘Once every 3 minutes’ )
    );
    return $schedules;
  }
 
  /**
   * Setup schedule for event.
   * we register it to
   */
  function setup_schedule() {
    if ( ! wp_next_scheduled( ‘metric_generate_every_3min’ ) ) {
          wp_schedule_event( time(), ‘3min’, ‘metric_generate_every_3min’);
      }
 
  }
 
  /**
   * The main function that runs on cron and
   * generate data
   */
  function generate_metric() {
    $widget = Widget::instance();
    foreach ($widget->get_provider() as $name=>$provider) {
      //By calling get_content, we trigger Cache::load process.
      $provider->get_content();
    }
  }
}

Во-первых, метод wp_schedule_event поддерживает только три типа повторения: ежедневно, ежечасно и дважды. Мы должны добавить новый тип повторения с фильтром wp_get_schedules .

Мы добавляем еще один повторяющийся тип, который запускается каждые три минуты. Его определение:

1
2
3
4
5
$schedules[‘3min’] = array(
       ‘interval’ => 3 * 60,
       ‘display’ => __( ‘Once every 3 minutes’ )
   );
   return $schedules;

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

1
add_action( ‘metric_generate_every_3min’, array($this, ‘generate_metric’) );

Это наш пользовательский хук, он не существует в WordPress. Мы регистрируем дескриптор с методом generate_metric для этого хука. Всякий раз, когда metric_generate_every_3min хук metric_generate_every_3min , будет выполняться generate_metric .

В следующем операторе мы подключаемся к действию init с setup_schedule метода setup_schedule чтобы проверить наличие следующего запланированного события ловушки metric_generate_every_3min . Если он еще не определен, мы запланируем событие с помощью wp_schedule_event , используя наши пользовательские повторения каждые три минуты для этой ловушки.

Внутри метода generate_metric мы перебираем все доступные get_content виджета и вызываем их метод get_content . Делая это, мы запускаем процесс Cache::load для этой метрики.
WordPress автоматически запускает эти запланированные события всякий раз, когда кто-то посещает ваш сайт WordPress. Он попытается найти запланированное событие, которое нужно запустить, и вызвать его.

Тем не менее, вы также можете запустить их вручную. WordPress запускает cronjob, посещая файл wp-content.php с URL yourdomain.com/wp-cron.php?doing_wp_cron .

Возможно, вы захотите обновить свой cronjob, чтобы добавить новую работу, которая пингует вышеуказанный URL каждую минуту
Давайте откроем ваш crontab на сервере с помощью crontab -e и добавим в конце следующую строку:

1
0 * * * *wget domain.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Мы использовали wget для отправки HTTP-запроса в файл wp-cron.php. Так как нас не волнует вывод и какие-либо ошибки, мы перенаправляем весь вывод в /dev/null .

Вы можете прочитать больше о настройке этих cronjob в следующих статьях:

  1. http://tommcfarlin.com/wordpress-cron-jobs/
  2. Http: //code.tutsplus.com/articles/insights-into-wp-cron-an-introduction-to-scheduling-tasks-in-wordp …
На этом мы завершаем наш длинный урок о том, как создать виджет панели управления сервером, который дает представление о различных аспектах нашей системы.
В этой серии мы использовали сторонние библиотеки, взяли идею, поэкспериментировали с командной строкой, узнали о ролях и возможностях, а также рассмотрели возможности WordPress Transient, а также механизмы планирования событий.
Наконец, мы связали все это вместе в плагине WordPress.
Пожалуйста, оставьте комментарий и дайте нам знать, какие дополнительные идеи и изменения вы предлагаете, а также любые вопросы и / или комментарии, которые могут у вас возникнуть по поводу этой конкретной серии.