web-dev-qa-db-fra.com

Code d'organisation dans le fichier functions.php de votre thème WordPress?

Plus je personnalise WordPress, plus je commence à me demander si je devrais organiser ce fichier ou le fractionner.

Plus précisément, si j’ai un ensemble de fonctions personnalisées qui s’appliquent uniquement à la zone administrative et à d’autres qui s’appliquent uniquement à mon site Web public, existe-t-il une raison pour éventuellement inclure toutes les fonctions administratives dans leur propre fichier ou les regrouper?

Est-ce que le fait de les séparer en fichiers séparés ou de les regrouper pourrait éventuellement accélérer un site Web WordPress ou WordPress/PHP ignore-t-il automatiquement les fonctions qui ont un préfixe is_admin?

Quelle est la meilleure façon de traiter un fichier de grandes fonctions (le mien a une longueur de 1370 lignes).

91
NetConstructor.com

Si vous arrivez au point où le code dans le functions.php de votre thème commence à vous submerger, je dirais certainement que vous êtes prêt à envisager de le scinder en plusieurs fichiers. J'ai tendance à le faire presque par seconde nature à ce stade.

Utilisez les fichiers d'inclusion dans le fichier functions.php de votre thème.

Je crée un sous-répertoire appelé "includes" dans mon répertoire de thèmes et segmente mon code en fichiers include organisés en fonction de ce qui a du sens pour moi à ce moment-là (ce qui signifie que je refacture et déplace constamment le code au fur et à mesure de l'évolution du site. ) J'ai aussi rarement mis un code réel dans functions.php; tout se passe dans les fichiers inclus; juste ma préférence.

Juste pour vous donner un exemple, voici mon installation de test que j'utilise pour tester mes réponses aux questions ici sur WordPress Answers. Chaque fois que je réponds à une question, je garde le code au cas où j'en aurais besoin à nouveau. Ce n'est pas exactement ce que vous ferez pour un site actif, mais cela montre la mécanique de la division du code:

<?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 créer des plugins

Une autre option est de commencer à regrouper votre code par fonction et de créer vos propres plugins. Pour moi, je commence à coder dans le fichier functions.php du thème et au moment où je complète le code, j'ai transféré la plupart de mon code dans des plugins.

Cependant, AUCUN gain de performance significatif de PHP organisation du code

D'autre part, structurer vos fichiers PHP concerne 99% de la commande et de la maintenabilité et 1% de la performance, si cela (organiser les fichiers .js et .css appelés par le navigateur via HTTP est un cas complètement différent et a conséquences sur les performances.) Cependant, la manière dont vous organisez votre PHP code sur le serveur n’importe pas du point de vue des performances.

Et l'organisation du code est la préférence personnelle

Enfin, l'organisation du code est une préférence personnelle. Certaines personnes détesteraient la façon dont j'organise le code, tout comme je le ferais peut-être aussi. Trouvez quelque chose que vous aimez et respectez-le, mais laissez votre stratégie évoluer avec le temps, à mesure que vous en apprendrez plus et en deviendrez plus à l'aise.

117
MikeSchinkel

Réponse tardive

Comment inclure vos fichiers correctement:

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

La même chose fonctionne aussi dans les plugins.

Comment obtenir le bon chemin ou URi

Jetez également un coup d'œil aux fonctions de l'API du système de fichiers telles que:

  • 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.

Comment réduire le nombre de include/require

Si vous avez besoin de récupérer tous fichiers d'un répertoire, allez avec

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

Gardez à l'esprit que cela ignore les échecs (peut-être bons pour une utilisation en production)/les fichiers non chargeables.

Pour modifier ce comportement, vous pouvez utiliser une configuration différente au cours du développement:

$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;

Edit: approche OOP/SPL

Alors que je revenais juste et que je voyais que cette réponse recevait de plus en plus de votes positifs, j'ai pensé pouvoir montrer comment je le fais aujourd'hui - dans un monde PHP 5.3+. L'exemple suivant charge tous les fichiers d'un sous-dossier de thèmes nommé src/. C'est là que mes bibliothèques gèrent certaines tâches telles que les menus, les images, etc. Vous n'avez même pas à vous soucier du nom car chaque fichier est chargé. Si vous avez d'autres sous-dossiers dans ce répertoire, ils sont ignorés.

Le \FilesystemIterator est le PHP 5.3+ supercedor sur le \DirectoryIterator. Les deux font partie de PHP SPL. Alors que PHP 5.2 permettait de désactiver l'extension SPL intégrée (moins de 1% de toutes les installations le faisaient), le SPL fait maintenant partie 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();
}

Auparavant, alors que je prenais toujours en charge PHP 5.2.x, j’utilisais la solution suivante: Un \FilterIterator dans le répertoire src/Filters pour extraire uniquement les fichiers (et non les pointeurs de dossiers) et un \DirectoryIterator pour effectuer la mise en boucle et le chargement.

