Page No.1

WordPress6.0 にバージョンアップした時に、お世話になっていた Google XML Sitemap プラグインがエラーを出すようになってしまった。ほどなくそちらもバージョンアップがなされてエラーはでなくなったが、Notice はまだ残ってしまっていた。Notice やら Deprecated は表示されないようにして、気にしないようにする、というのも一つの方法なのかもしれないけれど、いかんせん、自分でプログラムを書く側にいると、どうにも知らぬ顔ができないのが悲しいかな。

こうなるともはや自作するしかないという決断にいたり、プラグインの使用をやめてしまうこととなる。使うのをやめてしまってからもう一度アップデートがあったので、たぶん、すでに Notice も出ないように修正されたのではないだろうか。

自サイトにおいてはパーマリンクの設定をデフォルトのまま変えていない。その場合、WordPressXML-Sitemaprobots.txt の面倒をみてはくれず、アクセスしても知らぬ顔をきめこむばかり。まぁ、パーマリンクの設定もしない(必要ない)サイトには、そういったものは不必要だろう、ということなのだろう。

どこかで見たのだと思うが、たしか WordPress が標準で備えている XML-Sitemap 生成機能は、リストしたくないページの設定などはできなかったのではあるまいか。もし、そうだとすると、なんにしても自分的には、結局使えない機能だったのだと思う。

改めて考えてみると、サイトの構成においては、独自の方法でページを分割して複数ページにしているものもあり、taxonomy による独自の方法での画像一覧ページなども多量にあるので、そのあたりはサイト固有の形式であり、やはり汎用の sitemap システムではカバーできない部分があまりにも多いように思える。今まで、あまり気にもせず、Google XML Sitemap プラグインに任せっきりであったけれど、よくよく考えてみると半分もリストされていなかったのではないだろうか(しっかり、確認したこともなかったりして・・・)。まぁ、検索結果などということにも、あまり執着していなかったというのがほんとのところなんだけれど。

XML-Sitemap

XML-Sitemap の書式自体は実にシンプルなものである。
詳しくはこちらをご覧あれ -> 「 Sitemaps.org – サイトマップの XML 形式
上記サイトから拝借してきた基本的なパターンは下のとおり。

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">
   <url>
      <loc>https://www.example.com/</loc>
      <lastmod>2005-01-01</lastmod>
      <changefreq>monthly</changefreq>
      <priority>0.8</priority>
   </url>
</urlset> 
XML
CopyExpand

XML-Sitemap を設置するサイトにおいて、検索エンジンにインデックスさせたいページを、<url> タグでリストすればいいという、シンプルなものである。なお、googlebot においては、<changefreq><priority> タグに関しては無視されるとのこと。
書式自体はシンプルなものであるけれど、いざ、WordPress においての投稿ページを書き出すというと、なかなか、そんなに単純なことでもなさそうである。

まずは固定ページ。そして通常の投稿ページがあり、それに伴うカテゴリーや投稿タグのページもあるし、別のアーカイブもあるかも。そのうえ、それぞれにページネーションされてページが複数になっているということも。まだまだある・・・、カスタム投稿があれば、それもそれぞれに個別ページやらアーカイブページなどがあるわけで。と、なかなかに面倒なことが待ち受けているようである。

必要な情報としては、各投稿のリンク先と post_modified( 更新日時)。固定ページと全ての投稿タイプの投稿の情報をデータベースから取得する。あとは、 category や post_tag、各カスタムタクソノミーのリンク先と、ページが複数になっている可能性があるのでそれぞれに登録されている投稿数が必要ということか。
<changefreq> タグにおいては適当な値を設定するとして、<priority> においてはそれぞれのページにおいての値を設定する必要があると思われるので、その値は WordPressoption 値としてデータベースに保存できるようにし、管理画面のメニューにページを追加してそのページにおいて sitemap の作成を含め操作できるようにした。

まずは、管理画面のメニューに項目を追加して、そのページを表示できるようにする処理。これはお約束のきまった書式でただ登録するだけのこと。詳しくはここを -> 「wordpress管理画面にテーマのオプション設定ページを表示」。もう、ちょっと古くなっているので、新しい情報があるのかも。でも、今でもこのままで有効だった。といいつつ、他でいろいろと現役で使いまわしているのだから当然のことである。

そして、オプション値の取得に関する関数。普通に取得するだけなら、get_option を普通に使うだけだけれど、作成中にオプション値を増やしたり減らしたりするものだからこういう関数にしてる。なんとかとにかく少しでも負荷を少なくしたいということで、いろいろやっていて、今のところこれになってる。

