Autoloading & Namespaces em WordPress Plugins e Temas: pode funcionar?

65

Alguém usou autoloading e / ou namespaces PHP dentro de um plugin ou tema?

Pensamentos sobre como usá-los? Qualquer dano? Armadilhas?

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

    
por chrisguitarguy 30.08.2012 / 22:00
fonte

3 respostas

85

Ok, tive dois grandes projetos em que controlei o servidor o suficiente para usar namespace e confiar no autoloading.

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

Aqui está um loader que eu tenho usado em alguns projetos. Verifica se a classe está no namespace atual primeiro e, em seguida, repete se não. De lá, é apenas alguma 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 facilmente adaptar isso para uso sem namespaces. Supondo que você prefixar as classes do seu plugin / tema uniformemente, você poderia 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 vai querer usar algum tipo de autoloader de mapa de classe.

Espaços de nomes 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 strings e os chama quando a chamada de função do_action (e, subseqüentemente, call_user_func ) é feita.

Com namespaces, isso significa que você precisará passar nomes de funções totalmente qualificados que incluam o namespace em ganchos.

<?php
namespace WPSE\SomeNameSpace;

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

Provavelmente seria melhor usar liberalmente a constante mágica __NAMESPACE__ 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, fica mais fácil. A instância de criação padrão de uma classe e todos os ganchos no construtor com $this funcionam 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, você precisará passar o nome completo da classe como o primeiro argumento da matriz. Isso é muito trabalho, então você pode usar a mágica __CLASS__ constant 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 de nome de classe do PHP é um pouco instável. Se você for usar classes principais do WP ( WP_Widget no exemplo abaixo), você deve fornecer use declarações.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

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

<?php
namespace WPSE\SomeNameSpace;

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

Define

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

Você pode definir os itens que usará com frequência, como o caminho para o seu plug-in. Usar a instrução define coloca as coisas no namespace da raiz, a menos que você passe explicitamente o namespace para o primeiro argumento de 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__));

Você também pode usar a palavra-chave const no nível da raiz de um arquivo com o PHP 5.3 plus. consts s estão sempre no namespace atual, mas são menos flexíveis do que uma chamada define .

<?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 à vontade para adicionar outras dicas que você possa ter!

    
por chrisguitarguy 28.08.2013 / 23:17
fonte
11

Aqui está uma resposta de 2017.

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

Embora você mesmo possa reproduzi-lo, em 2017 faz mais sentido usar o Composer magnífico e onipresente para lidar com seus requisitos de PHP. O Compositor suporta tanto PSR-0 como PSR-4 autoloading, mas o primeiro foi preterido desde 2014, então use o PSR-4. Reduz a complexidade dos seus diretórios.

Mantemos cada um dos nossos plugins / temas em seu próprio repositório Github, cada um com seu próprio arquivo composer.json e composer.lock .

Aqui está a estrutura de diretórios que usamos para nossos plugins. (Nós realmente 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 arquivo composer.json 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 diretório vendor e o arquivo vendor/autoload.php , que carregará automaticamente todos os seus arquivos com nome em src/ e qualquer outra biblioteca que você possa precisar.

Em seguida, na parte superior do seu arquivo de plug-in principal (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 Bedrock do Wordpress para usar o Composer desde o início. Então podemos usar o Composer para montar os plugins que precisamos através do Composer, incluindo o seu próprio plugin que você escreveu acima. Além disso, graças ao WPackagist , você pode exigir qualquer outro plugin do Wordpress.org (veja o exemplo de cool-theme e cool-plugin abaixo). / p>

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

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

Nota 2: Eu cortei alguns pedaços do arquivo bedrock por brevidade.

Nota 3: É por isso que o campo type no primeiro arquivo composer.json é significativo. O Composer o coloca automaticamente no diretório web/app/plugins .

    
por haz 07.06.2017 / 02:34
fonte
4

Eu uso o autoloading (como meu plugin tem muitas classes - em parte porque inclui o Twig), nunca tive um problema que me chamou a atenção (plugin instalado > 20.000 vezes).

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

Eu pareço lembrar que os namespaces não diferenciam maiúsculas de minúsculas no php normal, mas quando usam 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 maliciosa.

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

    
por Daniel Chatfield 03.09.2012 / 16:07
fonte