自作 WordPressプラグインに、一日の投稿文字数をカウントする機能を追加しました。
![[WordPress] 自作プラグインに投稿文字数を計測するスクリプトを追加した【dbDelta】](https://chiilabo.com/wp-content/uploads/2021/08/sakurotu-ga-ru2.jpg)
一日の成果を文字数で把握できると、ちょっとモチベーションアップになるかも?
1. データベースの作成(dbDelta)
一番手間取ったのは、データベースを作成する処理です。
WordPressでは、MySQLのテーブルはかんたんに追加できます。
global $chiilabo_db_version;
$chiilabo_db_version = '1.1';
define('CHIILABO_COUNT_TABLE_NAME', $wpdb->prefix . 'chiilabo_daily_count');
function chiilabo_db_install() {
global $wpdb;
global $chiilabo_db_version;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
count mediumint(9) NOT NULL,
diff mediumint(9) NOT NULL,
UNIQUE KEY id (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
add_option( 'chiilabo_db_version', $chiilabo_db_version );
}
テーブルを作成するクエリ($sql)を dbDelta() に与えると、うまいこと テーブルを追加・更新してくれます。
1-1. プラグイン有効化時にテーブルを作成したい(register_activation_hook)
問題は、テーブル作成のトリガーです。
「プラグインの有効化」で実行させたかったのですが、どうもうまく反応してくれませんでした。
register_activation_hookで指定できるはずなのですが…。
register_activation_hook(__FILE__, 'chiilabo_db_install');
しかたないので、プラグインの管理メニューの表示スクリプトに テスト用の条件分岐を入れて、手動で chiilabo_db_install()関数を呼び出して、テーブルの初期化を操作しました。
2. 日ごとの投稿文字数の集計する(date_default_timezone_set)
投稿文字数は、get_posts()の content から勘定できます。
ただし、すでに同じ日付で記録されていたら、それを更新する必要があります。
まず、日付の区切りを日本時間に合わせます(date_default_timezone_set('Asia/Tokyo');
)。
すでに同じ日付で記録されていたら、いったん消すことにしました。
/* 当日の記録をいったん削除する*/
global $wpdb;
date_default_timezone_set('Asia/Tokyo');
$date = date("Y-m-d H-i-s", strtotime("today"));
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->query("DELETE FROM $table_name WHERE date = '$date'");
2-1. 不要な要素を除外する(preg_replace)
次に、全投稿の文字数を数えます。
/* 全投稿の文字数を数える */
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => array( 'publish', 'pending', 'draft', 'future'),
'orderby' => 'modified', /* date, modified */
'numberposts' => -1
) );
$count = 0;
foreach ( $posts as $post ) {
$content = $post->post_content;
$content = preg_replace('/<pre[\s\S]*?<\/pre>/','', $content);
$content = preg_replace('/<blockquote[\s\S]*?<\/blockquote>/','', $content);
$content = strip_tags($content);
$content = preg_replace('/\s*/','', $content);
$char_count = mb_strlen($content);
$count += $char_count;
}
記事内の自分の入力文字数をカウントしたかったので、引用とコードを除くようにしました。
preg_replace() で、不要な部分を正規表現パターンで削除しました。
例えば、このように空白文字をカウントから除外しました。
$content = preg_replace('/\s*/','', $content);
ただし、正規表現でのマッチで 2つハマりポイントがありました。
2-2. 「/」はそのままパターンに入れられない(エスケープ)
タグを閉じる「</pre>」でつまりました。
実は、「/」が正規表現パターンの「デリミタ」になっているから、そのまま指定できないのです。
「\/」とエスケープすることに気づくのに時間がかかりました。
$result = preg_replace('/<a .*?>(.*?)<\/a>/', "$1", $item);
メタ文字 | 説明 |
\ | 多目的に使う一般的なエスケープ文字。 「メタ文字」は、もれなくエスケープが必要。 正規表現パターンの中で「デリミタ」と同じ文字が出現するなら、エスケープが必要。 |
^ | 検索対象(複数行モードでは行)の始まりを言明 |
$ | 検索対象の終わりあるいは終端の改行文字の前(複数行モードでは行の終わり)を言明 |
. | 改行を除くすべての文字にマッチ(デフォルト時) |
[ | 文字クラス定義の開始 |
] | 文字クラス定義の終了 |
| | 選択肢の開始 |
( | サブパターンの開始 |
) | サブパターンの終了 |
? | ( の意味を拡張/0 または 1 回マッチ/なるべく少ない回数だけマッチ |
* | 0 回以上の繰り返し |
+ | 1 回以上の繰り返し |
{ | 最小/最大を指定する量指定子の開始 |
} | 最小/最大を指定する量指定子の終了 |
2-3. 「.」は改行にマッチしない「\s\S」
一つは、複数行の正規表現による置換です。
通常の「.*」でタグの中身にマッチさせたかったのですが、改行が含まれるとマッチしません。そこで、「\s\S」を使いました。
$subject = '
The text you
need to search.
';
// 改行コードを含むべての文字列
if ( preg_match( '/[\s\S]*/', $subject ) )
echo "Matched";
2-4. 【追記:2023-06-04】正規表現の最短一致・最長一致【*?】
ところが、<pre>タグではうまくマッチしません。
どうも、文字数が少なくカウントされてしまっているのです。
$content = preg_replace('/<pre[\s\S]*<\/pre>/','', $content);
実は、正規表現では、文字列内にパターンが複数存在するときに、長く取るのか、短く取るのかが重要です1。
基本は「最長一致」です。
マッチした全体が一つにカウントされます。
このスクリプトでは、改行を含めたすべての文字にマッチするために[\s\S]*を使っています。しかし、これだと、preタグが複数存在した場合、その間も含めてマッチしてしまいます。[\s\S]*が「<\/pre>」も含んでしまうからです。
そこで最短一致にするには、[\s\S]*?とします。
$content = preg_replace('/<pre[\s\S]*?<\/pre>/','', $content);
そうすれば、正しくタグに囲まれた部分だけがマッチします。
2-5. (あるいは)HTMLとしてパースする
一応、HTMLとしてパースする方法もあります2。
$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->preserveWhiteSpace = false;
$doc->documentEncoding = 'UTF-8';
@$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//div[@class="hoge"]') as $node) {
$node->parentNode->removeChild($node);
}
とりあえず、今回はかんたんな置換で解決しています。
3. 出来上がったコード
出来上がりました。
ちゃんと表示するごとに、サイト全体の文字数が更新されて、前日との差分が表示されます。

