it-swarm.cn

在WordPress主题的functions.php文件中组织代码?

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

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

将它们分成单独的文件或将它们组合在一起可能会加速WordPress网站或WordPress/PHP会自动跳过具有is_admin代码前缀的函数吗?

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

91
NetConstructor.com

如果你的主题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%关于性能,如果那样(组织.js.css文件由浏览器通过HTTP调用是完全不同的情况并且具有巨大的性能影响。)但是从性能角度看,如何在服务器上组织PHP代码几乎无关紧要。

代码组织是个人偏好

最后但并非最不重要的代码组织是个人偏好。有些人会讨厌我如何组织代码,就像我讨厌他们如何做代码一样。找到自己喜欢的东西并坚持下去,但随着时间的推移,让您的策略不断发展,因为您可以学到更多并且更加自如。

117
MikeSchinkel

迟到的答案

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

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的数量

如果你需要从目录中获取all文件,请使用

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上的PHP5.3 + supercedor。两者都是PHP SPL的一部分。虽然PHP 5.2可以关闭内置的SPL扩展(低于所有安装的1%),但SPL现在是PHP core的一部分。

<?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现在已经死了/ EOL(以及5.3),事实上它在游戏中有更多的代码和一个文件,所以没有理由去使用后者和支持PHP 5.2.x.

总结

可以找到更深入的文章 这里是WPKrauts

_ edit _ 显然正确的方法是使用namespaced代码,为 PSR-4 自动加载准备,方法是将所有内容放在已经通过命名空间定义的相应目录中。然后只需使用 Composercomposer.json来管理您的依赖项,并让它自动构建您的PHP自动加载器(通过调用use \<namespace>\ClassName自动导入文件)。这是PHP世界中事实上的标准,最简单的方法,甚至更多的预先自动化和简化为 WP St​​arter

50
kaiser

我喜欢使用函数来处理文件夹中的文件。这种方法可以在添加新文件时轻松添加新功能。但是我总是在类或命名空间中编写 - 让它更多地控制函数的命名空间,方法等。

下面是一个小例子; 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中定义了externel文件的功能,请参阅示例。如果我很容易停用externel文件的feture,那将非常有用。我使用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中看到更多这个

5
bueltge

在分解方面,在我的锅炉板中我使用自定义函数在主题目录中查找名为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);
    }
  }

}
5
Mild Fuzz

我通过网络安装管理一个网站,其中包含大约50种不同语言的独特自定义页面类型。随着TON的插件。

我们在某个时候被迫将它全部分开。具有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%,现在产品所有者无法为我们提供新任务。 :)

4
Patrik Grinsvall

来自子主题functions.php文件:

    require_once( get_stylesheet_directory() . '/inc/custom.php' );
3
Brad Dalton

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

require_once locate_template('/ inc/functions/shortcodes.php');

0
Imperative Ideas

我结合了 @kaiser 's和 @mikeschinkel 的答案。

我在/includes文件夹中对我的主题进行了所有自定义,并且在该文件夹中我将所有内容分解为子文件夹。

我只想要在true === is_admin()时包含/includes/admin及其子内容

如果通过返回falseiterator_check_traversal_callback中排除了一个文件夹,那么它的子目录将不会被迭代(或传递给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();
}
0
seangwright