Статьи

Объектно-ориентированная автозагрузка в WordPress, часть 3

В последнем уроке мы рассмотрели исходное состояние нашего автозагрузчика, а затем прошли процесс объектно-ориентированного анализа и проектирования. Цель этого состоит в том, чтобы мы могли связать воедино все, что мы рассмотрели в этой серии и вводной серии.

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

На данный момент, я предполагаю, что вы следили за этой серией. Если нет, то, пожалуйста, просмотрите первую часть и вторую часть. Предполагается, что вы следили за этим до сих пор.

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

Чтобы дать краткое резюме и убедиться, что мы все на одной странице, мы рассмотрели следующие темы в этой серии:

  • пересмотрел определение класса интерфейса
  • видел, как класс реализует интерфейс
  • пересмотрел принцип единоличной ответственности
  • проанализировал наш существующий автозагрузчик
  • создали дорожную карту для нашей объектно-ориентированной версии автозагрузчика
  • и разработал базовую реализацию для объектно-ориентированной версии автозагрузчика

На данный момент мы готовы заменить наш существующий автозагрузчик на объектно-ориентированный код. Обратите внимание, однако, что это не будет простым вопросом замены файлов.

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

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

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

  • локальная среда разработки, подходящая для вашей операционной системы
  • каталог, из которого размещается WordPress 4.6.1
  • текстовый редактор или IDE
  • знание API плагинов WordPress

С этим сказал, давайте начнем.

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

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

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

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
<?php
/**
 * Dynamically loads the class attempting to be instantiated elsewhere in the
 * plugin.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
 
/**
 * The primary point of entry for the autoloading functionality.
 * to work through the process of autoloading classes and interfaces in the project.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
class Autoloader {
 
    /**
     * Verifies the file being passed into the autoloader is of the same namespace as the
     * plugin.
     *
     * @var NamespaceValidator
     */
    private $namespace_validator;
 
    /**
     * Uses the fully-qualified file path ultimately returned from the other classes.
     *
     * @var FileRegistry
     */
    private $file_registry;
 
    /**
     * Creates an instance of this class by instantiating the NamespaceValidator and the
     * FileRegistry.
     */
    public function __construct() {
 
        $this->namespace_validator = new NamespaceValidator();
        $this->file_registry = new FileRegistry();
    }
 
    /**
     * Attempts to load the specified filename.
     *
     * @param string $filename The path to the file that we’re attempting to load.
     */
    public function load( $filename ) {
 
        if ( $this->namespace_validator->is_valid( $filename ) ) {
            $this->file_registry->load( $filename );
        }
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
/**
 * Looks at the incoming class or interface determines if it’s valid.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
 
/**
 * Looks at the incoming class or interface determines if it’s valid.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
class NamespaceValidator {
 
    /**
     * Yields the deciding factor if we can proceed with the rest of our code our not.
     *
     * @param string $filename The path to the file that we’re attempting to load.
     * @return bool Whether or not the specified file is in the correct namespace.
     */
    public function is_valid( $filename ) {
        return ( 0 === strpos( $filename, ‘Tutsplus_Namespace_Demo’ ) );
    }
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php
/**
 * This class looks at the type of file that’s being passed into the autoloader.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
 
/**
 * This class looks at the type of file that’s being passed into the autoloader.
 *
 * It will determine if it’s a class, an interface, or a namespace and return the fully-qualified
 * path name to the file so that it may be included.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
class FileInvestigator {
 
    /**
     * Returns the fully-qualified path to the file based on the incoming filename.
     *
     * @param string $filename The incoming filename based on the class or interface name.
     * @return string The path to the file.
     */
    public function get_filetype( $filename ) {
 
        $file_parts = explode( ‘\\’, $filename );
 
        $filepath = »;
        $length = count( $file_parts );
        for ( $i = 1; $i < $length; $i++ ) {
 
            $current = strtolower( $file_parts[ $i ] );
            $current = str_ireplace( ‘_’, ‘-‘, $current );
 
            $filepath .= $this->get_file_name( $file_parts, $current, $i );
            if ( count( $file_parts ) — 1 !== $i ) {
                $filepath = trailingslashit( $filepath );
            }
        }
 
        return $filepath;
    }
 
    /**
     * Retrieves the location of part of the filename on disk based on the current index of the
     * array being examined.
     *
     * @access private
     * @param array $file_parts The array of all parts of the file name.
     * @param string $current The current part of the file to examine.
     * @param int $i The current index of the array of $file_parts to examine.
     * @return string The name of the file on disk.
     */
    private function get_file_name( $file_parts, $current, $i ) {
 
        $filename = »;
 
        if ( count( $file_parts ) — 1 === $i ) {
 
            if ( $this->is_interface( $file_parts ) ) {
                $filename = $this->get_interface_name( $file_parts );
            } else {
                $filename = $this->get_class_name( $current );
            }
        } else {
            $filename = $this->get_namespace_name( $current );
        }
 
        return $filename;
    }
 
    /**
     * Determines if the specified file being examined is an interface.
     *
     * @access private
     * @param array $file_parts The parts of the filepath to examine.
     * @return bool True if interface is contained in the filename;
     */
    private function is_interface( $file_parts ) {
        return strpos( strtolower( $file_parts[ count( $file_parts ) — 1 ] ), ‘interface’ );
    }
 
    /**
     * Retrieves the filename of the interface based on the specified parts of the file passed
     * into the autoloader.
     *
     * @access private
     * @param array $file_parts The array of parts of the file to examine.
     * @return string The filename of the interface.
     */
    private function get_interface_name( $file_parts ) {
 
        $interface_name = explode( ‘_’, $file_parts[ count( $file_parts ) — 1 ] );
        $interface_name = $interface_name[0];
 
        return «interface-$interface_name.php»;
    }
 
    /**
     * Generates the name of the class filename on disk.
     *
     * @access private
     * @param string $current The current piece of the file name to examine.
     * @return string The class filename on disk.
     */
    private function get_class_name( $current ) {
        return «class-$current.php»;
    }
 
    /**
     * Creates a mapping of the namespace to the directory structure.
     *
     * @access private
     * @param string $current The current part of the file to examine.
     * @return string The path of the namespace mapping to the directory structure.
     */
    private function get_namespace_name( $current ) {
        return ‘/’ .
    }
}
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
<?php
/**
 * Includes the file from the plugin.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
 
/**
 * This will use the fully qualified file path ultimately returned from the other classes
 * and will include it in the plugin.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
class FileRegistry {
 
    /**
     * This class looks at the type of file that’s being passed into the autoloader.
     *
     * @var FileInvestigator
     */
    private $investigator;
 
    /**
     * Creates an instance of this class by creating an instance of the FileInvestigator.
     */
    public function __construct() {
        $this->investigator = new FileInvestigator();
    }
 
    /**
     * Uses the file investigator to retrieve the location of the file on disk.
     * it will include it in the project;
     *
     * @param string $filepath The path to the file on disk to include in the plugin.
     */
    public function load( $filepath ) {
 
        $filepath = $this->investigator->get_filetype( $filepath );
        $filepath = rtrim( plugin_dir_path( dirname( __FILE__ ) ), ‘/’ ) .
 
        if ( file_exists( $filepath ) ) {
            include_once( $filepath );
        } else {
 
            wp_die(
                esc_html( ‘The specified file does not exist.’ )
            );
        }
    }
}

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

  1. Нам нужно включить все классы в каталог inc.
  2. Нам нужно избавиться от старого кода автозагрузчика.
  3. И нам нужно использовать наш новый автозагрузчик с функцией spl_autoload_register .