<?php
    // 管理画面の設定メニューにページを追加
    add_action( 'admin_menu', 'addmenu_generate_sitemap' );

    function addmenu_generate_sitemap() {
        add_options_page( 'Generate-XML-Sitemap', 'Generate-XML-Sitemap', 'administrator', 'strix_xml_sitemap', 'generate_xml_sitemap' );
    }

    // 引数:$optionname->オプション名、$dft_options->オプションデフォルト設定値
    function current_option_check( $optionname, $dft_options ) {

		if ( ! $dft_options or ! is_array( $dft_options ) ) {
            return false;
        }

		if ( $optionname ) {
			$crt_options = get_option( $optionname );//現在のオプション設定値を取得

			if ( false === $crt_options ) {// 初使用の時などオプションが設定されていない時

				update_option( $optionname, $dft_options );// デフォルトでオプション設定
				return $dft_options;

			} else {// すでにデータベースに保存されたオプション値が存在する場合。

                // デフォルトとセーブデータで共通するキーの要素だけを key=>value のセットで取り出す
				$deff = array_intersect_key( $dft_options, $crt_options );
				$countary = array( count ( $dft_options ) , count ( $crt_options ) , count ( $deff ) );

                // 3つの数が同じなら、array_unique で一つになる
                // 3つの数が等しくないということはオプションに変更があったということ
				if ( 1 !== count ( array_unique( $countary ) ) ) {

					foreach ( $crt_options as $key => $val ) {
                        // デフォルトオプションにそのキーが存在する要素だけ保存されている値で上書き。
						if ( isset ( $dft_options[ $key ] ) ) {
							$dft_options[ $key ] = $val;
						}
					}
					update_option( $optionname, $dft_options );
					return $dft_options;
	
				} else {
					return $crt_options;
				}
			}
		} else {
			return false;
		}
	}
?>
PHP
CopyExpand

つづいて、中核となる、管理画面に表示させるべく add_options_page() 関数に登録した、実際に表示させるページの中身となる generate_xml_sitemap() 関数。

