問題描述
我對 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/
的主題子資料夾載入所有檔案。這是我有我的庫處理像選單,影像等某些任務的地方。甚至在每個檔案都被載入時甚至不需要關心這個名稱。如果您在此目錄中有其他子資料夾,則會被忽略。
FilesystemIterator
是 DirectoryIterator
上的 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 自動載入準備,將所有內容放在已經透過名稱空間定義的相應目錄中。然後只需使用 Composer 和 composer.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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。