¿Organizar el código en el archivo de funciones de su tema de WordPress?

85

Mientras más personalización le hago a WordPress, más empiezo a pensar si debería organizar este archivo o dividirlo.

Más específicamente, si tengo un montón de funciones personalizadas que solo se aplican al área de administración y otras que solo se aplican a mi sitio web público, ¿existe alguna razón para incluir todas las funciones de administración en su propio archivo o agruparlas? / p>

¿Dividirlos en archivos separados o agruparlos podría acelerar un sitio web de WordPress o WordPress / PHP omite automáticamente las funciones que tienen un prefijo de código is_admin?

¿Cuál es la mejor manera de tratar con un archivo de funciones de gran tamaño (el mío tiene 1370 líneas de longitud).

    
pregunta NetConstructor.com 06.09.2010 - 10:38

8 respuestas

113

Si está llegando al punto en que el código en functions.php de su tema está comenzando a abrumarlo, definitivamente diría que está listo para considerar dividirlo en varios archivos. Tiendo a hacer eso casi por segunda naturaleza en este punto.

Use Incluir archivos en el archivo functions.php de su tema

Creo un subdirectorio llamado "includes" en mi directorio de temas y segmento mi código para incluir archivos organizados por lo que tiene sentido para mí en ese momento (lo que significa que estoy refactorizando y moviendo constantemente el código). alrededor como un sitio evoluciona.) También rara vez pongo un código real en functions.php ; Todo va en los archivos de inclusión; solo mi preferencia.

Solo para darles un ejemplo, aquí está mi instalación de prueba que uso para probar mis respuestas a las preguntas aquí en WordPress Answers. Cada vez que respondo a una pregunta guardo el código en caso de que lo necesite de nuevo. Esto no es exactamente lo que harás para un sitio en vivo, pero muestra la mecánica de dividir el 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');  

O crear complementos

Otra opción es comenzar a agrupar su código por función y crear sus propios complementos. Para mí, comienzo a codificar en el archivo functions.php del tema y, en el momento en que he completado el código, he movido la mayor parte de mi código a complementos.

Sin embargo, NO hay un rendimiento significativo de la organización con código PHP

Por otra parte, estructurar sus archivos PHP es 99% sobre la creación de orden y mantenimiento y 1% sobre el rendimiento, si eso (organizar archivos .js y .css llamados por el navegador a través de HTTP es un caso completamente diferente y tiene enormes implicaciones de rendimiento.) Pero la forma en que organice su código PHP en el servidor no importa desde una perspectiva de rendimiento.

Y la organización del código es una preferencia personal

Y por último, pero no menos importante, la organización del código es la preferencia personal. Algunas personas odiarían la forma en que organizo el código, así como yo también odiaría la forma en que lo hacen. Encuentra algo que te guste y apégate a él, pero permite que tu estrategia evolucione con el tiempo a medida que aprendes más y te sientas más cómodo con él.

    
respondido por el MikeSchinkel 06.09.2010 - 12:38
47

Respuesta tardía

Cómo incluir sus archivos de la manera correcta:

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' );

Lo mismo funciona en los complementos también.

Cómo obtener el camino correcto o URi

También eche un vistazo a las funciones de la API del sistema de archivos 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.

Cómo reducir el número de include/require

Si necesita recuperar todos de un directorio, vaya con

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

Tenga en cuenta que esto ignora las fallas (quizás buenas para uso de producción) / archivos no cargables.

Para modificar este comportamiento, es posible que desee utilizar una configuración diferente durante el desarrollo:

$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: enfoque OOP / SPL

Cuando acabo de regresar y vi que esta respuesta está ganando cada vez más votos, pensé que podría mostrar cómo lo estoy haciendo hoy en día, en un mundo con PHP 5.3+. El siguiente ejemplo carga todos los archivos de una subcarpeta de temas llamada src/ . Aquí es donde tengo mis bibliotecas que manejan ciertas tareas como menús, imágenes, etc. Ni siquiera tiene que preocuparse por el nombre a medida que se carga cada archivo. Si tiene otras subcarpetas en este directorio, se ignorarán.

El \FilesystemIterator es el supercedor supercedor de PHP 5.3+ sobre el \DirectoryIterator . Ambos son parte del PHP SPL. Aunque PHP 5.2 hizo posible desactivar la extensión SPL incorporada (menos del 1% de todas las instalaciones lo hicieron), la SPL ahora es parte del núcleo de 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, mientras aún era compatible con PHP 5.2.x, utilicé la siguiente solución: un \FilterIterator en el directorio src/Filters para recuperar solo archivos (y no puntos de carpetas) y un \DirectoryIterator para hacer el bucle y cargando.     

namespace Theme;

use Theme\Filters\IncludesFilter;

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

