我对WordPress进行的自定义设置越多,我就开始考虑是否应该组织此文件或将其拆分。
更具体地说,如果我有一堆仅适用于管理区域的自定义功能,而其他仅适用于我的公共网站的自定义功能,是否有任何理由可能将所有管理功能包含在自己的文件中或将它们组合在一起?
将它们拆分成单独的文件或将它们分组在一起可能会加快WordPress网站的速度,还是WordPress / PHP是否会自动跳过具有is_admin代码前缀的功能?
处理大型函数文件(最小长度为1370行)的最佳方法是什么。
我对WordPress进行的自定义设置越多,我就开始考虑是否应该组织此文件或将其拆分。
更具体地说,如果我有一堆仅适用于管理区域的自定义功能,而其他仅适用于我的公共网站的自定义功能,是否有任何理由可能将所有管理功能包含在自己的文件中或将它们组合在一起?
将它们拆分成单独的文件或将它们分组在一起可能会加快WordPress网站的速度,还是WordPress / PHP是否会自动跳过具有is_admin代码前缀的功能?
处理大型函数文件(最小长度为1370行)的最佳方法是什么。
Answers:
如果您到了主题的代码functions.php
开始不知所措的地步,那么我肯定会说您已经准备好考虑将其拆分为多个文件。在这一点上,我倾向于几乎是天生的。
functions.php
文件我在主题目录下创建一个名为“ includes”的子目录,并将代码按当时对我有意义的方式组织到包含文件中(这意味着我会随着网站的发展不断地重构和移动代码。)我也很少放入任何真实代码functions.php
; 一切都在包含文件中;只是我的偏爱。
仅举一个例子,这是我的测试安装,用于测试WordPress Answers上的问题答案。每当我回答一个问题时,我都会保留代码以防万一我再次需要它。这并不完全是您对实时站点所做的工作,但是它显示了拆分代码的机制:
<?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文件的99%涉及创建顺序和可维护性,而1%涉及性能(如果是这样的话)(浏览器通过HTTP调用的组织.js
和.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' );
插件也一样。
还可以看一下文件系统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;
当我刚回来时,看到这个答案越来越多时,我想我可能会展示我现在是如何做的-在PHP 5.3+的世界中。以下示例从名为的主题子文件夹中加载所有文件src/
。在这里,我的库可以处理某些任务,例如菜单,图像等。加载每个文件时,您甚至不必担心名称。如果此目录中还有其他子文件夹,它们将被忽略。
的\FilesystemIterator
是PHP 5.3+ supercedor过\DirectoryIterator
。两者都是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,我用了以下解决方案:一个\FilterIterator
在src/Filters
目录中只检索文件(而不是点文件夹的指针)和\DirectoryIterator
做循环和负载。
namespace Theme;
use Theme\Filters\IncludesFilter;
$files = new IncludesFilter( new \DirectoryIterator( __DIR__.'/src' ) );
foreach ( $files as $file )
{
include_once $files->current()->getRealPath();
}
的\FilterIterator
就是这么简单:
<?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到现在已经死/停产(以及5.3)之外,还有一个事实,那就是游戏中包含了更多的代码和一个文件,因此没有理由去支持后者并支持PHP5.2.x。
在WPKrauts上可以找到更深入的文章。
编辑显然,正确的方法是使用namespace
d代码,将其放置在已经通过名称空间定义的适当目录中,从而为PSR-4自动加载做好了准备。然后,只需使用Composer和a composer.json
来管理您的依赖项,并使其自动构建您的PHP自动加载器(通过调用即可自动导入文件use \<namespace>\ClassName
)。这是PHP世界中的事实上的标准,这是最简单的方法,并且由WP Starter进行了更加自动化和简化。
关于分解,在样板中,我使用一个自定义函数在主题目录中查找一个名为functions的文件夹,如果没有,它将创建它。然后,创建一个在该文件夹(如果有)中找到的所有.php文件的数组,并运行一个include();。在每个。
这样,每次我需要编写一些新功能时,我只需将PHP文件添加到functions文件夹,而不必担心将其编码到站点中。
<?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);
}
}
}
我喜欢对文件夹内的文件使用函数。这种方法使添加新文件时轻松添加新功能。但是我总是用类或命名空间编写-给它更多关于函数,方法等的命名空间的控制权。
下面是一个小例子;ut也可以使用有关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;
}
在主题中,我经常使用其他方案。我在支持ID中定义了外部文件的功能,请参见示例。如果我将轻松停用外部文件的功能,那将很有用。我使用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'
);
我通过网络安装以服务器不同的语言管理大约50种独特的自定义页面类型的网站。以及大量的插件。
我们在某个时候被迫将其全部拆分。包含20-30k行代码的函数文件一点都不有趣。
我们决定完成对所有代码的重构,以便更好地管理代码库。默认的wordpress主题结构适用于小型网站,但不适用于大型网站。
我们的新functions.php仅包含启动站点所需的内容,但不包含属于特定页面的内容。
我们现在使用的主题布局类似于MCV设计模式,但是采用过程编码样式。
例如我们的会员页面:
page-member.php。负责初始化页面。调用正确的ajax函数或类似函数。可能与MCV样式的Controller零件等价。
functions-member.php。包含与此页面相关的所有功能。这也包含在其他需要我们成员功能的页面中。
content-member.php。为HTML准备数据可以等同于MCV中的模型。
layout-member.php。HTML部分。
我们进行了这些更改后,开发时间轻松减少了50%,现在产品负责人无法给我们新任务。:)
在functions.php中,调用所需文件的一种更优雅的方法是:
require_oncelocate_template('/ inc / functions / shortcodes.php');
locate_template()
有第三个参数…
我结合了@kaiser和@mikeschinkel的答案。
我将所有对主题的自定义设置在一个/includes
文件夹中,并且在该文件夹中,所有内容均分解为子文件夹。
我只想/includes/admin
在以下情况下包含其子内容true === is_admin()
如果iterator_check_traversal_callback
通过返回false
将一个文件夹排除在外,则该文件夹的子目录将不会被迭代(或传递给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();
}