В конечном итоге финальная версия autoload.php должна выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?php
/**
 * Loads all of the classes in the directory, instantiates the Autoloader,
 * and registers it with the standard PHP library.
 *
 * @package Tutsplus_Namespace_Demo\Inc
 */
 
foreach ( glob( dirname( __FILE__ ) . ‘/class-*.php’ ) as $filename ) {
    include_once( $filename );
}
 
$autoloader = new Autoloader();
spl_autoload_register( array( $autoloader, ‘load’ ) );

И это будет именно то, что мы описали выше.

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

Таким образом, как и любой разработчик, вы запускаете окно браузера, чтобы обновить страницу, только чтобы появилось сообщение об ошибке:

Сообщение об ошибке неопределенной функции

К счастью, это легко исправить. Проблема в том, что мы пытаемся добавить наш мета-блок слишком рано. Чтобы исправить это, мы обновим метод init в нашем классе Meta_Box чтобы включить это:

1
2
3
4
5
6
7
8
9
<?php
 
/**
 * Registers the add_meta_box function with the proper hook in the WordPress page
 * lifecycle.
 */
public function init() {
    add_action( ‘add_meta_boxes’, array( $this, ‘add_meta_box’ ) );
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<?php
 
/**
 * Registers this meta box with WordPress.
 *
 * Defines a meta box that will render inspirational questions at the top
 * of the sidebar of the ‘Add New Post’ page in order to help prompt
 * bloggers with something to write about when they begin drafting a post.
 */
public function add_meta_box() {
 
    add_meta_box(
        ‘tutsplus-post-questions’,
        ‘Inspiration Questions’,
        array( $this->display, ‘render’ ),
        ‘post’,
        ‘side’,
        ‘high’
    );
}

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

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

Обратите внимание, что я регулярно пишу для Envato Tuts +, и вы можете найти все мои предыдущие учебники на странице моего профиля . Кроме того, я часто обсуждаю разработку программного обеспечения в контексте WordPress в своем блоге и в Твиттере, поэтому не стесняйтесь следить за мной в любом месте.

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