web-development-kb-pt.site

Organizando o código no arquivo functions.php do seu tema WordPress?

Quanto mais personalização faço para o WordPress, mais começo a pensar se devo organizar esse arquivo ou dividi-lo.

Mais especificamente, se eu tiver um monte de funções personalizadas que se aplicam apenas à área de administração e outras que se aplicam ao meu site público, existe alguma razão para possivelmente incluir todas as funções administrativas dentro de seu próprio arquivo ou agrupá-las?

Desmembrá-los em arquivos separados ou agrupá-los possivelmente acelera um site WordPress ou o WordPress/PHP ignora automaticamente as funções que têm um prefixo de código is_admin?

Qual é a melhor maneira de lidar com um arquivo de funções grandes (o meu tem 1370 linhas de comprimento).

91
NetConstructor.com

Se você está chegando ao ponto em que o código do functions.php do seu tema está começando a sobrecarregar você, eu definitivamente diria que você está pronto para considerar dividi-lo em vários arquivos. Eu costumo fazer isso quase por segunda natureza neste momento.

Use arquivos de inclusão no arquivo functions.php do seu tema

Eu crio um subdiretório chamado "inclui" sob meu diretório de temas e segmento meu código em arquivos de inclusão organizados pelo que faz sentido para mim no momento (o que significa que estou constantemente refatorando e movendo o código conforme um site evolui. ) Eu também raramente coloco qualquer código real em functions.php; tudo vai nos arquivos de inclusão; apenas minha preferência.

Só para dar um exemplo, aqui está minha instalação de teste que eu uso para testar minhas respostas a perguntas aqui no WordPress Answers. Toda vez que respondo a uma pergunta, mantenho o código para o caso de precisar dele novamente. Isso não é exatamente o que você fará para um site ao vivo, mas mostra a mecânica de dividir o código:

<?php 
/*
 * functions.php
 * 
 */
require_once( __DIR__ . '/includes/null-meta-compare.php');
require_once( __DIR__ . '/includes/older-examples.php');
require_once( __DIR__ . '/includes/wp-admin-menu-classes.php');
require_once( __DIR__ . '/includes/admin-menu-function-examples.php');

// WA: Adding a Taxonomy Filter to Admin List for a Custom Post Type?
// http://wordpress.stackexchange.com/questions/578/
require_once( __DIR__ . '/includes/cpt-filtering-in-admin.php'); 
require_once( __DIR__ . '/includes/category-fields.php');
require_once( __DIR__ . '/includes/post-list-shortcode.php');
require_once( __DIR__ . '/includes/car-type-urls.php');
require_once( __DIR__ . '/includes/buffer-all.php');
require_once( __DIR__ . '/includes/get-page-selector.php');

// http://wordpress.stackexchange.com/questions/907/
require_once( __DIR__ . '/includes/top-5-posts-per-category.php'); 

// http://wordpress.stackexchange.com/questions/951/
require_once( __DIR__ . '/includes/alternate-category-metabox.php');  

// http://lists.automattic.com/pipermail/wp-hackers/2010-August/034384.html
require_once( __DIR__ . '/includes/remove-status.php');  

// http://wordpress.stackexchange.com/questions/1027/removing-the-your-backup-folder-might-be-visible-to-the-public-message-generate
require_once( __DIR__ . '/includes/301-redirects.php');  

Ou crie plugins

Outra opção é começar a agrupar seu código por função e criar seus próprios plugins. Para mim, começo a codificar no arquivo functions.php do tema e, quando obtenho o código, mudoi a maior parte do meu código para plugins.

No entanto, nenhum ganho significativo de desempenho da organização de código PHP

