Автозагрузка и пространства имен в плагинах и темах WordPress: Возможно ли это?

30 авг. 2012 г., 23:00:45
Просмотры: 29.2K
Голосов: 83

Кто-нибудь использовал автозагрузку и/или пространства имен PHP в плагине или теме?

Какие мысли по их использованию? Есть ли какой-то вред? Подводные камни?

Примечание: пространства имен доступны только в PHP 5.3+. Предположим, что для данного вопроса вы знаете, что будете работать с серверами, на которых установлен PHP 5.3 или выше.

0
Все ответы на вопрос 3
0
100

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

Во-первых. Автозагрузка — это потрясающе. Не беспокоиться о require — это довольно удобно.

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

<?php
spl_autoload_register(__NAMESPACE__ . '\\autoload');
function autoload($cls)
{
    $cls = ltrim($cls, '\\');
    if(strpos($cls, __NAMESPACE__) !== 0)
        return;

    $cls = str_replace(__NAMESPACE__, '', $cls);

    $path = PLUGIN_PATH_PATH . 'inc' . 
        str_replace('\\', DIRECTORY_SEPARATOR, $cls) . '.php';

    require_once($path);
}

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

Пространства имен и хуки

Система хуков WordPress работает через call_user_funccall_user_func_array), которая принимает имена функций в виде строк и вызывает их при выполнении do_action (и, соответственно, call_user_func).

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

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', 'WPSE\\SomeNameSpace\\the_function');
function the_function()
{
   return 'did stuff';
}

Лучше всего активно использовать магическую константу __NAMESPACE__, если вы хотите так делать.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', __NAMESPACE__ . '\\the_function');
function the_function()
{
   return 'did stuff';
}

Если вы всегда помещаете хуки в классы, всё проще. Стандартное создание экземпляра класса и всех хуков в конструкторе с $this работает отлично.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

class Plugin
{
    function __construct()
    {
        add_action('plugins_loaded', array($this, 'loaded'));
    }

    function loaded()
    {
        // это работает!
    }
}

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

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

class Plugin
{
    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'loaded'));
        // ИЛИ: add_action('plugins_loaded', array(get_class(), 'loaded'));
    }

    public static function loaded()
    {
        // это работает!
    }
}

Использование классов ядра

Разрешение имен классов в PHP немного странное. Если вы собираетесь использовать основные классы WordPress (например, WP_Widget в примере ниже), вы должны указать их через use.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

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

<?php
namespace WPSE\SomeNameSpace;

class MyWidget extends \WP_Widget
{
   // ...
}

Определения (Defines)

Это больше относится к PHP в целом, но однажды это меня подвело, так что вот.

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

<?php
namespace WPSE\SomeNameSpace;

// корневое пространство имен
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// в текущем пространстве имен
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

Также можно использовать ключевое слово const на верхнем уровне файла в PHP 5.3 и выше. Константы const всегда находятся в текущем пространстве имен, но они менее гибкие, чем вызов define.

<?php
namespace WPSE\SomeNameSpace;

// в текущем пространстве имен
const MY_CONST = 1;

// это не сработает!
const MY_PATH = plugin_dir_path(__FILE__);

Пожалуйста, делитесь любыми другими советами, которые у вас есть!

27 сент. 2012 г. 23:08:44
4
20

Вот ответ из 2017 года.

Автозагрузка — это великолепно. Пространства имён — тоже великолепно.

Хотя можно реализовать это самостоятельно, в 2017 году разумнее всего использовать великолепный и повсеместно распространённый Composer для управления PHP-зависимостями. Composer поддерживает автозагрузку как по PSR-0, так и по PSR-4, но первый стандарт устарел ещё в 2014 году, поэтому используйте PSR-4. Это упрощает структуру каталогов.

Мы храним каждый из наших плагинов и тем в отдельных репозиториях GitHub, каждый со своим файлом composer.json и файлом composer.lock.

Вот структура каталогов, которую мы используем для наших плагинов. (У нас на самом деле нет плагина под названием awesome-plugin, но стоило бы.)

