Carregamento automático e namespaces em plugins e temas do WordPress: ele pode funcionar?


70

Alguém já usou namespaces de carregamento automático e / ou PHP em um plugin ou tema?

Pensamentos em usá-los? Qualquer dano? Armadilhas?

Nota: os namespaces são apenas PHP 5.3+. Suponha, para esta pergunta, que você saiba que estará lidando com servidores que você sabe que possuem PHP 5.3 ou superior.

Respostas:


88

Ok, eu tive dois grandes projetos nos quais eu controlei o servidor o suficiente para namespace e confie no carregamento automático.

Primeiro. O carregamento automático é incrível. Não se preocupar com requer é uma coisa relativamente boa.

Aqui está um carregador que eu tenho usado em alguns projetos. Verifica se a classe está no namespace atual primeiro e, em seguida, falha se não. A partir daí, é apenas uma manipulação de string para encontrar a classe.

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

Pode-se adaptar isso facilmente para uso sem espaços para nome. Supondo que você prefixe as classes do seu plugin / tema de maneira uniforme, você pode apenas testar esse prefixo. Em seguida, use sublinhados no nome da classe como espaços reservados para separadores de diretório. Se você estiver usando muitas classes, provavelmente usará algum tipo de carregador automático de mapa de classes.

Namespaces e ganchos

O sistema de ganchos do WordPress funciona usando call_user_func(e call_user_func_array), que recebe nomes de funções como seqüências de caracteres e os chama quando a chamada de função do_action(e, posteriormente call_user_func) é feita.

Com espaços para nome, isso significa que você precisará passar nomes de funções totalmente qualificados que incluem o espaço para nome em ganchos.

<?php
namespace WPSE\SomeNameSpace;

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

Provavelmente seria melhor fazer uso liberal da __NAMESPACE__mágica constante se você quiser fazer isso.

<?php
namespace WPSE\SomeNameSpace;

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

Se você sempre coloca seus ganchos nas aulas, é mais fácil. O padrão cria a instância de uma classe e todos os ganchos no construtor $thisfuncionam bem.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

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

Se você usar métodos estáticos, como eu quero fazer, precisará passar o nome completo da classe como o primeiro argumento da matriz. Isso dá muito trabalho, então você pode simplesmente usar a __CLASS__constante mágica ou 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!
    }
}

Usando Classes Principais

A resolução do nome da classe do PHP é um pouco instável. Se você for usar as classes principais do WP ( WP_Widgetno exemplo abaixo), deverá fornecer useinstruções.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Ou você pode usar o nome completo da classe - basicamente prefixando-o com uma barra invertida.

<?php
namespace WPSE\SomeNameSpace;

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

Define

Este é o PHP mais geral, mas me mordeu, então aqui está.

Você pode definir coisas que usará com frequência, como o caminho para o seu plug-in. O uso da instrução define coloca coisas no espaço para nome raiz, a menos que você passe explicitamente o espaço para nome no primeiro argumento de definição.

<?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__));

Você também pode usar a constpalavra-chave on no nível raiz de um arquivo com PHP 5.3 plus. constss estão sempre no espaço para nome atual, mas são menos flexíveis que uma definechamada.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

Por favor, sinta-se livre para adicionar outras dicas que você possa ter!


16

Aqui está uma resposta de 2017.

O carregamento automático é incrível. O namespacing é incrível.

Embora você possa fazer isso sozinho, em 2017, faz mais sentido usar o magnífico e onipresente Composer para lidar com seus requisitos de PHP. O Composer suporta o carregamento automático do PSR-0 e PSR-4 , mas o primeiro foi preterido desde 2014, portanto, use o PSR-4. Reduz a complexidade dos seus diretórios.

Mantemos cada um de nossos plugins / temas em seu próprio repositório Github, cada um com seu próprio composer.jsonarquivo e composer.lockarquivo.

Aqui está a estrutura de diretórios que usamos para nossos plugins. (Na verdade, não temos um plugin chamado awesome-plugin, mas devemos.)

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/*

Se você fornecer um composer.jsonarquivo apropriado , o Composer manipulará o espaçamento de nomes e o carregamento automático aqui.

{
    "name": "awesome-company/awesome-plugin",
    "description": "Wordpress plugin for AwesomeCompany website, providing awesome functionality.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

Quando você executa composer install, ele cria o vendordiretório e o vendor/autoload.phparquivo, que carregam automaticamente todos os arquivos com espaçamento de nome src/e quaisquer outras bibliotecas que você possa precisar.

Então, na parte superior do seu principal arquivo de plug-in (que para nós é awesome-plugin.php), após os metadados do seu plug-in, você simplesmente precisa:

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

...

Recurso de bônus

Não é uma necessidade, mas usamos o padrão da Bedrock Wordpress para usar o Composer desde o início. Em seguida, podemos usar o Composer para montar os plugins necessários através do Composer, incluindo o seu próprio plugin que você escreveu acima. Além disso, graças ao WPackagist , você pode solicitar qualquer outro plugin do Wordpress.org (veja o exemplo de cool-themee cool-pluginabaixo).

{
  "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"
    ]
  }
}

Nota 1: os comentários não são legais em JSON, mas anotei o arquivo acima para obter mais clareza.

Nota 2: Recortei alguns bits do arquivo Bedrock do clichê por questões de concisão.

Nota 3: É por isso que o typecampo no primeiro composer.jsonarquivo é significativo. O Composer o coloca automaticamente no web/app/pluginsdiretório.


Aprecie sua resposta, muito útil! Mas estou curioso sobre o "bootstrap.php" ao qual você está se referindo. O que contém? :)
INT

11
Ter um arquivo bootstrap.php é uma coisa estilística que faço na maioria dos meus projetos, dentro ou fora do WP. Meu bootstrapper normalmente apenas verifica configurações e variáveis ​​de ambiente; seu principal objetivo é garantir que meu plug-in sempre tenha o que precisa para ser executado, independentemente de ter sido executado no WP ou como um aplicativo PHP independente.
Haz

4

Eu uso o carregamento automático (como meu plug-in possui muitas classes - em parte porque inclui o Twig), nunca tive um problema que me chamou atenção (plug-in instalado> 20.000 vezes).

Se você está confiante de que nunca precisará usar uma instalação php que não suporte espaços para nome, então você está bem (~ 70% dos blogs wordpress atuais não suportam espaços para nome). Algumas coisas a serem observadas:

Parece que me lembro que os namespaces não diferenciam maiúsculas de minúsculas no php normal, mas ao usar o fastcgi php no iis - isso causa algumas dores de cabeça se você testar no linux e não encontrar uma letra minúscula desonesta.

Além disso, mesmo se você tiver certeza de que o código que está desenvolvendo no momento será usado apenas em> 5.3.0, não poderá reutilizar nenhum código em projetos que não tenham esse luxo - essa é a principal razão pela qual não namespaces usados ​​em projetos internos. Eu descobri que namespaces realmente não acrescentar que muito quando comparado com a possível dor de cabeça de ter que remover a dependência sobre eles.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.