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).
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.
functions.php
do seu temaEu 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');
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.
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 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.
Resposta atrasada
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.
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()
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;
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.
Um artigo ainda mais aprofundado pode ser encontrado aqui no WPKrauts .
EDITA 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. 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 .
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 .
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);
}
}
}
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. :)
A partir do arquivo filho functions.php:
require_once( get_stylesheet_directory() . '/inc/custom.php' );
Em functions.php, uma maneira mais elegante de chamar um arquivo requerido seria:
require_once locate_template ('/ inc/funções/shortcodes.php');
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();
}