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

70

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

Мысли об их использовании? Любой вред? Ловушки?

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

chrisguitarguy
источник

Ответы:

88

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

Сначала. Автозагрузка потрясающая. Не беспокоиться о потребностях - это относительно хорошая вещь.

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

<?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()
    {
        // this works!
    }
}

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

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

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

    public static function loaded()
    {
        // this works!
    }
}

Использование основных классов

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

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

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

<?php
namespace WPSE\SomeNameSpace;

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

Определяет

Это более общий PHP, но он укусил меня, так что вот оно.

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

<?php
namespace WPSE\SomeNameSpace;

// root namespace
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// in the current namespace
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

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

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

// this won't work!
const MY_PATH = plugin_dir_path(__FILE__);

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

chrisguitarguy
источник
16

Вот ответ на 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 plugin for AwesomeCompany website, providing awesome functionality.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

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

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

// Composer autoloading.
require_once __DIR__ . '/vendor/autoload.php';

...

Бонус

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

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Tells Composer to look for our proprietary Awesome Plugin here.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // Our plugin!
    "wpackagist-plugin/cool-plugin": "dev-trunk",       // Someone else' plugin
    "wpackagist-theme/cool-theme": "dev-trunk",         // Someone else' theme
    "composer/installers": "~1.2.0",     // Bedrock default
    "vlucas/phpdotenv": "^2.0.1",        // Bedrock default
    "johnpbloch/wordpress": "4.7.5",     // Bedrock default
    "oscarotero/env": "^1.0",            // Bedrock default
    "roots/wp-password-bcrypt": "1.0.0"  // Bedrock default
  },
  "extra": {
    // This is the magic that drops packages with the correct TYPE in the correct location. 
    "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каталог.

ХАЗ
источник
Ценю ваш ответ, очень полезно! Но мне любопытно, что за "bootstrap.php" ты имеешь в виду. Что это содержит? :)
INT
1
Наличие файла bootstrap.php - это стилистическая вещь, которую я делаю в большинстве своих проектов, в WP или вне. Мой загрузчик обычно просто проверяет настройки и переменные окружения; его основная цель - убедиться, что мой плагин всегда имеет то, что ему нужно для запуска, независимо от того, был ли он запущен из WP или как отдельное приложение PHP.
хаз
4

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

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

Кажется, я помню, что пространства имен не чувствительны к регистру в обычном php, но при использовании fastcgi php на iis - это вызывает некоторые головные боли, если вы тестируете на linux и не обнаруживает мошенническую строчную букву.

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

Дэниел Чатфилд
источник