namespace Theme;

use Theme\Filters\IncludesFilter;

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

Le \FilterIterator était aussi simple que cela:

<?php

namespace Theme\Filters;

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

Mis à part PHP 5.2 étant mort/EOL maintenant (et 5.3 également), il y a le fait qu'il y a plus de code et un fichier de plus dans le jeu, il n'y a donc aucune raison de continuer avec ce dernier et de supporter PHP 5.2.x.

Résumé

Un article encore plus détaillé peut être trouvé ici sur WPKrauts .

EDITLa méthode correcte consiste évidemment à utiliser le code namespaced, préparé pour PSR-4 / autoloading, en plaçant tout dans le répertoire approprié déjà défini via l’espace de noms. Ensuite, utilisez simplement Composer et un composer.json pour gérer vos dépendances et laissez-le construire automatiquement votre PHP autochargeur (qui importe automatiquement un fichier en appelant simplement use \<namespace>\ClassName). C’est le standard de facto dans le monde PHP, le moyen le plus simple et encore plus pré-automatisé et simplifié par WP Starter .

50
kaiser

J'aime utiliser une fonction pour les fichiers dans un dossier. Cette approche facilite l'ajout de nouvelles fonctionnalités lors de l'ajout de nouveaux fichiers. Mais j'écris toujours en classe ou avec des espaces de noms - donnez-lui plus de contrôle sur l'espace de noms des fonctions, méthodes, etc.

Ci-dessous un petit exemple; ut également utilisation avec l'accord sur la 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;

}

Dans les thèmes, j'utilise souvent un autre scénario. Je définis la fonction du fichier externel dans un ID de support, voir l'exemple. C’est utile si je vais facilement désactiver la fonction du fichier externel. J'utilise la fonction principale WP _ require_if_theme_supports() et il ne charge que si l'ID de support était actif. Dans l'exemple suivant, j'ai défini cet ID pris en charge dans la ligne avant de charger le fichier.

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

Vous pouvez en voir plus dans le rapport de ce thème .

5
bueltge

pour ce qui est de le casser, dans ma plaque de chaudière, j'utilise une fonction personnalisée pour rechercher un dossier appelé fonctions dans le répertoire theme. S'il n'est pas présent, il le crée. Ensuite, crée un tableau de tous les fichiers .php trouvés dans ce dossier (le cas échéant) et exécute un include (); sur chacun d'eux.

Ainsi, chaque fois que j'ai besoin d'écrire de nouvelles fonctionnalités, j'ajoute simplement un fichier PHP au dossier des fonctions, sans avoir à vous soucier de le coder sur le 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

Je gère un site avec environ 50 types de page personnalisés uniques dans plusieurs langues sur une installation réseau. Avec une tonne de plugins.

Nous avons été obligés de tout séparer à un moment donné. Un fichier de fonctions avec 20-30k lignes de code n'est pas drôle du tout.

Nous avons décidé de complètement refactoriser tout le code afin de mieux gérer la base de code. La structure de thème wordpress par défaut convient aux petits sites, mais pas aux plus grands.

Notre nouveau functions.php ne contient que ce qui est nécessaire pour démarrer le site, mais rien qui appartient à une page spécifique.

La disposition de thème que nous utilisons maintenant est similaire au modèle de conception MCV, mais dans un style de codage procédural.

Par exemple notre page membre:

page-member.php . Responsable de l'initialisation de la page. Appeler les fonctions ajax correctes ou similaires. Pourrait être équivalent à la partie contrôleur dans le style MCV.

functions-member.php . Contient toutes les fonctions liées à cette page. Ceci est également inclus dans plusieurs autres pages qui ont besoin de fonctions pour nos membres.

content-member.php . Prépare les données pour HTML Peut être équivalent au modèle dans MCV.

layout-member.php . La partie HTML.

Après ces modifications, le temps de développement a facilement diminué de 50% et le responsable du produit a maintenant du mal à nous attribuer de nouvelles tâches. :)

4
Patrik Grinsvall

A partir du fichier functions.php des thèmes enfants:

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

Dans functions.php, un moyen plus élégant d’appeler un fichier requis serait:

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

0
Imperative Ideas

J'ai combiné les réponses de @kaiser et @mikeschinkel .

Toutes mes personnalisations sur mon thème se trouvent dans un dossier /includes. Dans ce dossier, tout est divisé en sous-dossiers.

Je veux seulement que /includes/admin et ses sous-contenus soient inclus lorsque true === is_admin()

Si un dossier est exclu dans iterator_check_traversal_callback en renvoyant false, ses sous-répertoires ne seront ni itérés (ni passés à 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