PHP

掲題の通り、FPDI(+TCPDF)を使用してPDFを出力しているのだが、濁点・半濁点が表示されないとのご指摘を受けた。
↓こんな感じ(□←こんな四角いのが出てるのは使用中のフォントで未定義のためで、IPAフォントとか使えば濁点・半濁点は(2文字になって)表示されるはず。この四角いのって、「豆腐」と呼ばれていて、外国でも「TOFU」と呼ばれているらしい)。
dakuten_bf

◎解決方法

原理などはさておき、コードを貼っておきます。
class DakutenFixer {
const DAKUTEN_NFD = '゙'; // 濁点の「結合文字」
const HANDAKUTEN_NFD = '゚'; // 半濁点の「結合文字」
const DAKUTEN_HANKAKU = '゙';
const HANDAKUTEN_HANKAKU = '゚';
public static function fix($str) {
if (false !== mb_strpos($str, self::DAKUTEN_NFD, 0, 'utf8')
|| false !== mb_strpos($str, self::HANDAKUTEN_NFD, 0, 'utf8')) {
$str = str_replace(
array(self::DAKUTEN_NFD, self::HANDAKUTEN_NFD),
array(self::DAKUTEN_HANKAKU, self::HANDAKUTEN_HANKAKU),
$str);
// カタカナを処理する(これによって元々存在する半角カタカナも全角化されます)
$str = mb_convert_kana($str, 'k', 'utf8');
$str = mb_convert_kana($str, 'KV', 'utf8');
// ひらがなを処理する
$str = mb_convert_kana($str, 'h', 'utf8');
$str = mb_convert_kana($str, 'HV', 'utf8');
}
return $str;
}
}

「結合文字」のところの表示がおかしくなっているかと思いますが、気にせず範囲指定してコピペしてください。当方の環境では正しくコピペできました。(ペースト先でも表示がおかしくなるでしょうが、気にせず使ってみてください)
↓こんな感じで使ってください。
$fixed_str = DakutenFixer::fix($str);

先程の出力が、↓のように正常になる!…はずです(^o^;)
dakuten_af

◎事象のイキサツをつらつらと…

