WordPressメンテナンス@2018冬

このブログはWordPressを使い運営しているのですが、年1で小規模な機能の見直しを行っています。今年も2週間ほど使いいくつか修正を行いました。第三者が読んでどこまで参考になるかはわかりませんが、今回いじったところを残しておきます。

今更な部分もありますし、利用しているテーマにより必ずしもベストプラクティスというわけにもいかないと思います。あしからず。

パフォーマンスチェック

今回のメンテナンスで新しい機能を導入するか・切るか迷ったときに役立ったプラグインが以下の3つ。ローカル開発環境に入れておくと便利です

  • Debug Bar:管理バーにクエリ、キャッシュなどを追加
  • Debug Bar Slow Action:遅いアクションとフィルタを一覧表示
  • Query Monitor:データベースクエリ数や条件の監視

ツールバーが必要なので、プロフィールから「サイトを見るときにツールバーを表示する」をチェックする。バーが表示されないときは、bodyタグの終了時前にwp_footer()があるかを確認します。また、function.phpに以下も必要です。

add_action( 'wp_footer', 'wp_admin_bar_render', 1000 );

wp-config.phpを編集します。

// falseからtrueにする
define('WP_DEBUG', true);
// 追加
define('SAVEQUERIES', true);

これでパフォーマンスチェックが行えます。尚、ローカル開発だとトップページにドメイン名でアクセスするとバーが表示されないことがあるので、そのときはIPアドレスでアクセスします。

リファクタリング

同じことを行うにしても、利用する関数で処理時間は変わります。例えば、the_excerpt()は抜粋を表示しますが、これはget_the_excerpt()echoしても同様のことが行えます。この場合、後者の方が一般的に速い。

実際に測定してみました。括弧の中が変更後の測定値です。

  • UNIQUE ACTIONS:389(388)
  • TOTAL ACTIONS:3214(3184)
  • ACTIONS EXECUTION TIME:61.88ms(47.25)
  • SLOWEST ACTION: 8.01ms(6.49)

こういったチェックもいくつか行ってみました。

SSL

独自SSLを導入した。Chrome 682018年7月リリース予定からHTTP通信での通信は「保護されていない」旨の警告が表示されるようになるとのことで、テストを兼ねて利用してみた。

このブログはよくある格安レンタルサーバーのスターサーバー上で動いているが、昨年の11月頃から独自SSLが利用可能となった。反映に小一時間を要したが、管理パネルからの操作だけで非常に簡単だった。

.htaccessにリダイレクトの設定を追加する。

RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

反映後は「WordPressの設定 > 一般」の「WordPress」と「サイトアドレス」をhttps版に変更する。また、カスタムメニュー内で使っているURLもhttps版に書き換える必要もあります。

PHP7

PHP5.5.30を利用していましたが、7.1.2にバージョンUPした。

スピードアップを謳っていますが、変更したからといって体感的な速度の違いはまったくわかりませんでした。

Sitemap

Sitemapのコンテンツ見直しを行いました。サイトマップはGoogle XML Sitemapsを使用しています。下記を管理対象としました。

  • ホームページ
  • 投稿(個別記事)
  • 固定ページ
  • カテゴリーページ
  • カスタム投稿(Work)
  • 最終更新時刻

robox.txtの末尾にサイトマップのファイル位置を追加。

Sitemap: https://the2g.com/sitemap.xml

また、サイトマップの見え方の確認を行うため、今更ながらGoogle Search Consoleに登録した。

データベースの最適化

1年ぶりに最適化を行った。phpmyadminからオーバーヘッドのあるテーブルを選択して最適化を選択するだけです。

不要なメタタグの削除

定期的におせっかいなメタタグが追加されますが、追加されたものの中で使用しないものを無効化した。

// @wp_oembed_add_discovery_links: Embed
// @wp_oembed_add_host_js: Embed
// @rest_output_link_header: Embed(http response)
// @wp_resouce_hints: DNS-prefetch
// @rest_output_link_wp_head: wp-json
jsonremove_action('wp_head', 'wp_oembed_add_discovery_links');
remove_action('wp_head', 'wp_oembed_add_host_js');
remove_action('template_redirect', 'rest_output_link_header', 11 );
remove_action('wp_head', 'wp_resource_hints', 2 );
remove_action('wp_head', 'rest_output_link_wp_head');