El \FilterIterator fue tan fácil como eso:

<?php

namespace Theme\Filters;

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

Además de que PHP 5.2 está muerto / EOL por ahora (y 5.3 también), existe el hecho de que hay más código y un archivo más en el juego, por lo que no hay razón para seguir adelante con PHP 5.2.x .

Resumido

Se puede encontrar un artículo aún más detallado en aquí en WPKrauts .

EDIT La forma obviamente correcta es usar el código namespace d, preparado para PSR-4 carga automática colocando todo en el directorio apropiado que ya está definido a través del espacio de nombres. Luego solo use Compositor y un composer.json para administrar sus dependencias y dejar que genere automáticamente su cargador automático de PHP (que importa automáticamente un archivo simplemente llamando %código%). Ese es el estándar de facto en el mundo de PHP, la forma más fácil de ir y aún más pre-automatizado y simplificado por WP Starter .

    
respondido por el kaiser 13.10.2012 - 15:53
5

en términos de dividirlo, en mi placa de calderas, uso una función personalizada para buscar una carpeta llamada funciones en el directorio de temas, si no está allí, la crea. Entonces se crea una matriz de todos los archivos .php que encuentra en esa carpeta (si existe) y ejecuta un include (); en cada uno de ellos.

De esa manera, cada vez que necesito escribir alguna nueva funcionalidad, solo agrego un archivo PHP a la carpeta de funciones, y no tengo que preocuparme por codificarlo en el sitio.

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

}
    
respondido por el Mild Fuzz 06.09.2010 - 15:50
5

Me gusta usar una función para los archivos dentro de una carpeta. Este enfoque facilita la adición de nuevas funciones al agregar nuevos archivos. Pero escribo siempre en clase o con espacios de nombres: dale más control sobre el espacio de nombres de las funciones, el método, etc.

Debajo de un pequeño ejemplo; Pero también uso con el acuerdo sobre la clase * .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;

}

En los temas que uso a menudo en otro escenario. Defino la función del archivo externo en un ID de soporte, vea el ejemplo. Eso es útil si fácilmente desactivaré la recuperación del archivo externo. Utilizo la función básica de WP require_if_theme_supports() y solo se carga, si el ID de soporte estaba activo. En el siguiente ejemplo, especifiqué este ID admitido en la línea antes de cargar el archivo.

    /**
     * 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'
    );

Puede ver más de esto en el repo de este tema .

    
respondido por el bueltge 04.10.2012 - 11:16
4

Administro un sitio con aproximadamente 50 tipos de páginas personalizadas únicas en diferentes idiomas en una instalación de red. Junto con una tonelada de complementos.

Nos vimos obligados a dividirlo todo en algún momento. Un archivo de funciones con 20-30k líneas de código no es divertido en absoluto.

Decidimos completar todo el código para poder administrar mejor la base de código. La estructura predeterminada del tema de wordpress es buena para sitios pequeños, pero no para sitios más grandes.

Nuestro nuevo functions.php solo contiene lo que es necesario para iniciar el sitio, pero nada que pertenezca a una página específica.

El diseño del tema que utilizamos ahora es similar al patrón de diseño de MCV, pero en un estilo de codificación de procedimiento.

Por ejemplo, nuestra página de miembros:

page-member.php . Responsable de inicializar la página. Llamando a las funciones correctas de ajax o similares. Podría ser equivalente a la parte del Controlador en el estilo MCV.

functions-member.php . Contiene todas las funciones relacionadas con esta página. Esto también se incluye en otras páginas que necesitan funciones para nuestros miembros.

content-member.php . Prepara los datos para HTML. Podría ser equivalente al modelo en MCV.

layout-member.php . La parte HTML.

Después de que hicimos estos cambios, el tiempo de desarrollo se redujo fácilmente en un 50% y ahora el propietario del producto tiene problemas para darnos nuevas tareas. :)

    
respondido por el Patrik Grinsvall 04.10.2012 - 11:37
3

Desde el archivo funciones.php de temas infantiles:

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

En functions.php, una forma más elegante de llamar a un archivo requerido sería:

require_once Locate_template ('/ inc / functions / shortcodes.php');

    
respondido por el Imperative Ideas 04.10.2012 - 09:23
0

Combiné las @kaiser 's y respuestas de @mikeschinkel .

Tengo todas mis personalizaciones para mi tema en una carpeta /includes y dentro de esa carpeta tengo todo dividido en subcarpetas.

Solo quiero que se incluya /includes/admin y sus sub-contenidos cuando true === is_admin()

Si una carpeta se excluye en iterator_check_traversal_callback devolviendo false , sus subdirectorios no se repetirán (ni se pasarán a 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();
}
    
respondido por el seangwright 19.08.2018 - 21:02

Lea otras preguntas en las etiquetas