Organizzare il codice nel file Functions.php del tuo tema WordPress?


92

Più personalizzazione faccio a WordPress, più comincio a pensare se dovrei organizzare questo file o dividerlo.

Più specificamente, se ho un sacco di funzioni personalizzate che si applicano solo all'area di amministrazione e altre che si applicano solo al mio sito Web pubblico, c'è qualche motivo per includere eventualmente tutte le funzioni di amministrazione nel proprio file o raggrupparle?

Suddividendoli in file separati o raggruppandoli insieme si potrebbe velocizzare un sito Web WordPress o WordPress / PHP salta automaticamente le funzioni che hanno un prefisso di codice is_admin?

Qual è il modo migliore per gestire un file di funzioni di grandi dimensioni (il mio è lungo 1370 righe).

Risposte:


120

Se stai arrivando al punto in cui il codice nel tuo tema functions.phpsta iniziando a sopraffarti, direi sicuramente che sei pronto a considerare di dividerlo in più file. Tendo a farlo quasi per seconda natura a questo punto.

Usa includere i file del vostro tema functions.phpFile

Creo una sottodirectory denominata "include" nella mia directory dei temi e segmento il mio codice in file include organizzati in base a ciò che ha senso per me in quel momento (il che significa che sto costantemente refactoring e muovo il codice mentre un sito si evolve). Anche raramente inserire qualsiasi codice reale functions.php; tutto va nei file include; solo la mia preferenza.

Solo per darvi un esempio, ecco la mia installazione di prova che utilizzo per testare le mie risposte alle domande qui su WordPress Answers. Ogni volta che rispondo a una domanda tengo il codice in giro nel caso ne avessi bisogno di nuovo. Questo non è esattamente quello che farai per un sito live, ma mostra i meccanismi di suddivisione del codice:

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

Oppure crea plugin

Un'altra opzione è quella di iniziare a raggruppare il codice per funzione e creare i propri plugin. Per me inizio a scrivere il codice nel functions.phpfile del tema e quando ho ottenuto il codice, ho spostato la maggior parte del mio codice in plugin.

Tuttavia NESSUN significativo guadagno di prestazioni dall'organizzazione del codice PHP

