Advertisement
  1. Code
  2. WordPress
  3. Plugin Development

Использование пространств имен и автозагрузок плагинов для WordPress, часть 4

Scroll to top
This post is part of a series called Using Namespaces and Autoloading in WordPress Plugins.
Using Namespaces and Autoloading in WordPress Plugins, Part 3

() translation by (you can also view the original English article)

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

По сути, вы попали к концу представления. На данный момент мы заложили фундамент для нашего плагина, написали плагин, дали определения для пространства имён и автозагрузок. Осталось только применить свои знания на практике.

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

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

Прежде, чем писать любой код

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

  • хотя бы PHP 5.6.20
  • веб-сервер Apache 
  • сервер базы данных MySQL
  • WordPress 4.6.1
  • навык работы с WordPress Plugin API.

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

Код пространства имён

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

The directory structure of our pluginThe directory structure of our pluginThe directory structure of our plugin

Заметим, что, если вы настроили ваш плагин иначе, это нормально. Пространства имён скорее всего будут отличаться, но это не должно на что-то повлиять. 

Используя структуру каталогов в качестве ориентира, пройдите все PHP-файлы, которые составляют наш плагин и определите их пространства имён. Сделать это просто: возьмите ключевое слово пространства имён и разместите в верхней части каждого файла.

Я буду перечислять каждое из них ниже.

tutsplus-namespace-demo.php

1
<?php
2
/**

3
 * The plugin bootstrap file

4
 *

5
 * This file is read by WordPress to generate the plugin information in the

6
 * plugin admin area. This file also includes all of the dependencies used by

7
 * the plugin, registers the activation and deactivation functions, and defines

8
 * a function that starts the plugin.

9
 *

10
 * @link              https://.tutsplus.com/tutorials/using-namespaces-and-autoloading-in-wordpress-plugins-part-1

11
 * @since             0.1.0

12
 * @package           tutsplus_namespace_demo

13
 *

14
 * @wordpress-plugin

15
 * Plugin Name:       Tuts+ Namespace Demo

16
 * Plugin URI:        http://.tutsplus.com/tutorials/using-namespaces-and-autoloading-in-wordpress-plugins-part-1

17
 * Description:       Learn how to use Namespaces and Autoloading in WordPress.

18
 * Version:           0.2.0

19
 * Author:            Tom McFarlin

20
 * Author URI:        https://tommcfarlin.com/

21
 * License:           GPL-2.0+

22
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt

23
 */
24
25
namespace Tutsplus_Namespace_Demo;
26
27
// If this file is accessed directory, then abort.

28
if ( ! defined( 'WPINC' ) ) {
29
    die;
30
}

class-meta-box.php

1
<?php
2
/**

3
 * Represents a meta box to be displayed within the 'Add New Post' page.

4
 */
5
6
namespace Tutsplus_Namespace_Demo\Admin;

class-meta-box-display.php

1
<?php
2
/**

3
 * Defines the functionality required to render the content within the Meta Box

4
 * to which this display belongs.

5
 */
6
7
namespace Tutsplus_Namespace_Demo\Admin;

interface-assets.php

1
<?php
2
namespace Tutsplus_Namespace_Demo\Admin\Util;

class-css-loader.php

1
<?php
2
/**

3
 * Provides a consistent way to enqueue all administrative-related stylesheets.

4
 */
5
6
namespace Tutsplus_Namespace_Demo\Admin\Util;

class-question-reader.php

1
<?php
2
/**

3
 * Reads the contents of a specified file and returns a random line from the

4
 * file.

5
 */
6
7
namespace Tutsplus_Namespace_Demo\Admin\Util;

Несколько вещей в качестве напоминания в связи со сказанным выше:

  • Корневое пространство имён Tutsplus_Namespace_Demo, для сообщения директории имени плагина.
  • Остальные пространства имён, вроде Tutsplus_Namespace_Demo\Admin и Tutsplus_Namespace_Demo\Admin\Util также соответствуют их папкам; однако имена папок зашиты (против находящихся в нижнем регистре).

Наконец, если вы обновляли страницу или пробовали перемещаться по WordPress, вводя определение пространства имён, то скорее всего вы видите сообщение об ошибке, которое выглядит примерно так: 

PHP Errors when loading namespaced codePHP Errors when loading namespaced codePHP Errors when loading namespaced code

И содержит следующее сообщение:

PHP предупреждение: call_user_func_array() expects parameter 1 to be a valid callback, функция 'tutsplus_namespace_demo' не найдена  или названа неверно /Users/tommcfarlin/Dropbox/Projects/tutsplus/wp-includes/plugin.php on line 524

Или, возможно, это:

