Mengatur Kode di File WordPress.fp Tema WordPress Anda

92

Semakin banyak penyesuaian yang saya buat untuk WordPress, semakin saya mulai berpikir apakah saya harus mengatur file ini atau membaginya.

Lebih khusus, jika saya memiliki banyak fungsi khusus yang hanya berlaku untuk area admin dan lainnya yang hanya berlaku untuk situs web publik saya, adakah alasan untuk memasukkan semua fungsi admin ke dalam file mereka sendiri atau mengelompokkannya bersama?

Apakah membaginya menjadi file terpisah atau mengelompokkannya bersama mungkin mempercepat situs web WordPress atau apakah WordPress / PHP secara otomatis melewati fungsi yang memiliki awalan kode is_admin?

Apa cara terbaik untuk menangani file fungsi besar (saya punya 1.370 baris).

NetConstructor.com
sumber

Jawaban:

120

Jika Anda sampai pada titik di mana kode dalam tema Anda functions.phpmulai membanjiri Anda, saya pasti akan mengatakan Anda siap mempertimbangkan untuk memecahnya menjadi beberapa file. Saya cenderung melakukan itu hampir berdasarkan sifat kedua pada saat ini.

Gunakan Sertakan File di Tema Anda functions.phpBerkas

Saya membuat subdirektori yang disebut "include" di bawah direktori tema saya dan mengelompokkan kode saya ke dalam termasuk file yang dikelola oleh apa yang masuk akal bagi saya pada saat itu (yang berarti saya terus-menerus melakukan refactoring dan memindahkan kode ketika situs berevolusi.) Saya juga jarang masukkan kode nyata functions.php; semuanya berjalan di file include; hanya preferensi saya.

Sekedar memberi Anda contoh, inilah pengujian yang saya gunakan untuk menguji jawaban atas pertanyaan saya di sini di WordPress Answers. Setiap kali saya menjawab pertanyaan saya menyimpan kode di sekitar kalau-kalau saya membutuhkannya lagi. Ini bukan apa yang akan Anda lakukan untuk situs langsung tetapi ini menunjukkan mekanisme pemisahan kode:

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

Atau Buat Plugin

Pilihan lain untuk mulai mengelompokkan kode Anda berdasarkan fungsi dan membuat plugin Anda sendiri. Bagi saya, saya mulai mengkode dalam file tema functions.phpdan pada saat saya mendapatkan kode itu saya sudah pindah sebagian besar kode saya ke plugin.

Namun TIDAK ADA Perolehan Kinerja Yang Signifikan Dari Organisasi Kode PHP

Di sisi lain, penataan file PHP Anda adalah 99% tentang menciptakan keteraturan dan pemeliharaan dan 1% tentang kinerja, jika itu (pengorganisasian .jsdan .cssfile yang dipanggil oleh browser melalui HTTP adalah kasus yang sama sekali berbeda dan memiliki implikasi kinerja yang sangat besar.) Tetapi bagaimana Anda mengatur kode PHP Anda di server cukup banyak tidak masalah dari perspektif kinerja.

Dan Organisasi Kode adalah Preferensi Pribadi

Dan organisasi kode yang tak kalah pentingnya adalah preferensi pribadi. Beberapa orang akan membenci bagaimana saya mengatur kode sama seperti saya mungkin membenci bagaimana mereka melakukannya juga. Temukan sesuatu yang Anda sukai dan tetap menggunakannya, tetapi biarkan strategi Anda berkembang seiring waktu ketika Anda belajar lebih banyak dan lebih nyaman dengannya.

MikeSchinkel
sumber
Jawaban yang bagus, saya baru saja tiba di titik ini di mana saya perlu membagi file fungsi. Menurut Anda kapan praktis untuk berpindah dari frunctions.php ke sebuah plugin. Anda mengatakan dalam jawaban Anda: pada saat saya mendapatkan kode itu, saya telah memindahkan sebagian besar kode saya ke plugin . Saya tidak mengerti sepenuhnya, apa yang Anda maksud dengan daging.
Saif Bechan
5
+1 untuk "atau membuat plugin". Lebih khusus lagi, " plugin fungsionalitas "
Ian Dunn
3
menggunakan jalur relatif mungkin tidak dapat diandalkan dalam semua jenis pengaturan, jalur absolut harus selalu digunakan sebagai gantinya
Mark Kaplun
2
@MarkKaplun - Anda benar sekali . Karena saya menulis jawaban ini, saya belajar pelajaran itu dengan cara yang sulit. Saya akan memperbarui jawaban saya. Terima kasih telah menunjukkan ini.
MikeSchinkel
Saya mendapatkan "Penggunaan DIR konstan yang tidak terdefinisi - diasumsikan ' DIR ' dalam C: \ wamp \ www \ situs \ wp-content \ themes \ mytheme \ functions.php" - PHP v5.6.25 dan PHP v7.0.10 - Saya tidak bisa format dengan benar DIR ini dalam komentar (underscoreunderscoreDIRunderscoreunderscore), tetapi berfungsi dengan dirname (underscoreunderscoreFILEunderscoreunderscore)
Marko
50