D'altra parte, strutturare i tuoi file PHP è del 99% sulla creazione di ordine e manutenibilità e dell'1% sulle prestazioni, se ciò (l'organizzazione .jse i .cssfile chiamati dal browser tramite HTTP sono un caso completamente diverso e hanno enormi implicazioni sulle prestazioni). Ma come organizzi il tuo codice PHP sul server praticamente non ha importanza dal punto di vista delle prestazioni.

E l'organizzazione del codice è una preferenza personale

E, ultimo ma non meno importante, l'organizzazione del codice è la preferenza personale. Alcune persone odierebbero il modo in cui organizzo il codice così come io odio anche il modo in cui lo fanno. Trova qualcosa che ti piace e seguilo, ma consenti alla tua strategia di evolversi nel tempo man mano che impari di più e ti senti più a tuo agio.


Bella risposta, sono appena arrivato a questo punto in cui ho bisogno di dividere il file delle funzioni. Quando pensi che sia utile passare da frunctions.php a un plugin. Hai detto nella tua risposta: quando ho capito il codice ho spostato la maggior parte del mio codice in plugin . Non lo capisco del tutto, cosa intendi con la carne.
Saif Bechan,

5
+1 per "o crea plug-in". Più specificamente, " plugin di funzionalità "
Ian Dunn,

3
l'uso di percorsi relativi potrebbe non essere affidabile in tutti i tipi di impostazioni, invece dovrebbe sempre essere usato il percorso assoluto
Mark Kaplun,

2
@MarkKaplun - Hai assolutamente ragione. Da quando ho scritto questa risposta ho imparato quella lezione nel modo più duro. Ho intenzione di aggiornare la mia risposta. Grazie per averlo segnalato.
MikeSchinkel,

Ottengo "Uso di DIR non definita costante - assunto ' DIR ' in C: \ wamp \ www \ site \ wp-content \ themes \ mytheme \ Functions.php" - PHP v5.6.25 e PHP v7.0.10 - Non riesco formatta correttamente questo DIR nel commento (underscoreunderscoreDIRunderscoreunderscore), ma funziona con dirname (underscoreunderscoreFILEunderscoreunderscore)
Marko

50

Risposta in ritardo

Come includere i tuoi file nel modo giusto:

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 stesso vale anche per i plugin.

Come ottenere la strada giusta o URi

Dai un'occhiata anche alle funzioni API del file system come:

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

Come ridurre il numero di include/require

Se devi recuperare tutti i file da una directory vai con

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

Tieni presente che ciò ignora i guasti (forse buono per l'uso in produzione) / i file non caricabili.

Per modificare questo comportamento potresti voler usare una configurazione diversa durante lo sviluppo:

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

Modifica: approccio OOP / SPL

Appena sono tornato e ho visto che questa risposta sta ottenendo sempre più voti, ho pensato di poter mostrare come sto andando al giorno d'oggi - in un mondo PHP 5.3+. L'esempio seguente carica tutti i file da una sottocartella temi denominata src/. È qui che ho le mie librerie che gestiscono determinate attività come menu, immagini, ecc. Non devi nemmeno preoccuparti del nome quando viene caricato ogni singolo file. Se hai altre sottocartelle in questa directory, vengono ignorate.

Il \FilesystemIteratorè il PHP 5.3+ supercedor sopra il \DirectoryIterator. Entrambi fanno parte di PHP SPL. Mentre PHP 5.2 ha reso possibile disattivare l'estensione SPL integrata (meno dell'1% di tutte le installazioni lo ha fatto), SPL ora fa parte del core PHP.

<?php

namespace Theme;

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

In precedenza, mentre supportavo ancora PHP 5.2.x, utilizzavo la seguente soluzione: A \FilterIteratornella src/Filtersdirectory per recuperare solo i file (e non puntatori di cartelle) e a \DirectoryIteratorper eseguire il loop e il caricamento.

namespace Theme;

use Theme\Filters\IncludesFilter;

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

È \FilterIteratorstato facile come quello:

<?php

namespace Theme\Filters;

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

A parte PHP 5.2 ormai morto / EOL (e anche 5.3), c'è il fatto che c'è più codice e un altro file nel gioco, quindi non c'è motivo di andare con il successivo e supportare PHP 5.2.x.

Riassunto

Un articolo ancora più approfondito è disponibile qui su WPKrauts .

MODIFICA Il modo ovviamente corretto è usare il namespacecodice d, preparato per il caricamento automatico di PSR-4 mettendo tutto nella directory appropriata che è già definita tramite lo spazio dei nomi. Quindi usa Composer e a composer.jsonper gestire le tue dipendenze e lascia che costruisca automaticamente il tuo caricatore automatico PHP (che importa automaticamente un file semplicemente chiamando use \<namespace>\ClassName). Questo è lo standard di fatto nel mondo PHP, il modo più semplice di procedere e ancora più pre-automatizzato e semplificato da WP Starter .


5

in termini di suddivisione, nella mia piastra della caldaia utilizzo una funzione personalizzata per cercare una cartella chiamata funzioni nella directory dei temi, se non è presente la crea. Quindi crea un array di tutti i file .php che trova nella cartella (se presente) ed esegue un include (); su ciascuno di essi.

In questo modo, ogni volta che devo scrivere alcune nuove funzionalità, aggiungo semplicemente un file PHP alla cartella delle funzioni e non devo preoccuparmi di codificarlo nel sito.

<?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
@mildfuzz : bel trucco. Personalmente non lo userei per il codice di produzione perché fa caricare per ogni pagina ciò che potremmo facilmente fare una volta all'avvio del sito. Inoltre, aggiungerei in qualche modo per omettere i file, come non caricare nulla a partire da un trattino basso in modo da poter ancora archiviare i lavori in corso nella directory dei temi. Altrimenti, bello!
MikeSchinkel,

mi piace l'idea, ma sono d'accordo che ciò potrebbe comportare un caricamento non necessario per ogni richiesta. Qualche idea se ci sarebbe un modo semplice per generare automaticamente il file definitivo func.php memorizzato nella cache con un tipo di aggiornamento se / quando vengono aggiunti nuovi file o in un intervallo di tempo specifico?
NetConstructor.com

Bello ma porta a inflessibilità, cosa succede anche se un attaccante riesce a far cadere il proprio codice lì dentro? E se l'ordinamento di include è importante?
Tom J Nowell

1
@MikeSchinkel Ho appena chiamato i miei file di lavoro foo._php, quindi ho lasciato _php quando volevo che funzionasse.
Fuzz delicato,

@NetConstructor: Sarebbe interessato anche ad alcuni sollution.
Kaiser

5

Mi piace usare una funzione per i file all'interno di una cartella. Questo approccio semplifica l'aggiunta di nuove funzionalità quando si aggiungono nuovi file. Ma scrivo sempre in classe o con spazi dei nomi - gli conferisco un maggiore controllo sullo spazio dei nomi di funzioni, metodo ecc.

Di seguito un piccolo esempio; ut anche con l'accordo sulla 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;

}

