问题描述

我对 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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。