Jawaban terlambat

Cara memasukkan file Anda dengan cara yang benar:

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

Hal yang sama juga berfungsi pada plugin.

Cara mendapatkan jalur yang benar atau URi

Lihat juga fungsi sistem file API seperti:

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

Cara mengurangi jumlah include/require

Jika Anda perlu mengambil semua file dari direktori, ikuti

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

Perlu diingat bahwa ini mengabaikan kegagalan (mungkin baik untuk penggunaan produksi) / tidak dapat memuat file.

Untuk mengubah perilaku ini, Anda mungkin ingin menggunakan konfigurasi yang berbeda selama pengembangan:

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

Ketika saya baru saja kembali dan melihat bahwa jawaban ini semakin meningkat, saya pikir saya mungkin menunjukkan bagaimana saya melakukannya sekarang - di dunia PHP 5.3+. Contoh berikut memuat semua file dari subfolder tema yang bernama src/. Di sinilah saya memiliki perpustakaan saya yang menangani tugas-tugas tertentu seperti menu, gambar, dll. Anda bahkan tidak perlu peduli dengan nama karena setiap file tunggal dimuat. Jika Anda memiliki subfolder lain di direktori ini, mereka diabaikan.

The \FilesystemIteratoradalah PHP 5.3+ supercedor atas \DirectoryIterator. Keduanya adalah bagian dari PHP SPL. Sementara PHP 5.2 memungkinkan untuk menonaktifkan ekstensi SPL bawaan (di bawah 1% dari semua pemasangan yang melakukannya), SPL sekarang adalah bagian dari inti PHP.

<?php

namespace Theme;

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

Sebelumnya ketika saya masih mendukung PHP 5.2.x, saya menggunakan solusi berikut: A \FilterIteratordalam src/Filtersdirektori hanya mengambil file (dan bukan dot pointer folder) dan \DirectoryIteratoruntuk melakukan perulangan dan memuat.

namespace Theme;

use Theme\Filters\IncludesFilter;

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

The \FilterIteratorsemudah itu:

<?php

namespace Theme\Filters;

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

Selain PHP 5.2 sudah mati / EOL sekarang (dan 5.3 juga), ada fakta bahwa itu lebih banyak kode dan satu file lagi dalam permainan, jadi tidak ada alasan untuk menggunakannya nanti dan mendukung PHP 5.2.x.

Jumlahkan

Artikel yang lebih mendalam dapat ditemukan di sini di WPKrauts .

EDIT Cara yang jelas benar adalah dengan menggunakan namespacekode d, disiapkan untuk autoloading PSR-4 dengan meletakkan segala sesuatu di direktori yang sesuai yang sudah ditentukan melalui namespace. Kemudian gunakan saja Komposer dan a composer.jsonuntuk mengelola dependensi Anda dan biarkan ia membuat autoloader PHP Anda secara otomatis (yang mengimpor file secara otomatis hanya dengan menelepon use \<namespace>\ClassName). Itulah standar de-facto di dunia PHP, cara termudah untuk pergi dan bahkan lebih otomatis dan disederhanakan oleh WP Starter .

kaisar
sumber
5

dalam hal memecahnya, di piring ketel saya, saya menggunakan fungsi kustom untuk mencari folder yang disebut fungsi dalam direktori tema, jika tidak ada itu membuatnya. Kemudian adalah menciptakan array dari semua file .php yang ditemukan di folder itu (jika ada) dan menjalankan include (); pada masing-masing dari mereka.

