it-swarm.com.ru

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

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

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

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

69
chrisguitarguy

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

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

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

<?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';
}

Если вы всегда помещаете свои крючки в классы, это проще. Стандартный экземпляр create класса и все хуки в конструкторе с $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 на корневом уровне файла с 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__);

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

87
chrisguitarguy

Вот ответ на 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';

...

Бонус

Не обязательно, но мы используем Bedrock 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.

13
haz

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

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

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

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

4
Daniel Chatfield