どうやらMac環境で入力された文字列が悪さ想定外の動きをしているらしい。
調べてみると、NFD形式??NFC形式?に変換すると良いらしい???
うーん、なんかね。
結合文字列と言って、データ的には2文字なんだけど見た目は1文字、な文字列があって、それがPDFでは出力できないみたい。いや、TCPDF側の問題なのかもしれないけど…詳しくは調べてません…あしからず(゚∀゚)
もうどうしようもないじゃんー、と思ったけど、世の中には解決方法を編み出してしまう天才が存在します(後述の「参照元」を参照のこと)。
結合文字列を構成する結合文字を探し出して、その1文字手前の文字を濁音化しようというもの。
なるほどねー。
でも、ひらがなカタカナを全文字並べて濁音との変換用配列を作っていて、なんとなくスマートでない感じ(こんなこと言ってごめんなさいぃぃm(_ _)m)。
濁音をつける関数とか存在しないのかねぇ…と思ったけど、、、
無 か っ た。
でもね、世の中には(ry
全角文字に濁点文字「゛」をつけてmb_convert_kana()を使って一旦半角化した後、「V」オプションを用いて全角化すると、濁音化した1文字になる、という理論。
これを応用して、先の「結合文字の濁点」を「半角の濁点文字」に変換した後、全体を半角化する。
すると結合文字列だった文字列は「半角文字+半角濁点」になるので、再度全角化すれば、1文字化完了!という算段です\(^o^)/

◎参照元

Macのファイル名(NFD形式)をNFC形式に変換するPHPのロジック
↑こちらでも同じ結果が得られると思われます。
というか、こちらの記事にインスパイアされて当記事ができたんだよね(^o^;)
こちらのほうが確実に期待したとおりに動くのではないかと…。
当方のはmb_convert_kana()に頼っているので、想定外の動きがある可能性もありですからね。。。

Unicode結合文字列の怪 Mac OS X & fish shell & JavaScript編
こちらに今回の事象の原因となる「結合文字列」について詳しく書いてあります。
そして、↑の記事の参照元↓で、、
Mac OS X は結合文字列だらけ

Macよ。なぜにそんな特殊なことをした…orz

PHP 半角/全角 変換 mb_convert_kana()の濁点半濁点処理の罠について | 或るエンジニアの trash logs - 10倍役に立たないweblog (再々)
こちらの記事のおかげでmb_convert_kana()の使用を思いつきました。
こちらに書かれている通り、「ヱ」などの異体字の情報は失われてしまいますが、まず使わないでしょうよ!
そして、半角カタカナに一旦変換するため、元々存在する半角カタカナと区別がつかなくなり、そちらも全角化してしまいます。でも、今時半角カタカナなんて使わないっしょ!!
json_encode()で「Malformed UTF-8 characters」となった話ですが、根本的には別のお話しです…。(初歩的ミスが原因)
が、もしかしたら同じ目に遭うヒトもいるかもしれないので、一助となればと思い、投稿します。

あらすじ
・ある配列をjson_encode()に渡した。
・falseが返ってきた。
・「Malformed UTF-8 characters」というエラー。
・配列内に「〜」という文字列があるのを発見。またコヤツの仕業か!と疑う
・さんざんググったが、原因わからず(´;ω;`)
・配列の要素を一つ一つ削除していって、エラー原因を突き止め…
・結論、「〜」は濡れ衣。m(_ _)m
・原因は、全角文字列に対してsubstr()を使用したため。
・文字の途中で切れた?のであれば「Malformed UTF-8 characters」となるのはマットウ。
・またしてもPEBCAK!

うーむ、あらすじとして書いたけど、これがすべてだな(^o^;)
というわけでおしまい。
こんなことで数時間も無駄にした…orz
Eclipseを使用していて、いつもお世話になっているコードアシスト(コード補完、Content Assist)機能だが、最近アシストが起動するたびプチフリーズする(10秒近く固まるのでプチとは言いたくないが…)ようになった。
ついには設定で自動起動をオフにまでしてしまったが、やはり使いたい。。
使うとフリーズ…orz
っという二律背反状態に陥ったため、根本的解決を図って改めて調査したところ、下記記事を発見。
Q.Eclipse で コード補完 や 定義ジャンプ をしようとすると エラーになったり, Eclipse が固まったりする.
(中略)
A.ワークスペースの .metadata/.plugins/org.eclipse.dltk.core.index.sql.h2 ディレクトリ内のファイルをすべて削除し, Eclipse を再起動する.

ありがたや〜。これで治りました\(^o^)/

っでも、なんかこの作業、以前にもしたような気がするなぁ…。
もしかして、自分でも記事にしてたりしないよね…コワいので調べないけど(^o^;)
ctype_alnum 英数字判定
ctype_alpha 英字判定
ctype_cntrl 制御文字判定
ctype_digit 数字判定
ctype_graph 空白以外の印字可能文字判定
ctype_lower 小文字判定
ctype_print 印字可能文字判定 *空白含む
ctype_punct 空白及び英数字以外文字判定 *ざっくりいうと記号判定
ctype_space 空白文字判定 *空白文字なので空白以外にもタブとか改行とかも含まれる
ctype_upper 大文字判定
ctype_xdigit 16進数判定


整数値を渡すとASCII値として処理されたり、ちょっとひとクセあるみたいだけど、処理は速そう。
覚えておこう(`・ω・´)

…大抵忘れる訳だがorz
例えば、今日(2018年4月13日)下記を実行すると
$time = strtotime('next month');
echo date('Y/m/d', $time);
「2018/05/13」と表示される。
また、今日だけではなくて任意の日時を指定すると
$time1 = mktime(4, 32, 10, 6, 5, 1987);
$time2 = strtotime('next month', $time1);
echo date('Y/m/d', $time1) . ' -> ' . date('Y/m/d', $time2);
「1987/06/05 -> 1987/07/05」と表示される。

とまあ、strtotime('next month')は翌月の同日を返してくれて便利な訳だが、、
そう、翌月の同日、ということで、該当する日付が無い場合はどうなる?
$time1 = mktime(0, 0, 0, 3, 31, 2018);
$time2 = strtotime('next month', $time1);
echo date('Y/m/d', $time1) . ' -> ' . date('Y/m/d', $time2);
「2018/03/31 -> 2018/05/01」と表示される!
3月31日の翌月同日は、広義の4月31日となるが、実在しないため、4月30日+1日と解釈されて5月1日となる。
っということですね(-_-;)

ということは…1月31日だと
$time1 = mktime(0, 0, 0, 4, 31, 2018);
$time2 = strtotime('next month', $time1);
echo date('Y/m/d', $time1) . ' -> ' . date('Y/m/d', $time2);
「2018/01/31 -> 2018/03/03」と表示される。やはり、2月31日→2月28日+3日→3月3日ってことですね…。

うーん、こうなるということを覚えておかないと、将来罠にハマりそうだ。
こんな記事を書いたことすら忘れてそうだが…(;´Д`)

strtotimeでは、他にも柔軟な書式に対応しているので、下記リファレンスは一読の価値ありかと。
PHP: 相対的な書式 - Manual

例えば、「来月の第3水曜日」とか