かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

PHP 配列からいい感じに CSS のクラス名を作りたい

3億年ぶりに古の WordPress 製のサイトを更新する必要があり、PHP のテンプレートで CSS のクラス名を動的に作成する必要があった JavaScript で言うところの classnames のような挙動をさせたい

CSS のクラス

class="foo bar mofu" のような形

  • スペース区切りの文字列
  • クラス名が重複していても特に問題はない

配列から Falsey な値を取り除いて スペースで結合すれば良い

TypeScript での例

export const classnames = (
 ...args: (string | undefined | null | false)[]
): string => {
  if (!args.length) {
    return '';
  }
  return args.filter((value) => 
    value !== null && value !== undefined && value !== false && value !== ""
  ).join(' ').trim();
};

細かいことを抜きにしらこんな感じで実現できる
これに近いことを PHP で実装できれば OK

array_filter + implode で実現する

  1. array_filter で配列から Falsey な値を取り除く
  2. implode で配列をスペース区切りで文字列結合する
<?php declare(strict_types=1);
function classnames(array $arr): string {
  return implode(' ', array_filter($arr));
}

echo classnames(['ichigo', false, 'aoi', 0, 'ran', '', null, 'akari']);
// -> "ichigo aoi ran akari"

このコードには下記の問題があるが、ライブラリとして公開しているわけでもないのでヨシ!とした

  • 0"0" も Falsey な値として除去されてしまうが、そんな class 名を付けることはほぼ無い想定とした
  • 関数に型を設定しているが、PHP の形は array に含まれる要素の型を指定する方法がイマイチわからなかったので、文字列変換できないものが渡されるとクラッシュする可能性がある

array_filter Example #2 array_filter() without callback

<?php
$entry = [
    0 => 'foo',
    1 => false,
    2 => -1,
    3 => null,
    4 => '',
    5 => '0',
    6 => 0,
];
print_r(array_filter($entry));
// Array
// (
//     [0] => foo
//     [2] => -1
// )

cf. PHP: array_filter - Manual

うごいてるからヨシ!

おわり ฅ^•ω•^ฅ


[参考]

React TypeScript 改行が含まれれるテキストをいい感じに改行させたい

前にも同じようなことやったけど React.createElement を使った TypeScript 版を作ったのでメモ

import { ReactElement, createElement } from 'react';
const newlineRegex = /(\r\n|\r|\n)/g;

export const nl2br = (text: string): (string | ReactElement)[] => {
  return text.split(newlineRegex).map((line, index) => {
    if (line.match(newlineRegex)) {
      return createElement('br', { key: index });
    }
    return line;
  });
};

1 引数で渡された文字列 text に含まれる改行コードをキャプチャして分割

split(separator)
If separator is a regular expression that contains capturing parentheses ( ), matched results are included in the array.
cf. String.prototype.split() - JavaScript | MDN

"星宮いちご\n霧矢あおい\n紫吹蘭".split(/(\r\n|\r|\n)/g);
// => ['星宮いちご', '\n', '霧矢あおい', '\n', '紫吹蘭']

2 改行コードを createElement('br', { key: index }) で brタグ に変換して返す

createElement(type, props, ...children)
props: The props argument must either be an object or null. If you pass null, it will be treated the same as an empty object. React will create an element with props matching the props you have passed. Note that ref and key from your props object are special and will not be available as element.props.ref and element.props.key on the returned element. They will be available as element.ref and element.key.
cf. createElement – React

React.createElement の第二引数が props なので key を設定できる

やってることは同じだけど、前の React.Fragment を使う方法よりシンプルにかけた気がする

おわり ₍ ᐢ. ̫ .ᐢ ₎


[参考]

Node.js ファイル名から拡張子を取得したい

正引き表現や . で区切る方法を考えてたけど path を使えば簡単だった

path.extname(path) で拡張子を取得できる

path.extname で取得できる拡張子は . 始まりの文字列
拡張子が無い場合は空文字列が返される
サーチクエリがある場合は拡張子含まれる

import path from "path";

path.extname('index.html');
// -> '.html'
path.extname('index.coffee.md');
// -> '.md'
path.extname('index.');
// -> '.'
path.extname('index');
// -> ''
path.extname('.index');
// -> ''
path.extname('.index.md');
// -> '.md' 
path.extname('/aikatsu/idol/ichigo.md?foo=bar');
// -> '.md?foo=bar' 

cf. Path | Node.js v21.7.2 Documentation

ファイル名・拡張子を別々に取得したいときは path.parse(path) が便利

path.parse()root, dir (ファイルまでのパス), base (ファイル名 + 拡張子), name (ファイル名), ext (拡張子) のオブジェクトを返す

import path from "path";

path.parse('/home/user/dir/file.txt');
// { root: '/',
//   dir: '/home/user/dir',
//   base: 'file.txt',
//   name: 'file',
//   ext: '.txt' }

const pathData = path.parse('./aikatsu/idol/ichigo.md?foo=bar');
// { root: '',
//   dir: './aikatsu/idol',
//   base: 'ichigo.md?foo=bar',
//   name: 'ichigo',
//   ext: '.md?foo=bar' }

該当するものがない場合は空文字になる

おわり ₍ᐢ..ᐢ₎


[参考]

今年度もアイカツ!