<?php
    function generate_xml_sitemap() {

        // ABSPATH はWordPress のインストールされているフォルダのパス。WordPress により設定されている定数
        $sitemap = 'sitemap.xml';
        $sitemappath = ABSPATH . $sitemap;

        // wp nonce の値を取得
        $ajax_nonce = wp_create_nonce( __FILE__ );

        $options_name = 'sitemap_options';

       // オプションデフォルト値設定。デフォルト値+説明文
        $sitemap_dft_options = array(
            'pages_ignore' => [ '', 'post_type -> page でsitemap にリストしない無視する post->ID。"," 区切り。<br>default:164284,118534,78381,70152,39776,27498,48' ],
            'pages_priority' => [ '', 'post_type -> page でリストするもののpriority設定。ID:値 で "," 区切り。<br>default:31002:0.7,26870:0.7,24452:0.8,16227:0.9<br>指定なしは 0.5' ],
            'gulls_wing_priority' => [ '0.9', 'Gulls Wing-tip Patterns page の priority、default 0.9' ],
            'gulls_wlist_priority' => [ '0.8', 'Gulls Wing-tip Patterns list page の priority、default 0.8' ],
            'diy_priority' => [ '0.8', 'post_type diys 関連ページの priority、default 0.8' ],
            'gulls_priority' => [ '0.8', 'post_type gulls シングルページの priority、default 0.8' ],
            'blues_priority' => [ '0.8', 'post_type blues 関連ページの priority、default 0.8' ],
            'standard_priority' => [ '0.5', '設定のないページのデフォルト priority、default 0.5' ],
            'category_priority' => [ '0.7', 'category taxonomy の priority、default 0.7' ],
            'tag_priority' => [ '0.7', 'post_tag taxonomy の priority、default 0.7' ],
            'gull_tax_priority' => [ '0.7', 'gull taxonomy の priority、default 0.7' ],
        );

       $dft_options = array();

        foreach ( $sitemap_dft_options as $key => $val ) {
            $dft_options[ $key ] = $val[0];
        }

       // dev mode(制作中でオプションの数を変更する可能性がある場合)。
       $is_dev_mode = 1;
       // 可変変数でオプション値の名前の入っている変数により変数名を設定。この変数は -> $sitemap_options となる
        ${ $options_name } = current_option_check( $options_name, $dft_options, $is_dev_mode );

        if ( ! $sitemap_options ) {
            exit;
        }
        // ここから表示するページの内容を出力
?>
        <div class="wrap" id="custom-logo" style="padding:10px;font-size:18px;letter-spacing:1.5px;border-radius:10px;background:white;">
            <h2>Generate XML-Sitemap</h2>
            <h3>Priority option config</h3>
            <form name="sitemapoptions">
                <table class="options-table">
                    <tr><th>option</th><th>value</th><th>explain</th></tr>
<?php
                    foreach ( $sitemap_options as $key => $val ) {
                        echo '<tr><td>'.$key.'</td><td><input type="text" name="' . $key . '" value="' . $val . '"></td><td>' . $sitemap_dft_options[ $key ][1] . '</td></tr>';
                    }
?> 
                </table>
            </form>
            <p id="updateres"><button id="optionupdate" style="padding:5px;color:blue;border:solid 1px blue;background:linear-gradient( #f2f4f4, white, #f2f4f4 );border-radius:5px;box-shadow:2px 2px 2px rgba( 0, 0, 0, 0.3 );cursor:pointer;">Option Update</button></p>
        </div>
        <div id="xmlsitemap">
<?php
        if ( file_exists ( $sitemappath ) ) {
            echo '<p>Already XML-Sitemap exist.</p>';
            echo '<p>Open <a href="' . site_url() . '/' . $sitemap . '" target="_blank" rel="noopener noreferrer">XML-Sitemap</a> another tab.</p>';
        } else {
            echo '<p>XML-Sitemap.xml url : ' . site_url() . '/' . $sitemap . '<p>';
            echo '<p>XML-Sitemap none yet.</p>';
		}
?>
        <p>Generate new XML-Sitemap <button id="genesitemap" style="padding:5px;color:blue;border:solid 1px blue;background:linear-gradient( #f2f4f4, white, #f2f4f4 );border-radius:5px;box-shadow:2px 2px 2px rgba( 0, 0, 0, 0.3 );cursor:pointer;">Generate</button></p>
        <div id="genewait"></div>
        <p id="genesmresult"></p>
        <div id="waitback" style="display:flex;justify-content:center;align-items:center;position:fixed;width:100vw;height:100vh;top:0px;left:0px;background:rgba(0,0,0,0.3);visibility:hidden;opacity:0;transition:all 0.5s;"><div id="waiticon" style="position:relative;width:100px;height:100px;background:url(<?php echo get_bloginfo('template_url'); ?>/wait_icon.png) no-repeat center / contain;"></div></div>
        </div>
        <script>
            // ここから Javascript の書き出し
            // クリックイベントが2つ
            // 一つはオプション更新。もう一つは sitemap 生成。どちらも Ajax。
            ( function() {
                // simple Ajax with callback function
                function ajax_callback( str, callback, parent = 'genesmresult' ) {
                    if ( str ) {
                        const url = '<?php echo site_url(); ?>/wp-admin/admin-ajax.php';

                        const req=new XMLHttpRequest();

                        req.onreadystatechange = function() {
                            let result = '';

                            if (req.readyState == 4) { // 通信の完了時
                                if (req.status == 200) { // 通信の成功時
                                    result = req.responseText;

                                    callback( result );
                                } else {
                                    resstr = '<p style="color:red;font-size:20px;">error : Disable Ajax send!</p>';
                                    document.getElementById( parent ).innerHTML = resstr;
                                }
                            }
                        }
                        req.open( 'POST', url, true );
                        req.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
                        req.send( str );
                    } else {
                        console.log( 'error : no data url' );
                    }
                }

                const doc = document,
                    target = doc.getElementById( 'genesitemap' ),
                    oform = doc.forms.sitemapoptions,
                    ou = doc.getElementById( 'optionupdate' );

                // XML-Sitemap 生成
                target.onclick = () => {

                    const nonce = '<?php echo $ajax_nonce; ?>',
                        strsend = 'action=generatesitemap&nonce=' + nonce;

                    ajax_callback( strsend, function( result ) {

                        if ( -1 !== result.indexOf( 'error' ) ) {
                            resstr = '<p style="color:red;font-size:18px">failed generate sitemap : ' + result + '</p>'; 
                        } else {
                            resstr = '<p style="font-size:18px;">Success generate sitemap :<br>' + result + '</p>';
                        }
                        doc.getElementById( 'genesmresult' ).innerHTML = resstr;
                    });

                    return false;
                }

                // Options Update
                ou.onclick = () => {

                    const nonce = '<?php echo $ajax_nonce; ?>',
                       parent = 'updateres',
                        // form の子要素である input を全て取得
                        ips = oform.elements;

                    let strsend = 'action=udsitemapopt&nonce=' + nonce;

                    // input の name と value の値を = でつなぎ、送信する文字列を作成
                    for ( let i = 0, ips_len = ips.length; i < ips_len; ++i ) {
                        strsend += '&' + ips[ i ].name + '=' + ips[ i ].value;
                    }

                    ajax_callback( strsend, function( result ) {

                        if ( -1 !== result.indexOf( 'error' ) ) {
                            resstr = '<p style="color:red;font-size:18px">failed update options : ' + result + '</p>'; 
                        } else {
                            resstr = '<p style="color:blue;padding:10px;font-size:18px;border-top:double 4px lime;border-bottom:double 4px lime">' + result + '</p>';
                        }
                        doc.getElementById( parent ).innerHTML = resstr;
                    }, parent );

                    return false;
                }
            })();
        </script>
<?php
    }
?>
PHP
CopyExpand

