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

85

Quanto mais personalização eu faço no WordPress, mais eu começo a pensar se eu deveria organizar este 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 apenas se aplicam ao meu site público, há alguma razão para possivelmente incluir todas as funções administrativas dentro de seu próprio arquivo ou agrupá-las? / p>

Seria possível dividi-los em arquivos separados ou agrupá-los, possivelmente, acelerar um site WordPress ou o WordPress / PHP automaticamente ignora 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).

    
por NetConstructor.com 06.09.2010 / 10:38
fonte

8 respostas

113

Se você está chegando ao ponto em que o código no 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 criei um subdiretório chamado "inclui" no diretório do meu tema 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 como 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 utilizei para testar minhas respostas a perguntas aqui no WordPress Answers. Toda vez que eu respondo 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 plug-ins

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, movo a maior parte do meu código para plugins.

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

Por outro lado, estruturar seus arquivos PHP é 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 imensas 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 de que goste e continue com isso, mas permita que sua estratégia evolua com o tempo à medida que você aprende mais e fica mais à vontade com ela.

    
por MikeSchinkel 06.09.2010 / 12:38
fonte
47

Resposta tardia

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

Veja também as 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, use

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, convém 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 mundo PHP 5.3 +. 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 supercedor do PHP 5.3 sobre o \DirectoryIterator . Ambos fazem parte do PHP SPL. Enquanto o PHP 5.2 tornou possível desativar a extensão de SPL embutida (abaixo de 1% de todas as instalações fizeram isso), o SPL agora faz parte do núcleo do PHP.

<?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 o PHP 5.2.x, usei a seguinte solução: A \FilterIterator no diretório src/Filters para recuperar apenas arquivos (e não pontos de pastas) e um \DirectoryIterator para fazer o loop e carregando.     

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 assim:

<?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 do 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 suportar o PHP 5.2.x .

Resumido

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

EDIT A maneira obviamente correta é usar o código namespace d, preparado para PSR-4 autoloading colocando tudo no diretório apropriado que já está definido através do namespace. Em seguida, basta usar o Composer e um composer.json para gerenciar suas dependências e permitir que ele crie automaticamente o autoloader do PHP (que importa automaticamente um arquivo apenas chamando %código%). Esse é o padrão de fato no mundo PHP, a maneira mais fácil de ir e ainda mais pré-automatizado e simplificado pelo WP Starter .

    
por kaiser 13.10.2012 / 15:53
fonte
5

Em termos de quebra, na minha placa de caldeira eu uso uma função personalizada para procurar por 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 algumas novas funcionalidades, basta adicionar um arquivo PHP à 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);
    }
  }

}
    
por Mild Fuzz 06.09.2010 / 15:50
fonte
5

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 de 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 frequentemente outro cenário. Eu defino a função do arquivo externel em um ID de suporte, veja o exemplo. Isso é útil se eu facilmente desabilitar o arquivo externo. Eu uso a função principal do WP require_if_theme_supports() e ele só carrega, se o ID de suporte estivesse 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 sobre isso no repositório deste tema .

    
por bueltge 04.10.2012 / 11:16
fonte
4

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. Junto 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 refatorar 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 do MCV, mas em um estilo de codificação procedimental.

Por exemplo, nossa página de 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. :)

    
por Patrik Grinsvall 04.10.2012 / 11:37
fonte
3

A partir de temas filhos, o arquivo functions.php:

    require_once( get_stylesheet_directory() . '/inc/custom.php' );
    
por Brad Dalton 20.10.2013 / 20:34
fonte
0

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

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

    
por Imperative Ideas 04.10.2012 / 09:23
fonte
0

Combinei @kaiser e @ mikeschinkel 's respostas.

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();
}
    
por seangwright 19.08.2018 / 21:02
fonte