WordPress插件和主题中的自动加载和命名空间:可以工作吗?


70

有没有人在插件或主题中使用过自动加载和/或PHP名称空间?

关于使用它们的想法?有什么伤害吗 陷阱?

注意:名称空间仅适用于PHP 5.3+。假设,对于这个问题,您知道将要使用PHP 5.3或更高版本的服务器。

Answers:


89

好的,我有两个大项目,在这些项目中,我对服务器的控制足以控制名称空间并依靠自动加载。

第一。自动加载很棒。不担心需求是一件相对好的事情。

这是我在一些项目中一直使用的装载机。首先检查以确保该类位于当前名称空间中,如果没有,则进行保释。从那里开始,只有一些字符串操作才能找到该类。

<?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的hooks系统通过使用call_user_func(和call_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__));

您还可以在constPHP 5.3 plus的文件根级别中使用关键字on。 consts始终位于当前名称空间中,但不如define调用灵活。

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

请随时添加您可能有的其他提示!


16

这是2017年的答案。

自动加载很棒。命名空间很棒。

尽管您可以自己滚动它,但在2017年,使用宏伟而无处不在的Composer来满足您的PHP需求是最有意义的。Composer支持PSR-0PSR-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:为简洁起见,我已将样板基岩文件切掉了一些。

注意3:这就是为什么type第一个composer.json文件中的字段很重要的原因。Composer会自动将其拖放到web/app/plugins目录中。


感谢您的回答,非常有帮助!但是我很好奇您所指的“ bootstrap.php”。它包含什么?:)
INT

1
在我的大多数项目中,无论在WP中还是在WP中,都有bootstrap.php文件都是一种风格上的事情。我的引导程序通常只检查设置和环境变量。它的主要目的是确保我的插件始终具有运行所需的功能,而不管它是从WP内部运行还是作为独立的PHP应用程序运行。
haz

4

我使用自动加载功能(因为我的插件有很多类,部分是因为它包含Twig),从来没有引起我注意的问题(插件安装> 20,000次)。

如果您有信心永远不需要使用不支持名称空间的php安装,那么就可以了(大约70%的当前wordpress博客不支持名称空间)。注意事项:

我似乎记得,在常规php中,名称空间不区分大小写,但是在iis上使用fastcgi php时-如果在Linux上进行测试并且没有发现流氓的小写字母,这会引起一些麻烦。

同样,即使您确定当前正在开发的代码仅在> 5.3.0上使用,您也将无法在不那么奢侈的项目中重用任何代码-这就是我没有这样做的主要原因内部项目上使用的名称空间。我发现,命名空间实在不加当用具有去除对他们的依赖的可能比较头疼了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.