Por outro lado, a estruturação de seus arquivos PHP é de 99% sobre a criação de ordem e manutenção e 1% sobre desempenho, se isso (organizar .js e .css arquivos chamados pelo navegador via HTTP é um caso completamente diferente e tem enormes implicações de desempenho Mas como você organiza seu código PHP no servidor praticamente não importa do ponto de vista do desempenho.

E a organização do código é uma preferência pessoal

E por último, mas não menos importante, a organização do código é uma preferência pessoal. Algumas pessoas odeiam como eu organizo o código, assim como eu odeio como eles fazem isso também. Encontre algo que você goste e cumpra-o, mas permita que sua estratégia evolua com o tempo, à medida que você aprende mais e fica mais confortável com ela.

117
MikeSchinkel

Resposta atrasada

Como incluir seus arquivos da maneira correta:

function wpse1403_bootstrap()
{
    // Here we load from our includes directory
    // This considers parent and child themes as well    
    locate_template( array( 'inc/foo.class.php' ), true, true );
}
add_action( 'after_setup_theme', 'wpse1403_bootstrap' );

O mesmo funciona em plugins também.

Como obter o caminho certo ou URi

Também dê uma olhada nas funções da API do sistema de arquivos como:

  • home_url()
  • plugin_dir_url()
  • plugin_dir_path()
  • admin_url()
  • get_template_directory()
  • get_template_directory_uri()
  • get_stylesheet_directory()
  • get_stylesheet_directory_uri()
  • etc.

Como reduzir o número de include/require

Se você precisar buscar todos arquivos de um diretório, vá com

foreach ( glob( 'path/to/folder/*.php' ) as $file )
    include $file;

Tenha em mente que isso ignora falhas (talvez boas para uso em produção)/arquivos não carregáveis.

Para alterar esse comportamento, você pode querer usar uma configuração diferente durante o desenvolvimento:

$files = ( defined( 'WP_DEBUG' ) AND WP_DEBUG )
    ? glob( 'path/to/folder/*.php', GLOB_ERR )
    : glob( 'path/to/folder/*.php' )

foreach ( $files as $file )
    include $file;

Editar: abordagem OOP/SPL

Como eu acabei de voltar e vi que esta resposta está ficando mais e mais votada, eu pensei em mostrar como estou fazendo hoje - em um PHP 5.3+ mundo. O exemplo a seguir carrega todos os arquivos de uma subpasta de temas chamada src/. É onde eu tenho minhas bibliotecas que lidam com certas tarefas, como menus, imagens, etc. Você nem precisa se preocupar com o nome, já que cada arquivo é carregado. Se você tiver outras subpastas nesse diretório, elas serão ignoradas.

O \FilesystemIterator é o PHP 5.3+ supercedor sobre o \DirectoryIterator. Ambos são parte do PHP SPL. Enquanto PHP 5.2 tornou possível desativar a extensão de SPL incorporada (abaixo de 1% de todas as instalações fez isso), o SPL agora é parte de PHP core.

<?php

namespace Theme;

$files = new \FilesystemIterator( __DIR__.'/src', \FilesystemIterator::SKIP_DOTS );
foreach ( $files as $file )
{
    /** @noinspection PhpIncludeInspection */
    ! $files->isDir() and include $files->getRealPath();
}

Anteriormente, embora eu ainda suportasse PHP 5.2.x, usei a seguinte solução: A \FilterIterator no diretório src/Filters para recuperar apenas os arquivos (e não os ponteiros de pontos das pastas) e um \DirectoryIterator para fazer o loop e o carregamento.

namespace Theme;

use Theme\Filters\IncludesFilter;

$files = new IncludesFilter( new \DirectoryIterator( __DIR__.'/src' ) );
foreach ( $files as $file )
{
    include_once $files->current()->getRealPath();
}

O \FilterIterator foi tão fácil quanto isso:

<?php

namespace Theme\Filters;

class IncludesFilter extends \FilterIterator
{
    public function accept()
    {
        return
            ! $this->current()->isDot()
            and $this->current()->isFile()
            and $this->current()->isReadable();
    }
}

Além de PHP 5.2 estar morto/EOL agora (e 5.3 também), há o fato de que é mais código e mais um arquivo no jogo, então não há razão para ir com o posterior e suporte PHP 5.2.x.

Resumido

Um artigo ainda mais aprofundado pode ser encontrado aqui no WPKrauts .

EDITA maneira obviamente correta é usar o código namespaced, preparado para PSR-4 autoloading colocando tudo no diretório apropriado que já está definido através do namespace. Então apenas use Composer e um composer.json para gerenciar suas dependências e deixe-o auto-construir seu PHP autoloader (que importa automaticamente um arquivo apenas chamando use \<namespace>\ClassName). Esse é o padrão de fato no PHP world, a maneira mais fácil de ir e ainda mais pré-automatizada e simplificada por WP Starter .

50
kaiser

Eu gosto de usar uma função para os arquivos dentro de uma pasta. Essa abordagem facilita a adição de novos recursos ao adicionar novos arquivos. Mas eu escrevo sempre em classe ou com namespaces - dá mais controle sobre o Namespace de funções, método etc.

Abaixo um pequeno exemplo; ut também useage com o acordo sobre a classe * .php

public function __construct() {

    $this->load_classes();
}

/**
 * Returns array of features, also
 * Scans the plugins subfolder "/classes"
 *
 * @since   0.1
 * @return  void
 */
protected function load_classes() {

    // load all files with the pattern class-*.php from the directory classes
    foreach( glob( dirname( __FILE__ ) . '/classes/class-*.php' ) as $class )
        require_once $class;

}

Em Temas eu uso muitas vezes um outro cenário. Eu defino a função do arquivo externel em um ID de suporte, veja o exemplo. Isso é útil se eu facilmente desativar o arquivo externo. Eu uso o WP função principal require_if_theme_supports() e ele só carrega, se o ID de suporte estava ativo. No exemplo a seguir, eu deifenquei essa ID suportada na linha antes de carregar o arquivo.

    /**
     * Add support for Theme Customizer
     * 
     * @since  09/06/2012
     */
    add_theme_support( 'documentation_customizer', array( 'all' ) );
    // Include the theme customizer for options of theme options, if theme supported
    require_if_theme_supports( 
        'documentation_customizer',
        get_template_directory() . '/inc/theme-customize.php'
    );

Você pode ver mais disso no repo deste tema .

5
bueltge

em termos de quebra, na minha placa de caldeira eu uso uma função personalizada para procurar uma pasta chamada funções no diretório do tema, se não estiver lá, ele cria. Em seguida, cria uma matriz de todos os arquivos .php encontrados nessa pasta (se houver) e executa um include (); em cada um deles.

Dessa forma, toda vez que eu precisar escrever alguma nova funcionalidade, basta adicionar um arquivo PHP na pasta de funções e não precisa se preocupar em codificá-lo no site.

<?php
/* 
FUNCTIONS for automatically including php documents from the functions folder.
*/
//if running on php4, make a scandir functions
if (!function_exists('scandir')) {
  function scandir($directory, $sorting_order = 0) {
    $dh = opendir($directory);
    while (false !== ($filename = readdir($dh))) {
      $files[] = $filename;
    }
    if ($sorting_order == 0) {
      sort($files);
    } else {
      rsort($files);
    }
    return ($files);
  }
}
/*
* this function returns the path to the funtions folder.
* If the folder does not exist, it creates it.
*/
function get_function_directory_extension($template_url = FALSE) {
  //get template url if not passed
  if (!$template_url)$template_url = get_bloginfo('template_directory');


  //replace slashes with dashes for explode
  $template_url_no_slash = str_replace('/', '.', $template_url);

  //create array from URL
  $template_url_array = explode('.', $template_url_no_slash);

  //--splice array

  //Calculate offset(we only need the last three levels)
  //We need to do this to get the proper directory, not the one passed by the server, as scandir doesn't work when aliases get involved.
  $offset = count($template_url_array) - 3;

  //splice array, only keeping back to the root WP install folder (where wp-config.php lives, where the front end runs from)
  $template_url_array = array_splice($template_url_array, $offset, 3);
  //put back togther as string
  $template_url_return_string = implode('/', $template_url_array);
  fb::log($template_url_return_string, 'Template'); //firephp

  //creates current working directory with template extention and functions directory    
  //if admin, change out of admin folder before storing working dir, then change back again.
  if (is_admin()) {
    $admin_directory = getcwd();
    chdir("..");
    $current_working_directory = getcwd();
    chdir($admin_directory);
  } else {
    $current_working_directory = getcwd();
  }
  fb::log($current_working_directory, 'Directory'); //firephp

  //alternate method is chdir method doesn't work on your server (some windows servers might not like it)
  //if (is_admin()) $current_working_directory = str_replace('/wp-admin','',$current_working_directory);

  $function_folder = $current_working_directory . '/' . $template_url_return_string . '/functions';


  if (!is_dir($function_folder)) mkdir($function_folder); //make folder, if it doesn't already exist (lazy, but useful....ish)
  //return path
  return $function_folder;

}

//removed array elements that do not have extension .php
function only_php_files($scan_dir_list = false) {
  if (!$scan_dir_list || !is_array($scan_dir_list)) return false; //if element not given, or not array, return out of function.
  foreach ($scan_dir_list as $key => $value) {
    if (!strpos($value, '.php')) {

      unset($scan_dir_list[$key]);
    }
  }
  return $scan_dir_list;
}
//runs the functions to create function folder, select it,
//scan it, filter only PHP docs then include them in functions

add_action('wp_head', fetch_php_docs_from_functions_folder(), 1);
function fetch_php_docs_from_functions_folder() {

  //get function directory
  $functions_dir = get_function_directory_extension();
  //scan directory, and strip non-php docs
  $all_php_docs = only_php_files(scandir($functions_dir));

  //include php docs
  if (is_array($all_php_docs)) {
    foreach ($all_php_docs as $include) {
      include($functions_dir . '/' . $include);
    }
  }

}
5
Mild Fuzz

Eu gerencio um site com cerca de 50 tipos de página personalizados exclusivos em diferentes idiomas do servidor em uma instalação de rede. Juntamente com uma tonelada de plugins.

Nós fomos obrigados a dividir tudo em algum momento. Um arquivo de funções com 20-30k linhas de código não é nada engraçado.

Decidimos refazer completamente todo o código para gerenciar melhor a base de código. A estrutura padrão do tema wordpress é boa para sites pequenos, mas não para sites maiores.

Nosso novo functions.php contém apenas o que é necessário para iniciar o site, mas nada que pertença a uma página específica.

O layout do tema que usamos agora é semelhante ao padrão de design MCV, mas em um estilo de codificação procedural.

Por exemplo, nossa página membro:

page-member.php . Responsável pela inicialização da página. Chamando as funções corretas de ajax ou similares. Poderia ser equivalente à parte do controlador no estilo MCV.

functions-member.php . Contém todas as funções relacionadas a esta página. Isso também está incluído em outras páginas do servidor que precisam de funções para nossos membros.

content-member.php . Prepara os dados para HTML Poderia ser equivalente ao modelo em MCV.

layout-member.php . A parte HTML.

Depois que fizemos essas alterações, o tempo de desenvolvimento diminuiu facilmente em 50% e agora o proprietário do produto tem problemas para nos dar novas tarefas. :)