PHP фатальная ошибка: Class 'Meta_Box' не найден в/Users/tommcfarlin/Dropbox/Projects/tutsplus/wp-content/plugins/tutsplus-namespace-demo/tutsplus-namespace-demo.php on line 48

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

Но возникает вопрос: что такое с нашим плагином? К счастью, ничего. Это ожидаемое поведение.

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

Когда вы кодируете пространство имён, PHP старается найти класс в заданном пространстве. В принципе вы можете думать о ваших классах, как о принадлежащих теперь package (или subpackage) до тех пор, пока вы их сами определите. Чтобы у функции был доступ к классу внутри package, необходимо быть осведомлённым о существующих packages.

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

Всё об автозагрузке

Для написания автозагрузчика потребуется следующее:

  1. понимание PHP функции  spl_autoload_register
  2. написание функции, которая автоматически загрузит файлы пространства имён
  3. включение настроенной нами функции

Не позволяйте имени spl_autoload_register запугать вас. Это означает функцию, которая является частью «Стандартной библиотеки PHP» и это вроде как мы пользуемся функцией "register" на "autoload". Трудно выговорить и долго писать, но это всего лишь функция, которая сообщает PHP как разобрать пространства имён и имена классов, и где искать наши файлы.

Эта функция позволяет нам написать собственный пользовательский код для файлов автозагрузки и подцепить указание к PHP. Так мы говорим PHP где искать наши файлы и как разобрать пространства имён и прочее, чтобы он включил их в себя.

После всего сказанного мы готовы к написанию автозагрузчика.

Написание автозагрузчика

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

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

Когда PHP пытается загрузить класс, наш автозагрузчик собирается сделать следующее:

  1. Пространства имён разделить обратными косыми чертами.
  2. Разделить package и subpackages подчёркиванием и дефисами (по необходимости).
  3. Узнать, как сопоставлять имена классов, интерфейсов и так далее, с именами файлов.
  4. Создать строку представления имени файла на основании вышеуказанной информации.
  5. Вставить файл.

При выполнении всех условий, работу можно считать сделанной. В папке плагинов создайте подпапку inc и в папке inc создайте файл с именем autoload.php .

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

1
<?php
2
/**

3
 * Dynamically loads the class attempting to be instantiated elsewhere in the

4
 * plugin.

5
 *

6
 * @package Tutsplus_Namespace_Demo\Inc

7
 */
8
9
spl_autoload_register( 'tutsplus_namespace_demo_autoload' );
10
11
/**

12
 * Dynamically loads the class attempting to be instantiated elsewhere in the

13
 * plugin by looking at the $class_name parameter being passed as an argument.

14
 *

15
 * The argument should be in the form: TutsPlus_Namespace_Demo\Namespace. The

16
 * function will then break the fully-qualified class name into its pieces and

17
 * will then build a file to the path based on the namespace.

18
 *

19
 * The namespaces in this plugin map to the paths in the directory structure.

20
 *

21
 * @param string $class_name The fully-qualified name of the class to load.

22
 */
23
function tutsplus_namespace_demo_autoload( $class_name ) {
24
25
    // If the specified $class_name does not include our namespace, duck out.

26
    if ( false === strpos( $class_name, 'Tutsplus_Namespace_Demo' ) ) {
27
        return;
28
    }
29
30
    // More to come...

31
}

Очевидно, это ни для чего больше.

Заметки при написании автозагрузчика

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

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

Комплектация автозагрузчика

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

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

1
<?php
2
function tutsplus_namespace_demo_autoload( $class_name ) {
3
4
    // If the specified $class_name does not include our namespace, duck out.

5
    if ( false === strpos( $class_name, 'Tutsplus_Namespace_Demo' ) ) {
6
        return;
7
    }
8
9
    // Split the class name into an array to read the namespace and class.

10
    $file_parts = explode( '\\', $class_name );
11
12
    // Do a reverse loop through $file_parts to build the path to the file.

13
    $namespace = '';
14
    for ( $i = count( $file_parts ) - 1; $i > 0; $i-- ) {
15
        // More to come...

16
    }
17
}

После этого мы должны взглянуть на $file_parts и заменить все знаки подчеркивания дефисом, потому что имена класса и интерфейса используют подчеркивание, тогда как имена файлов используют дефисы.

Следующие две строки являются первыми двумя строками внутри цикла, которые мы погасили раньше:

1
<?php
2
3
// Read the current component of the file part.

4
$current = strtolower( $file_parts[ $i ] );
5
$current = str_ireplace( '_', '-', $current );

Далее мы собираемся создать условие для нескольких вещей.

  1. Необходимо проверять при каждом входе путь имени файла для прочтения.
  2. Если это первая запись, то мы на имени файла; в противном случае мы находимся на его пространстве имён.
  3. Далее, если мы читаем первую запись, нужно определить, пытаемся ли мы сделать автозагрузку интерфейса или загружаем класс.
  4. В первом случае необходимо настроить имя интерфейса так, чтобы загрузить его на основе имени его файла, в противном случае будем загружать класс на основе значения переменной $current.

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