次は、オプションを更新する AjaxJavaScript から送信されたデータを受けて、実際にデータベースを更新するための php 側の関数。オプションを更新するページの形式には基本となるものがあって、いままではそれにならった形式でやってきたが、今回は Ajax での方法にしてみた。というのは、実際に sitemap を生成する関数もそれ自体がオプション値を取得するようになっているので、オプション更新後のページ再読み込みが必要ないからである。

セキュリティ的には、ログイン状況とそれが管理者であるかの確認と、WordPressnonce を使ってる。これで十分ではないかと。

<?php
    // option を update する Ajax を受けるほうの php 関数
    function udsitemapopt() {

        // ログインしているのが user_id === 1 の管理人であるか確認する
        // function 'wp_get_current_user' がないということは WordPress が起動すらしていないということ
        if ( function_exists( 'wp_get_current_user' ) ) {
            $user_id = get_current_user_id();

            if ( 1 === $user_id ) {

                //nonceを取得して認証を得る
                if ( check_ajax_referer( __FILE__, 'nonce', false ) ) {

                    $options = array();
                    // 送信されてきたオプション値は文字列なので配列に分解して一つのオプション値として保存
                    foreach ( $_POST as $key => $val ) {
                        if ( 'nonce' !== $key and 'action' !== $key ) {
                            $options[ $key ] = $val;
                        }
                    }

                    $res = update_option( 'sitemap_options', $options );

                    if ( $res ) {
                        echo 'Success update options!';
                    } else {
                        echo 'error:Failed to update options!';
                    }
                } else {
                    echo 'error:Access denied.';
                }
            } else {
                echo 'error:none log-in';
            }
        } else {
            exit;
        }
    }
    // 管理画面でしか呼ばれないのでフックはこちらの一つのみ
    add_action( 'wp_ajax_udsitemapopt', 'udsitemapopt' );
?>
PHP
CopyExpand

そして最後に AjaxJavaScript から呼ばれて、XML-Sitemap を生成する関数。SQL でデータベースから投稿等の情報を取得して、XML データを生成してファイル sitemap.xml に保存する。投稿の種類とか、taxonomy とかページネーションされた複数のページとか、ちょっとややこしい部分である。
と、そのまえにちょっとデータベースにある日付データを加工するだけの簡単な関数を。ただ、空白以降の時間の文字列を削除して年月日だけにするもの。

<?php
    function w3cdt( $datestr = '' ) {
        $ans = '';
        if ( $datestr ) {
            $ans = substr( $datestr, 0, strpos( $datestr, ' ' ) );
        }
        return $ans;
    }
?>
PHP
CopyExpand

そして本丸の generatesitemap() 関数。
実際に XML を出力する部分は、そのサイトが特徴のある部分が多ければ多いほど、それ固有の部分が多くなるだろう。
ログインユーザーチェックのあと、オプションを取得してそのあと SQL で投稿の情報を取得する。
この SQL は投稿と taxonomy を紐づけて taxonomy のあるだけデータを取得するので、同じ投稿がその投稿に紐づけてある taxonomy の分だけ重複しているということになる。ソートは投稿日の逆順なので、ループさせた場合、新しいものほど早く出てくる。

通常の taxonomy 等のアーカイブページは、投稿数が多い場合はページが複数になるわけだけれど、その場合は投稿数がわかりさえすれば、そのページ数を取得するのは簡単なことである。ただ、自サイトの場合ちょっと事情が異なるページが存在する。

それはカスタム投稿 diys のページなのだけれど( 今、表示されているページがその diys である )、このカスタム投稿の個別ページにおいては、ページの表示高速化のために独自キャッシュ化している。キャッシュファイルが存在する場合は、WordPress からの動的な出力ではなく、すでに生成された静的な HTML データをキャッシュファイルから読み込んで表示するようにさせている。その上、コンテンツ量の多いページに関しては、複数のページに分割してキャッシュファイルにしている。

それらのページのページ数を取得するのに、投稿の内容を読み出せば可能ではあるけれど、非常に処理が重くなりそうである。それよりも、同じ投稿 ID のキャッシュファイルの数をしらべる方がよほど手っ取り早く処理も断然軽い。と、いうことでその処理も途中に入っている。複数ある ID のものだけ、それがいくつあるかを取得させている。

処理の流れとしては、データベースから取得したデータを、各投稿別に、 post、page、そしてカスタム投稿の diys、gulls、blues、をそれぞれの配列にまとめて、taxonomy もそのアーカイブページが存在する category、post_tag、gull、においてそれぞれの配列にとりあえずまとめておいて、後で、それぞれがひとかたまりになるように XML の要素として生成させている。

