今回は、自作プラグインのクラス化に挑戦してみました
プラグインは、クラスを使用せずに記述することも出来ますが、変数や関数名の名前が衝突する可能性を低くして、安全で保守しやすいプラグインとするためには、クラスを使用して記述することがお勧めです
クラスというと難しく聞こえますが、プログラムをメンテしやすいようにするのが目的ですので、自分のスキルに合わせた範囲で対応しておくだけでOKです。
より高機能な使い方等への対応は、スキルアップに合わせて書き換えることで完成度が上げていこうと思っています
クラス化のポイント
クラスの実装は、Class定義の中に変数(プロパティ)や関数(メソッド)を記述していきます
プロパティやメソッドには、アクセス修飾子でアクセスを制限出来ます
アクセス修飾子には、public, private, protected の3種類があります
public | どこからでもアクセス可能 |
protected | クラス内部と、派生クラスの内部からのみアクセス可能 |
private | クラス内部からのみアクセス可能 |
クラスを利用するためには、インスタンスを作成します
newキーワードでインスタンスを作成し、用意した変数に格納します
インスタンス変数 = new クラス名();
newでインスタンスを作成せずに static 宣言で静的変数、静的メソッドとすれば、インスタンス変数を用いずにアクセスすることも出来ます
クラス名::メソッド名()
コンストラクタ
インスタンスが作成されると、定義したコンストラクタメンバ関数 __construct() が自動的に実行されます
インスタンスが破棄される時には、デストラクタが呼び出されると思うが、いくつかのプラグインのコードを見た限りでは、特に記述していないので、システム任せで問題ないと思われます(もう少し調べる必要があり)
継承
また、クラス化すると継承を用いたプログラミングが可能になるので、本格的なオブジェクト指向のプログラムを記述することも出来ます
もう少しPHPをマスターしたら、いづれは、継承を使用したプログラムにも挑戦してみようと思っています (^_^;)
これだけで、クラスです
最低限の作業でクラス化してみます
クラス定義
初めに下記のようなクラス定義を記述します
クラス名の1文字目は大文字にしてクラスとわかりやすくしておきます
class Post6widgetarea {
public function __construct()
{
}
}
$post6widgetarea = new Post6widgetarea();
クラスを定義するブロックエリアを設け、そのクラスのインスタンスを作成すれば、コンストラクタ部分に記述したコードが実行されます
次に、このクラスの中に今まで作成していたコードを移していきます
1.コンストラクタ
初期化処理を記述します
今まで作成したコードの関数以外のプログラム記述部分で add_action, add_filter や get_option によるオプションデータの読み込み処理等が該当します
2.変数(プロパティ)
グローバル定義されていた変数をクラス内 __construct() メソッドの上へ移動して、アクセス修飾子を private とします
3.関数(メソッド)
グローバル定義されていた関数をクラス内 __construct() メソッドの下へ移動して、アクセス修飾子を public とします
クラス内でしか使用しない関数なら private や protected としておいたほうがより良いです
4.変数(プロパティ)、関数(メソッド)へのアクセスの記述を修正
今までグローバルに定義されていた変数や関数をカプセル化してクラス内の定義に修正したので、変数(プロパティ)、関数(メソッド)は、インスタンスからアロー演算子(->)で呼び出すよう修正します。
自クラスの場合は、$this が自分自身のインスタンスを示しており、他クラスのオブジェクトなら、new演算子で作成したインスタンス変数を用いて、アロー演算子で呼び出すことが出来ます
従って、次のように記述していたグローバル変数、関数へのアクセス
$goption = get_option(‘post6widget_option’);
update_exclude_option($goption);
自クラス内のインスタンス変数を用いたアクセスに書き換えます
$this->goption = get_option(‘post6widget_option’);
$this->update_exclude_option($this->goption);
また、クラス内でフック等でコールバック関数を登録している場合は、配列を使用して記述するように修正する必要があります
コールバックは、配列の最初の要素がオブジェクトインスタンスとなるので、クラス内のメソッドを指定する場合は、$this を指定します
従って、次のように記述していたコールバック関数
add_action(‘admin_menu’, ‘my_menu’)
どのオブジェクトインスタンスの関数なのかを配列を用いて記述します
add_action(‘admin_menu’, array(&$this, ‘my_menu’));
以上で終了です
最低限のクラス化対応が出来ました
一部静的関数のクラス等も作り、こんな感じのクラスに書き換えました
今回は、機能的な追加は何もありませんが、参考までにここにソースコードを貼り付けます
<?php
/*
Plugin Name: Post 6 WidgetArea
Plugin URI: https://celtislab.net/wp_plugin_post6widgetarea
Description: 投稿記事前後等に6箇所のウイジェットエリアを追加します。1.ページエリアスタート部、2.投稿記事タイトル後、3.記事中のショートコード位置、4.投稿記事コンテンツ後、5.ページエリアエンド部、6.head 要素内
Author: enomoto celtislab
Version: 0.3.0
Author URI: https://celtislab.net/
*/
class CeltisLib {
//固定ページが除外指定ポストIDであるか判定する
public static function isnot_exclude_page($exclude_id)
{
$exclude = (count($exclude_id) > 0 ) && is_page($exclude_id);
return( ! $exclude );
}
//投稿記事がが除外カテゴリーまたは除外指定ポストIDであるか判定する
public static function isnot_exclude_single($exclude_cat, $exclude_id)
{
$exclude1 = in_category($exclude_cat);
$exclude2 = (count($exclude_id) > 0 ) && is_single($exclude_id);
return( (! $exclude1) && (! $exclude2) );
}
//dyndamic_sidebar の文字列化
public static function get_mydynamic_sidebar($index = 1)
{
ob_start();
dynamic_sidebar($index);
$sidebar_contents = ob_get_clean();
return $sidebar_contents;
}
}
class Post6widgetarea {
private $goption;
//コンストラクタ
public function __construct() {
//管理画面(設定メニュー)
if(is_admin()) {
add_action('admin_menu', array(&$this, 'my_menu')); //オプション表示
add_action('admin_init', array(&$this, 'my_register')); //オプション更新
}
//プラグイン削除時のフック
if ( function_exists('register_uninstall_hook') ) {
register_uninstall_hook(__FILE__, array(&$this, 'my_uninstall_hook'));
}
//ウィジェットエリア定義
add_action('widgets_init', array(&$this, 'my_register_wedgets'));
//ウィジェットをアクション/フィルターフックにセット
add_action('wp_head', array(&$this, 'my_wp_head'));
add_action('loop_start', array(&$this, 'my_loop_start'));
add_filter('the_content', array(&$this, 'my_content'));
add_action('loop_end', array(&$this, 'my_loop_end'));
//delete_option('post6widget_option'); //for debug
$this->goption = get_option('post6widget_option');
$this->update_exclude_option($this->goption); // update post
//未設定時には、デフォルトを仮設定しておく
if(!isset($this->goption['home']))
$this->goption['home'] = 0;
if(!isset($this->goption['page']))
$this->goption['page'] = 0;
if(!isset($this->goption['single']))
$this->goption['single'] = 1;
if(!isset($this->goption['archive']))
$this->goption['archive'] = 1;
if(!isset($this->goption['exclude_id']))
$this->goption["exclude_id"] = array();
if(!isset($this->goption['exclude_cat']))
$this->goption["exclude_cat"] = array();
if(!isset($this->goption['shortcode']))
$this->goption["shortcode"] = 'Post6ins';
add_shortcode($this->goption['shortcode'], array(&$this, 'my_content_shortcode'));
}
//設定オプション表示(メニュー)
public function my_menu()
{
add_options_page('Post 6 WidgetArea Settings', __('Post 6 WidgetArea'), 'manage_options', __FILE__, array(&$this, 'post6widget_area_options'));
}
//設定オプション更新
// 引数1:グループ名(settings_fields関数の引数で使用する)
// 引数2:オプション名(input要素などのname属性で使用する)
public function my_register()
{
register_setting('post6widget_optiongroup', 'post6widget_option');
}
//プラグイン削除時のオプションクリア
public function my_uninstall_hook()
{
delete_option('post6widget_option');
}
//ウイジェットエリア登録
public function my_register_wedgets()
{
register_sidebar( array(
'name' => __( 'Post6 wp_head widget' ),
'id' => 'post6_wp_head',
'description' => __( '<head> 要素内のウィジェットエリアです' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
register_sidebar( array(
'name' => __( 'Post6 start area widget' ),
'id' => 'post6_start_area',
'description' => __( 'ホーム/固定/投稿記事/アーカイブページエリア上部に表示されるウィジェットエリアです。' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
register_sidebar( array(
'name' => __( 'Post6 single start widget' ),
'id' => 'post6_single_start',
'description' => __( '投稿記事コンテンツ上部に表示されるウィジェットエリアです。' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
register_sidebar( array(
'name' => __( 'Post6 shortcode widget' ),
'id' => 'post6_shortcode',
'description' => __( '投稿記事コンテンツ内のシュートコード挿入部に表示されるウィジェットエリアです。' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
register_sidebar( array(
'name' => __( 'Post6 single end widget' ),
'id' => 'post6_single_end',
'description' => __( '投稿記事コンテンツ下部に表示されるウィジェットエリアです' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
register_sidebar( array(
'name' => __( 'Post6 end area widget' ),
'id' => 'post6_end_area',
'description' => __( 'ホーム/固定/投稿記事/アーカイブページエリア下部に表示されるウィジェットエリアです' ),
'before_title' => '<div class="widget-title">',
'after_title' => '</div>',
'before_widget' => '<div id="%1$s" class="widget-wrapper %2$s">',
'after_widget' => '</div>'
) );
}
public function my_wp_head()
{
dynamic_sidebar('Post6 wp_head widget');
remove_action('wp_head', array(&$this, 'my_wp_head'));
}
public function my_loop_start()
{
$option = $this->goption;
if( (is_home() && $option['home'])
|| (is_page() && CeltisLib::isnot_exclude_page($option["exclude_id"]) && $option['page'] )
|| (is_single() && CeltisLib::isnot_exclude_single($option["exclude_cat"], $option["exclude_id"]) && $option['single'])
|| (is_archive() && $option['archive']) ) {
dynamic_sidebar('Post6 start area widget');
}
remove_action('loop_start', array(&$this, 'my_loop_start'));
}
public function my_loop_end()
{
$option = $this->goption;
if( (is_home() && $option['home'])
|| (is_page() && CeltisLib::isnot_exclude_page($option["exclude_id"]) && $option['page'] )
|| (is_single() && CeltisLib::isnot_exclude_single($option["exclude_cat"], $option["exclude_id"]) && $option['single'])
|| (is_archive() && $option['archive']) ) {
dynamic_sidebar('Post6 end area widget');
}
remove_action('loop_end', array(&$this, 'my_loop_end'));
}
public function my_content_shortcode()
{
$option = $this->goption;
$ins_content = '';
if( is_single() && CeltisLib::isnot_exclude_single($option["exclude_cat"], $option["exclude_id"]) ){
$ins_content .= CeltisLib::get_mydynamic_sidebar('Post6 shortcode widget');
}
return $ins_content;
}
public function my_content($content)
{
$option = $this->goption;
if(! is_single()){
return $content;
}
else {
$new_content = "";
if(CeltisLib::isnot_exclude_single($option["exclude_cat"], $option["exclude_id"]) ){
$new_content .= CeltisLib::get_mydynamic_sidebar('Post6 single start widget');
$new_content .= $content;
$new_content .= CeltisLib::get_mydynamic_sidebar('Post6 single end widget');
}
else
$new_content .= $content;
return $new_content;
}
}
//パラメータ更新データの受信 exclude_id, exclude_cat は表示前に型変換しているので
//options.php に渡す前にデータを検証してから型を戻してセットし直す
//もっとスマートな書き方もあると思うがとりあえず動作することを優先する
//
public function update_exclude_option(&$option)
{
if (!empty($_POST['action'])) { // Update
if(isset($_POST['post6widget_option']['exclude_id'])){
$idlist = array();
$ids = explode(",", $_POST['post6widget_option']['exclude_id']);
for($x = 0; $x<count($ids); $x++) {
$id = (int)trim($ids[$x]);
if($id>0)
$idlist[] = $id;
}
$option["exclude_id"] = $idlist;
$_POST['post6widget_option']['exclude_id'] = $idlist;
}
if(isset($_POST['post_category'])){
$excat = array();
foreach((array) $_POST['post_category'] AS $val){
if(!empty($val) && is_numeric($val))
$excat[] = (int)$val;
}
$option["exclude_cat"] = $excat;
$_POST['post6widget_option']['exclude_cat'] = $excat;
}
}
}
//-------------------------------------------------------------------------
function post6widget_area_options() { ?>
<div class="wrap">
<div id="icon-options-general" class="icon32"><br /></div>
<h2>Post 6 WidgetArea</h2>
<p>Post 6 WidgetArea プラグインは、ブログ記事前後等に6箇所のウイジェットエリアを追加します</p>
<p>1.ページエリアスタート部、2.投稿記事タイトル後、3.記事中のショートコード位置、4.投稿記事コンテンツ後、5.ページエリアエンド部、6.head 要素内</p>
<p><br /></p>
<p>ウイジェットエリアは、管理画面の外観→ウイジェットへ追加されているので、定型文や広告、ソーシャルボタン設置等のテキストやHTMLコードを「テキストウイジェット」を用いて挿入して下さい</p>
<p>PHPコードを実行させる場合には、別途「Executable PHP widget」プラグインを導入して、「PHP Code ウイジェット」を使用して下さい</p>
<p>また、HTMLのhead要素内へのウイジェットエリアには、メタデータやCSS等のドキュメントについての情報を挿入することが出来ます</p>
<p><br /></p>
<!-- submit ボタンクリックで input 要素の name 属性データを post メソッドで option.php へ送信する -->
<form method="post" action="options.php">
<?php settings_fields('post6widget_optiongroup'); ?>
<?php
$option = $this->goption;
?>
<h3 style="margin-top: 10px;">記事エリアスタート/エンド部のウイジェット処理を有効とするページ種別</h3>
<p>ウイジェット処理を有効にするページ種別を指定して下さい</p>
<label><input type="checkbox" name="post6widget_option[home]" value="1" <?php checked( $option['home'], 1 ); ?>" /> ホームページ</label><br />
<label><input type="checkbox" name="post6widget_option[page]" value="1" <?php checked( $option['page'], 1 ); ?>" /> 固定ページ</label><br />
<label><input type="checkbox" name="post6widget_option[single]" value="1" <?php checked( $option['single'], 1 ); ?>" /> 個別投稿ページ</label><br />
<label><input type="checkbox" name="post6widget_option[archive]" value="1" <?php checked( $option['archive'], 1 ); ?>" /> アーカイブページ</label><br />
<p><br />※記事タイトル後、ショートコードによる挿入、記事コンテンツ後のウイジェット処理は、個別投稿ページのみが対象となります</p>
<p><br /></p>
<h3 style="margin-top: 10px;">ウイジェット処理を行わない除外条件設定 </h3>
<p>指定カテゴリーの投稿記事を除外</p>
<div style="border-color:#CEE1EF; border-style:solid; border-width:2px; height:10em; margin:5px 100px 5px 20px; overflow:auto; padding:0.5em 0.5em;">
<ul>
<?php wp_category_checklist(0,0,$option['exclude_cat'],FALSE,NULL,FALSE); ?>
</ul>
</div>
<p>指定 post ID の投稿記事、固定ページを除外 (カンマ区切りで指定して下さい)</p>
<p><label>Post ID:<input type="text" style="width:200px;" name="post6widget_option[exclude_id]" value="<?php echo implode(",", $option['exclude_id']); ?>" /></label></p>
<p><br /></p>
<h3 style="margin-top: 10px;">記事中に挿入するウイジェット処理を有効とするショートコードの定義</h3>
<p>定義されている文字列をショートコードとして処理します</p>
<p><label>ショートコード名称:<input type="text" name="post6widget_option[shortcode]" value="<?php echo $option['shortcode']; ?>" /></label></p>
<p class="submit">
<input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e('Save Changes') ?>" />
</p>
</form>
</div>
<?php }
}
//インスタンス作成
$post6widgetarea = new Post6widgetarea();
?>
とりあえずクラス化が出来ましたが、PHPのオブジェクト指向プログラミングについてはもっと調べてすこしづつ使っていこうと思います
次は、国際化対応について調べます (^^)