WordPress Temanızın İşlev.php Dosyasında Kod Düzenleme?


92

WordPress için ne kadar çok kişiselleştirme yaparsam, o dosyayı düzenlemem mi yoksa bölmem mi gerektiğini düşünmeye başladım.

Daha spesifik olarak, yalnızca yönetici alanı için geçerli olan bir sürü özel işlevim varsa ve sadece genel web siteme uygulanan diğerleri, tüm yönetici işlevlerini kendi dosyalarına dahil etmenin veya bunları gruplandırmanın bir nedeni olabilir mi?

Bunları ayrı dosyalara bölmek ya da birlikte gruplamak, bir WordPress web sitesini hızlandırabilir mi ya da WordPress / PHP, is_admin kod öneki olan işlevleri otomatik olarak atlar mı?

Büyük işlevler dosyasıyla başa çıkmanın en iyi yolu nedir (benimki 1370 satır uzunluğunda).

Yanıtlar:


120

Temanızdaki kodun sizi ezmeye başladığı noktaya functions.phpgeliyorsanız, kesinlikle onu birden fazla dosyaya bölmeyi düşünmeye hazır olduğunuzu söyleyebilirim. Ben bu noktada neredeyse ikinci doğası gereği yapmak eğilimindedir.

Kullanım sizin Tema'nın içinde dosyalarını dahil functions.phpDosya

Tema dizinimin altında "include" adında bir alt dizin oluşturuyorum ve kodumu, o zaman bana mantıklı gelenler tarafından organize edilmiş dosyaları içerecek şekilde bölümlere ayırıyorum (bu, bir site geliştikçe kodları sürekli olarak yeniden gözden geçirip taşıdığım anlamına gelir.) herhangi bir gerçek kodu koymak functions.php; her şey dosya içerisine girer; sadece benim tercihim.

Sadece size bir örnek vermek için, burada WordPress Cevaplarında sorulara cevaplarımı test etmek için kullandığım test kurulumum var. Bir soruya her cevap verdiğimde tekrar ihtiyacım olması durumunda kodu saklı tutuyorum. Bu, canlı bir site için tam olarak ne yapacağınız değildir, ancak kodu bölme mekanizmasını gösterir:

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

Veya Eklenti Oluştur

Kodunuzu işleve göre gruplandırmaya başlamak ve kendi eklentilerinizi oluşturmak için başka bir seçenek. Benim için temanın functions.phpdosyasına kodlamaya başladım ve kodun kodunu çektiğimde kodumun çoğunu eklentilere taşıdım.

Ancak PHP Code Organisation'dan Önemli Performans Kazancı Yok

PHP dosyalarınızı yapılandırma Öte yandan (organize% 99 olduğu takdirde, düzen ve idame ve performansı hakkında% 1 üretmekle ilgili .jsve .css. HTTP üzerinden tarayıcı tarafından adlandırılan dosyalar tamamen farklı bir durumdur ve büyük performans sonuçları vardır) Ama nasıl düzenlemek Sunucudaki PHP kodunuz performans açısından pek önemli değil.

Ve Kod Organizasyonu Kişisel Tercihtir

Ve son fakat en az olmayan kod organizasyonu kişisel tercihtir. Bazı insanlar, kodları nasıl düzenlediğimden de nefret ediyor olabilirim. Hoşunuza giden bir şey bulun ve buna bağlı kalın, ancak daha fazla bilgi edindikçe ve daha rahat ederken stratejinizin zaman içinde gelişmesine izin verin.


Güzel cevap, fonksiyonlar dosyasını bölmem gereken noktaya geldim. Frunctions.php dosyasından eklentiye geçmek ne zaman faydalı olabilir? Cevabınız demiştiniz: kodları aldığımda kodumun çoğunu eklentilere taşıdım . Bunu tam olarak anlamadım, ete kasten neyi kastediyorsunuz?
Saif Bechan

5
"Veya eklenti oluştur" için +1. Daha spesifik olarak, " işlevsellik eklentileri "
Ian Dunn

3
Bağıl yolları kullanmak her türlü ayarda güvenilir olmayabilir, mutlak yol her zaman kullanılmalı
Mark Kaplun 22:16

2
@MarkKaplun - Kesinlikle haklısın . Bu cevabı yazdığımdan bu dersi zor yoldan öğrendim. Cevabımı güncelleyeceğim. Bunu gösterdiğin için teşekkürler.
MikeSchinkel

"Tanımlanmamış sabit DIR kullanımı - C: \ wamp \ www \ site \ wp-content \ themes \ mytheme \ functions.php 'de ' DIR 'olarak kabul edildi) - PHP v5.6.25 ve PHP v7.0.10 - Yapamam Bu DIR'i yorumunda doğru biçimde biçimlendirin (undercoreunderscoreDIRunderscoreunderscore), ancak dirname ile çalışır (undercoreunderscoreFILEunderscoreunderscore)
Marko