4
Patrik Grinsvall

A partir do arquivo filho functions.php:

    require_once( get_stylesheet_directory() . '/inc/custom.php' );
3
Brad Dalton

Em functions.php, uma maneira mais elegante de chamar um arquivo requerido seria:

require_once locate_template ('/ inc/funções/shortcodes.php');

0
Imperative Ideas

Eu combinei as respostas de @kaiser e @mikeschinkel .

Eu tenho todas as minhas personalizações para o meu tema em uma pasta /includes e dentro dessa pasta eu tenho tudo dividido em subpastas.

Eu só quero que /includes/admin e seus sub-conteúdos sejam incluídos quando true === is_admin()

Se uma pasta for excluída em iterator_check_traversal_callback retornando false, seus subdiretórios não serão iterados (ou passados ​​para iterator_check_traversal_callback)

/**
 *  Require all customizations under /includes
 */
$includes_import_root = 
    new \RecursiveDirectoryIterator( __DIR__ . '/includes', \FilesystemIterator::SKIP_DOTS );

function iterator_check_traversal_callback( $current, $key, $iterator ) {
    $file_name = $current->getFilename();

    // Only include *.php files
    if ( ! $current->isDir() ) {
        return preg_match( '/^.+\.php$/i', $file_name );
    }

    // Don't include the /includes/admin folder when on the public site
    return 'admin' === $file_name
        ? is_admin()
        : true;
}

$iterator_filter = new \RecursiveCallbackFilterIterator(
    $includes_import_root, 'iterator_check_traversal_callback'
);

foreach ( new \RecursiveIteratorIterator( $iterator_filter ) as $file ) {
    include $file->getRealPath();
}
0
seangwright