<?php
    function generatesitemap() {

        // ログインしているのが user_id === 1 の管理人であるか確認する
        // function 'wp_get_current_user' がないということは WordPress が起動すらしていないということ
        if ( ! function_exists( 'wp_get_current_user' ) ) {
            exit;
        }

        $user_id = get_current_user_id();

        if ( 1 === $user_id ) {

            //nonceを取得して認証を得る    
            if ( check_ajax_referer( __FILE__, 'nonce', false ) ) {
                global $wpdb;// 関数でデータベースを使うときのお約束

                // priority が設定してあるオプション値を取得
                $sitemap_options = get_option( 'sitemap_options' );

                $error = '';

                // 自サイトの場合、カスタム投稿 diys の投稿は独自キャッシュ化しているので、そのキャッシュファイルのあるフォルダの指定
                // diys の投稿はコンテンツが長い場合ページが分割してあり、そのページ分だけキャッシュファイルも存在している
                // ゆえにそのファイルの数でその投稿が何ページあるのかわかる
                $diys_cache_dir = get_template_directory() . '/diy_cache/';
                // 1ページあたりの投稿数の設定を取得
                $posts_per_page = get_option( 'posts_per_page' );

                // 投稿(post、page、カスタム投稿、すべて含む)にtaxonomy(category, post_tag, custom taxonomy) を紐づけて取得する。
                // 投稿に紐づけてあるtaxonomy の数だけ同じ投稿がダブって取得される
                $sql = <<< HERE
                    SELECT p.ID, p.post_title, p.post_modified, p.post_type, p.post_name, t.term_id, x.taxonomy, t.slug, x.count
                    FROM $wpdb->posts p
                    LEFT OUTER JOIN $wpdb->term_relationships r on p.ID = r.object_id
                    LEFT OUTER JOIN $wpdb->term_taxonomy x on r.term_taxonomy_id = x.term_taxonomy_id
                    LEFT OUTER JOIN $wpdb->terms t on x.term_id = t.term_id
                    WHERE p.post_status = 'publish'
                    ORDER BY p.post_modified DESC
HERE;
                $pt = $wpdb->get_results( $sql );

                $posts = array();
                $pages = array();
                $diys = array();
                $gulls = array();
                $blues = array();
                $ptags = array();
                $pcats = array();
                $gtags = array();
                $allposts = array();

                // 投稿の種別に連想配列を作り、それにより得られる文字列で可変変数を使って各変数を指定する。
                // カスタム投稿は、diys、gulls、blues の3つある
                $types = array( 'post' => 'posts', 'page' => 'pages', 'diys' => 'diys', 'gulls' => 'gulls', 'blues' => 'blues' );
                $post_last_date = '';
                $diys_last_date = '';
                $gull_last_date = '';
                $firstel = array( 0, 0, 0 );
                $pt_count = 0;

                if ( $pt ) {
                    $pt_count = count( $pt );

                    foreach ( $pt as $p ) {
                        $ptype = $p->post_type;

                        // post_type により可変変数で変数を指定して、各投稿種別に投稿ID を Key としてデータを蓄積する。
                        // 投稿ID をKeyにしているので、taxonomy によりタブっているデータも一つになる。
                        if ( isset ( $types[ $ptype ] ) ) {
                            ${ $types[ $ptype ] }[ $p->ID ] = [ $p->post_title, $p->post_modified, $p->post_name ];
                        }

                        // 各投稿データを taxonomy ( category, post_tag ,custom taxonomy )別にterm_id を key としてそれぞれの変数へと蓄積する
                        // 同じ term_id が既存の場合は入力しない。故にその term_id のデータは一番最初に現れたデータとなり、
                        // SQL において日付の逆順(新しい順)でソートしているので一番最初に現れたデータが一番新しい投稿の日付となる
                        if ( 'post' === $ptype ) {
                            if ( 'category' === $p->taxonomy ) {
                                if ( ! isset ( $pcats[ $p->term_id ] ) ) {
                                    $pcats[ $p->term_id ] = [ $p->ID, $p->post_modified, $p->slug, $p->count ];
                                }
                            } else if ( 'post_tag' === $p->taxonomy ) {
                                if ( ! isset ( $ptags[ $p->term_id ] ) ) {
                                    $ptags[ $p->term_id ] = [ $p->ID, $p->post_modified, $p->slug, $p->count ];
                                }
                            }
                            if ( 0 === $firstel[0] ) {
                                $post_last_date = $p->post_modified;
                            }
                            ++$firstel[0];
                        } else if ( 'gulls' === $ptype ) {
                            if ( 'gull' === $p->taxonomy ) {
                                if ( ! isset ( $gtags[ $p->term_id ] ) ) {
                                    $gtags[ $p->term_id ] = [ $p->ID, $p->post_modified, $p->slug, $p->count ];
                                }
                            }
                            if ( 0 === $firstel[1] ) {
                                $gull_last_date = $p->post_modified;
                            }
                            ++$firstel[1];
                
                        } else if ( 'diys' === $ptype ) {
                            if ( 0 === $firstel[2] ) {
                                $diys_last_date = $p->post_modified;
                            }
                            ++$firstel[2];
                        }
                    }
                } else {
                    if ( is_array( $pt ) ) {
                        $error .= implode( '<br>', $pt );
                    } else {
                        $error .= $pt;
                    }
                }

                $pt = null;
                // カスタム投稿 diys においては各投稿を独自キャッシュ化している
                // その上、コンテンツの量が多いものは複数ページに分割している
                // それゆえにその投稿が何ページあるのかは、キャッシュファイルを数える事でしか知りようがない
                // キャッシュファイルのディレクトリをスキャンして同じIDで複数あるものをリストする処理
                $diys_cache_pages = array();

                if ( file_exists ( $diys_cache_dir ) ) {
                    $diy_caches = array_diff( scandir( $diys_cache_dir ) , array( '..', '.' ) );
            
                    if ( $diy_caches ) {
                        foreach ( $diy_caches as $val ) {
                            $pi = pathinfo( $val );
                            if ( isset( $pi['extension'] ) and 'php' === $pi['extension'] ) {
                                $el = explode( '_', $pi['filename'] );
                                if ( isset ( $el[3] ) ) {
                                    if ( isset ( $diys_cache_pages[ $el[2] ]) ) {
                                        $diys_cache_pages[ $el[2] ] += 1;
                                    } else {
                                        $diys_cache_pages[ $el[2] ] = 1;
                                    }
                                }
                            }
                        }
                    }
                }
            
                if ( $error ) {
                    echo 'failed SQL : ' . $error;
                } else {

                    $xml = array(
                        '<?xml version="1.0" encoding="UTF-8"?>' . "\n",
                        '<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">' . "\n",
                        '<url><loc>https://strix.main.jp/</loc><lastmod>' . w3cdt( $post_last_date ) . '</lastmod><changefreq>daily</changefreq><priority>1.0</priority></url>' . "\n",
                    );
                    $url = 'https://strix.main.jp/?';

                    // 固定ページで検索エンジンにインデックスさせたくない物のpost_IDをkeyとした連想配列のリスト
                    $pages_ignore = array();
                    if ( $sitemap_options['pages_ignore'] ) {
                        $tmpel = explode ( ',', $sitemap_options['pages_ignore'] );
                        foreach ( $tmpel as $val ) {
                            $pages_ignore[ ( int ) $val ] = 0;
                        }
                    }

                    // 固定ページのpriority が設定してある物のpost_ID を key とした連想配列のリスト
                    $pages_priority = array();
                    if ( $sitemap_options['pages_priority'] ) {
                        $tmpel = explode ( ',', $sitemap_options['pages_priority'] );
                        foreach ( $tmpel as $val ) {
                            $elms = explode ( ':', $val );
                            if ( isset( $elms[1] ) and $elms[0] ) {
                                $pages_priority[ ( int ) $elms[0] ] = $elms[1];
                            }
                        }
                    }

                    $nowdate = new DateTime();
                    $blues_count = 1;
                    $posts_count = 0;
                    $ans = array();
        
                    $show_types = array( 'pages', 'diys', 'gulls', 'blues', 'posts' );
        
                    foreach ( $show_types as $val ) {

                        $tartype = ${ $val };// 可変変数で対象の post_type の変数を指定、$val === 'pages' の時 $pages

                        if ( $tartype ) {
                            $ans[] = $val . ' : ' . ( string ) count( $tartype );

                            foreach( $tartype as $key => $value ) {
        
                                if ( 'pages' === $val ) {
                                    // 固定ページな各ページによって複数のページになっているものがあるので、
                                    // それぞれのページで処理を変える
        
                                    if ( 69149 === $key ) {// Gulls Wing-tip Patterns
                                        
                                        $page_count = ceil( count ( $gulls ) / $posts_per_page );
                                        // w3cdt は時間付きの日付データから時間部分を取り除くシンプルな独自関数
                                        $lastmod = w3cdt( $gull_last_date );
        
                                        for ( $i = 0; $i < $page_count; ++$i ) {
                                            $paged = '';
                                            if ( $i > 0 ) {
                                                // xml ファイルの場合、& などはエスケープする必要があり
                                                $paged = '&paged=' . ( string ) ( $i + 1 );
                                            }
                                            // 自サイトの場合、パーマリンクはデフォルトのままなので各投稿へのリンクは
                                            // デフォルトの決まりきった形式のパターン
                                            // パーマリンク設定がある場合は、get_permalink() 等を使用
                                            $xml[] = '<url><loc>' . $url . 'page_id=' . $key . $paged . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>' . $sitemap_options['gulls_wing_priority'] . '</priority></url>' . "\n";
                                        }
                                    } elseif ( 69697 === $key ) {
        
                                        $lastmod = w3cdt( $gull_last_date );
                                        
                                        // このページは画像の枚数でページ数がきまるが、ある特定の画像なのでデータベースで数を取得するのはやっかい
                                        // ゆえに確実に存在するページ数を指定
                                        for ( $i = 0; $i < 6; ++$i ) {
                                            $paged = '';
                                            if ( $i > 0 ) {
                                                $paged = '&paged=' . ( string ) ( $i + 1 );
                                            }
                                            $xml[] = '<url><loc>' . $url . 'page_id=' . $key . $paged . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>' . $sitemap_options['gulls_wlist_priority'] . '</priority></url>' . "\n";
                                        }
                                    } else {
                                        if ( 46 == $key ) {// imageviewer page
                                            $lmod = $post_last_date;
                                        } elseif ( 16227 === $key ) {// WordPress の Diy 備忘録 page
                                            $lmod = $diys_last_date;
                                        } else {
                                            $lmod = $value[1];
                                        }
        
                                        $priority = '0.5';
                                        if ( isset ( $pages_priority[ $key ] ) ) {
                                            $priority = $pages_priority[ $key ];
                                        }
        
                                        if ( ! isset ( $pages_ignore[ $key ] ) ) {
                                            $xml[] = '<url><loc>' . $url . 'page_id=' . $key . '</loc><lastmod>' . w3cdt( $lmod ) . '</lastmod><changefreq>yearly</changefreq><priority>' . $priority . '</priority></url>' . "\n";
                                        }
                                    }
                                } elseif ( 'diys' === $val ) {
                                    // カスタム投稿 diys においては投稿の新しさで priority と changefreq の設定を変えた
                                    $moddate = new DateTime( $value[1] );
                                    $diff = $moddate -> diff( $nowdate );
                                    $daydiff = $diff -> format( '%a' );
        
                                    $priority = $sitemap_options['diy_priority'];
                                    $chgfrq = 'yearly';
        
                                    if ( $daydiff < 10 ) {
                                        $chgfrq ='weekly';
                                        $priority = '0.9';
                                    } elseif ( $daydiff < 30 ) {
                                        $chgfrq = 'monthly';
                                        $priority = '0.8';
                                    }
        
                                    $lastmod = w3cdt( $value[1] );
                                    $xml[] = '<url><loc>' . $url . 'diys=' . $value[2] . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>' . $chgfrq . '</changefreq><priority>' . $priority .  '</priority></url>' . "\n";
        
                                    if ( isset ( $diys_cache_pages[ $key ] ) ) {
                                        ++$diys_cache_pages[ $key ];
        
                                        for ( $i = $diys_cache_pages[ $key ]; $i > 1; --$i ) {
                                            $xml[] = '<url><loc>' . $url . 'diys=' . $value[2] . '&dev=' . ( string) $i . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>' . $chgfrq . '</changefreq><priority>' . $priority .  '</priority></url>' . "\n";
                                        }
                                    }
                                } elseif ( 'gulls' === $val ) {
                                    $xml[] = '<url><loc>' . $url . 'gulls=' . $value[2] . '</loc><lastmod>' . w3cdt( $value[1] ) . '</lastmod><changefreq>never</changefreq><priority>' . $sitemap_options['gulls_priority'] . '</priority></url>' . "\n";
                                } elseif ( 'blues' === $val ) {
                                    if ( 1 === $blues_count ) {
                                        $xml[] = '<url><loc>' . $url . 'post_type=blues</loc><lastmod>' . w3cdt( $value[1] ) . '</lastmod><changefreq>yearly</changefreq><priority>' . $sitemap_options['blues_priority'] . '</priority></url>' . "\n";
                                    }
                                    $xml[] = '<url><loc>' . $url . 'blues=' . $value[2] . '</loc><lastmod>' . w3cdt( $value[1] ) . '</lastmod><changefreq>never</changefreq><priority>' . $sitemap_options['blues_priority'] . '</priority></url>' . "\n";
                                    ++$blues_count;
                                } else {
                                    $xml[] = '<url><loc>' . $url . 'p=' . $key . '</loc><lastmod>' . w3cdt( $value[1] ) . '</lastmod><changefreq>never</changefreq><priority>' . $sitemap_options['standard_priority'] . '</priority></url>' . "\n";
                                    ++$posts_count;
                                }
                            }
                        }
                    }

                    // ここから taxonomy に関しての処理
                    // すでにそれぞれの taxonomy の配列にまとめてあるので順に出力するだけ
                    // それぞれの taxonomy にはそれ専用の画像だけをまとめたページもあるので、それも一緒にその1ページ目だけを出力する
                    $rooptar = array( 'category' => 'pcats', 'post_tag' => 'ptags', 'gull' => 'gtags' );

                    foreach ( $rooptar as $key => $tax ) {
                        $tartax = ${ $tax };
                        if ( $tartax ) {
                            $ans[] = $key . ' : ' . ( string ) count( $tartax );

                            foreach ( $tartax as $key => $row ) {

                                $lmod = $row[1];
        
                                $lastmod = w3cdt( $lmod );
                                $target = array( 'pcats' => [ 'cat=' . ( string ) $key, 'z' . $row[2] ], 'ptags' => [ 'tag=' . $row[2], 'x' . $row[2] ], 'gtags' => [ 'gull=' . $row[2] ] );
            
                                $xml[] = '<url><loc>' . $url . $target[ $tax ][0] . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>0.7</priority></url>' . "\n";
        
                                $pcount = ceil( $row[3] / $posts_per_page );
                                if ( $pcount > 1 ) {
                                    ++$pcount;
                                    for ( $i = 2; $i < $pcount; ++$i ) {
                                        $xml[] = '<url><loc>' . $url . $target[ $tax ][0] . '&paged=' . ( string ) $i . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>0.7</priority></url>' . "\n";
                                    }
                                }
                                
                                // 画像専用ページ
                                if ( 'gtags' === $tax ) {
                                    $xml[] = '<url><loc>' . $url . 'page_id=69697&' . $target[ $tax ][0] . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>0.7</priority></url>' . "\n";
                                } else {
                                    $xml[] = '<url><loc>' . $url . 'page_id=48&tagname=' . $target[ $tax ][1] . '</loc><lastmod>' . $lastmod . '</lastmod><changefreq>yearly</changefreq><priority>0.7</priority></url>' . "\n";
                                }
            
                            }
                        }
                    }
        
                    $xml[] = '<url><loc>https://strix.main.jp/canus/</loc><lastmod>2017-10-04</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>' . "\n";
                    $xml[] = '</urlset>';
        
                    $fh = fopen( ABSPATH. 'sitemap.xml', 'w' );
                    if ( $fh ) {
                        fwrite( $fh, implode( '', $xml ) );
                        fclose( $fh );
                    } else {
                        echo 'error: Disable open xml file.';
                    }

                    echo 'All posts : ' . $pt_count . '<br>' . implode( '<br>', $ans ) . '<br>xml objects count : ' . ( count( $xml ) - 3 );
                }
            } else {
                echo 'error: Access denied.';
            }
        } elseif ( 0 === $user_id ) {
            echo 'error:none log-in';
        } else {
            echo 'error! log-in not admin : ' . $user_id;
        }
    
        exit;
    }
    // 管理画面だけでしか使わないのでフックは一つだけ
    add_action( 'wp_ajax_generatesitemap', 'generatesitemap' );