/** 2023-05-06 データベース追加*/
global $chiilabo_db_version;
$chiilabo_db_version = '1.1';
define('CHIILABO_COUNT_TABLE_NAME', $wpdb->prefix . 'chiilabo_daily_count');
function chiilabo_db_install() {
global $wpdb;
global $chiilabo_db_version;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
count mediumint(9) NOT NULL,
diff mediumint(9) NOT NULL,
UNIQUE KEY id (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
add_option( 'chiilabo_db_version', $chiilabo_db_version );
}
/** 2023-05-06 なぜかプラグイン有効化時に実行されない*/
register_activation_hook(__FILE__, 'chiilabo_db_install');
function chiilabo_db_initial_data() {
global $wpdb;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->insert(
$table_name,
array(
'date' => "2023-05-05 00:00:00",
'count' => "2780000",
)
);
}
function delete_db() {
global $wpdb;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->query("DELETE FROM $table_name");
}
function add_count_to_db() {
/* 全投稿の文字数を数える */
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => array( 'publish', 'pending', 'draft', 'future'),
'orderby' => 'modified', /* date, modified */
'numberposts' => -1
) );
$count = 0;
foreach ( $posts as $post ) {
$content = $post->post_content;
$content = preg_replace('/<pre[\s\S]*?<\/pre>/','', $content);
$content = preg_replace('/<blockquote[\s\S]*?<\/blockquote>/','', $content);
$content = strip_tags($content);
$content = preg_replace('/\s*/','', $content);
$char_count = mb_strlen($content);
$count += $char_count;
}
/* 当日の記録をいったん削除する*/
global $wpdb;
date_default_timezone_set('Asia/Tokyo');
$date = date("Y-m-d H-i-s", strtotime("today"));
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->query("DELETE FROM $table_name WHERE date = '$date'");
/* 前日の記録との差分を計算する*/
$query = "
SELECT date, count, diff
FROM $table_name
ORDER BY date DESC
";
$results = $wpdb->get_results("SELECT date, count, diff FROM $table_name ORDER BY date DESC LIMIT 1");
$diff = $count - $results[0]->count;
$wpdb->insert(
$table_name,
array(
'date' => $date,
'count' => $count,
'diff' => $diff,
)
);
}
/** 日々の投稿文字数の表示*/
function chiilabo_daily_count_display(){
chiilabo_daily_count_display_inner(-1);
}
/** 日々の投稿文字数の表示*/
function chiilabo_daily_count_display_inner($num){
global $wpdb;
/* データベースのクリア用フラグ 通常はfalse*/
if (false) {
chiilabo_db_install();
delete_db();
chiilabo_db_initial_data();
}
add_count_to_db();
$table_name = CHIILABO_COUNT_TABLE_NAME;
if ($num>0) {
$limit = "LIMIT $num";
}
$query = "
SELECT date, count, diff
FROM $table_name
ORDER BY date DESC
$limit
";
// Retrieve the access logs
$results = $wpdb->get_results($query);
// Initialize the table
echo '<table class="a">';
echo '<thead><tr>';
echo '<th>日付</th>';
echo '<th>入力文字数</th>';
echo '</tr></thead>';
// Populate the table
echo '<tbody>';
foreach ( $results as $idx => $row) {
echo '<tr>';
echo '<td>' . substr($row->date, 0, 10) . '</td>';
echo '<td align="right">' . ( $row->diff) . '</td>';
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
}
これらをプラグインPHPに追加することで、機能を追加できました。
<?php
/**
* Plugin Name: ちいラボ統計
* Plugin URI: chiilabo.com
* Author: chiilabo
* Author URI: chiilabo.com
* Description: 投稿月ごとの月別ページビュー集計表を表示する。
* Version: 2.1.0
* Update: 月別ページビュー
* 2.0.0: 2023-04-23 グリッドビューを追加
* 2.0.1: 2023-04-24 グリッドビューに検索バーを追加
* 2.0.2: 2023-05-05 文字数のカウントからコード・引用を省く
* 2.0.3: 2023-05-05 グリッドビューに簡易ページを追加した
* 2.1.0: 2023-05-06 記事の集計データベースを追加した
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/** 管理メニューの設定・追加*/
define( 'ACCESSES_TABLE_NAME', $wpdb->prefix . 'cocoon' . '_accesses' );
// Register the admin menu for the plugin
function chiilabo_stats_admin_menu() {
add_menu_page(
'chiilabo-stats' // ページのタイトルタグ<title>に表示されるテキスト
, 'ちいラボ統計' // 左メニューとして表示されるテキスト
, 'edit_posts' // 必要な権限 manage_options は通常
, 'chiilabo-stats' // 左メニューのスラッグ名
, '' // メニューページを表示する際に実行される関数
, 'dashicons-admin-users' // メニューのアイコンを指定 https
, 0 // メニューが表示される位置のインデックス(0が先頭) 5=投稿,10=メディア,20=固定ページ,25=コメント,60=テーマ,65=プラグイン,70=ユーザー,75=ツール,80=設定
);
add_submenu_page(
'chiilabo-stats', // 親メニューのスラッグ
'記事分析',
'記事分析',
'edit_posts',
'chiilabo-stats',
'posts_stats_display',
1
);
add_submenu_page(
'chiilabo-stats', // 親メニューのスラッグ
'月別集計',
'月別集計',
'edit_posts',
'monthly-pageview-totals',
'monthly_pageview_totals_display',
2
);
add_submenu_page(
'chiilabo-stats', // 親メニューのスラッグ
'グリッド集計',
'グリッド集計',
'edit_posts',
'grid-stats',
'grid_stats_display',
3
);
add_submenu_page(
'chiilabo-stats', // 親メニューのスラッグ
'グリッド集計all',
'グリッド集計all',
'edit_posts',
'grid-stats-all',
'grid_stats_display_all',
4
);
add_submenu_page(
'chiilabo-stats', // 親メニューのスラッグ
'日々の投稿文字数',
'日々の投稿文字数',
'edit_posts',
'daily-count',
'chiilabo_daily_count_display',
4
);
}
add_action( 'admin_menu', 'chiilabo_stats_admin_menu' );
// Callback to display the page content
function monthly_pageview_totals_display() {
global $wpdb;
// Use the Transients API to cache the page view data for 12 hours
// Retrieve all the posts and their permalinks
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => 'publish',
'numberposts' => -1
) );
$urls = array();
$post_ms = array();
$results = array();
foreach ( $posts as $post ) {
$url = parse_url( get_permalink( $post ) );
$urls[$post->ID] = substr( $url['path'], 1 );
$post_m = substr($urls[$post->ID], 0,7);
$post_ms[$post_m]=$post_m;
$results[$post_m]=array();
}
$table_name = ACCESSES_TABLE_NAME;
$query = "
SELECT post_id, SUM(count) as pv, YEAR(date) as year, MONTH(date) as month, date
FROM $table_name
GROUP BY post_id, year, month
ORDER BY date DESC
";
// Retrieve the access logs
$logs = $wpdb->get_results($query);
$months = array();
foreach ( $logs as $log ) {
$post_m = substr($urls[$log->post_id], 0,7);
$access_m = $log->year.sprintf('%02d', $log->month);
$pv = $results[$post_m][$access_m];
if (!$pv) {
$pv =0;
}
$results[$post_m][$access_m] = $pv + $log->pv;
$months[ $access_m ] = $log->year.'年'.$log->month.'月';
}
// Initialize the table
echo '<table class="widefat">';
echo '<thead><tr>';
echo '<th width="40%">投稿月</th>';
foreach ( $months as $month ) {
echo '<th>' . esc_html( $month ) . '</th>';
}
echo '</tr></thead>';
// Populate the table
echo '<tbody>';
foreach ( $results as $m => $row ) {
echo '<tr>';
echo '<td>' . $m . '</td>';
foreach ( $months as $key => $month ) {
$pv = 0;
foreach ( $row as $mm => $mpv ) {
if ( $mm == $key ) {
$pv = $mpv;
break;
}
}
echo '<td align="right">' . ( $pv ? number_format_i18n( $pv ) : '' ) . '</td>';
}
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
}
/**
* 記事ごとの分析
*
*/
function posts_stats_display() {
global $wpdb;
// Retrieve all the posts and their permalinks
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => 'publish',
'numberposts' => -1
) );
$post_id2obj = array();
$post_words = array();
foreach ( $posts as $post ) {
$post_id2obj[$post->ID] = $post;
$post_words[$post->ID] = mb_strlen($post->post_content);
}
$table_name = ACCESSES_TABLE_NAME;
$query = "
SELECT post_id, SUM(count) as pv, date
FROM $table_name
GROUP BY post_id
ORDER BY date DESC
";
// Retrieve the access logs
$logs = $wpdb->get_results($query);
$pvs = array();
foreach ( $logs as $log ) {
$days[$log->post_id] = (strtotime("now") - strtotime($log->date)) / 86400;
}
// Initialize the table
echo '<style>.widefat.stats td, .widefat.stats th { padding: 2px 10px !IMPORTANT; } </style>';
echo '<table class="widefat stats">';
echo '<thead><tr>';
echo '<th>投稿日</th>';
echo '<th>文字数</th>';
echo '<th>PV/日</th>';
echo '<th>タイトル</th>';
echo '<th>星</th>';
echo '</tr></thead>';
// Populate the table
echo '<tbody>';
echo '<tr>';
echo '<td>';
foreach ( $logs as $log ) {
$post = $post_id2obj[$log->post_id];
$pv_ave = ceil($log->pv / ceil($days[$log->post_id]));
if ($pv_ave > 2) {
echo '</td>';
echo '</tr>';
echo '<tr>';
echo '<td>';
echo $log->date;
echo '</td>';
echo '<td align="right">';
echo mb_strlen(strip_tags($post->post_content));
echo '</td>';
echo '<td align="right">';
echo $pv_ave;
echo '</td>';
echo '<td>';
echo '<a href="'. get_permalink($post) . '">';
echo mb_substr($post->post_title, 0, 50) .' ... ('. $log->pv .') ';
echo '</a>';
echo '</td>';
echo '<td>';
echo '<a href="'. get_permalink($post) . '">';
echo '○';
echo '</a>';
} else {
echo '<a href="'. get_permalink($post) . '">';
echo '✕';
echo '</a>';
}
}
echo '</tbody>';
echo '</table>';
}
function grid_stats_display() {
grid_stats_display_inner(360);
}
function grid_stats_display_all() {
grid_stats_display_inner(-1);
}
/**
*
*
*/
function grid_stats_display_inner($disp_count = -1) {
global $wpdb;
// Retrieve all the posts and their permalinks
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => array( 'publish', 'pending', 'draft', 'future'),
'orderby' => 'modified', /* date, modified */
'numberposts' => -1
) );
if ($disp_count > 0) {
$posts = array_slice($posts, 0, $disp_count);
}
$post_id2obj = array();
$post_words = array();
foreach ( $posts as $post ) {
$post_id2obj[$post->ID] = $post;
$post_words[$post->ID] = mb_strlen($post->post_content);
}
$table_name = ACCESSES_TABLE_NAME;
$query = "
SELECT post_id, SUM(count) as pv, date
FROM $table_name
GROUP BY post_id
";
// Retrieve the access logs
$logs = $wpdb->get_results($query);
$post_id2log = array();
$days = array();
date_default_timezone_set('Asia/Tokyo');
foreach ( $logs as $log ) {
$post_id2log[$log->post_id] = $log;
$days[$log->post_id] = (strtotime("now") - strtotime($log->date)) / 86400;
}
// Initialize the table
$row = 9;
if (wp_is_mobile()) {
$row = 4;
}
echo '<style>
table.widefat.stats{
width: 100%;
}
.widefat.stats td, .widefat.stats th {
border: 1px solid black;
padding: 0px 0px !IMPORTANT;
width: calc(100%/'.$row.');
}
.container{
position: relative;
width:calc(100%/'.$row.');
}
.widefat.stats img{
width: 100%;
height: 100%;
object-fit:cover;
aspect-ratio: 100/100;
opacity: 0.6;
}
.container p{
position: absolute;
padding: 5px;
font-size: 7px;
margin:0;
}
.container .date{
top: 5px;
left: 5px;
text-align: center;
background-color: brown;
font-weight: bold;
color: #fff;
}
.container .chars {
background-color: #fff;
left: 5px;
bottom: -5px;
font-weight: 900;
}
.container .pv {
background-color: #fff;
right: 5px;
bottom: -5px;
font-weight: 900;
}
.container .rank {
position: absolute;
font-size: 300%;
font-weight: 900;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
text-shadow: var(--cocoon-white-color) 3px 0px 0px, var(--cocoon-white-color) 2px 1px 0px, var(--cocoon-white-color) 2px 2px 0px, var(--cocoon-white-color) 2px 3px 0px, var(--cocoon-white-color) 1px 3px 0px, var(--cocoon-white-color) 0px 3px 0px, var(--cocoon-white-color) -1px 3px 0px, var(--cocoon-white-color) -2px 2px 0px, var(--cocoon-white-color) -3px 1px 0px, var(--cocoon-white-color) -3px 0px 0px, var(--cocoon-white-color) -3px -1px 0px, var(--cocoon-white-color) -3px -2px 0px, var(--cocoon-white-color) -2px -2px 0px, var(--cocoon-white-color) -1px -3px 0px, var(--cocoon-white-color) 0px -3px 0px, var(--cocoon-white-color) 1px -3px 0px, var(--cocoon-white-color) 2px -2px 0px, var(--cocoon-white-color) 2px -2px 0px, var(--cocoon-white-color) 3px -1px 0px;
}
.container.todo_post .rank, .container.todo_post .chars {
color: red;
}
</style>
';
$search_box = '<div class="searchbox-wrap"><div class="gcse-searchbox-only"></div></div>
<script async src="https://cse.google.com/cse.js?cx=b8f3ee6d995f66436"></script>
<div class="searchbox-space"></div>
<style>
.searchbox-space {
height: 64px;
}
.searchbox-wrap {
position: fixed; /* ヘッダーを固定する */
top: 32px; /* 上部から配置の基準位置を決める */
left: 0px;
padding: 10px;
background-color: #f0f0f1;
opacity: 1;
z-index: 999;
padding-left: 172px;
border-bottom: 1px solid #aaa;
width: 100%;
height: 64px;
box-sizing: border-box;
}
.gcse-searchbox-only {
display: block;
background-color: #f0f0f1;
}
@media screen and (max-width: 960px){
.searchbox-wrap {
padding-left: 48px;
position: absolute;
}
.gcse-searchbox-only {
}
}
@media screen and (max-width: 782px){
.searchbox-wrap {
top: 48px; /* 上部から配置の基準位置を決める */
padding-left: 0px;
height: 100px;
}
.searchbox-space {
height: 100px;
}
}
.gcse-searchbox-only {
}
}
</style>';
echo $search_box;
/* 入力文字数カウントの表示 */
chiilabo_daily_count_display_inner(2);
echo '<table class="widefat stats">';
// Populate the table
echo '<tbody>';
$count = 0;
$last_date = "tomorrow";
foreach ( $posts as $post ) {
if($count == 0) {
echo '<tr>';
}
$log = $post_id2log[$post->ID];
$pv_ave = ($log->pv / ceil($days[$post->ID]));
if (is_nan($pv_ave)) {
$pv_ave = 0;
}
$is_todo_post = 0;
if ($post->post_status == "publish") {
$categories = get_the_category($post->ID);
foreach ( $categories as $category) {
if ($category->term_id == 2458) {
$is_todo_post = 1;
}
}
} else {
$is_todo_post = 1;
}
if ($is_todo_post == 1) {
echo '<td class="container todo_post">';
} else {
echo '<td class="container">';
}
echo '<a href="'. get_permalink($post) . '" title="'. mb_substr($post->post_title, 0, 50) .'">';
$img = get_the_post_thumbnail_url($post->ID, 'thumbnail' );
echo '<img src="' .$img. '" alt="' . mb_substr($post->post_title, 0, 50) . '">';
$date = substr($post->post_modified, 0, 10);
if (strtotime($date) != false && floor(strtotime($date)/86400) < floor( strtotime($last_date)/86400)) {
echo '<p class="date">'.$date.'</p>';
$last_date = $date;
}
$content = $post->post_content;
$content = preg_replace('/<pre[\s\S]*?<\/pre>/','', $content);
$content = preg_replace('/<blockquote[\s\S]*?<\/blockquote>/','', $content);
$content = preg_replace('/\[amazon.*\]/','', $content);
$content = preg_replace('/\3/','', $content);
$content = strip_tags($content);
$content = preg_replace('/\s*/','', $content);
$char_count = mb_strlen($content);
echo '<p class="chars">'.$char_count.'</p>';
echo '<p class="pv">'. floor($pv_ave).'</p>';
$pv_rank = "";
if ($pv_ave >= 100) {
$pv_rank = "飛";
} else if ($pv_ave >= 40) {
$pv_rank = "角";
} else if ($pv_ave >= 15) {
$pv_rank = "金";
} else if ($pv_ave >= 12) {
$pv_rank = "銀";
} else if ($pv_ave >= 9) {
$pv_rank = "桂";
} else if ($pv_ave >= 6) {
$pv_rank = "香";
} else if ($pv_ave >= 2.5) {
$pv_rank = "歩";
} else {
$pv_rank = "";
}
echo '<p class="rank">'.$pv_rank.'</p>';
echo '</a>';
echo '</td>';
$count = $count+1;
if ($count == $row) {
echo '</tr>';
$count = 0;
}
}
echo '</tbody>';
echo '</table>';
}
/** 2023-05-06 データベース追加*/
global $chiilabo_db_version;
$chiilabo_db_version = '1.0';
define('CHIILABO_COUNT_TABLE_NAME', $wpdb->prefix . 'chiilabo_daily_count');
function chiilabo_db_install() {
global $wpdb;
global $chiilabo_db_version;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
count mediumint(9) NOT NULL,
diff mediumint(9) NOT NULL,
UNIQUE KEY id (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
add_option( 'chiilabo_db_version', $chiilabo_db_version );
}
function chiilabo_db_initial_data() {
global $wpdb;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->insert(
$table_name,
array(
'date' => "2023-05-05 00:00:00",
'count' => "2470000",
)
);
}
/** 2023-05-06 なぜかプラグイン有効化時に実行されない*/
register_activation_hook(__FILE__, 'chiilabo_db_install');
function delete_db() {
global $wpdb;
$table_name = CHIILABO_COUNT_TABLE_NAME;
$wpdb->query("DELETE FROM $table_name");
}
function add_count_to_db() {
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => array( 'publish', 'pending', 'draft', 'future'),
'orderby' => 'modified', /* date, modified */
'numberposts' => -1
) );
$count = 0;
foreach ( $posts as $post ) {
$content = $post->post_content;
$content = preg_replace('/<pre[\s\S]*<\/pre>/','', $content);
$content = preg_replace('/<blockquote[\s\S]*<\/blockquote>/','', $content);
$content = preg_replace('/\[amazon.*\]/','', $content);
$content = preg_replace('/\4/','', $content);
$content = strip_tags($content);
$content = preg_replace('/\s*/','', $content);
$char_count = mb_strlen($content);
$count += $char_count;
}
global $wpdb;
$table_name = CHIILABO_COUNT_TABLE_NAME;
date_default_timezone_set('Asia/Tokyo');
$date = date("Y-m-d H-i-s", strtotime("today"));
$wpdb->query("DELETE FROM $table_name WHERE date = '$date'");
$query = "
SELECT date, count, diff
FROM $table_name
ORDER BY date DESC
";
$results = $wpdb->get_results("SELECT date, count, diff FROM $table_name ORDER BY date DESC LIMIT 1");
$diff = $count - $results[0]->count;
$wpdb->insert(
$table_name,
array(
'date' => $date,
'count' => $count,
'diff' => $diff,
)
);
}
/** 日々の投稿文字数の表示*/
function chiilabo_daily_count_display(){
chiilabo_daily_count_display_inner(-1);
}
/** 日々の投稿文字数の表示*/
function chiilabo_daily_count_display_inner($num){
global $wpdb;
/* データベースのクリア用フラグ 通常はfalse*/
if (false) {
chiilabo_db_install();
delete_db();
chiilabo_db_initial_data();
}
add_count_to_db();
$table_name = CHIILABO_COUNT_TABLE_NAME;
if ($num>0) {
$limit = "LIMIT $num";
}
$query = "
SELECT date, count, diff
FROM $table_name
ORDER BY date DESC
$limit
";
// Retrieve the access logs
$results = $wpdb->get_results($query);
// Initialize the table
echo '<table class="a">';
echo '<thead><tr>';
echo '<th>日付</th>';
echo '<th>入力文字数</th>';
echo '</tr></thead>';
// Populate the table
echo '<tbody>';
foreach ( $results as $idx => $row) {
echo '<tr>';
echo '<td>' . substr($row->date, 0, 10) . '</td>';
echo '<td align="right">' . ( $row->diff) . '</td>';
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
}
![[WordPress]カスタムプラグインで投稿文字数の自動集計機能を追加した](https://chiilabo.com/wp-content/uploads/2023/05/image-3-16-320x198.jpg)
![[WordPress] 投稿月でグループ分けした月別PV集計表を見るためのカスタムプラグインを作った【ChatGPTと】](https://chiilabo.com/wp-content/uploads/2023/03/image-23-26-320x198.jpg)
![[WordPress] 「月の投稿ごとの集計」をするためにブログのパーマリンクを変更した【Search Regexプラグイン】](https://chiilabo.com/wp-content/uploads/2021/11/image-14-320x198.png)