In Temi utilizzo spesso un altro scenario. Definisco la funzione del file externel in un ID supporto, vedere l'esempio. Ciò è utile se disattiverò facilmente la figura del file esterno. Uso la funzione core WP require_if_theme_supports()e si carica solo se l'ID supporto era attivo. Nel seguente esempio ho definito questo ID supportato nella riga prima di caricare il file.

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

Puoi vedere di più nel repository di questo tema .


4

Gestisco un sito con circa 50 tipi di pagine personalizzate univoci in diverse lingue del server su un'installazione di rete. Insieme a una tonnellata di plugin.

Siamo stati costretti a dividere tutto ad un certo punto. Un file di funzioni con 20-30k righe di codice non è affatto divertente.

Abbiamo deciso di completare il refactoring di tutto il codice per gestire meglio la base di codice. La struttura del tema wordpress di default è buona per i siti di piccole dimensioni, ma non per i siti più grandi.

Le nostre nuove funzioni.php contengono solo ciò che è necessario per avviare il sito, ma nulla che appartiene a una pagina specifica.

Il layout del tema che usiamo ora è simile al modello di progettazione MCV, ma in uno stile di codifica procedurale.

Ad esempio la nostra pagina dei membri:

page-member.php . Responsabile dell'inizializzazione della pagina. Chiamata delle funzioni Ajax corrette o simili. Potrebbe essere equivalente alla parte Controller nello stile MCV.

features-member.php . Contiene tutte le funzioni relative a questa pagina. Questo è incluso anche in altre pagine serverali che richiedono funzioni per i nostri membri.

content-member.php . Prepara i dati per HTML Potrebbe essere equivalente al Modello in MCV.

layout-member.php . La parte HTML.

Dopo aver apportato queste modifiche, i tempi di sviluppo sono diminuiti facilmente del 50% e ora il proprietario del prodotto ha difficoltà a assegnarci nuove attività. :)


7
Per rendere questo più utile, potresti considerare di mostrare come funziona davvero questo modello MVC.
Kaiser

sarei anche curioso di vedere un esempio del tuo approccio, preferibilmente con alcuni dettagli / varie situazioni. L'approccio sembra molto interessante. Hai confrontato il carico / le prestazioni del server con la metodologia standard utilizzata da altri? fornire un esempio github se possibile.
NetConstructor.com il

3

Dal file funzioni.php di temi figlio:

    require_once( get_stylesheet_directory() . '/inc/custom.php' );

0

In funzioni.php, un modo più elegante per chiamare un file richiesto sarebbe:

Require_once Locate_template ('/ inc / funzioni / shortcodes.php');


4
locate_template()ha un terzo parametro ...
fuxia

0

Ho combinato le risposte di @kaiser e @mikeschinkel .

Ho tutte le mie personalizzazioni per il mio tema in una /includescartella e all'interno di quella cartella ho tutto suddiviso in sottocartelle.

Voglio solo che i /includes/adminsuoi contenuti secondari vengano inclusi quandotrue === is_admin()

Se una cartella viene esclusa iterator_check_traversal_callbackdalla restituzione, le falsesue sottodirectory non verranno ripetute (o passate 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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.