50

Geç cevap

Dosyalarınızı doğru şekilde nasıl dahil edersiniz:

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

Aynı eklentileri de çalışır.

Doğru yol veya URi nasıl alınır

Ayrıca aşağıdaki gibi dosya sistemi API işlevlerine de göz atın:

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

Sayısını azaltmak nasıl include/require

Bir dizindeki tüm dosyaları almanız gerekiyorsa ,

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

Bunun başarısızlıkları görmezden geldiğini (üretim kullanımı için iyi olabilir) / yüklenemez dosyaları göz önünde bulundurun.

Bu davranışı değiştirmek için geliştirme sırasında farklı bir config kullanmak isteyebilirsiniz:

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

Düzenleme: OOP / SPL yaklaşımı

Yeni döndüm ve bu cevabın gittikçe daha fazla puan aldığını gördüm, bugünlerde nasıl yaptığımı gösterebileceğimi düşündüm - bir PHP 5.3+ dünyasında. Aşağıdaki örnek, tüm dosyaları adlı bir tema alt klasöründen yükler src/. Burası, menüler, görüntüler vb. Gibi belirli görevleri yerine getiren kütüphanelerim var. Her bir dosya yüklenirken adı bile umursamalısınız. Bu dizinde başka alt klasörleriniz varsa, dikkate alınmazlar.

\FilesystemIteratorPHP 5.3+ olan supercedor üzerinde \DirectoryIterator. Her ikisi de PHP SPL'nin bir parçasıdır. PHP 5.2, yerleşik SPL uzantısını kapatmayı mümkün kılarken (tüm kurulumların% 1'inden azı bunu yaptı), SPL artık PHP çekirdeğinin bir parçası.

<?php

namespace Theme;

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

A: Hala PHP 5.2.x desteklenen Daha önce ise, şu çözümü kullanılan \FilterIteratoriçinde src/Filtersyalnızca dosyaları (ve klasörlerin işaretçileri dot değil) almak için dizin ve bir \DirectoryIteratorloop ve yükleme yapmak.

namespace Theme;

use Theme\Filters\IncludesFilter;

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

Bu \FilterIteratorkadar kolaydı:

<?php

namespace Theme\Filters;

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

PHP 5.2'nin şu anda ölü / EOL olmasının yanı sıra (ve ayrıca 5.3), oyunda daha fazla kod ve bir dosya daha olması, bu nedenle daha sonra gidip PHP 5.2.x'i desteklemenin bir nedeni yok.

Özetlenmiş

WPKrauts'da daha ayrıntılı bir makale bulunabilir .

EDIT Açıkça doğru bir yol, PSR-4 otomatik yüklemesi namespaceiçin hazırlanan her şeyi ad alanıyla tanımlanmış uygun dizine koyarak d kodunu kullanmaktır . Sonra sadece kullanmak Besteci ve bağımlılıkları yönetmek ve PHP autoloader otomatik yapalım (yani otomatik ithalatı sadece arayarak bir dosya ). Bu, PHP dünyasındaki fiili standart, gitmenin en kolay yolu ve WP Starter tarafından önceden otomatikleştirilmiş ve sadeleştirilmiş .composer.jsonuse \<namespace>\ClassName


5

Ayrılma açısından, kazan tabağımda, tema dizininde fonksiyonlar adında bir klasör aramak için özel bir fonksiyon kullanıyorum, eğer orada değilse yaratıyor. Sonra bu klasörde (varsa) bulduğu tüm .php dosyalarının bir dizisini yaratır ve bir include () çalıştırır; her birinde.

Bu şekilde, her yeni işlevsellik yazmam gerektiğinde, sadece işlevler klasörüne bir PHP dosyası ekliyorum ve siteye kodlama konusunda endişelenmenize gerek yok.

<?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 : Güzel numara. Kişisel olarak üretim kodu için kullanmazdım, çünkü siteyi başlattığımızda bir kez kolayca yapabileceğimizi her sayfa yüklemesi için yapar. Ayrıca, bir alt çizgi ile başlayan herhangi bir şeyi yüklememek gibi devam etmekte olan çalışmaları tema dizininde saklayabilmem için dosyaları atlamak için bir yol eklerdim. Aksi takdirde, güzel!
MikeSchinkel

fikri sevdim ama bunun her talep için gereksiz yere yüklenmesine yol açabileceğini kabul ediyorum. Yeni dosyalar eklendiğinde / yeni bir dosya eklendiğinde veya belirli bir zaman aralığında, nihai işlev.php dosyasının otomatik olarak bir tür güncelleme ile önbelleğe alınmasının basit bir yolu varsa, herhangi bir fikir?
NetConstructor.com

