問題描述

我對 WordPress 的定製越多,我開始考慮如果我應該組織這個文件或分解它。

更具體地説,如果我有一堆僅適用於管理區域的自定義函數,而其他適用於我的公共網站的自定義函數有可能包括所有管理功能在自己的文件中或將它們組合在一起嗎?

將它們分割成單獨的文件或將它們分組在一起可能加速 WordPress 網站,或者 WordPress / PHP 自動跳過具有 is_admin 代碼前綴的函數?

處理大型函數文件 (我的長度為 1370 行) 的最佳方法是什麼。

最佳解決方案

如果你到達主題 functions.php 中的代碼開始壓倒你的時候,我一定會説你已經準備好考慮將它分解成多個文件。在這一點上,我傾向於通過第二性質來做這件事。

使用在您的主題的 functions.php 文件中包含文件

我在我的主題目錄下創建一個名為”includes” 的子目錄,並將我的代碼分割成包含文件,由當時有用的東西組織起來 (這意味着我隨着網站的發展而不斷重構和移動代碼。) 我也很少把任何 functions.php 中的實際代碼; 一切都在包含文件中; 只是我的喜好

只是給你一個例子,這裏是我的測試安裝,我用來測試我的問題在這裏的 WordPress 答案的答案。每次我回答一個問題,我會保留代碼,以防再次需要它。這不完全是為即時網站做的,但它顯示了分解代碼的機制:

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

或創建插件

另一個選項是通過功能開始分組你的代碼,並創建自己的插件。對於我來説,我開始在主題的 functions.php 文件中編碼,當我得到代碼的時候,我把大部分代碼都移植到了插件中。

然而,PHP 代碼組織沒有顯着的表現

另一方面,構建您的 PHP 文件是 99%關於創建順序和可維護性,1%關於性能,如果 (組織.js 和瀏覽器通過 HTTP 調用的.css 文件是完全不同的情況,並且具有巨大的性能影響) 但是如何在服務器上組織您的 PHP 代碼從性能的角度來看並不重要。

代碼組織是個人偏好

最後但並非最不重要的代碼組織是個人偏好。有些人會討厭如何組織代碼,就像我可能討厭它們如何做的那樣。找到你喜歡的東西,堅持下去,但讓你的策略隨着時間的推移發展,你會學到更多的東西,並且更加舒適。

次佳解決方案

遲到的答案

如何以正確的方式包含您的文件:

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

插件也是一樣的。

如何獲得正確的路徑或 URi

還要看一下文件系統 API 函數,如:

  •  home_url()
  •  plugin_dir_url()
  •  plugin_dir_path()
  •  admin_url()
  •  get_template_directory()
  •  get_template_directory_uri()
  •  get_stylesheet_directory()
  •  get_stylesheet_directory_uri()
  • 等等

如何減少 include/require 的數量

如果您需要從目錄中獲取所有文件

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

請記住,這將忽略故障 (可能對生產使用有利)/不可加載文件。

要更改此行為,您可能希望在開發期間使用其他配置:

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

編輯:OOP / SPL 方法

當我剛剛回來,看到這個答案越來越多的昇華,我想我可能會顯示我現在在做什麼 – 在 PHP 5.3+世界。以下示例從名為 src/的主題子文件夾加載所有文件。這是我有我的庫處理像菜單,圖像等某些任務的地方。甚至在每個文件都被加載時甚至不需要關心這個名稱。如果您在此目錄中有其他子文件夾,則會被忽略。

FilesystemIteratorDirectoryIterator 上的 PHP 5.3+超級服務器。兩者都是 PHP SPL 的一部分。雖然 PHP 5.2 使得可以關閉內置 SPL 擴展 (低於所有安裝的 1%),但 SPL 現在是 PHP 內核的一部分。

<?php

namespace Theme;

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

以前,我仍然支持 PHP 5.2.x,我使用以下解決方案:src/Filters 目錄中的 FilterIterator 僅檢索文件 (而不是文件夾的點指針) 和 DirectoryIterator 進行循環和加載。

namespace Theme;

use ThemeFiltersIncludesFilter;

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

FilterIterator 很簡單:

<?php

namespace ThemeFilters;

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

除了現在的 PHP 5.2 死亡/ EOL(還有 5.3),事實上游戲中還有更多的代碼和一個文件,所以沒有理由去支持 PHP 5.2.x.

總結

更深入的文章可以發現 here on WPKrauts

編輯顯然正確的方法是使用 namespace d 代碼,為 PSR-4 自動加載準備,將所有內容放在已經通過命名空間定義的相應目錄中。然後只需使用 Composercomposer.json 來管理您的依賴關係,並讓其自動加載程序 (它通過調用 use <namespace>ClassName 自動導入一個文件)auto-build 。這是 PHP 世界中的 de-facto 標準,最簡單的方法,甚至更多的 pre-automated,並由 WP Starter 簡化。

第三種解決方案

在我的鍋爐板上,我使用自定義函數在主題目錄中找到一個名為 functions 的文件夾,如果它不在那裏創建它。然後創建它在該文件夾 (如果有) 中找到的所有.php 文件的數組,並運行一個 include(); 他們中的每一個。

這樣,每次我需要編寫一些新的功能,我只需要在函數文件夾中添加一個 PHP 文件,而不用擔心將其編碼到該站點。

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

}

第四種方案

我喜歡使用一個文件夾中的文件的功能。這種方法使得添加新文件時可以輕鬆添加新功能。但是我總是在類或命名空間中寫下 – 更多地控制命名空間的功能,方法等。

下面一個小例子也可以使用關於類* .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;

}

在主題中,我經常使用其他的場景。我在支持 ID 中定義了 externel 文件的功能,參見示例。這是有用的,如果我將輕鬆停用外部文件的外觀。我使用 WP 核心功能 require_if_theme_supports()並且只能加載支持 ID 。在下面的示例中,我在加載文件之前,將該支持的 ID 修改為行。

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

您可以在 repo of this theme 中看到更多的信息。

第五種方案

我通過網絡安裝管理具有約 50 種獨特自定義頁面類型的網站,服務器不同的語言。隨着插件的 TON 。

在某些地方,我們被迫分裂。具有 20-30k 行代碼的函數文件根本就不好笑。

我們決定整合重構所有代碼,以便更好地管理代碼庫。默認的 wordpress 主題結構對小網站是好的,但不適用於較大的網站。

我們新的 functions.php 只包含啓動站點所需的內容,但是沒有屬於特定頁面的內容。

我們現在使用的主題佈局類似於 MCV 設計模式,但是在程序編碼風格中。

例如我們的會員頁面:

page-member.php 。負責初始化頁面。調用正確的 ajax 函數或類似。可以等同於 MCV 風格的控制器部分。

functions-member.php 。包含與此頁面相關的所有功能。這也包含在需要我們會員職能的其他服務器頁面中。

content-member.php 。準備 HTML 的數據可以與 MCV 中的模型相等。

layout-member.php 。 HTML 部分。

在我們做這些變化之後,開發時間容易下降了 50%,現在產品主人無法給我們新的任務。 🙂

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。