カスタマイズ 2023.09.29 2023.10.23
WordPressテーマのfunctions.phpを見やすくする
目次
はじめに
様々な理由で1つのソースコードに関数を多数記述していることがあるかと思います。私もその一人であり、 functions.php に関数をまとめていました。本記事は、そのようなソースコードを読み返したときに理解しやすくする方法を書きました。
前提条件
最初に、手直しする前の functions.php のソースコードを確認し、そのあと「分割手順」にて、ソースコードを複数のファイルに分割し、コードの記述量を減らしていきます。
最後に分割後の functions.php を確認し、リファクタリングの参考にしてもらえればと思います。
前提として、私は以下の条件で作業をしました。
使用したアプリケーション
ソースコードの編集にVSCode、ローカル環境構築にDocker、コマンド入力にコマンドプロンプトを使用しました。
- VSCode (PHP Intelephense インストール済み)
- Docker Desktop (Windows 版)
- コマンドプロンプト (Bash 使用)
使用したコンテナイメージのバージョン
Dockerコンテナのイメージについて、それぞれ以下のバージョンで構成しました。
- WordPress: 6.2
- PHP: 8.2
- Mysql: 5.7
作業用ディレクトリの構成図
1 2 3 4 5 |
txt /path/to/geek (Bind mount: /var/www/html/wp-content/themes/geek) ├── functions.php ├── index.php └── style.css |
本目的はfunctions.phpのリファクタリングのため、本記事では、開発したサイトの機能等はほぼ完成の状態から進めることを想定しています。
整理する前のソースコード
テーマ作成を進めていく中で、私は functions.php に以下の機能を追加しました。
- style.css を WordPress に取得させるための関数[^1]
- カスタム投稿タイプを定義するための配列と関数[^2]
- カスタムフィールドを定義するための配列と関数[^3]
- カスタムフィールドの値を保存するための関数[^4]
次に functions.php のソースコードになります。ここでは関数処理の説明は割愛します。
整理前のfunctions.phpのソースコード(クリックで開きます)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
<?php // Themeのスタイル読み込み function enqueueScripts() { wp_enqueue_style('stylesheet', get_stylesheet_uri()); } //実行処理 add_action( 'wp_enqueue_scripts', enqueueScripts(...) ); //カスタム投稿タイプの作成 function createPostType() { $POST_TYPES = [ 'news' => 'NEWS', 'media' => 'Media', 'work' => 'WORK', 'office' => 'Office', ]; foreach ($POST_TYPES as $key => $post_type) { register_post_type( $key, getPostTypeDefinition($post_type) ); register_taxonomy( ($key . '_cat'), $key, getTaxonomyDefinitionWhetherTagOrCat( 'カテゴリー[' . $post_type . ']', true ) ); register_taxonomy( ($key . '_tag'), $key, getTaxonomyDefinitionWhetherTagOrCat( 'タグ[' . $post_type . ']', false ) ); } } //カスタム投稿の定義 function getPostTypeDefinition($label) { return (array( 'label' => $label, 'public' => true, 'has_archive' => true, 'menu_position' => 5, 'show_in_rest' => true, 'supports' => [ 'title', 'editor', 'thumbnail', 'revisions', 'excerpt', ] )); } //カスタム投稿タイプのタクソノミー定義 function getTaxonomyDefinitionWhetherTagOrCat($label, $is_hierarchical) { return (array( 'label' => $label, 'hierarchical' => $is_hierarchical, 'public' => true, 'show_in_rest' => true, 'update_count_callback' => $is_hierarchical == true ? '' : '_update_post_term_count', )); } //実行処理 add_action( 'init', createPostType(...) ); //投稿作成画面で、カスタムフィールドのフォーム欄を出力する function formCustomFields($post, $box) { wp_nonce_field('meta_box_action', 'my_meta_box_nonce'); ?> <div id="meta_info"> <p>ページのメタ情報を入力してください。</p> <?php foreach ($box['args'] as $key => $label) : $cf_title_key = trim($key); $cf_title_label = trim($label); $cf_title_value = esc_html(get_post_meta( $post->ID, $cf_title_key, true )); ?> <div> <label for="<?php echo $cf_title_key ?>"> <?php echo $cf_title_label; ?> </label><br> <input type="text" name="<?php echo $cf_title_key ?>" value="<?php echo $cf_title_value ?>" size=70> </div> <?php endforeach; ?> </div> <?php } //カスタムフィールドの定義 function createCustomFields() { $CUSTOM_FIELDS = [ 'addressNumber' => '住所番号', 'address' => '住所欄', 'tel' => '電話番号', 'fax' => 'ファックス番号', 'email' => 'メールアドレス', ]; add_meta_box( 'custom_field_office', '内部情報入力欄', formCustomFields(...), 'office', 'normal', 'high', $CUSTOM_FIELDS, ); } //実行処理 add_action( 'admin_menu', createCustomFields(...) ); //カスタムフィールドの値を保存 function saveCustomFields($post_id) { foreach ($_POST as $key => $value) { if (!empty($_POST[$key])) { update_post_meta($post_id, $key, $value); } else { delete_post_meta($post_id, $key); } }; } //実行処理 add_action( 'save_post', saveCustomFields(...) ); |
整理前のソースコードの大きな問題点は以下になります。
- ソースコード全体が長文化していること
- 機能ごとに分けられて記述しているため、関数の定義とアクションフックの実行が繰り返し記述されていること
これらの問題を以下のように修正することで解消していきます。
- 機能ごとに外部ファイル化を行う
- 定義箇所と実行箇所を整理する
- functions.php内では値を扱わない
- PHPのコーディング規約に則った記述を行う
- オートロード機能を活用する
実際に、分割の手順を確認していきましょう。
分割手順
1. ファイルごとに関数をひとつにする
上記ソースコードには、7つの関数(function)があるので、それぞれの関数ごとに外部ファイル化していきます。
- 新規作成したファイル名には関数名と同一にします。
- そして、作成したファイルを新規作成したincフォルダにまとめます。
- functions.phpで外部ファイルを取り込めるようにします。
この工程で、ソースコード全体の記述量は大きく変わりませんが、 functions.phpの記述量を大幅に減らし、見やすくなります。
以下、ディレクトリ構成と、ソースコードの抜粋です。
ディレクトリ構成
1 2 3 4 5 6 7 8 9 10 11 12 13 |
txt /path/to/geek ├── inc │ ├── enqueueScripts.php │ ├── createPostType.php │ ├── getPostTypeDefinition.php │ ├── getTaxonomyDefinitionWhetherTagOrCat.php │ ├── createCustomFields.php │ ├── formCustomFields.php │ └── saveCustomFields.php ├── functions.php ├── index.php └── style.css |
functions.php のソースコード
functions.php では WordPressフック関数を実行したいため、必要なファイルを読み込ませています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php require_once __DIR__ . "/inc/enqueueScripts.php" require_once __DIR__ . "/inc/createPostType.php" //以下のファイルは createPostType.php より取得。 /* require_once __DIR__ . "/inc/getPostTypeDefinition.php" require_once __DIR__ . "/inc/getTaxonomyDefinitionWhetherTagOrCat.php" */ require_once __DIR__ . "/inc/createCustomFields.php" //以下のファイルは createCustomFields.php より取得。 /* require_once __DIR__ . "/inc/formCustomFields.php" */ require_once __DIR__ . "/inc/saveCustomFields.php" add_action('wp_enqueue_scripts',enqueueScripts(...)); add_action('init',createPostType(...)); add_action('admin_menu',createCustomFields(...)); add_action('save_post',saveCustomFields(...)); |
inc/createPostType.php のソースコード
createPostType.php では、カスタム投稿タイプを定義しています。その定義に必要なファイルを createPostType.php から取得しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php require_once __DIR__ . "/getPostTypeDefinition.php" require_once __DIR__ . "/getTaxonomyDefinitionWhetherTagOrCat.php" $POST_TYPES = [ 'news' => 'NEWS', 'media' => 'Media', 'work' => 'WORK', 'office' => 'Office', ]; function createPostType() { foreach ($POST_TYPES as $key => $post_type) { register_post_type( $key, getPostTypeDefinition($post_type) //arrayを返す。 ); register_taxonomy( ($key . '_cat'), $key, getTaxonomyDefinitionWhetherTagOrCat( 'カテゴリー[' . $post_type . ']', true )//arrayを返す。 ); register_taxonomy( ($key . '_tag'), $key, getTaxonomyDefinitionWhetherTagOrCat( 'タグ[' . $post_type . ']', false )//arrayを返す。 ); } } |
2. 関数をクラスへ
次に関数をクラスへ書き換えていきます。 書き換えは以下の手順で実施しました。
- functions.phpで取り込まれる外部ファイルでクラスを新たに定義
- 定義したクラスに関数やデータをまとめる
また、その外部ファイルで別の外部ファイルを取り込んでいた場合、その外部ファイル内の関数や変数をクラスで再定義(不要になったソースコードは破棄) - ファイル名をキャメルケースからパスカルケースに変更してクラス名と同一に修正
functions.phpでの外部ファイルを取り込むパスを修正 - add_actionを実行する関数をそれぞれのクラス内で定義
(アクション関数も機能の一部分でありクラスにまとめて扱いたいため。また、クラスの外から想定外に関数を呼び出されることを減らすべき) - アクセス修飾子を追加(アクションフックを呼び出す関数には「public」、それ以外の変数や関数には「private」)
- functions.phpでインスタンスを生成し、アクションフックを呼び出して実行
以上の方法で、ディレクトリ構成は以下のように書き換えました。
ディレクトリ構成
1 2 3 4 5 6 7 8 9 |
/path/to/geek ├── inc │ ├── CreatePostType.php │ ├── EnqueueScripts.php │ ├── CreateCustomFields.php │ └── SaveCustomFields.php ├── functions.php ├── index.php └── style.css |
inc/CreatePostType.php は以下のコードになりました。
inc/CreatePostType.php のソースコード(クリックで開きます)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<?php class CreatePostType { private const POST_TYPES = [ 'news' => 'NEWS', 'media' => 'Media', 'work' => 'WORK', 'office' => 'Office', ]; //カスタム投稿タイプ作成 private function createPostType() { foreach (self::POST_TYPES as $key => $post_type) { register_post_type( $key, self::getPostTypeDefinition($post_type) ); register_taxonomy( ($key . '_cat'), $key, self::getTaxonomyDefinitionWhetherTagOrCat( 'カテゴリー[' . $post_type . ']', true ) ); register_taxonomy( ($key . '_tag'), $key, self::getTaxonomyDefinitionWhetherTagOrCat( 'タグ[' . $post_type . ']', false ) ); } } private function getPostTypeDefinition($label) { return (array( 'label' => $label, 'public' => true, 'has_archive' => true, 'menu_position' => 5, 'show_in_rest' => true, 'supports' => [ 'title', 'editor', 'thumbnail', 'revisions', 'excerpt', ] )); } private function getTaxonomyDefinitionWhetherTagOrCat($label, $is_hierarchical) { return (array( 'label' => $label, 'hierarchical' => $is_hierarchical, 'public' => true, 'show_in_rest' => true, 'update_count_callback' => $is_hierarchical == true ? '' : '_update_post_term_count', )); } public function addAction() { add_action( 'init', $this->createPostType(...) ); } } |
functions.php では、次のように CreatePostType クラスを呼び出して、実行します。
functions.php より抜粋
1 2 3 4 5 |
<?php require_once __DIR__ . "/inc/CreatePostType.php" (new CreatePostType())->addAction(); |
3. namespace を設定する
次に、それぞれの外部ファイルにnamespace5を命名して、カプセル化をします。
カプセル化をすることで関数名やクラス名での衝突を防ぎます。
また後述するオートロードに役立ちます。
namespace は declare キーワードを例外として、ファイルの先頭に記述しなければなりません。
私は namespace を階層構造で宣言しました。
createPostType.php に namespace を宣言
1 2 3 4 5 6 7 8 |
<?php namespace Geek\Inc; //追加した文 require_once __DIR__ . "/inc/getPostTypeDefinition.php" require_once __DIR__ . "/inc/getTaxonomyDefinitionWhetherTagOrCat.php" $POST_TYPES = [/* データの値に変更はありません。 */]; function createPostType(){/* 処理内容に変更はありません。 */} |
use キーワードを使って functions.php からクラスを呼び出せるようにします。
functions.php で use を記述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php /* ここから追加した */ use Geek\Inc\{ EnqueueScripts, CreatePostType, CreateCustomFields, SaveCustomFields, }; /* ここまで追加した */ require_once __DIR__ . "/inc/enqueueScripts.php" require_once __DIR__ . "/inc/createPostType.php" require_once __DIR__ . "/inc/createCustomFields.php" require_once __DIR__ . "/inc/saveCustomFields.php" /* 以降、実行処理 */ |
4. PHP コーディング規約に則って整形
現段階でファイルを複数作成しコーディングをしました。ここでいったん、コード整形に話を変えます。
まず、コーディングをするうえで「コーディング規約」というルールがあります。このルールは、どのプログラマーが書いたとしても、誰が見ても分かるシンプルなコードになるように定められています。
本記事では、代表的な PHP のコーディング規約の 1 つである「PSR-12」に沿ってコード整形をします。[^6]
ただし、目視と手作業でルールに沿っているかチェックするのは大変なので、自動で規約チェックと整形をするスクリプトを使います。
そのために、Composer から PHP_CodeSniffer をインストールします。[^7]
Docker Desktop を起動し、WordPressコンテナのTerminalでコマンドを打ちます。
以下の手順でコーディング規約のチェックとコード整形をします。
- コンテナの/var/www/html ディレクトリで Composer コマンドを使えるようにする。
- Composer コマンドで PHP_CodeSniffer をインストールする。
- 作成された ./vendor/bin 直下に phpcs, phpcbf があるか確認する。
- phpcs 実行でコーディング規約のチェック、phpcbf 実行でコード整形をする。
本記事では、Composer の導入(手順 1.)を割愛し、手順 2.より説明します。[^8]
次のコマンドをすることで、カレントディレクトリに composer.json, composer.lock, vendor が作成されます。
1 |
composer require "squizlabs/php_codesniffer" |
「-h」で phpcs、phpcbfのコマンドヘルプを開けるか確認してください。
また「-i」でインストールされたコーディング規約に「psr12」があるか確認してください。
1 2 3 4 |
./vendor/bin/phpcs -h ./vendor/bin/phpcbf -h ./vendor/bin/phpcs -i ./vendor/bin/phpcbf -i |
functions.php と inc 直下の PHP ファイルに規約チェックをするときは、以下のようにコマンドを打ちます。
1 2 |
./vendor/bin/phpcs --standard=psr12 ./wp-content/themes/geek/functions.php ./vendor/bin/phpcs --standard=psr12 ./wp-content/themes/geek/inc/*.php |
コードを自動整形する場合は「./vendor/bin/phpcbf」に変えてください。
規約チェックでエラーが無くなったら次の工程に進みます。
5. require による読込回数を減らす
PHP には「PSR-4」というオートロードと呼ばれる外部ファイルの自動読み込みに関する仕様があります。[^9]
この「PSR-4」は「namespace の修飾部分をベースディレクトリを基準にした相対パスとして、クラス名をファイル名として対応する」仕様になっています。[^10]
現段階の functions.php はクラスの呼び出し回数分、require と use をする必要があり、必要なクラスが増えていくたびに読みにくくなってしまいます。 そこで、オートロード機能で use キーワードから外部ファイルを読み込むようにして、読みにくさを回避しましょう。
まず、composer.json を破棄し、初期化で改めて新規作成します。
composer の初期設定の途中で PSR-4 の設定を聞かれるので、オートロードさせるパスを入力します。
1 2 3 |
composer init Add PSR-4 autoload mapping? Maps namespace "Root\html" to the entered relative path. [src/, n to skip]: wp-content/themes/geek/inc/ |
新規作成された composer.json を確認するとオートロードの項目があるので、次のように編集し、保存をします。
composer.json
1 2 3 |
composer dump-autoload Generating autoload files Generated autoload files |
fuctions.phpでvendor直下にあるautoload.php を読み込ませるように追記します。
functions.phpに追記したら、以下のコマンドでファイルを読み込ませます。
1 2 3 |
composer dump-autoload Generating autoload files Generated autoload files |
今後のコーディングでクラスの処理などを変更したら、先ほどのコマンドで再読み込みをしてください。
これでrequireを書かずに外部ファイルを読み込むことができました。
分割後の functions.php
以上の工程で、functions.phpは以下のようになりました。
当初のコードと比べると、functions.phpの中身がかなりスッキリしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php use Geek\Inc\{ EnqueueScripts, CreatePostType, CreateCustomFields, SaveCustomFields, }; require_once $_SERVER['DOCUMENT_ROOT']. ('/vendor/autoload.php'); // テーマのスクリプトを読み込む (new EnqueueScripts())->addAction(); //カスタム投稿タイプ作成 (new CreatePostType())->addAction(); //カスタムフィールドを作成 (new CreateCustomFields())->addAction(); //カスタムフィールドの値を保存 (new SaveCustomFields())->addAction(); |
まとめ
functions.phpの記述量を減らしたり、可読性を上げたりするため、以下を行いました。
- 機能ごとにファイルを分割し、クラスにまとめる。
- コーディングをしたら、コーディング規約に沿っているか修正する。
- オートロード機能を活用する。
本記事がリファクタリングの参考になれば幸いです。
参考記事 URL
1. wp_enqueue_style() | Function | WordPress Developer Resources
2. register_post_type() | Function | WordPress Developer Resources
3. add_meta_box() | Function | WordPress Developer Resources
4. update_post_meta() | Function | WordPress Developer Resources
5. PHP: 名前空間の概要 – Manual
6. PSR-12: Extended Coding Style – PHP-FIG
7. squizlabs/PHP_CodeSniffer: PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.
8. Composer
9. PSR-4: Autoloader – PHP-FIG
10. The composer.json schema – Composer
WordPressサイト制作・カスタマイズなら「wp.make」にお任せ!
WordPressでのサイト制作やリニューアルを検討する時、以下のようなお悩みはありませんか?
- WordPressに詳しい制作会社に依頼したいが、どこがいいかわからない…
- セキュリティ対策をしっかりしたいが、社内にノウハウがないのでプロに任せたい…
- WordPressに最適なサーバーの選定や構築から依頼したい…
- SEO対策や高速化も考慮したサイト構築を行なってほしい…
- 制作後の保守・運用についてもサポートしてほしい…
- 今のサイトを簡単に運用できるようにしてほしい…
「wp.make」は、WordPressのプロフェッショナル集団によるWordPressサイト制作・カスタマイズサービスです。
サイトの制作だけでなく、WordPressに最適なサーバーの選定や構築といったインフラ面の支援から、SEO対策や表示スピードの高速化、高度なセキュリティ対策や制作後の保守・運用サポートまで、WordPressに関わることならあらゆるお悩みを解消いたします。
既存のお客さまからも
「コミュニケーションが取りやすく、クオリティが高い」
「WordPressのプロとして信頼感がある」
と大変ご好評をいただいています。
WordPressサイトの制作・カスタマイズをご検討されているなら、ぜひ以下からお気軽にご相談ください。
WordPress開発・カスタマイズなら 『wp.make』
全案件WordPressのみ!
株式会社e2eの『wp.make』はWordPress専門のWeb制作サービスです。
WordPress案件だけを扱っているから、技術・ノウハウ・対応力が圧倒的!
【WordPressサイト制作でよくあるお悩み】
・運用シーンが想定されておらず、更新しづらかった…
・打ち合わせで専門用語が多くてわかりづらい…
・制作後の保守には対応してくれなかった…
こんな事態になる前に、ぜひ一度、ご相談ください!
WordPressサイトを作るなら、一番WordPressに詳しいところへ!