Первая попытка - оболочка API-интерфейса Wordpress PHP

Я работал над тем, что по сути является моей первой правильной попыткой создания полезного класса оболочки PHP для Wordpress Settings API. Он отлично работает до сих пор, и я планирую добавить больше методов, поскольку я иду так, чтобы в конечном итоге создать класс, который можно использовать практически в любой ситуации.

Поскольку это мой первый правильный класс PHP, я ищу предложения о том, как я мог бы реорганизовать или сделать что-то более эффективно. Возможно, места, где я мог бы писать код по-разному /использовать разные функции, чтобы улучшить его.

Код также находится на моем GitHub repo , поэтому я был бы признателен за конструктивную обратную связь.

<?php
/**
 * A PHP Wrapper class to help set up an options page for the Wordpress API.
 *
 * @package Wordpress
 * @subpackage ThemeName
 * @version 1.0
 *
 */
class ThemeOptions
{
    // Declare class variables
    public $pages = array();
    public $sections = array();
    public $fields = array();

    /**
     * Construct
     *
     * @since 1.0
     */
    public function __construct()
    {   

        // Set the pages, sections and fields   
        add_action( 'admin_menu', array( &$this, 'addOptionsPages' ) );
        add_action( 'admin_init', array( &$this, 'initializeThemeOptions' ) );

    }

    /**
     * Add the options pages
     *
     * @since 1.0
     * @todo Add support for other menu types
     */
    public function addOptionsPages()
    {   

        foreach ( $this->pages as $page )
        {
            if ($page['type']=='menu')
            {
                add_menu_page(
                    __( $page['title'] ),
                    __( $page['title'] ),
                    'administrator',
                    $page['id'],
                    array( &$this, 'displayOptionsPage' )
                );
            }
            else if ($page['type'] == 'submenu')
            {
                add_submenu_page(
                    $page['parent'],
                    __( $page['title'] ),
                    __( $page['title'] ),
                    'administrator',
                    $page['id'],
                    array( &$this, 'displayOptionsPage' )
                );
            }
        }

    }

    /**
     * Add the settings sections
     *
     * @since 1.0
     */
    private function addOptionsSections()
    {
        foreach( $this->sections as $section )
        {
            add_settings_section(
                $section['id'],
                __($section['title']),
                '', // Add the description for the section in later
                $section['page']
            );
        }
    }


    /**
     * Add the settings fields
     *
     * @since 1.0
     */
    private function addOptionsFields()
    {
        foreach ($this->fields as $section => $field)
        {
            foreach ($field as $option)
            {
                add_settings_field(
                    $option['name'],
                    __($option['label']),
                    array(&$this, 'display' . ucfirst(strtolower($option['type'])) . 'Field'),
                    $section,
                    $option['section'],
                    array(
                        'option'        => $section,
                        'optionName'    => $option['name'] 
                    )
                );
            }
        }
    }

    /**
     * Register the settings with register_setting()
     *
     * @since 1.0
     */
    private function registerSettings()
    {
        foreach ($this->pages as $page)
        {
            register_setting(
                $page['id'],
                $page['id']
            );
        }
    }

    /**
     * Display options page
     *
     * @since 1.0
     */
    public function displayOptionsPage()
    {
        if (isset($_GET['tab']))
        {
            $activeTab = $_GET['tab'];
        }
        else
        {
            $activeTab = strtolower( str_replace('_', '-', $_GET['page']) );
        }
    ?>
        <div class="wrap">
            <div id="icon-themes" class="icon32"></div>
            <h2><?php _e( $this->pageTitle ); ?></h2>
            <?php
            settings_errors();
            $this->displayOptionsTabs( $activeTab );
            ?>
            <form method="post" action="options.php">
                <?php $this->displayOptionsSettings( $activeTab ); ?>
                <?php submit_button(); ?>
            </form>
        </div>
    <?php
    }

    /**
     * Set the pages, sections and fields
     *
     * @since 1.0
     */
    public function initializeThemeOptions()
    {
        $this->setOptionsDefaults();
        // Create the sections
        $this->addOptionsSections();
        // Create the fields
        $this->addOptionsFields(); 
        // Register the settings
        $this->registerSettings();

    }

    /**
     * Display the tabs
     *
     * @param string $activeTab;
     * @since 1.0
     */
    private function displayOptionsTabs( $activeTab = '' )
    {
        if ( count($this->pages) > 0 )
        {   

            $output = '<h2 class="nav-tab-wrapper">';

            foreach ( $this->pages as $page )
            {
                $currentTab = strtolower(str_replace('_', '-', $page['id']));
                $activeClass = $activeTab == $currentTab ? ' nav-tab-active' : '';

                $output .= sprintf(
                    '<a href="?page=%1$s&tab=%2$s" class="nav-tab%4$s" id="%2$s-tab">%3$s</a>',
                    $page['id'],
                    $currentTab,
                    $page['title'],
                    $activeClass
                );
            }

            $output .= '</h2>';
            echo $output;
        }
    }