Dengan begitu, setiap kali saya perlu menulis beberapa fungsi baru, saya hanya menambahkan file PHP ke folder fungsi, dan tidak perlu khawatir tentang pengkodean ke situs.

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

}
Fuzz Ringan
sumber
5
@mildfuzz : Trik yang bagus. Saya pribadi tidak akan menggunakannya untuk kode produksi karena itu untuk setiap halaman memuat apa yang bisa kita lakukan dengan mudah sekali ketika kita meluncurkan situs. Juga, saya menambahkan beberapa cara untuk menghilangkan file, seperti tidak memuat apa pun yang dimulai dengan garis bawah sehingga saya masih bisa menyimpan karya yang sedang berjalan dalam direktori tema. Kalau tidak, bagus!
MikeSchinkel
Saya suka ide itu tetapi saya setuju ini mungkin mengarah pada pemuatan yang tidak perlu untuk setiap permintaan. Adakah ide jika ada cara sederhana untuk membuat file functions.php akhir dibuat secara otomatis di-cache dengan beberapa jenis pembaruan jika / ketika file baru ditambahkan atau pada interval waktu tertentu?
NetConstructor.com
Bagus tapi itu mengarah pada fleksibilitas, juga apa yang terjadi jika seorang penyerang berhasil menjatuhkan kode mereka di sana? Dan bagaimana jika pemesanan termasuk penting?
Tom J Nowell
1
@MikeSchinkel Saya baru saja memanggil file kerja saya foo._php, lalu letakkan _php ketika saya ingin menjalankannya.
Mild Fuzz
@ NetConstructor: Akan tertarik pada beberapa solusi juga.
kaiser
5

Saya suka menggunakan fungsi untuk file di dalam folder. Pendekatan ini memudahkan untuk menambahkan fitur baru saat menambahkan file baru. Tapi saya selalu menulis di kelas atau dengan ruang nama - berikan lebih banyak kontrol tentang Namespace fungsi, metode dll.

Di bawah ini contoh kecil; ut juga penggunaan dengan perjanjian tentang class * .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;

}

Dalam Tema saya sering menggunakan skenario lain. Saya mendefinisikan fungsi file externel di ID dukungan, lihat contohnya. Itu berguna jika saya akan dengan mudah menonaktifkan feture dari file externel. Saya menggunakan fungsi inti WP require_if_theme_supports()dan dia memuat hanya, jika ID dukungan aktif. Pada contoh berikut ini saya membuat ID yang didukung ini di baris sebelum memuat 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'
    );

Anda dapat melihat lebih banyak dari ini di repo tema ini .

bueltge
sumber
4

Saya mengelola situs dengan sekitar 50 jenis halaman khusus yang unik dalam berbagai bahasa serveral melalui instalasi jaringan. Bersamaan dengan satu TON plugin.

Kami di mana terpaksa membagi semuanya pada suatu titik. File fungsi dengan 20-30 ribu baris kode sama sekali tidak lucu.

Kami memutuskan untuk menyelesaikan refactor semua kode untuk mengelola basis kode lebih baik. Struktur tema wordpress default baik untuk situs kecil, tetapi tidak untuk situs yang lebih besar.

Function.php baru kami hanya berisi apa yang diperlukan untuk memulai situs, tetapi tidak ada yang menjadi milik halaman tertentu.

Tata letak tema yang kita gunakan sekarang mirip dengan pola desain MCV, tetapi dalam gaya pengkodean prosedural.

Misalnya halaman anggota kami :

halaman-member.php . Bertanggung jawab untuk menginisialisasi halaman. Memanggil fungsi ajax yang benar atau serupa. Bisa jadi sama dengan bagian Controller dalam gaya MCV.

functions-member.php . Berisi semua fungsi yang terkait dengan halaman ini. Ini juga termasuk dalam halaman serveral lainnya yang membutuhkan fungsi untuk anggota kami.

content-member.php . Mempersiapkan data untuk HTML Bisa sama dengan Model di MCV.

layout-member.php . Bagian HTML.

Setelah kami melakukan perubahan ini, waktu pengembangan dengan mudah turun sebesar 50% dan sekarang pemilik produk kesulitan memberi kami tugas baru. :)

Patrik Grinsvall
sumber
7
Untuk membuatnya lebih bermanfaat, Anda dapat mempertimbangkan untuk menunjukkan bagaimana pola MVC ini benar-benar bekerja.
kaiser
Saya juga ingin melihat contoh pendekatan Anda, lebih disukai dengan beberapa detail / berbagai situasi. Pendekatannya terdengar sangat interresting. Sudahkah Anda membandingkan beban / kinerja server dengan metodologi standar yang digunakan orang lain? berikan contoh github jika memungkinkan.
NetConstructor.com
3

Dari file child functions.php tema:

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

Di functions.php, cara yang lebih elegan untuk memanggil file yang diperlukan adalah:

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

Ide Imperatif
sumber
4
locate_template()memiliki parameter ketiga ...
fuxia
0

Saya menggabungkan jawaban @kaiser dan @mikeschinkel .

Saya memiliki semua penyesuaian untuk tema dalam /includesfolder dan di dalam folder itu saya memiliki segalanya yang dibagi menjadi sub folder.

Saya hanya ingin /includes/admindan sub-kontennya dimasukkan ketikatrue === is_admin()

Jika folder dikecualikan iterator_check_traversal_callbackdengan mengembalikan falsemaka sub-direktori tidak akan diulang (atau diteruskan ke 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();
}
seangwright
sumber