?>
PHP
CopyExpand

これでおわりである。
とりあえず、これで XML-Sitemap にはこまらなくてすみそうではあるのだけれど・・・。まぁ、ただ、新規投稿した場合の sitemap 更新は手動である。検索エンジンへの通知もなし。
と、いうことでさっそく GoogleBing に新しい XML-Sitemapurl を送信した。が、Google Search Console が新 sitemap を読み込んでくれない。Bing の方は送信すると同時に読んでくれたのだけれど。二、三日様子を見ていても読んでくれないので、robots.txt も自作して設置してみたが、今のところ改善されない。これはいったい・・・?
あれま、散々ないがしろにしていたから意地悪されているのかしら?どうしたものか!

実は、プログラムに二度手間な部分を見つけ(投稿カウント 0 のカテゴリーページがリストされていたりもした)、それを直して(上に掲載しているのは訂正したもの)再び送信してみたが結果に変化はなし。何度も送信するのも問題がありそうなので、それはやめてちょっと様子を見ているしか無いなと。

話はちょっとそれるが、このプログラムを初めてサーバーにアップさせて動かした時に、SQL において、必要もないのに prepare を使っていて Notice をもらってしまった。これ、Google XML Sitemap プラグインで出ていた Notice と全く同じもの。はは・・・、ちょっと親しみを感じてしまった。