meta description

これまで、このブログはいくつかの理由からmetaタグのdescriptionを一切挿入していませんでした。今回metaタグの見直しを行い、descriptionタグの書き出しを設定しました。

description自体を設定するのはとても簡単です。投稿でカスタムフィールドをONにし、新規追加で「名前:description」、「値:記事の説明」を打ち込みます

これをfunction.phpで書き出すだけです。

function add_custom_meta_des() {
	#Homepage
	if( is_home() || is_front_page() ) {
		$meta_des = "Enter your homepage meta description here"; #Edit here
		echo '<meta name="description" content="' . $meta_des . '" />';
	}
	#Single Page
	if( is_single() ){
		$des = get_post_meta( get_the_id(), 'description', true);
		if( ! empty( $des )  ){
			$meta_des = esc_html($des);
			echo '<meta name="description" content="' . $meta_des . '" />';
		}
	}
}
add_action( 'wp_head', 'add_custom_meta_des', 4 );

記事ではdescriptionカスタムフィールドを記述していればそれを挿入し、なければ何も挿入しない。ホームではブログの説明を吐き出します。

これで問題ないのですが、自分の場合descriptionの内容を別の処理からも読み出す必要があったので、再利用可能な形に書き換えます。まず書き出すヘッダーに出力を定義します。

<?php echo '<meta name="description" content="' . get_meta_desc() . '" />' . "\n" ?>

function.phpに以下を追加します。

// meta descriptionの本文を返す
// @return string $meta_desc meta description text
function get_meta_desc () {
    $meta_desc = '';

    if ( is_singular()) { // 投稿、カスタム投稿、固定ページ、添付ファイルのシングルページ
        $desc = get_post_meta( get_the_id(), 'description', true );
        if ( !empty($desc) ) {
            $meta_desc = esc_html($desc);
        } else {
            global $post;
            $body = get_post($post->id)->post_content;
            $body = trim(strip_tags($body)); // remove space and html tag
            $meta_desc = mb_strstr($body, '。', true, 'UTF-8');
            if (!$meta_desc) { $meta_desc = get_bloginfo("description"); }
        }
    } else { 
        $meta_desc = get_bloginfo("description");
    }

    return $meta_desc;
}

個別記事でdescripitonカスタムフィールドが定義されていればそれを利用し、なければ本文から最初の「。」が出るところまでの内容を抽出し、これをdescripitonの内容として利用します。

個別記事以外は、ブログの説明をそのまま利用します。同じdescripitonが重複するページが多少できますが、必ず何かのメタタグを抽出する関数としたいので、止むを得ずといった感じです。頑張れば分岐で対応できますが、そこまでする必要はないかなと感じたため。

セキュリティ

このブログは最低限のプラグインしか入れていません。その中の1つがall-one-security-firewallというセキュリティツールです。オプションを1つ1つONにしていくと、素人でもセキュアなサイトが維持できます。2年半ほどお世話になりましたが、負荷が気になっていました。

大規模なサイトの場合はこういったツールを入れておくのがいいのですが、所詮個人ブログというのもあり、このたび一時的に外すことにしました。また、必要なものは自分で.htaccessに記述も可能なので、プラグインはなくてもいいかなというのもありました。

ただし、ログインページだけは保護したい(デフォルトのURL以外にしたい)ので、代わりにWPS Hide Loginというものを入れました。プラグインに頼らなくても、wp-login.phpを書き換えれば可能ですが、今後のWordPressのバージョンアップでどうなるかわからないというのもあり、プラグインに頼ることにしました。

prism

シンタックスハイライトのスクリプトで、これもブログ開設時から使用しています。2015年の段階だと、先頭行の改行のバグというか仕様があり、CSSでそれを取り除いていたのですが、現在はNomalize Whitespaceというプラグインでそれが解決できるようになっていました。導入は他の言語追加と同じで上記を選択するだけです。

