对WordPress进行的自定义设置越多,我就应该开始考虑是否应该组织此文件或将其拆分。

更具体地说,如果我有一堆自定义功能仅适用于管理员区域以及仅适用于我的公共网站的其他区域,是否有任何理由可能将所有管理功能包含在自己的文件中或将它们组合在一起?

是否将它们拆分成单独的文件或将它们组合在一起可能会提高速度上一个WordPress网站,还是WordPress / PHP自动跳过具有is_admin代码前缀的函数?

处理大型函数文件(我的文件长1370行)的最佳方法是什么? >

#1 楼

如果您到达主题的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代码组织没有显着的性能提升
/>
另一方面,如果要构造PHP文件,则创建顺序和可维护性的比例为99%,而性能则为1%(如果浏览器通过HTTP来组织.js.css文件是完全不同的情况,并且结构庞大,但是,从性能的角度来看,如何在服务器上组织PHP代码几乎没有关系。

代码组织是个人首选项

最后但并非最不重要的代码组织是个人喜好。某些人会讨厌我如何组织代码,就像我可能会讨厌他们也是如此。找到喜欢的东西并坚持下去,但是随着您学到更多并逐渐适应它,您的策略就会随着时间的推移而发展。

评论


好的答案,我到了需要分割功能文件的位置。您认为什么时候从frunctions.php转到插件很方便。您在回答中说:到我充实代码的时候,我已经将大部分代码移到了插件中。我不完全理解,充血是什么意思。

– Saif Bechan
11年8月16日在7:02

为“或创建插件” +1。更具体地说,“功能插件”

–伊恩·邓恩
2012年4月18日在21:29

使用相对路径在所有设置中可能都不可靠,因此应始终使用绝对路径

–马克·卡普伦
16/09/22在15:27

@MarkKaplun-你是绝对正确的。自从我写了这个答案以来,我就很难学到这一课。我将更新我的答案。感谢您指出了这一点。

– MikeSchinkel
16/09/23在2:00



我收到“使用未定义的恒定DIR-在C:\ wamp \ www \ site \ wp-content \ themes \ mytheme \ functions.php中假定为'DIR'”-PHP v5.6.25和PHP v7.0.10-我无法正确格式化此DIR的注释格式(underscoreunderscoreDIRunderscoreunderscore),但可以与目录名一起使用(underscoreunderscoreFILEunderscoreunderscore)

– Marko
16-11-1在8:08



#2 楼

最新答案

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

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,但是我使用以下解决方案:\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。 />
总结

编辑显然,正确的方法是使用namespace d代码,该代码通过将所有内容放入已经通过名称空间定义的适当目录中而准备用于PSR-4自动加载。然后,只需使用Composer和composer.json来管理您的依赖项,并使其自动构建PHP自动加载器(通过调用use \<namespace>\ClassName即可自动导入文件)。这是PHP世界中的事实上的标准,是最简单的方法,并且由WP Starter进行了更加自动化和简化。

#3 楼

我喜欢对文件夹内的文件使用函数。这种方法使添加新文件时轻松添加新功能。但是我总是用类或名称空间来编写-给它更多有关函数,方法等名称空间的控制。

下面是一个小例子; ut还可以使用有关类* .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'
    );


您可以在此主题的回购中看到更多有关此内容的信息。 >

#4 楼

关于分解,在样板中,我使用一个自定义函数在主题目录中查找一个名为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);
    }
  }

}


评论


@mildfuzz:好招。我个人不会将其用于生产代码,因为它可以在每次页面加载时执行我们在启动网站时可以轻松完成的操作。另外,我将以某种方式添加文件以省略文件,例如不加载任何以下划线开头的内容,这样我仍可以将正在进行的作品存储在主题目录中。否则,很好!

– MikeSchinkel
2010-09-06 19:55

喜欢这个主意,但我同意这可能会导致每个请求不必要的加载。是否知道是否有一种简单的方法可以使最终生成的functions.php文件在添加新文件时或在特定时间间隔自动缓存并以某种类型的更新进行缓存?

– NetConstructor.com
2010-09-07 4:27

很好,但这会导致灵活性不足,如果攻击者设法将其代码放在那里,该怎么办?而且,如果包含的顺序很重要怎么办?

– Tom J Nowell♦
2010年9月7日于12:09

@MikeSchinkel我只是将工作文件命名为foo._php,然后在希望运行时删除_php。

–轻度绒毛
2010-09-10 15:25



@NetConstructor:也会对某些解决方案感兴趣。

– kaiser
2011年2月1日于7:35

#5 楼

我通过网络安装以服务器不同的语言管理大约50种独特的自定义页面类型的网站。以及大量的插件。

我们不得不在某个时候将其全部拆分。包含20-30k行代码的函数文件一点都不有趣。

我们决定完全重构所有代码,以便更好地管理代码库。默认的wordpress主题结构适用于小型网站,但不适用于大型网站。

我们的新功能。php仅包含启动网站所需的内容,但不包含属于特定页面的内容。

我们现在使用的主题布局类似于MCV设计模式,但是采用过程编码样式。

例如我们的会员页面:

page-member.php。负责初始化页面。调用正确的ajax函数或类似函数。可能与MCV样式中的Controller零件等价。

functions-member.php。包含与此页面相关的所有功能。这也包含在其他需要我们成员功能的其他页面中。

content-member.php。为HTML准备数据可能与MCV中的模型等效。

layout-member.php。 HTML部分。

在我们进行了这些更改之后,开发时间很容易减少了50%,现在产品负责人无法给我们新任务。 :)

评论


为了使此功能更有用,您可以考虑显示此MVC模式是如何工作的。

– kaiser
2012年10月5日上午10:57

我也很急于看到您的方法的示例,最好提供一些细节/各种情况。这个方法听起来很有趣。您是否将服务器负载/性能与其他人使用的标准方法进行了比较?尽可能提供一个github示例。

– NetConstructor.com
2012年10月10日上午11:29

#6 楼

来自子主题functions.php文件:

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


#7 楼

在functions.php中,一种调用所需文件的更优雅的方法是:

require_oncelocate_template('/ inc / functions / shortcodes.php');

评论


locate_template()具有第三个参数……

– fuxia♦
2012年10月4日7:38

#8 楼

我将@kaiser和@mikeschinkel的答案结合在一起。我只希望在/includes时包含/includes/admin及其子内容

如果通过返回true === is_admin()iterator_check_traversal_callback中排除了文件夹,则将不会迭代(或将其子目录传递给false

 iterator_check_traversal_callback