    /**
     * Display the sections and fields according to the page
     *
     * @param string $activeTab;
     * @since 1.0
     */
    private function displayOptionsSettings( $activeTab = '' )
    {
        $currentTab = strtolower( str_replace('-', '_', $activeTab) );
        settings_fields( $currentTab );
        do_settings_sections( $currentTab );
    }

    /**
     * HTML output for text field
     *
     * @param array $option;
     * @since 1.0
     */
    public function displayTextField( $option = array() )
    {
        $value = get_option( $option['option'] );

        printf(
            '<input type="text" id="%1$s" class="regular-text" name="%2$s[%3$s]" value="%4$s">',
            str_replace('_', '-', $option['optionName']),
            $option['option'],
            $option['optionName'],
            sanitize_text_field($value[$option['optionName']])
        );

    }

    /**
     * HTML output for textarea field
     *
     * @param array $option;
     * @since 1.0
     */
    public function displayTextareaField( $option = array() )
    {
        $value = get_option( $option['option'] );

        printf(
            '<textarea id="%1$s" name="%2$s[%3$s]" rows="5" cols="60">%4$s</textarea>',
            str_replace('_', '-', $option['optionName']),
            $option['option'],
            $option['optionName'],
            esc_textarea($value[$option['optionName']])
        );

    }

    /**
     * HTML output for url field
     *
     * @param array $option;
     * @since 1.0
     */
    public function displayUrlField( $option = array() )
    {
        $value = get_option( $option['option'] );

        printf(
            '<input type="url" id="%1$s" class="regular-text" name="%2$s[%3$s]" value="%4$s">',
            str_replace('_', '-', $option['optionName']),
            $option['option'],
            $option['optionName'],
            esc_url($value[$option['optionName']])
        );
    }

    /**
     * Set default values for the settings.
     *
     * @since 1.0
     */
    private function setOptionsDefaults()
    {
        $defaults = array();

        foreach ($this->fields as $optionsName => $options)
        {
            $defaults[$optionsName] = array();

            foreach($options as $option)
            {
                $defaults[$optionsName][$option['name']] = '';
            }

        }
        foreach ($defaults as $option => $value)
        {
            if( get_option( $option ) == false )
            {
                add_option(
                    $option,
                    apply_filters( $option . '_defaults', $value )
                );
            }
        }

    }

    /**
     * Validate the input depending on it's type. Used in register_setting().
     *
     * @since 1.0
     * @todo Write the function!!!
     */
    public function validateOptions( $input )
    {
        return $input;
    }

}
11 голосов | спросил glitchmunki 14 Maypm14 2014, 16:21:05

2 ответа


7

Первая маленькая вещь, которая появляется, - array( &$this, 'method_name' ). Это PHP 4, и вы можете полностью отказаться от & (WordPress требуется PHP 5.2.4 +).

Во-вторых, если вы собираетесь использовать локализацию , лучше разместить все на месте по всему вашему коду, то есть: $translated = __( 'Hello World!', 'mytextdomain' );.

И, наконец, возможно, это микро-оптимизация, говорится, что === быстрее, чем == .

Примечания относительно примеров кода ниже:

Интерфейс

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

Метод addOptionsPages() будет:

public function add_options_pages() { 
    foreach ( $this->pages as $page ) {
        if ( $page['type'] == 'menu' )
            add_theme_page( $args );
        else if ( $page['type'] == 'submenu' )
            add_submenu_page(
                null, # <-- Invisible page attached to /wp-admin/options.php?page=THIS-SUBMENU
                __( $page['title'], 'mytextdomain' ),
                __( $page['title'], 'mytextdomain' ),
                'administrator',
                $page['id'],
                array( $this, 'display_options_page' )
            );
    }
}

И настройте метод displayOptionsTabs(), добавив полный URL-адрес в %5$s:

$output .= sprintf(
    '<a href="%5$s?page=%1$s&tab=%2$s" class="nav-tab%4$s" id="%2$s-tab">%3$s</a>',
    $page['id'],
    $currentTab,
    $page['title'],
    $activeClass,
    ( $page['type'] == 'menu' ) ? admin_url( 'themes.php') : admin_url( 'options.php')
);

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