確か、prepare を使ったほうがより高速だったと記憶しているが、それは同じ SQL を二度、三度と使った時のことで、一度しか使わないのであればそのアドバンテージはなかったのだと思う。

話を戻して、様子を見つつ、なにかできることはないのだろうかとネットを徘徊などしていた。インデックスファイルを作って、いくつかのファイルに分割したほうが良いのか、など考えもしたが。一つ気がついたのは、Google Search Console を使い始めてすでに随分と時間が経っているが、その間、サイトにもかなり手をいれていたので、いつの間にか、GSC の認識用のタグがなくなってしまっていた。なので、改めてその meta タグを入れて確認させた。

それが功を奏したというわけでもないのだろうけれど、最後に送信してから4日後にめでたく読み込んでくれた。ただ単に、送信すればすぐに読み込んでくれるわけではない(そう言うことが書いてあったとも思う)、ということなのだろうけれど、それならそれで表示される文言が、「読み込まれるまでしばらくお待ち下さい」とかだったら、じたばたすることなく待てるように思えるのではあるけれど。

とにかく、ボットさんにはボットさんの都合というものがあるということで。
ちなみに、サイトマップを読み込んでくれた後、クローラのアクセス回数がどっと増えたので、読み込んでくれたのは間違いないようである。
しかし、これで終わりかと思いきや、実はまだまだつづくのである。

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=171038

Sanbanse Funabashi
2011.01.01 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。