WordPressテーマのfunctions.phpファイルでコードを整理しますか?


92

WordPressをさらにカスタマイズすればするほど、このファイルを整理するか分割するかについて考えるようになります。

より具体的には、管理領域にのみ適用されるカスタム関数と、公開Webサイトにのみ適用されるその他のカスタム関数がある場合、すべての管理関数を独自のファイルに含めるか、グループ化する理由はありますか?

それらを別々のファイルに分割するか、グループ化すると、WordPress Webサイトが高速化されるか、WordPress / PHPはis_adminコードプレフィックスを持つ関数を自動的にスキップしますか?

大きな関数ファイルを処理する最良の方法は何ですか(私の場合は1370行の長さです)。

回答:


120

あなたのテーマのコードがあなた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コードは、パフォーマンスの観点からはほとんど問題ではありません。

そして、コード編成は個人的な好みです

最後になりましたが、コードの構成は個人的な好みです。一部の人々は、私がコードを整理する方法を嫌うかもしれません。好きなものを見つけてそれを使い続けますが、時間をかけて戦略を進化させて、より多くのことを学び、より快適になります。


いい答えです。関数ファイルを分割する必要があるこの時点にたどり着きました。frunctions.phpからプラグインに移行すると便利だと思いますか。あなたはあなたの答えで言った:私はコードを肉付けするまでに、ほとんどのコードをプラグインに移動しました。私はそれを完全に理解していません、あなたは肉体で何を意味しますか。
サイフBechan

5
「またはプラグインを作成する」ための+1。具体的には、「機能のプラグイン
イアン・ダン

3
設定のすべての種類の信頼性がないかもしれない相対パスを使用して、絶対パスは常に代わりに使用されなければならない
マーク・Kaplun

2
@MarkKaplun-あなたは絶対に正しいです。この答えを書いてから、私はそのレッスンを難しい方法で学びました。回答を更新します。これを指摘してくれてありがとう。
MikeSchinkel

「未定義の定数DIRの使用-C:\ wamp \ www \ site \ wp-content \ themes \ mytheme \ functions.phpで' DIR 'を想定」-PHP v5.6.25およびPHP v7.0.10-取得できませんフォーマット適切にコメントで、このDIR(underscoreunderscoreDIRunderscoreunderscore)が、それはのdirname(underscoreunderscoreFILEunderscoreunderscore)で動作します
マルコ

50

遅い答え

ファイルを正しい方法で含める方法:

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/。ここには、メニューや画像などの特定のタスクを処理するライブラリがあります。すべてのファイルがロードされるので、名前を気にする必要さえありません。このディレクトリに他のサブフォルダがある場合、それらは無視されます。

\FilesystemIteratorPHP 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ディレクトリ内のA (フォルダーのドットポインターではなく)のみを取得\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である)ことを除けば、ゲーム内にはコードとファイルがもう1つあるという事実があるため、PHP 5.2.xをサポートする理由はありません。

要約する

さらに詳細な記事は、WPKrautsにあります。

編集明らかに正しい方法は、ネームスペースを介してすでに定義されている適切なディレクトリにすべてを置くことによりnamespacePSR-4オートロード用に準備されたdコードを使用することです。次に、Composerとa composer.jsonを使用して依存関係を管理し、PHPオートローダー(を呼び出すだけでファイルを自動的にインポートします)を自動ビルドしますuse \<namespace>\ClassName。これは、PHPの世界の事実上の標準であり、最も簡単な方法であり、WP Starterによって事前に自動化および簡素化されています。


5

それを分割するという点では、私のボイラープレートで、カスタム関数を使用してテーマディレクトリ内の関数と呼ばれるフォルダーを探します。次に、そのフォルダー(存在する場合)で検出されたすべての.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
@mildfuzz:すてきなトリック。私は個人的に本番コードには使用しません。なぜなら、サイトを立ち上げるときに簡単にできることをすべてのページ読み込みに対して行うからです。また、アンダースコアで始まるものをロードしないように、進行中の作品をテーマディレクトリに保存できるように、何らかの方法でファイルを省略します。そうでなければ、いいね!
マイクシンケル

このアイデアは大好きですが、これは各リクエストの不必要なロードにつながる可能性があることに同意します。新しいファイルが追加された場合/新しいファイルが追加された場合、または特定の時間間隔で、最終的なfunctions.phpファイルが何らかの更新で自動的にキャッシュされる簡単な方法があるとしたらどうでしょうか?
-NetConstructor.com

いいですが、柔軟性に欠けます。また、攻撃者がコードをそこにドロップした場合はどうなりますか?また、インクルードの順序が重要な場合はどうなりますか?
トムJノウェル

1
@MikeSchinkel作業ファイルfoo._phpを呼び出し、実行したいときに_phpをドロップします。
マイルドファズ

@NetConstructor:いくつかのソリューションにも興味があります。
カイザー

5

フォルダー内のファイルに関数を使用するのが好きです。このアプローチにより、新しいファイルを追加するときに新しい機能を簡単に追加できます。しかし、私は常にクラスで、または名前空間を使用して記述します-関数、メソッドなどの名前空間についてより多くの制御を与えます。

小さな例を以下に示します。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でexternelファイルの機能を定義します。例を参照してください。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'
    );

このテーマのレポでこれをもっと見ることができます。


4

ネットワークインストールを介して、さまざまな言語で作成された約50の一意のカスタムページタイプを持つサイトを管理しています。プラグインのトンとともに。

私たちはどこかでそれをすべて分割することを余儀なくされました。20〜30k行のコードを含む関数ファイルはまったく面白くありません。

コードベースをより適切に管理するために、すべてのコードを完全にリファクタリングすることにしました。デフォルトのワードプレスのテーマ構造は小さなサイトには適していますが、大きなサイトには適していません。

新しいfunctions.phpには、サイトを開始するために必要なものだけが含まれていますが、特定のページに属するものは何も含まれていません。

現在使用しているテーマレイアウトは、MCVデザインパターンに似ていますが、手続き型のコーディングスタイルです。

たとえば、メンバーページ:

ページmember.php。ページの初期化を担当します。正しいajax関数などを呼び出します。MCVスタイルのコントローラーパーツと同等である可能性があります。

機能-member.php。このページに関連するすべての機能が含まれています。これは、メンバーの機能を必要とするその他のページにも含まれています。

コンテンツmember.php。HTMLのデータを準備します。MCVのモデルと同等です。

レイアウトmember.php。HTMLパーツ。

これらの変更を行った後、開発時間は簡単に50%短縮され、製品所有者は新しいタスクを提供するのに苦労しています。:)


7
これをさらに役立つものにするために、このMVCパターンが実際にどのように機能するかを示すことを検討してください。
カイザー

また、できればいくつかの詳細/さまざまな状況で、あなたのアプローチの例を見るのも面白いでしょう。このアプローチは非常に興味深いものです。サーバーの負荷/パフォーマンスを他の人が使用する標準的な方法論と比較しましたか?可能な場合は、githubの例を提供してください。
NetConstructor.com


0

functions.phpで、必要なファイルを呼び出すよりエレガントな方法は次のとおりです。

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


4
locate_template()3番目のパラメーターがあります...
fuxia

0

@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();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.