加えてJavaScript(prism)を非同期で読み込むようにしました。

// wp_enqueue_scriptsでロードするスクリプトをフィルタする関数
function regal_tag( $tag, $handle, $src ) {
    if ( $handle !== 'prism-js') {
        return $tag;
    }
    return "<script src='${src}' defer></script>";
}
add_filter( 'script_loader_tag', 'regal_tag', 10, 3);

script_loader_tagは、wp_enqueue_scriptsのスクリプトタグのフィルタ処理です。上記では、エンキューするスクリプトがprism-jsならばdeferで書き出しを行っています。当初asyncで記述していたのですが、prismの場合はdeferの方がいいのかなと思い上記に至っています。

キャッチ

.htaccessにcontrol-cacheを追加しました。

<filesMatch ".(jpg|jpeg|png|gif|ico)$">
Header set Cache-Control "max-age=31536000, public"
</filesMatch>

<filesMatch ".(css|js)$">
Header set Cache-Control "max-age=2628000, public"
</filesMatch>

リビジョンの停止

リビジョンはDBを圧迫する上に使用したこともないので、停止しました。


/* wp-config.php */

/** Stop revison(追加) */
define('AUTOSAVE_INTERVAL', 300);
define('WP_POST_REVISIONS', false);

/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');

コメントエリアの停止

使用頻度も低いので思い切って削除しました。空いたスペースには、ツイート用のリンクを配置しています。

<aside id="sns">
    <a href="https://twitter.com/intent/tweet?" target="_blank" 
    onclick="window.open('https://twitter.com/intent/tweet?text=' + 
    encodeURIComponent(document.title) + '%20 ' + 
    encodeURIComponent(document.URL)); return false;">Tweet This</a>
</aside>

コメント周りは独自で多くのフィルタをかけて整形していたため、排除するとコードがかなり減りました。また、コメントの問い合わせがなくなるのでクエリ数も減ります。

Twitter Card

ツイートリンクを置いたので、今更ながらTwitter Cardの設定を行った。

function get_meta_ogp () {
    $ogp_title = '';
    $ogp_desc = '';
    $ogp_url = '';
    $ogp_img = '';
    $ogp_type = '';
    $insert_meta = '';

    if ( is_singular() ) {
        $ogp_title = get_the_title();
        $ogp_desc = get_meta_desc();
        $ogp_url = get_permalink();
    } elseif ( is_front_page() || is_home() ) {
        $ogp_title = get_bloginfo('name');
        $ogp_desc = get_bloginfo('description');
        $ogp_url = get_site_url();
    } else {
        $ogp_title = get_the_title();
        $ogp_desc = get_bloginfo('description');
        global $wp;
        $ogp_url = home_url(add_query_arg(array(),$wp->request));
    }
    $ogp_img = get_article_thumbnail();
    $ogp_type = ( is_singular() ) ? 'article' : 'website';

    $insert_meta .= '<meta property="og:title" content="' .esc_attr($ogp_title) . '" />' . "\n";
    $insert_meta .= '<meta property="og:description" content="' .esc_attr($ogp_desc) . '" />' . "\n";
    $insert_meta .= '<meta property="og:type" content="' . $ogp_type . '" />' . "\n";
    $insert_meta .= '<meta property="og:url" content="' . $ogp_url . '" />' . "\n";
    $insert_meta .= '<meta property="og:image" content="' . esc_url($ogp_img) . '" />' . "\n";
    $insert_meta .= '<meta property="og:site_name" content="' . esc_attr(get_bloginfo('name')) . '" />' . "\n";
    $insert_meta .= '<meta property="og:local" content="ja_JP" />'. "\n";
    $insert_meta .= '<meta name="twitter:site" content="@__maeda" />' . "\n";
    $insert_meta .= '<meta name="twitter:card" content="summary_large_image" />';

    return $insert_meta;
}

ここでも先に定義したget_meta_description()を呼び出しています。また、サムネイルとなる画像が必要なので、それを取得する関数を別途定義しました。

サムネイルの設定