Güzel, ama esnekliklere yol açar, ayrıca bir saldırgan kodunu oraya bırakmayı başarırsa ne olur? Peki ya içerme sıralaması önemliyse?
Tom J Nowell

1
@MikeSchinkel Sadece çalışma dosyalarımı foo._php olarak adlandırıyorum, sonra çalıştırılmasını istediğimde _php'yi bırakıyorum.
Hafif Fuzz

@ NetConstructor: Bazı çözümlerle de ilgilenir.
kaiser

5

Bir klasör içindeki dosyalara bir işlev kullanmayı seviyorum. Bu yaklaşım, yeni dosyalar eklerken yeni özellikler eklemeyi kolaylaştırır. Ama ben her zaman sınıfta ya da isim alanında yazıyorum - ona fonksiyonların ismi, metodu vb.

Küçük bir örneğin altında; ut ayrıca, * .php sınıfı ile ilgili anlaşmayla birlikte kullanılır.

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;

}

Temalarda sıklıkla başka bir senaryo kullanıyorum. Externel dosyasının fonksiyonunu bir destek ID'sinde tanımlarım, örneğe bakın. Externel dosyasının getirisini kolayca devre dışı bırakacaksam, kullanışlıdır. WP çekirdek işlevini kullanıyorum require_if_theme_supports()ve yalnızca destek kimliği etkinse yüklenir. Takip eden örnekte, bu desteklenen kimliği dosyayı yüklemeden önceki satırda tanımladım.

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

Bu konunun deposunda daha fazlasını görebilirsiniz .


4

Bir ağ kurulumu üzerinden genel farklı dillerde yaklaşık 50 benzersiz özel sayfa türüne sahip bir siteyi yönetiyorum. TON eklentileri ile birlikte.

Bir noktada hepsini bölmek zorunda kaldık. 20-30k kod satırlı bir fonksiyon dosyası hiç de komik değil.

Kod tabanını daha iyi yönetmek için tüm kodları tamamlayıcıya karar vermeye karar verdik. Varsayılan wordpress tema yapısı küçük siteler için iyidir, ancak daha büyük siteler için iyidir.

Yeni fonksiyonlarımız.pp yalnızca siteyi başlatmak için neyin gerekli olduğunu, ancak belirli bir sayfaya ait hiçbir şeyi içermemektedir.

Şimdi kullandığımız tema düzeni MCV tasarım modeline benzer, ancak prosedürel kodlama tarzında.

Örneğin üye sayfamız:

page-member.php . Sayfayı başlatmaktan sorumludur. Doğru ajax fonksiyonlarını çağırmak veya benzeri. MCV tarzındaki Controller kısmına eşdeğer olabilir.

functions-member.php . Bu sayfa ile ilgili tüm fonksiyonları içerir. Bu aynı zamanda üyelerimiz için fonksiyonlara ihtiyaç duyan diğer web sayfalarına da dahil edilmiştir.

content-member.php . HTML için veri hazırlar MCV'deki modele eşit olabilir.

layout-member.php . HTML bölümü.

Bu değişiklikleri yaptıktan sonra, geliştirme süresi% 50 oranında kolayca azaldı ve şimdi ürün sahibi bize yeni görevler vermekte zorlanıyor. :)


7
Bunu daha faydalı hale getirmek için bu MVC modelinin gerçekte nasıl çalıştığını göstermeyi düşünebilirsiniz.
kaiser

Ayrıca, yaklaşımınızın bir örneğini görmek, hatta bazı detaylar / çeşitli durumlar için de acayip olur. Yaklaşım çok iç içe geliyor. Sunucu yükünü / performansını diğerlerinin kullandığı standart yöntemle karşılaştırdınız mı? mümkünse bir github örneği sağlayın.
NetConstructor.com

3

Alt temalardan functions.php dosyası:

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

0

Functions.php dosyasında gerekli bir dosyayı çağırmanın daha şık bir yolu olacaktır:

request_once locate_template ('/ inc / functions / shortcodes.php');


4
locate_template()üncü bir parametresi var…
fuxia

0

Ben kombine @kaiser 'ın ve @mikeschinkel ' ın cevapları.

Bir /includesklasördeki temamla ilgili tüm özelleştirmelerim var ve bu klasör içinde her şeyi alt klasörlere böldüm.

Sadece istediğim zaman /includes/adminve alt içeriğinin ne zaman ekleneceğinitrue === is_admin()

Bir klasör hariç ise iterator_check_traversal_callbackdönerek falsedaha sonra alt dizinleri iterated (veya geçirilen edilmeyecektir 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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.