1
<?php
2
3
// If we're at the first entry, then we're at the filename.

4
if ( count( $file_parts ) - 1 === $i ) {
5
6
        /* If 'interface' is contained in the parts of the file name, then

7
   * define the $file_name differently so that it's properly loaded.

8
	 * Otherwise, just set the $file_name equal to that of the class

9
	 * filename structure.

10
	 */
11
	if ( strpos( strtolower( $file_parts[ count( $file_parts ) - 1 ] ), 'interface' ) ) {
12
13
		// Grab the name of the interface from its qualified name.

14
		$interface_name = explode( '_', $file_parts[ count( $file_parts ) - 1 ] );
15
		$interface_name = $interface_name[0];
16
17
		$file_name = "interface-$interface_name.php";
18
19
	} else {
20
		$file_name = "class-$current.php";
21
	}
22
} else {
23
	$namespace = '/' . $current . $namespace;
24
}

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

1
<?php
2
3
// Now build a path to the file using mapping to the file location.

4
$filepath  = trailingslashit( dirname( dirname( __FILE__ ) ) . $namespace );
5
$filepath .= $file_name;

И наконец нам нужно убедиться, что файл существует. Если нет, то отображается стандартное сообщение об ошибке WordPress:

1
<?php
2
3
// If the file exists in the specified path, then include it.

4
if ( file_exists( $filepath ) ) {
5
    include_once( $filepath );
6
} else {
7
    wp_die(
8
        esc_html( "The file attempting to be loaded at $filepath does not exist." )
9
    );
10
}

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

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

Но мы всё ещё включаем файлы

Если взглянуть на верхушку файла главного плагина (или файла bootstrap, как мы его ещё называем), вы заметите несколько include, вроде этих:

1
<?php
2
3
// Include the files for rendering the display.

4
include_once( 'admin/class-meta-box.php' );
5
include_once( 'admin/class-meta-box-display.php' );
6
include_once( 'admin/util/class-question-reader.php' );
7
8
// Include the files for loading the assets

9
include_once( 'admin/util/interface-assets.php' );
10
include_once( 'admin/util/class-css-loader.php' );

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

1
<?php
2
3
// Include the autoloader so we can dynamically include the rest of the classes.

4
require_once( trailingslashit( dirname( __FILE__ ) ) . 'inc/autoloader.php' );

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

Собираем всё вместе

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

Напоследок надо убедиться, что мы обновили файл bootstrap так, что можем указать PHP использовать пространства имён для Meta_BoxMeta_Box_Display, для Question_Reader и  CSS_Loader.

1
<?php
2
3
use Tutsplus_Namespace_Demo\Admin;
4
use Tutsplus_Namespace_Demo\Admin\Util;
5
6
// If this file is accessed directory, then abort.

7
if ( ! defined( 'WPINC' ) ) {
8
    die;
9
}
10
11
// Include the autoloader so we can dynamically include the rest of the classes.

12
require_once( trailingslashit( dirname( __FILE__ ) ) . 'inc/autoloader.php' );
13
14
add_action( 'plugins_loaded', 'tutsplus_namespace_demo' );
15
/**

16
 * Starts the plugin by initializing the meta box, its display, and then

17
 * sets the plugin in motion.

18
 */
19
function tutsplus_namespace_demo() {
20
21
    $meta_box = new Admin\Meta_Box(
22
        new Admin\Meta_Box_Display(
23
            new Util\Question_Reader()
24
        )
25
    );
26
27
    $css_loader = new Util\CSS_Loader();
28
    $css_loader->init();
29
30
    $meta_box->init();
31
}

Обратите внимание, в приведённом выше коде мы используем PHP use ключевое слово и добавляем префикс имён классов с их subpackages. Вы можете прочитать об этом подробней в руководстве, вот выдержка из него:

Ключевое слово use должно быть объявлено в рамках внешнего файла (глобальной области) или внутри объявления пространств имён. Это потому, что импорт выполняется во время компиляции, а не во время выполнения, поэтому он не может быть областью действия блока.

Теперь, если всё работает правильно, вы можете переместиться на страницу Add New Post (или Edit Post), посмотреть наш метабокс и вопрос в верхней части боковой панели:

The meta box prompting users for inspirationThe meta box prompting users for inspirationThe meta box prompting users for inspiration

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

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

Заключение

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

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

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

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

Не забудьте, ссылка на скачивание финального кода находится в боковой панели под кнопкой Download Attachment. Конечно, задавайте любые вопросы в комментариях!

Ресурсы

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.