個人的にアイキャッチ画像というものは好きではないのですが、Twitter Card及び後述するjsonldで必要なため設定しました。記事にアイキャッチ画像がセットされていればそのURLを返し、セットされていなければデフォルトで用意したURLを返すという関数を定義します。

尚、実際にサムネイル画像を記事内でセットしても、埋め込むコードを記述していなければ出力されないので、自分のようにアイキャッチ画像は苦手という人でも問題ありません。

function.phpに以下を追加します。add_theme_support()は、initafter_setup_themeアクション内に追加する必要があります。

function custom_init() {
	// (略)
    // 投稿サムネイルのサポート(投稿のみ)
    add_theme_support('post-thumbnails', array('post'));
}
add_action('init', 'custom_init');

function.phpにアイキャッチ画像を取得する関数を定義します。

// アイキャッチ画像が設定されていればそれを返し、なければ固定画像を返す
// @return ogp_img string img url
function get_article_thumbnail () {
    if ( is_singular() && has_post_thumbnail() ) {
        $ps_thumb = wp_get_attachment_image_src( get_post_thumbnail_id(), 'full');
        $ogp_img = $ps_thumb[0];
    } else {
        $ogp_img = "https://the2g.com/wp-content/uploads/other/article.png";
    }
    return $ogp_img;
}

Twitter Cardの画像サイズは公式では、2:1に対応している旨が記載されています。ただ、リサーチすると海外では1200*675pxで設定しているところが多かったのがわかりました。

また、後述のjsonldでも記事内の画像をセットする箇所がありますが、Googleの公式ブログでは1440*810pxが使われています。どちらも16:9のため、このどちらかで画像を用意するのがモダンかなと思います。このブログでは、1つの画像で済むという理由で1440*810pxの画像を両者で使用することにしました。

Schema

3年ほどマイクロデータを利用していましたが、Googleの構造化データテストツールを使うと、エラーが出る状態になっていました。自分がこのテーマを作成したときは、ArticleやWebsiteに必須項目というものはなかったはずですが、現在は前述のツールを使うと特定のpropで決められたフォーマットから外れるものはエラーや警告が出るようになっています。

これはSchemeのエラーというよりは、Googleが求める形かのバリデーションチェックかと思います。scheme.orgには必須項目やitempropにより定義できない値があるといった記述は見られません。例えばGoogleではArticleのPublisherにPersonを指定するとエラーになります。

とは言え、一般的にGoogleの求める形が標準みたいものなので、それに従うことにしました。併せて今までは全てマイクロデータで構造化を記述していましたが、パンくずリストやグローバルナビゲーション以外はGoogleの推奨するjsonldを使用してみました。

function add_header () {
	//(略)
    echo add_jsonld() ."\n";
}
add_action( 'wp_head', 'add_header');

// jsonldを返す関数
// @return script tag string
function add_jsonld() {
    $schemaAry = array(); 
    $homeSchemaAry = array (
        "@context" => "http://schema.org",
        "@type" => "WebSite",
        "encoding" => "UTF-8",
        "inLanguage" => "ja-JP", 
        "keywords" => "php, javascript, linux, apache, react, mysql, node.js",
        "name" => get_bloginfo('name'),
        "url" => get_site_url(),
        "description" => get_bloginfo('description'),
        "publisher" => array (
            "@type" => "Person",
            "name" => "maeda",
            "image" => array (
                "@type" => "imageObject",
                "url" => "https://the2g.com/wp-content/uploads/other/profile.png",
                "width" => 400,
                "height" => 400
            ),
            "sameAs" => array (
                "https://twitter.com/__maeda",
                "https://addons.mozilla.org/ja/firefox/user/mae_da"
            )
        )
    );

    $schemaAry[] = $homeSchemaAry;

    if ( is_single() ) {
        $schemaAry[] = get_article_schema();
    }

    return '<script type="application/ld+json">' . json_encode($schemaAry) . '</script>';
}