plugins/awesome-plugin/bootstrap.php
plugins/awesome-plugin/composer.json
plugins/awesome-plugin/composer.lock
plugins/awesome-plugin/awesome-plugin.php
plugins/awesome-plugin/src/*

plugins/awesome-plugin/vendor/autoload.php
plugins/awesome-plugin/vendor/*

Если предоставить корректный файл composer.json, Composer позаботится о пространствах имён и автозагрузке.

{
    "name": "awesome-company/awesome-plugin",
    "description": "WordPress-плагин для сайта AwesomeCompany, обеспечивающий потрясающую функциональность.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

Когда вы запускаете composer install, создаётся каталог vendor и файл vendor/autoload.php, который будет автоматически загружать все ваши файлы с пространствами имён из src/, а также любые другие требуемые библиотеки.

Затем в начале главного файла плагина (у нас это awesome-plugin.php), после метаданных плагина, вам достаточно добавить:

// Автозагрузка через Composer.
require_once __DIR__ . '/vendor/autoload.php';

...

Бонусный функционал

Не обязательный, но полезный момент: мы используем Bedrock — шаблон WordPress, который позволяет использовать Composer с самого начала. Таким образом, мы можем собирать необходимые плагины через Composer, включая ваш собственный плагин, написанный выше. Кроме того, благодаря WPackagist, вы можете подключить любой другой плагин с WordPress.org (см. примеры cool-theme и cool-plugin ниже).

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "Шаблон WordPress с современными инструментами разработки, упрощённой конфигурацией и улучшенной структурой каталогов",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Указывает Composer искать наш проприетарный Awesome Plugin здесь.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // Наш плагин!
    "wpackagist-plugin/cool-plugin": "dev-trunk",      // Чужой плагин
    "wpackagist-theme/cool-theme": "dev-trunk",        // Чужая тема
    "composer/installers": "~1.2.0",     // Стандартный для Bedrock
    "vlucas/phpdotenv": "^2.0.1",        // Стандартный для Bedrock
    "johnpbloch/wordpress": "4.7.5",     // Стандартный для Bedrock
    "oscarotero/env": "^1.0",            // Стандартный для Bedrock
    "roots/wp-password-bcrypt": "1.0.0"  // Стандартный для Bedrock
  },
  "extra": {
    // Это магия, которая размещает пакеты с правильным TYPE в нужных местах.
    "installer-paths": {
      "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"],
      "web/app/plugins/{$name}/": ["type:wordpress-plugin"],
      "web/app/themes/{$name}/": ["type:wordpress-theme"]
    },
    "wordpress-install-dir": "web/wp"
  },
  "scripts": {
    "test": [
      "vendor/bin/phpcs"
    ]
  }
}

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

Примечание 2: Я убрал некоторые части стандартного файла Bedrock для краткости.

Примечание 3: Вот почему поле type в первом файле composer.json так важно. Composer автоматически помещает плагин в каталог web/app/plugins.

7 июн. 2017 г. 03:34:02
Комментарии

Спасибо за ответ, очень полезно! Но мне интересно, что содержит файл "bootstrap.php", о котором вы упомянули? :)

INT INT
22 сент. 2018 г. 23:15:53

Файл bootstrap.php — это стилистический подход, который я использую в большинстве своих проектов, как в WordPress, так и за его пределами. Мой загрузчик обычно просто проверяет настройки и переменные окружения; его основная цель — убедиться, что мой плагин всегда имеет всё необходимое для работы, независимо от того, запущен ли он внутри WordPress или как самостоятельное PHP-приложение.

haz haz
24 сент. 2018 г. 01:43:25

Здравствуйте, где нужно выполнить команду ´composer install´ ?

LΞИIИ LΞИIИ
27 апр. 2020 г. 08:13:09

@LeninZapata Вам нужно выполнить composer install в директории с файлом composer.json. Например, если вы клонировали Bedrock (https://github.com/roots/bedrock), перейдите в директорию bedrock и выполните команду там.

haz haz
6 мая 2020 г. 09:43:54
0

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

Если вы уверены, что вам никогда не понадобится использовать PHP-установку без поддержки пространств имён, тогда проблем тоже не будет (~70% текущих WordPress-блогов не поддерживают пространства имён). Несколько важных замечаний:

Кажется, я помню, что пространства имён не чувствительны к регистру в обычном PHP, но становятся таковыми при использовании FastCGI PHP на IIS - это вызывает головную боль, если вы тестируете на Linux и не замечаете случайную строчную букву.

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

3 сент. 2012 г. 17:07:31