Учитывая, что все это заканчивается исправлением jQuery, вы можете рассматривать только add_theme_page() и обрабатывать отображение вкладок, выполняя switch( $_GET['tab'] ) {}

Скрипты и стили

Чтобы настроить таргетинг на наши собственные страницы, функции add_(sub)menu_page возвращают имя крюка, которое мы можем использовать, например:

$hook = add_menu_page( $args );
add_action( "admin_print_scripts-$hook", array( $this, 'print_scripts' );

public function print_scripts(){
    ?>
    <style> /* declare styles */ </style>
    <script>/* run scripts */ </style>
    <?php
}

И для установки в очередь и использования вашего кода:

private $hooks = array();

public function add_options_pages() { 
    foreach ( $this->pages as $page ) {
        if ($page['type']=='menu')
            $this->hooks[] = add_theme_page( $args );
        else if ($page['type'] == 'submenu')
            $this->hooks[] = add_submenu_page( $args );
    }
    add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
}

public function enqueue( $hook ){
    if( !in_array( $hook, $this->hooks ) )
        return;
    wp_enqueue_script( 'my-scp', plugins_url( '/js/my-script.js', __FILE__) ); # Adjust to use with themes
    wp_enqueue_style( 'my-stl', plugins_url( '/css/my-style.js', __FILE__) ); 
}

API настроек

Единственное, что мне не хватает, это проверка и дезинфекция в register_setting( $option_group, $option_name, $sanitize_callback ); . Две отличные рекомендации ведущих разработчиков WP:

  • Учебное пособие по API WordPress , Отто Вуд

    // validate our options
    function plugin_options_validate( $input ) {
        $newinput['text_string'] = trim( $input['text_string'] );
        if( !preg_match( '/^[a-z0-9]{32}$/i', $newinput['text_string'] ) )
            $newinput['text_string'] = '';
        return $newinput;
    }
    
      

    Особо обратите внимание на то, что я не возвращаю исходный массив. [...] Короче говоря, $ input - это недоверенные данные, но возвращаемые $ newinput должны быть доверенными данными.

  • API-интерфейс WordPress , Константин Ковшенин

    function my_settings_validate( $input ) {
        $output = get_option( 'my-settings' );        
        if ( is_email( $input['email'] ) )
            $output['email'] = $input['email'];
        else
            add_settings_error( 'my-settings', 'invalid-email', 'You have entered an invalid e-mail address.' );        
        return $output;
    }
    
      

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

Вам, вероятно, придется добавить тип проверки внутри вашего кода $PBThemeOptions->fields;

База данных

В setOptionsDefaults() вы сохраняете один параметр для каждого элемента в массиве полей ( example-options.php в вашем репо ):

$fields = array(
    // Example Section One
    'sandbox_theme_menu' => array( /* etc */ ),
    // Example Section Two
    'sandbox_theme_display_options' => array( /* etc */ )
);

Это всего лишь два элемента, но, как хорошая практика, рекомендуется хранить все наши настройки плагина /темы только в одном варианте. По правде говоря, так как у вас есть это, это намного лучше, чем то, что мы видим в дикой природе, одна запись db для каждой вспомогательной опции .

ответил brasofilo 20 Maypm14 2014, 21:15:22
4

ваша первая функция должна использовать оператор switch вместо сложного if then.

    foreach ( $this->pages as $page )
    {
        if ($page['type']=='menu')
        {
            add_menu_page(
                __( $page['title'] ),
                __( $page['title'] ),
                'administrator',
                $page['id'],
                array( &$this, 'displayOptionsPage' )
            );
        }
        else if ($page['type'] == 'submenu')
        {
            add_submenu_page(
                $page['parent'],
                __( $page['title'] ),
                __( $page['title'] ),
                'administrator',
                $page['id'],
                array( &$this, 'displayOptionsPage' )
            );
        }
    }

Это будет выглядеть так:

    foreach ( $this->pages as $page )
    {
        switch ($page['type']) {
            case 'menu':
                add_menu_page(
                    __( $page['title'] ),
                    __( $page['title'] ),
                    'administrator',
                    $page['id'],
                    array( &$this, 'displayOptionsPage' )
                );
                break;
            case 'submenu':
                add_submenu_page(
                    $page['parent'],
                    __( $page['title'] ),
                    __( $page['title'] ),
                    'administrator',
                    $page['id'],
                    array( &$this, 'displayOptionsPage' )
                );
                break;
        }
    }

Возможно, вы даже сможете превратить кишки этих операторов в функцию с параметром.

Превратив это в коммутатор, он будет намного легче поддерживать позже.

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

ответил Malachi 20 Maypm14 2014, 19:32:25

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132