// articleのマイクロデータを返す関数
// @return $schemaAry array
function get_article_schema () {
    $schemaAry = array (
        "@context" => "http://schema.org",
        "@type" => "Article",
        "datePublished" => get_the_date(),
        "headline" => get_the_title(),
        "description" => get_meta_desc(), 
        "name" => get_the_title(),
        "mainEntityOfPage" => get_permalink(),
        "dateModified" => get_the_modified_date(),
        "author" => array (
            "@type" => "Person",
            "name" => "maeda"
        ),
        "image" => array (
            "@type" => "imageObject",
            "url" => get_article_thumbnail()
        ),
        "publisher" => array (
            "@type" => "Organization",
            "name" => "maeda",
            "logo" => array (
                "@type" => "imageObject",
                "url" => "https://the2g.com/wp-content/uploads/other/profile.png"
             )
        )
    );

    return $schemaAry;
}

全ページでWebsiteを吐き出し、個別記事ではArticleを追加しています。今回は設定していませんが、全体を配列で囲ってあるのでカスタムフィールドに入力したjsonldをget_post_meta();から取得して末尾追加みたいなことも可能です。

タイトルの書き出し

タイトルの書き出しをwp_title()からadd_theme_support('titile_tag')に変更した。


function wp1_setup() {
    // titleタグとRSS Feedの書き出し
    add_theme_support( 'title-tag' );
    add_theme_support( 'automatic-feed-links' );
    // 以下略
}
add_action( 'after_setup_theme', 'wp1_setup' )

// コメントのフィードは不要なので削除
add_filter( 'feed_links_show_comments_feed', '__return_false' );

// homeのdescriptionは独自のものを書き出す
function wp_document_title_parts ($title) {
    if ( is_home() || is_front_page() ) {
        $title['tagline'] = "Web開発雑事ブログ";
    }
    return $title;
}
add_filter( 'document_title_parts', 'wp_document_title_parts', 10, 1 );

function add_header () {
    // metaタグの書き出し(略)
}
add_action( 'wp_head', 'add_header');

add_theme_support()は便利ですが、書き出しの順番に柔軟性がないのがややネック。metaタグをうまく組み込むアクションが定義されればいいのですけども。

フォント

游ゴシックはやはり読みづらい。綺麗なんだけども、読み物には向いていないと思うのですね。大手のニュースサイトなどを見ても游ゴシックを使用したところはほとんどなく、利用しているのは大体がWebデザイナーが運営してるようなサイトぐらいですし。

そんなわけで、今更ながらこのブログも游ゴシックの優先順位を落とすことにしました。現状、本文で利用しているのは以下です。

font-family: "segoe ui", meiryo, "yu gothic", "hiragino kaku gothic pron", sans-serif;

これはMSNの設定をそのまま利用しています。

アクセシビリティ

一昔前はviewportでuser-scalable=noneを目にしましたが、昨今はアクセシビリティが下がるという理由で推奨されていないようです。GoogleのLighthouseでもアクセシビリティで警告が出ていたので、取り除くことにしました。

<meta name="viewport" content="width=device-width, initial-scale=1">

また、Lighthouseで配色のアクセシビリティ警告が出ていたのでいくつか修正しました。ただ、完全に取り払うのは難しい。このブログのテーマカラーは納戸色(#089)と白(#FFF)なのですが、背景色と文字色で利用するとコントラスト比は4.215。18pt以下では、WCAG 2に準拠しません。これを修正するにはテーマカラーを触る必要が出るため、お手上げです。

他にもサードライブラリのCSS…例えばコードシンタックスのコントラスト比もLighthouseは警告を出してきますし、全てを取り払うにはデザインを1から見直す必要が出てきます。サイト立ち上げ時の配色設計は思っていたよりも重要だと理解しました。

おわりに

年々Webが複雑化しているのでこんな個人ブログでさえ、内部ではそれなりのコード量になってきています。使わないと思う部分は切ってしまって、メンテを楽にするのがいいのかもしれません。修正前は500行を超えていたfunction.phpも、いくつか関数化したり機能を取り除くことで最終的に300行まで抑えることができました。これは今回いくつか機能を追加した後の数値なので、中々削れた方だと思います。最近は多少速度が下がるにしても、ブラックボックスを極力減らし、メンテしやすい状態を残しておく方が大事かなと思うようになっています。