[N] ryunosuke\Functions\Package\

[G] constants

[C] IS_OWNSELF 自分自身を表す定数

自分自身を表す定数
const int IS_OWNSELF = 2

[C] IS_PUBLIC public を表す定数

public を表す定数
const int IS_PUBLIC = 4

[C] IS_PROTECTED protected を表す定数

protected を表す定数
const int IS_PROTECTED = 8

[C] IS_PRIVATE private を表す定数

private を表す定数
const int IS_PRIVATE = 16

[C] EN_MONTH_SHORT 月名(省略)

月名(省略)
const array EN_MONTH_SHORT = [ 1 => "jan", 2 => "feb", 3 => "mar", 4 => "apr", 5 => "may", 6 => "jun", 7 => "jul", 8 => "aug", 9 => "sep", 10 => "oct", 11 => "nov", 12 => "dec", ]

[C] EN_MONTH_LONG 月名(フル)

月名(フル)
const array EN_MONTH_LONG = [ 1 => "january", 2 => "february", 3 => "march", 4 => "april", 5 => "may", 6 => "june", 7 => "july", 8 => "august", 9 => "september", 10 => "october", 11 => "november", 12 => "december", ]

[C] JP_ERA 和暦

和暦
const array JP_ERA = [ [ "name" => "令和", "abbr" => "R", "since" => 1556636400, ], [ "name" => "平成", "abbr" => "H", "since" => 600188400, ], [ "name" => "昭和", "abbr" => "S", "since" => -1357635600, ], [ "name" => "大正", "abbr" => "T", "since" => -1812186000, ], [ "name" => "明治", "abbr" => "M", "since" => -3216790800, ], ]

[C] GENERAL_MIMETYPE 一般的な mimetype

一般的な mimetype
const array GENERAL_MIMETYPE = [ "csv" => "text/csv", "dcm" => "application/dicom", "dvc" => "application/dvcs", "finf" => "application/fastinfoset", "stk" => "application/hyperstudio", "ipfix" => "application/ipfix", "json" => "application/json", "mrc" => "application/marc", "nb" => "application/mathematica", "ma" => "application/mathematica", "mb" => "application/mathematica", "mbox" => "application/mbox", "m21" => "application/mp21", "mp21" => "application/mp21", "xls" => "application/vnd.ms-excel", "doc" => "application/vnd.ms-word", "mxf" => "application/mxf", "oda" => "application/oda", "ogx" => "application/ogg", "pdf" => "application/pdf", "p10" => "application/pkcs10", "ai" => "application/postscript", "eps" => "application/postscript", "ps" => "application/postscript", "rtf" => "application/rtf", "sdp" => "application/sdp", "siv" => "application/sieve", "sieve" => "application/sieve", "smil" => "application/smil", "smi" => "application/smil", "sml" => "application/smil", "gram" => "application/srgs", "xml" => "text/xml", "zip" => "application/x-zip-compressed", "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 726 => "audio/32kadpcm", "amr" => "audio/AMR", "at3" => "audio/ATRAC3", "aa3" => "audio/ATRAC3", "omg" => "audio/ATRAC3", "evc" => "audio/EVRC", "evb" => "audio/EVRCB", "evw" => "audio/EVRCWB", "l16" => "audio/L16", "smv" => "audio/SMV", "ac3" => "audio/ac3", "au" => "audio/basic", "snd" => "audio/basic", "dls" => "audio/dls", "lbc" => "audio/iLBC", "mid" => "audio/midi", "midi" => "audio/midi", "kar" => "audio/midi", "mpga" => "audio/mpeg", "mp1" => "audio/mpeg", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "oga" => "audio/ogg", "ogg" => "audio/ogg", "spx" => "audio/ogg", "qcp" => "audio/qcelp", "bmp" => "image/bmp", "fits" => "image/fits", "fit" => "image/fits", "fts" => "image/fits", "gif" => "image/gif", "ief" => "image/ief", "jp2" => "image/jp2", "jpg2" => "image/jp2", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "jpe" => "image/jpeg", "jfif" => "image/jpeg", "jpm" => "image/jpm", "jpgm" => "image/jpm", "jpx" => "image/jpx", "jpf" => "image/jpx", "svg" => "image/svg+xml", "png" => "image/png", "t38" => "image/t38", "tiff" => "image/tiff", "tif" => "image/tiff", "u8msg" => "message/global", "eml" => "message/rfc822", "mail" => "message/rfc822", "art" => "message/rfc822", "igs" => "model/iges", "iges" => "model/iges", "msh" => "model/mesh", "mesh" => "model/mesh", "silo" => "model/mesh", "wrl" => "model/vrml", "vrml" => "model/vrml", "ics" => "text/calendar", "ifb" => "text/calendar", "css" => "text/css", "soa" => "text/dns", "zone" => "text/dns", "html" => "text/html", "htm" => "text/html", "js" => "text/javascript", "asc" => "text/plain", "txt" => "text/plain", "text" => "text/plain", "pm" => "text/plain", "el" => "text/plain", "c" => "text/plain", "h" => "text/plain", "cc" => "text/plain", "hh" => "text/plain", "cxx" => "text/plain", "hxx" => "text/plain", "f90" => "text/plain", "rtx" => "text/richtext", "sgml" => "text/sgml", "sgm" => "text/sgml", "3gp" => "video/3gpp", "3gpp" => "video/3gpp", "3g2" => "video/3gpp2", "3gpp2" => "video/3gpp2", "mj2" => "video/mj2", "mjp2" => "video/mj2", "mp4" => "video/mp4", "mpg4" => "video/mp4", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpe" => "video/mpeg", "ogv" => "video/ogg", "qt" => "video/quicktime", "mov" => "video/quicktime", "webm" => "video/webm", ]

[C] GLOB_RECURSIVE glob 系関数で ** を有効にするか

glob 系関数で ** を有効にするか
const int GLOB_RECURSIVE = 65536

[C] JSON_MAX_DEPTH json_*** 関数で $depth 引数を表す定数

json_*** 関数で $depth 引数を表す定数
const int JSON_MAX_DEPTH = -1

[C] JSON_INDENT json_*** 関数でインデント数・文字を指定する定数

json_*** 関数でインデント数・文字を指定する定数
const int JSON_INDENT = -71

[C] JSON_CLOSURE json_*** 関数でクロージャをサポートするかの定数

json_*** 関数でクロージャをサポートするかの定数
const int JSON_CLOSURE = -72

[C] JSON_NEST_LEVEL json_*** 関数で初期ネストレベルを指定する定数

json_*** 関数で初期ネストレベルを指定する定数
const int JSON_NEST_LEVEL = -73

[C] JSON_INLINE_LEVEL json_*** 関数で一定以上の階層をインライン化するかの定数

json_*** 関数で一定以上の階層をインライン化するかの定数
const int JSON_INLINE_LEVEL = -74

[C] JSON_INLINE_SCALARLIST json_*** 関数でスカラーのみのリストをインライン化するかの定数

json_*** 関数でスカラーのみのリストをインライン化するかの定数
const int JSON_INLINE_SCALARLIST = -75

[C] JSON_ES5 json_*** 関数で json5 を取り扱うかの定数

json_*** 関数で json5 を取り扱うかの定数
const int JSON_ES5 = -100

[C] JSON_INT_AS_STRING json_*** 関数で整数を常に文字列で返すかの定数

json_*** 関数で整数を常に文字列で返すかの定数
const int JSON_INT_AS_STRING = -101

[C] JSON_FLOAT_AS_STRING json_*** 関数で小数を常に文字列で返すかの定数

json_*** 関数で小数を常に文字列で返すかの定数
const int JSON_FLOAT_AS_STRING = -102

[C] JSON_TRAILING_COMMA json_*** 関数で強制ケツカンマを振るかの定数

json_*** 関数で強制ケツカンマを振るかの定数
const int JSON_TRAILING_COMMA = -103

[C] JSON_COMMENT_PREFIX json_*** 関数でコメントを判定するプレフィックス定数

json_*** 関数でコメントを判定するプレフィックス定数
const int JSON_COMMENT_PREFIX = -104

[C] JSON_TEMPLATE_LITERAL json_*** 関数でテンプレートリテラルを有効にするかの定数

json_*** 関数でテンプレートリテラルを有効にするかの定数
const int JSON_TEMPLATE_LITERAL = -105

[C] JSON_BARE_AS_STRING json_*** 関数で bare string を文字列として扱うか

json_*** 関数で bare string を文字列として扱うか
const int JSON_BARE_AS_STRING = -106

[C] JSON_OBJECT_HANDLER json_*** 関数で特定クラスの独自変換の定数

json_*** 関数で特定クラスの独自変換の定数
const int JSON_OBJECT_HANDLER = -107

[C] JSON_ESCAPE_SINGLE_QUOTE json_*** 関数で ' でエスケープシーケンスを扱うか

json_*** 関数で ' でエスケープシーケンスを扱うか
const int JSON_ESCAPE_SINGLE_QUOTE = -108

[C] SI_UNITS

const array SI_UNITS = [ -8 => ["y"], -7 => ["z"], -6 => ["a"], -5 => ["f"], -4 => ["p"], -3 => ["n"], -2 => ["u", "μ", "µ"], -1 => ["m"], 0 => [], 1 => ["k", "K"], 2 => ["M"], 3 => ["G"], 4 => ["T"], 5 => ["P"], 6 => ["E"], 7 => ["Z"], 8 => ["Y"], ]

[C] SORT_STRICT SORT_XXX 定数の厳密版

SORT_XXX 定数の厳密版
const int SORT_STRICT = 256

[N] ryunosuke\Functions\Package\array\

[F] functions

[F] array_add 配列の+演算子の関数版

配列の+演算子の関数版
function array_add(array ...$variadic): array

Example:

// ただの加算の関数版なので同じキーは上書きされない that(array_add(['a', 'b', 'c'], ['X']))->isSame(['a', 'b', 'c']); // 異なるキーは生える that(array_add(['a', 'b', 'c'], ['x' => 'X']))->isSame(['a', 'b', 'c', 'x' => 'X']);
Parameter
type name summary
​array ...$variadic 足す配列(可変引数)
Return
type summary
​array 足された配列

[F] array_aggregate 配列をコールバックの返り値で集計する

配列をコールバックの返り値で集計する
function array_aggregate( iterable $array, callable|callable[] $columns, string|array|null $key = null ): array

$columns で集計列を指定する。 単一の callable を渡すと結果も単一になる。 複数の callable 連想配列を渡すと [キー => 集系列] の連想配列になる。 いずれにせよ引数としてそのグループの配列が渡ってくるので返り値がその列の値になる。 第2引数には「今までの結果が詰まった配列」が渡ってくる(count, avg, sum など何度もでてくる集計で便利)。

$key で集約列を指定する。 指定しなければ引数の配列そのままで集計される。 複数要素の配列を与えるとその数分潜って集計される。 クロージャを与えると返り値がキーになる。

Example:

// 単純な配列の集計 that(array_aggregate([1, 2, 3], [ 'min' => fn($elems) => min($elems), 'max' => fn($elems) => max($elems), 'avg' => fn($elems) => array_sum($elems) / count($elems), ]))->isSame([ 'min' => 1, // 最小値 'max' => 3, // 最大値 'avg' => 2, // 平均値 ]); $row1 = ['user_id' => 'hoge', 'group' => 'A', 'score' => 4]; $row2 = ['user_id' => 'fuga', 'group' => 'B', 'score' => 6]; $row3 = ['user_id' => 'fuga', 'group' => 'A', 'score' => 5]; $row4 = ['user_id' => 'hoge', 'group' => 'A', 'score' => 8]; // user_id, group ごとの score を集計して階層配列で返す(第2引数 $current を利用している) that(array_aggregate([$row1, $row2, $row3, $row4], [ 'scores' => fn($rows) => array_column($rows, 'score'), 'score' => fn($rows, $current) => array_sum($current['scores']), ], ['user_id', 'group']))->isSame([ 'hoge' => [ 'A' => [ 'scores' => [4, 8], 'score' => 12, ], ], 'fuga' => [ 'B' => [ 'scores' => [6], 'score' => 6, ], 'A' => [ 'scores' => [5], 'score' => 5, ], ], ]); // user_id ごとの score を集計して単一列で返す(キーのクロージャも利用している) that(array_aggregate([$row1, $row2, $row3, $row4], fn($rows) => array_sum(array_column($rows, 'score')), fn($row) => strtoupper($row['user_id'])))->isSame([ 'HOGE' => 12, 'FUGA' => 11, ]);
Parameter
type name summary
​iterable $array 対象配列
​callable|​callable[] $columns 集計関数
​string|​array|​null $key = null 集約列。クロージャを与えると返り値がキーになる
Return
type summary
​array 集約配列

[F] array_all 全要素が true になるなら true を返す(1つでも false なら false を返す)

全要素が true になるなら true を返す(1つでも false なら false を返す)
function array_all( $array, $callback = null, $default = true )
Parameter
type name summary
$array
$callback = null
$default = true
See
type summary
array_and()

[F] array_and 全要素が true になるなら true を返す(1つでも false なら false を返す)

全要素が true になるなら true を返す(1つでも false なら false を返す)
function array_and( iterable $array, ?callable $callback = null, bool|mixed $default = true ): bool

$callback が要求するならキーも渡ってくる。

Example:

that(array_and([true, true]))->isTrue(); that(array_and([true, false]))->isFalse(); that(array_and([false, false]))->isFalse();
Parameter
type name summary
​iterable $array 対象配列
​?callable $callback = null 評価クロージャ。 null なら値そのもので評価
​bool|​mixed $default = true 空配列の場合のデフォルト値
Return
type summary
​bool 全要素が true なら true

[F] array_any 全要素が false になるなら false を返す(1つでも true なら true を返す)

全要素が false になるなら false を返す(1つでも true なら true を返す)
function array_any( $array, $callback = null, $default = false )
Parameter
type name summary
$array
$callback = null
$default = false
See
type summary
array_or()

[F] array_append 配列の末尾に要素を追加する

配列の末尾に要素を追加する
function array_append( array $array, $value, $key = null ): array

array_push のキーが指定できる参照渡しでない版と言える。 キー指定でかつそのキーが存在するとき、値を変えつつ末尾に移動する動作となる。

Example:

// キー未指定は言語機構を利用して末尾に追加される that(array_append([1, 2, 3], 99))->is([1, 2, 3, 99]); // キーを指定すればそのキーで生える that(array_append([1, 2, 3], 99, 'newkey'))->is([1, 2, 3, 'newkey' => 99]); // 存在する場合は値が変わって末尾に移動する that(array_append([1, 2, 3], 99, 1))->is([0 => 1, 2 => 3, 1 => 99]);
Parameter
type name summary
​array $array 対象配列
$value
$key = null
Return
type summary
​array 要素が追加された配列

[F] array_assort 配列をコールバックに従って分類する

配列をコールバックに従って分類する
function array_assort( iterable $array, callable[] $rules ): array

コールバックは配列で複数与える。そのキーが結果配列のキーになるが、一切マッチしなくてもキー自体は作られる。 複数のコールバックにマッチしたらその分代入されるし、どれにもマッチしなければ代入されない。 つまり5個の配列を分類したからと言って、全要素数が5個になるとは限らない(多い場合も少ない場合もある)。

$rule が要求するならキーも渡ってくる。

Example:

// lt2(2より小さい)で分類 $lt2 = fn($v) => $v < 2; that(array_assort([1, 2, 3], [ 'lt2' => $lt2, ]))->isSame([ 'lt2' => [1], ]); // lt3(3より小さい)、ctd(ctype_digit)で分類(両方に属する要素が存在する) $lt3 = fn($v) => $v < 3; that(array_assort(['1', '2', '3'], [ 'lt3' => $lt3, 'ctd' => 'ctype_digit', ]))->isSame([ 'lt3' => ['1', '2'], 'ctd' => ['1', '2', '3'], ]);
Parameter
type name summary
​iterable $array 対象配列
​callable[] $rules 分類ルール。[key => callable] 形式
Return
type summary
​array 分類された新しい配列

[F] array_columns 全要素に対して array_column する

全要素に対して array_column する
function array_columns( array $array, string|array|null $column_keys = null, mixed $index_key = null ): array

行列が逆転するイメージ。

Example:

$row1 = ['id' => 1, 'name' => 'A']; $row2 = ['id' => 2, 'name' => 'B']; $rows = [$row1, $row2]; that(array_columns($rows))->isSame(['id' => [1, 2], 'name' => ['A', 'B']]); that(array_columns($rows, 'id'))->isSame(['id' => [1, 2]]); that(array_columns($rows, 'name', 'id'))->isSame(['name' => [1 => 'A', 2 => 'B']]);
Parameter
type name summary
​array $array 対象配列
​string|​array|​null $column_keys = null 引っ張ってくるキー名
​mixed $index_key = null 新しい配列のキーとなるキー名
Return
type summary
​array 新しい配列

[F] array_convert 配列の各要素に再帰的にコールバックを適用して変換する

配列の各要素に再帰的にコールバックを適用して変換する
function array_convert( array $array, callable $callback, bool $apply_array = false ): array

$callback は下記の仕様。

引数は (キー, 値, 今まで処理したキー配列) で渡ってくる。 返り値は新しいキーを返す。

  • 文字列や数値を返すとそれがキーとして使われる
  • null を返すと元のキーがそのまま使われる
  • true を返すと数値連番が振られる
  • false を返すとその要素は無かったことになる
  • 配列を返すとその配列で完全に置換される

$apply_array=false で要素が配列の場合は再帰され、コールバックが適用されない(array_walk_recursive と同じ仕様)。

$apply_array=true だと配列かは問わず全ての要素にコールバックが適用される。 配列も渡ってきてしまうのでコールバック内部で is_array 判定が必要になる場合がある。

「map も filter も可能でキー変更可能かつ再帰的」というとてもマッチョな関数。 複雑だが実質的には「キーも設定できる array_walk_recursive」のように振る舞う(そしてそのような使い方を想定している)。

Example:

$array = [ 'k1' => 'v1', 'k2' => [ 'k21' => 'v21', 'k22' => [ 'k221' => 'v221', 'k222' => 'v222', ], 'k23' => 'v23', ], ]; // 全要素に 'prefix-' を付与する。キーには '_' をつける。ただし 'k21' はそのままとする。さらに 'k22' はまるごと伏せる。 'k23' は数値キーになる $callback = function ($k, &$v) { if ($k === 'k21') return null; if ($k === 'k22') return false; if ($k === 'k23') return true; if (!is_array($v)) $v = "prefix-$v"; return "_$k"; }; that(array_convert($array, $callback, true))->isSame([ '_k1' => 'prefix-v1', '_k2' => [ 'k21' => 'v21', 0 => 'v23', ], ]);
Parameter
type name summary
​array $array 対象配列
​callable $callback 適用するコールバック
​bool $apply_array = false 配列要素にもコールバックを適用するか
Return
type summary
​array 変換された配列

[F] array_count 配列をコールバックに従ってカウントする

配列をコールバックに従ってカウントする
function array_count( iterable $array, callable $callback, bool $recursive = false ): int|array

コールバックが true 相当を返した要素をカウントして返す。 普通に使う分には count(array_filter($array, $callback)) とほとんど同じだが、下記の点が微妙に異なる。

  • $callback が要求するならキーも渡ってくる
  • $callback には配列が渡せる。配列を渡した場合は件数を配列で返す(Example 参照)

$recursive に true を渡すと再帰的に動作する。 末端・配列を問わずに呼び出されるので場合によっては is_array などの判定が必要になる。

Example:

$array = ['hoge', 'fuga', 'piyo']; // 'o' を含むものの数(2個) that(array_count($array, fn($s) => strpos($s, 'o') !== false))->isSame(2); // 'a' と 'o' を含むものをそれぞれ(1個と2個) that(array_count($array, [ 'a' => fn($s) => strpos($s, 'a') !== false, 'o' => fn($s) => strpos($s, 'o') !== false, ]))->isSame([ 'a' => 1, 'o' => 2, ]); // 再帰版 $array = [ ['1', '2', '3'], ['a', 'b', 'c'], ['X', 'Y', 'Z'], [[[['a', 'M', 'Z']]]], ]; that(array_count($array, [ 'lower' => fn($v) => !is_array($v) && ctype_lower($v), 'upper' => fn($v) => !is_array($v) && ctype_upper($v), 'array' => fn($v) => is_array($v), ], true))->is([ 'lower' => 4, // 小文字の数 'upper' => 5, // 大文字の数 'array' => 7, // 配列の数 ]);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback カウントルール。配列も渡せる
​bool $recursive = false 再帰フラグ
Return
type summary
​int|​array 条件一致した件数

[F] array_cross 配列の直積を返す

配列の直積を返す
function array_cross(array ...$arrays): array

文字キーは保存されるが数値キーは再割り振りされる。 ただし、文字キーが重複すると例外を投げる。

Example:

// 普通の直積 that(array_cross( [1, 2], [3, 4] ))->isSame([[1, 3], [1, 4], [2, 3], [2, 4]]); // キーが維持される that(array_cross( ['a' => 1, 2], ['b' => 3, 4] ))->isSame([['a' => 1, 'b' => 3], ['a' => 1, 4], [2, 'b' => 3], [2, 4]]);
Parameter
type name summary
​array ...$arrays 対象配列(可変引数)
Return
type summary
​array 各配列値の直積

[F] array_depth 配列の次元数を返す

配列の次元数を返す
function array_depth( array $array, int|null $max_depth = null ): int

フラット配列は 1 と定義する。 つまり、配列を与える限りは 0 以下を返すことはない。

第2引数 $max_depth を与えるとその階層になった時点で走査を打ち切る。 「1階層のみか?」などを調べるときは指定したほうが高速に動作する。

Example:

that(array_depth([]))->isSame(1); that(array_depth(['hoge']))->isSame(1); that(array_depth([['nest1' => ['nest2']]]))->isSame(3);
Parameter
type name summary
​array $array 調べる配列
​int|​null $max_depth = null 最大階層数
Return
type summary
​int 次元数。素のフラット配列は 1

[F] array_difference 配列の差分を取り配列で返す

配列の差分を取り配列で返す
function array_difference( iterable $array1, iterable $array2, string $delimiter = "." ): array

返り値の配列は構造化されたデータではない。 主に文字列化して出力することを想定している。

ユースケースとしては「スキーマデータ」「各環境の設定ファイル」などの差分。

  • '+' はキーが追加されたことを表す
  • '-' はキーが削除されたことを表す
  • 両方が含まれている場合、値の変更を表す

数値キーはキーの比較は行われない。値の差分のみ返す。

Example:

// common は 中身に差分がある。 1 に key1 はあるが、 2 にはない。2 に key2 はあるが、 1 にはない。 that(array_difference([ 'common' => [ 'sub' => [ 'x' => 'val', ] ], 'key1' => 'hoge', 'array' => ['a', 'b', 'c'], ], [ 'common' => [ 'sub' => [ 'x' => 'VAL', ] ], 'key2' => 'fuga', 'array' => ['c', 'd', 'e'], ]))->isSame([ 'common.sub.x' => ['-' => 'val', '+' => 'VAL'], 'key1' => ['-' => 'hoge'], 'array' => ['-' => ['a', 'b'], '+' => ['d', 'e']], 'key2' => ['+' => 'fuga'], ]);
Parameter
type name summary
​iterable $array1 対象配列1
​iterable $array2 対象配列2
​string $delimiter = "." 差分配列のキー区切り文字
Return
type summary
​array 差分を表す配列

[F] array_distinct 比較関数が渡せる array_unique

比較関数が渡せる array_unique
function array_distinct( iterable $array, callable|int|string|null $comparator = null ): array

array_unique は微妙に癖があるのでシンプルに使いやすくしたもの。

  • SORT_STRING|SORT_FLAG_CASE のような指定が使える(大文字小文字を無視した重複除去)
    • 厳密に言えば array_unique も指定すれば動く(が、ドキュメントに記載がない)
  • 配列を渡すと下記の動作になる
    • 数値キーは配列アクセス
    • 文字キーはメソッドコール(値は引数)
  • もちろん($a, $b を受け取る)クロージャも渡せる
  • 引数1つのクロージャを渡すとシュワルツ的動作になる(Example 参照)

Example:

// シンプルな重複除去 that(array_distinct([1, 2, 3, '3']))->isSame([1, 2, 3]); // 大文字小文字を無視した重複除去 that(array_distinct(['a', 'b', 'A', 'B'], SORT_STRING|SORT_FLAG_CASE))->isSame(['a', 'b']); $v1 = new \ArrayObject(['id' => '1', 'group' => 'aaa']); $v2 = new \ArrayObject(['id' => '2', 'group' => 'bbb', 'dummy' => 123]); $v3 = new \ArrayObject(['id' => '3', 'group' => 'aaa', 'dummy' => 456]); $v4 = new \ArrayObject(['id' => '4', 'group' => 'bbb', 'dummy' => 789]); // クロージャを指定して重複除去 that(array_distinct([$v1, $v2, $v3, $v4], fn($a, $b) => $a['group'] <=> $b['group']))->isSame([$v1, $v2]); // 単純な配列アクセスなら文字列や配列でよい(上記と同じ結果になる) that(array_distinct([$v1, $v2, $v3, $v4], 'group'))->isSame([$v1, $v2]); // 文字キーの配列はメソッドコールになる(ArrayObject::count で重複検出) that(array_distinct([$v1, $v2, $v3, $v4], ['count' => []]))->isSame([$v1, $v2]); // 上記2つは混在できる(group キー + count メソッドで重複検出。端的に言えば "aaa+2", "bbb+3", "aaa+3", "bbb+3" で除去) that(array_distinct([$v1, $v2, $v3, $v4], ['group', 'count' => []]))->isSame([$v1, $v2, 2 => $v3]); // 引数1つのクロージャ that(array_distinct([$v1, $v2, $v3, $v4], fn($ao) => $ao['group']))->isSame([$v1, $v2]);
Parameter
type name summary
​iterable $array 対象配列
​callable|​int|​string|​null $comparator = null 比較関数
Return
type summary
​array 重複が除去された配列

[F] array_dive パス形式で配列値を取得

パス形式で配列値を取得
function array_dive( array|\ArrayAccess $array, string|array $path, mixed $default = null, string $delimiter = "." ): mixed

存在しない場合は $default を返す。

Example:

$array = [ 'a' => [ 'b' => [ 'c' => 'vvv' ] ] ]; that(array_dive($array, 'a.b.c'))->isSame('vvv'); that(array_dive($array, 'a.b.x', 9))->isSame(9); // 配列を与えても良い。その場合 $delimiter 引数は意味をなさない that(array_dive($array, ['a', 'b', 'c']))->isSame('vvv');
Parameter
type name summary
​array|​ArrayAccess $array 調べる配列
​string|​array $path パス文字列。配列も与えられる
​mixed $default = null 無かった場合のデフォルト値
​string $delimiter = "." パスの区切り文字。大抵は '.' か '/'
Return
type summary
​mixed パスが示す配列の値

[F] array_each array_reduce の参照版(のようなもの)

array_reduce の参照版(のようなもの)
function array_each( iterable $array, callable $callback, mixed $default = null ): mixed

配列をループで回し、その途中経過、値、キー、連番をコールバック引数で渡して最終的な結果を返り値として返す。 array_reduce と少し似てるが、下記の点が異なる。

  • いわゆる $carry は返り値で表すのではなく、参照引数で表す
  • 値だけでなくキー、連番も渡ってくる
  • 巨大配列の場合でも速度劣化が少ない(array_reduce に巨大配列を渡すと実用にならないレベルで遅くなる)

$callback の引数は ($value, $key, $n) ($n はキーとは関係がない 0 ~ 要素数-1 の通し連番)。

返り値ではなく参照引数なので return する必要はない(ワンライナーが書きやすくなる)。 返り値が空くのでループ制御に用いる。 今のところ $callback が false を返すとそこで break するのみ。

第3引数を省略した場合、クロージャの第1引数のデフォルト値が使われる これは特筆すべき動作で、不格好な第3引数を完全に省略することができる(サンプルコードを参照)。 ただし「php の文法違反(今のところエラーにはならないし、全てにデフォルト値をつければ一応回避可能)」「リフレクションを使う(ほんの少し遅くなる)」などの弊害が有るので推奨はしない。 (ただ、「意図していることをコードで表す」といった観点ではこの記法の方が正しいとも思う)。

Example:

// 全要素を文字列的に足し合わせる that(array_each([1, 2, 3, 4, 5], function (&$carry, $v) {$carry .= $v;}, ''))->isSame('12345'); // 値をキーにして要素を2乗値にする that(array_each([1, 2, 3, 4, 5], function (&$carry, $v) {$carry[$v] = $v * $v;}, []))->isSame([ 1 => 1, 2 => 4, 3 => 9, 4 => 16, 5 => 25, ]); // 上記と同じ。ただし、3 で break する that(array_each([1, 2, 3, 4, 5], function (&$carry, $v, $k){ if ($k === 3) return false; $carry[$v] = $v * $v; }, []))->isSame([ 1 => 1, 2 => 4, 3 => 9, ]); // 下記は完全に同じ(第3引数の代わりにデフォルト引数を使っている) that(array_each([1, 2, 3], function (&$carry = [], $v = null) { $carry[$v] = $v * $v; }))->isSame(array_each([1, 2, 3], function (&$carry, $v) { $carry[$v] = $v * $v; }, []) // 個人的に↑のようなぶら下がり引数があまり好きではない(クロージャを最後の引数にしたい) );
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 評価クロージャ。(&$carry, $key, $value) を受ける
​mixed $default = null ループの最初や空の場合に適用される値
Return
type summary
​mixed each した結果

[F] array_explode 配列を指定条件で分割する

配列を指定条件で分割する
function array_explode( iterable $array, mixed $condition, int $limit = PHP_INT_MAX ): array

文字列の explode を更に一階層掘り下げたイメージ。 $condition で指定された要素は結果配列に含まれない。

$condition にはクロージャが指定できる。クロージャの場合は true 相当を返した場合に分割要素とみなされる。 引数は (値, キー)の順番。

$limit に負数を与えると「その絶対値-1までを結合したものと残り」を返す。 端的に言えば「正数を与えると後詰めでその個数で返す」「負数を与えると前詰めでその(絶対値)個数で返す」という動作になる。

Example:

// null 要素で分割 that(array_explode(['a', null, 'b', 'c'], null))->isSame([['a'], [2 => 'b', 3 => 'c']]); // クロージャで分割(大文字で分割) that(array_explode(['a', 'B', 'c', 'D', 'e'], fn($v) => ctype_upper($v)))->isSame([['a'], [2 => 'c'], [4 => 'e']]); // 負数指定 that(array_explode(['a', null, 'b', null, 'c'], null, -2))->isSame([[0 => 'a', 1 => null, 2 => 'b'], [4 => 'c']]);
Parameter
type name summary
​iterable $array 対象配列
​mixed $condition 分割条件
​int $limit = PHP_INT_MAX 最大分割数
Return
type summary
​array 分割された配列

[F] array_extend 独自拡張した array_replace_recursive

独自拡張した array_replace_recursive
function array_extend( array|bool $default = [], iterable|\Closure ...$arrays ): array

下記の点で array_replace_recursive と異なる。

  • 元配列のキーのみでフィルタされる
  • 対象配列にクロージャを渡すと現在の状態を引数にしてコールバックされる
  • 値に Generator や Generator 関数を渡すと最後にまとめて値化される
    • つまり、上書きされた値は実質的にコールされない
  • recursive かどうかは 最初の引数で分岐する(jQuery の extend と同じ)
  • recursive の場合、元配列の値が配列の場合のみ対象となる。配列でない場合は単純に上書きされる
    • 次の値が非配列の場合、末尾に追加される
    • 次の値がクロージャの場合、元の値を引数にしてコールバックされる
    • 次の値が配列の場合
      • 元配列が数値配列なら完全にマージされる
      • 元配列が連想配列なら再帰される

この仕様上、Generator を値とする配列を対象にすることはできないが、そのような状況は稀だろう。 その代わり、使われるかどうか分からない状態でもとりあえず Generator にしておけば無駄な処理を省くことができる。

Example:

# $deep ではない単純呼び出し that(array_extend([ 'overwrite' => 'hoge', // 後段で指定されているので上書きされる 'through' => 'fuga', // 後段で指定されていないので生き残る 'array' => ['a', 'b', 'c'], // $deep ではないし後段で指定されているので完全に上書きされる 'yield1' => fn() => yield 123, // 後段で指定されているのでコールすらされない 'yield2' => fn() => yield 456, // 後段で指定されていないのでコールされる ], [ 'ignore' => null, // 元配列に存在しないのでスルーされる 'overwrite' => 'HOGE', 'array' => ['x', 'y', 'z'], 'yield1' => fn() => yield 234, ]))->is([ 'overwrite' => 'HOGE', 'through' => 'fuga', 'array' => ['x', 'y', 'z'], 'yield1' => 234, 'yield2' => 456, ]); # $deep の場合のマージ呼び出し that(array_extend(true, [ 'array' => ['hoge'], // 後段がスカラーなので末尾に追加される 'callback' => ['fuga'], // 後段がクロージャなのでコールバックされる 'list' => ['a', 'b', 'c'], // 数値配列なのでマージされる 'hash' => ['a' => null], // 連想配列なので再帰される 'yield' => [fn() => yield 123], // generator は解決される ], [ 'array' => 'HOGE', 'callback' => fn($v) => $v + [1 => 'FUGA'], 'list' => ['x', 'y', 'z'], 'hash' => ['a' => ['x' => ['y' => ['z' => 'xyz']]]], 'yield' => [fn() => yield 456], ]))->is([ 'array' => ['hoge', 'HOGE'], 'callback' => ['fuga', 'FUGA'], 'list' => ['a', 'b', 'c', 'x', 'y', 'z'], 'hash' => ['a' => ['x' => ['y' => ['z' => 'xyz']]]], 'yield' => [123, 456], ]);
Parameter
type name summary
​array|​bool $default = [] 基準となる配列
​iterable|​Closure ...$arrays マージする配列
Return
type summary
​array 新しい配列

[F] array_fill_callback array_fill_keys のコールバック版のようなもの

array_fill_keys のコールバック版のようなもの
function array_fill_callback( iterable $keys, callable $callback ): array

指定したキー配列をそれらのマップしたもので配列を生成する。 array_combine($keys, array_map($callback, $keys)) とほぼ等価。

Example:

$abc = ['a', 'b', 'c']; // [a, b, c] から [a => A, b => B, c => C] を作る that(array_fill_callback($abc, 'strtoupper'))->isSame([ 'a' => 'A', 'b' => 'B', 'c' => 'C', ]); // [a, b, c] からその sha1 配列を作って大文字化する that(array_fill_callback($abc, fn($v) => strtoupper(sha1($v))))->isSame([ 'a' => '86F7E437FAA5A7FCE15D1DDCB9EAEAEA377667B8', 'b' => 'E9D71F5EE7C92D6DC9E92FFDAD17B8BD49418F98', 'c' => '84A516841BA77A5B4648DE2CD0DFCB30EA46DBB4', ]);
Parameter
type name summary
​iterable $keys キーとなる配列
​callable $callback 要素のコールバック(引数でキーが渡ってくる)
Return
type summary
​array 新しい配列

[F] array_fill_gap 配列の隙間を埋める

配列の隙間を埋める
function array_fill_gap( array $array, mixed ...$values ): array

「隙間」とは数値キーの隙間のこと。文字キーには関与しない。 連番の抜けている箇所に $values の値を順次詰めていく動作となる。

値が足りなくてもエラーにはならない。つまり、この関数を通したとしても隙間が無くなるわけではない。 また、隙間を埋めても値が余る場合(隙間より与えられた値が多い場合)は末尾に全て追加される。

負数キーは考慮しない。

Example:

// ところどころキーが抜け落ちている配列の・・・ $array = [ 1 => 'b', 2 => 'c', 5 => 'f', 7 => 'h', ]; // 抜けているところを可変引数で順次埋める('i', 'j' は隙間というより末尾追加) that(array_fill_gap($array, 'a', 'd', 'e', 'g', 'i', 'j'))->isSame([ 0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f', 6 => 'g', 7 => 'h', 8 => 'i', 9 => 'j', ]); // 文字キーには関与しないし、値は足りなくても良い $array = [ 1 => 'b', 'x' => 'noize', 4 => 'e', 'y' => 'noize', 7 => 'h', 'z' => 'noize', ]; // 文字キーはそのまま保持され、値が足りないので 6 キーはない that(array_fill_gap($array, 'a', 'c', 'd', 'f'))->isSame([ 0 => 'a', 1 => 'b', 'x' => 'noize', 2 => 'c', 3 => 'd', 4 => 'e', 'y' => 'noize', 5 => 'f', 7 => 'h', 'z' => 'noize', ]);
Parameter
type name summary
​array $array 対象配列
​mixed ...$values 詰める値(可変引数)
Return
type summary
​array 隙間が詰められた配列

[F] array_filter_map array_filter + array_map する

array_filter + array_map する
function array_filter_map( iterable $array, callable $callback ): iterable

コールバックを適用して、結果が !false 要素のみ取り出す。 コールバックの第1引数を参照にして書き換えると結果にも反映される。

$callback が要求するならキーも渡ってくる。

Example:

that(array_filter_map([' a ', ' b ', ''], fn(&$v) => strlen($v) ? $v = trim($v) : false))->isSame(['a', 'b']);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 評価クロージャ
Return
type summary
​iterable $callback が !false を返し map された配列

[F] array_filter_recursive 再帰的に array_filter する

再帰的に array_filter する
function array_filter_recursive( iterable|T $array, callable $callback, bool $unset_empty = true ): iterable

要素が配列だった場合に再帰する点とコールバックの引数以外は array_filter とほとんど変わらない。 $callback が要求するならキーも渡ってくる。

$unset_empty:true だと再帰で伏せられた結果が空になった場合、そのキーも伏せられる。

Example:

$array = [ 'a' => 'A', // 生き残る 'e' => '', // 生き残らない 'a1' => [ // A がいるため $unset_empty は無関係に a1 自体も生き残る 'A', // 生き残る '', // 生き残らない ], 'a2' => [ // 生き残りが居ないため $unset_empty:true だと a2 自体も生き残らない '', // 生き残らない '', // 生き残らない ], 'b1' => [ // a1 のネスト版 'a' => [ 'a' => 'A', 'e' => '', ], ], 'b2' => [ // a2 のネスト版 'a' => [ 'a' => '', 'e' => '', ], ], ]; // 親を伏せない版 that(array_filter_recursive($array, fn($v) => strlen($v), false))->isSame([ "a" => "A", "a1" => ["A"], "a2" => [], "b1" => [ "a" => [ "a" => "A", ], ], "b2" => [ "a" => [], ], ]); // 親を伏せる版 that(array_filter_recursive($array, fn($v) => strlen($v), true))->isSame([ "a" => "A", "a1" => ["A"], "b1" => [ "a" => [ "a" => "A", ], ], ]);
Parameter
type name summary
​iterable|​T $array 対象配列
​callable $callback 評価クロージャ(値, キー, 親キー配列)
​bool $unset_empty = true 再帰の際に空になった要素も伏せるか
Return
type summary
​iterable フィルタされた配列

[F] array_filters 複数コールバックを指定できる array_filter

複数コールバックを指定できる array_filter
function array_filters( iterable $array, callable ...$callbacks ): array

指定したコールバックで複数回回してフィルタする。 array_filter($array, $f, $g)array_filter(array_filter($array, $f), $g) とほぼ等しい。 コールバックが要求するならキーも渡ってくる。 さらに文字列関数で "..." から始まっているなら可変引数としてコールする。

少し変わった仕様として、コールバックに [$method => $args] を付けるとそれはメソッド呼び出しになる。 つまり各要素 $v に対して $v->$method(...$args) がフィルタ結果になる。 さらに引数が不要なら @method とするだけで良い。

Example:

// 非 null かつ小文字かつ16進数 that(array_filters(['abc', 'XYZ', null, 'ABC', 'ff', '3e7'], fn($v) => $v !== null, fn($v) => ctype_lower("$v"), fn($v) => ctype_xdigit("$v"), ))->isSame([0 => 'abc', 4 => 'ff']);
Parameter
type name summary
​iterable $array 対象配列
​callable ...$callbacks 評価クロージャ配列
Return
type summary
​array 評価クロージャでフィルタした新しい配列

[F] array_find array_search のクロージャ版のようなもの

array_search のクロージャ版のようなもの
function array_find( $array, $callback, $is_key = true )
Parameter
type name summary
$array
$callback
$is_key = true
See
type summary
array_find_first()

[F] array_find_first array_search のクロージャ版のようなもの

array_search のクロージャ版のようなもの
function array_find_first( iterable $array, callable $callback, bool $is_key = true ): mixed

コールバックの返り値が true 相当のものを返す。 $is_key に true を与えるとそのキーを返す(デフォルトの動作)。 $is_key に false を与えるとコールバックの結果を返す。

この関数は論理値 FALSE を返す可能性がありますが、FALSE として評価される値を返す可能性もあります。

Example:

// 最初に見つかったキーを返す that(array_find_first(['a', '8', '9'], 'ctype_digit'))->isSame(1); that(array_find_first(['a', 'b', 'b'], fn($v) => $v === 'b'))->isSame(1); // 最初に見つかったコールバック結果を返す(最初の数字の2乗を返す) $ifnumeric2power = fn($v) => ctype_digit($v) ? $v * $v : false; that(array_find_first(['a', '8', '9'], $ifnumeric2power, false))->isSame(64);
Parameter
type name summary
​iterable $array 調べる配列
​callable $callback 評価コールバック
​bool $is_key = true キーを返すか否か
Return
type summary
​mixed コールバックが true を返した最初のキー。存在しなかったら null

[F] array_find_last array_find の後ろから探す版

array_find の後ろから探す版
function array_find_last( iterable $array, callable $callback, bool $is_key = true ): mixed

コールバックの返り値が true 相当のものを返す。 $is_key に true を与えるとそのキーを返す(デフォルトの動作)。 $is_key に false を与えるとコールバックの結果を返す。

この関数は論理値 FALSE を返す可能性がありますが、FALSE として評価される値を返す可能性もあります。

Example:

// 最後に見つかったキーを返す that(array_find_last(['a', '8', '9'], 'ctype_digit'))->isSame(2); that(array_find_last(['a', 'b', 'b'], fn($v) => $v === 'b'))->isSame(2); // 最後に見つかったコールバック結果を返す(最初の数字の2乗を返す) $ifnumeric2power = fn($v) => ctype_digit($v) ? $v * $v : false; that(array_find_last(['a', '8', '9'], $ifnumeric2power, false))->isSame(81);
Parameter
type name summary
​iterable $array 調べる配列
​callable $callback 評価コールバック
​bool $is_key = true キーを返すか否か
Return
type summary
​mixed コールバックが true を返した最初のキー。存在しなかったら false

[F] array_find_recursive array_find の再帰版

array_find の再帰版
function array_find_recursive( iterable $array, callable $callback, bool $is_key = true ): mixed

コールバックの返り値が true 相当のものを返す。 $is_key に true を与えるとそのキー配列を返す(デフォルトの動作)。 $is_key に false を与えるとコールバックの結果を返す。

この関数は論理値 FALSE を返す可能性がありますが、FALSE として評価される値を返す可能性もあります。

Example:

// 最初に見つかったキーを配列で返す that(array_find_recursive([ 'a' => [ 'b' => [ 'c' => [1, 2, 3], ], ], ], fn($v) => $v === 2))->isSame(['a', 'b', 'c', 1]);
Parameter
type name summary
​iterable $array 調べる配列
​callable $callback 評価コールバック
​bool $is_key = true キーを返すか否か
Return
type summary
​mixed コールバックが true を返した最初のキー。存在しなかったら false

[F] array_flatten 多階層配列をフラットに展開する

多階層配列をフラットに展開する
function array_flatten( iterable $array, string|\Closure|null $delimiter = null ): array

巷にあふれている実装と違って、 ["$pkey.$ckey" => $value] 形式の配列でも返せる。 $delimiter で区切り文字を指定した場合にそのようになる。 $delimiter = null の場合に本当の配列で返す(巷の実装と同じ)。

Example:

$array = [ 'k1' => 'v1', 'k2' => [ 'k21' => 'v21', 'k22' => [ 'k221' => 'v221', 'k222' => 'v222', 'k223' => [1, 2, 3], ], ], ]; // 区切り文字指定なし that(array_flatten($array))->isSame([ 0 => 'v1', 1 => 'v21', 2 => 'v221', 3 => 'v222', 4 => 1, 5 => 2, 6 => 3, ]); // 区切り文字指定 that(array_flatten($array, '.'))->isSame([ 'k1' => 'v1', 'k2.k21' => 'v21', 'k2.k22.k221' => 'v221', 'k2.k22.k222' => 'v222', 'k2.k22.k223.0' => 1, 'k2.k22.k223.1' => 2, 'k2.k22.k223.2' => 3, ]);
Parameter
type name summary
​iterable $array 対象配列
​string|​Closure|​null $delimiter = null キーの区切り文字。 null を与えると連番になる
Return
type summary
​array フラット化された配列

[F] array_get デフォルト値付きの配列値取得

デフォルト値付きの配列値取得
function array_get( array|\ArrayAccess $array, string|int|array|\Closure $key, mixed $default = null ): mixed

存在しない場合は $default を返す。

$key に配列を与えるとそれらの値の配列を返す(lookup 的な動作)。 その場合、$default が活きるのは「全て無かった場合」となる。

さらに $key が配列の場合に限り、 $default を省略すると空配列として動作する。

同様に、$key にクロージャを与えると、その返り値が true 相当のものを返す。 その際、 $default が配列なら一致するものを配列で返し、配列でないなら単値で返す。

Example:

// 単純取得 that(array_get(['a', 'b', 'c'], 1))->isSame('b'); // 単純デフォルト that(array_get(['a', 'b', 'c'], 9, 999))->isSame(999); // 配列取得 that(array_get(['a', 'b', 'c'], [0, 2]))->isSame([0 => 'a', 2 => 'c']); // 配列部分取得 that(array_get(['a', 'b', 'c'], [0, 9]))->isSame([0 => 'a']); // 配列デフォルト(null ではなく [] を返す) that(array_get(['a', 'b', 'c'], [9]))->isSame([]); // クロージャ指定&単値(コールバックが true を返す最初の要素) that(array_get(['a', 'b', 'c'], fn($v) => in_array($v, ['b', 'c'])))->isSame('b'); // クロージャ指定&配列(コールバックが true を返すもの) that(array_get(['a', 'b', 'c'], fn($v) => in_array($v, ['b', 'c']), []))->isSame([1 => 'b', 2 => 'c']);
Parameter
type name summary
​array|​ArrayAccess $array 配列
​string|​int|​array|​Closure $key 取得したいキー。配列を与えると全て返す。クロージャの場合は true 相当を返す
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 指定したキーの値

[F] array_grep_key キーを正規表現でフィルタする

キーを正規表現でフィルタする
function array_grep_key( iterable $array, string $regex, bool $not = false ): array

Example:

that(array_grep_key(['a' => 'A', 'aa' => 'AA', 'b' => 'B'], '#^a#'))->isSame(['a' => 'A', 'aa' => 'AA']); that(array_grep_key(['a' => 'A', 'aa' => 'AA', 'b' => 'B'], '#^a#', true))->isSame(['b' => 'B']);
Parameter
type name summary
​iterable $array 対象配列
​string $regex 正規表現
​bool $not = false true にすると「マッチしない」でフィルタする
Return
type summary
​array 正規表現でフィルタされた配列

[F] array_group 配列をコールバックの返り値でグループ化する

配列をコールバックの返り値でグループ化する
function array_group( iterable $array, ?callable|string|array $callback = null, bool $preserve_keys = false ): array

コールバックを省略すると値そのもので評価する。 コールバックに配列・文字列を与えるとキーでグループ化する。 コールバックが配列を返すと入れ子としてグループ化する。

Example:

that(array_group([1, 1, 1]))->isSame([ 1 => [1, 1, 1], ]); that(array_group([1, 2, 3], fn($v) => $v % 2))->isSame([ 1 => [1, 3], 0 => [2], ]); // group -> id で入れ子グループにする $row1 = ['id' => 1, 'group' => 'hoge']; $row2 = ['id' => 2, 'group' => 'fuga']; $row3 = ['id' => 3, 'group' => 'hoge']; that(array_group([$row1, $row2, $row3], fn($row) => [$row['group'], $row['id']]))->isSame([ 'hoge' => [ 1 => $row1, 3 => $row3, ], 'fuga' => [ 2 => $row2, ], ]);
Parameter
type name summary
​iterable $array 対象配列
​?callable|​string|​array $callback = null 評価クロージャ。 null なら値そのもので評価
​bool $preserve_keys = false キーを保存するか。 false の場合数値キーは振り直される
Return
type summary
​array グルーピングされた配列

[F] array_implode 配列の各要素の間に要素を差し込む

配列の各要素の間に要素を差し込む
function array_implode( iterable|string $array, string $glue ): array

歴史的な理由はないが、引数をどちらの順番でも受けつけることが可能。 ただし、$glue を先に渡すパターンの場合は配列指定が可変引数渡しになる。

文字キーは保存されるが数値キーは再割り振りされる。

Example:

// (配列, 要素) の呼び出し that(array_implode(['a', 'b', 'c'], 'X'))->isSame(['a', 'X', 'b', 'X', 'c']); // (要素, ...配列) の呼び出し that(array_implode('X', 'a', 'b', 'c'))->isSame(['a', 'X', 'b', 'X', 'c']);
Parameter
type name summary
​iterable|​string $array 対象配列
​string $glue 差し込む要素
Return
type summary
​array 差し込まれた配列

[F] array_insert 配列・連想配列を問わず任意の位置に値を挿入する

配列・連想配列を問わず任意の位置に値を挿入する
function array_insert( array $array, mixed $value, int|null $position = null ): array

$position を省略すると最後に挿入される(≒ array_push)。 $position に負数を与えると後ろから数えられる。 $value には配列も与えられるが、その場合数値キーは振り直される

Example:

that(array_insert([1, 2, 3], 'x'))->isSame([1, 2, 3, 'x']); that(array_insert([1, 2, 3], 'x', 1))->isSame([1, 'x', 2, 3]); that(array_insert([1, 2, 3], 'x', -1))->isSame([1, 2, 'x', 3]); that(array_insert([1, 2, 3], ['a' => 'A', 'b' => 'B'], 1))->isSame([1, 'a' => 'A', 'b' => 'B', 2, 3]);
Parameter
type name summary
​array $array 対象配列
​mixed $value 挿入値
​int|​null $position = null 挿入位置
Return
type summary
​array 挿入された新しい配列

[F] array_join 配列の SQL 的な JOIN を行う

配列の SQL 的な JOIN を行う
function array_join( array|iterable&\ArrayAccess $from, array|iterable&\ArrayAccess $join, array|callable $on, bool $outer = false ): array

$from に対して $on を満たす $join の行を結合する。 $from,$join は共にいわゆる「配列の配列」でなければならない。

$on は各レコードが渡ってくるので、true を返せば結合、false を返せば結合されない。 $on には配列も渡すことができ、配列を渡した場合は USING 的な動作になる。

  • キーが駆動表のカラム名、値が結合表のカラム名を表す
  • キーの値同士を json で判定する(厳密に言えば値を json キーにした一時配列で逆引きされる)
  • 複数指定時は AND になる 上記の前提・制約が許容できるなら配列指定の方が高速に動作する。

join 後のキーは各キーを "+" で連結したものになるが、このキーは暫定的なもの。

Example:

// t_article と t_comment の JOIN(のイメージ) $articles = [ 2 => ['article_id' => 2, 'article_name' => 'hoge'], 4 => ['article_id' => 4, 'article_name' => 'fuga'], 5 => ['article_id' => 5, 'article_name' => 'piyo'], ]; $comments = [ 4 => ['comment_id' => 4, 'article_id' => 2, 'comment' => 'foo'], 6 => ['comment_id' => 6, 'article_id' => 4, 'comment' => 'bar'], 7 => ['comment_id' => 7, 'article_id' => 4, 'comment' => 'baz'], 9 => ['comment_id' => 9, 'article_id' => 9, 'comment' => 'dmy'], ]; // INNER っぽい動き that(array_join($articles, $comments, fn($article, $comment) => $article['article_id'] === $comment['article_id'], false))->isSame([ '2+4' => ['article_id' => 2, 'article_name' => 'hoge', 'comment_id' => 4, 'comment' => 'foo'], '4+6' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 6, 'comment' => 'bar'], '4+7' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 7, 'comment' => 'baz'], ]); // LEFT っぽい動き that(array_join($articles, $comments, fn($article, $comment) => $article['article_id'] === $comment['article_id'], true))->isSame([ '2+4' => ['article_id' => 2, 'article_name' => 'hoge', 'comment_id' => 4, 'comment' => 'foo'], '4+6' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 6, 'comment' => 'bar'], '4+7' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 7, 'comment' => 'baz'], '5+null' => ['article_id' => 5, 'article_name' => 'piyo', 'comment_id' => null, 'comment' => null], ]); // ↑のような単純比較クロージャなら配列でも指定できる(そしてこの方がはるかに速い) that(array_join($articles, $comments, ['article_id' => 'article_id'], false))->isSame([ '2+4' => ['article_id' => 2, 'article_name' => 'hoge', 'comment_id' => 4, 'comment' => 'foo'], '4+6' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 6, 'comment' => 'bar'], '4+7' => ['article_id' => 4, 'article_name' => 'fuga', 'comment_id' => 7, 'comment' => 'baz'], ]);
Parameter
type name summary
​array|​ArrayAccess $from 駆動表
​array|​ArrayAccess $join 結合表
​array|​callable $on 結合条件。原則として callable で、ある程度の前提制約が置けるときのみ配列を指定する
​bool $outer = false true だと OUTER, false だと INNER 的挙動になる
Return
type summary
​array JOIN された配列

[F] array_keys_exist array_key_exists の複数版

array_key_exists の複数版
function array_keys_exist( array|string $keys, array|\ArrayAccess $array ): bool

指定キーが全て存在するなら true を返す。 配列ではなく単一文字列を与えても動作する(array_key_exists と全く同じ動作になる)。

$keys に空を与えると例外を投げる。 $keys に配列を与えるとキーで潜ってチェックする(Example 参照)。

Example:

// すべて含むので true that(array_keys_exist(['a', 'b', 'c'], ['a' => 'A', 'b' => 'B', 'c' => 'C']))->isTrue(); // N は含まないので false that(array_keys_exist(['a', 'b', 'N'], ['a' => 'A', 'b' => 'B', 'c' => 'C']))->isFalse(); // 配列を与えると潜る(日本語で言えば「a というキーと、x というキーとその中に x1, x2 というキーがあるか?」) that(array_keys_exist(['a', 'x' => ['x1', 'x2']], ['a' => 'A', 'x' => ['x1' => 'X1', 'x2' => 'X2']]))->isTrue();
Parameter
type name summary
​array|​string $keys 調べるキー
​array|​ArrayAccess $array 調べる配列
Return
type summary
​bool 指定キーが全て存在するなら true

[F] array_kvmap 配列の各キー・値にコールバックを適用する

配列の各キー・値にコールバックを適用する
function array_kvmap( iterable $array, callable $callback ): array

$callback は (キー, 値, $callback) が渡ってくるので 「その位置に配置したい配列」を返せばそこに置換される。 つまり、空配列を返せばそのキー・値は消えることになるし、複数の配列を返せば要素が増えることになる。 ただし、数値キーは新しく採番される。 null を返すと特別扱いで、そのキー・値をそのまま維持する。 iterable を返す必要があるが、もし iterable でない場合は配列キャストされる。

「map も filter も可能でキー変更可能」というとてもマッチョな関数。 実質的には「数値キーが再採番される再帰的でない array_convert」のように振る舞う。 ただし、再帰処理はないので自前で管理する必要がある。

Example:

$array = [ 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', ]; // キーに '_' 、値に 'prefix-' を付与。'b' は一切何もしない。'c' は値のみ。'd' はそれ自体伏せる that(array_kvmap($array, function ($k, $v) { if ($k === 'b') return null; if ($k === 'd') return []; if ($k !== 'c') $k = "_$k"; return [$k => "prefix-$v"]; }))->isSame([ '_a' => 'prefix-A', 'b' => 'B', 'c' => 'prefix-C', ]); // 複数返せばその分増える(要素の水増し) that(array_kvmap($array, fn($k, $v) => [ "{$k}1" => "{$v}1", "{$k}2" => "{$v}2", ]))->isSame([ 'a1' => 'A1', 'a2' => 'A2', 'b1' => 'B1', 'b2' => 'B2', 'c1' => 'C1', 'c2' => 'C2', 'd1' => 'D1', 'd2' => 'D2', ]); // $callback には $callback 自体も渡ってくるので再帰も比較的楽に書ける that(array_kvmap([ 'x' => [ 'X', 'y' => [ 'Y', 'z' => ['Z'], ], ], ], function ($k, $v, $callback) { // 配列だったら再帰する return ["_$k" => is_array($v) ? array_kvmap($v, $callback) : "prefix-$v"]; }))->isSame([ "_x" => [ "_0" => "prefix-X", "_y" => [ "_0" => "prefix-Y", "_z" => [ "_0" => "prefix-Z", ], ], ], ]);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 適用するコールバック
Return
type summary
​array 変換された配列

[F] array_limit limit を主体とした array_slice

limit を主体とした array_slice
function array_limit( iterable $array, int $limit, ?int $offset = null, ?bool $preserve_keys = null ): array

「配列の上から/下からN件取りたい」というユースケースがそれなりに多いが、毎回迷う上に1文で書けないので関数化した。

$limit が負数の場合は戻って読む。 $offset 省略時は $limit の符号で自動で先頭か末尾になる。 $offset を指定することは少なく、端的に言えば「$limit が整数なら先頭から、負数なら末尾から $limit 件返す」と考えてもよい。 配列を無限数直線にマッピングし、位置範囲指定で切り取るイメージ。 文章で書くと複雑なので Example を参照。

また、「array_slice しても結果が変わらないケース」でコールせずに結果を返すようにしてある。 (結果が変わらなくても無駄に呼ばれて速度低下があるため)。 例えば array_slice($array, 0, 0) は必ず空配列を返すし array_slice($array, 0, null) は必ず元の配列を返すはず。 にも関わらず array_slice は愚直に切り取り処理をしているようで、その分岐の有る無しで速度がだいぶ違う。

$preserve_keys は array_slice と同じだが、 null を渡すと「通常配列時に true, 連想配列時に false」が動的に決まるようになる。 ユースケースとしてはそのような使い方が多いはず。 あくまで null 指定の場合のみなのでこの動作が嫌なら明示的に bool を渡せばよい。

Example:

$array = ['a', 'b', 'c', 'd', 'e']; that(array_limit($array, 3))->isSame(['a', 'b', 'c']); // シンプルに先頭から3件 that(array_limit($array, -3))->isSame(['c', 'd', 'e']); // シンプルに末尾から3件 that(array_limit($array, 3, 1))->isSame(['b', 'c', 'd']); // 1番目('b')から正順に3件 that(array_limit($array, -3, 1))->isSame(['a', 'b']); // 1番目('b')から逆順に3件(足りないので結果は2件) that(array_limit($array, 3, 3))->isSame(['d', 'e']); // 3番目('d')から正順に3件(足りないので結果は2件) that(array_limit($array, -3, 3))->isSame(['b', 'c', 'd']); // 3番目('d')から逆順に3件 that(array_limit($array, 3, -2))->isSame(['a']); // -2番目(範囲外)から正順に3件('a'だけがギリギリ範囲に入る) that(array_limit($array, -3, 6))->isSame(['e']); // 6番目(範囲外)から逆順に3件('e'だけがギリギリ範囲に入る) that(array_limit($array, 3, -100))->isSame([]); // -100番目(範囲外)から正順に3件(完全に範囲外なので空) that(array_limit($array, -3, 100))->isSame([]); // 100番目(範囲外)から逆順に3件(完全に範囲外なので空)
Parameter
type name summary
​iterable $array 対象配列
​int $limit 切り詰めるサイズ
​?int $offset = null 開始位置
​?bool $preserve_keys = null キーの保存フラグ(null にすると連想配列の時のみ保存される)
Return
type summary
​array slice した配列

[F] array_lookup キー保存可能な array_column

キー保存可能な array_column
function array_lookup( iterable $array, array|string|null $column_key = null, string|\Closure|null $index_key = null ): array

array_column は キーを保存することが出来ないが、この関数は引数を2つだけ与えるとキーはそのままで array_column 相当の配列を返す。 逆に第3引数にクロージャを与えるとその結果をキーにすることが出来る。

$column_key に配列を与えるとそれだけの配列を返す。

Example:

$array = [ 11 => ['id' => 1, 'name' => 'name1', 'status' => true], 12 => ['id' => 2, 'name' => 'name2', 'status' => false], 13 => ['id' => 3, 'name' => 'name3', 'status' => true], ]; // 第3引数を渡せば array_column と全く同じ that(array_lookup($array, 'name', 'id'))->isSame(array_column($array, 'name', 'id')); that(array_lookup($array, 'name', null))->isSame(array_column($array, 'name', null)); // 省略すればキーが保存される that(array_lookup($array, 'name'))->isSame([ 11 => 'name1', 12 => 'name2', 13 => 'name3', ]); // クロージャを指定すればキーが生成される that(array_lookup($array, 'name', fn($v, $k) => $k * 2))->isSame([ 22 => 'name1', 24 => 'name2', 26 => 'name3', ]); // $column_key に配列を与えるとそれだけの配列を返す that(array_lookup($array, ['id', 'status']))->isSame([ 11 => ['id' => 1, 'status' => true], 12 => ['id' => 2, 'status' => false], 13 => ['id' => 3, 'status' => true], ]);
Parameter
type name summary
​iterable $array 対象配列
​array|​string|​null $column_key = null 値となるキー
​string|​Closure|​null $index_key = null キーとなるキー
Return
type summary
​array 新しい配列

[F] array_map_filter array_map + array_filter する

array_map + array_filter する
function array_map_filter( iterable $array, callable $callback, bool $strict = false ): iterable

コールバックを適用して、結果が true 相当の要素のみ取り出す。 $strict に true を与えると「null でない」要素のみ返される。

$callback が要求するならキーも渡ってくる。

Example:

that(array_map_filter([' a ', ' b ', ''], 'trim'))->isSame(['a', 'b']); that(array_map_filter([' a ', ' b ', ''], 'trim', true))->isSame(['a', 'b', '']);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 評価クロージャ
​bool $strict = false 厳密比較フラグ。 true だと null のみが偽とみなされる
Return
type summary
​iterable $callback が真を返した新しい配列

[F] array_map_key キーをマップして変換する

キーをマップして変換する
function array_map_key( iterable $array, callable $callback ): array

$callback が null を返すとその要素は取り除かれる。

Example:

that(array_map_key(['a' => 'A', 'b' => 'B'], 'strtoupper'))->isSame(['A' => 'A', 'B' => 'B']); that(array_map_key(['a' => 'A', 'b' => 'B'], function () { }))->isSame([]);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 評価クロージャ
Return
type summary
​array キーが変換された新しい配列

[F] array_map_recursive array_map の再帰版

array_map の再帰版
function array_map_recursive( iterable $array, callable $callback, bool $iterable = true, bool $apply_array = false ): array

下記の点で少し array_map とは挙動が異なる。

  • 配列だけでなく iterable も対象になる(引数で指定可能。デフォルト true)
    • つまりオブジェクト構造は維持されず、結果はすべて配列になる
  • 値だけでなくキーも渡ってくる

Example:

// デフォルトでは array_walk 等と同様に葉のみが渡ってくる(iterable も対象になる) that(array_map_recursive([ 'k' => 'v', 'c' => new \ArrayObject([ 'k1' => 'v1', 'k2' => 'v2', ]), ], 'strtoupper'))->isSame([ 'k' => 'V', 'c' => [ 'k1' => 'V1', 'k2' => 'V2', ], ]); // ただし、その挙動は引数で変更可能 that(array_map_recursive([ 'k' => 'v', 'c' => new \ArrayObject([ 'k1' => 'v1', 'k2' => 'v2', ]), ], 'gettype', false))->isSame([ 'k' => 'string', 'c' => 'object', ]); // さらに、自身にも適用できる(呼び出しは子が先で、本当の意味で「すべての要素」で呼び出される) that(array_map_recursive([ 'k' => 'v', 'c' => [ 'k1' => 'v1', 'k2' => 'v2', ], ], function ($v) { // 配列は stdclass 化、それ以外は大文字化 return is_array($v) ? (object) $v : strtoupper($v); }, true, true))->is((object) [ 'k' => 'V', 'c' => (object) [ 'k1' => 'V1', 'k2' => 'V2', ], ]);
Parameter
type name summary
​iterable $array 対象配列
​callable $callback 評価クロージャ
​bool $iterable = true is_iterable で判定するか
​bool $apply_array = false 配列要素にもコールバックを適用するか
Return
type summary
​array map された新しい配列

[F] array_maps 複数コールバックを指定できる array_map

複数コールバックを指定できる array_map
function array_maps( iterable $array, callable ...$callbacks ): iterable

指定したコールバックで複数回回してマップする。 array_maps($array, $f, $g)array_map($g, array_map($f, $array)) とほぼ等しい。 ただし、引数は順番が違う(可変引数のため)し、コールバックが要求するならキーも渡ってくる。 さらに文字列関数で "..." から始まっているなら可変引数としてコールする。

少し変わった仕様として、コールバックに [$method => $args] を付けるとそれはメソッド呼び出しになる。 つまり各要素 $v に対して $v->$method(...$args) がマップ結果になる。 さらに引数が不要なら @method とするだけで良い。

Example:

// 値を3乗したあと16進表記にして大文字化する that(array_maps([1, 2, 3, 4, 5], fn($v) => pow($v, 3), 'dechex', 'strtoupper'))->isSame(['1', '8', '1B', '40', '7D']); // キーも渡ってくる that(array_maps(['a' => 'A', 'b' => 'B'], fn($v, $k) => "$k:$v"))->isSame(['a' => 'a:A', 'b' => 'b:B']); // ... で可変引数コール that(array_maps([[1, 3], [1, 5, 2]], '...range'))->isSame([[1, 2, 3], [1, 3, 5]]); // メソッドコールもできる(引数不要なら `@method` でも同じ) that(array_maps([new \Exception('a'), new \Exception('b')], ['getMessage' => []]))->isSame(['a', 'b']); that(array_maps([new \Exception('a'), new \Exception('b')], '@getMessage'))->isSame(['a', 'b']);
Parameter
type name summary
​iterable $array 対象配列
​callable ...$callbacks 評価クロージャ配列
Return
type summary
​iterable 評価クロージャを通した新しい配列

[F] array_merge2 配列をマージして通常配列+αで返す

配列をマージして通常配列+αで返す
function array_merge2(array ...$arrays): array

キー・値が維持される点で array_merge とは異なる(振り直しをせず数値配列で返す)。 きちんと0からの連番で構成される点で配列の加算とは異なる。 要するに「できるだけキーが自然数(の並び)になるように」マージする。

歯抜けはそのまま維持され、文字キーは後ろに追加される(負数キーも同様)。

Example:

// キーが入り乱れているがよく見ると通し番号が振られている配列をマージ that(array_merge2([4 => 4, 1 => 1], [0 => 0], [5 => 5, 2 => 2, 3 => 3]))->isSame([0, 1, 2, 3, 4, 5]); // 歯抜けの配列をマージ that(array_merge2([4 => 4, 1 => 1], [0 => 0], [5 => 5, 3 => 3]))->isSame([0, 1, 3 => 3, 4 => 4, 5 => 5]); // 負数や文字キーは後ろに追いやられる that(array_merge2(['a' => 'A', 1 => 1], [0 => 0], [-1 => 'X', 2 => 2, 3 => 3]))->isSame([0, 1, 2, 3, -1 => 'X', 'a' => 'A']); // 同じキーは後ろ優先 that(array_merge2([0, 'a' => 'A0'], [1, 'a' => 'A1'], [2, 'a' => 'A2']))->isSame([2, 'a' => 'A2']);
Parameter
type name summary
​array ...$arrays マージする配列
Return
type summary
​array マージされた配列

[F] array_mix 配列を交互に追加する

配列を交互に追加する
function array_mix(array ...$variadic): array

引数の配列を横断的に追加して返す。 数値キーは振り直される。文字キーはそのまま追加される(同じキーは後方上書き)。

配列の長さが異なる場合、短い方に対しては何もしない。そのまま追加される。

Example:

// 奇数配列と偶数配列をミックスして自然数配列を生成 that(array_mix([1, 3, 5], [2, 4, 6]))->isSame([1, 2, 3, 4, 5, 6]); // 長さが異なる場合はそのまま追加される(短い方の足りない分は無視される) that(array_mix([1], [2, 3, 4]))->isSame([1, 2, 3, 4]); that(array_mix([1, 3, 4], [2]))->isSame([1, 2, 3, 4]); // 可変引数なので3配列以上も可 that(array_mix([1], [2, 4], [3, 5, 6]))->isSame([1, 2, 3, 4, 5, 6]); that(array_mix([1, 4, 6], [2, 5], [3]))->isSame([1, 2, 3, 4, 5, 6]); // 文字キーは維持される that(array_mix(['a' => 'A', 1, 3], ['b' => 'B', 2]))->isSame(['a' => 'A', 'b' => 'B', 1, 2, 3]);
Parameter
type name summary
​array ...$variadic 対象配列(可変引数)
Return
type summary
​array 引数配列が交互に追加された配列

[F] array_nest シンプルな [キー => 値] な配列から階層配列を生成する

シンプルな [キー => 値] な配列から階層配列を生成する
function array_nest( iterable $array, string $delimiter = "." ): array

定義的に array_flatten の逆関数のような扱いになる。 $delimiter で階層を表現する。

同名とみなされるキーは上書きされるか例外が飛ぶ。具体的には Example を参照。

Example:

// 単純な階層展開 $array = [ 'k1' => 'v1', 'k2.k21' => 'v21', 'k2.k22.k221' => 'v221', 'k2.k22.k222' => 'v222', 'k2.k22.k223.0' => 1, 'k2.k22.k223.1' => 2, 'k2.k22.k223.2' => 3, ]; that(array_nest($array))->isSame([ 'k1' => 'v1', 'k2' => [ 'k21' => 'v21', 'k22' => [ 'k221' => 'v221', 'k222' => 'v222', 'k223' => [1, 2, 3], ], ], ]); // 同名になるようなキーは上書きされる $array = [ 'k1.k2' => 'v1', // この時点で 'k1' は配列になるが・・・ 'k1' => 'v2', // この時点で 'k1' は文字列として上書きされる ]; that(array_nest($array))->isSame([ 'k1' => 'v2', ]); // 上書きすら出来ない場合は例外が飛ぶ $array = [ 'k1' => 'v1', // この時点で 'k1' は文字列になるが・・・ 'k1.k2' => 'v2', // この時点で 'k1' にインデックスアクセスすることになるので例外が飛ぶ ]; try { array_nest($array); } catch (\Exception $e) { that($e)->isInstanceOf(\InvalidArgumentException::class); }
Parameter
type name summary
​iterable $array 対象配列
​string $delimiter = "." キーの区切り文字
Return
type summary
​array 階層化された配列

[F] array_of 配列を与えると指定キーの値を返すクロージャを返す

配列を与えると指定キーの値を返すクロージャを返す
function array_of( string|int|array $key, mixed $default = null ): \Closure

存在しない場合は $default を返す。

$key に配列を与えるとそれらの値の配列を返す(lookup 的な動作)。 その場合、$default が活きるのは「全て無かった場合」となる。 さらに $key が配列の場合に限り、 $default を省略すると空配列として動作する。

Example:

$fuga_of_array = array_of('fuga'); that($fuga_of_array(['hoge' => 'HOGE', 'fuga' => 'FUGA']))->isSame('FUGA');
Parameter
type name summary
​string|​int|​array $key 取得したいキー
​mixed $default = null デフォルト値
Return
type summary
Closure $key の値を返すクロージャ

[F] array_or 全要素が false になるなら false を返す(1つでも true なら true を返す)

全要素が false になるなら false を返す(1つでも true なら true を返す)
function array_or( iterable $array, ?callable $callback = null, bool|mixed $default = false ): bool

$callback が要求するならキーも渡ってくる。

Example:

that(array_or([true, true]))->isTrue(); that(array_or([true, false]))->isTrue(); that(array_or([false, false]))->isFalse();
Parameter
type name summary
​iterable $array 対象配列
​?callable $callback = null 評価クロージャ。 null なら値そのもので評価
​bool|​mixed $default = false 空配列の場合のデフォルト値
Return
type summary
​bool 全要素が false なら false

[F] array_order 配列を $orders に従って並べ替える

配列を $orders に従って並べ替える
function array_order( array $array, mixed $orders, bool $preserve_keys = false ): array

データベースからフェッチしたような連想配列の配列を想定しているが、スカラー配列(['key' => 'value'])にも対応している。 その場合 $orders に配列ではなく直値を渡せば良い。

$orders には下記のような配列を渡す。 キーに空文字を渡すとそれは「キー自体」を意味する。

$orders = [ 'col1' => true, // true: 昇順, false: 降順。照合は型に依存 'col2' => SORT_NATURAL, // SORT_NATURAL, SORT_REGULAR などで照合。正数で昇順、負数で降順 'col3' => ['sort', 'this', 'order'], // 指定した配列順で昇順 'col4' => fn($v) => $v, // 引数1個: クロージャを通した値で昇順。照合は返り値の型に依存 // 'col4' => fn($v, $o = SORT_DESC) => $v, // ↑の亜種(第2引数のデフォルト値がオーダーを表す) 'col5' => fn($av, $bv) => $av - $bv, // 引数2個: クロージャで比較して値昇順(いわゆる比較関数を渡す) 'col6' => fn($ak, $bk, $array) => $ak - $bk, // 引数3個: クロージャで比較してキー昇順(いわゆる比較関数を渡す) ];

Example:

$v1 = ['id' => '1', 'no' => 'a03', 'name' => 'yyy']; $v2 = ['id' => '2', 'no' => 'a4', 'name' => 'yyy']; $v3 = ['id' => '3', 'no' => 'a12', 'name' => 'xxx']; // name 昇順, no 自然降順 that(array_order([$v1, $v2, $v3], ['name' => true, 'no' => -SORT_NATURAL]))->isSame([$v3, $v2, $v1]);
Parameter
type name summary
​array $array 対象配列
​mixed $orders ソート順
​bool $preserve_keys = false キーを保存するか。 false の場合数値キーは振り直される
Return
type summary
​array 並び替えられた配列

[F] array_pickup キーを指定してそれだけの配列にする

キーを指定してそれだけの配列にする
function array_pickup( iterable|object $array, array $keys ): array

array_intersect_key($array, array_flip($keys)) とほぼ同義。 違いは Traversable を渡せることと、結果配列の順番が $keys に従うこと。

$keys に連想配列を渡すとキーを読み替えて動作する(Example を参照)。 さらにその時クロージャを渡すと($key, $value)でコールされた結果が新しいキーになる。

Example:

$array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // a と c を取り出す that(array_pickup($array, ['a', 'c']))->isSame(['a' => 'A', 'c' => 'C']); // 順番は $keys 基準になる that(array_pickup($array, ['c', 'a']))->isSame(['c' => 'C', 'a' => 'A']); // 連想配列を渡すと読み替えて返す that(array_pickup($array, ['c' => 'cX', 'a' => 'aX']))->isSame(['cX' => 'C', 'aX' => 'A']); // コールバックを渡せる that(array_pickup($array, ['c' => fn($k, $v) => "$k-$v"]))->isSame(['c-C' => 'C']);
Parameter
type name summary
​iterable|​object $array 対象配列
​array $keys 取り出すキー
Return
type summary
​array 新しい配列

[F] array_pos 配列・連想配列を問わず「N番目(0ベース)」の要素を返す

配列・連想配列を問わず「N番目(0ベース)」の要素を返す
function array_pos( array $array, int $position, bool $return_key = false ): mixed

負数を与えると逆から N 番目となる。

Example:

that(array_pos([1, 2, 3], 1))->isSame(2); that(array_pos([1, 2, 3], -1))->isSame(3); that(array_pos(['a' => 'A', 'b' => 'B', 'c' => 'C'], 1))->isSame('B'); that(array_pos(['a' => 'A', 'b' => 'B', 'c' => 'C'], 1, true))->isSame('b');
Parameter
type name summary
​array $array 対象配列
​int $position 取得する位置
​bool $return_key = false true にすると値ではなくキーを返す
Return
type summary
​mixed 指定位置の値

[F] array_pos_key 配列の指定キーの位置を返す

配列の指定キーの位置を返す
function array_pos_key( array $array, string|int|array $key, mixed $default = null ): int|int[]

$key に配列を与えるとその全ての位置を返す。

Example:

that(array_pos_key(['a' => 'A', 'b' => 'B', 'c' => 'C'], 'c'))->isSame(2); that(array_pos_key(['a' => 'A', 'b' => 'B', 'c' => 'C'], 'x', -1))->isSame(-1); that(array_pos_key(['a' => 'A', 'b' => 'B', 'c' => 'C'], ['a', 'c']))->isSame(['a' => 0, 'c' => 2]);
Parameter
type name summary
​array $array 対象配列
​string|​int|​array $key 取得したい位置のキー
​mixed $default = null 見つからなかったときのデフォルト値。指定しないと例外。$key が配列の場合は見つからなかったキー全てに代入される
Return
type summary
​int|​int[] 指定キーの位置

[F] array_prepend 配列の先頭に要素を追加する

配列の先頭に要素を追加する
function array_prepend( array $array, $value, $key = null ): array

array_unshift のキーが指定できる参照渡しでない版と言える。 配列の数値キーは振り直される。 キー指定でかつそのキーが存在するとき、値を変えつつ先頭に移動する動作となる。

Example:

// キー未指定は0で挿入される that(array_prepend([1, 2, 3], 99))->is([99, 1, 2, 3]); // キーを指定すればそのキーで生える that(array_prepend([1, 2, 3], 99, 'newkey'))->is(['newkey' => 99, 1, 2, 3]); // 存在する場合は値が変わって先頭に移動する that(array_prepend([1, 2, 3], 99, 1))->is([1 => 99, 0 => 1, 2 => 3]);
Parameter
type name summary
​array $array 対象配列
$value
$key = null
Return
type summary
​array 要素が追加された配列

[F] array_random array_rand の要素版

array_rand の要素版
function array_random( array $array, ?int $count = null, $preserve_keys = false ): mixed

とはいえ多少の差異がある。

  • 第2引数に null を与えると単一の値として返す
  • 第2引数に数値を与えると配列で返す(たとえ1でも配列で返す)
  • 第2引数に 0 を与えてもエラーにはならない(空配列を返す)
  • 第2引数に負数を与えるとその個数に満たなくても例外にならない
  • 第3引数に true を与えるとキーを維持して返す

Example:

mt_srand(4); // テストがコケるので種固定 // 配列からランダムに値1件取得(単一で返す) that(array_random(['a' => 'A', 'b' => 'B', 'c' => 'C']))->isSame('B'); // 配列からランダムに値2件取得(配列で返す) that(array_random(['a' => 'A', 'b' => 'B', 'c' => 'C'], 2))->isSame(['B', 'C']); // 配列からランダムに値2件取得(キーを維持) that(array_random(['a' => 'A', 'b' => 'B', 'c' => 'C'], 2, true))->isSame(['a' => 'A', 'c' => 'C']); // 配列からランダムに値N件取得(負数指定。配列数を超えた指定は例外になるので負数にする必要がある) that(array_random(['a' => 'A', 'b' => 'B', 'c' => 'C'], -999, true))->isSame(['a' => 'A', 'b' => 'B', 'c' => 'C']);
Parameter
type name summary
​array $array 対象配列
​?int $count = null 取り出す個数
$preserve_keys = false
Return
type summary
​mixed ランダムな要素

[F] array_range range を少し改良したもの

range を少し改良したもの
function array_range( int|float|string|\DateTimeInterface $start, int|float|string|\DateTimeInterface $end, int|float|string|null|\DateInterval $step = null, $options = [] ): array
  • 文字列に対応
  • 日時に対応
  • $start, $end から $step の自動算出
  • $start < $end で $step < 0 の場合、空配列を返す
  • $start > $end で $step > 0 の場合、空配列を返す

逆に言うと $start, $end の大小を意識しないと正しい値は返らないことになる。 標準 range の下記の挙動が個人的に違和感があるので実装した。

  • range(1, 3, -1); // [1, 2, 3]
  • range(3, 1, +1); // [3, 2, 1]

Example:

// 文字列(具体的にはデクリメント) that(array_range('a', 'c', +1))->isSame(['a', 'b', 'c']); that(array_range('c', 'a', -1))->isSame(['c', 'b', 'a']); // 日時 that(array_range('2014/12/24 12:34:56', '2014/12/26 12:34:56', 'P1D', ['format' => 'Y/m/d H:i:s']))->isSame([ '2014/12/24 12:34:56', '2014/12/25 12:34:56', '2014/12/26 12:34:56', ]); that(array_range('2014/12/26 12:34:56', '2014/12/24 12:34:56', 'P-1D', ['format' => 'Y/m/d H:i:s']))->isSame([ '2014/12/26 12:34:56', '2014/12/25 12:34:56', '2014/12/24 12:34:56', ]); // step は省略可能(+/-1 になる) that(array_range(1, 3))->isSame([1, 2, 3]); that(array_range(3, 1))->isSame([3, 2, 1]); that(array_range('a', 'c'))->isSame(['a', 'b', 'c']); that(array_range('c', 'a'))->isSame(['c', 'b', 'a']); // 範囲外は空配列を返す that(array_range(1, 3, -1))->isSame([]); that(array_range(3, 1, +1))->isSame([]); that(array_range('a', 'c', -1))->isSame([]); that(array_range('c', 'a', +1))->isSame([]); that(array_range('2014/12/24', '2014/12/27', 'P-1D'))->isSame([]); that(array_range('2014/12/27', '2014/12/24', 'P1D'))->isSame([]);
Parameter
type name summary
​int|​float|​string|​DateTimeInterface $start 最初の値
​int|​float|​string|​DateTimeInterface $end 最後の値
​int|​float|​string|​null|​DateInterval $step = null 増分
$options = []
Return
type summary
​array $start ~ $end の配列

[F] array_rank 配列をランク付けしてその順番でN件返す

配列をランク付けしてその順番でN件返す
function array_rank( iterable $array, int $length, ?callable $rankfunction = null ): array

同ランクはすべて返す。 つまり $length=10 でも10件以上を返すこともある。

$length が負数の場合、降順ソートして後ろから取り出す。 端的に言えば

正数
下位N件
負数
上位N件

という動作になる。

ソートの型は最初の要素で決まる。 文字列なら SORT_STRING で、違うなら SORT_NUMERIC

Parameter
type name summary
​iterable $array 対象配列
​int $length 取り出す件数
​?callable $rankfunction = null ランク付けクロージャ
Return
type summary
​array 上位N件の配列

[F] array_rekey キーをマップ配列・callable で置換する

キーをマップ配列・callable で置換する
function array_rekey( iterable $array, array|callable $keymap ): array

変換先・返り値が null だとその要素は取り除かれる。 callable 指定時の引数は (キー, 値, 連番インデックス, 対象配列そのもの) が渡ってくる。

Example:

$array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // a は x に c は z に置換される that(array_rekey($array, ['a' => 'x', 'c' => 'z']))->isSame(['x' => 'A', 'b' => 'B', 'z' => 'C']); // b は削除され c は z に置換される that(array_rekey($array, ['b' => null, 'c' => 'z']))->isSame(['a' => 'A', 'z' => 'C']); // キーの交換にも使える(a ⇔ c) that(array_rekey($array, ['a' => 'c', 'c' => 'a']))->isSame(['c' => 'A', 'b' => 'B', 'a' => 'C']); // callable that(array_rekey($array, 'strtoupper'))->isSame(['A' => 'A', 'B' => 'B', 'C' => 'C']);
Parameter
type name summary
​iterable $array 対象配列
​array|​callable $keymap マップ配列かキーを返すクロージャ
Return
type summary
​array キーが置換された配列

[F] array_remove キーを指定してそれらを除いた配列にする

キーを指定してそれらを除いた配列にする
function array_remove( array|\Traversable $array, array|int|string $keys ): array

array_diff_key($array, array_flip($keys)) とほぼ同義。 違いは Traversable を渡せること。

array_pickup の逆とも言える。

Example:

$array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // a と c を伏せる(b を残す) that(array_remove($array, ['a', 'c']))->isSame(['b' => 'B']);
Parameter
type name summary
​array|​Traversable $array 対象配列
​array|​int|​string $keys 伏せるキー
Return
type summary
​array 新しい配列

[F] array_revise 配列要素の追加・変更・削除を行う

配列要素の追加・変更・削除を行う
function array_revise( iterable $array, array ...$maps ): array

$map の当該キー要素が・・・

クロージャの場合
キーの有無に関わらずコールされる
null の場合
キーが削除される
それ以外の場合
キーが追加される(存在しない場合のみ)

という処理を行う。

Example:

that(array_revise([ 'id' => 123, 'name' => 'hoge', 'age' => 18, 'delete' => '', ], [ 'name' => 'ignored', // 存在するのでスルーされる 'append' => 'newkey', // 存在しないので追加される 'age' => fn($age) => $age + 1, // クロージャは現在の値を引数にしてコールされる 'delete' => null, // null は削除される 'null' => fn() => null, // 削除の目印として null を使っているので null を追加したい場合はクロージャで包む必要がある ]))->isSame([ 'id' => 123, 'name' => 'hoge', 'age' => 19, 'append' => 'newkey', 'null' => null, ]);
Parameter
type name summary
​iterable $array
​array ...$maps
Return
type summary
​array 変更された新しい配列

[F] array_schema 配列のスキーマを定義して配列を正規化する

配列のスキーマを定義して配列を正規化する
function array_schema( array $schema, mixed ...$arrays ): array
type
値の型を指定する
is_XXX の XXX 部分
左記で検証
number
is_int or is_float で検証
class 名
instanceof で検証
list
値がマージされて通常配列になる
  • list@string のようにすると配列の中身の型を指定できる
hash
連想配列になる
string|int
string or int
['string', 'int']
上と同じ
closure
指定クロージャで検証・フィルタ
all
値を引数に取り、返り値が新しい値となる
unique
重複を除去する
list
重複除去(パラメータがソートアルゴリズムになる)
enum
値が指定値のいずれかであるか検証する
all
in_array で検証する
min
値が指定値以上であるか検証する
string
strlen で検証
list
count で検証
all
その値で検証
max
値が指定値以下であるか検証する
  • min の逆
match
値が正規表現にマッチするか検証する
all
preg_match で検証する
unmatch
値が正規表現にマッチしないか検証する
  • match の逆
include
値が指定値を含むか検証する
string
strpos で検証
list
in_array で検証
exclude
値が指定値を含まないか検証する
  • include の逆

検証・フィルタは原則として型を見ない(指定されていればすべて実行される)。 のでおかしな型におかしな検証・フィルタを与えると型エラーが出ることがある。

検証は途中経過を問わない。 後ろの配列で上書きされた値や unique で減った配列などは以下に違反していても valid と判断される。

素直に json schema を使えという内なる声が聞こえなくもない。

Parameter
type name summary
​array $schema スキーマ配列
​mixed ...$arrays 検証する配列(可変引数。マージされる)
Return
type summary
​array 正規化された配列

[F] array_select 指定キーの要素で抽出する

指定キーの要素で抽出する
function array_select( iterable $array, string|iterable|\Closure $columns, int|string|null $index = null ): array

$columns に単純な値を渡すとそのキーの値を選択する。 キー付きで値を渡すと読み替えて選択する。 キー付きでクロージャを渡すと (キーの値, 行自体, 現在行のキー) を引数としてコールバックして選択する。 単一のクロージャを渡すと (行自体, 現在行のキー) を引数としてコールバックして選択する(array_map とほぼ同じ)。

Example:

$array = [ 11 => ['id' => 1, 'name' => 'name1'], 12 => ['id' => 2, 'name' => 'name2'], 13 => ['id' => 3, 'name' => 'name3'], ]; that(array_select($array, [ 'id', // id を単純取得 'alias' => 'name', // name を alias として取得 ]))->isSame([ 11 => ['id' => 1, 'alias' => 'name1'], 12 => ['id' => 2, 'alias' => 'name2'], 13 => ['id' => 3, 'alias' => 'name3'], ]); that(array_select($array, [ // id の 10 倍を取得 'id' => fn($id) => $id * 10, // id と name の結合を取得 'idname' => fn($null, $row, $index) => $row['id'] . $row['name'], ]))->isSame([ 11 => ['id' => 10, 'idname' => '1name1'], 12 => ['id' => 20, 'idname' => '2name2'], 13 => ['id' => 30, 'idname' => '3name3'], ]);
Parameter
type name summary
​iterable $array 対象配列
​string|​iterable|​Closure $columns 抽出項目
​int|​string|​null $index = null キーとなるキー
Return
type summary
​array 新しい配列

[F] array_set キー指定の配列値設定

キー指定の配列値設定
function array_set( array &$array, mixed $value, array|string|int|null $key = null, callable|null $condition = null ): string|int|null

第3引数を省略すると(null を与えると)言語機構を使用して配列の最後に設定する($array[] = $value)。 第3引数に配列を指定すると潜って設定する。

第4引数で追加する条件クロージャを指定できる。 クロージャには (追加する要素, 追加するキー, 追加される元配列) が渡ってくる。 このクロージャが false 相当を返した時は追加されないようになる。

Example:

$array = ['a' => 'A', 'B']; // 第3引数省略(最後に連番キーで設定) that(array_set($array, 'Z'))->isSame(1); that($array)->isSame(['a' => 'A', 'B', 'Z']); // 第3引数でキーを指定 that(array_set($array, 'Z', 'z'))->isSame('z'); that($array)->isSame(['a' => 'A', 'B', 'Z', 'z' => 'Z']); that(array_set($array, 'Z', 'z'))->isSame('z'); // 第3引数で配列を指定 that(array_set($array, 'Z', ['x', 'y', 'z']))->isSame('z'); that($array)->isSame(['a' => 'A', 'B', 'Z', 'z' => 'Z', 'x' => ['y' => ['z' => 'Z']]]); // 第4引数で条件を指定(キーが存在するなら追加しない) that(array_set($array, 'Z', 'z', fn($v, $k, $array) => !isset($array[$k])))->isSame(null); // 第4引数で条件を指定(値が存在するなら追加しない) that(array_set($array, 'Z', null, fn($v, $k, $array) => !in_array($v, $array)))->isSame(null);
Parameter
type name summary
​array &$array 配列
​mixed $value 設定する値
​array|​string|​int|​null $key = null 設定するキー
​callable|​null $condition = null 追加する条件
Return
type summary
​string|​int|​null 設定したキー

[F] array_shrink_key 値の優先順位を逆にした array_intersect_key

値の優先順位を逆にした array_intersect_key
function array_shrink_key(iterable|array|object ...$variadic): array

array_intersect_key は「左優先で共通項を取る」という動作だが、この関数は「右優先で共通項を取る」という動作になる。 「配列の並び順はそのままで値だけ変えたい/削ぎ落としたい」という状況はまれによくあるはず。

Example:

$array1 = ['a' => 'A1', 'b' => 'B1', 'c' => 'C1']; $array2 = ['c' => 'C2', 'b' => 'B2', 'a' => 'A2']; $array3 = ['c' => 'C3', 'dummy' => 'DUMMY']; // 全共通項である 'c' キーのみが生き残り、その値は最後の 'C3' になる that(array_shrink_key($array1, $array2, $array3))->isSame(['c' => 'C3']);
Parameter
type name summary
​iterable|​array|​object ...$variadic 共通項を取る配列(可変引数)
Return
type summary
​array 新しい配列

[F] array_shuffle shuffle のキーが保存される+参照渡しではない版

shuffle のキーが保存される+参照渡しではない版
function array_shuffle(array $array): array

Example:

that(array_shuffle(['a' => 'A', 'b' => 'B', 'c' => 'C']))->is(['b' => 'B', 'a' => 'A', 'c' => 'C']);
Parameter
type name summary
​array $array 対象配列
Return
type summary
​array shuffle された配列

[F] array_sprintf キーと値で sprintf する

キーと値で sprintf する
function array_sprintf( iterable $array, string|callable|null $format = null, ?string $glue = null ): array|string

配列の各要素を文字列化して返すイメージ。 $glue を与えるとさらに implode して返す(返り値が文字列になる)。

$format は書式文字列($v, $k)。 callable を与えると sprintf ではなくコールバック処理になる($v, $k)。 省略(null)するとキーを format 文字列、値を引数として vsprintf する。

Example:

$array = ['key1' => 'val1', 'key2' => 'val2']; // key, value を利用した sprintf that(array_sprintf($array, '%2$s=%1$s'))->isSame(['key1=val1', 'key2=val2']); // 第3引数を与えるとさらに implode される that(array_sprintf($array, '%2$s=%1$s', ' '))->isSame('key1=val1 key2=val2'); // クロージャを与えるとコールバック動作になる $closure = fn($v, $k) => "$k=" . strtoupper($v); that(array_sprintf($array, $closure, ' '))->isSame('key1=VAL1 key2=VAL2'); // 省略すると vsprintf になる that(array_sprintf([ 'str:%s,int:%d' => ['sss', '3.14'], 'single:%s' => 'str', ], null, '|'))->isSame('str:sss,int:3|single:str');
Parameter
type name summary
​iterable $array 対象配列
​string|​callable|​null $format = null 書式文字列あるいはクロージャ
​?string $glue = null 結合文字列。未指定時は implode しない
Return
type summary
​array|​string sprintf された配列

[F] array_strpad 配列のキー・要素に文字列を付加する

配列のキー・要素に文字列を付加する
function array_strpad( iterable $array, string|array $key_prefix, string|array $val_prefix = "" ): array

$key_prefix, $val_prefix でそれぞれ「キーに付与する文字列」「値に付与する文字列」が指定できる。 配列を与えると [サフィックス, プレフィックス] という意味になる。 デフォルト(ただの文字列)はプレフィックス(値だけに付与したいなら array_map で十分なので)。

Example:

$array = ['key1' => 'val1', 'key2' => 'val2']; // キーにプレフィックス付与 that(array_strpad($array, 'prefix-'))->isSame(['prefix-key1' => 'val1', 'prefix-key2' => 'val2']); // 値にサフィックス付与 that(array_strpad($array, '', ['-suffix']))->isSame(['key1' => 'val1-suffix', 'key2' => 'val2-suffix']);
Parameter
type name summary
​iterable $array 対象配列
​string|​array $key_prefix キー側の付加文字列
​string|​array $val_prefix = "" 値側の付加文字列
Return
type summary
​array 文字列付与された配列

[F] array_uncolumns array_columns のほぼ逆で [キー => [要素]] 配列から連想配列の配列を生成する

array_columns のほぼ逆で [キー => [要素]] 配列から連想配列の配列を生成する
function array_uncolumns( array $array, ?array $template = null ): array

$template を指定すると「それに含まれる配列かつ値がデフォルト」になる(要するに $default みたいなもの)。 キーがバラバラな配列を指定する場合は指定したほうが良い。が、null を指定すると最初の要素が使われるので大抵の場合は null で良い。

Example:

that(array_uncolumns([ 'id' => [1, 2], 'name' => ['A', 'B'], ]))->isSame([ ['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B'], ]);
Parameter
type name summary
​array $array 対象配列
​?array $template = null 抽出要素とそのデフォルト値
Return
type summary
​array 新しい配列

[F] array_unset 伏せると同時にその値を返す

伏せると同時にその値を返す
function array_unset( array|\ArrayAccess &$array, string|int|array|callable $key, mixed $default = null ): mixed

$key に配列を与えると全て伏せて配列で返す。 その場合、$default が活きるのは「全て無かった場合」となる。

さらに $key が配列の場合に限り、 $default を省略すると空配列として動作する。

配列を与えた場合の返り値は与えた配列の順番・キーが活きる。 これを利用すると list の展開の利便性が上がったり、連想配列で返すことができる。

同様に、$key にクロージャを与えると、その返り値が true 相当のものを伏せて配列で返す。 callable ではなくクロージャのみ対応する。

Example:

$array = ['a' => 'A', 'b' => 'B']; // ない場合は $default を返す that(array_unset($array, 'x', 'X'))->isSame('X'); // 指定したキーを返す。そのキーは伏せられている that(array_unset($array, 'a'))->isSame('A'); that($array)->isSame(['b' => 'B']); $array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // 配列を与えるとそれらを返す。そのキーは全て伏せられている that(array_unset($array, ['a', 'b', 'x']))->isSame(['A', 'B']); that($array)->isSame(['c' => 'C']); $array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // 配列のキーは返されるキーを表す。順番も維持される that(array_unset($array, ['x2' => 'b', 'x1' => 'a']))->isSame(['x2' => 'B', 'x1' => 'A']); $array = ['hoge' => 'HOGE', 'fuga' => 'FUGA', 'piyo' => 'PIYO']; // 値に "G" を含むものを返す。その要素は伏せられている that(array_unset($array, fn($v) => strpos($v, 'G') !== false))->isSame(['hoge' => 'HOGE', 'fuga' => 'FUGA']); that($array)->isSame(['piyo' => 'PIYO']);
Parameter
type name summary
​array|​ArrayAccess &$array 配列
​string|​int|​array|​callable $key 伏せたいキー。配列を与えると全て伏せる。クロージャの場合は true 相当を伏せる
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 指定したキーの値

[F] array_walk_recursive2 array_walk_recursive の改善版

array_walk_recursive の改善版
function array_walk_recursive2( array $array, callable $callback ): array

違いは下記。

  • 第3引数はなし
    • クロージャの use で十分だしそちらの方が優れている
  • コールバックは ($value, $key, $array, $keys) が渡ってくる
    • $value, $array はリファレンスにすることで書き換え可能
  • 返り値で返す
    • 元の array_walk_recursive の返り値はほとんど意味がない
    • 返り値が空いてるなら変に参照を使わず返り値の方がシンプル

array_walk_recursive で「この要素は伏せたいのに…」「このノードだけ触りたいのに…」ということはままあるが、

  • $array が渡ってくるので unset したり他のキーを生やしたりすることが可能
  • $keys が渡ってくるのでツリー構造の特定のノードだけ触ることが可能 になっている。

「map も filter も可能」という少しマッチョな関数。 実質的には「再帰的な array_kvmap」のように振る舞う。

Parameter
type name summary
​array $array 対象配列
​callable $callback コールバック
Return
type summary
​array walk 後の配列

[F] array_where 指定キーの要素で array_filter する

指定キーの要素で array_filter する
function array_where( iterable $array, string|array|null $column = null, ?callable $callback = null ): array

array_column があるなら array_where があってもいいはず。

$column はコールバックに渡ってくる配列のキー名を渡す。null を与えると行全体が渡ってくる。 $callback は絞り込み条件を渡す。null を与えると true 相当の値でフィルタする。 つまり $column も $callback も省略した場合、実質的に array_filter と同じ動作になる。

$column は配列を受け入れる。配列を渡した場合その値の共通項がコールバックに渡る。 連想配列の場合は「キーのカラム == 値」で filter する(それぞれで AND。厳密かどうかは $callback で指定。説明が難しいので Example を参照)。

$callback が要求するならキーも渡ってくる。

Example:

$array = [ 0 => ['id' => 1, 'name' => 'hoge', 'flag' => false], 1 => ['id' => 2, 'name' => 'fuga', 'flag' => true], 2 => ['id' => 3, 'name' => 'piyo', 'flag' => false], ]; // 'flag' が true 相当のものだけ返す that(array_where($array, 'flag'))->isSame([ 1 => ['id' => 2, 'name' => 'fuga', 'flag' => true], ]); // 'name' に 'h' を含むものだけ返す $contain_h = fn($name) => strpos($name, 'h') !== false; that(array_where($array, 'name', $contain_h))->isSame([ 0 => ['id' => 1, 'name' => 'hoge', 'flag' => false], ]); // $callback が引数2つならキーも渡ってくる(キーが 2 のものだけ返す) $equal_2 = fn($row, $key) => $key === 2; that(array_where($array, null, $equal_2))->isSame([ 2 => ['id' => 3, 'name' => 'piyo', 'flag' => false], ]); // $column に配列を渡すと共通項が渡ってくる $idname_is_2fuga = fn($idname) => ($idname['id'] . $idname['name']) === '2fuga'; that(array_where($array, ['id', 'name'], $idname_is_2fuga))->isSame([ 1 => ['id' => 2, 'name' => 'fuga', 'flag' => true], ]); // $column に連想配列を渡すと「キーのカラム == 値」で filter する(要するに「name が piyo かつ flag が false」で filter) that(array_where($array, ['name' => 'piyo', 'flag' => false]))->isSame([ 2 => ['id' => 3, 'name' => 'piyo', 'flag' => false], ]); // 上記において値に配列を渡すと in_array で判定される that(array_where($array, ['id' => [2, 3]]))->isSame([ 1 => ['id' => 2, 'name' => 'fuga', 'flag' => true], 2 => ['id' => 3, 'name' => 'piyo', 'flag' => false], ]); // $column の連想配列の値にはコールバックが渡せる(それぞれで AND) that(array_where($array, [ 'id' => fn($id) => $id >= 3, // id が 3 以上 'name' => fn($name) => strpos($name, 'o') !== false, // name に o を含む ]))->isSame([ 2 => ['id' => 3, 'name' => 'piyo', 'flag' => false], ]);
Parameter
type name summary
​iterable $array 対象配列
​string|​array|​null $column = null キー名
​?callable $callback = null 評価クロージャ
Return
type summary
​array $where が真を返した新しい配列

[F] array_zip 配列の各要素値で順番に配列を作る

配列の各要素値で順番に配列を作る
function array_zip(array ...$arrays): array

array_map(null, ...$arrays) とほぼ同義。ただし

  • 文字キーは保存される(数値キーは再割り振りされる)
  • 一つだけ配列を与えても構造は壊れない(array_map(null) は壊れる)

Example:

// 普通の zip that(array_zip( [1, 2, 3], ['hoge', 'fuga', 'piyo'] ))->is([[1, 'hoge'], [2, 'fuga'], [3, 'piyo']]); // キーが維持される that(array_zip( ['a' => 1, 2, 3], ['hoge', 'b' => 'fuga', 'piyo'] ))->is([['a' => 1, 'hoge'], [2, 'b' => 'fuga'], [3, 'piyo']]);
Parameter
type name summary
​array ...$arrays 対象配列(可変引数)
Return
type summary
​array 各要素値の配列

[F] arrayize 引数の配列を生成する。

引数の配列を生成する。
function arrayize(mixed ...$variadic): array

配列以外を渡すと配列化されて追加される。 配列を渡してもそのままだが、連番配列の場合はマージ、連想配列の場合は結合となる。 iterable や Traversable は考慮せずあくまで「配列」としてチェックする。

Example:

// 値は配列化される that(arrayize(1, 2, 3))->isSame([1, 2, 3]); // 配列はそのまま that(arrayize([1], [2], [3]))->isSame([1, 2, 3]); // 連想配列、連番配列の挙動 that(arrayize([1, 2, 3], [4, 5, 6], ['a' => 'A1'], ['a' => 'A2']))->isSame([1, 2, 3, 4, 5, 6, 'a' => 'A1']); // stdClass は foreach 可能だがあくまで配列としてチェックする $object = new \stdClass(); that(arrayize($object, false, [1, 2, 3]))->isSame([$object, false, 1, 2, 3]);
Parameter
type name summary
​mixed ...$variadic 生成する要素(可変引数)
Return
type summary
​array 引数を配列化したもの

[F] arrays 配列をシーケンシャルに走査するジェネレータを返す

配列をシーケンシャルに走査するジェネレータを返す
function arrays(iterable $array): \Generator

「シーケンシャルに」とは要するに数値連番が得られるように走査するということ。 0ベースの連番を作ってインクリメントしながら foreach するのと全く変わらない。

キーは連番、値は [$key, $value] で返す。 つまり、 Example のように foreach の list 構文を使えば「連番、キー、値」でループを回すことが可能になる。 「foreach で回したいんだけど連番も欲しい」という状況はまれによくあるはず。

Example:

$array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; $nkv = []; foreach (arrays($array) as $n => [$k, $v]) { $nkv[] = "$n,$k,$v"; } that($nkv)->isSame(['0,a,A', '1,b,B', '2,c,C']);
Parameter
type name summary
​iterable $array 対象配列
Return
type summary
Generator [$seq => [$key, $value]] を返すジェネレータ

[F] first_key 配列の最初のキーを返す

配列の最初のキーを返す
function first_key( iterable $array, mixed $default = null ): mixed

空の場合は $default を返す。

Example:

that(first_key(['a', 'b', 'c']))->isSame(0); that(first_key([], 999))->isSame(999);
Parameter
type name summary
​iterable $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 最初のキー

[F] first_keyvalue 配列の最初のキー/値ペアをタプルで返す

配列の最初のキー/値ペアをタプルで返す
function first_keyvalue( iterable|object $array, mixed $default = null ): array

空の場合は $default を返す。

Example:

that(first_keyvalue(['a', 'b', 'c']))->isSame([0, 'a']); that(first_keyvalue([], 999))->isSame(999);
Parameter
type name summary
​iterable|​object $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​array [最初のキー, 最初の値]

[F] first_value 配列の最初の値を返す

配列の最初の値を返す
function first_value( iterable $array, mixed $default = null ): mixed

空の場合は $default を返す。

Example:

that(first_value(['a', 'b', 'c']))->isSame('a'); that(first_value([], 999))->isSame(999);
Parameter
type name summary
​iterable $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 最初の値

[F] groupsort 配列を部分的にソートする

配列を部分的にソートする
function groupsort( iterable $array, callable $grouper, callable $comparator ): array

$grouper でグルーピングされた部分配列を $comparator でソートし、元の位置に埋め込む。 元の配列の並び順は可能な限り維持される。

$grouper はグループを決定づける何かを返す。 一意であれば何でも良いが、内部的に配列のキーに格納されるため、文字列であることが望ましい。

Example:

// 下記のような配列を元の順番を保ちつつ各々の group で部分的にソートする $array = [ ['id' => 1, 'group' => 'A', 'name' => 'q'], ['id' => 2, 'group' => 'A', 'name' => 'a'], ['id' => 3, 'group' => 'A', 'name' => 'z'], ['id' => 4, 'group' => null, 'name' => 'noise'], ['id' => 5, 'group' => 'B', 'name' => 'w'], ['id' => 6, 'group' => 'B', 'name' => 's'], ['id' => 7, 'group' => 'B', 'name' => 'x'], ['id' => 8, 'group' => 'C', 'name' => 'e'], ['id' => 9, 'group' => 'C', 'name' => 'd'], ['id' => 10, 'group' => null, 'name' => 'noise'], ['id' => 11, 'group' => 'C', 'name' => 'c'], ]; that(groupsort($array, fn($v, $k) => $v['group'], fn($a, $b) => $a['name'] <=> $b['name']))->is([ 1 => ["id" => 2, "group" => "A", "name" => "a"], 0 => ["id" => 1, "group" => "A", "name" => "q"], 2 => ["id" => 3, "group" => "A", "name" => "z"], 3 => ["id" => 4, "group" => null, "name" => "noise"], 5 => ["id" => 6, "group" => "B", "name" => "s"], 4 => ["id" => 5, "group" => "B", "name" => "w"], 6 => ["id" => 7, "group" => "B", "name" => "x"], 10 => ["id" => 11, "group" => "C", "name" => "c"], 8 => ["id" => 9, "group" => "C", "name" => "d"], 7 => ["id" => 8, "group" => "C", "name" => "e"], 9 => ["id" => 10, "group" => null, "name" => "noise"], ]);
Parameter
type name summary
​iterable $array 対象配列
​callable $grouper グループ導出関数
​callable $comparator 部分配列の比較関数
Return
type summary
​array 部分ソートされた配列

[F] in_array_and in_array の複数版(AND)

in_array の複数版(AND)
function in_array_and( array|mixed $needle, array $haystack, bool $strict = false ): bool

配列 $haystack が $needle の「すべてを含む」ときに true を返す。

$needle が非配列の場合は配列化される。 $needle が空の場合は常に false を返す。

Example:

that(in_array_and([1], [1, 2, 3]))->isTrue(); that(in_array_and([9], [1, 2, 3]))->isFalse(); that(in_array_and([1, 9], [1, 2, 3]))->isFalse();
Parameter
type name summary
​array|​mixed $needle 調べる値
​array $haystack 調べる配列
​bool $strict = false 厳密フラグ
Return
type summary
​bool $needle のすべてが含まれているなら true

[F] in_array_or in_array の複数版(OR)

in_array の複数版(OR)
function in_array_or( array|mixed $needle, array $haystack, bool $strict = false ): bool

配列 $haystack が $needle の「どれかを含む」ときに true を返す。

$needle が非配列の場合は配列化される。 $needle が空の場合は常に false を返す。

Example:

that(in_array_or([1], [1, 2, 3]))->isTrue(); that(in_array_or([9], [1, 2, 3]))->isFalse(); that(in_array_or([1, 9], [1, 2, 3]))->isTrue();
Parameter
type name summary
​array|​mixed $needle 調べる値
​array $haystack 調べる配列
​bool $strict = false 厳密フラグ
Return
type summary
​bool $needle のどれかが含まれているなら true

[F] is_hasharray 配列が連想配列か調べる

配列が連想配列か調べる
function is_hasharray(array $array): bool

空の配列は普通の配列とみなす。

Example:

that(is_hasharray([]))->isFalse(); that(is_hasharray([1, 2, 3]))->isFalse(); that(is_hasharray(['x' => 'X']))->isTrue();
Parameter
type name summary
​array $array 調べる配列
Return
type summary
​bool 連想配列なら true

[F] is_indexarray 配列が数値配列か調べる

配列が数値配列か調べる
function is_indexarray(array $array): bool

空の配列も数値配列とみなす。 さらにいわゆる「連番配列」ではなく「キーが数値のみか?」で判定する。

つまり、 is_hasharray とは排他的ではない。

Example:

that(is_indexarray([]))->isTrue(); that(is_indexarray([1, 2, 3]))->isTrue(); that(is_indexarray(['x' => 'X']))->isFalse(); // 抜け番があっても true になる(これは is_hasharray も true になる) that(is_indexarray([1 => 1, 2 => 2, 3 => 3]))->isTrue();
Parameter
type name summary
​array $array 調べる配列
Return
type summary
​bool 数値配列なら true

[F] kvsort 比較関数にキーも渡ってくる安定ソート

比較関数にキーも渡ってくる安定ソート
function kvsort( T $array, callable|int|null $comparator = null, callable|callable[] $schwartzians = [] ): T

比較関数は ($valueA, $valueB, $keyA, $keyB) という引数を取る。 「値で比較して同値だったらキーも見たい」という状況はまれによくあるはず。 さらに安定ソートであり、同値だとしても元の並び順は維持される。

$schwartzians を指定した場合は呼び出しが ($schwartzianA, $schwartzianB, $valueA, $valueB, $keyA, $keyB) になる。 $schwartzianX は単一値の場合はその結果、配列の場合はキー構造が維持されて渡ってくる。 このあたりは表現しにくいので Example を参照。

$comparator は省略できる。省略した場合、型に基づいてよしなにソートする。 (が、比較のたびに型チェックが入るので指定したほうが高速に動く)。

ただし、標準のソート関数とは異なり、参照渡しではなくソートして返り値で返す。 また、いわゆる asort であり、キー・値は常に維持される。

Example:

$array = [ 'a' => 3, 'b' => 1, 'c' => 2, 'x1' => 9, 'x2' => 9, 'x3' => 9, ]; // 普通のソート that(kvsort($array))->isSame([ 'b' => 1, 'c' => 2, 'a' => 3, 'x1' => 9, 'x2' => 9, 'x3' => 9, ]); // キーを使用したソート that(kvsort($array, fn($av, $bv, $ak, $bk) => strcmp($bk, $ak)))->isSame([ 'x3' => 9, 'x2' => 9, 'x1' => 9, 'c' => 2, 'b' => 1, 'a' => 3, ]); // シュワルツ変換を使用したソート(引数説明のために全て列挙している) that(kvsort($array, fn($hashA, $hashB, $av, $bv, $ak, $bk) => ($hashA['md5'] <=> $hashB['md5']) ?: ($hashA['sha1'] <=> $hashB['sha1']), [ 'md5' => fn($v) => md5($v), 'sha1' => fn($v) => sha1($v), ]))->isSame([ 'x1' => 9, 'x2' => 9, 'x3' => 9, 'b' => 1, 'c' => 2, 'a' => 3, ]); // シュワルツ変換の場合 $comparator は省略可能(昇順)で、配列ではなく単一値を渡せばその結果値が渡ってくる(これは要するに md5 での昇順ソート) that(kvsort($array, null, fn($v) => md5($v)))->isSame([ 'x1' => 9, 'x2' => 9, 'x3' => 9, 'b' => 1, 'c' => 2, 'a' => 3, ]);
Parameter
type name summary
T $array 対象配列
​callable|​int|​null $comparator = null 比較関数。SORT_XXX も使える
​callable|​callable[] $schwartzians = [] シュワルツ変換に使用する仮想列
Return
type summary
T ソートされた配列

[F] last_key 配列の最後のキーを返す

配列の最後のキーを返す
function last_key( iterable $array, mixed $default = null ): mixed

空の場合は $default を返す。

Example:

that(last_key(['a', 'b', 'c']))->isSame(2); that(last_key([], 999))->isSame(999);
Parameter
type name summary
​iterable $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 最後のキー

[F] last_keyvalue 配列の最後のキー/値ペアをタプルで返す

配列の最後のキー/値ペアをタプルで返す
function last_keyvalue( iterable|object $array, mixed $default = null ): array

空の場合は $default を返す。

Example:

that(last_keyvalue(['a', 'b', 'c']))->isSame([2, 'c']); that(last_keyvalue([], 999))->isSame(999);
Parameter
type name summary
​iterable|​object $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​array [最後のキー, 最後の値]

[F] last_value 配列の最後の値を返す

配列の最後の値を返す
function last_value( iterable $array, mixed $default = null ): mixed

空の場合は $default を返す。

Example:

that(last_value(['a', 'b', 'c']))->isSame('c'); that(last_value([], 999))->isSame(999);
Parameter
type name summary
​iterable $array 対象配列
​mixed $default = null 無かった場合のデフォルト値
Return
type summary
​mixed 最後の値

[F] next_key 配列の指定キーの次のキーを返す

配列の指定キーの次のキーを返す
function next_key( array $array, string|int|null $key = null ): string|int|bool|null

$key が最後のキーだった場合は null を返す。 $key が存在しない場合は false を返す。 $key が未指定だと「次に生成されるキー」($array[]='hoge' で生成されるキー)を返す。

$array[] = 'hoge' で作成されるキーには完全準拠しない(標準は unset すると結構乱れる)。公式マニュアルを参照。

Example:

$array = [9 => 9, 'a' => 'A', 'b' => 'B', 'c' => 'C']; // 'b' キーの次は 'c' that(next_key($array, 'b'))->isSame('c'); // 'c' キーの次は無いので null that(next_key($array, 'c'))->isSame(null); // 'x' キーはそもそも存在しないので false that(next_key($array, 'x'))->isSame(false); // 次に生成されるキーは 10 that(next_key($array, null))->isSame(10);
Parameter
type name summary
​array $array 対象配列
​string|​int|​null $key = null 調べるキー
Return
type summary
​string|​int|​bool|​null $key の次のキー

[F] prev_key 配列の指定キーの前のキーを返す

配列の指定キーの前のキーを返す
function prev_key( array $array, string|int $key ): string|int|bool|null

$key が最初のキーだった場合は null を返す。 $key が存在しない場合は false を返す。

Example:

$array = ['a' => 'A', 'b' => 'B', 'c' => 'C']; // 'b' キーの前は 'a' that(prev_key($array, 'b'))->isSame('a'); // 'a' キーの前は無いので null that(prev_key($array, 'a'))->isSame(null); // 'x' キーはそもそも存在しないので false that(prev_key($array, 'x'))->isSame(false);
Parameter
type name summary
​array $array 対象配列
​string|​int $key 調べるキー
Return
type summary
​string|​int|​bool|​null $key の前のキー

[N] ryunosuke\Functions\Package\classobj\

[F] functions

[F] auto_loader vendor/autoload.php を返す

vendor/autoload.php を返す
function auto_loader(?string $startdir = null): string

かなり局所的な実装で vendor ディレクトリを変更していたりするとそれだけで例外になる。

Example:

that(auto_loader())->contains('autoload.php');
Parameter
type name summary
​?string $startdir = null 高速化用の検索開始ディレクトリを指定するが、どちらかと言えばテスト用
Return
type summary
​string autoload.php のフルパス

[F] class_aliases 遅延ロードする class_alias

遅延ロードする class_alias
function class_aliases(array $aliases): array

class_alias は即座にオートロードされるが、この関数は必要とされるまでオートロードしない。

Example:

class_aliases([ 'TestCase' => \PHPUnit\Framework\TestCase::class, ]); that(class_exists('TestCase', false))->isFalse(); // オートロードを走らせなければまだ定義されていない that(class_exists('TestCase', true))->isTrue(); // オートロードを走らせなければ定義されている
Parameter
type name summary
​array $aliases
Return
type summary
​array エイリアス配列

[F] class_constants クラス定数を配列で返す

クラス定数を配列で返す
function class_constants( string|object $class, ?int $filter = null ): array

(new \ReflectionClass($class))->getConstants() とほぼ同じだが、可視性でフィルタができる。 さらに「自分自身の定義か?」でもフィルタできる。

Example:

$class = new class extends \ArrayObject { private const C_PRIVATE = 'private'; protected const C_PROTECTED = 'protected'; public const C_PUBLIC = 'public'; }; // 普通に全定数を返す that(class_constants($class))->isSame([ 'C_PRIVATE' => 'private', 'C_PROTECTED' => 'protected', 'C_PUBLIC' => 'public', 'STD_PROP_LIST' => \ArrayObject::STD_PROP_LIST, 'ARRAY_AS_PROPS' => \ArrayObject::ARRAY_AS_PROPS, ]); // public のみを返す that(class_constants($class, IS_PUBLIC))->isSame([ 'C_PUBLIC' => 'public', 'STD_PROP_LIST' => \ArrayObject::STD_PROP_LIST, 'ARRAY_AS_PROPS' => \ArrayObject::ARRAY_AS_PROPS, ]); // 自身定義でかつ public のみを返す that(class_constants($class, IS_OWNSELF | IS_PUBLIC))->isSame([ 'C_PUBLIC' => 'public', ]);
Parameter
type name summary
​string|​object $class クラス名 or オブジェクト
​?int $filter = null アクセスレベル定数
Return
type summary
​array クラス定数の配列

[F] class_extends インスタンスを動的に拡張する

インスタンスを動的に拡張する
function class_extends( T $object, \Closure[] $methods, array $fields = [], array $implements = [] ): T

インスタンスに特異メソッド・特異フィールドのようなものを生やす。 ただし、特異フィールドの用途はほとんどない(php はデフォルトで特異フィールドのような動作なので)。 そのクラスの __set/__get が禁止されている場合に使えるかもしれない程度。

クロージャ配列を渡すと特異メソッドになる。 そのクロージャの $this は元オブジェクトで bind される。 ただし、static closure を渡した場合はそれは static メソッドとして扱われる。

$implements でインターフェースの配列を渡すとすべてが動的に implement される。 つまり得られたオブジェクトが instanceof を通るようになる。 もちろんメソッド配列としてその名前が含まれていなければならない。

内部的にはいわゆる Decorator パターンを動的に実行しているだけであり、実行速度は劣悪。 当然ながら final クラス/メソッドの拡張もできない。

Example:

// Exception に「count」メソッドと「コードとメッセージを結合して返す」メソッドを動的に生やす $object = new \Exception('hoge', 123); $newobject = class_extends($object, [ 'count' => function () { return $this->code; }, 'codemessage' => function () { // bind されるので protected フィールドが使える return $this->code . ':' . $this->message; }, ], [], [\Countable::class]); that($newobject->count())->isSame(123); that($newobject->codemessage())->isSame('123:hoge'); that($newobject)->isInstanceOf(\Countable::class); // instanceof をパスできる // オーバーライドもできる(ArrayObject の count を2倍になるように上書き) $object = new \ArrayObject([1, 2, 3]); $newobject = class_extends($object, [ 'count' => function () { // parent は元オブジェクトを表す return parent::count() * 2; }, ]); that($newobject->count())->isSame(6);
Parameter
type name summary
T $object 対象オブジェクト
Closure[] $methods 注入するメソッド
​array $fields = [] 注入するフィールド
​array $implements = [] 実装するインターフェース
Return
type summary
T $object を拡張した object

[F] class_loader composer のクラスローダを返す

composer のクラスローダを返す
function class_loader(?string $startdir = null): Composer\Autoload\ClassLoader

かなり局所的な実装で vendor ディレクトリを変更していたりするとそれだけで例外になる。

Example:

that(class_loader())->isInstanceOf(\Composer\Autoload\ClassLoader::class);
Parameter
type name summary
​?string $startdir = null 高速化用の検索開始ディレクトリを指定するが、どちらかと言えばテスト用
Return
type summary
ClassLoader クラスローダ

[F] class_map 指定クラスローダで読み込まれるであろう class => file を返す

指定クラスローダで読み込まれるであろう class => file を返す
function class_map( ?object|?Composer\Autoload\ClassLoader $loader = null, ?string $basePath = null, bool $cache = true ): array

実質的には composer で読み込まれるクラスマップを返す。 つまり dump-autoload -o したときの getClassMap 相当を返す。

ファイル名からクラス名を逆引きする都合上、猛烈に遅いので注意。

Parameter
type name summary
​?object|​?ClassLoader $loader = null オートローダオブジェクト
​?string $basePath = null パスが相対パスだった場合の基底ディレクトリ
​bool $cache = true キャッシュを使用するか
Return
type summary
​array [class => file] の配列

[F] class_namespace クラスの名前空間部分を取得する

クラスの名前空間部分を取得する
function class_namespace(string|object $class): string

Example:

that(class_namespace('vendor\\namespace\\ClassName'))->isSame('vendor\\namespace');
Parameter
type name summary
​string|​object $class 対象クラス・オブジェクト
Return
type summary
​string クラスの名前空間

[F] class_replace 既存(未読み込みに限る)クラスを強制的に置換する

既存(未読み込みに限る)クラスを強制的に置換する
function class_replace( string $class, \Closure|object|array $register )

例えば継承ツリーが下記の場合を考える。

classA <- classB <- classC

この場合、「classC は classB に」「classB は classA に」それぞれ依存している、と考えることができる。 これは静的に決定的であり、この依存を壊したり注入したりする手段は存在しない。 例えば classA の実装を差し替えたいときに、いかに classA を継承した classAA を定義したとしても classB の親は classA で決して変わらない。

この関数を使うと本当に classA そのものを弄るので、継承ツリーを下記のように変えることができる。

classA <- classAA <- classB <- classC

つまり、classA を継承した classAA を定義してそれを classA とみなすことが可能になる。 ただし、内部的には class_alias を使用して実現しているので厳密には異なるクラスとなる。

実際のところかなり強力な機能だが、同時にかなり黒魔術的なので乱用は控えたほうがいい。

Example:

// Y1 extends X1 だとしてクラス定義でオーバーライドする class_replace('\\ryunosuke\\Test\\Package\\files\\classes\\X1', function () { // アンスコがついたクラスが定義されるので匿名クラスを返す return new class() extends \ryunosuke\Test\Package\files\classes\X1_ { function method(){return 'this is X1d';} function newmethod(){return 'this is newmethod';} }; }); // X1 を継承している Y1 にまで影響が出ている(X1 を完全に置換できたということ) that((new \ryunosuke\Test\Package\files\classes\Y1())->method())->isSame('this is X1d'); that((new \ryunosuke\Test\Package\files\classes\Y1())->newmethod())->isSame('this is newmethod'); // Y2 extends X2 だとしてクロージャ配列でオーバーライドする class_replace('\\ryunosuke\\Test\\Package\\files\classes\\X2', fn() => [ 'method' => function () {return 'this is X2d';}, 'newmethod' => function () {return 'this is newmethod';}, ]); // X2 を継承している Y2 にまで影響が出ている(X2 を完全に置換できたということ) that((new \ryunosuke\Test\Package\files\classes\Y2())->method())->isSame('this is X2d'); that((new \ryunosuke\Test\Package\files\classes\Y2())->newmethod())->isSame('this is newmethod'); // メソッド定義だけであればクロージャではなく配列指定でも可能。さらに trait 配列を渡すとそれらを use できる class_replace('\\ryunosuke\\Test\\Package\\files\classes\\X3', [ [\ryunosuke\Test\Package\files\classes\XTrait::class], 'method' => function () {return 'this is X3d';}, ]); // X3 を継承している Y3 にまで影響が出ている(X3 を完全に置換できたということ) that((new \ryunosuke\Test\Package\files\classes\Y3())->method())->isSame('this is X3d'); // トレイトのメソッドも生えている that((new \ryunosuke\Test\Package\files\classes\Y3())->traitMethod())->isSame('this is XTrait::traitMethod'); // メソッドとトレイトだけならば無名クラスを渡すことでも可能 class_replace('\\ryunosuke\\Test\\Package\\files\classes\\X4', new class() { use \ryunosuke\Test\Package\files\classes\XTrait; function method(){return 'this is X4d';} }); // X4 を継承している Y4 にまで影響が出ている(X4 を完全に置換できたということ) that((new \ryunosuke\Test\Package\files\classes\Y4())->method())->isSame('this is X4d'); // トレイトのメソッドも生えている that((new \ryunosuke\Test\Package\files\classes\Y4())->traitMethod())->isSame('this is XTrait::traitMethod');
Parameter
type name summary
​string $class 対象クラス名
Closure|​object|​array $register 置換クラスを定義 or 返すクロージャ or 定義メソッド配列 or 無名クラス

[F] class_shorten クラスの名前空間部分を除いた短い名前を取得する

クラスの名前空間部分を除いた短い名前を取得する
function class_shorten(string|object $class): string

Example:

that(class_shorten('vendor\\namespace\\ClassName'))->isSame('ClassName');
Parameter
type name summary
​string|​object $class 対象クラス・オブジェクト
Return
type summary
​string クラスの短い名前

[F] class_uses_all クラスが use しているトレイトを再帰的に取得する

クラスが use しているトレイトを再帰的に取得する
function class_uses_all( string|object $class, bool $autoload = true ): array

トレイトが use しているトレイトが use しているトレイトが use している・・・のような場合もすべて返す。

Example:

trait T1{} trait T2{use T1;} trait T3{use T2;} that(class_uses_all(new class{use T3;}))->isSame([ 'Example\\T3' => 'Example\\T3', // クラスが直接 use している 'Example\\T2' => 'Example\\T2', // T3 が use している 'Example\\T1' => 'Example\\T1', // T2 が use している ]);
Parameter
type name summary
​string|​object $class
​bool $autoload = true オートロードを呼ぶか
Return
type summary
​array トレイト名の配列

[F] const_exists クラス定数が存在するか調べる

クラス定数が存在するか調べる
function const_exists( string|object $classname, string $constname = "" ): bool

グローバル定数も調べられる。ので実質的には defined とほぼ同じで違いは下記。

  • defined は単一引数しか与えられないが、この関数は2つの引数も受け入れる
  • defined は private const で即死するが、この関数はきちんと調べることができる
  • ClassName::class は常に true を返す

あくまで存在を調べるだけで実際にアクセスできるかは分からないので注意(property_exists と同じ)。

Example:

// クラス定数が調べられる(1引数、2引数どちらでも良い) that(const_exists('ArrayObject::STD_PROP_LIST'))->isTrue(); that(const_exists('ArrayObject', 'STD_PROP_LIST'))->isTrue(); that(const_exists('ArrayObject::UNDEFINED'))->isFalse(); that(const_exists('ArrayObject', 'UNDEFINED'))->isFalse(); // グローバル(名前空間)もいける that(const_exists('PHP_VERSION'))->isTrue(); that(const_exists('UNDEFINED'))->isFalse();
Parameter
type name summary
​string|​object $classname 調べるクラス
​string $constname = "" 調べるクラス定数
Return
type summary
​bool 定数が存在するなら true

[F] namespace_detect ディレクトリ構造から名前空間を推測して返す

ディレクトリ構造から名前空間を推測して返す
function namespace_detect(string $location): string

指定パスに名前空間を持つような php ファイルが有るならその名前空間を返す。 指定パスに名前空間を持つような php ファイルが無いなら親をたどる。 親に名前空間を持つような php ファイルが有るならその名前空間+ローカルパスを返す。

言葉で表すとややこしいが、「そのパスに配置しても違和感の無い名前空間」を返してくれるはず。

Example:

// Example 用としてこのパッケージの Transporter を使用してみる $dirname = dirname(class_loader()->findFile(\ryunosuke\Functions\Transporter::class)); // "$dirname/Hoge" の名前空間を推測して返す that(namespace_detect("$dirname/Hoge"))->isSame("ryunosuke\\Functions\\Hoge");
Parameter
type name summary
​string $location 配置パス。ファイル名を与えるとそのファイルを配置すべきクラス名を返す
Return
type summary
​string 名前空間

[F] object_dive パス形式でプロパティ値を取得

パス形式でプロパティ値を取得
function object_dive( object $object, string|array $path, mixed $default = null, string $delimiter = "." ): mixed

存在しない場合は $default を返す。

Example:

$class = (object) [ 'a' => (object) [ 'b' => (object) [ 'c' => 'vvv' ] ] ]; that(object_dive($class, 'a.b.c'))->isSame('vvv'); that(object_dive($class, 'a.b.x', 9))->isSame(9); // 配列を与えても良い。その場合 $delimiter 引数は意味をなさない that(object_dive($class, ['a', 'b', 'c']))->isSame('vvv');
Parameter
type name summary
​object $object 調べるオブジェクト
​string|​array $path パス文字列。配列も与えられる
​mixed $default = null 無かった場合のデフォルト値
​string $delimiter = "." パスの区切り文字。大抵は '.' か '/'
Return
type summary
​mixed パスが示すプロパティ値

[F] object_id 重複しない spl_object_id

重複しない spl_object_id
function object_id(null|object|int $objectOrId): null|int|object

内部でオブジェクト自体は保持しない。 つまり、そのオブジェクトは GC の対象になる。

オブジェクトを与えると一意な整数を返す。 内部で連番を保持するので PHP_INT_MAX まで生成できる。 PHP_INT_MAX を超えた場合の挙動は未定義(まぁまずありえないだろう)。 null は特別扱いとして必ず 0 を返す。

逆に整数を与えると対応したオブジェクトを返す。 設定していないか既に GC されている場合は null を返す。 0 は特別扱いとして必ず null を返す。

Example:

// spl_object_id は容易に重複するが・・・ that(spl_object_id(new \stdClass()) === spl_object_id(new \stdClass()))->isTrue(); // この object_id 関数は重複しない that(object_id(new \stdClass()) === object_id(new \stdClass()))->isFalse(); $o = new \stdClass(); // オブジェクトを与えると固有IDを返す that($id = object_id($o))->isInt(); // そのIDを与えると元のオブジェクトが得られる that($o === object_id($id))->isTrue(); // 参照を握っているわけではないので GC されていると null を返す unset($o); that(null === object_id($id))->isTrue();
Parameter
type name summary
​null|​object|​int $objectOrId 対象オブジェクト or オブジェクトID
Return
type summary
​null|​int|​object オブジェクトID or 対象オブジェクト

[F] object_properties オブジェクトのプロパティを可視・不可視を問わず取得する

オブジェクトのプロパティを可視・不可視を問わず取得する
function object_properties( object $object, array &$privates = [] ): array

get_object_vars + no public プロパティを返すイメージ。 クロージャだけは特別扱いで this + use 変数を返す。

Example:

$object = new #[\AllowDynamicProperties] class('something', 42) extends \Exception{}; $object->oreore = 'oreore'; // get_object_vars はそのスコープから見えないプロパティを取得できない // var_dump(get_object_vars($object)); // array キャストは全て得られるが null 文字を含むので扱いにくい // var_dump((array) $object); // この関数を使えば不可視プロパティも取得できる that(object_properties($object))->subsetEquals([ 'message' => 'something', 'code' => 42, 'oreore' => 'oreore', ]); // クロージャは this と use 変数を返す that(object_properties(fn() => $object))->is([ 'this' => $this, 'object' => $object, ]);
Parameter
type name summary
​object $object オブジェクト
​array &$privates = [] 継承ツリー上の private が格納される
Return
type summary
​array 全プロパティの配列

[F] object_storage オブジェクトに付加データを付与する

オブジェクトに付加データを付与する
function object_storage(string $namespace = "global"): ObjectStorage|object

実質的に WeakMap と同じ。 ただし php 内部では本来オブジェクトであるべきものもリソースとして扱われる(curl とか)ので統一のためにリソースも扱える。

典型的な利用法として「クロージャに値を持たせたい」がある。 クロージャに限らず、大抵の内部オブジェクトは動的プロパティを生やせないので、値の保持がめんどくさい。 (spl_object_id は重複するので使えないし、下手に実装すると参照が握られて GC されない羽目になる)。

Example:

$storage = object_storage('test'); $closure = fn() => 123; $resource = tmpfile(); // このように set すると・・・ $storage->set($closure, 'attached data1'); // get で取り出せる that($storage->get($closure))->isSame('attached data1'); // リソースも扱える $storage->set($resource, 'attached data2'); that($storage->get($resource))->isSame('attached data2'); // 名前空間が同じならインスタンスをまたいで取得できる $storage2 = object_storage('test'); that($storage2->get($closure))->isSame('attached data1'); // オブジェクトが死ぬと同時に消える unset($closure); that($storage2->count())->isSame(1); // リソースの場合は close でも消える fclose($resource); that($storage2->count())->isSame(0); that($storage2->get($resource))->is(null);
Parameter
type name summary
​string $namespace = "global" 名前空間
Return
type summary
ObjectStorage|​object ストレージオブジェクト

[F] register_autoload_function オートロード前後にコールバックする

オートロード前後にコールバックする
function register_autoload_function( ?callable $before = null, ?callable $after = null ): object

before を使って「クラスを読み込み前に動的に書き換える」のようなことが可能になる。 after を使って「__initialize を呼ぶ」のような規約を定めればスタティックイニシャライザのようなことが可能になる。

prepend なオートローダで実装してあるので、その後のさらに prepend されたようなオートローダでの読み込みは感知できない。 もちろんロード済みクラスも感知できない。

before 内で無条件にクラスを呼ぶと無限ループになるので注意(オートローダが呼ばれて before が呼ばれてオートローダが呼ばれて before が呼ばれて・・・)。

ローダーオブジェクトを返すが特に意味はなく、使うべきではない。

Parameter
type name summary
​?callable $before = null 読み込み前コールバック
​?callable $after = null 読み込み後コールバック
Return
type summary
​object ローダーオブジェクト

[F] stdclass stdClass を生成して返す

stdClass を生成して返す
function stdclass(mixed ...$fields): \stdClass

object キャストとほとんど同じだが、名前付き可変引数を採用しているので JSON ライクに宣言することができる。 その代わり数値キー等の php の識別子として不正なキーを生やすことはできない。 (厳密に言えば名前付き引数を使わなければ数値キーは生成できるが…そんなことをするなら普通に object キャストをすればよい)。

Example:

// 名前付き可変引数でコールできる that(stdclass(a: 1, b: 2))->isInstanceOf(\stdClass::class); // iterable も渡せる(この場合は実質的に object キャストと同義) that(stdclass(...['a' => 1, 'b' => 2]))->isInstanceOf(\stdClass::class);
Parameter
type name summary
​mixed ...$fields メンバー配列
Return
type summary
stdClass stdClass

[F] type_exists 型が存在するか返す

型が存在するか返す
function type_exists( string $typename, bool $autoload = true ): bool

class/interface/trait/enum exists の合せ技。 trait/enum のように今後型的なものがさらに増えるかもしれないし、class_exists だけして interface/trait が抜けているコードを何度も見てきた。 それを一元管理するような関数となる。

Example:

that(class_exists(\Throwable::class))->isFalse(); // class_exists は class にしか反応しない that(interface_exists(\Exception::class))->isFalse(); // interface_exists は interface にしか反応しない that(trait_exists(\Error::class))->isFalse(); // trait_exists は trait にしか反応しない // type_exists であれば全てに反応する that(type_exists(\Throwable::class))->isTrue(); that(type_exists(\Exception::class))->isTrue(); that(type_exists(\Error::class))->isTrue();
Parameter
type name summary
​string $typename 調べる型名
​bool $autoload = true オートロードを行うか
Return
type summary
​bool 型が存在するなら true

[N] ryunosuke\Functions\Package\database\

[F] functions

[F] sql_bind ものすごく雑に SQL に値を埋め込む

ものすごく雑に SQL に値を埋め込む
function sql_bind( string $sql, array|mixed $values, ?callable $quote = null ): mixed

非常に荒くアドホックに実装しているのでこの関数で得られた SQL を実際に実行してはならない あくまでログ出力やデバッグ用途で視認性を高める目的である。

プレースホルダは ? か :alnum で混在していても良い。

Example:

that(sql_bind('select ?', 1))->isSame("select 1"); that(sql_bind('select :hoge', ['hoge' => 'hoge']))->isSame("select 'hoge'"); that(sql_bind('select ?, :hoge', [1, 'hoge' => 'hoge']))->isSame("select 1, 'hoge'");
Parameter
type name summary
​string $sql 値を埋め込む SQL
​array|​mixed $values 埋め込む値
​?callable $quote = null 値をクォートするクロージャ
Return
type summary
​mixed 値が埋め込まれた SQL

[F] sql_format ものすごく雑に SQL を整形する

ものすごく雑に SQL を整形する
function sql_format( string $sql, array $options = [] ): string

非常に荒くアドホックに実装しているのでこの関数で得られた SQL を実際に実行してはならない あくまでログ出力やデバッグ用途で視認性を高める目的である。

JOIN 句は FROM 句とみなさず、別句として処理する。 AND と && は微妙に処理が異なる。 AND は改行されるが && は改行されない(OR と || も同様)。

Parameter
type name summary
​string $sql 整形する SQL
​array $options = [] 整形オプション
Return
type summary
​string 整形された SQL

[F] sql_quote ものすごく雑に値をクオートする

ものすごく雑に値をクオートする
function sql_quote(mixed $value): mixed

非常に荒くアドホックに実装しているのでこの関数で得られた値で実際に実行してはならない あくまでログ出力やデバッグ用途で視認性を高める目的である。

  • null は NULL になる
  • 数字はそのまま数字になる
  • bool は 0 or 1 になる
  • 配列は再帰的にカンマ区切りになる
    • この実装はエラー回避の意味合いが強く、実装は変更される可能性がある
  • それ以外は addcslashes される

Example:

that(sql_quote(null))->isSame('NULL'); that(sql_quote(123))->isSame(123); that(sql_quote(true))->isSame(1); that(sql_quote("hoge"))->isSame("'hoge'"); that(sql_quote([1, 2, 3]))->isSame("1,2,3");
Parameter
type name summary
​mixed $value クオートする値
Return
type summary
​mixed クオートされた値

[N] ryunosuke\Functions\Package\dataformat\

[F] functions

[F] css_selector CSS セレクタ文字をパースして配列で返す

CSS セレクタ文字をパースして配列で返す
function css_selector(string $selector): array

包含などではない属性セレクタを与えると属性として認識する。 独自仕様として・・・

[!attr]
否定属性として false を返す
styles
style 属性とみなす

がある。

Example:

that(css_selector('#hoge.c1.c2[name=hoge\[\]][href="http://hoge"][hidden][!readonly]{width:123px;height:456px}'))->is([ 'id' => 'hoge', 'class' => ['c1', 'c2'], 'name' => 'hoge[]', 'href' => 'http://hoge', 'hidden' => true, 'readonly' => false, 'style' => [ 'width' => '123px', 'height' => '456px', ], ]);
Parameter
type name summary
​string $selector CSS セレクタ
Return
type summary
​array 属性配列

[F] csv_export 連想配列の配列を CSV 的文字列に変換する

連想配列の配列を CSV 的文字列に変換する
function csv_export( iterable $csvarrays, array $options = [] ): string|int

CSV ヘッダ行は全連想配列のキーの共通項となる。 順番には依存しないが、余計な要素があってもそのキーはヘッダには追加されないし、データ行にも含まれない。 ただし、オプションで headers が与えられた場合はそれを使用する。 この headers オプションに連想配列を与えるとヘッダ文字列変換になる([key => header] で「key を header で吐き出し」となる)。 数値配列を与えると単純に順序指定での出力指定になるが、ヘッダ行が出力されなくなる。

callback オプションが渡された場合は「あらゆる処理の最初」にコールされる。 つまりヘッダの読み換えや文字エンコーディングの変換が行われる前の状態でコールされる。 また、 false を返すとその要素はスルーされる。

output オプションにリソースを渡すとそこに対して書き込みが行われる(fclose はされない)。

Example:

// シンプルな実行例 $csvarrays = [ ['a' => 'A1', 'b' => 'B1', 'c' => 'C1'], // 普通の行 ['c' => 'C2', 'a' => 'A2', 'b' => 'B2'], // 順番が入れ替わっている行 ['c' => 'C3', 'a' => 'A3', 'b' => 'B3', 'x' => 'X'], // 余計な要素が入っている行 ]; that(csv_export($csvarrays))->is("a,b,c A1,B1,C1 A2,B2,C2 A3,B3,C3 "); // ヘッダを指定できる that(csv_export($csvarrays, [ 'headers' => ['a' => 'A', 'c' => 'C'], // a と c だけを出力+ヘッダ文字変更 ]))->is("A,C A1,C1 A2,C2 A3,C3 "); // ヘッダ行を出さない that(csv_export($csvarrays, [ 'headers' => ['a', 'c'], // a と c だけを出力+ヘッダ行なし ]))->is("A1,C1 A2,C2 A3,C3 "); // structure:true で配列も扱える that(csv_export([ ['scalar' => '123', 'list' => ['list11', 'list12'], 'hash' => ['a' => 'hash1A', 'b' => 'hash1B']], ['scalar' => '456', 'list' => ['list21', 'list22'], 'hash' => ['a' => 'hash2A', 'b' => 'hash2B']], ], [ 'structure' => true, ]))->is("scalar,list[],list[],hash[a],hash[b] 123,list11,list12,hash1A,hash1B 456,list21,list22,hash2A,hash2B ");
Parameter
type name summary
​iterable $csvarrays 連想配列の配列
​array $options = [] オプション配列。fputcsv の第3引数以降もここで指定する
Return
type summary
​string|​int CSV 的文字列。output オプションを渡した場合は書き込みバイト数

[F] csv_import CSV 的文字列を連想配列の配列に変換する

CSV 的文字列を連想配列の配列に変換する
function csv_import( string|resource $csvstring, array $options = [] ): array

1行目をヘッダ文字列とみなしてそれをキーとした連想配列の配列を返す。 ただし、オプションで headers が与えられた場合はそれを使用する。 この headers オプションはヘッダフィルタも兼ねる([n => header] で「n 番目フィールドを header で取り込み」となる)。 入力にヘッダがありかつ headers に連想配列が渡された場合はフィルタ兼読み換えとなる(Example を参照)。

structure オプションが渡された場合は query like なヘッダーで配列になる。

callback オプションが渡された場合は「あらゆる処理の最後」にコールされる。 つまりヘッダの読み換えや文字エンコーディングの変換が行われた後の状態でコールされる。 また、 false を返すとその要素はスルーされる。

メモリ効率は意識しない(どうせ配列を返すので意識しても無駄)。

Example:

// シンプルな実行例 that(csv_import(" a,b,c A1,B1,C1 A2,B2,C2 A3,B3,C3 "))->is([ ['a' => 'A1', 'b' => 'B1', 'c' => 'C1'], ['a' => 'A2', 'b' => 'B2', 'c' => 'C2'], ['a' => 'A3', 'b' => 'B3', 'c' => 'C3'], ]); // ヘッダを指定できる that(csv_import(" A1,B1,C1 A2,B2,C2 A3,B3,C3 ", [ 'headers' => [0 => 'a', 2 => 'c'], // 1がないので1番目のフィールドを読み飛ばしつつ、0, 2 は "a", "c" として取り込む ]))->is([ ['a' => 'A1', 'c' => 'C1'], ['a' => 'A2', 'c' => 'C2'], ['a' => 'A3', 'c' => 'C3'], ]); // ヘッダありで連想配列で指定するとキーの読み換えとなる(指定しなければ読み飛ばしも行える) that(csv_import(" a,b,c A1,B1,C1 A2,B2,C2 A3,B3,C3 ", [ 'headers' => ['a' => 'hoge', 'c' => 'piyo'], // a は hoge, c は piyo で読み込む。 b は指定がないので飛ばされる ]))->is([ ['hoge' => 'A1', 'piyo' => 'C1'], ['hoge' => 'A2', 'piyo' => 'C2'], ['hoge' => 'A3', 'piyo' => 'C3'], ]); // structure:true で配列も扱える that(csv_import(" scalar,list[],list[],hash[a],hash[b] 123,list11,list12,hash1A,hash1B 456,list21,list22,hash2A,hash2B ", [ 'structure' => true, ]))->is([ ['scalar' => '123', 'list' => ['list11', 'list12'], 'hash' => ['a' => 'hash1A', 'b' => 'hash1B']], ['scalar' => '456', 'list' => ['list21', 'list22'], 'hash' => ['a' => 'hash2A', 'b' => 'hash2B']], ]);
Parameter
type name summary
​string|​resource $csvstring CSV 的文字列。ファイルポインタでも良いが終了後に必ず閉じられる
​array $options = [] オプション配列。fgetcsv の第3引数以降もここで指定する
Return
type summary
​array 連想配列の配列

[F] html_attr 配列を html の属性文字列に変換する

配列を html の属性文字列に変換する
function html_attr( iterable $array, string|array|null $options = [] ): string|array

data-* や style, 論理属性など、全てよしなに変換して文字列で返す。 返り値の文字列はエスケープが施されており、基本的にはそのまま html に埋め込んで良い。 (オプション次第では危険だったり乱れたりすることはある)。

separator オプションを指定すると属性の区切り文字を指定できる。 大抵の場合は半角スペースであり、少し特殊な場合に改行文字を指定するくらいだろう。 ただし、この separator に null を与えると文字列ではなく生の配列で返す。 この配列は 属性名 => 属性値 な生の配列であり、エスケープも施されていない。 $options 自体に文字列を与えた場合は separator 指定とみなされる。

属性の変換ルールは下記。

  • 属性名が数値の場合は属性としては生成されない
  • 属性名は camelCase -> cahin-case の変換が行われる
  • 値が null の場合は無条件で無視される
    • 下記 false との違いは「配列返しの場合に渡ってくるか?」である(null は無条件フィルタなので配列返しでも渡ってこない)
  • 値が true の場合は論理属性とみなし値なしで生成される
  • 値が false の場合は論理属性とみなし、 属性としては生成されない
  • 値が配列の場合は ","(カンマ)で結合される
    • これは観測範囲内でカンマ区切りが多いため(srcset, accept など)。属性によってはカンマが適切ではない場合がある
    • 更にその配列が文字キーを持つ場合、キーが "=" で結合される
      • これは観測範囲内で = 区切りが多いため(viewport など)。属性によっては = が適切ではない場合がある
  • 値が配列で属性名が class, style, data の場合は下記の特殊な挙動を示す
    class
    半角スペースで結合される
    • キーは無視される
    style
    キーが css 名、値が css 値として ";" で結合される
    • キーは cahin-case に変換される
    • キーが数値の場合は値がそのまま追加される
    • 値が配列の場合は半角スペースで結合される
    data-
    キーが data 名、値が data 値として data 属性になる
    • キーは cahin-case に変換される
    • 値が真偽値以外のスカラーの場合はそのまま、非スカラー||真偽値の場合は json で埋め込まれる
      • これは jQuery において json をよしなに扱うことができるため

※ 上記における「配列」とは iterable を指すが、toString を実装した iterable なオブジェクトは toString が優先され、文字列とみなされる

複雑に見えるが「よしなに」やってくれると考えて良い。 配列や真偽値で分岐が大量にあるが、大前提として「string だった場合は余計なことはしない」がある。 ので迷ったり予期しない結果の場合は呼び出し側で文字列化して呼べば良い。

Example:

that(html_attr([ // camelCase は camel-case になる 'camelCase' => '<value>', // true は論理属性 true とみなし、値なし属性になる 'checked' => true, // false は論理属性 false とみなし、属性として現れない 'disabled' => false, // null は無条件で無視され、属性として現れない 'readonly' => null, // 配列はカンマで結合される 'srcset' => [ 'hoge.jpg 1x', 'fuga.jpg 2x', ], // 連想配列は = で結合される 'content' => [ 'width' => 'device-width', 'scale' => '1.0', ], // class はスペースで結合される 'class' => ['hoge', 'fuga'], // style 原則的に proerty:value; とみなす 'style' => [ 'color' => 'red', 'backgroundColor' => 'white', // camel-case になる 'margin' => [1, 2, 3, 4], // スペースで結合される 'opacity:0.5', // 直値はそのまま追加される ], // data- はその分属性が生える 'data-' => [ 'camelCase' => 123, 'hoge' => false, // 真偽値は文字列として埋め込まれる 'fuga' => "fuga", // 文字列はそのまま文字列 'piyo' => ['a' => 'A'], // 非スカラー以外は json になる ], ], ['separator' => "\n"]))->is('camel-case="&lt;value&gt;" checked srcset="hoge.jpg 1x,fuga.jpg 2x" content="width=device-width,scale=1.0" class="hoge fuga" style="color:red;background-color:white;margin:1 2 3 4;opacity:0.5" data-camel-case="123" data-hoge="false" data-fuga="fuga" data-piyo="{&quot;a&quot;:&quot;A&quot;}"');
Parameter
type name summary
​iterable $array 属性配列
​string|​array|​null $options = [] オプション配列
Return
type summary
​string|​array 属性文字列 or 属性配列

[F] html_strip html の空白類を除去して minify する

html の空白類を除去して minify する
function html_strip( string $html, array $options = [] ): string

文字列的ではなく DOM 的に行うのでおかしな部分 html を食わせると意図しない結果になる可能性がある。 その副作用として属性のクオートやタグ内空白は全て正規化される。

html コメントも削除される。 また、空白が意味を持つタグ(textarea, pre)は対象にならない。 さらに、php を含むような html (テンプレート)の php タグは一切の対象外となる。

これらの挙動の一部はオプションで指定が可能。

Example:

// e.g. id が " でクオートされている // e.g. class のクオートが " になっている // e.g. タグ内空白(id, class の間隔等)がスペース1つになっている // e.g. php タグは一切変更されていない // e.g. textarea は保持されている that(html_strip("<span id=id class='c1 c2 c3'><?= '<hoge> </hoge>' ?> a b c </span> <pre> a b c </pre>"))->isSame('<span id="id" class="c1 c2 c3"><?= \'<hoge> </hoge>\' ?> a b c </span><pre> a b c </pre>');
Parameter
type name summary
​string $html html 文字列
​array $options = [] オプション配列
Return
type summary
​string 空白除去された html 文字列

[F] html_tag css セレクタから html 文字列を生成する

css セレクタから html 文字列を生成する
function html_tag(string|array $selector): string

tag#id.class[attr=value] のような css セレクタから <tag id="id" class="class" attr="value"></tag> のような html 文字列を返す。 配列を与えるとキーがセレクタ、値がコンテント文字列になる。 さらに値が配列だと再帰して生成する。

値や属性は適切に htmlspecialchars される。

Example:

// 単純文字列はただのタグを生成する that( html_tag('a#hoge.c1.c2[name=hoge\[\]][href="http://hoge"][hidden]')) ->isSame('<a id="hoge" class="c1 c2" name="hoge[]" href="http://hoge" hidden></a>' ); // ペア配列を与えるとコンテント文字列になる that( html_tag(['a.c1#hoge.c2[name=hoge\[\]][href="http://hoge"][hidden]' => "this is text's content"])) ->isSame('<a id="hoge" class="c1 c2" name="hoge[]" href="http://hoge" hidden>this is text&#039;s content</a>' ); // ネストした配列を与えると再帰される that( html_tag([ 'div#wrapper' => [ 'b.class1' => [ '<plain>', ], 'b.class2' => [ '<plain1>', 's' => '<strike>', '<plain2>', ], ], ])) ->isSame('<div id="wrapper"><b class="class1">&lt;plain&gt;</b><b class="class2">&lt;plain1&gt;<s>&lt;strike&gt;</s>&lt;plain2&gt;</b></div>' );
Parameter
type name summary
​string|​array $selector
Return
type summary
​string html 文字列

[F] ini_export 連想配列を INI 的文字列に変換する

連想配列を INI 的文字列に変換する
function ini_export( array $iniarray, array $options = [] ): string

Example:

that(ini_export(['a' => 1, 'b' => 'B', 'c' => PHP_SAPI]))->is('a = 1 b = "B" c = "cli" ');
Parameter
type name summary
​array $iniarray ini 化する配列
​array $options = [] オプション配列
Return
type summary
​string ini 文字列

[F] ini_import INI 的文字列を連想配列に変換する

INI 的文字列を連想配列に変換する
function ini_import( string $inistring, array $options = [] ): array

Example:

that(ini_import(" a = 1 b = 'B' c = PHP_VERSION "))->is(['a' => 1, 'b' => 'B', 'c' => PHP_VERSION]);
Parameter
type name summary
​string $inistring ini 文字列
​array $options = [] オプション配列
Return
type summary
​array 配列

[F] json_export json_encode のプロキシ関数

json_encode のプロキシ関数
function json_export( mixed $value, array $options = [] ): string

引数体系とデフォルト値を変更してある。また、エラー時に例外が飛ぶ。

下記の拡張オプションがある。

JSON_INLINE_LEVEL
PRETTY_PRINT 時に指定以上の階層をインライン化する(数値以外にパスで階層も指定できる)
JSON_INLINE_SCALARLIST
PRETTY_PRINT 時にスカラーのみのリストをインライン化する
JSON_INDENT
PRETTY_PRINT 時にインデント数・文字列を指定する
JSON_CLOSURE
任意のリテラルを埋め込む
  • クロージャの返り値がそのまま埋め込まれるので、文字列化可能な結果を返さなければならない

JSON_ES5 を与えると JSON5 互換でエンコードされる。 その際下記のプションも使用可能になる。

JSON_TEMPLATE_LITERAL
改行を含む文字列をテンプレートリテラルで出力する
JSON_TRAILING_COMMA
末尾カンマを強制する
JSON_COMMENT_PREFIX
コメントとして埋め込まれるキープレフィックスを指定する
  • そのキーで始まる要素が文字列なら // コメントになる
  • そのキーで始まる要素が配列なら /* コメントになる

Example:

// オプションはこのように [定数 => bool] で渡す。false は指定されていないとみなされる(JSON_MAX_DEPTH 以外) that(json_export(['a' => 'A', 'b' => 'B'], [ JSON_PRETTY_PRINT => false, ]))->is('{"a":"A","b":"B"}'); // json5 でコメント付きかつ末尾カンマ強制モード that(json_export(['a' => 'A', '#comment' => 'this is comment', 'b' => 'B'], [ JSON_ES5 => true, JSON_TRAILING_COMMA => true, JSON_COMMENT_PREFIX => '#', JSON_PRETTY_PRINT => true, ]))->is('{ a: "A", // this is comment b: "B", }');
Parameter
type name summary
​mixed $value encode する値
​array $options = [] JSON_*** をキーにした連想配列。 値が false は指定されていないとみなされる
Return
type summary
​string JSON 文字列

[F] json_import json_decode のプロキシ関数

json_decode のプロキシ関数
function json_import( string $value, array $options = [] ): mixed

引数体系とデフォルト値を変更してある。

JSON_ES5 に null か true を渡すと json5 としてでデコードする(null はまず json_decode で試みる、true は json5 のみ)。 その場合拡張オプションとして下記がある。

JSON_INT_AS_STRING
常に整数を文字列で返す
JSON_FLOAT_AS_STRING
常に小数を文字列で返す
JSON_BARE_AS_STRING
bare string を文字列として扱う
JSON_TEMPLATE_LITERAL
テンプレートリテラルが使用可能になる
  • あくまで「文字列の括りに ` が使えるようになる」というものでテンプレートリテラルそのものではない
  • 末尾のインデントと同じインデントがすべて除去され、前後の改行は取り除かれる

Example:

// オプションはこのように [定数 => bool] で渡す。false は指定されていないとみなされる(JSON_MAX_DEPTH 以外) that(json_import('{"a":"A","b":"B"}', [ JSON_OBJECT_AS_ARRAY => true, ]))->is(['a' => 'A', 'b' => 'B']); // json5 が使える that(json_import('{a: "A", b: "B", }'))->is(['a' => 'A', 'b' => 'B']); // テンプレートリテラル that(json_import('` 1 2 3 `', [ JSON_TEMPLATE_LITERAL => true, ]))->is("1\n2\n3");
Parameter
type name summary
​string $value JSON 文字列
​array $options = [] JSON_*** をキーにした連想配列。値が false は指定されていないとみなされる
Return
type summary
​mixed decode された値

[F] ltsv_export 配列を LTSV 的文字列に変換する

配列を LTSV 的文字列に変換する
function ltsv_export( array $ltsvarray, array $options = [] ): string

ラベル文字列に ":" を含む場合は例外を投げる(ラベルにコロンが来るとどうしようもない)。

escape オプションで「LTSV 的にまずい文字」がその文字でエスケープされる(具体的には "\n" と "\t")。 デフォルトでは "\" でエスケープされるので、整合性が崩れることはない。

encode オプションで「文字列化できない値」が来たときのその関数を通して出力される(その場合、目印として値の両サイドに ` が付く)。 デフォルトでは json_encode される。

エンコード機能はおまけに過ぎない(大抵の場合はそんな機能は必要ない)。 ので、この実装は互換性を維持せず変更される可能性がある。

Example:

// シンプルな実行例 that(ltsv_export([ "label1" => "value1", "label2" => "value2", ]))->is("label1:value1 label2:value2"); // タブや改行文字のエスケープ that(ltsv_export([ "label1" => "val\tue1", "label2" => "val\nue2", ]))->is("label1:val\\tue1 label2:val\\nue2"); // 配列のエンコード that(ltsv_export([ "label1" => "value1", "label2" => [1, 2, 3], ]))->is("label1:value1 label2:`[1,2,3]`");
Parameter
type name summary
​array $ltsvarray 配列
​array $options = [] オプション配列
Return
type summary
​string LTSV 的文字列

[F] ltsv_import LTSV 的文字列を配列に変換する

LTSV 的文字列を配列に変換する
function ltsv_import( string $ltsvstring, array $options = [] ): array

escape オプションで「LTSV 的にまずい文字」がその文字でエスケープされる(具体的には "\n" と "\t")。 デフォルトでは "\" でエスケープされるので、整合性が崩れることはない。

decode オプションで「`` で囲まれた値」が来たときのその関数を通して出力される。 デフォルトでは json_decode される。

エンコード機能はおまけに過ぎない(大抵の場合はそんな機能は必要ない)。 ので、この実装は互換性を維持せず変更される可能性がある。

Example:

// シンプルな実行例 that(ltsv_import("label1:value1 label2:value2"))->is([ "label1" => "value1", "label2" => "value2", ]); // タブや改行文字のエスケープ that(ltsv_import("label1:val\\tue1 label2:val\\nue2"))->is([ "label1" => "val\tue1", "label2" => "val\nue2", ]); // 配列のデコード that(ltsv_import("label1:value1 label2:`[1,2,3]`"))->is([ "label1" => "value1", "label2" => [1, 2, 3], ]);
Parameter
type name summary
​string $ltsvstring LTSV 的文字列
​array $options = [] オプション配列
Return
type summary
​array 配列

[F] markdown_list 配列を markdown リスト文字列にする

配列を markdown リスト文字列にする
function markdown_list( array $array, array $option = [] ): string

Example:

// 最初の "\n" に意味はない(ズレると見づらいので冒頭に足しているだけ) that("\n" . markdown_list([ 'dict' => [ 'Key1' => 'Value1', 'Key2' => 'Value2', ], 'list' => ['Item1', 'Item2', 'Item3'], 'dict & list' => [ 'Key' => 'Value', ['Item1', 'Item2', 'Item3'], ], ], ['separator' => ':']))->is(" - dict: - Key1:Value1 - Key2:Value2 - list: - Item1 - Item2 - Item3 - dict & list: - Key:Value - Item1 - Item2 - Item3 ");
Parameter
type name summary
​array $array 配列
​array $option = [] オプション配列
Return
type summary
​string markdown リスト文字列

[F] markdown_table 連想配列の配列を markdown テーブル文字列にする

連想配列の配列を markdown テーブル文字列にする
function markdown_table( array $array, array $option = [] ): string

見出しはキーの和集合で生成され、改行は <br> に置換される。 要素が全て数値の場合は右寄せになる。

Example:

// 最初の "\n" に意味はない(ズレると見づらいので冒頭に足しているだけ) that("\n" . markdown_table([ ['a' => 'a1', 'b' => 'b1'], ['b' => 'b2', 'c' => '2'], ['a' => 'a3', 'c' => '3'], ]))->is(" | a | b | c | | --- | --- | --: | | a1 | b1 | | | | b2 | 2 | | a3 | | 3 | ");
Parameter
type name summary
​array $array 連想配列の配列
​array $option = [] オプション配列
Return
type summary
​string markdown テーブル文字列

[F] paml_export 連想配列を paml 的文字列に変換する

連想配列を paml 的文字列に変換する
function paml_export( array $pamlarray, array $options = [] ): string

paml で出力することはまずないのでおまけ(import との対称性のために定義している)。

Example:

that(paml_export([ 'n' => null, 'f' => false, 'i' => 123, 'd' => 3.14, 's' => 'this is string', ]))->isSame('n: null, f: false, i: 123, d: 3.14, s: "this is string"');
Parameter
type name summary
​array $pamlarray 配列
​array $options = [] オプション配列
Return
type summary
​string PAML 的文字列

[F] paml_import paml 的文字列をパースする

paml 的文字列をパースする
function paml_import( string $pamlstring, array $options = [] ): array

paml とは yaml を簡易化したような独自フォーマットを指す(Php Array Markup Language)。 ざっくりと下記のような特徴がある。

  • ほとんど yaml と同じだがフロースタイルのみでキーコロンの後のスペースは不要
  • yaml のアンカーや複数ドキュメントのようなややこしい仕様はすべて未対応
  • 配列を前提にしているので、トップレベルの [] {} は不要
  • [] でいわゆる php の配列、 {} で stdClass を表す(オプション指定可能)
  • bare string で php の定数を表す(クラス定数も完全修飾すれば使用可能)

簡易的な設定の注入に使える(yaml は標準で対応していないし、json や php 配列はクオートの必要やケツカンマ問題がある)。 なお、かなり緩くパースしてるので基本的にエラーにはならない。

早見表:

php
["n" => null, "f" => false, "i" => 123, "d" => 3.14, "s" => "this is string", "a" => [1, 2, "x" => "X"]]
  • ダブルアローとキーのクオートが冗長
json
{"n":null, "f":false, "i":123, "d":3.14, "s":"this is string", "a":{"0": 1, "1": 2, "x": "X"}}
  • キーのクオートが冗長だしケツカンマ非許容
yaml
{n: null, f: false, i: 123, d: 3.14, s: "this is string", a: {0: 1, 1: 2, x: X}}
  • 理想に近いが、コロンの後にスペースが必要だし連想配列が少々難。なにより拡張や外部ライブラリが必要
paml
n:null, f:false, i:123, d:3.14, s:"this is string", a:[1, 2, x:X]
  • シンプルイズベスト

Example:

// こういったスカラー型はほとんど yaml と一緒だが、コロンの後のスペースは不要(あってもよい) that(paml_import('n:null, f:false, i:123, d:3.14, s:"this is string"'))->isSame([ 'n' => null, 'f' => false, 'i' => 123, 'd' => 3.14, 's' => 'this is string', ]); // 配列が使える(キーは連番なら不要)。ネストも可能 that(paml_import('a:[1,2,x:X,3], nest:[a:[b:[c:[X]]]]'))->isSame([ 'a' => [1, 2, 'x' => 'X', 3], 'nest' => [ 'a' => [ 'b' => [ 'c' => ['X'] ], ], ], ]); // bare 文字列で定数が使える。::class も特別扱いで定数とみなす that(paml_import('pv:PHP_VERSION, ao:ArrayObject::STD_PROP_LIST, class:ArrayObject::class'))->isSame([ 'pv' => \PHP_VERSION, 'ao' => \ArrayObject::STD_PROP_LIST, 'class' => \ArrayObject::class, ]);
Parameter
type name summary
​string $pamlstring PAML 文字列
​array $options = [] オプション配列
Return
type summary
​array php 配列

[F] xmlss_export 連想配列の配列を XML SpreadSheet 的文字列に変換する

連想配列の配列を XML SpreadSheet 的文字列に変換する
function xmlss_export( iterable $xmlssarrays, array $options = [] ): string|int

単純に入出力に足る最低限の xml を返す(かつ1シートのみ)。

Example:

$xmlss = xmlss_export([ ['id' => 1, 'name' => 'hoge', 'flag' => true], ['id' => 2, 'name' => 'fuga', 'flag' => true], ['id' => 3, 'name' => 'piyo', 'flag' => false], ], [ 'xml' => ['style' => ['Default' => ['Name' => null]]], 'break' => "\n", ]); // 実際はスタイルやコメント、幅などに対応しているが長くなるので割愛 that($xmlss)->is(<<<XMLSS <?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> <Worksheet ss:Name="Sheet1"> <Table ss:ExpandedColumnCount="3"> <Row> <Cell><Data ss:Type="String">id</Data></Cell> <Cell><Data ss:Type="String">name</Data></Cell> <Cell><Data ss:Type="String">flag</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">1</Data></Cell> <Cell><Data ss:Type="String">hoge</Data></Cell> <Cell><Data ss:Type="Boolean">1</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">2</Data></Cell> <Cell><Data ss:Type="String">fuga</Data></Cell> <Cell><Data ss:Type="Boolean">1</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">3</Data></Cell> <Cell><Data ss:Type="String">piyo</Data></Cell> <Cell><Data ss:Type="Boolean"></Data></Cell> </Row> </Table> </Worksheet> </Workbook> XMLSS,);
Parameter
type name summary
​iterable $xmlssarrays 連想配列の配列
​array $options = [] オプション配列
Return
type summary
​string|​int XML SpreadSheet 的文字列。output オプションを渡した場合は書き込みバイト数

[F] xmlss_import XML SpreadSheet 的文字列を連想配列の配列に変換する

XML SpreadSheet 的文字列を連想配列の配列に変換する
function xmlss_import( string|resource $xmlssstring, array $options = [] ): array|iterable

厳密な形式チェックは特に行わないが、xml としての体裁や最低限 Workbook/Worksheet だけはチェックされる。

Example:

// このような xml を読み込ませると $rows = xmlss_import(<<<XMLSS <?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> <Worksheet ss:Name="Sheet1"> <Table ss:ExpandedColumnCount="3"> <Row> <Cell><Data ss:Type="String">id</Data></Cell> <Cell><Data ss:Type="String">name</Data></Cell> <Cell><Data ss:Type="String">flag</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">1</Data></Cell> <Cell><Data ss:Type="String">hoge</Data></Cell> <Cell><Data ss:Type="Boolean">1</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">2</Data></Cell> <Cell><Data ss:Type="String">fuga</Data></Cell> <Cell><Data ss:Type="Boolean">1</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">3</Data></Cell> <Cell><Data ss:Type="String">piyo</Data></Cell> <Cell><Data ss:Type="Boolean"></Data></Cell> </Row> </Table> </Worksheet> </Workbook> XMLSS, [ 'method' => 'sax', ]); // このような配列を返す that($rows)->is([ ['id' => 1, 'name' => 'hoge', 'flag' => true], ['id' => 2, 'name' => 'fuga', 'flag' => true], ['id' => 3, 'name' => 'piyo', 'flag' => false], ]);
Parameter
type name summary
​string|​resource $xmlssstring XML SpreadSheet 的文字列。ファイルポインタでも良い
​array $options = [] オプション配列
Return
type summary
​array|​iterable 連想配列の配列

[N] ryunosuke\Functions\Package\datetime\

[F] functions

[F] date_alter 日付を除外日リストに基づいてずらす

日付を除外日リストに基づいてずらす
function date_alter( string|int|\DateTimeInterface $datetime, array $excluded_dates, ?int $follow_count, string $format = "Y-m-d" ): string|null

典型的には「祝日前の営業日」「祝日後の営業日」のような代理日を返すイメージ。 $follow_count に応じて下記のように返す。

null
除外日でもずらさないでそのまま返す
-N
除外日なら最大N日分前倒しした日付を返す
+N
除外日なら最大N日分先送りした日付を返す
0
除外日でもずらさないで null を返す
Parameter
type name summary
​string|​int|​DateTimeInterface $datetime 調べる日付
​array $excluded_dates 除外日(いわゆる祝休日リスト)
​?int $follow_count ずらす範囲
​string $format = "Y-m-d" 日付フォーマット($excluded_dates の形式+返り値の形式)
Return
type summary
​string|​null 代替日。除外日 null

[F] date_convert 日時文字列をよしなに別のフォーマットに変換する

日時文字列をよしなに別のフォーマットに変換する
function date_convert( ?string $format, string|int|float|\DateTimeInterface|null $datetimedata = null ): string|\DateTimeInterface

マイクロ秒にも対応している。 かなり適当に和暦にも対応している。

拡張書式は下記。

J
日本元号
  • e.g. 平成
  • e.g. 令和
b
日本元号略称
  • e.g. H
  • e.g. R
k
日本元号年
  • e.g. 平成7年
  • e.g. 令和1年
K
日本元号年(1年が元年)
  • e.g. 平成7年
  • e.g. 令和元年
x
日本語曜日
  • e.g. 日
  • e.g. 月
Q
月内週番号(商が第N、余が曜日)
  • e.g. 6(7 * 0 + 6 第1土曜日)
  • e.g. 15(7 * 2 + 1 第3月曜日)

php8.2 から x,X が追加されたため上記はあくまで参考となる。

Example:

// 和暦を Y/m/d H:i:s に変換 that(date_convert('Y/m/d H:i:s', '昭和31年12月24日 12時34分56秒'))->isSame('1956/12/24 12:34:56'); // 単純に「マイクロ秒が使える date」としても使える $now = 1234567890.123; // テストがしづらいので固定時刻にする that(date_convert('Y/m/d H:i:s.u', $now))->isSame('2009/02/14 08:31:30.122999'); // $format に DateTimeInterface 実装クラス名を与えるとそのインスタンスを返す that(date_convert(\DateTimeImmutable::class, $now))->isInstanceOf(\DateTimeImmutable::class); // null は DateTimeInterface を意味する that(date_convert(null, $now))->isInstanceOf(\DateTimeImmutable::class);
Parameter
type name summary
​?string $format フォーマット
​string|​int|​float|​DateTimeInterface|​null $datetimedata = null 日時データ。省略時は microtime
Return
type summary
​string|​DateTimeInterface 日時文字列。$format が null の場合は DateTime

[F] date_fromto 日時っぽい文字列とフォーマットを与えると取りうる範囲を返す

日時っぽい文字列とフォーマットを与えると取りうる範囲を返す
function date_fromto( string $format, string $datetimestring ): array|null

与えられた日時の最大の切り捨て日時と最小の切り上げ日時の配列を返す。 日付文字列はある程度よしなに補完される(例えば "2014/12" は"2014年12月01日" と解釈されるし "12/24" は "今年12月24日" と解釈される)。

Example:

that(date_fromto('Y/m/d H:i:s', '2010/11'))->isSame(["2010/11/01 00:00:00", "2010/12/01 00:00:00"]); that(date_fromto('Y/m/d H:i:s', '2010/11/24'))->isSame(["2010/11/24 00:00:00", "2010/11/25 00:00:00"]); that(date_fromto('Y/m/d H:i:s', '2010/11/24 13'))->isSame(["2010/11/24 13:00:00", "2010/11/24 14:00:00"]); that(date_fromto('Y/m/d H:i:s', '2010/11/24 13:24'))->isSame(["2010/11/24 13:24:00", "2010/11/24 13:25:00"]);
Parameter
type name summary
​string $format フォーマット。 null を与えるとタイムスタンプで返す
​string $datetimestring 日時データ
Return
type summary
​array|​null [from ~ to] な配列。解釈できない場合は null

[F] date_interval ISO8601継続時間文字列を DateInterval に変換する

ISO8601継続時間文字列を DateInterval に変換する
function date_interval(string $interval): \DateInterval

各要素には負数を与えることができる。 秒にはミリ秒を与えることもできる。

Example:

// 普通のISO8601継続時間 that(date_interval('P1Y2M3DT4H5M6S'))->format('%R%Y-%M-%DT%H:%I:%S.%f')->is('+01-02-03T04:05:06.0'); // 負数が使える that(date_interval('P1Y2M3DT4H5M-6S'))->format('%R%Y-%M-%DT%H:%I:%S.%f')->is('+01-02-03T04:05:-6.0'); // ミリ秒が使える that(date_interval('P1Y2M3DT4H5M6.789S'))->format('%R%Y-%M-%DT%H:%I:%S.%f')->is('+01-02-03T04:05:06.788999');
Parameter
type name summary
​string $interval ISO8601継続時間文字列か相対表記
Return
type summary
DateInterval DateInterval オブジェクト

[F] date_interval_second DateInterval を秒に変換する

DateInterval を秒に変換する
function date_interval_second( \DateInterval|string|float|int $interval, string|float|int $basetime = 0 ): float|int

1ヶ月の間隔は時期によって異なるため、 $basetime 次第で結果が変わることがあるので注意。 $interval は DateInterval だけでなく ISO8601 文字列も与えることができる。 その際、下記の拡張仕様がある。

  • 先頭の正負記号(-+)を受け入れる(DateInterval->invert で表現される)
  • 秒だけは小数表記を受け入れる(DateInterval->f で表現される。元々 ISO8601 の仕様だが DateInterval は対応していないっぽい)

Example:

// 1分30秒は90秒 that(date_interval_second('PT1M30S'))->isSame(90); // 負数が使える that(date_interval_second('-PT1M30S'))->isSame(-90); // 秒は小数が使える that(date_interval_second('-PT1M30.5S'))->isSame(-90.5); // 1980/01/01 からの3ヶ月は 7862400 秒(うるう年なので 91 日) that(date_interval_second('P3M', '1980/01/01'))->isSame(7862400); // 1981/01/01 からの3ヶ月は 7776000 秒(うるう年じゃないので 90 日) that(date_interval_second('P3M', '1981/01/01'))->isSame(7776000);
Parameter
type name summary
DateInterval|​string|​float|​int $interval DateInterval インスタンスか間隔を表す ISO8601 文字列
​string|​float|​int $basetime = 0 基準日時(省略時 1970/01/01 00:00:00)
Return
type summary
​float|​int 秒($interval->f を含んでいるとき float で返す)

[F] date_interval_string 秒を世紀・年・月・日・時間・分・秒・ミリ秒の各要素に分解する

秒を世紀・年・月・日・時間・分・秒・ミリ秒の各要素に分解する
function date_interval_string( \DateInterval|string|float|int $interval, string|array|\Closure $format = null, string|int $limit_type = "y" ): string|\DateInterval

例えば 60 * 60 * 24 * 900 + 12345.678 (約900日12345秒)は・・・

  • 2 年(約900日なので)
  • 5 ヶ月(約(900 - 365 * 2 = 170)日なので)
  • 18 日(約(170 - 30.416 * 5 = 18)日なので)
  • 3 時間(約12345秒なので)
  • 25 分(約(12345 - 3600 * 3 = 1545)秒なので)
  • 45 秒(約(1545 - 60 * 25 = 45)秒なので)
  • 678 ミリ秒(.678 部分そのまま)

となる(年はうるう年未考慮で365日、月は30.41666666日で換算)。

$format を与えると DateInterval::format して文字列で返す。与えないと DateInterval をそのまま返す。 $format はクロージャを与えることができる。クロージャを与えた場合、各要素を引数としてコールバックされる。 $format は配列で与えることができる。配列で与えた場合、 0 になる要素は省かれる。 セパレータを与えたり、pre/suffix を与えたりできるが、難解なので省略する。

$limit_type で換算のリミットを指定できる。例えば 'y' を指定すると「2年5ヶ月」となるが、 'm' を指定すると「29ヶ月」となる。 数値を与えるとその範囲でオートスケールする。例えば 3 を指定すると値が大きいとき ymd の表示になり、年が 0 になると mdh の表示に切り替わるようになる。

Example:

// 書式文字列指定(%vはミリ秒) that(date_interval_string(60 * 60 * 24 * 900 + 12345.678, '%Y/%M/%D %H:%I:%S.%v'))->isSame('02/05/18 03:25:45.678'); // 書式にクロージャを与えるとコールバックされる(引数はスケールの小さい方から) that(date_interval_string(60 * 60 * 24 * 900 + 12345.678, fn() => implode(',', func_get_args())))->isSame('678,45,25,3,18,5,2,0'); // リミットを指定(month までしか計算しないので year は 0 になり month は 29になる) that(date_interval_string(60 * 60 * 24 * 900 + 12345.678, '%Y/%M/%D %H:%I:%S.%v', 'm'))->isSame('00/29/18 03:25:45.678'); // 書式に配列を与えてリミットに数値を与えるとその範囲でオートスケールする $format = [ 'y' => '%y年', 'm' => '%mヶ月', 'd' => '%d日', ' ', 'h' => '%h時間', 'i' => '%i分', 's' => '%s秒', ]; // 数が大きいので年・月・日の3要素のみ that(date_interval_string(60 * 60 * 24 * 900 + 12345, $format, 3))->isSame('2年5ヶ月18日'); // 数がそこそこだと日・時間・分の3要素に切り替わる that(date_interval_string(60 * 60 * 24 * 20 + 12345, $format, 3))->isSame('20日 3時間25分'); // どんなに数が小さくても3要素以下にはならない that(date_interval_string(1234, $format, 3))->isSame('0時間20分34秒');
Parameter
type name summary
DateInterval|​string|​float|​int $interval DateInterval インスタンスか間隔を表す ISO8601 文字列
​string|​array|​Closure $format = null 時刻フォーマット
​string|​int $limit_type = "y" どこまで換算するか([c
Return
type summary
​string|​DateInterval 時間差文字列

[F] date_match 日時と cron ライクな表記のマッチングを行う

日時と cron ライクな表記のマッチングを行う
function date_match( mixed $datetime, string $cronlike ): bool

YYYY/MM/DD(W) hh:mm:ss のようなパターンを与える。

YYYY
年を表す(1~9999)
MM
月を表す(1~12)
DD
日を表す(1~31, 99,L で末日を表す)
  • e.g. 2/99 は 2/28,2/29 を表す(年に依存)
  • e.g. 2/L も同様
W
曜日を表す(0~6, #N で第Nを表す、#o,#e で隔週を表す)
  • e.g. 3 は毎水曜日を表す
  • e.g. 3#4 は第4水曜日を表す
  • e.g. 5#L は最終水曜日を表す
  • e.g. 4#o は隔週水曜日を表す(週番号奇数)
  • e.g. 4#e は隔週水曜日を表す(週番号偶数)
hh
時を表す(任意で 0~23)
mm
分を表す(任意で 0~59)
ss
秒を表す(任意で 0~59)

DD と W は AND 判定される(cron は OR)。 また /(毎)にあたる表記はない。

9,L は「最後」を意味し、文脈に応じた値に書き換わる。 「最終」が可変である日付と曜日のみ指定可能。 例えば 2012/02/99 は「2014/02/29」になるし、2012/02/**(3#L) は「2012/02の最終水曜」になる。

各区切り内の値は下記が許可される。

*
任意の値を表す(桁合わせのためにいくつあってもよい)
特定の一つの数値を表す
数字-数字
範囲を表す
数字,数字
いずれかを表す

* 以外は複合できるので Example を参照。

この関数は実験的なので互換性担保に含まれない。

Example:

// 2014年の12月にマッチする that(date_match('2014/12/24 12:34:56', '2014/12/*'))->isTrue(); that(date_match('2014/11/24 12:34:56', '2014/12/*'))->isFalse(); that(date_match('2015/12/24 12:34:56', '2014/12/*'))->isFalse(); // 2014年の12月20日~25日にマッチする that(date_match('2014/12/24 12:34:56', '2014/12/20-25'))->isTrue(); that(date_match('2014/12/26 12:34:56', '2014/12/20-25'))->isFalse(); that(date_match('2015/12/24 12:34:56', '2014/12/20-25'))->isFalse(); // 2014年の12月10,20,30日にマッチする that(date_match('2014/12/20 12:34:56', '2014/12/10,20,30'))->isTrue(); that(date_match('2014/12/24 12:34:56', '2014/12/10,20,30'))->isFalse(); that(date_match('2015/12/30 12:34:56', '2014/12/10,20,30'))->isFalse(); // 2014年の12月10,20~25,30日にマッチする that(date_match('2014/12/24 12:34:56', '2014/12/10,20-25,30'))->isTrue(); that(date_match('2014/12/26 12:34:56', '2014/12/10,20-25,30'))->isFalse(); that(date_match('2015/12/26 12:34:56', '2014/12/10,20-25,30'))->isFalse();
Parameter
type name summary
​mixed $datetime 日時を表す引数
​string $cronlike マッチパターン
Return
type summary
​bool マッチしたら true

[F] date_modulate 日時を加減算する

日時を加減算する
function date_modulate( string $datetimedata, int|string|\DateInterval $modify ): string

フォーマットを維持しつつ日時文字列の最小単位でよしなに加算する。 具体的には Example を参照。

Example:

// 年加算 that(date_modulate('2014', 1))->isSame('2015'); // 月加算 that(date_modulate('2014/12', 1))->isSame('2015/01'); // 日加算 that(date_modulate('2014/12/24', 1))->isSame('2014/12/25'); // 時加算 that(date_modulate('2014/12/24 12', 1))->isSame('2014/12/24 13'); // 分加算 that(date_modulate('2014/12/24 12:34', 1))->isSame('2014/12/24 12:35'); // 秒加算 that(date_modulate('2014/12/24 12:34:56', 1))->isSame('2014/12/24 12:34:57'); // ミリ秒加算 that(date_modulate('2014/12/24 12:34:56.789', 1))->isSame('2014/12/24 12:34:56.790');
Parameter
type name summary
​string $datetimedata 日時文字列
​int|​string|​DateInterval $modify 加減算値
Return
type summary
​string 加算された日時文字列

[F] date_parse_format 日時文字列のフォーマットを返す

日時文字列のフォーマットを返す
function date_parse_format( string $datetimestring, array &$parsed = [] ): ?string

例えば "2014/12/24 12:34:56" から "Y/m/d H:i:s" を逆算して返す。 精度は非常に低く、相対表現なども未対応(そもそも逆算は一意に決まるものでもない)。

Example:

// RFC3339 that(date_parse_format('2014-12-24T12:34:56'))->isSame('Y-m-d\TH:i:s'); // 日本式 that(date_parse_format('2014/12/24 12:34:56'))->isSame('Y/m/d H:i:s'); // アメリカ式 that(date_parse_format('12/24/2014 12:34:56'))->isSame('m/d/Y H:i:s'); // イギリス式 that(date_parse_format('24.12.2014 12:34:56'))->isSame('d.m.Y H:i:s');
Parameter
type name summary
​string $datetimestring 日時文字列
​array &$parsed = [] パースの参考情報が格納される(内部向け)
Return
type summary
​?string フォーマット文字列

[F] date_timestamp 日時的なものをよしなにタイムスタンプに変換する

日時的なものをよしなにタイムスタンプに変換する
function date_timestamp( string|int|float|\DateTimeInterface $datetimedata, int|null $baseTimestamp = null ): int|float|null

マイクロ秒にも対応している。つまり返り値は int か float になる。 また、相対指定の +1 month の月末問題は起きないようにしてある。

かなり適当に和暦にも対応している。 さらに必要に迫られてかなり特殊な対応を行っているので Example を参照。

Example:

// 普通の日時文字列 that(date_timestamp('2014/12/24 12:34:56'))->isSame(strtotime('2014/12/24 12:34:56')); // 和暦 that(date_timestamp('昭和31年12月24日 12時34分56秒'))->isSame(strtotime('1956/12/24 12:34:56')); // 相対指定 that(date_timestamp('2012/01/31 +1 month'))->isSame(strtotime('2012/02/29')); that(date_timestamp('2012/03/31 -1 month'))->isSame(strtotime('2012/02/29')); // マイクロ秒 that(date_timestamp('2014/12/24 12:34:56.789'))->isSame(1419392096.789); // ベース日時 $baseTimestamp = strtotime('2012/01/31'); // ベース日時の25日(strtotime の序数日付は first/last しか対応していないが、この関数は対応している) that(date_timestamp('25th of this month', $baseTimestamp))->isSame(strtotime('2012/01/25')); // ベース日時の第2月曜(strtotime の序数曜日は 1st のような表記に対応していないが、この関数は対応している) that(date_timestamp('2nd monday of this month', $baseTimestamp))->isSame(strtotime('2012/01/09'));
Parameter
type name summary
​string|​int|​float|​DateTimeInterface $datetimedata 日時データ
​int|​null $baseTimestamp = null 日時データ
Return
type summary
​int|​float|​null タイムスタンプ。パース失敗時は null

[F] date_validate 日時文字列のバリデーション

日時文字列のバリデーション
function date_validate( string $datetime_string, string $format = "Y/m/d H:i:s", int $overhour = 0 ): bool

存在しない日付・時刻・相対指定などは全て不可。 あくまで「2014/12/24 12:34:56」のような形式と妥当性だけでチェックする。 $overhour 引数で 27:00 のような拡張時刻も許容させることができる(6 を指定すればいわゆる30時間制になる)。

日時形式は結構複雑なので「正しいはずだがなぜか false になる」という事象が頻発する。 その時、調査が大変(どの段階で false になっているか分からない)なので@で抑制しつつも内部的には user_error を投げている。 このエラーは error_get_last で取得可能だが、行儀の悪い(@抑制を見ない)エラーハンドラが設定されていると例外として送出されることがあるので注意。

Parameter
type name summary
​string $datetime_string 日付形式の文字列
​string $format = "Y/m/d H:i:s" フォーマット文字列
​int $overhour = 0 24時以降をどこまで許すか
Return
type summary
​bool valid な日時なら true

[F] now 現在日時を float で返す

現在日時を float で返す
function now(bool $persistence = true): float

microtime(true) と同じ。 ただし、引数 $persistence を true(デフォルト) にすると以降の呼び出しですべて同じ値を返すようになる。

Example:

// 現在時刻 $now = now(); sleep(1); // 1秒経っても同じ値を返す that(now())->is($now); // false を与えると新しい時刻を返す that(now(false))->isNot($now);
Parameter
type name summary
​bool $persistence = true 固定化フラグ
Return
type summary
​float 現在日時

[N] ryunosuke\Functions\Package\errorfunc\

[F] functions

[F] add_error_handler エラーハンドラを追加する

エラーハンドラを追加する
function add_error_handler( callable $handler, int $error_types = 32767 ): callable|null

追加したエラーハンドラが false を返すと標準のエラーハンドラではなく、直近の設定されていたエラーハンドラに移譲される。 (直近にエラーハンドラが設定されていなかったら標準ハンドラになる)。

「局所的にエラーハンドラを変更したいけど特定の状況は設定済みハンドラへ流したい」という状況はまれによくあるはず。

Example:

// @ 付きなら元々のハンドラに移譲、@ なしなら何らかのハンドリングを行う例 add_error_handler(function ($errno) { if (!(error_reporting() & $errno)) { // この false はマニュアルにある「この関数が FALSE を返した場合は、通常のエラーハンドラが処理を引き継ぎます」ではなく、 // 「さっきまで設定されていたエラーハンドラが処理を引き継ぎます」という意味になる return false; } // do something }); // false の扱いが異なるだけでその他の挙動はすべて set_error_handler と同じなので restore_error_handler で戻せる restore_error_handler();
Parameter
type name summary
​callable $handler エラーハンドラ
​int $error_types = 32767 エラータイプ
Return
type summary
​callable|​null 直近に設定されていたエラーハンドラ(未設定の場合は null)

[F] backtrace 特定条件までのバックトレースを取得する

特定条件までのバックトレースを取得する
function backtrace( int $flags = DEBUG_BACKTRACE_PROVIDE_OBJECT, array $options = [] ): array

第2引数 $options を満たすトレース以降を返す。 $options は ['$trace の key' => "条件"] を渡す。 条件は文字列かクロージャで、文字列の場合は緩い一致、クロージャの場合は true を返した場合にそれ以降を返す。

Example:

function f001 () {return backtrace(0, ['function' => __NAMESPACE__ . '\\f002', 'limit' => 2]);} function f002 () {return f001();} function f003 () {return f002();} $traces = f003(); // limit 指定してるので2個 that($traces)->count(2); // 「function が f002 以降」を返す that($traces[0])->subsetEquals([ 'function' => __NAMESPACE__ . '\\f002' ]); that($traces[1])->subsetEquals([ 'function' => __NAMESPACE__ . '\\f003' ]);
Parameter
type name summary
​int $flags = DEBUG_BACKTRACE_PROVIDE_OBJECT debug_backtrace の引数
​array $options = [] フィルタ条件
Return
type summary
​array バックトレース

[F] error エラー出力する

エラー出力する
function error( string|mixed $message, resource|string|mixed $destination = null ): int

第1引数 $message はそれらしく文字列化されて出力される。基本的にはあらゆる型を与えて良い。

第2引数 $destination で出力対象を指定する。省略すると error_log 設定に従う。 文字列を与えるとファイル名とみなし、ファイルに追記される。 ファイルを開くが、ファイルは閉じない。閉じ処理は php の終了処理に身を任せる。 したがって閉じる必要がある場合はファイルポインタを渡す必要がある。

Parameter
type name summary
​string|​mixed $message 出力メッセージ
​resource|​string|​mixed $destination = null 出力先
Return
type summary
​int 書き込んだバイト数

[F] set_all_error_handler あらゆるエラーをハンドルする

あらゆるエラーをハンドルする
function set_all_error_handler( \Closure $handler, bool $atmark_error = false, int $reserved_byte = 0 ): callable

実質的には set_error_handler+set_exception_handler+register_shutdown_function してるだけ。 あと小細工して初動エラーも拾うがあまり気にしなくてよい。

ハンドラの引数は Throwable 固定(エラーの場合は Error に変換されてコールされる)。 ハンドラが true/null を返すと設定前(ない場合は標準)のハンドラがコールされる。 実用上は「ログるかログらないか」くらいの差でしかない。

Parameter
type name summary
Closure $handler 実行されるクロージャ
​bool $atmark_error = false エラー抑制演算子をハンドリングするか
​int $reserved_byte = 0 fatal 用に予約するサイズ
Return
type summary
​callable キャンセルする callable

[F] set_error_exception_handler エラーを ErrorException に変換するハンドラを設定する

エラーを ErrorException に変換するハンドラを設定する
function set_error_exception_handler( int $error_levels = E_ALL, bool $handle_atmark_error = false ): callable

かなり局所的だが、使用するケースは結構ある。 戻り値として callable を返すので、それを呼べば restore される。 あるいは参照が切れれば RAII で restore される。

Example:

// 呼ぶとエラーが例外に変換されるようになる $restore = set_error_exception_handler(); try { $array = []; $dummy = $array['undefined']; } catch (\Throwable $t) { // undefined 例外が飛ぶ that($t)->isInstanceof(\ErrorException::class); } finally { // こうするとハンドラが戻る $restore(); }
Parameter
type name summary
​int $error_levels = E_ALL エラーレベル
​bool $handle_atmark_error = false エラー抑制時もハンドリングするか
Return
type summary
​callable restore するコールバック

[F] set_trace_logger メソッド呼び出しロガーを仕込む

メソッド呼び出しロガーを仕込む
function set_trace_logger( Psr\Log\LoggerInterface $logger, string $target ): mixed

この関数はかなり実験的なもので、互換性を考慮しない。

Parameter
type name summary
LoggerInterface $logger 書き出すファイル名
​string $target
Return
type summary
​mixed

[F] stacktrace スタックトレースを文字列で返す

スタックトレースを文字列で返す
function stacktrace( ?array $traces = null, int|string|array $option = [] ): string|array

(new \Exception())->getTraceAsString() と実質的な役割は同じ。 ただし、 getTraceAsString は引数が Array になったりクラス名しか取れなかったり微妙に使い勝手が悪いのでもうちょっと情報量を増やしたもの。

第1引数 $traces はトレース的配列を受け取る((new \Exception())->getTrace() とか)。 未指定時は debug_backtrace() で採取する。

第2引数 $option は文字列化する際の設定を指定する。 情報量が増える分、機密も含まれる可能性があるため、 mask オプションで塗りつぶすキーや引数名を指定できる(クロージャの引数までは手出ししないため留意)。 limit と format は比較的指定頻度が高いかつ互換性維持のため配列オプションではなく直に渡すことが可能になっている。

Parameter
type name summary
​?array $traces = null debug_backtrace 的な配列
​int|​string|​array $option = [] オプション
Return
type summary
​string|​array トレース文字列(delimiter オプションに null を渡すと配列で返す)

[N] ryunosuke\Functions\Package\exec\

[F] functions

[F] process proc_open ~ proc_close の一連の処理を行う

proc_open ~ proc_close の一連の処理を行う
function process( string $command, array|string $args = [], string|resource $stdin = "", string|resource &$stdout = "", string|resource &$stderr = "", ?string $cwd = null, ?array $env = null, ?array $options = null ): int

標準入出力は文字列で受け渡しできるが、決め打ち実装なのでいわゆる対話型なプロセスは起動できない。 また、標準入出力はリソース型を渡すこともできる。

Example:

// サンプル実行用ファイルを用意 $phpfile = sys_get_temp_dir() . '/rf-sample.php'; file_put_contents($phpfile, "<?php fwrite(STDOUT, fgets(STDIN)); fwrite(STDERR, 'err'); exit((int) ini_get('max_file_uploads')); "); // 引数と標準入出力エラーを使った単純な例 $rc = process(PHP_BINARY, [ '-d' => 'max_file_uploads=123', $phpfile, ], 'out', $stdout, $stderr); that($rc)->isSame(123); // -d で与えた max_file_uploads で exit してるので 123 that($stdout)->isSame('out'); // 標準出力に標準入力を書き込んでいるので "out" が格納される that($stderr)->isSame('err'); // 標準エラーに書き込んでいるので "err" が格納される
Parameter
type name summary
​string $command 実行コマンド。php7.4 未満では escapeshellcmd される
​array|​string $args = [] コマンドライン引数。php7.4 未満では文字列はそのまま結合され、配列は escapeshellarg された上でキーと結合される
​string|​resource $stdin = "" 標準入力(string を渡すと単純に読み取れられる。resource を渡すと fread される)
​string|​resource &$stdout = "" 標準出力(string を渡すと参照渡しで格納される。resource を渡すと fwrite される)
​string|​resource &$stderr = "" 標準エラー(string を渡すと参照渡しで格納される。resource を渡すと fwrite される)
​?string $cwd = null 作業ディレクトリ
​?array $env = null 環境変数
​?array $options = null その他の追加オプション
Return
type summary
​int リターンコード

[F] process_async proc_open ~ proc_close の一連の処理を行う(非同期版)

proc_open ~ proc_close の一連の処理を行う(非同期版)
function process_async( string $command, array|string $args = [], string|resource $stdin = "", string|resource &$stdout = "", string|resource &$stderr = "", ?string $cwd = null, ?array $env = null, ?array $options = null ): ProcessAsync|object
Parameter
type name summary
​string $command 実行コマンド
​array|​string $args = [] コマンドライン引数。文字列はそのまま結合され、配列は escapeshellarg された上でキーと結合される
​string|​resource $stdin = "" 標準入力(string を渡すと単純に読み取れられる。resource を渡すと fread される)
​string|​resource &$stdout = "" 標準出力(string を渡すと参照渡しで格納される。resource を渡すと fwrite される)
​string|​resource &$stderr = "" 標準エラー(string を渡すと参照渡しで格納される。resource を渡すと fwrite される)
​?string $cwd = null 作業ディレクトリ
​?array $env = null 環境変数
​?array $options = null その他の追加オプション
Return
type summary
ProcessAsync|​object プロセスオブジェクト
See
type summary
process()

[F] process_closure クロージャを別プロセスで実行する

クロージャを別プロセスで実行する
function process_closure( \Closure $closure, mixed $args = [], bool|int $throw = true, ?array $autoload = null, ?string $workdir = null, $env = null, ?array $options = null ): ProcessAsync|object

process_parallel の単一クロージャ特化版。 クロージャを別プロセスでバックグラウンド実行して結果を返すオブジェクトを返す。 クロージャは独自の方法でエクスポートしてから実行するので可能な限り this bind は外したほうが良い。

バックグラウンドで実行するので指定クロージャを実行中に別のことができる。 これはマルチコアを活かすのにも有用であるし、IO を伴う処理を効率よく実行できる。

Example:

$hugefile1 = tempnam(sys_get_temp_dir(), 't'); $hugefile2 = tempnam(sys_get_temp_dir(), 't'); file_put_contents($hugefile1, str_repeat('x', 1024* 1024)); // もっとでかくないと効果は薄いがテストなのであまり大きくしない file_put_contents($hugefile2, str_repeat('y', 1024* 1024)); // 超絶でかいファイルの sha1 を計算してる間に・・・ $hash1 = process_closure(static fn() => sha1_file($hugefile1)); $hash2 = process_closure(static fn() => sha1_file($hugefile2)); // ここで別のことができる(裏で処理は走っている) // doSomething()) // 返り値オブジェクトをコールすると結果が得られる(この時、処理が未完なら待たされる) that($hash1())->is('e37f4d5be56713044d62525e406d250a722647d6'); that($hash2())->is('2b6a7ad91a60e40a5fd37abe06e165dc7498b24e'); // あるいは http リクエストを走らせている間に・・・ $start = microtime(true); $response1 = process_closure(static fn($url) => file_get_contents($url), TESTWEBSERVER . '/delay/2'); $response2 = process_closure(static fn($url) => file_get_contents($url), TESTWEBSERVER . '/delay/2'); // 別のことができる(裏でリクエストは走っている) // doSomething()) // 返り値オブジェクトをコールするとレスポンスが得られる that($response1())->isNotEmpty(); that($response2())->isNotEmpty(); // トータルで2秒程度である(少なくとも 2 * 2 で4秒にはならない) that(microtime(true) - $start)->break()->isBetween(2.0, 4.0);
Parameter
type name summary
Closure $closure 非同期実行するクロージャ
​mixed $args = [] クロージャの引数
​bool|​int $throw = true exitcode で例外を投げるか(現在は bool のみ対応)
​?array $autoload = null 実行前に読み込むスクリプト。省略時は自動検出された vendor/autoload.php と function_configure/process.autoload
​?string $workdir = null ワーキングディレクトリ。省略時はテンポラリディレクトリ
$env = null
​?array $options = null その他の追加オプション
Return
type summary
ProcessAsync|​object プロセスオブジェクト

[F] process_parallel 複数の callable を並列で実行する

複数の callable を並列で実行する
function process_parallel( callable|callable[] $tasks, array $args = [], ?array $autoload = null, ?string $workdir = null, $env = null, ?array $options = null ): array

callable はクロージャも使用できるが、独自の方法でエクスポートしてから実行するので可能な限り this bind は外したほうが良い。

Example:

# 単一のクロージャを複数の引数で回す $t = microtime(true); $result = process_parallel(static function ($arg1, $arg2) { usleep(1000 * 1000); fwrite(STDOUT, "this is stdout"); fwrite(STDERR, "this is stderr"); return $arg1 + $arg2; }, ['a' => [1, 2], 'b' => [2, 3], [3, 4]]); // 1000ms かかる処理を3本実行するが、トータル時間は 3000ms ではなくそれ以下になる(多少のオーバーヘッドはある) that(microtime(true) - $t)->break()->lessThan(2.0); // 実行結果は下記のような配列で返ってくる(その際キーは維持される) that($result)->isSame([ 'a' => [ 'status' => 0, 'stdout' => "this is stdout", 'stderr' => "this is stderr", 'return' => 3, ], 'b' => [ 'status' => 0, 'stdout' => "this is stdout", 'stderr' => "this is stderr", 'return' => 5, ], [ 'status' => 0, 'stdout' => "this is stdout", 'stderr' => "this is stderr", 'return' => 7, ], ]); # 複数のクロージャを複数の引数で回す(この場合、引数のキーは合わせなければならない) $t = microtime(true); $result = process_parallel([ 'a' => static function ($arg1, $arg2) { usleep(300 * 1000); return $arg1 + $arg2; }, 'b' => static function ($arg1, $arg2) { usleep(500 * 1000); return $arg1 * $arg2; }, static function ($arg) { usleep(1000 * 1000); exit($arg); }, ], ['a' => [1, 2], 'b' => [2, 3], [127]]); // 300,500,1000ms かかる処理を3本実行するが、トータル時間は 1800ms ではなくそれ以下になる(多少のオーバーヘッドはある) that(microtime(true) - $t)->break()->lessThan(1.5); // 実行結果は下記のような配列で返ってくる(その際キーは維持される) that($result)->isSame([ 'a' => [ 'status' => 0, 'stdout' => "", 'stderr' => "", 'return' => 3, ], 'b' => [ 'status' => 0, 'stdout' => "", 'stderr' => "", 'return' => 6, ], [ 'status' => 127, // 終了コードが入ってくる 'stdout' => "", 'stderr' => "", 'return' => null, ], ]);
Parameter
type name summary
​callable|​callable[] $tasks 並列実行する callable. 単一の場合は引数分実行して結果を返す
​array $args = [] 各々の引数。$tasks が配列の場合はそれに対応する引数配列。単一の場合は実行回数も兼ねた引数配列
​?array $autoload = null 実行前に読み込むスクリプト。省略時は自動検出された vendor/autoload.php と function_configure/process.autoload
​?string $workdir = null ワーキングディレクトリ。省略時はテンポラリディレクトリ
$env = null
​?array $options = null その他の追加オプション
Return
type summary
​array 実行結果(['return' => callable の返り値, 'status' => 終了コード, 'stdout' => 標準出力, 'stderr' => 標準エラー])

[N] ryunosuke\Functions\Package\filesystem\

[F] functions

[F] cp_rf ディレクトリのコピー

ディレクトリのコピー
function cp_rf( string $src, string $dst ): bool

$dst に / を付けると「$dst に自身をコピー」する。付けないと「$dst に中身をコピー」するという動作になる。

ディレクトリではなくファイルを与えても動作する(copy とほぼ同じ動作になるが、対象にディレクトリを指定できる点が異なる)。

Example:

// /tmp/src/hoge.txt, /tmp/src/dir/fuga.txt を作っておく $tmp = sys_get_temp_dir(); file_set_contents("$tmp/src/hoge.txt", 'hoge'); file_set_contents("$tmp/src/dir/fuga.txt", 'fuga'); // "/" を付けないと中身コピー cp_rf("$tmp/src", "$tmp/dst1"); that("$tmp/dst1/hoge.txt")->fileEquals('hoge'); that("$tmp/dst1/dir/fuga.txt")->fileEquals('fuga'); // "/" を付けると自身コピー cp_rf("$tmp/src", "$tmp/dst2/"); that("$tmp/dst2/src/hoge.txt")->fileEquals('hoge'); that("$tmp/dst2/src/dir/fuga.txt")->fileEquals('fuga'); // $src はファイルでもいい($dst に "/" を付けるとそのディレクトリにコピーする) cp_rf("$tmp/src/hoge.txt", "$tmp/dst3/"); that("$tmp/dst3/hoge.txt")->fileEquals('hoge'); // $dst に "/" を付けないとそのパスとしてコピー(copy と完全に同じ) cp_rf("$tmp/src/hoge.txt", "$tmp/dst4"); that("$tmp/dst4")->fileEquals('hoge');
Parameter
type name summary
​string $src コピー元パス
​string $dst コピー先パス。末尾/でディレクトリであることを明示できる
Return
type summary
​bool 成功した場合に TRUE を、失敗した場合に FALSE を返します

[F] dir_clean 指定条件のファイル・ディレクトリを再帰的に消す

指定条件のファイル・ディレクトリを再帰的に消す
function dir_clean( string $directory, int $atime = 0, int $mtime = 0, array|string $excludePattern = [] ): array

tmpwatch みたいなもので、キャッシュなどのゴミ掃除に使う想定。

Example:

// 1時間以上アクセスのないファイルを消す dir_clean(sys_get_temp_dir() . '/cache', atime: 3600, ); // 1時間以上更新されていないファイルを消す dir_clean(sys_get_temp_dir() . '/cache', mtime: 3600, ); // 2時間以上アクセスのない かつ 1時間以上更新されていないファイルを消す(両指定は AND) dir_clean(sys_get_temp_dir() . '/cache', atime: 7200, mtime: 3600, );
Parameter
type name summary
​string $directory 対象ディレクトリ
​int $atime = 0 対象アクセス日時秒数
​int $mtime = 0 対象更新日時秒数
​array|​string $excludePattern = [] 除外パターン
Return
type summary
​array 消したエントリ配列

[F] dir_diff ファイルツリーを比較して配列で返す

ファイルツリーを比較して配列で返す
function dir_diff( string $path1, string $path2, array $options = [] ): array

ファイル名をキーとし、

  • $path1 にしかないファイルは true
  • $path2 にしかないファイルは false
  • 両方にあり、内容が異なる場合はなんらかの文字列(comparator オプション)
  • 両方にあり、内容が同じ場合は結果に含まれない

comparator オプションは両方に存在した場合にコールされるので差分を返したり更新日時を返したリできる。 comparator が null を返した場合、その要素は内容が同じとみなされ、結果配列に含まれなくなる。

Example:

// 適当にファイルツリーを用意 $dir1 = sys_get_temp_dir() . '/diff1'; $dir2 = sys_get_temp_dir() . '/diff2'; file_set_tree([ $dir1 => [ 'file1.txt' => 'file1', 'file2.txt' => 'file2', 'sub1' => [ 'file.txt' => 'sub1file', ], 'sub2' => [], ], $dir2 => [ 'file1.txt' => 'file1', 'file2.txt' => 'FILE2', 'sub1' => [], 'sub2' => [ 'file.txt' => 'sub2file', ], ], ]); // dir_diff すると下記のような差分が得られる $DS = DIRECTORY_SEPARATOR; that(dir_diff($dir1, $dir2))->is([ // "file1.txt" => "", // 差分がないので含まれない "file2.txt" => "", // 両方に存在して差分あり "sub1{$DS}file.txt" => true, // dir1 にのみ存在する "sub2{$DS}file.txt" => false, // dir2 にのみ存在する ]);
Parameter
type name summary
​string $path1 パス1
​string $path2 パス2
​array $options = [] オプション
Return
type summary
​array 比較配列

[F] dirmtime ディレクトリの最終更新日時を返す

ディレクトリの最終更新日時を返す
function dirmtime( string $dirname, bool $recursive = true ): int

「ディレクトリの最終更新日時」とは filemtime で得られる結果ではなく、「配下のファイル群で最も新しい日時」を表す。 ディレクトリの mtime も検出に含まれるので、ファイルを削除した場合も検知できる。

ファイル名を与えると例外を投げる。 空ディレクトリの場合は自身の mtime を返す。

Example:

$dirname = sys_get_temp_dir() . '/mtime'; rm_rf($dirname); mkdir($dirname); // この時点では現在日時(単純に自身の更新日時) that(dirmtime($dirname))->isBetween(time() - 2, time()); // ファイルを作って更新するとその時刻 touch("$dirname/tmp", time() + 10); that(dirmtime($dirname))->isSame(time() + 10);
Parameter
type name summary
​string $dirname ディレクトリ名
​bool $recursive = true 再帰フラグ
Return
type summary
​int 最終更新日時

[F] dirname_r コールバックが true 相当を返すまで親ディレクトリを辿り続ける

コールバックが true 相当を返すまで親ディレクトリを辿り続ける
function dirname_r( string $path, callable $callback ): mixed

コールバックには親ディレクトリが引数として渡ってくる。

Example:

// //tmp/a/b/file.txt を作っておく $tmp = sys_get_temp_dir(); file_set_contents("$tmp/a/b/file.txt", 'hoge'); // /a/b/c/d/e/f から開始して「どこかの階層の file.txt を探したい」という状況を想定 $callback = fn($path) => realpath("$path/file.txt"); that(dirname_r("$tmp/a/b/c/d/e/f", $callback))->isSame(realpath("$tmp/a/b/file.txt"));
Parameter
type name summary
​string $path パス名
​callable $callback コールバック
Return
type summary
​mixed $callback の返り値。頂上まで辿ったら null

[F] file_equals ファイルが同じ内容か返す

ファイルが同じ内容か返す
function file_equals( string $file1, string $file2, ?int $chunk_size = null ): bool

ファイルサイズで比較して一致したら更に内容を読んで判定する。 ディレクトリ同士の場合は直下のファイル群を内容とみなして判定する。

Example:

// 適当にファイルを用意 $testpath1 = sys_get_temp_dir() . '/file_equals1.txt'; $testpath2 = sys_get_temp_dir() . '/file_equals2.txt'; $testpath3 = sys_get_temp_dir() . '/file_equals3.txt'; file_put_contents($testpath1, "hoge"); file_put_contents($testpath2, "foo"); file_put_contents($testpath3, "hoge"); // 異なるなら false that(file_equals($testpath1, $testpath2))->isFalse(); // 同じなら true that(file_equals($testpath1, $testpath3))->isTrue();
Parameter
type name summary
​string $file1 ファイル名1
​string $file2 ファイル名2
​?int $chunk_size = null 読み込みチャンクサイズ
Return
type summary
​bool ファイルが同じ内容なら true

[F] file_extension ファイルの拡張子を変更する。引数を省略すると拡張子を返す

ファイルの拡張子を変更する。引数を省略すると拡張子を返す
function file_extension( string $filename, string $extension = "" ): string

pathinfo に準拠。例えば「filename.hoge.fuga」のような形式は「fuga」が変換対象になる。

Example:

that(file_extension('filename.ext'))->isSame('ext'); that(file_extension('filename.ext', 'txt'))->isSame('filename.txt'); that(file_extension('filename.ext', ''))->isSame('filename');
Parameter
type name summary
​string $filename 調べるファイル名
​string $extension = "" 拡張子。nullや空文字なら拡張子削除
Return
type summary
​string 拡張子変換後のファイル名 or 拡張子

[F] file_get_arrays 指定ファイルを拡張子別に php の配列として読み込む

指定ファイルを拡張子別に php の配列として読み込む
function file_get_arrays( string $filename, array $options = [] ): array

形式は拡張子で自動判別する。 その際、2重拡張子で hoge.sjis.csv のように指定するとそのファイルのエンコーディングを指定したことになる。

Example:

// csv ファイルを読み込んで配列で返す file_put_contents($csvfile = sys_get_temp_dir() . '/hoge.csv', 'a,b,c 1,2,3 4,5,6 7,8,9 '); that(file_get_arrays($csvfile))->isSame([ ['a' => '1', 'b' => '2', 'c' => '3'], ['a' => '4', 'b' => '5', 'c' => '6'], ['a' => '7', 'b' => '8', 'c' => '9'], ]); // sjis の json ファイルを読み込んで配列で返す file_put_contents($jsonfile = sys_get_temp_dir() . '/hoge.sjis.json', '[ {"a": 1, "b": 2, "c": 3}, {"a": 4, "b": 5, "c": 6}, {"a": 7, "b": 8, "c": 9} ]'); that(file_get_arrays($jsonfile))->isSame([ ['a' => 1, 'b' => 2, 'c' => 3], ['a' => 4, 'b' => 5, 'c' => 6], ['a' => 7, 'b' => 8, 'c' => 9], ]);
Parameter
type name summary
​string $filename 読み込むファイル名
​array $options = [] 各種オプション
Return
type summary
​array レコード配列

[F] file_list ファイル一覧を配列で返す

ファイル一覧を配列で返す
function file_list( string $dirname, array $filter_condition = [] ): ?array

Example:

// 適当にファイルを用意 $DS = DIRECTORY_SEPARATOR; $tmp = sys_get_temp_dir() . "{$DS}file_list"; rm_rf($tmp, false); file_set_contents("$tmp/a.txt", 'a'); file_set_contents("$tmp/dir/b.txt", 'b'); file_set_contents("$tmp/dir/dir/c.txt", 'c'); // ファイル一覧が取得できる that(file_list($tmp))->equalsCanonicalizing([ "$tmp{$DS}a.txt", "$tmp{$DS}dir{$DS}b.txt", "$tmp{$DS}dir{$DS}dir{$DS}c.txt", ]);
Parameter
type name summary
​string $dirname 調べるディレクトリ名
​array $filter_condition = [] フィルタ条件
Return
type summary
​?array ファイルの配列

[F] file_matcher 各種属性を指定してファイルのマッチングを行うクロージャを返す

各種属性を指定してファイルのマッチングを行うクロージャを返す
function file_matcher(array $filter_condition): \Closure

※ 内部向け

Parameter
type name summary
​array $filter_condition マッチャーコンディション配列(ソースを参照)
Return
type summary
Closure ファイルマッチャー

[F] file_mimetype ファイルの mimetype を返す

ファイルの mimetype を返す
function file_mimetype( string $filename, array|bool $prefer_extension = [], ?array &$parameters = null ): string|null

mime_content_type の http 対応版。 変更点は下記。

  • http(s) に対応(HEAD メソッドで取得する)
  • 失敗時に false ではなく null を返す

for compatible: $prefer_extension と $parameters 引数は将来的に入れ替わる。

Example:

that(file_mimetype(__FILE__))->is('text/x-php'); that(file_mimetype('http://httpbin.org/get?name=value'))->is('application/json');
Parameter
type name summary
​string $filename ファイル名(URL)
​array|​bool $prefer_extension = [] extension => mimetype のマップ(true を与えると組み込みを使用する)
​?array &$parameters = null 引数=値 の連想配列
Return
type summary
​string|​null MIME タイプ

[F] file_pos 範囲指定でファイルを読んで位置を返す

範囲指定でファイルを読んで位置を返す
function file_pos( string $filename, string|array $needle, int $start = 0, int|null $end = null, int|null $chunksize = null ): ?int

$needle に配列を与えると OR 的動作で一つでも見つかった時点の位置を返す。 このとき「どれが見つかったか?」は得られない(場合によっては不便なので将来の改修対象)。

Example:

// 適当にファイルを用意 $testpath = sys_get_temp_dir() . '/file_pos.txt'; file_put_contents($testpath, "hoge\nfuga\npiyo\nfuga"); // fuga の位置を返す that(file_pos($testpath, 'fuga'))->is(5); // 2つ目の fuga の位置を返す that(file_pos($testpath, 'fuga', 6))->is(15); // 見つからない場合は false を返す that(file_pos($testpath, 'hogera'))->is(null);
Parameter
type name summary
​string $filename ファイル名
​string|​array $needle 探す文字列
​int $start = 0 読み込み位置
​int|​null $end = null 読み込むまでの位置。省略時は指定なし(最後まで)。負数は後ろからのインデックス
​int|​null $chunksize = null 読み込みチャンクサイズ。省略時は 4096 の倍数に正規化
Return
type summary
​?int $needle の位置。見つからなかった場合は null

[F] file_rewrite_contents ファイルを読み込んで内容をコールバックに渡して書き込む

ファイルを読み込んで内容をコールバックに渡して書き込む
function file_rewrite_contents( string $filename, callable $callback, int $operation = 0 ): int

Example:

// 適当にファイルを用意 $testpath = sys_get_temp_dir() . '/rewrite.txt'; file_put_contents($testpath, 'hoge'); // 前後に 'pre-', '-fix' を付与する file_rewrite_contents($testpath, fn($contents, $fp) => "pre-$contents-fix"); that($testpath)->fileEquals('pre-hoge-fix');
Parameter
type name summary
​string $filename 読み書きするファイル名
​callable $callback 書き込む内容。引数で $contents, $fp が渡ってくる
​int $operation = 0 ロック定数(LOCL_SH, LOCK_EX, LOCK_NB)
Return
type summary
​int 書き込まれたバイト数

[F] file_rotate ファイルをローテーションする

ファイルをローテーションする
function file_rotate( string $filename, bool $ifempty = false, bool $copytruncate = false, bool $append = false, ?string $olddir = null, ?string $dateformat = null, ?int $rotate = null, ?int $compress = null ): ?array

オプションは logrotate に意図的に似せてある。 返り値としてローテーションファイル配列を返す。 基本的に決め打ちな使い方で細かいオプションは実装していない。

Example:

// 適当にファイルを用意 rm_rf(sys_get_temp_dir() . '/rotate'); $logfile = sys_get_temp_dir() . '/rotate/log.txt'; file_set_contents($logfile, ''); // 5回ローテートしてみる foreach (range(1, 5) as $i) { file_rotate($logfile, ifempty: true, rotate: 4, compress: 2, dateformat: "-$i"); // dateformat は普通は日付書式文字列(↓の確認がしんどくなるのでここでは連番) } // rotate:4 効果で全部で4世代であり、compress:2 効果でうち2世代は圧縮されている $dirname = dirname($logfile); that(glob("$dirname/log-*"))->is([ "$dirname/log-2.txt.gz", "$dirname/log-3.txt.gz", "$dirname/log-4.txt", "$dirname/log-5.txt", ]);
Parameter
type name summary
​string $filename 対象ファイル名
​bool $ifempty = false 空ファイルでもローテーションするか
​bool $copytruncate = false 同じファイル(inode)を使い続けるか
​bool $append = false 既存ローテーションファイルに追記するか
​?string $olddir = null ローテーションファイルのディレクトリ
​?string $dateformat = null ローテーションファイルのサフィックス
​?int $rotate = null 保持世代数(null で無世代)
​?int $compress = null 圧縮世代数(例えば 3 を指定すると3世代目から圧縮される)
Return
type summary
​?array ローテーションファイル配列

[F] file_set_contents ディレクトリも掘る file_put_contents

ディレクトリも掘る file_put_contents
function file_set_contents( string $filename, string $data, int $umask = 2 ): ?int

書き込みは一時ファイルと rename を使用してアトミックに行われる。

Example:

file_set_contents(sys_get_temp_dir() . '/not/filename.ext', 'hoge'); that(file_get_contents(sys_get_temp_dir() . '/not/filename.ext'))->isSame('hoge');
Parameter
type name summary
​string $filename 書き込むファイル名
​string $data 書き込む内容
​int $umask = 2 ディレクトリを掘る際の umask
Return
type summary
​?int 書き込まれたバイト数

[F] file_set_tree ツリー構造で file_set_contents する

ツリー構造で file_set_contents する
function file_set_tree( array $contents_tree, ?int $umask = null ): array

値が配列の場合はディレクトリ、それ以外の場合はファイルとなる。 値がクロージャーの場合はコールされる。 返り値として書き込んだバイト数のフルパス配列を返す。

Example:

// 一時ディレクトリにツリー構造でファイルを配置する $root = sys_get_temp_dir(); file_set_tree([ $root => [ 'hoge.txt' => 'HOGE', 'dir1' => [ 'fuga.txt' => 'FUGA', 'dir2' => [ 'piyo.txt' => 'PIYO', ], ], ], ]); that("$root/hoge.txt")->fileEquals('HOGE'); that("$root/dir1/fuga.txt")->fileEquals('FUGA'); that("$root/dir1/dir2/piyo.txt")->fileEquals('PIYO');
Parameter
type name summary
​array $contents_tree コンテンツツリー
​?int $umask = null umask
Return
type summary
​array 書き込まれたバイト数配列

[F] file_slice file の行範囲を指定できる板

file の行範囲を指定できる板
function file_slice( string $filename, int $start_line = 1, ?int $length = null, int $flags = 0, ?resource $context = null ): array

原則 file をベースに作成しているが、一部独自仕様がある。

  • 結果配列は行番号がキーになる
    • あくまで行番号なので 1 オリジン
    • スキップされた行は歯抜けになる
  • FILE_SKIP_EMPTY_LINES の動作が FILE_IGNORE_NEW_LINES ありきではない
    • file における FILE_SKIP_EMPTY_LINES の単独指定は意味を成さないっぽい
      • FILE_IGNORE_NEW_LINES しないと空文字ではなく改行文字が含まれるので空判定にならないようだ
    • この関数はその動作を撤廃しており、単独で FILE_SKIP_EMPTY_LINES を指定しても空行が飛ばされる動作になっている
  • $end_line に負数を指定すると行番号の直指定となる
    • file_slice($filename, 120, -150) で 120行目から150行目までを読む
    • 負数なのは気持ち悪いが、範囲指定のハイフン(120-150)だと思えば割と自然

使用用途としては

  1. 巨大ファイルの前半だけ読みたい
  2. 1行だけサクッと読みたい

が挙げられる。

1 は自明(file は全行読む)だが、終端付近を読む場合は file の方が若干速い。 ただし、期待値としてはこの関数の方が格段に低い(file は下手すると何十倍も遅い)。

2 は典型的には「1行目だけ読みたい」場合、fopen+fgets+fclose(finally)という手順を踏む必要があり煩雑になる。 この関数を使えばサクッと取得することができる。

Example:

// 適当にファイルを用意(奇数行は行番号、偶数行は空行) $testpath = sys_get_temp_dir() . '/file_slice.txt'; file_put_contents($testpath, implode("\n", array_map(fn($n) => $n % 2 ? $n : "", range(1, 20)))); // 3行目から4行を返す that(file_slice($testpath, 3, 4))->is([ 3 => "3\n", 4 => "\n", 5 => "5\n", 6 => "\n", ]); // 3行目から6行目までを返す that(file_slice($testpath, 3, -6))->is([ 3 => "3\n", 4 => "\n", 5 => "5\n", 6 => "\n", ]); // 改行文字や空行を含めない(キーは保持される) that(file_slice($testpath, 3, 4, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES))->is([ 3 => "3", 5 => "5", ]);
Parameter
type name summary
​string $filename ファイル名
​int $start_line = 1 開始行。1 オリジン
​?int $length = null 終了行。null を指定すると最後まで読む。負数にすると行番号直指定になる
​int $flags = 0 file と同じフラグ定数(FILE_IGNORE_NEW_LINES, etc)
​?resource $context = null file と同じ context 指定
Return
type summary
​array 部分行

[F] file_suffix ファイル名にサフィックスを付与する

ファイル名にサフィックスを付与する
function file_suffix( string $filename, string $suffix ): string

pathinfoに非準拠。例えば「filename.hoge.fuga」のような形式は「filename」が変換対象になる。

Example:

that(file_suffix('filename.ext', '-min'))->isSame('filename-min.ext'); that(file_suffix('filename.ext1.ext2', '-min'))->isSame('filename-min.ext1.ext2');
Parameter
type name summary
​string $filename パス・ファイル名
​string $suffix 付与するサフィックス
Return
type summary
​string サフィックスが付与されたパス名

[F] file_tree ディレクトリ階層をツリー構造で返す

ディレクトリ階層をツリー構造で返す
function file_tree( string $dirname, array $filter_condition = [] ): ?array

Example:

// 適当にファイルを用意 $DS = DIRECTORY_SEPARATOR; $tmp = sys_get_temp_dir() . "{$DS}file_tree"; rm_rf($tmp, false); file_set_contents("$tmp/a.txt", 'a'); file_set_contents("$tmp/dir/b.txt", 'b'); file_set_contents("$tmp/dir/dir/c.txt", 'c'); // ファイルツリーが取得できる that(file_tree($tmp))->is([ 'file_tree' => [ 'a.txt' => "$tmp{$DS}a.txt", 'dir' => [ 'b.txt' => "$tmp{$DS}dir{$DS}b.txt", 'dir' => [ 'c.txt' => "$tmp{$DS}dir{$DS}dir{$DS}c.txt", ], ], ], ]);
Parameter
type name summary
​string $dirname 調べるディレクトリ名
​array $filter_condition = [] フィルタ条件
Return
type summary
​?array ツリー構造の配列

[F] fnmatch_and fnmatch の AND 版

fnmatch の AND 版
function fnmatch_and( array|string $patterns, string $string, int $flags = 0 ): bool

$patterns のうちどれか一つでもマッチしなかったら false を返す。 $patterns が空だと例外を投げる。

Example:

// すべてにマッチするので true that(fnmatch_and(['*aaa*', '*bbb*'], 'aaaXbbbX'))->isTrue(); // aaa にはマッチするが bbb にはマッチしないので false that(fnmatch_and(['*aaa*', '*bbb*'], 'aaaX'))->isFalse();
Parameter
type name summary
​array|​string $patterns パターン配列(単一文字列可)
​string $string 調べる文字列
​int $flags = 0 FNM_***
Return
type summary
​bool すべてにマッチしたら true

[F] fnmatch_or fnmatch の OR 版

fnmatch の OR 版
function fnmatch_or( array|string $patterns, string $string, int $flags = 0 ): bool

$patterns のうちどれか一つでもマッチしたら true を返す。 $patterns が空だと例外を投げる。

Example:

// aaa にマッチするので true that(fnmatch_or(['*aaa*', '*bbb*'], 'aaaX'))->isTrue(); // どれともマッチしないので false that(fnmatch_or(['*aaa*', '*bbb*'], 'cccX'))->isFalse();
Parameter
type name summary
​array|​string $patterns パターン配列(単一文字列可)
​string $string 調べる文字列
​int $flags = 0 FNM_***
Return
type summary
​bool どれかにマッチしたら true

[F] globstar globstar(再帰パターン有効)な glob

globstar(再帰パターン有効)な glob
function globstar( string $pattern, int $flags = 0 ): ?array

file_list でも代替可能だが、もっと手軽にササっとファイル一覧が欲しいこともある。

Parameter
type name summary
​string $pattern glob パターン。** が使えること以外は glob と同じ
​int $flags = 0 glob フラグ
Return
type summary
​?array マッチしたファイル名配列

[F] mkdir_p ディレクトリを再帰的に掘る

ディレクトリを再帰的に掘る
function mkdir_p( string $dirname, int $umask = 2 ): bool

既に存在する場合は何もしない(エラーも出さない)。

Parameter
type name summary
​string $dirname ディレクトリ名
​int $umask = 2 ディレクトリを掘る際の umask
Return
type summary
​bool 作成したら true

[F] path_is_absolute パスが絶対パスか判定する

パスが絶対パスか判定する
function path_is_absolute(string $path): bool

Example:

that(path_is_absolute('/absolute/path'))->isTrue(); that(path_is_absolute('relative/path'))->isFalse(); // Windows 環境では下記も true になる if (DIRECTORY_SEPARATOR === '\\') { that(path_is_absolute('\\absolute\\path'))->isTrue(); that(path_is_absolute('C:\\absolute\\path'))->isTrue(); }
Parameter
type name summary
​string $path パス文字列
Return
type summary
​bool 絶対パスなら true

[F] path_normalize パスを正規化する

パスを正規化する
function path_normalize(string $path): string

具体的には ./ や ../ を取り除いたり連続したディレクトリ区切りをまとめたりする。 realpath ではない。のでシンボリックリンクの解決などはしない。その代わりファイルが存在しなくても使用することができる。

Example:

$DS = DIRECTORY_SEPARATOR; that(path_normalize('/path/to/something'))->isSame("{$DS}path{$DS}to{$DS}something"); that(path_normalize('/path/through/../something'))->isSame("{$DS}path{$DS}something"); that(path_normalize('./path/current/./through/../something'))->isSame("path{$DS}current{$DS}something");
Parameter
type name summary
​string $path パス文字列
Return
type summary
​string 正規化されたパス

[F] path_parse パスをパースする

パスをパースする
function path_parse(string $path): array

pathinfo の(ほぼ)上位互換で下記の差異がある。

drive
Windows 環境におけるドライブ文字
root
絶対パスの場合はルートパス
parents
正規化したディレクトリ名の配列
dirnames
ディレクトリ名の配列(余計なことはしない)
localname
複数拡張子を考慮した本当のファイル名部分
localpath
ディレクトリ名(余計なことはしない)+複数拡張子を考慮した本当のファイル名部分(フルパス - 拡張子)
extensions
複数拡張子の配列(余計なことはしない)

「余計なことはしない」とは空文字をフィルタしたりパスを正規化したりを指す。 結果のキーはバージョンアップで増えることがある(その場合は互換性破壊とはみなさない)。

なお、いわゆる URL はサポートしない(スキーム付きを与えた場合の挙動は未定義)。

Example:

$DS = DIRECTORY_SEPARATOR; // 色々混ぜたサンプル that(path_parse('C:/dir1/.././dir2/file.sjis..min.js'))->is([ "dirname" => "C:/dir1/.././dir2", "basename" => "file.sjis..min.js", "extension" => "js", "filename" => "file.sjis..min", // ここまでオリジナルの pathinfo 結果 "drive" => "C:", "root" => "/", // 環境依存しない元のルートパス "parents" => ["dir2"], // 正規化されたディレクトリ配列 "dirnames" => ["dir1", "..", ".", "dir2"], // 余計なことをしていないディレクトリ配列 "localname" => "file", // 複数拡張子を考慮した本当のファイル名部分 "localpath" => "C:/dir1/.././dir2{$DS}file", // ↑にディレクトリ名を付与したもの "extensions" => ["sjis", "", "min", "js"], // 余計なことをしていない拡張子配列 ]); // linux における絶対パス that(path_parse('/dir1/dir2/file.sjis.min.js'))->is([ "dirname" => "/dir1/dir2", "basename" => "file.sjis.min.js", "extension" => "js", "filename" => "file.sjis.min", // ここまでオリジナルの pathinfo 結果 "drive" => "", // 環境を問わず空 "root" => "/", // 絶対パスなので "/" "parents" => ["dir1", "dir2"], // ..等がないので dirnames と同じ "dirnames" => ["dir1", "dir2"], // ディレクトリ配列 "localname" => "file", "localpath" => "/dir1/dir2{$DS}file", "extensions" => ["sjis", "min", "js"], // 余計なことをしていない拡張子配列 ]); // linux における相対パス that(path_parse('dir1/dir2/file.sjis.min.js'))->is([ "dirname" => "dir1/dir2", "basename" => "file.sjis.min.js", "extension" => "js", "filename" => "file.sjis.min", // ここまでオリジナルの pathinfo 結果 "drive" => "", "root" => "", // 相対パスなので空(ここ以外は絶対パスと同じ) "parents" => ["dir1", "dir2"], "dirnames" => ["dir1", "dir2"], "localname" => "file", "localpath" => "dir1/dir2{$DS}file", "extensions" => ["sjis", "min", "js"], ]); // ディレクトリ無し that(path_parse('file.sjis.min.js'))->is([ "dirname" => ".", "basename" => "file.sjis.min.js", "extension" => "js", "filename" => "file.sjis.min", // ここまでオリジナルの pathinfo 結果 "drive" => "", "root" => "", "parents" => [], // オリジナルの pathinfo のようにドットが紛れ込んだりはしない "dirnames" => [], // オリジナルの pathinfo のようにドットが紛れ込んだりはしない "localname" => "file", "localpath" => "file", "extensions" => ["sjis", "min", "js"], ]);
Parameter
type name summary
​string $path パス
Return
type summary
​array パス情報

[F] path_relative パスを相対パスに変換して正規化する

パスを相対パスに変換して正規化する
function path_relative( string $from, string $to ): string

$from から $to への相対パスを返す。

Example:

$DS = DIRECTORY_SEPARATOR; that(path_relative('/a/b/c/X', '/a/b/c/d/X'))->isSame("..{$DS}d{$DS}X"); that(path_relative('/a/b/c/d/X', '/a/b/c/X'))->isSame("..{$DS}..{$DS}X"); that(path_relative('/a/b/c/X', '/a/b/c/X'))->isSame("");
Parameter
type name summary
​string $from 元パス
​string $to 対象パス
Return
type summary
​string 相対パス

[F] path_resolve パスを絶対パスに変換して正規化する

パスを絶対パスに変換して正規化する
function path_resolve(string ...$paths): ?string

可変引数で与えられた文字列群を結合して絶対パス化して返す。 出来上がったパスが絶対パスでない場合は PATH 環境変数を使用して解決を試みる。 それでも絶対パスにできない場合は null を返す。

Example:

$DS = DIRECTORY_SEPARATOR; that(path_resolve('/absolute/path'))->isSame("{$DS}absolute{$DS}path"); that(path_resolve('/absolute/path/through', '../current/./path'))->isSame("{$DS}absolute{$DS}path{$DS}current{$DS}path");
Parameter
type name summary
​string ...$paths パス文字列(可変引数)
Return
type summary
​?string 絶対パス

[F] rm_rf 中身があっても消せる rmdir

中身があっても消せる rmdir
function rm_rf( string $dirname, bool $self = true ): bool

Example:

mkdir(sys_get_temp_dir() . '/new/make/dir', 0777, true); rm_rf(sys_get_temp_dir() . '/new'); that(file_exists(sys_get_temp_dir() . '/new'))->isSame(false);
Parameter
type name summary
​string $dirname 削除するディレクトリ名。glob パターンが使える
​bool $self = true 自分自身も含めるか。false を与えると中身だけを消す
Return
type summary
​bool 成功した場合に TRUE を、失敗した場合に FALSE を返します

[F] rsync php レイヤーで rsync 的なことをする

php レイヤーで rsync 的なことをする
function rsync( string $src, string $dst, array $options = [] ): \Generator

rsync プロトコルを喋ったりオプションを完全網羅していたりはしない(意図的に合わせたりはしているが)。 単純に必要に迫られて実装したものであり、効率も度外視。

generator で実装されており、動作ログを generate する。 generator は返り値として統計情報を返す。

ただの劣化 rsync だが php 実装なのでストリームラッパーさえ実装すればプロトコルを超えることができる。 やる気になればリモート間転送も可能(自身経由なので速度は劣悪になるが)。 つまり file -> S3 や S3 -> S3 等も可能。

generate される文字列は互換性を担保しない。

Parameter
type name summary
​string $src 送信側ディレクトリ
​string $dst 受信側ディレクトリ
​array $options = [] オプション
Return
type summary
Generator 動作ログを返す generator

[F] strmode 8進数値を ls -l 準拠なパーミッション文字列に変換する

8進数値を ls -l 準拠なパーミッション文字列に変換する
function strmode(int $octet): string

000_0000 のような6桁を想定しているが、 0000 のような3桁でも受け入れる。 その場合、ファイル種別のプレフィックスは付与されず、 "rwxrwxrwx" のような9文字を返す。

Example:

// 通常ファイル 0700 that(strmode(010_0700))->is('-rwx------'); // ディレクトリ 0770 that(strmode(004_0770))->is('drwxrwx---'); // ファイル種別はなくても良い that(strmode(0770))->is('rwxrwx---');
Parameter
type name summary
​int $octet 8進数
Return
type summary
​string パーミッション文字列(ls -l 準拠)

[F] strmode2oct ls -l 準拠なパーミッション文字列を8進数値に変換する

ls -l 準拠なパーミッション文字列を8進数値に変換する
function strmode2oct(string $perms): int

drwxrwxrwx のような10文字を想定しているが、 rwxrwxrwx のような9文字でも受け入れる。

set や sticky は現時点では未対応。

Example:

// 通常ファイル rwx------ that(strmode2oct('-rwx------'))->is(010_0700); // ディレクトリ rwxrwx--- that(strmode2oct('drwxrwx---'))->is(004_0770); // 9文字でも良い that(strmode2oct('rwxrwx---'))->is(0770);
Parameter
type name summary
​string $perms パーミッション文字列(ls -l 準拠)
Return
type summary
​int 8進数

[F] tmpname 終了時に削除される一時ファイル名を生成する

終了時に削除される一時ファイル名を生成する
function tmpname( string $prefix = "rft", ?string $dir = null ): string

tempnam とほぼ同じで違いは下記。

  • 引数が逆
  • 終了時に削除される
  • 失敗時に false を返すのではなく例外を投げる
Parameter
type name summary
​string $prefix = "rft" ファイル名プレフィックス
​?string $dir = null 生成ディレクトリ。省略時は sys_get_temp_dir()
Return
type summary
​string 一時ファイル名

[N] ryunosuke\Functions\Package\funchand\

[F] functions

[F] by_builtin Countable#count, Serializable#serialize などの「ネイティブ由来かメソッド由来か」を判定して返す

Countable#count, Serializable#serialize などの「ネイティブ由来かメソッド由来か」を判定して返す
function by_builtin( object|string $class, string $function ): bool

Countable#count, Serializable#serialize のように「インターフェースのメソッド名」と「ネイティブ関数名」が一致している必要がある。

Example:

class CountClass implements \Countable { public function count(): int { // count 経由なら 1 を、メソッド経由なら 0 を返す return (int) by_builtin($this, 'count'); } } $counter = new CountClass(); that(count($counter))->isSame(1); that($counter->count())->isSame(0);

のように判定できる。

Parameter
type name summary
​object|​string $class
​string $function
Return
type summary
​bool ネイティブなら true

[F] chain 関数をメソッドチェーンできるオブジェクトを返す

関数をメソッドチェーンできるオブジェクトを返す
function chain(mixed $source = null): ChainObject

ChainObject という関数をチェーンできるオブジェクトを返す。 ChainObject は大抵のグローバル関数がアノテーションされており、コード補完することが出来る(利便性のためであり、IDE がエラーなどを出しても呼び出し自体は可能)。 呼び出しは「第1引数に現在の値が適用」されて実行される(下記の func[X] コールで任意の位置に適用されることもできる)。

下記の特殊ルールにより、特殊な呼び出し方ができる。

  • nullsafe 設定にすると「値が null の場合は呼び出し自体を行わない」という動作になり null をそのまま返す
  • array_XXX, str_XXX は省略して XXX で呼び出せる
    • 省略した結果、他の関数と被る場合は可能な限り型で一致する呼び出しを行う
  • func(..., , ...) で で「値があたる位置」を明示できる
    • str_replace('from', 'to', _) のように呼び出せる
  • func[1] で「引数1(0 ベースなので要は2番目)に適用して func を呼び出す」ことができる
    • func[2], func[3] 等も呼び出し可能
  • func['E'] で eval される文字列のクロージャを呼べる
    • 引数名は $1, $2 のような文字列で指定できる
    • $X が無いときに限り 最左に $1 が自動付与される
  • 引数が1つの呼び出しは () を省略できる

この特殊ルールは普通に使う分にはそこまで気にしなくて良い。 map や filter を駆使しようとすると必要になるが、イテレーション目的ではなく文字列のチェインなどが目的であればほぼ使うことはない。

上記を含むメソッド呼び出しはすべて自分自身を返すので、最終結果を得たい場合は invoke を実行する必要がある。 ただし、 IteratorAggregate が実装されているので、配列の場合に限り foreach で直接回すことができる。 その他、 Stringable や Countable, JsonSerializable など「値が必要になりそうなインターフェース」が実装されている。

用途は配列のイテレーションを想定しているが、あくまで「チェイン可能にする」が目的なので、ソースが文字列だろうとオブジェクトだろうと何でも呼び出しが可能。 ただし、遅延評価も最適化も何もしていないので、 chain するだけでも動作は相当遅くなることに注意。

Example:

# 1~9 のうち「5以下を抽出」して「値を2倍」して「合計」を出すシチュエーション $n1_9 = range(1, 9); // 素の php で処理したもの。パッと見で何してるか分からないし、処理の順番が思考と逆なので混乱する that(array_sum(array_map(fn($v) => $v * 2, array_filter($n1_9, fn($v) => $v <= 5))))->isSame(30); // chain でクロージャを渡したもの。処理の順番が思考どおりだが、 fn() が微妙にうざい(array_ は省略できるので filter, map, sum のような呼び出しができている) that(chain($n1_9)->filter(fn($v) => $v <= 5)->maps(fn($v) => $v * 2)->sum()())->isSame(30); // func['E'] を介したもの。かなり直感的だが eval なので少し不安 that(chain($n1_9)->filter['E']('<= 5')->maps['E']('* 2')->sum()())->isSame(30); # "hello world" を「" " で分解」して「空文字を除去」してそれぞれに「ucfirst」して「"/" で結合」して「rot13」して「md5」して「大文字化」するシチュエーション $string = 'hello world'; // 素の php で処理したもの。もはやなにがなんだか分からない that(strtoupper(md5(str_rot13(implode('/', array_map('ucfirst', array_filter(explode(' ', $string))))))))->isSame('10AF4DAF67D0D666FCEA0A8C6EF57EE7'); // chain だとかなりそれっぽくできる。 explode/implode の第1引数は区切り文字なので func[1] 構文を使用している。また、 rot13 以降は引数がないので () を省略している that(chain($string)->explode[1](' ')->filter()->maps('ucfirst')->implode[1]('/')->rot13->md5->strtoupper()())->isSame('10AF4DAF67D0D666FCEA0A8C6EF57EE7'); # よくある DB レコードをあれこれするシチュエーション $rows = [ ['id' => 1, 'name' => 'hoge', 'sex' => 'F', 'age' => 17, 'salary' => 230000], ['id' => 3, 'name' => 'fuga', 'sex' => 'M', 'age' => 43, 'salary' => 480000], ['id' => 7, 'name' => 'piyo', 'sex' => 'M', 'age' => 21, 'salary' => 270000], ['id' => 9, 'name' => 'hage', 'sex' => 'F', 'age' => 30, 'salary' => 320000], ]; // e.g. 男性の平均給料 that(chain($rows)->where['E']('sex', '=== "M"')->column('salary')->mean()())->isSame(375000); // e.g. 女性の平均年齢 that(chain($rows)->where['E']('sex', '=== "F"')->column('age')->mean()())->isSame(23.5); // e.g. 30歳以上の平均給料 that(chain($rows)->where['E']('age', '>= 30')->column('salary')->mean()())->isSame(400000); // e.g. 20~30歳の平均給料 that(chain($rows)->where['E']('age', '>= 20')->where['E']('age', '<= 30')->column('salary')->mean()())->isSame(295000); // e.g. 男性の最小年齢 that(chain($rows)->where['E']('sex', '=== "M"')->column('age')->min()())->isSame(21); // e.g. 女性の最大給料 that(chain($rows)->where['E']('sex', '=== "F"')->column('salary')->max()())->isSame(320000);
Parameter
type name summary
​mixed $source = null 元データ
Return
type summary
ChainObject

[F] func_eval 指定コードで eval するクロージャを返す

指定コードで eval するクロージャを返す
function func_eval( string $expression, mixed ...$variadic ): \Closure

create_function のクロージャ版みたいなもの。 参照渡しは未対応。

コード中の $1, $2 等の文字は func_get_arg(1) のような引数関数に変換される。

Example:

$func_eval = func_eval('$a + $b + $c', 'a', 'b', 'c'); that($func_eval(1, 2, 3))->isSame(6); // $X による参照 $func_eval = func_eval('$1 + $2 + $3'); that($func_eval(1, 2, 3))->isSame(6);
Parameter
type name summary
​string $expression eval コード
​mixed ...$variadic 引数名(可変引数)
Return
type summary
Closure 新しいクロージャ

[F] func_method 指定メソッドを呼び出すクロージャを返す

指定メソッドを呼び出すクロージャを返す
function func_method( string $methodname, mixed ...$defaultargs ): \Closure

この関数を呼ぶとメソッドのクロージャを返す。 そのクロージャにオブジェクトを与えて呼び出すとそれはメソッド呼び出しとなる。

オプションでデフォルト引数を設定できる(Example を参照)。

Example:

// 与えられた引数を結合して返すメソッド hoge を持つクラス $object = new class() { function hoge(...$args) { return implode(',', $args); } }; // hoge を呼び出すクロージャ $hoge = func_method('hoge'); // ↑を使用して $object の hoge を呼び出す that($hoge($object, 1, 2, 3))->isSame('1,2,3'); // デフォルト値付きで hoge を呼び出すクロージャ $hoge789 = func_method('hoge', 7, 8, 9); // ↑を使用して $object の hoge を呼び出す(引数指定してるので結果は同じ) that($hoge789($object, 1, 2, 3))->isSame('1,2,3'); // 同上(一部デフォルト値) that($hoge789($object, 1, 2))->isSame('1,2,9'); // 同上(全部デフォルト値) that($hoge789($object))->isSame('7,8,9');
Parameter
type name summary
​string $methodname メソッド名
​mixed ...$defaultargs メソッドのデフォルト引数
Return
type summary
Closure メソッドを呼び出すクロージャ

[F] func_new 指定クラスのコンストラクタを呼び出すクロージャを返す

指定クラスのコンストラクタを呼び出すクロージャを返す
function func_new( string $classname, mixed ...$defaultargs ): \Closure

この関数を呼ぶとコンストラクタのクロージャを返す。

オプションでデフォルト引数を設定できる(Example を参照)。

Example:

// Exception のコンストラクタを呼ぶクロージャ $newException = func_new(\Exception::class, 'hoge'); // デフォルト引数を使用して Exception を作成 that($newException()->getMessage())->isSame('hoge'); // 引数を指定して Exception を作成 that($newException('fuga')->getMessage())->isSame('fuga');
Parameter
type name summary
​string $classname クラス名
​mixed ...$defaultargs コンストラクタのデフォルト引数
Return
type summary
Closure コンストラクタを呼び出すクロージャ

[F] func_operator 演算子のクロージャを返す

演算子のクロージャを返す
function func_operator( string $operator, mixed ...$operands ): \Closure

関数ベースなので ?? のような言語組み込みの特殊な演算子は若干希望通りにならない(Notice が出る)。 2つ目以降の引数でオペランドを指定できる。

Example:

$not = func_operator('!'); // 否定演算子クロージャ that(false)->isSame($not(true)); $minus = func_operator('-'); // マイナス演算子クロージャ that($minus(2))->isSame(-2); // 引数1つで呼ぶと1項演算子 that($minus(3, 2))->isSame(3 - 2); // 引数2つで呼ぶと2項演算子 $cond = func_operator('?:'); // 条件演算子クロージャ that($cond('OK', 'NG'))->isSame('OK' ?: 'NG'); // 引数2つで呼ぶと2項演算子 that($cond(false, 'OK', 'NG'))->isSame(false ? 'OK' : 'NG'); // 引数3つで呼ぶと3項演算子 $gt5 = func_operator('<=', 5); // 5以下を判定するクロージャ that(array_filter([1, 2, 3, 4, 5, 6, 7, 8, 9], $gt5))->isSame([1, 2, 3, 4, 5]);
Parameter
type name summary
​string $operator 演算子
​mixed ...$operands 右オペランド
Return
type summary
Closure 演算子のクロージャ

[F] func_user_func_array パラメータ定義数に応じて呼び出し引数を可変にしてコールする

パラメータ定義数に応じて呼び出し引数を可変にしてコールする
function func_user_func_array(callable|null $callback): callable

デフォルト引数はカウントされない。必須パラメータの数で呼び出す。

$callback に null を与えると例外的に「第1引数を返すクロージャ」を返す。

php の標準関数は定義数より多い引数を投げるとエラーを出すのでそれを抑制したい場合に使う。

Example:

// strlen に2つの引数を渡してもエラーにならない $strlen = func_user_func_array('strlen'); that($strlen('abc', null))->isSame(3);
Parameter
type name summary
​callable|​null $callback 呼び出すクロージャ
Return
type summary
​callable 引数ぴったりで呼び出すクロージャ

[F] func_wiring 引数の型情報に基づいてワイヤリングしたクロージャを返す

引数の型情報に基づいてワイヤリングしたクロージャを返す
function func_wiring( callable $callable, array|\ArrayAccess $dependency ): \Closure

$dependency に数値キーの配列を混ぜるとデフォルト値として使用される。 得られたクロージャの呼び出し時に引数を与える事ができる。

parameter_wiring も参照。

Example:

$closure = fn ($a, $b) => func_get_args(); $new_closure = func_wiring($closure, [ '$a' => 'a', '$b' => 'b', 1 => 'B', ]); that($new_closure())->isSame(['a', 'B']); // 同時指定の場合は数値キー優先 that($new_closure('A'))->isSame(['A', 'B']); // 呼び出し時の引数優先
Parameter
type name summary
​callable $callable 対象 callable
​array|​ArrayAccess $dependency 引数候補配列
Return
type summary
Closure 引数を確定したクロージャ

[F] function_alias 関数のエイリアスを作成する

関数のエイリアスを作成する
function function_alias( callable $original, string $alias )

単に移譲するだけではなく、参照渡し・参照返しも模倣される。 その代わり、単純なエイリアスではなく別定義で吐き出すので「エイリアス」ではなく「処理が同じな別関数」と思ったほうがよい。

静的であればクラスメソッドも呼べる。

Example:

// trim のエイリアス function_alias('trim', 'trim_alias'); that(trim_alias(' abc '))->isSame('abc');
Parameter
type name summary
​callable $original 元となる関数
​string $alias 関数のエイリアス名

[F] function_shorten 関数の名前空間部分を除いた短い名前を取得する

関数の名前空間部分を除いた短い名前を取得する
function function_shorten(string $function): string
Parameter
type name summary
​string $function 短くする関数名
Return
type summary
​string 短い関数名

[F] is_bindable_closure $this を bind 可能なクロージャか調べる

$this を bind 可能なクロージャか調べる
function is_bindable_closure(\Closure $closure): bool

Example:

that(is_bindable_closure(function () {}))->isTrue(); that(is_bindable_closure(static function () {}))->isFalse();
Parameter
type name summary
Closure $closure 調べるクロージャ
Return
type summary
​bool $this を bind 可能なクロージャなら true

[F] is_callback callable のうち、関数文字列を false で返す

callable のうち、関数文字列を false で返す
function is_callback(mixed $callable): bool

歴史的な経緯で php の callable は多岐に渡る。

単純なコールバック
"strtolower"
staticメソッドのコール
["ClassName", "method"]
オブジェクトメソッドのコール
[$object, "method"]
staticメソッドのコール
"ClassName::method"
相対指定によるstaticメソッドのコール
["ClassName", "parent::method"]
__invoke実装オブジェクト
$object
クロージャ
fn() => something()

上記のうち 1 を callable とはみなさず false を返す。 現代的には Closure::fromCallable, $object->method(...) などで callable == Closure という概念が浸透しているが、そうでないこともある。 本ライブラリでも preg_splicearray_sprintf などで頻出しているので関数として定義する。

副作用はなく、クラスのロードや関数の存在チェックなどは行わない。あくまで型と形式で判定する。 引数は callable でなくても構わない。その場合単に false を返す。

Parameter
type name summary
​mixed $callable 対象 callable
Return
type summary
​bool 関数呼び出しの callable なら false

[N] ryunosuke\Functions\Package\info\

[F] functions

[F] ansi_colorize 文字列に ANSI Color エスケープシーケンスを埋め込む

文字列に ANSI Color エスケープシーケンスを埋め込む
function ansi_colorize( string $string, string $color ): string
  • "blue" のような小文字色名は文字色
  • "BLUE" のような大文字色名は背景色
  • "bold" のようなスタイル名は装飾

となる。その区切り文字は現在のところ厳密に定めていない(fore+back|bold のような形式で定めることも考えたけどメリットがない)。 つまり、アルファベット以外で分割するので、

blue|WHITE@bold
文字青・背景白・太字
blue WHITE bold underscore
文字青・背景白・太字・下線
italic|bold,blue+WHITE
文字青・背景白・太字・斜体

という動作になる(記号で区切られていれば形式はどうでも良いということ)。 ただ、この指定方法は変更が入る可能性が高いのでスペースあたりで区切っておくのがもっとも無難。

Parameter
type name summary
​string $string 対象文字列
​string $color 色とスタイル文字列
Return
type summary
​string エスケープシーケンス付きの文字列

[F] ansi_strip ANSI エスケープ文字を取り除く

ANSI エスケープ文字を取り除く
function ansi_strip(string $string): string

Example:

$ansi_string = ansi_colorize('hoge', 'bold red'); // エスケープ文字も含めて 19 文字 that(strlen($ansi_string))->isSame(19); // ansi_strip すると本来の hoge がえられる that(ansi_strip($ansi_string))->isSame('hoge');
Parameter
type name summary
​string $string 対象文字列
Return
type summary
​string ANSI エスケープ文字が取り除かれた文字

[F] arguments コマンドライン引数をパースして引数とオプションを返す

コマンドライン引数をパースして引数とオプションを返す
function arguments( array $rule, array|string|null $argv = null ): array

少しリッチな getopt として使える(shell 由来のオプション構文(a:b::)はどうも馴染みにくい)。 ただし「値が必須なオプション」はサポートしない。 もっとも、オプションとして空文字が来ることはほぼ無いのでデフォルト値を空文字にすることで対応可能。

$rule に従って --noval filename --opt optval のような文字列・配列をパースする。 $rule 配列の仕様は下記。

  • キーは「オプション名」を指定する。ただし・・・
    • 数値キーは「引数」を意味する
    • スペースの後に「ショート名」を与えられる
  • 値は「デフォルト値」を指定する。ただし・・・
    • [] は「複数値オプション」を意味する(配列にしない限り同オプションの多重指定は許されない)
    • null は「値なしオプション」を意味する(スイッチングオプション)
  • 空文字キーは解釈自体のオプションを与える
    • 今のところ throw のみの実装。配列ではなく bool を与えられる

上記の仕様でパースして「引数は数値連番、オプションはオプション名をキーとした配列」を返す。 なお、いわゆる「引数」はどこに来ても良い(前オプション、後オプションの区別がない)。

$argv には配列や文字列が与えられるが、ほとんどテスト用に近く、普通は未指定で $argv を使うはず。

Example:

// いくつか織り交ぜたスタンダードな例 $rule = [ 'opt' => 'def', // 基本的には「デフォルト値」を表す 'longopt l' => '', // スペース区切りで「ショート名」を意味する 1 => 'defarg', // 数値キーは「引数」を意味する ]; that(arguments($rule, '--opt optval arg1 -l longval'))->isSame([ 'opt' => 'optval', // optval と指定している 'longopt' => 'longval', // ショート名指定でも本来の名前で返ってくる 'arg1', // いわゆるコマンドライン引数(optval は opt に飲まれるので含まれない) 'defarg', // いわゆるコマンドライン引数(与えられていないが、ルールの 1 => 'defarg' が活きている) ]); // 「値なしオプション」と「複数値オプション」の例 $rule = [ 'noval1 l' => null, // null は「値なしオプション」を意味する(指定されていれば true されていなければ false を返す) 'noval2 m' => null, // 同上 'noval3 n' => null, // 同上 'opts o' => [], // 配列を与えると「複数値オプション」を表す ]; that(arguments($rule, '--opts o1 -ln arg1 -o o2 arg2 --opts o3'))->isSame([ 'noval1' => true, // -ln で同時指定されているので true 'noval2' => false, // -ln で同時指定されてないので false 'noval3' => true, // -ln の同時指定されているので true 'opts' => ['o1', 'o2', 'o3'], // ロング、ショート混在でも OK 'arg1', // 一見 -ln のオプション値に見えるが、 noval は値なしなので引数として得られる 'arg2', // 前オプション、後オプションの区別はないのでどこに居ようと引数として得られる ]); // 空文字で解釈自体のオプションを与える $rule = [ '' => false, // 定義されていないオプションが来ても例外を投げずに引数として処理する ]; that(arguments($rule, '--long A -short B'))->isSame([ '--long', // 明らかにオプション指定に見えるが、 long というオプションは定義されていないので引数として解釈される 'A', // 同上。long のオプション値に見えるが、ただの引数 '-short', // 同上。short というオプションは定義されていない 'B', // 同上。short のオプション値に見えるが、ただの引数 ]);
Parameter
type name summary
​array $rule オプションルール
​array|​string|​null $argv = null パースするコマンドライン引数。未指定時は $argv が使用される
Return
type summary
​array コマンドライン引数+オプション

[F] cpu_timer CPU 時間を計れるオブジェクトを返す

CPU 時間を計れるオブジェクトを返す
function cpu_timer(): CpuTimer|object

コンストラクタ(あるいは start)時点から stop までの下記を返す。

real
実際の経過時間
time
CPU時間(user + system)
user
ユーザー時間
system
システム時間
idle
アイドル時間(real - time)
time%
CPU使用率(time / real)
user%
ユーザー使用率(user / time)
system%
システム使用率(system / time)
idle%
アイドル率(idle / real)

要するに POSIX の time コマンドとほぼ同じで、計算から導出できる値がちょっと増えただけ。

  • user が大きい場合(time と user が近い場合)、ユーザーランドの処理が多かったことを表す
  • system が大きい場合(time と system が近い場合)、システムコールが多かったことを表す
  • idle が大きい場合(real と time が離れている場合)、ネットワークや IO 等で CPU が遊んでいたことを表す
    • もっとも、コア数によってはその限りではない(単に他のプロセスを捌いていただけ、もあり得る)
    • linux 版 getrusage だとコンテキストスイッチが取れるので傾向は表せるけど…正確には無理だし Windows が対応していないので未対応

Example:

$timer = cpu_timer(); foreach (range(0, 999) as $i) { // ファイル IO を伴う sha1 なら user,system,idle を程よく使うはず $hash = sha1_file(__FILE__); } //var_dump($timer->result()); //{ // real: 0.13377594947814941, // user: 0.078125, // system: 0.046875, // time: 0.125, // idle: 0.008775949478149414, // user%: 62.5, // system%: 37.5, // time%: 93.4398152191154, // idle%: 6.560184780884589, //}
Return
type summary
CpuTimer|​object タイマーオブジェクト

[F] finalize 自身が死ぬときに指定 callable を呼ぶオブジェクトを返す

自身が死ぬときに指定 callable を呼ぶオブジェクトを返す
function finalize(callable $finalizer): callable

invoke を実装しているので明示的にも呼べる。 明示的だろうと暗黙的だろうと必ず1回しか呼ばれない。

Example:

$called = 0; $finalizer = finalize(function()use(&$called){$called++;}); that($called)->is(0); // まだ呼ばれていない // コールすると・・・ $finalizer(); that($called)->is(1); // 呼ばれている // unset(GC)でも呼ばれる unset($finalizer); that($called)->is(1); // が、一度しか呼ばれないので呼ばれない
Parameter
type name summary
​callable $finalizer 実行する php コード
Return
type summary
​callable GC 時に $finalizer を実行する callable

[F] get_modified_files 初回読み込み時から変更のあったファイル配列を返す

初回読み込み時から変更のあったファイル配列を返す
function get_modified_files( string|array $target_pattern = "*.php", string|array $ignore_pattern = "*.phtml" ): array

初回呼び出し時は必ず空配列を返し、以後の呼び出しで変更のあったファイルを返す。 削除されたファイルも変更とみなす。

用途的には「php で書かれたデーモンで、変更感知して自動で再起動する(systemd に任せる)」がある。

Example:

// 別プロセスで3秒後に自分自身を触る $p = process_async(PHP_BINARY, ['-r' => 'sleep(3);touch($argv[1]);', __FILE__]); $time = microtime(true); foreach (range(1, 10) as $i) { // 何らかのデーモン(完全に wait する系ではなく時々処理が戻ってくる必要がある) sleep(1); // 自身の変更を感知したら break なり exit なりで抜ける(大抵はそのまま終了する。起動は systemd に丸投げする) if (get_modified_files(__FILE__)) { break; } } // 全ループすると10秒かかるが、大体3秒程度で抜けているはず that(microtime(true) - $time)->break()->lt(3.9); unset($p);
Parameter
type name summary
​string|​array $target_pattern = "*.php" 対象ファイルパターン(マッチしないものは無視される)
​string|​array $ignore_pattern = "*.phtml" 除外ファイルパターン(マッチしたものは無視される)
Return
type summary
​array 変更のあったファイル名配列

[F] get_uploaded_files $_FILES の構造を組み替えて $_POST などと同じにする

$_FILES の構造を組み替えて $_POST などと同じにする
function get_uploaded_files(?array $files = null): array

$_FILES の配列構造はバグとしか思えないのでそれを是正する関数。 第1引数 $files は指定可能だが、大抵は $_FILES であり、指定するのはテスト用。

サンプルを書くと長くなるので例はテストファイルを参照。

Parameter
type name summary
​?array $files = null $_FILES の同じ構造の配列。省略時は $_FILES
Return
type summary
​array $_FILES を $_POST などと同じ構造にした配列

[F] getenvs 連想配列を元に環境変数を取得する

連想配列を元に環境変数を取得する
function getenvs(iterable $env_vars): array

配列のキーで取得するキーを指定できる。 連番の場合は元の環境変数名がキーとして使用される。

環境変数が存在しない場合、false ではなく null になる。 環境変数名に配列を与えた場合、順に取得を試みて、見つからなかった場合に null になる。

キーが指定されておらず、さらに環境変数候補数が1以外で環境変数が見つからない場合は例外を投げる。 (返すキーが一意に定まらない場合に例外を投げる)。

Example:

putenv('ENV1=env_1'); putenv('ENV2=env_2'); putenv('ENV3=env_3'); that(getenvs([ 'ENV1', // キー指定がない 'e2' => 'ENV2', // キー指定がある 'e3' => ['ENV4', 'ENV3', 'ENV2'], // 配列の左から取得を試みる 'e8' => 'ENV8', // 存在しない環境変数 'e9' => ['ENV9', 'ENV8', 'ENV7'], // 存在しない環境変数配列 ]))->is([ 'ENV1' => 'env_1', // キー指定がない場合、環境変数名がキーになる 'e2' => 'env_2', // キー指定がある場合、それがキーになる 'e3' => 'env_3', // ENV3 が見つかった 'e8' => null, // ENV8 が見つからないので null 'e9' => null, // ENV9, ENV8, ENV7 のどれも見つからないので null ]);
Parameter
type name summary
​iterable $env_vars [キー => 環境変数名]
Return
type summary
​array 環境変数

[F] ini_sets 複数の php.ini の設定をまとめて設定する

複数の php.ini の設定をまとめて設定する
function ini_sets(array $values): callable

返り値として「もとに戻すためのクロージャ」を返すので、復元するためにはそのクロージャを呼ぶだけで良い。

Parameter
type name summary
​array $values ini のエントリ名と値の配列
Return
type summary
​callable ini を元に戻す callable

[F] is_ansi リソースが ansi color に対応しているか返す

リソースが ansi color に対応しているか返す
function is_ansi(resource $stream): bool

パイプしたりリダイレクトしていると false を返す。

Parameter
type name summary
​resource $stream 調べるリソース
Return
type summary
​bool ansi color に対応しているなら true

[F] php_binary php-cli のパスを返す

php-cli のパスを返す
function php_binary(): string|null

見つからない場合は null を返す。 実質的には PHP_BINARY と同じと考えてよい。 ただ PHP_BINARY は SAPI によって異なるので fpm 時にはこの関数を用いて php-cli のパスを得る必要がある。

Return
type summary
​string|​null php-cli のパス

[F] setenvs 連想配列を元に環境変数を設定する

連想配列を元に環境変数を設定する
function setenvs(iterable $env_vars): array

値に null を渡すと環境変数の削除動作となる。 返り値として成否配列を返すが、この返り値の形式は互換性を維持せず、変更になる可能性がある。

Example:

setenvs([ 'ENV1' => 'e1', 'ENV2' => 'e2', 'ENV3' => null, ]); that(getenv('ENV1'))->isSame('e1'); that(getenv('ENV2'))->isSame('e2'); that(getenv('ENV3'))->isFalse();
Parameter
type name summary
​iterable $env_vars [環境変数名 => 値]
Return
type summary
​array 成否配列

[F] sys_get_memory システムのメモリを取得する

システムのメモリを取得する
function sys_get_memory(int $cacheSecond = 0): array

php にはメモリ情報を返す関数が存在しないので共通のために作成。 Windows 版はかなりやっつけなので過度に呼んではならない。

$cacheSecond を指定するとその秒数分はキャッシュを返すようになる。

Parameter
type name summary
​int $cacheSecond = 0 キャッシュ秒数
Return
type summary
​array メモリ情報

[F] sys_set_temp_dir sys_get_temp_dir が返すディレクトリを変更する

sys_get_temp_dir が返すディレクトリを変更する
function sys_set_temp_dir( string $directory, bool $creates = true, $check_settled = true ): bool

ただし、sys_get_temp_dir は一度でも呼ぶと内部的にキャッシュされるので、必ず呼ぶ前に設定しなければならない。 相対パスを指定すると標準設定からの相対になる。

$check_settled はデバッグ用なので運用では使わないこと。

Parameter
type name summary
​string $directory 一時ディレクトリ
​bool $creates = true 設定すると同時に作成するか
$check_settled = true
Return
type summary
​bool 成功時に true

[F] system_status システムの各種情報配列を返す

システムの各種情報配列を返す
function system_status( string $siunit = "", string $datetime_format = DateTime::RFC3339 ): array

Windows 版はオマケ実装。 この関数の結果は互換性を考慮しない。

Parameter
type name summary
​string $siunit = "" バイト系数値の単位
​string $datetime_format = DateTime::RFC3339 日時系のフォーマット
Return
type summary
​array

[N] ryunosuke\Functions\Package\iterator\

[F] functions

[F] iterator_chunk イテレータも使える array_chunk

イテレータも使える array_chunk
function iterator_chunk( iterable $iterator, int|\Closure $length, bool $preserve_keys = false ): \Generator[]|\Generator

Generator を返す Generator を返す。 foreach で使う分には普通の配列と遜色なく使うことができる。

大本 Generator は return 値として総数を返す。 各種 Generator は return 値として要素数を返す。

Example:

// 要素7の Generator を3つに分割 $generator = (function () { yield 'a'; yield 'b'; yield 'c'; yield 'd'; yield 'e'; yield 'f'; yield 'g'; })(); $generators = iterator_chunk($generator, 3); // 3要素 that(iterator_to_array($generators->current()))->is(['a', 'b', 'c']); that($generators->current()->getReturn())->is(3); // 3要素 $generators->next(); that(iterator_to_array($generators->current()))->is(['d', 'e', 'f']); that($generators->current()->getReturn())->is(3); // 1要素 $generators->next(); that(iterator_to_array($generators->current()))->is(['g']); that($generators->current()->getReturn())->is(1); // 大本の Generator は総数を返す $generators->next(); that($generators->getReturn())->is(7); // ハイフンが来るたびに分割(クロージャ内で next しているため、ハイフン自体は結果に含まれない) $generator = (function () { yield 'a'; yield 'b'; yield '-'; yield 'c'; yield 'd'; yield 'e'; yield 'f'; yield '-'; yield 'g'; })(); $generators = iterator_chunk($generator, function ($v, $k, $n, $c, $it) { if ($v === '-') { $it->next(); return false; } return true; }); that(iterator_to_array($generators->current()))->is(['a', 'b']); $generators->next(); that(iterator_to_array($generators->current()))->is(['c', 'd', 'e', 'f']); $generators->next(); that(iterator_to_array($generators->current()))->is(['g']);
Parameter
type name summary
​iterable $iterator イテレータ
​int|​Closure $length チャンクサイズ。クロージャを渡すと毎ループ(値, キー, ステップ, チャンク番号, イテレータ)でコールされて false を返すと1チャンク終了となる
​bool $preserve_keys = false キーの保存フラグ
Return
type summary
Generator[]|​Generator チャンク化された Generator

[F] iterator_combine キーと値のイテレータから キー => 値 なイテレータを作る

キーと値のイテレータから キー => 値 なイテレータを作る
function iterator_combine( iterable $keys, iterable $values ): \Iterator

要するに array_combine のイテレータ版。 数が一致しないと array_combine と同様例外を投げるが、呼び出し時点では投げられず、ループ後に呼ばれることに注意。

Example:

// 配列から key => value なイテレータを作る $it = iterator_combine(['a', 'b', 'c'], [1, 2, 3]); that(iterator_to_array($it))->isSame(['a' => 1, 'b' => 2, 'c' => 3]);
Parameter
type name summary
​iterable $keys キー
​iterable $values
Return
type summary
Iterator $key => $value なイテレータ

[F] iterator_join 複数の iterator を一括して回せる iterator を返す。

複数の iterator を一括して回せる iterator を返す。
function iterator_join( iterable $iterables, $preserve_keys = true ): \Iterator

要するにほぼ AppendIterator のラッパーだが、$iterable には配列も渡すことができるし、キーも振り直すことができる。

Example:

$iterator = iterator_join([ ['A'], // ただの配列 new \ArrayIterator(['B']), // Iterator (fn() => yield 'C')(), // Generator ], false); // 上記を回すと 1 ループで全要素を回せる that(iterator_to_array($iterator))->is(['A', 'B', 'C']);
Parameter
type name summary
​iterable $iterables 結合する iterable
$preserve_keys = true
Return
type summary
Iterator 一括で回せる iterator

[F] iterator_map array_map の iterator 版

array_map の iterator 版
function iterator_map( ?callable $callback, iterable ...$iterables ): \Iterator

基本的に array_map と同じ動作にしてあるが、下記の点が異なる。

  • $callback に null, $iterables に1つだけ渡したときも zip 的動作をする
    • array_map は1つの時の一貫性がなく、やや非直感的な動作をする
  • 値だけではなくキーも渡ってくる
    • 例えば $iterables が2つの場合 ($v1, $v2, $k1, $k2) の4引数が渡ってくる

数が不一致の場合、(v, k) の組が共に null で渡ってくる。

Example:

// いわゆる zip 操作 $it = iterator_map(null, (function () { yield 1; yield 2; yield 3; })(), (function () { yield 7; yield 8; yield 9; })()); that(iterator_to_array($it))->isSame([[1, 7], [2, 8], [3, 9]]); // キーも渡ってくる $it = iterator_map(fn($v1, $v2, $k1, $k2) => "$k1:$v1, $k2:$v2", (function () { yield 'a' => 1; yield 'b' => 2; yield 'c' => 3; })(), (function () { yield 'g' => 7; yield 'h' => 8; yield 'i' => 9; })()); that(iterator_to_array($it))->isSame(["a:1, g:7", "b:2, h:8", "c:3, i:9"]);
Parameter
type name summary
​?callable $callback コールバック
​iterable ...$iterables iterable
Return
type summary
Iterator コールバックを適用した iterable

[F] iterator_maps iterable にコールバック(複数)を適用した Iterator を返す

iterable にコールバック(複数)を適用した Iterator を返す
function iterator_maps( iterable $iterable, callable ...$callbacks ): \Iterator

指定したコールバックで複数回回してマップする。 引数は (値, キー, 連番) が渡ってくる。

Example:

// Generator の値を2乗してから3を足す $it = iterator_maps((function () { yield 1; yield 2; yield 3; })(), fn($v) => $v ** 2, fn($v) => $v + 3); that(iterator_to_array($it))->isSame([4, 7, 12]);
Parameter
type name summary
​iterable $iterable iterable
​callable ...$callbacks コールバック
Return
type summary
Iterator コールバックを適用した iterable

[F] iterator_split iterator から頭何件かを取り出してチャンク化して返す

iterator から頭何件かを取り出してチャンク化して返す
function iterator_split( iterable $iterable, array $chunk_sizes, bool $preserve_keys = false ): array

rewind できない iterator から頭1件だけ取り出して rewind して再ループしたい、という状況が稀によくある。 この関数と iterator_join と組み合わせればそれが容易に実現できる。

$iterable には配列も渡すことができる。

Example:

$generator = function () { yield 'a'; yield 'b'; yield 'c'; yield 'd'; yield 'e'; yield 'f'; yield 'g'; }; // 要素7の Generator から頭1,2件を取り出す [$one, $two, $gen] = iterator_split($generator(), [1, 2]); // 最初の要素は1件 that($one)->is(['a']); // 次の要素は2件 that($two)->is(['b', 'c']); // かならず最後の要素に元の iterator が来る that($gen)->isInstanceOf(\Iterator::class); // $chunk_sizes の配列のキーは返り値のキーになる ['one' => $one, 'two' => $two, 0 => $gen] = iterator_split($generator(), ['one' => 1, 'two' => 2]); // one は1件 that($one)->is(['a']); // two は2件 that($two)->is(['b', 'c']); // かならず最後の要素に元の iterator が来る that($gen)->isInstanceOf(\Iterator::class);
Parameter
type name summary
​iterable $iterable 対象 iterator
​array $chunk_sizes 各チャンクの数を指定する
​bool $preserve_keys = false キーを保存するか
Return
type summary
​array $chunk_sizes の数+1 の iterable 配列

[N] ryunosuke\Functions\Package\math\

[F] functions

[F] average 引数の意味平均値を返す

引数の意味平均値を返す
function average(mixed ...$variadic): mixed
  • 3座標の重心座標とか日付の平均とかそういうもの
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 意味平均値

[F] base_convert_array 配列を対象とした base_convert

配列を対象とした base_convert
function base_convert_array( array $array, int $from_base, int $to_base ): array

配列の各要素を数値の桁とみなして基数を変換する。

Example:

// 123(10進)を7B(16進)に変換 that(base_convert_array([1, 2, 3], 10, 16))->isSame([7, 11]); // つまりこういうこと(10 * 10^2 + 20 * 10^1 + 30 * 10^0 => 4 * 16^2 + 12 * 16^1 + 14 * 16^0) that(base_convert_array([10, 20, 30], 10, 16))->isSame([4, 12, 14]);
Parameter
type name summary
​array $array 対象配列
​int $from_base 変換元基数
​int $to_base 変換先基数
Return
type summary
​array 基数変換後の配列

[F] calculate_formula 数式を計算して結果を返す

数式を計算して結果を返す
function calculate_formula(string $formula): int|float

内部的には eval で計算するが、文字列や関数呼び出しなどは受け付けないため原則としてセーフティ。 許可されるのは定数・数値リテラルと演算子のみ。 定数を許可しているのは PI(3.14) や HOUR(3600) などの利便性のため。 定数値が非数値の場合、強制的に数値化して警告を促す。

Example:

that(calculate_formula('1 + 2 - 3 * 4'))->isSame(-9); that(calculate_formula('1 + (2 - 3) * 4'))->isSame(-3); that(calculate_formula('PHP_INT_SIZE * 3'))->isSame(PHP_INT_SIZE * 3);
Parameter
type name summary
​string $formula 計算式
Return
type summary
​int|​float 計算結果

[F] clamp 値を一定範囲に収める

値を一定範囲に収める
function clamp( int|mixed $value, int|mixed $min, int|mixed $max, bool $circulative = false ): int

$circulative に true を渡すと値が循環する。 ただし、循環的な型に限る(整数のみ?)。

Example:

// 5~9 に収める that(clamp(4, 5, 9))->isSame(5); // 4 は [5~9] の範囲外なので 5 に切り上げられる that(clamp(5, 5, 9))->isSame(5); // 範囲内なのでそのまま that(clamp(6, 5, 9))->isSame(6); // 範囲内なのでそのまま that(clamp(7, 5, 9))->isSame(7); // 範囲内なのでそのまま that(clamp(8, 5, 9))->isSame(8); // 範囲内なのでそのまま that(clamp(9, 5, 9))->isSame(9); // 範囲内なのでそのまま that(clamp(10, 5, 9))->isSame(9); // 10 は [5~9] の範囲外なので 9 に切り下げられる // 5~9 に収まるように循環する that(clamp(4, 5, 9, true))->isSame(9); // 4 は [5~9] の範囲外なので循環して 9 になる that(clamp(5, 5, 9, true))->isSame(5); // 範囲内なのでそのまま that(clamp(6, 5, 9, true))->isSame(6); // 範囲内なのでそのまま that(clamp(7, 5, 9, true))->isSame(7); // 範囲内なのでそのまま that(clamp(8, 5, 9, true))->isSame(8); // 範囲内なのでそのまま that(clamp(9, 5, 9, true))->isSame(9); // 範囲内なのでそのまま that(clamp(10, 5, 9, true))->isSame(5); // 10 は [5~9] の範囲外なので循環して 5 になる
Parameter
type name summary
​int|​mixed $value 対象の値
​int|​mixed $min 最小値
​int|​mixed $max 最大値
​bool $circulative = false true だと切り詰めるのではなく循環する
Return
type summary
​int 一定範囲に収められた値

[F] decimal 数値を指定桁数に丸める

数値を指定桁数に丸める
function decimal( int|float $value, int $precision = 0, mixed $mode = 0 ): float

感覚的には「桁数指定できる ceil/floor」に近い。 ただし、正の方向(ceil)、負の方向(floor)以外にも0の方向、無限大の方向も実装されている(さらに四捨五入もできる)。

0
0 に近づく方向: 絶対値が必ず減る
null
0 から離れる方向: 絶対値が必ず増える
-INF
負の無限大の方向: 数値として必ず減る
+INF
正の無限大の方向: 数値として必ず増える

のように「持っていきたい方向(の数値)」を指定すれば良い(正負自動だけ null で特殊だが)。

Example:

that(decimal(-3.14, 1, 0))->isSame(-3.1); // 0 に近づく方向 that(decimal(-3.14, 1, null))->isSame(-3.2); // 0 から離れる方向 that(decimal(-3.14, 1, -INF))->isSame(-3.2); // 負の無限大の方向 that(decimal(-3.14, 1, +INF))->isSame(-3.1); // 正の無限大の方向 that(decimal(3.14, 1, 0))->isSame(3.1); // 0 に近づく方向 that(decimal(3.14, 1, null))->isSame(3.2); // 0 から離れる方向 that(decimal(3.14, 1, -INF))->isSame(3.1); // 負の無限大の方向 that(decimal(3.14, 1, +INF))->isSame(3.2); // 正の無限大の方向
Parameter
type name summary
​int|​float $value 丸める値
​int $precision = 0 有効桁数
​mixed $mode = 0 丸めモード(0
Return
type summary
​float 丸めた値

[F] maximum 引数の最大値を返す

引数の最大値を返す
function maximum(mixed ...$variadic): mixed
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

that(maximum(-1, 0, 1))->isSame(1);
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 最大値

[F] mean 引数の相加平均値を返す

引数の相加平均値を返す
function mean(mixed ...$variadic): int|float
  • is_numeric でない値は除外される(計算結果に影響しない)
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

that(mean(1, 2, 3, 4, 5, 6))->isSame(3.5); that(mean(1, '2', 3, 'noize', 4, 5, 'noize', 6))->isSame(3.5);
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​int|​float 相加平均値

[F] median 引数の中央値を返す

引数の中央値を返す
function median(mixed ...$variadic): mixed
  • 要素数が奇数の場合は完全な中央値/偶数の場合は中2つの平均。「平均」という概念が存在しない値なら中2つの後の値
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

// 偶数個なので中2つの平均 that(median(1, 2, 3, 4, 5, 6))->isSame(3.5); // 奇数個なのでど真ん中 that(median(1, 2, 3, 4, 5))->isSame(3); // 偶数個だが文字列なので中2つの後 that(median('a', 'b', 'c', 'd'))->isSame('c');
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 中央値

[F] minimum 引数の最小値を返す

引数の最小値を返す
function minimum(mixed ...$variadic): mixed
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

that(minimum(-1, 0, 1))->isSame(-1);
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 最小値

[F] mode 引数の最頻値を返す

引数の最頻値を返す
function mode(mixed ...$variadic): mixed
  • 等価比較は文字列で行う。小数時は注意。おそらく php.ini の precision に従うはず
  • 等価値が複数ある場合の返り値は不定
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

that(mode(0, 1, 2, 2, 3, 3, 3))->isSame(3);
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 最頻値

[F] sum 引数の合計値を返す

引数の合計値を返す
function sum(mixed ...$variadic): mixed
  • is_numeric でない値は除外される(計算結果に影響しない)
  • 配列は個数ではなくフラット展開した要素を対象にする
  • 候補がない場合はエラーではなく例外を投げる

Example:

that(sum(1, 2, 3, 4, 5, 6))->isSame(21);
Parameter
type name summary
​mixed ...$variadic 対象の変数・配列・リスト
Return
type summary
​mixed 合計値

[N] ryunosuke\Functions\Package\misc\

[F] functions

[F] annotation_parse アノテーションっぽい文字列をそれっぽくパースして返す

アノテーションっぽい文字列をそれっぽくパースして返す
function annotation_parse( string|\Reflector $annotation, array|mixed $schema = [], string|array $nsfiles = [] ): array

$annotation にはリフレクションオブジェクトも渡せる。 その場合、getDocComment や getFilename, getNamespaceName などを用いてある程度よしなに名前解決する。 もっとも、@Class(args) 形式を使わないのであれば特に意味はない。

$schame で「どのように取得するか?」のスキーマ定義が渡せる。 スキーマ定義は連想配列で各アノテーションごとに下記のような定義を指定でき、連想配列でない場合はすべてのアノテーションにおいて指定したとみなされる。

true
余計なことはせず、アノテーションの文字列をそのまま返す
false
下記にようによしなに変換して返す
[]
複数値モードを強制する
null
単一値モードを強制する

アノテーションの仕様は下記(すべて $schema が false であるとする)。

  • @から行末まで(1行に複数のアノテーションは含められない)
    • ただし行末が ({[ のいずれかであれば次の ]}) までブロックを記載する機会が与えられる
    • ブロックを見つけたときは本来値となるべき値がキーに、ブロックが値となり、結果は必ず配列化される
  • 同じアノテーションを複数見つけたときは配列化される
  • @hogera
    値なしは null を返す
    @hogera v1 "v2 v3"
    ["v1", "v2 v3"] という配列として返す
    @hogera {key: 123}
    ["key" => 123] という(連想)配列として返す
    @hogera [123, 456]
    [123, 456] という連番配列として返す
    @hogera ("2019/12/23")
    hogera で解決できるクラス名で new して返す($filename 引数の指定が必要)
  • 下3つの形式はアノテーション区切りのスペースはあってもなくても良い
  • $schema が true だと上記のような変換は一切行わず、素朴な文字列で返す。 あくまで簡易実装であり、本格的に何かをしたいなら専用のパッケージを導入したほうが良い。

    Example:

    $annotations = annotation_parse(' @noval @single this is value @closure this is value @array this is value @hash {key: 123} @list [1, 2, 3] @ArrayObject([1, 2, 3]) @block message { this is message1 this is message2 } @same this is same value1 @same this is same value2 @same this is same value3 ', [ 'single' => true, 'closure' => fn($value) => explode(' ', strtoupper($value)), ]); that($annotations)->is([ 'noval' => null, // 値なしは null になる 'single' => 'this is value', // $schema 指定してるので文字列になる 'closure' => ['THIS', 'IS', 'VALUE'], // $schema 指定してそれがクロージャだとコールバックされる 'array' => ['this', 'is', 'value'], // $schema 指定していないので配列になる 'hash' => ['key' => '123'], // 連想配列になる 'list' => [1, 2, 3], // 連番配列になる 'ArrayObject' => new \ArrayObject([1, 2, 3]), // new されてインスタンスになる "block" => [ // ブロックはブロック外をキーとした連想配列になる(複数指定でキーは指定できるイメージ) "message" => ["this is message1\n this is message2"], ], 'same' => [ // 複数あるのでそれぞれの配列になる ['this', 'is', 'same', 'value1'], ['this', 'is', 'same', 'value2'], ['this', 'is', 'same', 'value3'], ], ]);
    Parameter
    type name summary
    ​string|​Reflector $annotation アノテーション文字列
    ​array|​mixed $schema = [] スキーマ定義
    ​string|​array $nsfiles = [] ファイル名 or [ファイル名 => 名前空間名]
    Return
    type summary
    ​array アノテーション配列

    [F] console_log js の console に値を吐き出す

    js の console に値を吐き出す
    function console_log(mixed ...$values)

    script タグではなく X-ChromeLogger-Data を使用する。 したがってヘッダ送信前に呼ぶ必要がある。

    Parameter
    type name summary
    ​mixed ...$values 出力する値(可変引数)

    [F] evaluate eval のプロキシ関数

    eval のプロキシ関数
    function evaluate( string $phpcode, array $contextvars = [], int $cachesize = 256 ): mixed

    一度ファイルに吐いてから require した方が opcache が効くので抜群に速い。 また、素の eval は ParseError が起こったときの表示がわかりにくすぎるので少し見やすくしてある。

    関数化してる以上 eval におけるコンテキストの引き継ぎはできない。 ただし、引数で変数配列を渡せるようにしてあるので get_defined_vars を併用すれば基本的には同じ($this はどうしようもない)。

    短いステートメントだと opcode が少ないのでファイルを経由せず直接 eval したほうが速いことに留意。 一応引数で指定できるようにはしてある。

    Example:

    $a = 1; $b = 2; $phpcode = '; $c = $a + $b; return $c * 3; '; that(evaluate($phpcode, get_defined_vars()))->isSame(9);
    Parameter
    type name summary
    ​string $phpcode 実行する php コード
    ​array $contextvars = [] コンテキスト変数配列
    ​int $cachesize = 256 キャッシュするサイズ
    Return
    type summary
    ​mixed eval の return 値

    [F] namespace_parse php ファイルをパースして名前空間配列を返す

    php ファイルをパースして名前空間配列を返す
    function namespace_parse( string $filename, array $options = [] ): array

    ファイル内で use/use const/use function していたり、シンボルを定義していたりする箇所を検出して名前空間単位で返す。 クラスコンテキストでの解決できないシンボルはその名前空間として返す。 つまり、 use せずに いきなり new Hoge() などとしてもその同一名前空間の Hoge として返す。 これは同一名前空間であれば use せずとも使用できる php の仕様に合わせるため。 対象はクラスのみであり、定数・関数は対象外。 use せずに hoge_function() などとしても、それが同一名前空間なのかグローバルにフォールバックされるのかは静的には決して分からないため。

    その他、#[AttributeName]や ClassName::class など、おおよそクラス名が必要とされるコンテキストでのシンボルは全て返される。

    Example:

    // このような php ファイルをパースすると・・・ file_set_contents(sys_get_temp_dir() . '/namespace.php', ' <?php namespace NS1; use ArrayObject as AO; use function strlen as SL; function InnerFunc(){} class InnerClass{} define("OUTER\\\\CONST", "OuterConst"); namespace NS2; use RuntimeException as RE; use const COUNT_RECURSIVE as CR; class InnerClass{} const InnerConst = 123; // いきなり Hoge を new してみる new Hoge(); '); // このような名前空間配列が得られる that(namespace_parse(sys_get_temp_dir() . '/namespace.php'))->isSame([ 'NS1' => [ 'const' => [], 'function' => [ 'SL' => 'strlen', 'InnerFunc' => 'NS1\\InnerFunc', ], 'alias' => [ 'AO' => 'ArrayObject', 'InnerClass' => 'NS1\\InnerClass', ], ], 'OUTER' => [ 'const' => [ 'CONST' => 'OUTER\\CONST', ], 'function' => [], 'alias' => [], ], 'NS2' => [ 'const' => [ 'CR' => 'COUNT_RECURSIVE', 'InnerConst' => 'NS2\\InnerConst', ], 'function' => [], 'alias' => [ 'RE' => 'RuntimeException', 'InnerClass' => 'NS2\\InnerClass', 'Hoge' => 'NS2\\Hoge', // 同一名前空間として返される ], ], ]);
    Parameter
    type name summary
    ​string $filename ファイル名
    ​array $options = [] オプション配列
    Return
    type summary
    ​array 名前空間配列

    [F] namespace_resolve エイリアス名を完全修飾名に解決する

    エイリアス名を完全修飾名に解決する
    function namespace_resolve( string $shortname, string|array $nsfiles, array $targets = ["const", "function", "alias"] ): string|null

    例えばあるファイルのある名前空間で use Hoge\Fuga\Piyo; してるときの PiyoHoge\Fuga\Piyo に解決する。

    Example:

    // このような php ファイルがあるとして・・・ file_set_contents(sys_get_temp_dir() . '/symbol.php', ' <?php namespace vendor\NS; use ArrayObject as AO; use function strlen as SL; function InnerFunc(){} class InnerClass{} '); // 下記のように解決される that(namespace_resolve('AO', sys_get_temp_dir() . '/symbol.php'))->isSame('ArrayObject'); that(namespace_resolve('SL', sys_get_temp_dir() . '/symbol.php'))->isSame('strlen'); that(namespace_resolve('InnerFunc', sys_get_temp_dir() . '/symbol.php'))->isSame('vendor\\NS\\InnerFunc'); that(namespace_resolve('InnerClass', sys_get_temp_dir() . '/symbol.php'))->isSame('vendor\\NS\\InnerClass');
    Parameter
    type name summary
    ​string $shortname エイリアス名
    ​string|​array $nsfiles ファイル名 or [ファイル名 => 名前空間名]
    ​array $targets = ["const", "function", "alias"] エイリアスタイプ('const', 'function', 'alias' のいずれか)
    Return
    type summary
    ​string|​null 完全修飾名。解決できなかった場合は null

    [F] php_highlight php のコードをハイライトする

    php のコードをハイライトする
    function php_highlight( string $phpcode, array|int $options = [] ): string

    SAPI に応じて自動でハイライトする(html タグだったり ASCII color だったり)。 highlight_string の CLI 対応版とも言える。

    Parameter
    type name summary
    ​string $phpcode ハイライトする php コード
    ​array|​int $options = [] オプション
    Return
    type summary
    ​string ハイライトされた php コード

    [F] php_indent php のコードのインデントを調整する

    php のコードのインデントを調整する
    function php_indent( string $phpcode, array|int|string $options = [] ): string

    baseline で基準インデント位置を指定する。 その基準インデントを削除した後、指定したインデントレベルでインデントするようなイメージ。

    Example:

    $phpcode = ' echo 123; if (true) { echo 456; } '; // 数値指定は空白換算 that(php_indent($phpcode, 8))->isSame(' echo 123; if (true) { echo 456; } '); // 文字列を指定すればそれが使用される that(php_indent($phpcode, " "))->isSame(' echo 123; if (true) { echo 456; } '); // オプション指定 that(php_indent($phpcode, [ 'baseline' => 1, // 基準インデントの行番号(負数で下からの指定になる) 'indent' => 4, // インデント指定(上記の数値・文字列指定はこれの糖衣構文) 'trimempty' => true, // 空行を trim するか 'heredoc' => true, // Flexible Heredoc もインデントするか ]))->isSame(' echo 123; if (true) { echo 456; } ');
    Parameter
    type name summary
    ​string $phpcode インデントする php コード
    ​array|​int|​string $options = [] オプション
    Return
    type summary
    ​string インデントされた php コード

    [F] php_opcode コード断片の opcode を返す

    コード断片の opcode を返す
    function php_opcode( string $phpcode, int $level = 131072 ): string

    php レベルでサクッと取りたいことはあるし、ini 設定がややややこしく、簡単に取れない場合があるので関数化した。 phpdbg 等ではなく opcache で出すため、与えたコードは実行されることに注意。

    Parameter
    type name summary
    ​string $phpcode php コード
    ​int $level = 131072 opt_debug_level に渡される(URL 参照だが、正味使うのは 0x10000:最適化前, 0x20000:最適化後 くらいだろう)
    Return
    type summary
    ​string opcode

    [F] php_parse php のコード断片をパースする

    php のコード断片をパースする
    function php_parse( string $phpcode, array|int $option = [] ): \PhpToken[]
    Parameter
    type name summary
    ​string $phpcode パースする php コード
    ​array|​int $option = [] パースオプション
    Return
    type summary
    PhpToken[] トークン配列

    [F] php_strip 文字列から php コードを取り除く

    文字列から php コードを取り除く
    function php_strip( string $phtml, array $option = [], array &$mapping = [] ): string

    正確にはオプションの replacer で指定したものに置換される(デフォルト空文字なので削除になる)。 replacer にクロージャを渡すと(phpコード, 出現番号) が渡ってくるので、それに応じて値を返せばそれに置換される。 文字列を指定すると自動で出現番号が付与される。

    $mapping 配列には「どれをどのように」と言った変換表が格納される。 典型的には strtr に渡して php コードを復元させるのに使用する。

    Example:

    $phtml = 'begin php code <?php echo 123 ?> end'; // php コードが消えている that(php_strip($phtml))->is('begin php code end'); // $mapping を使用すると元の文字列に復元できる $html = php_strip($phtml, [], $mapping); that(strtr($html, $mapping))->is($phtml);
    Parameter
    type name summary
    ​string $phtml php コードを含む文字列
    ​array $option = [] オプション配列
    ​array &$mapping = [] 変換表が格納される参照変数
    Return
    type summary
    ​string php コードが除かれた文字列

    [F] php_tokens PhpToken に便利メソッドを生やした配列を返す

    PhpToken に便利メソッドを生やした配列を返す
    function php_tokens( string $code, int $flags = 0 ): PhpTokens[]

    php_parse とは似て非なる(あっちは何がしたいのかよく分からなくなっている)。 この関数はシンプルに PhpToken の拡張版として動作する。

    生えているメソッドは下記。

    __debugInfo
    デバッグしやすい情報で吐き出す
    clone
    新プロパティを指定して clone する
    name
    getTokenName のエイリアス
    prev
    条件一致した直前のトークンを返す
    • 引数未指定時は isIgnorable でないもの
    next
    条件一致した直後のトークンを返す
    • 引数未指定時は isIgnorable でないもの
    find
    ブロック内部を読み飛ばしつつ指定トークンを探す
    end
    自身の対応するペアトークンまで飛ばして返す
    • 要するに { や (, " などの中途半端ではない終わりのトークンを返す
    contents
    自身と end 間のトークンを文字列化する
    resolve
    text が名前空間を解決して完全修飾になったトークンを返す

    Example:

    $phpcode = '<?php // dummy namespace Hogera; class Example { // something }'; $tokens = php_tokens($phpcode); // name でトークン名が得られる that($tokens[0])->name()->is('T_OPEN_TAG'); // ↑の次はコメントだが next で namespace が得られる that($tokens[0])->next()->text->is('namespace'); // 同じく↑の次はホワイトスペースだが next で Hogera が得られる that($tokens[0])->next()->next()->text->is('Hogera');
    Parameter
    type name summary
    ​string $code
    ​int $flags = 0 パースオプション
    Return
    type summary
    PhpTokens[] トークン配列

    [F] unique_id 一意な文字列を返す

    一意な文字列を返す
    function unique_id( array &$id_info = [], array $debug = [] ): string

    最大でも8バイト(64ビット)に収まるようにしてある。

    41bit
    1ミリ秒単位
    7bit
    シーケンス
    16bit
    IPv4ホストアドレス

    いわゆる snowflake 系で sonyflake が近い。

    シーケンスは「単位時間(1ミリ秒)あたりに発行できる数」を表す。 7bit で少し少ないが、生成にファイルロックを使用しているため、1ミリ秒で 128 回生成されることがそもそもレアケース。 7bit を超えると強制的に1ミリ秒待って timestamp を変えることで重複を防ぐ(≒その時は発行に1ミリ秒かかる)。

    複数の IPv4 アドレスを持つ場合はサブネットが最も長いもの(ホストアドレスが最も短いもの)が使用される。 IPv6 は未対応で、サブネット /16 未満も未対応。

    引数はデバッグ用でいくつか用意しているが、実運用としては引数ゼロとして扱うこと。

    以下、思考過程と備考。

    • 単調増加の値としてタイムスタンプを採用
      • 如何にホスト・シーケンスが同じになろうと発行時刻が別なら別IDにするためのプレフィックスとして使用する
      • 時間が巻き戻らない限りは大丈夫
      • 2093年までしか発行できない((2 ** 41) * (10 ** -3) / 60 / 60 / 24 / 365 = 69.73 年)
    • サーバー間で重複を許したくない場合の候補は下記で、現在は IPv4 を採用している
      • ホスト名
        • 文字列で大きさが読めないし一意である保証もない
      • MAC アドレス
        • 一意だが少し大きすぎる
        • 下3桁がシリアルIDらしいので同メーカーなら重複しないかもしれない
        • ただそもそも php で MAC アドレスを簡単に得る手段がない
      • IPv4
        • サーバー間で重複を許さないような id の生成は大抵クラスタを組んでいて IP が振られているはず
        • そして 4byte でユニークな IP が振られることはまずない(大抵は 2,3byte で固有のはず。/12未満のクラスタ構成なんて見たことない)
      • IPv6
        • 桁が大きすぎる
        • まだまだ IPv4 も現役なので積極的に採用する理由に乏しい
      • 引数で machine id を渡す
        • 大抵のシステムでホスト数は1,2桁だろうので小bitで済む
        • でも引数無しにしたかった
    • プロセス間の重複(シーケンス)はファイルロックを使用して採番している
      • プロセスIDを採用してたが思ったより大きすぎた(現代的な linux ではデフォルト 2**22 らしい)
      • 逐次ロックなので大量生成には全く向かない
    Parameter
    type name summary
    ​array &$id_info = [] 元になった生成データのレシーバ引数
    ​array $debug = [] デバッグ用引数(配列で内部の動的な値を指定できる)
    Return
    type summary
    ​string 一意なバイナリ文字列

    [N] ryunosuke\Functions\Package\network\

    [F] functions

    [F] cidr2ip cidr 内の IP アドレスを返す

    cidr 内の IP アドレスを返す
    function cidr2ip(string $cidr): array

    すべての IP アドレスを返すため、/1 のような極端な値を投げてはならない。 (Generator の方がいいかもしれない)。

    ipv6 は今のところ未対応。

    Example:

    that(cidr2ip('192.168.0.0/30'))->isSame(['192.168.0.0', '192.168.0.1', '192.168.0.2', '192.168.0.3']); that(cidr2ip('192.168.0.255/30'))->isSame(['192.168.0.252', '192.168.0.253', '192.168.0.254', '192.168.0.255']);
    Parameter
    type name summary
    ​string $cidr cidr
    Return
    type summary
    ​array IP アドレス

    [F] cidr_parse cidr を分割する

    cidr を分割する
    function cidr_parse(string $cidr): array

    ※ 内部向け

    Parameter
    type name summary
    ​string $cidr
    Return
    type summary
    ​array [$address, $networkBit, $localBit]

    [F] dns_resolve TTL 対応の DNS リゾルバ

    TTL 対応の DNS リゾルバ
    function dns_resolve( string $hostname, int $type = ryunosuke\Functions\Package\DNS_A, string $returnAs = "value", int $ttl0 = 0, int $nxdomainTtl = 60, bool $flush = false, array $hosts = [] )

    server を指定して自ら UDP で問い合わせたりするような機能はない(将来的にはやってもいいけど)。 リゾルバとしては OS に設定されているネームサーバが使われる(php で言えば dns_get_record)。

    hosts ファイルに記載されているレコードは ttl が 0 になるが、 $ttl0 で明示指定できる。 $nxdomainTtl もほぼ hosts 用の引数(SOA がないドメインなんてほぼ存在しない)。

    結果はローカルファイルシステムに保存されるので TTL が切れるまでは別プロセスでも同じ結果を返す。 ネガティブキャッシュも実装されており TTL は SOA の minimum-ttl に従う。

    $returnAs で返り値の形式を指定できる。

    'raw'
    ほぼ生のまま返す(「ほぼ」というのは type ごとにカテゴライズされるため)
    'values'
    主たる値を配列で返す(「主たる」とは A だったら IP, MX だったら優先度ソート済みの target)
    'value'
    主たる値をスカラーで返す(「主たる」とは A だったらランダムの IP, MX だったら高優先度 target)

    いずれにせよ DNS_XXX に複数の値を含めると複数の値を返し得るので注意。 (根本的に raw+ALL 以外の複数値指定は推奨しない)。

    普通は 'value' で十分で、MX で別レコードにリトライしたい場合くらいにしか 'values' は使用しない。 'raw' に至ってはほぼデバック・確認用で通常用途での使用はほぼないはず。

    Example:

    // example.com A の主たる値をスカラーで返す that(dns_resolve('example.com', DNS_A, 'value'))->isString(); // '96.7.128.175' 等(毎回異なる) // example.com A の主たる値を配列で返す that(dns_resolve('example.com', DNS_A, 'values'))->isArray(); // ['23.192.228.80'] 等(順番は毎回異なる) // example.com 全レコードをカテゴライズして返す that(dns_resolve('example.com', DNS_ALL, 'raw'))->hasKeyAll(['A', 'AAAA', 'SOA']); // var_dump してみれば一発で分かる
    Parameter
    type name summary
    ​string $hostname 取得するドメイン名
    ​int $type = ryunosuke\Functions\Package\DNS_A 取得するレコードタイプ
    ​string $returnAs = "value" 返り値のタイプ
    ​int $ttl0 = 0 TTL が 0(hosts 等)の場合の代替値
    ​int $nxdomainTtl = 60 SOA がない場合(hosts 等)のネガティブキャッシュの TTL
    ​bool $flush = false フラッシュフラグ
    ​array $hosts = [] 注入用 hosts ファイルだが実質的にテスト用

    [F] fcgi_request FastCGI リクエストを行う

    FastCGI リクエストを行う
    function fcgi_request( string $url, array $params = [], array|string $stdin = "", array $options = [] ): array

    ※ 完全に特定用途向けで普通の使い方は想定していない

    できるだけ http に似せたかったので $url からある程度 $params を推測して自動設定する。

    TCP
    tcp://localhost:9000/path/to/script?a=A
    UDS
    unix://run%2Fphp-fpm%2Fwww.sock/path/to/script?a=A
    • とても気持ち悪いので UDS ファイルは $options で渡すこともできる
    • unix:///path/to/script?a=A ($options:['udsFile' => '/run/php-fpm/www.sock']) 上記で SCRIPT_FILENAME, QUERY_STRING が設定される。 $stdin を指定すると REQUEST_METHOD, CONTENT_LENGTH 等も設定される。 $stdin は配列を渡すとよしなに扱われる。

    $params の自動設定は明示指定を決して上書きしない。 ただし null だけは上書きするので自動設定の明示に使える。

    「任意のホスト(http ではないのでドメイン(≒Host ヘッダ))」に「ドキュメントルートと無関係」に「fpm のコンテキストで実行」できることがほぼ唯一のメリット。 要するに cli から fpm の opcache を温めたいような限定的なケースでしか使わないし使うべきでない。

    Parameter
    type name summary
    ​string $url URL
    ​array $params = [] FCGI パラメータ
    ​array|​string $stdin = "" FCGI ボディ
    ​array $options = [] その他のオプション
    Return
    type summary
    ​array FCGI レスポンス

    [F] getipaddress 接続元となる IP を返す

    接続元となる IP を返す
    function getipaddress(string|int|null $target = null): ?string

    IP を指定してそこへ接続する際の SourceIP を返す(省略すると最初のエントリを返す)。 複数のネットワークにマッチした場合の結果は不定(最長が無難だろうがそもそも SourceIP がどうなるかが不定)。

    Example:

    // 何らかの IP アドレスが返ってくる that(getipaddress())->isValidIpv4(); // 自分への接続元は自分なので 127.0.0.1 を返す that(getipaddress('127.0.0.9'))->isSame('127.0.0.1');
    Parameter
    type name summary
    ​string|​int|​null $target = null 接続先
    Return
    type summary
    ​?string IP アドレス

    [F] http_bechmark http のベンチマークを取る

    http のベンチマークを取る
    function http_bechmark( array|string $urls, int $requests = 10, int $concurrency = 3, $output = null ): array

    結果の各意味合いは下記の通り。

    status
    http status code の統計
    • 200 以外が混ざっている場合は何かが間違ってるので結果を疑うべき
    wait
    接続確立後から最初の応答までの時間(いわゆる TTFB)
    • ストリーミングなどしていなければ(かつ相手先が php であれば)これが実質的な応答速度と言える(バッファリングされるため transfer はただの消化試合になる)
    • 全体として遅いならスコアは低いと言ってよい
    • min/max の差が激しいならおそらく捌き切れていない(backlog に溜まるなど)
    transfer
    最初の応答から全レスポンス完了までの時間
    • いわゆる帯域…のような単純な値ではない
    • 例えばストリーミングをしている場合はその生成速度と言える
    total
    TCP/TLS 等のメタい時間を除いた合計時間
    • レスポンスサイズに引きずられるため参考程度でよい
    • 敢えて言うなら min/max の差が激しいようなら何かを疑うべき

    例えば下記のような php のベンチを取ると概ね wait:1, transfer:2, total:3 になる。

    ob_end_clean(); sleep(1); echo "wait"; flush(); sleep(2); echo 'done';

    外れ値のフィルタなどは行わない。 例えば backlog に溜まって応答が10倍になったとしてもそれは外れ値ではないだろう。 と考えるとそもそも「外れ値」の定義自体が不可能であり、余計なことは一切しない。

    Parameter
    type name summary
    ​array|​string $urls URLs
    ​int $requests = 10 合計リクエスト
    ​int $concurrency = 3 同時接続数
    $output = null
    Return
    type summary
    ​array 結果配列

    [F] http_delete http_request の DELETE 特化版

    http_request の DELETE 特化版
    function http_delete( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_get http_request の GET 特化版

    http_request の GET 特化版
    function http_get( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_head http_request の HEAD 特化版

    http_request の HEAD 特化版
    function http_head( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): array
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​array レスポンスヘッダ

    [F] http_patch http_request の PATCH 特化版

    http_request の PATCH 特化版
    function http_patch( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_post http_request の POST 特化版

    http_request の POST 特化版
    function http_post( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_put http_request の PUT 特化版

    http_request の PUT 特化版
    function http_put( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
    Parameter
    type name summary
    ​string $url 対象 URL
    ​mixed $data = [] パラメータ
    $options = []
    &$response_header = []
    &$info = []
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_request curl のラッパー関数

    curl のラッパー関数
    function http_request( array $options = [], array &$response_header = [], array &$info = [] ): mixed

    curl は高機能だけど、低レベルで設定が細かすぎる上に似たようなものが大量にあるので素で書くのが割とつらい。 のでデフォルトをスタンダードな設定に寄せつつ、多少便利になるようにラップしている。 まぁ現在では guzzle みたいなクライアントも整ってるし、使い捨てスクリプトでサクッとリクエストを投げたい時用。

    生 curl との差異は下記。

    • CURLOPT_HTTPHEADER は連想配列指定が可能
    • CURLOPT_POSTFIELDS は連想配列・多次元配列指定が可能
    • 単一ファイル指定は単一アップロードになる

    さらに独自のオプションとして下記がある。

    raw (bool)
    curl インスタンスと変換クロージャを返すだけになる
    • ただし、ほぼデバッグや内部用なので指定することはほぼ無いはず
    async (bool)
    リクエストを投げて、結果を返すオブジェクトを返す
    • いわゆる非同期で、すぐさま処理を返し、結果は返り値オブジェクト経由で取得する
    nobody (bool)
    ヘッダーの受信が完了したらただちに処理を返す
    • ボディは空文字になる(CURLOPT_NOBODY とは全く性質が異なるので注意)
    throw (bool)
    ステータスコードが 400 以上のときに例外を投げる
    • CURLOPT_FAILONERROR は原則使わないほうがいいと思う
    retry (float[]|callable)
    エラーが出た場合にリトライする
    • 配列で指定した回数・秒数のリトライを行う([1, 2, 3] で、1秒後、2秒後、3秒後になる)
    • callable を指定するとその callable が false を返すまでリトライする(引数として curl_info が渡ってきて待機秒数を返す)
    atfile (bool)
    キーに @ があるフィールドをファイルアップロードとみなす
    • 悪しき CURLOPT_SAFE_UPLOAD の代替。ただし値ではなくキーで判別する
    • 値が配列のフィールドのキーに @ をつけると連番要素のみアップロードになる
    cachedir (string)
    GET のときにクライアントキャッシュや 304 キャッシュが効くようになる
    • Cache-Control の private, public は見ないので一応注意
    parser (array)
    Content-Type に基づいて body をパースする
    • 今のところ application/json のみ

    また、頻出するオプションは下記の定数のエイリアスがあり、単純に読み替えられる。

    url
    CURLOPT_URL
    method
    CURLOPT_CUSTOMREQUEST
    cookie
    CURLOPT_COOKIE
    header
    CURLOPT_HTTPHEADER
    body
    CURLOPT_POSTFIELDS
    cookie_file
    CURLOPT_COOKIEJAR, CURLOPT_COOKIEFILE

    Example:

    $response = http_request([ 'url' => TESTWEBSERVER . '/post?name=value', 'method' => 'POST', 'body' => ['k1' => 'v1', 'k2' => 'v2'], ]); that($response['args'])->is([ 'name' => 'value', ]); that($response['form'])->is([ 'k1' => 'v1', 'k2' => 'v2', ]);
    Parameter
    type name summary
    ​array $options = [] curl_setopt_array に渡される
    ​array &$response_header = [] レスポンスヘッダが連想配列で格納される
    ​array &$info = [] curl_getinfo が格納される
    Return
    type summary
    ​mixed レスポンスボディ

    [F] http_requests http リクエストを並列で投げる

    http リクエストを並列で投げる
    function http_requests( array $urls, array $single_options = [], array $multi_options = [], array &$infos = [] ): array

    $urls で複数の curl を渡し、並列で実行して複数の結果をまとめて返す。 $urls の要素は単一の文字列か curl のオプションである必要がある。 リクエストの実体は http_request なので、そっちで使えるオプションは一部を除きすべて使える。

    返り値は $urls のキーを保持したまま、レスポンスが返ってきた順にボディを格納して配列で返す。 構造は下記のサンプルを参照。

    ただし、オプションに callback がある場合、各リクエスト完了直後にそれがコールされ、返り値はそのコールバックの返り値になる。

    Example:

    $responses = http_requests([ // このように [キー => CURL オプション] 形式が正しい使い方 'fuga' => [ CURLOPT_URL => 'http://unknown-host', CURLOPT_TIMEOUT => 5, ], // ただし、このように [キー => URL] 形式でもいい(オプションはデフォルトが使用される) 'hoge' => 'http://127.0.0.1', // さらに、このような [URL => CURL オプション] 形式も許容される(あまり用途はないだろうが) 'http://127.0.0.1' => [ CURLOPT_TIMEOUT => 5, 'callback' => fn($key, $body) => $body, ], ], [ // 第2引数で各リクエストの共通オプションを指定できる(個別指定優先) // @see https://www.php.net/manual/ja/function.curl-setopt.php ], [ // 第3引数でマルチリクエストのオプションを指定できる // @see https://www.php.net/manual/ja/function.curl-multi-setopt.php ], // 第4引数を与えるとボディ以外の各種情報が格納される $infos ); # 返り値 [ // キーが維持されるので hoge キー 'hoge' => 'response body', // curl のエラーが出た場合は null になる(詳細なエラー情報は $infos に格納される) 'fuga' => null, // callback を設定しているので strlen($body) が返り値になる 'http://127.0.0.1' => 12345, ]; # 第4引数(要するにキーを維持しつつ [header, curlinfo] が格納される) [ 'hoge' => [['response header'], ['curl_info']], 'fuga' => [['response header'], ['curl_info']], 'http://127.0.0.1' => [['response header'], ['curl_info']], ];
    Parameter
    type name summary
    ​array $urls 実行する curl オプション
    ​array $single_options = [] 全 $urls に適用されるデフォルトオプション
    ​array $multi_options = [] 並列リクエストとしてのオプション
    ​array &$infos = [] curl 情報やヘッダなどが格納される受け変数
    Return
    type summary
    ​array レスポンスボディ配列。取得した順番でキーを保持しつつ追加される

    [F] incidr ipv4 の cidr チェック

    ipv4 の cidr チェック
    function incidr( string $ipaddr, string|array $cidr ): bool

    $ipaddr が $cidr のレンジ内なら true を返す。 $cidr は複数与えることができ、どれかに合致したら true を返す。

    ipv6 は今のところ未対応。

    Example:

    // 範囲内なので true that(incidr('192.168.1.1', '192.168.1.0/24'))->isTrue(); // 範囲外なので false that(incidr('192.168.1.1', '192.168.2.0/24'))->isFalse(); // 1つでも範囲内なら true that(incidr('192.168.1.1', ['192.168.1.0/24', '192.168.2.0/24']))->isTrue(); // 全部範囲外なら false that(incidr('192.168.1.1', ['192.168.2.0/24', '192.168.3.0/24']))->isFalse();
    Parameter
    type name summary
    ​string $ipaddr 調べられる IP/cidr アドレス
    ​string|​array $cidr 調べる cidr アドレス
    Return
    type summary
    ​bool $ipaddr が $cidr 内なら true

    [F] ip2cidr IP アドレスを含みうる cidr を返す

    IP アドレスを含みうる cidr を返す
    function ip2cidr( string $fromipaddr, string $toipaddr ): array

    from, to の大小関係には言及しないので、from > to を与えると空配列を返す。

    ipv6 は今のところ未対応。

    Example:

    that(ip2cidr('192.168.1.1', '192.168.2.64'))->isSame([ '192.168.1.1/32', '192.168.1.2/31', '192.168.1.4/30', '192.168.1.8/29', '192.168.1.16/28', '192.168.1.32/27', '192.168.1.64/26', '192.168.1.128/25', '192.168.2.0/26', '192.168.2.64/32', ]);
    Parameter
    type name summary
    ​string $fromipaddr ipaddrs
    ​string $toipaddr ipaddrs
    Return
    type summary
    ​array cidr

    [F] ip_info ipv4 の情報を返す

    ipv4 の情報を返す
    function ip_info( string $ipaddr, array $options = [] ): null|array|iterable

    登録機関とか登録日、国等を返すが、実際のところ cc(国)くらいしか使わないはず。 データソースは各 RIR の delegated-afrinic-latest だが、かなりでかいのでデフォルトでは24時間のキャッシュが効く。 キャッシュ切れ/効かせないと最悪30秒くらいかかるので注意(バッチで叩くといいと思う)。

    $ipaddr に null を渡すと全 ip 情報を返す。 上記の通り、情報としてかなりでかいので php で処理するのではなく、全取得して RDBMS に登録したり htaccess に書き込んだりするのに使える。

    ipv6 は今のところ未対応。

    Example:

    // apnic 管轄 that(ip_info(gethostbyname('www.nic.ad.jp'), ['timeout' => 300, 'throw' => false]))->is([ 'cidr' => '192.41.192.0/24', 'ipaddress' => '192.41.192.0', 'netmask' => 24, 'registry' => 'apnic', 'cc' => 'JP', 'date' => '19880620', ]); // arin 管轄 that(ip_info(gethostbyname('www.internic.net'), ['timeout' => 300, 'throw' => false]))->is([ 'cidr' => '192.0.32.0/20', 'ipaddress' => '192.0.32.0', 'netmask' => 20, 'registry' => 'arin', 'cc' => 'US', 'date' => '20090629', ]); // こういう特殊なアドレスも一応対応している(全てではない) that(ip_info('127.0.0.1'))['registry']->is('RFC1122'); that(ip_info('192.168.0.1'))['registry']->is('RFC1918');
    Parameter
    type name summary
    ​string $ipaddr 調べる IP アドレス
    ​array $options = [] オプション配列
    Return
    type summary
    ​null|​array|​iterable IP の情報。ヒットしない場合は null

    [F] ip_normalize IP アドレスを正規化する

    IP アドレスを正規化する
    function ip_normalize(string $ipaddr)

    IPv4 は(あまり有名ではないが)4 オクテット未満や 16,8 進数表記を標準的な 1.2.3.4 形式に正規化する。 IPv6 は 0 の省略や :: 短縮表記を完全表記の 1111:2222:3333:4444:5555:6666:7777:8888 形式に正規化する。 完全におかしな形式については例外を投げる。

    Example:

    // v4(RFC はないがこのような記法が許されている) that(ip_normalize('0xff.077.500'))->isSame('255.63.1.244'); // v6(RFC 5952 の短縮表記を完全表記にする) that(ip_normalize('a::f'))->isSame('000a:0000:0000:0000:0000:0000:0000:000f');
    Parameter
    type name summary
    ​string $ipaddr

    [F] ping ネットワーク疎通を返す

    ネットワーク疎通を返す
    function ping( string $host, int|null $port = null, int $timeout = 1, string &$errstr = "" ): ?float

    $port を指定すると TCP/UDP、省略(null)すると ICMP で繋ぐ。 が、 ICMP は root ユーザしか実行できないので ping コマンドにフォールバックする。 TCP/UDP の分岐はマニュアル通り tcp://, udp:// のようなスキームで行う(スキームがなければ tcp)。

    udp は結果が不安定なので信頼しないこと(タイムアウトも疎通 OK とみなされる。プロトコルの仕様上どうしようもない)。

    Example:

    // 自身へ ICMP ping を打つ(正常終了なら float を返し、失敗なら false を返す) that(ping('127.0.0.1'))->isFloat(); // 自身の tcp:1234 が開いているか(開いていれば float を返し、開いていなければ null を返す) that(ping('tcp://127.0.0.1', 1234))->isNull(); that(ping('127.0.0.1', 1234))->isNull(); // tcp はスキームを省略できる
    Parameter
    type name summary
    ​string $host ホスト名(プロトコルも指定できる)
    ​int|​null $port = null ポート番号。指定しないと ICMP になる
    ​int $timeout = 1 タイムアウト秒
    ​string &$errstr = "" エラー文字列が格納される
    Return
    type summary
    ​?float 成功したときは疎通時間。失敗したときは null

    [F] snmp_trap SNMPTrap を送信する

    SNMPTrap を送信する
    function snmp_trap( int $version, string $target, string $community, string $enterprise, int $specific, ?int $generic = null, array $variables = [], ?string $agent = null, int $retry = 0, int $timeout = 1 )

    UDP で送ろうかと思ったけど、実装が大変なので snmptrap コマンドに日和っている(ので Windows では動かない)。 将来的には UDP/TCP にするかもしれない。

    インターフェースは v1 に寄せているが、v2 送信も可能。 大抵の場合、generic:6 で固有トラップを送りたい場合に使うので $generic はオプショナルで未指定の場合自動設定される。

    $variables は値の型を見てバインド型を決めるので厳密に渡さなければならない(1 と 1.0 と "1" は全く別の意味になる)。

    Parameter
    type name summary
    ​int $version snmp バージョン
    ​string $target 送信先
    ​string $community コミュニティ
    ​string $enterprise エンタープライズ OID
    ​int $specific 固有トラップ番号
    ​?int $generic = null 標準トラップ番号
    ​array $variables = [] バインド変数
    ​?string $agent = null 送信元アドレス(v1のみ)
    ​int $retry = 0 リトライ回数
    ​int $timeout = 1 タイムアウト秒

    [N] ryunosuke\Functions\Package\opcache\

    [F] functions

    [F] opcache_gc opcache を減らす

    opcache を減らす
    function opcache_gc( int $thresholdLifetime = 86400, int $thresholdHits = 0, bool $deletedFile = true, bool $modifiedFile = true, ?\Closure $includeCondition = null, ?\Closure $excludeCondition = null ): array

    生存期間が $thresholdLifetime 以上で hits が $thresholdHits 以下の opcache を消す。 cli で呼んでも意味がないので health check script あたりで呼ぶといいかもしれない。

    $deletedFile は存在しない場合に消す。 存在しないファイルは validate_timestamp で次読み込み時に自動で無効になるが、それが無効だったり今すぐ消したいときに使う。 ので実質的に false 指定することはない(実ファイル無しで opcache のみで運用しているような特殊なケースでしか意味はない)。

    $modifiedFile は更新された場合に消す。 消えたか変更されたかの違いで挙動自体は $deletedFile と同じ(次回無効になるのではなく今すぐ無効化したい場合に使う)。

    $includeCondition を指定すると必ず維持される。例えば vendor を維持するなど。 $excludeCondition を指定すると必ず除去される。例えば php cache を除去するなど。

    なお、opcache_invalidate しても無効化されるだけで opcache_get_status のエントリには残る(次回アクセス時に再コンパイルされる)。 つまり一向に opcache_get_status から消えないのは正常動作。 (メモリ使用量にはちゃんと換算されているが、スクリプト数には換算されない。おそらくネガティブキャッシュみたいなものなんだろう)。 この時 opcache.max_accelerated_files を超えていても残り続けるので以後新しいファイルもコンパイルされない。 基本的に「opcache_reload の前段でコールして無用なキャッシュを保存されないようにする」くらいの用途しかない。

    この関数は互換性を考慮しない。

    Parameter
    type name summary
    ​int $thresholdLifetime = 86400 生存期間が指定以上を対象にする
    ​int $thresholdHits = 0 ヒット数が指定以下を対象にする
    ​bool $deletedFile = true 元ファイルが存在しないものを対象にする
    ​bool $modifiedFile = true 元ファイルが変更されたものを対象にする
    ​?Closure $includeCondition = null 強制的に維持する条件クロージャ
    ​?Closure $excludeCondition = null 強制的に除去する条件クロージャ
    Return
    type summary
    ​array gc したファイル配列

    [F] opcache_info phpinfo の opcache 特化版

    phpinfo の opcache 特化版
    function opcache_info()

    この関数は互換性を考慮しない。

    [F] opcache_reload opcache の保存兼ウォームアップ

    opcache の保存兼ウォームアップ
    function opcache_reload( array $includePatterns = [], array $excludePatterns = [], bool $reset = false, ?bool $ignoreErrors = null, ?string $cachefile = null ): array

    コールすると現在キャッシュされている opcache のリストを保存しつつ、必要であれば再コンパイルする。 動的な preload として使用することを想定しているので定期的に呼ぶ必要がある。 cli で呼んでも意味がないので health check script あたりで呼ぶといいかもしれない。

    health check script で呼ぶ時は $reset に注意。 true にすると opcache_reset が呼ばれるので再コンパイルが終わるまで処理が遅くなる可能性がある。 この引数は「ネガティブキャッシュもクリーンにしたい」という状況のためで、logrotate 等で reload してしまえば原則的に指定不要。

    preload は強力だが運用が難しく、「動的に育てつつある程度キャッシュできれば構わない」というゆるふわな運用が難しい。 この関数を呼ぶ script を systemd start 等で叩けばそれだけで簡易ウォームアップとなる。 様々な理由でそのリクエストは失敗するかもしれないが、本運用には何も影響しない。 あるいは preload に設定してもよい。少なくともエラーにはならないようにしてある。

    この関数は互換性を考慮しない。

    Parameter
    type name summary
    ​array $includePatterns = [] 対象パターン
    ​array $excludePatterns = [] 除外パターン
    ​bool $reset = false reset を伴うか
    ​?bool $ignoreErrors = null エラーを無視するか(null なら SAPI に応じて自動)
    ​?string $cachefile = null キャッシュファイル名(原則としてテスト用)
    Return
    type summary
    ​array キャッシュ結果配列

    [N] ryunosuke\Functions\Package\outcontrol\

    [F] functions

    [F] ob_capture ob_start ~ ob_get_clean のブロックでコールバックを実行する

    ob_start ~ ob_get_clean のブロックでコールバックを実行する
    function ob_capture( callable $callback, mixed ...$variadic ): string

    Example:

    // コールバック内のテキストが得られる that(ob_capture(fn() => print(123)))->isSame('123'); // こういう事もできる that(ob_capture(function () { ?> bare string1 bare string2 <?php }))->isSame("bare string1\nbare string2\n");
    Parameter
    type name summary
    ​callable $callback 実行するコールバック
    ​mixed ...$variadic $callback に渡される引数(可変引数)
    Return
    type summary
    ​string オフスリーンバッファの文字列

    [F] ob_include 変数を extract して include する

    変数を extract して include する
    function ob_include( string $include_file, array $array = [] ): string

    Example:

    // このようなテンプレートファイルを用意すると file_put_contents(sys_get_temp_dir() . '/template.php', ' This is plain text. This is <?= $var ?>. This is <?php echo strtoupper($var) ?>. '); // このようにレンダリングできる that(ob_include(sys_get_temp_dir() . '/template.php', ['var' => 'hoge']))->isSame(' This is plain text. This is hoge. This is HOGE. ');
    Parameter
    type name summary
    ​string $include_file include するファイル名
    ​array $array = [] extract される連想変数
    Return
    type summary
    ​string レンダリングされた文字列

    [F] ob_stdout 標準出力をコメント化して埋め込む ob_start

    標準出力をコメント化して埋め込む ob_start
    function ob_stdout(): object|string

    非常にニッチな関数で、想定用途は「README.md の中の php を実行して出力結果を埋め込みたい」のみ。 例えば php のマニュアルみたいな「上記の結果は下記のようになります」は対応が分かりにくいのでコメントで埋め込みたいことがある。 いちいち結果を手で埋め込むのも馬鹿らしいのでこの関数を使えば自動で埋め込まれる。

    言語仕様上の制約で echo/print は不可。 また、 ob_content 系で途中経過の出力は取れない(chunk_size を指定すると強制フラッシュされるらしいので制御できない)。 返り値オブジェクトが toString を実装してるのでそれで得ること(ただし返り値は toString であること以外はいかなる前提も置いてはならない)。

    Return
    type summary
    ​object|​string 制御用(デバッグ用)

    [N] ryunosuke\Functions\Package\pcre\

    [F] functions

    [F] glob2regex glob 記法を正規表現に変換する

    glob 記法を正規表現に変換する
    function glob2regex( string $pattern, int $flags = 0 ): string

    原則的に glob/fnmatch における「パスとしてのマッチ(ディレクトリ区切りの特別扱い)」という性質は失われ、あくまで文字列として扱う。 サポートしている記法は下記( https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AD%E3%83%96 )。

    *
    0文字以上の任意の文字列にマッチ
    ?
    任意の1文字にマッチ
    [...]
    括弧内で列挙されたどれか1文字にマッチ
    [!...]
    括弧内で列挙されていない何かの1文字にマッチ
    [0-9]
    括弧内で指定された範囲内の1文字にマッチ
    [!0-9]
    括弧内で指定されていない範囲内の1文字にマッチ
    {a,b,c}
    「a」、「b」あるいは「c」のいずれかにマッチ(要 GLOB_BRACE)

    GLOB_RECURSIVE を与えた場合、挙動が下記のように変化する。

    *
    "/" を含まない0文字以上の文字列にマッチ
    **
    "/" を含む任意の0文字以上の文字列にマッチ

    いわゆる double star での再帰パターンのためだが、ディレクトリセパレータは "/" 固定とする(glob の "\" エスケープと絡んで非常にややこしいため)。

    Example:

    $files = ['hoge.jpg', 'test1.jpg', 'test12.jpg', 'test123.png', 'testA.jpg', 'testAB.jpg', 'testABC.png', 'test.jpg', 'test.jpeg']; // 先頭一致する jpg that(preg_grep('#' . glob2regex('test*.jpg') . '#', $files))->isSame([ 1 => 'test1.jpg', 2 => 'test12.jpg', 4 => 'testA.jpg', 5 => 'testAB.jpg', 7 => 'test.jpg', ]); // 先頭一致した2文字の jpg that(preg_grep('#' . glob2regex('test??.jpg') . '#', $files))->isSame([ 2 => 'test12.jpg', 5 => 'testAB.jpg', ]); // 先頭一致した数値1桁の jpg that(preg_grep('#' . glob2regex('test[0-9].jpg') . '#', $files))->isSame([ 1 => 'test1.jpg', ]); // 先頭一致した数値1桁でない jpg that(preg_grep('#' . glob2regex('test[!0-9].jpg') . '#', $files))->isSame([ 4 => 'testA.jpg', ]); // jpeg, jpg のどちらにもマッチ(GLOB_BRACE 使用) that(preg_grep('#' . glob2regex('test.jp{e,}g', GLOB_BRACE) . '#', $files))->isSame([ 7 => 'test.jpg', 8 => 'test.jpeg', ]); // 深い階層を含めたすべての png にマッチ(GLOB_RECURSIVE 使用) that(preg_grep('#^' . glob2regex('/**.png', GLOB_RECURSIVE) . '$#', [ '/path/a.png', '/path/to/b.png', '/path/to/dir/c.png', ]))->isSame([ '/path/a.png', '/path/to/b.png', '/path/to/dir/c.png', ]);
    Parameter
    type name summary
    ​string $pattern glob パターン文字列
    ​int $flags = 0 glob フラグ
    Return
    type summary
    ​string 正規表現パターン文字列

    [F] preg_capture キャプチャを主軸においた preg_match

    キャプチャを主軸においた preg_match
    function preg_capture( string $pattern, string $subject, array $default ): array

    $pattern で $subject をマッチングして $default で埋めて返す。$default はフィルタも兼ねる。 空文字マッチは「マッチしていない」とみなすので注意($default が使用される)。

    キャプチャを主軸においているので「マッチしなかった」は検出不可能。 $default がそのまま返ってくる。

    Example:

    $pattern = '#(\d{4})/(\d{1,2})(/(\d{1,2}))?#'; $default = [1 => '2000', 2 => '1', 4 => '1']; // 完全にマッチするのでそれぞれ返ってくる that(preg_capture($pattern, '2014/12/24', $default))->isSame([1 => '2014', 2 => '12', 4 => '24']); // 最後の \d{1,2} はマッチしないのでデフォルト値が使われる that(preg_capture($pattern, '2014/12', $default))->isSame([1 => '2014', 2 => '12', 4 => '1']); // 一切マッチしないので全てデフォルト値が使われる that(preg_capture($pattern, 'hoge', $default))->isSame([1 => '2000', 2 => '1', 4 => '1']);
    Parameter
    type name summary
    ​string $pattern 正規表現
    ​string $subject 対象文字列
    ​array $default デフォルト値
    Return
    type summary
    ​array キャプチャした配列

    [F] preg_matches 複数マッチに対応した preg_match

    複数マッチに対応した preg_match
    function preg_matches( string $pattern, string $subject, int $flags = 0, int $offset = 0 ): array

    要するに preg_match_all とほぼ同義だが、下記の差異がある。

    • 正規表現フラグに "g" フラグが使用できる。 "g" を指定すると preg_match_all 相当の動作になる
    • キャプチャは参照引数ではなく返り値で返す
    • 「パターン全体マッチ」を表す 0 キーは返さない
    • 上記2つの動作により「マッチしなかったら空配列を返す」という動作になる
    • 名前付きキャプチャーに対応する数値キーは伏せられる
    • 伏せられても数値キーは 0 ベースで通し連番となる

    Example:

    $pattern = '#(\d{4})/(?<month>\d{1,2})(?:/(\d{1,2}))?#'; // 1(month)番目は名前付きキャプチャなので 1 キーとしては含まれず month というキーで返す(2 が詰められて 1 になる) that(preg_matches($pattern, '2014/12/24'))->isSame([0 => '2014', 'month' => '12', 1 => '24']); // 一切マッチしなければ空配列が返る that(preg_matches($pattern, 'hoge'))->isSame([]); // g オプションを与えると preg_match_all 相当の動作になる(flags も使える) $pattern = '#(\d{4})/(?<month>\d{1,2})(?:/(\d{1,2}))?#g'; that(preg_matches($pattern, '2013/11/23, 2014/12/24', PREG_SET_ORDER))->isSame([ [0 => '2013', 'month' => '11', 1 => '23'], [0 => '2014', 'month' => '12', 1 => '24'], ]);
    Parameter
    type name summary
    ​string $pattern 正規表現
    ​string $subject 対象文字列
    ​int $flags = 0 PREG 定数
    ​int $offset = 0 開始位置
    Return
    type summary
    ​array キャプチャした配列

    [F] preg_replaces パターン番号を指定して preg_replace する

    パターン番号を指定して preg_replace する
    function preg_replaces( string $pattern, array|string|callable $replacements, string $subject, int $limit = -1, null &$count = null ): string

    パターン番号を指定してそれのみを置換する。 名前付きキャプチャを使用している場合はキーに文字列も使える。 値にクロージャを渡した場合はコールバックされて置換される。

    $replacements に単一文字列を渡した場合、 [1 => $replacements] と等しくなる(第1キャプチャを置換)。

    Example:

    // a と z に囲まれた数字を XXX に置換する that(preg_replaces('#a(\d+)z#', [1 => 'XXX'], 'a123z'))->isSame('aXXXz'); // 名前付きキャプチャも指定できる that(preg_replaces('#a(?<digit>\d+)z#', ['digit' => 'XXX'], 'a123z'))->isSame('aXXXz'); // クロージャを渡すと元文字列を引数としてコールバックされる that(preg_replaces('#a(?<digit>\d+)z#', ['digit' => fn($src) => $src * 2], 'a123z'))->isSame('a246z'); // 複合的なサンプル(a タグの href と target 属性を書き換える) that(preg_replaces('#<a\s+href="(?<href>.*)"\s+target="(?<target>.*)">#', [ 'href' => fn($href) => strtoupper($href), 'target' => fn($target) => strtoupper($target), ], '<a href="hoge" target="fuga">inner text</a>'))->isSame('<a href="HOGE" target="FUGA">inner text</a>');
    Parameter
    type name summary
    ​string $pattern 正規表現
    ​array|​string|​callable $replacements 置換文字列
    ​string $subject 対象文字列
    ​int $limit = -1 置換回数
    ​null &$count = null 置換回数格納変数
    Return
    type summary
    ​string 置換された文字列

    [F] preg_splice キャプチャも行える preg_replace

    キャプチャも行える preg_replace
    function preg_splice( string $pattern, string|callable $replacement, string $subject, array &$matches = [], int $limit = -1 ): string

    「置換を行いつつ、キャプチャ文字列が欲しい」状況はまれによくあるはず。

    $replacement に callable を渡すと preg_replace_callback がコールされる。 callable とはいっても単純文字列 callble ("strtoupper" など)は callable とはみなされない。 配列形式の callable や クロージャのみ preg_replace_callback になる。

    Example:

    // 数字を除去しつつその除去された数字を得る that(preg_splice('#\\d+#', '', 'abc123', $m))->isSame('abc'); that($m)->isSame(['123']); // callable だと preg_replace_callback が呼ばれる that(preg_splice('#[a-z]+#', fn($m) => strtoupper($m[0]), 'abc123', $m))->isSame('ABC123'); that($m)->isSame(['abc']); // ただし、 文字列 callable は文字列として扱う that(preg_splice('#[a-z]+#', 'strtoupper', 'abc123', $m))->isSame('strtoupper123'); that($m)->isSame(['abc']);
    Parameter
    type name summary
    ​string $pattern 正規表現
    ​string|​callable $replacement 置換文字列
    ​string $subject 対象文字列
    ​array &$matches = [] キャプチャ配列が格納される
    ​int $limit = -1 置換回数
    Return
    type summary
    ​string 置換された文字列

    [N] ryunosuke\Functions\Package\random\

    [F] functions

    [F] probability 一定確率で true を返す

    一定確率で true を返す
    function probability( int $probability, int $divisor = 100 ): bool

    具体的には $probability / $divisor の確率で true を返す。 $divisor のデフォルトは 100 にしてあるので、 $probability だけ与えれば $probability パーセントで true を返すことになる。

    Example:

    // 50% の確率で "hello" を出す if (probability(50)) { echo "hello"; }
    Parameter
    type name summary
    ​int $probability 分子
    ​int $divisor = 100 分母
    Return
    type summary
    ​bool true or false

    [F] probability_array 指定確率で配列のキーを返す

    指定確率で配列のキーを返す
    function probability_array( array $array, ?int $divisor = 100 ): mixed

    確率の合計が 100% を超えている場合は例外を投げる。 100% に満たない場合は残り確率で null を返す。

    分母の 100% は $divisor 引数で指定可能(1000 を指定すればパーミルになる)。 null を与えると確率の合計値が設定される(いわゆる重み付け乱数になる)。

    Example:

    srand(123); // a:10%, b:20%, c:30%, d:40% の確率で返す that(probability_array([ 'a' => 10, 'b' => 20, 'c' => 30, 'd' => 40, ]))->isSame('b'); // a:16.6%, b:32.3%, c:50% の確率で返す(いわゆる重み付け) that(probability_array([ 'a' => 1, 'b' => 2, 'c' => 3, ], null))->isSame('c');
    Parameter
    type name summary
    ​array $array 配列
    ​?int $divisor = 100 分母
    Return
    type summary
    ​mixed $array のどれか1つ

    [F] random_at 引数をランダムで返す

    引数をランダムで返す
    function random_at(mixed ...$args): mixed
    • 候補がない場合はエラーではなく例外を投げる

    Example:

    // 1 ~ 6 のどれかを返す that(random_at(1, 2, 3, 4, 5, 6))->isAny([1, 2, 3, 4, 5, 6]);
    Parameter
    type name summary
    ​mixed ...$args 候補
    Return
    type summary
    ​mixed 引数のうちどれか

    [F] random_float 疑似乱数小数を返す

    疑似乱数小数を返す
    function random_float( float $min, float $max ): float

    疑似的に生成しているので偏りはあることに注意。 https://www.php.net/manual/random-randomizer.getfloat.php https://www.php.net/manual/random-randomizer.nextfloat.php

    Example:

    // [-M_PI~+M_PI] の区間でランダムに返す that(random_float(-M_PI, +M_PI))->isBetween(-M_PI, +M_PI);
    Parameter
    type name summary
    ​float $min 最小値
    ​float $max 最大値
    Return
    type summary
    ​float min~maxの乱数

    [F] random_normal 正規乱数(正規分布に従う乱数)を返す

    正規乱数(正規分布に従う乱数)を返す
    function random_normal( float $average = 0.0, float $std_deviation = 1.0 ): float

    ※ ボックス=ミュラー法

    Example:

    mt_srand(4); // テストがコケるので種固定 // 平均 100, 標準偏差 10 の正規乱数を得る that(random_normal(100, 10))->isSame(101.16879645296162); that(random_normal(100, 10))->isSame(96.49615862542069); that(random_normal(100, 10))->isSame(87.74557282679618); that(random_normal(100, 10))->isSame(117.93697951557125); that(random_normal(100, 10))->isSame(99.1917453115627); that(random_normal(100, 10))->isSame(96.74688207698713);
    Parameter
    type name summary
    ​float $average = 0.0 平均
    ​float $std_deviation = 1.0 標準偏差
    Return
    type summary
    ​float 正規乱数

    [F] random_range 指定範囲内からランダムで返す

    指定範囲内からランダムで返す
    function random_range( int $min, int $max, ?int $count = null ): array

    $count を null にすると個数すらもランダムで返す。 結果は範囲内では重複しない。

    範囲が負数の場合は例外を投げるが、$count の 0 や範囲超過数は許容される(array_rand とは違う)。

    Example:

    mt_srand(5); // テストがコケるので種固定 // [10~20] の区間でランダムに3件返す that(random_range(10, 20, 3))->is([19, 20, 10]); // 0 を渡しても OK(単に空配列を返す) that(random_range(10, 20, 0))->is([]); // 範囲超過数を渡しても OK(最大個数で返す) that(count(random_range(10, 20, 999)))->is(11);
    Parameter
    type name summary
    ​int $min 最小値
    ​int $max 最大値
    ​?int $count = null 返す個数
    Return
    type summary
    ​array min~maxの数値の配列

    [F] random_string 安全な乱数文字列を生成する

    安全な乱数文字列を生成する
    function random_string( int $length = 8, string $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ): string
    Parameter
    type name summary
    ​int $length = 8 生成文字列長
    ​string $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 使用する文字セット
    Return
    type summary
    ​string 乱数文字列

    [F] unique_string 文字列に含まれない文字列を生成する

    文字列に含まれない文字列を生成する
    function unique_string( string $source, string|int $initial = null, string|array $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ): string

    例えば http のマルチパートバウンダリのような、「競合しない文字列」を生成する。 実装は愚直に文字列を調べて存在しなければそれを返すようになっている。 一応初期値や文字セットは指定可能。

    $initial に int を与えると初期値としてその文字数分 $charlist から確保する。 例えば生成後の変更が前提で、ある程度の長さを担保したいときに指定すれば最低でもその長さ以上は保証される。 $initial に string を与えるとそれがそのまま初期値として使用される。 例えば「ほぼ存在しない文字列」が予測できるのであればそれを指定すれば無駄な処理が省ける。

    Example:

    // 単純に呼ぶと生成1,2文字程度の文字列になる that(unique_string('hello, world'))->stringLengthEqualsAny([1, 2]); // 数値を含んでいないので候補文字に数値のみを指定すれば1文字で「存在しない文字列」となる that(unique_string('hello, world', null, range(0, 9)))->stringLengthEquals(1); // int を渡すと最低でもそれ以上は保証される that(unique_string('hello, world', 5))->stringLengthEqualsAny([5, 6]); // string を渡すとそれが初期値となる that(unique_string('hello, world', 'prefix-'))->stringStartsWith('prefix');
    Parameter
    type name summary
    ​string $source 元文字列
    ​string|​int $initial = null 初期文字列あるいは文字数
    ​string|​array $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 使用する文字セット
    Return
    type summary
    ​string 一意な文字列

    [N] ryunosuke\Functions\Package\reflection\

    [F] functions

    [F] callable_code callable のコードブロックを返す

    callable のコードブロックを返す
    function callable_code( callable|\ReflectionFunctionAbstract $callable, bool $return_token = false ): array

    返り値は2値の配列。0番目の要素が定義部、1番目の要素が処理部を表す。

    Example:

    list($meta, $body) = callable_code(function (...$args) {return true;}); that($meta)->isSame('function (...$args)'); that($body)->isSame('{return true;}'); // ReflectionFunctionAbstract を渡しても動作する list($meta, $body) = callable_code(new \ReflectionFunction(function (...$args) {return true;})); that($meta)->isSame('function (...$args)'); that($body)->isSame('{return true;}');
    Parameter
    type name summary
    ​callable|​ReflectionFunctionAbstract $callable コードを取得する callable
    ​bool $return_token = false true にすると生のトークン配列で返す
    Return
    type summary
    ​array ['定義部分', '処理コード']

    [F] function_doccomments $callable の本体・引数・返り値の DocComment を返す

    $callable の本体・引数・返り値の DocComment を返す
    function function_doccomments(\ReflectionFunctionAbstract|callable $callable): array

    下記のような配列を返す(仕様上 Example が書けない)。

    [ "" => "本体の DocComment", 0 => "引数1の DocComment", 1 => "引数2の DocComment", // ...他の引数 -1 => "返り値の DocComment", ];

    それぞれ存在しない場合はキー自体が抜け落ちる(null で入ったりはしない)。 非常に雑に実装しているので、配列や new(8.1以降)の引数に反応することがある。

    本体の DocComment は ReflectionFunctionAbstract::getDocComment と同等である。 引数の DocComment は必ず型宣言の直前(ない場合は引数名の直前)に記述しなければならない。 返り値の DocComment は必ず型宣言の直前(ない場合は{の直前)に記述しなければならない。

    Parameter
    type name summary
    ReflectionFunctionAbstract|​callable $callable 対象 callable
    Return
    type summary
    ​array 本体・引数・返り値の DocComment 配列

    [F] function_export_false2null 存在する関数の内、false 返しを null 返しに再定義したファイルを返す

    存在する関数の内、false 返しを null 返しに再定義したファイルを返す
    function function_export_false2null( string $namespace, bool $false_only = true ): string

    標準関数は歴史的な事情で false を返すものが多い(strpos, strtotime 等)。 ただ大抵の場合は null の方が好ましいことが多い(int|false より ?int の方が分かりやすいし ?? を駆使できる)。 strpos(',', 'hello, world') ?? -1 みたいなことができない)。

    $false_only:false にすると存在する全関数も返す。false 返しでない関数は単純な委譲で余計なことはしない。 これは「strpos は nullable だから F\strpos で、他はグローバル」という使い分けの利便性のためだが、出力数が膨大になるので留意。

    Parameter
    type name summary
    ​string $namespace 吐き出す名前空間
    ​bool $false_only = true false 返しのみか。false を与えると全関数を返す
    Return
    type summary
    ​string 吐き出された関数定義を含むファイル内容

    [F] function_parameter 関数/メソッドの引数定義を取得する

    関数/メソッドの引数定義を取得する
    function function_parameter(\ReflectionFunctionAbstract|callable $eitherReffuncOrCallable): array

    ほぼ内部向けで外から呼ぶことはあまり想定していない。

    Parameter
    type name summary
    ReflectionFunctionAbstract|​callable $eitherReffuncOrCallable 関数/メソッドリフレクション or callable
    Return
    type summary
    ​array [引数名 => 引数宣言] の配列

    [F] parameter_default callable のデフォルト引数を返す

    callable のデフォルト引数を返す
    function parameter_default( callable $callable, iterable|array $arguments = [] ): array

    オプションで指定もできる。 負数を指定した場合「最後の引数から数えた位置」になる。

    内部関数には使用できない(リフレクションが対応していない)。

    Example:

    $f = function ($a, $b = 'b') {}; // デフォルト引数である b を返す that(parameter_default($f))->isSame([1 => 'b']); // 引数で与えるとそれが優先される that(parameter_default($f, ['A', 'B']))->isSame(['A', 'B']);
    Parameter
    type name summary
    ​callable $callable 対象 callable
    ​iterable|​array $arguments = [] デフォルト引数
    Return
    type summary
    ​array デフォルト引数

    [F] parameter_length callable の引数の数を返す

    callable の引数の数を返す
    function parameter_length( callable $callable, bool $require_only = false, bool $thought_variadic = false ): int

    Example:

    // trim の引数は2つ that(parameter_length('trim'))->isSame(2); // trim の必須引数は1つ that(parameter_length('trim', true))->isSame(1);
    Parameter
    type name summary
    ​callable $callable 対象 callable
    ​bool $require_only = false true を渡すと必須パラメータの数を返す
    ​bool $thought_variadic = false 可変引数を考慮するか。 true を渡すと可変引数の場合に無限長を返す
    Return
    type summary
    ​int 引数の数

    [F] parameter_wiring callable の引数の型情報に基づいてワイヤリングした引数配列を返す

    callable の引数の型情報に基づいてワイヤリングした引数配列を返す
    function parameter_wiring( callable $callable, array|\ArrayAccess $dependency ): array

    ワイヤリングは下記のルールに基づいて行われる。

    • 引数の型とキーが完全一致
    • 引数の型とキーが継承・実装関係
      • 複数一致した場合は解決されない
    • 引数名とキーが完全一致
      • 可変引数は追加
    • 引数のデフォルト値
    • 得られた値がクロージャの場合は再帰的に解決
      • $this は $dependency になるが FromCallable 経由の場合は元のまま

    Example:

    $closure = function (\ArrayObject $ao, \Throwable $t, $array, $none, $default1, $default2 = 'default2', ...$misc) { return get_defined_vars(); }; $params = parameter_wiring($closure, [ \ArrayObject::class => $ao = new \ArrayObject([1, 2, 3]), \RuntimeException::class => $t = new \RuntimeException('hoge'), '$array' => fn (\ArrayObject $ao) => (array) $ao, 4 => 'default1', '$misc' => ['x', 'y', 'z'], ]); that($params)->isSame([ 0 => $ao, // 0番目はクラス名が完全一致 1 => $t, // 1番目はインターフェース実装 2 => [1, 2, 3], // 2番目はクロージャをコール // 3番目は解決されない 4 => 'default1', // 4番目は順番指定のデフォルト値 5 => 'default2', // 5番目は引数定義のデフォルト値 6 => 'x', // 可変引数なのでフラットに展開 7 => 'y', 8 => 'z', ]);
    Parameter
    type name summary
    ​callable $callable 対象 callable
    ​array|​ArrayAccess $dependency 引数候補配列
    Return
    type summary
    ​array 引数配列

    [F] reflect_callable callable から ReflectionFunctionAbstract を生成する

    callable から ReflectionFunctionAbstract を生成する
    function reflect_callable(callable $callable): ReflectCallable|\ReflectionFunction|\ReflectionMethod

    実際には ReflectionFunctionAbstract を下記の独自拡張した Reflection クラスを返す(メソッドのオーバーライド等はしていないので完全互換)。

    __invoke
    元となったオブジェクトを $this として invoke する(関数・クロージャは invoke と同義)
    call
    実行 $this を指定して invoke する(クロージャ・メソッドのみ)
    • 上記二つは call/callStatic のメソッドも呼び出せる
    getDeclaration
    宣言部のコードを返す
    getCode
    定義部のコードを返す
    isAnonymous
    無名関数なら true を返す(8.2 の isAnonymous 互換)
    isStatic
    $this バインド可能かを返す(クロージャのみ)
    getUsedVariables
    use している変数配列を返す(クロージャのみ)
    getClosure
    元となったオブジェクトを $object としたクロージャを返す(メソッドのみ)
    • 上記二つは call/callStatic のメソッドも呼び出せる
    getTraitMethod
    トレイト側のリフレクションを返す(メソッドのみ)

    Example:

    that(reflect_callable('sprintf'))->isInstanceOf(\ReflectionFunction::class); that(reflect_callable('\Closure::bind'))->isInstanceOf(\ReflectionMethod::class); $x = 1; $closure = function ($a, $b) use (&$x) { return $a + $b; }; $reflection = reflect_callable($closure); // 単純実行 that($reflection(1, 2))->is(3); // 無名クラスを $this として実行 that($reflection->call(new class(){}, 1, 2))->is(3); // 宣言部を返す that($reflection->getDeclaration())->is('function ($a, $b) use (&$x)'); // 定義部を返す that($reflection->getCode())->is('{ return $a + $b; }'); // static か返す that($reflection->isStatic())->is(false); // use 変数を返す that($reflection->getUsedVariables())->is(['x' => 1]);
    Parameter
    type name summary
    ​callable $callable 対象 callable
    Return
    type summary
    ReflectCallable|​ReflectionFunction|​ReflectionMethod リフレクションインスタンス

    [F] reflect_type_resolve ReflectionType の型に \ を付与する

    ReflectionType の型に \ を付与する
    function reflect_type_resolve(?string $type): ?string

    php8.0 で ReflectionType の __toString が解放されたけど、それをそのまま埋め込んだりすると \ がないのでエラーになったりする。 この関数を通してから埋め込めば \ が付くので回避できる、という非常にニッチな関数。

    型 exists で判定するため、付与するクラスは存在している必要がある(オプション引数で対応するかもしれない)。

    Example:

    // このような DNF 型も形式を保ったまま \\ を付与できる that(reflect_type_resolve('(Countable&Traversable)|object'))->is('(\\Countable&\\Traversable)|object');
    Parameter
    type name summary
    ​?string $type string だが実用上は getType 等で得られるインスタンスでよい
    Return
    type summary
    ​?string 解決された文字列

    [F] reflect_types ReflectionType の型配列を返す

    ReflectionType の型配列を返す
    function reflect_types(\ReflectionFunctionAbstract|\ReflectionType|\ReflectionType[]|null $reflection_type = null): ReflectTypes|object

    ReflectionType のインターフェース・仕様がコロコロ変わってついていけないので関数化した。

    ReflectionType に準ずるインスタンスを渡すと取り得る候補を配列ライクなオブジェクトで返す。 引数は配列で複数与えても良い。よしなに扱って複数型として返す。 また「Type が一意に導出できる Reflection」を渡しても良い(ReflectionProperty など)。 null を与えた場合はエラーにはならず、スルーされる(getType は null を返し得るので利便性のため)。

    単純に ReflectionType の配列ライクなオブジェクトを返すが、そのオブジェクトは __toString が実装されており、文字列化するとパイプ区切りの型文字列を返す。 これは 8.0 における ReflectionUnionType の __toString を模倣したものである。 互換性のある型があった場合、上位の型に内包されて型文字列としては出現しない。

    Countable も実装されているが、その結果は「内部 Type の数」ではなく、論理的に「取り得る型の数」を返す。 例えば ?int は型としては1つだが、実際は int, null の2つを取り得るため、 count は 2 を返す。 端的に言えば「__toString のパイプ区切りの型の数」を返す。

    あとは便利メソッドとして下記が生えている。

    jsonSerialize
    JsonSerializable 実装
    getTypes
    取り得る型をすべて返す(ReflectionUnionType 互換)
    getName
    ReflectionUnionType 非互換 toString な型宣言文字列を返す
    allows
    その値を取りうるか判定して返す

    ReflectionUnionType とは完全互換ではないので、php8.0が完全に使える環境であれば素直に ReflectionUnionType を使ったほうが良い。 (「常に(型分岐せずに)複数形で扱える」程度のメリットしかない。allows は惜しいが)。

    ちなみに型の変遷は下記の通り。

    php7.1
    ReflectionType::__toString が非推奨になった
    php7.1
    ReflectionNamedType が追加され、各種 getType でそれを返すようになった
    php8.0
    ReflectionType::__toString が非推奨ではなくなった
    php8.0
    ReflectionUnionType が追加され、複合の場合は getType でそれを返すようになった

    Example:

    $object = new class { function method(object $o):?string {} }; $method = new \ReflectionMethod($object, 'method'); $types = reflect_types($method->getParameters()[0]->getType()); // 文字列化すると型宣言文字列を返すし、配列アクセスや count, iterable でそれぞれの型が得られる that((string) $types)->is('object'); that($types[0])->isInstanceOf(\ReflectionType::class); that(iterator_to_array($types))->eachIsInstanceOf(\ReflectionType::class); that(count($types))->is(1); // 返り値でも同じ(null 許容なので null が付くし count も 2 になる) $types = reflect_types($method->getReturnType()); that((string) $types)->is('string|null'); that(count($types))->is(2);
    Parameter
    type name summary
    ReflectionFunctionAbstract|​ReflectionType|​ReflectionType[]|​null $reflection_type = null getType 等で得られるインスタンス
    Return
    type summary
    ReflectTypes|​object

    [N] ryunosuke\Functions\Package\stream\

    [F] functions

    [F] include_stream file スキームを上書きして include/require をフックできるストリームオブジェクトを返す

    file スキームを上書きして include/require をフックできるストリームオブジェクトを返す
    function include_stream(): IncludeStream|object

    register で include/require しようとしている $filename が渡ってくる callable を登録する。 restore で登録を解除する。

    stream wrapper にはスタッキングや取得系関数がないため、この関数を使うと file:// の登録は全て解除されるので注意。

    Return
    type summary
    IncludeStream|​object stream wrapper オブジェクト

    [F] memory_stream ファイルのように扱えるメモリ上のパスを返す

    ファイルのように扱えるメモリ上のパスを返す
    function memory_stream(string $path = ""): string

    劣化 vfsStream のようなもの。 stream wrapper を用いて実装しており、そのプロトコルは初回呼び出し時に1度だけ登録される。 プロトコル名は決め打ちだが、 php.ini に "rfunc.memory_stream" というキーで文字列を指定するとそれが使用される。

    Example:

    // ファイル名のように読み書きができるパスを返す(一時ファイルを使用するよりかなり高速に動作する) $memory_stream = memory_stream('filename.txt'); // 呼んだだけでは何もしないので存在しない that(file_exists($memory_stream))->isSame(false); // file_put_contents が使える that(file_put_contents($memory_stream, 'Hello, World'))->isSame(12); // file_get_contents が使える that(file_get_contents($memory_stream))->isSame('Hello, World'); // 上記の操作で実体が存在している that(file_exists($memory_stream))->isSame(true); // unlink が使える that(unlink($memory_stream))->isSame(true); // unlink したので存在しない that(file_exists($memory_stream))->isSame(false);
    Parameter
    type name summary
    ​string $path = "" パス名(実質的に一意なファイル名)
    Return
    type summary
    ​string メモリ上のパス

    [F] profiler 外部ツールに頼らない pure php なプロファイラを返す

    外部ツールに頼らない pure php なプロファイラを返す
    function profiler(array $options = []): \Traversable|callable

    file プロトコル上書きと ticks と debug_backtrace によるかなり無理のある実装なので動かない環境・コードは多い。 その分お手軽だが下記の注意点がある。

    • file プロトコルを上書きするので、既に読み込み済みのファイルは計上されない
    • tick されないステートメントは計上されない
      • 1行メソッドなどでありがち
    • A->B->C という呼び出しで C が 3秒、B が 2秒、A が1秒かかった場合、 A は 6 秒、B は 5秒、C は 3 秒といて計上される
      • つまり、配下の呼び出しも重複して計上される

    この関数を呼んだ時点で計測は始まる。 返り値としてイテレータを返すので、foreach で回せばコールスタック・回数・時間などが取得できる。 配列で欲しい場合は直に呼べば良い。

    Parameter
    type name summary
    ​array $options = [] オプション配列
    Return
    type summary
    Traversable|​callable プロファイライテレータ

    [F] var_stream 変数をリソースのように扱えるファイルポインタを返す

    変数をリソースのように扱えるファイルポインタを返す
    function var_stream( string|null &$var, string $initial = "" ): resource

    得られたファイルポインタに fread すれば変数の値が見えるし、 fwrite すれば変数の値が書き換わる。 逆に変数を書き換えればファイルポインタで得られる値も書き換わる。

    用途は主にテスト用。 例えば「何らかのファイルポインタを要求する処理」に対して fopen や tmpfile を駆使して値の確認をするのは結構めんどくさい。 rewind したり stream_get_contents したり削除したりする必要がある)。 それよりもこの関数で得られたファイルポインタを渡し、 that($var)->is($expected) とできる方がテストの視認性が良くなる。

    Example:

    // $var のファイルポインタを取得 $fp = var_stream($var); // ファイルポインタに書き込みを行うと変数にも反映される fwrite($fp, 'hoge'); that($var)->is('hoge'); // 変数に追記を行うとファイルポインタで読み取れる $var .= 'fuga'; that(fread($fp, 1024))->is('fuga'); // 変数をまるっと置換するとファイルポインタ側もまるっと変わる $var = 'hello, world'; that(stream_get_contents($fp, -1, 0))->is('hello, world'); // ファイルポインタをゴリっと削除すると変数も空になる ftruncate($fp, 0); that($var)->is('');
    Parameter
    type name summary
    ​string|​null &$var 対象の変数
    ​string $initial = "" 初期値。与えたときのみ初期化される
    Return
    type summary
    ​resource 変数のファイルポインタ

    [N] ryunosuke\Functions\Package\strings\

    [F] functions

    [F] camel_case camelCase に変換する

    camelCase に変換する
    function camel_case( ?string|string $string, ?string|string $delimiter = "_" ): string

    Example:

    that(camel_case('this_is_a_pen'))->isSame('thisIsAPen');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $delimiter = "_" デリミタ
    Return
    type summary
    ​string 変換した文字列

    [F] chain_case chain-case に変換する

    chain-case に変換する
    function chain_case( ?string|string $string, ?string|string $delimiter = "-" ): string

    Example:

    that(chain_case('ThisIsAPen'))->isSame('this-is-a-pen');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $delimiter = "-" デリミタ
    Return
    type summary
    ​string 変換した文字列

    [F] concat strcat の空文字回避版

    strcat の空文字回避版
    function concat(?string ...$variadic): ?string

    基本は strcat と同じ。ただし、引数の内1つでも空文字を含むなら空文字を返す さらに*引数の内1つでも null を含むなら null を返す**。

    「プレフィックスやサフィックスを付けたいんだけど、空文字の場合はそのままで居て欲しい」という状況はまれによくあるはず。 コードで言えば strlen($string) ? 'prefix-' . $string : ''; のようなもの。 可変引数なので 端的に言えば mysql の CONCAT みたいな動作になる。

    that(concat('prefix-', 'middle', '-suffix'))->isSame('prefix-middle-suffix'); that(concat('prefix-', '', '-suffix'))->isSame(''); that(concat('prefix-', null, '-suffix'))->isSame(null);
    Parameter
    type name summary
    ​?string ...$variadic 結合する文字列(可変引数)
    Return
    type summary
    ​?string 結合した文字列

    [F] damerau_levenshtein Damerau–Levenshtein 距離を返す

    Damerau–Levenshtein 距離を返す
    function damerau_levenshtein( string $s1, string $s2, int $cost_ins = 1, int $cost_rep = 1, int $cost_del = 1, int $cost_swp = 1 ): int

    簡単に言えば「転置(入れ替え)を考慮したレーベンシュタイン」である。 例えば "destroy" と "destory" は 「1挿入1削除=2」であるが、Damerau 版だと「1転置=1」となる。

    また、マルチバイト(UTF-8 のみ)にも対応している。

    Example:

    // destroy と destory は普通にレーベンシュタイン距離を取ると 2 になるが・・・ that(levenshtein("destroy", "destory"))->isSame(2); // damerau_levenshtein だと1である that(damerau_levenshtein("destroy", "destory"))->isSame(1); // UTF-8 でも大丈夫 that(damerau_levenshtein("あいうえお", "あいえうお"))->isSame(1);
    Parameter
    type name summary
    ​string $s1 対象文字列1
    ​string $s2 対象文字列2
    ​int $cost_ins = 1 挿入のコスト
    ​int $cost_rep = 1 置換のコスト
    ​int $cost_del = 1 削除のコスト
    ​int $cost_swp = 1 転置のコスト
    Return
    type summary
    ​int Damerau–Levenshtein 距離

    [F] ends_with 指定文字列で終わるか調べる

    指定文字列で終わるか調べる
    function ends_with( ?string|string $string, string|string[] $with, bool $case_insensitivity = false ): bool

    $with に配列を渡すといずれかで終わるときに true を返す。

    Example:

    that(ends_with('abcdef', 'def'))->isTrue(); that(ends_with('abcdef', 'DEF', true))->isTrue(); that(ends_with('abcdef', 'xyz'))->isFalse(); that(ends_with('abcdef', ['d', 'e', 'f']))->isTrue(); that(ends_with('abcdef', ['x', 'y', 'z']))->isFalse();
    Parameter
    type name summary
    ​?string|​string $string 探される文字列
    ​string|​string[] $with 探す文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​bool 対象文字列で終わるなら true

    [F] include_string 変数を extract して include する(文字列指定)

    変数を extract して include する(文字列指定)
    function include_string( ?string|string $template, array $array = [] ): string
    Parameter
    type name summary
    ​?string|​string $template テンプレート文字列
    ​array $array = [] extract される連想変数
    Return
    type summary
    ​string レンダリングされた文字列
    See
    type summary
    ob_include()

    [F] kvsprintf 連想配列を指定できるようにした vsprintf

    連想配列を指定できるようにした vsprintf
    function kvsprintf( ?string|string $format, array $array ): string

    sprintf の順序指定構文('%1$d')にキーを指定できる。

    Example:

    that(kvsprintf('%hoge$s %fuga$d', ['hoge' => 'ThisIs', 'fuga' => '3.14']))->isSame('ThisIs 3');
    Parameter
    type name summary
    ​?string|​string $format フォーマット文字列
    ​array $array フォーマット引数
    Return
    type summary
    ​string フォーマットされた文字列

    [F] mb_compatible_encoding 指定エンコーディング間に互換性があるかを返す

    指定エンコーディング間に互換性があるかを返す
    function mb_compatible_encoding( ?string|string $from, ?string|string $to ): ?bool

    ※ ユースケースとして多い utf8,sjis 以外はほぼ実装していないので注意(かなり適当なのでそれすらも怪しい)

    mb_convert_encoding/mb_convert_variables は実際に変換が行われなくても処理が走ってしまうので、それを避けるための関数。 エンコーディングはただでさえカオスなのに utf8, UTF-8, sjis, sjis-win, cp932 などの表記揺れやエイリアスがあるので判定が結構しんどい。

    Example:

    // ほぼ唯一のユースケース(互換性があるなら変換しない) if (!mb_compatible_encoding(mb_internal_encoding(), 'utf8')) { mb_convert_encoding('utf8 string', 'utf8'); }
    Parameter
    type name summary
    ​?string|​string $from 変換元エンコーディング
    ​?string|​string $to 変換先エンコーディング
    Return
    type summary
    ​?bool from が to に対して互換性があるなら true(8bit binary の時のみ例外的に null を返す)

    [F] mb_ellipsis 文字列を指定幅に丸める

    文字列を指定幅に丸める
    function mb_ellipsis( ?string|string $string, int $width, string $trimmarker = "...", int|null $pos = null ): string

    mb_strimwidth と機能的には同じだが、省略文字の差し込み位置を $pos で指定できる。 $pos は負数が指定できる。負数の場合後ろから数えられる。 省略した場合は真ん中となる。

    Example:

    // 10文字幅に丸める($pos 省略なので真ん中が省略される) that(mb_ellipsis('あいうえお1234567890', 10, '...'))->isSame('あい...890'); // 10文字幅に丸める($pos=1 なので1幅目から省略される…が、1文字は「あ」なので前方に切られる) that(mb_ellipsis('あいうえお1234567890', 10, '...', 1))->isSame('...567890'); // 10文字幅に丸める($pos=2 なので2幅目から省略される) that(mb_ellipsis('あいうえお1234567890', 10, '...', 2))->isSame('あ...67890'); // 10文字幅に丸める($pos=-1 なので後ろから1幅目から省略される) that(mb_ellipsis('あいうえお1234567890', 10, '...', -1))->isSame('あいう...0');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int $width 丸める幅
    ​string $trimmarker = "..." 省略文字列
    ​int|​null $pos = null 省略記号の差し込み位置
    Return
    type summary
    ​string 丸められた文字列

    [F] mb_ereg_options mb_系の全体設定を一括設定する

    mb_系の全体設定を一括設定する
    function mb_ereg_options(array $options): callable

    返り値として「コールすると元に戻す callable」を返す。 あるいはその返り値はスコープが外れると自動で元に戻す処理が行われる。

    余計なことはしない素直な実装だが、'encoding' というキーは mb_internal_encoding と regex_encoding の両方に適用される。

    Example:

    $recover = mb_ereg_options([ 'internal_encoding' => 'SJIS', // 今だけは internal_encoding を SJIS にする 'regex_options' => 'ir', // 今だけは regex_options を ir にする ]); that($recover)->isCallable(); // 返り値は callable that(mb_internal_encoding())->is('SJIS'); // 今だけは SJIS that(mb_regex_set_options())->is('ir'); // 今だけは ir $recover(); that(mb_internal_encoding())->is('UTF-8'); // $recover をコールすると戻る that(mb_regex_set_options())->is('pr'); // $recover をコールすると戻る
    Parameter
    type name summary
    ​array $options オプション配列
    Return
    type summary
    ​callable 元に戻す callable

    [F] mb_ereg_split マルチバイト対応 preg_split

    マルチバイト対応 preg_split
    function mb_ereg_split( ?string|string $pattern, ?string|string $subject, int $limit = -1, int $flags = 0 ): ?array

    preg_split の PREG_SPLIT_NO_EMPTY, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE も使用できる。

    Example:

    # 下記のようにすべて preg_split と合わせてある // limit:2 that(mb_ereg_split(",", "a,b,c", 2))->is(['a', 'b,c']); // flags:PREG_SPLIT_NO_EMPTY that(mb_ereg_split(",", ",a,,b,,c,", -1, PREG_SPLIT_NO_EMPTY))->is(['a', 'b', 'c']); // flags:PREG_SPLIT_DELIM_CAPTURE that(mb_ereg_split("(,)", "a,c", -1, PREG_SPLIT_DELIM_CAPTURE))->is(['a', ',', 'c']); // flags:PREG_SPLIT_OFFSET_CAPTURE that(mb_ereg_split(",", "a,b,c", -1, PREG_SPLIT_OFFSET_CAPTURE))->is([['a', 0], ['b', 2], ['c', 4]]); # 他の preg_split 特有の動きも同じ // 例えば limit は PREG_SPLIT_DELIM_CAPTURE には作用しない that(mb_ereg_split("(,)", "a,b,c", 2, PREG_SPLIT_DELIM_CAPTURE))->is(['a', ',', 'b,c']);
    Parameter
    type name summary
    ​?string|​string $pattern パターン文字列
    ​?string|​string $subject 対象文字列
    ​int $limit = -1 分割数
    ​int $flags = 0 フラグ
    Return
    type summary
    ​?array 分割された文字列

    [F] mb_monospace ASCII 文字を1, それ以外を2で計算した文字幅を返す

    ASCII 文字を1, それ以外を2で計算した文字幅を返す
    function mb_monospace( ?string|string $string, array $codepoints = [] ): int

    mb_strwidth は記号も1で返すので若干使いづらい(仕様的にしょうがない)。 日本語圏内であれば記号や絵文字も2バイト換算の方が便利なことが多いのでそのようにしたもの。

    オプションでコードポイント配列を渡すとそれに従って幅を加算する。 コードポイントの指定は Example を参照。

    Example:

    that(mb_monospace("※★▼…"))->is(8); // 記号類も2バイト換算で8 that(mb_monospace("123456788"))->is(11); // 比較用(フォントに依存するが) that(mb_monospace("Σ(゚Д゚)え!!"))->is(15); // 半角全角の判定ではなく ASCII 判定なので 15 that(mb_monospace("Σ(゚Д゚)え!!", [ // コードポイントを指定すれば合わせることが可能 "Σ" => 1, // 単体指定(シグマ) "Ѐ-ӿ" => 1, // 範囲指定(キリル文字) 0xFF9F => 1, // 直指定(半角半濁点) ]))->is(11);
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​array $codepoints = [] コードポイント配列
    Return
    type summary
    ​int 等幅の文字幅

    [F] mb_pad_width マルチバイト版 str_pad

    マルチバイト版 str_pad
    function mb_pad_width( ?string|string $string, int $width, string $pad_string = " ", int $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT ): string

    単純な mb_strlen での実装ではなく mb_monospace による実装となっている。 「文字数を指定して pad したい」という状況は utf8 で2バイト超えという状況がふさわしくないことが非常に多い。 多くは単純に「全角は2文字、半角は1文字」というユースケースが多い(埋める文字がスペースなら特に)。

    また、$pad_string が切り捨てられることもない。 標準の str_pad はできるだけ詰めようとして中途半端な $pad_string になることがあるが、その動作は模倣していない。 端的に「$width を超えないようにできる限り敷き詰めて返す」という動作になる。

    Example:

    // マルチバイトは2文字幅として換算される that(mb_pad_width('aaaa', 12, '-'))->isSame('aaaa--------'); that(mb_pad_width('ああ', 12, '-'))->isSame('ああ--------'); // $pad_string は切り捨てられない that(mb_pad_width('aaaa', 12, 'xyz'))->isSame('aaaaxyzxyz'); // 10文字で返す(あと1回 xyz すると 13 文字になり width を超えてしまう(かといって xy だけを足したりもしない)) that(mb_pad_width('ああ', 12, 'xyz'))->isSame('ああxyzxyz'); // マルチバイトでも同じ
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int $width 埋める幅
    ​string $pad_string = " " 埋める文字列
    ​int $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT 埋める位置
    Return
    type summary
    ​string 指定文字で埋められた文字列

    [F] mb_str_pad マルチバイト版 str_pad

    マルチバイト版 str_pad
    function mb_str_pad( ?string $string, $width, $pad_string = " ", $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT )
    Parameter
    type name summary
    ​?string $string
    $width
    $pad_string = " "
    $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT
    See
    type summary
    mb_pad_width()

    [F] mb_substr_replace マルチバイト対応 substr_replace

    マルチバイト対応 substr_replace
    function mb_substr_replace( ?string|string $string, ?string|string $replacement, int $start, ?int $length = null ): string

    本家は配列を与えたりできるが、ややこしいし使う気がしないので未対応。

    Example:

    // 2文字目から5文字を「あいうえお」に置換する that(mb_substr_replace('0123456789', 'あいうえお', 2, 5))->isSame('01あいうえお789');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $replacement 置換文字列
    ​int $start 開始位置
    ​?int $length = null 置換長
    Return
    type summary
    ​string 置換した文字列

    [F] mb_trim マルチバイト対応 trim

    マルチバイト対応 trim
    function mb_trim(?string $string)
    Parameter
    type name summary
    ​?string $string

    [F] mb_wordwrap 文字列を指定文字幅で改行を挟み込む

    文字列を指定文字幅で改行を挟み込む
    function mb_wordwrap( ?string|string $string, int $width, ?string $break = "\n" ): string|array

    基本的に wordwrap のマルチバイト対応版だと思って良い。 ただし下記の点が異なる。

    • マルチバイトは一律2文字換算
      • これ系の関数の「幅」はいわゆる半角/全角として扱ったほうが都合が良い
    • $cut_long_words 引数はない
      • 用途的に true がデフォだろうし、マルチバイトが絡んでくると「単語」の定義がそもそも曖昧なので実装しない
      • 実装するにしても禁則処理の方が用途は多いだろう
    • $break に null を与えると配列で返す

    Example:

    that(mb_wordwrap("todayは晴天なり", 10, null))->is([ 'todayは晴', '天なり', ]);
    Parameter
    type name summary
    ​?string|​string $string 分割する文字列
    ​int $width 分割する最大幅
    ​?string $break = "\n" 分割文字
    Return
    type summary
    ​string|​array 指定幅で改行が差し込まれた文字列

    [F] multiexplode explode の配列対応と $limit の挙動を変えたもの

    explode の配列対応と $limit の挙動を変えたもの
    function multiexplode( string|array $delimiter, ?string|string $string, int $limit = PHP_INT_MAX ): array

    $delimiter には配列が使える。いわゆる「複数文字列での分割」の動作になる。

    $limit に負数を与えると「その絶対値-1までを結合したものと残り」を返す。 端的に言えば「正数を与えると後詰めでその個数で返す」「負数を与えると前詰めでその(絶対値)個数で返す」という動作になる。

    Example:

    // 配列を与えると複数文字列での分割 that(multiexplode([',', ' ', '|'], 'a,b c|d'))->isSame(['a', 'b', 'c', 'd']); // 負数を与えると前詰め that(multiexplode(',', 'a,b,c,d', -2))->isSame(['a,b,c', 'd']); // もちろん上記2つは共存できる that(multiexplode([',', ' ', '|'], 'a,b c|d', -2))->isSame(['a,b c', 'd']);
    Parameter
    type name summary
    ​string|​array $delimiter 分割文字列。配列可
    ​?string|​string $string 対象文字列
    ​int $limit = PHP_INT_MAX 分割数
    Return
    type summary
    ​array 分割された配列

    [F] namespace_split 文字列を名前空間とローカル名に区切ってタプルで返す

    文字列を名前空間とローカル名に区切ってタプルで返す
    function namespace_split(?string|string $string): array

    class_namespace/class_shorten や function_shorten とほぼ同じだが下記の違いがある。

    • あくまで文字列として処理する
      • 例えば class_namespace は get_class されるが、この関数は(いうなれば) strval される
    • \ を trim しないし、特別扱いもしない
      • ns\\hoge\\ns\\hoge で返り値が微妙に異なる
      • ns\\ のような場合は名前空間だけを返す

    Example:

    that(namespace_split('ns\\hoge'))->isSame(['ns', 'hoge']); that(namespace_split('hoge'))->isSame(['', 'hoge']); that(namespace_split('ns\\'))->isSame(['ns', '']); that(namespace_split('\\hoge'))->isSame(['', 'hoge']);
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    Return
    type summary
    ​array [namespace, localname]

    [F] ngram N-gram 化して配列で返す

    N-gram 化して配列で返す
    function ngram( ?string|string $string, int $N, string $encoding = "UTF-8" ): array

    素朴な実装であり特記事項はない。 末端要素や除去フィルタくらいは実装するかもしれない。

    Example:

    that(ngram("あいうえお", 1))->isSame(["あ", "い", "う", "え", "お"]); that(ngram("あいうえお", 2))->isSame(["あい", "いう", "うえ", "えお", "お"]); that(ngram("あいうえお", 3))->isSame(["あいう", "いうえ", "うえお", "えお", "お"]);
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int $N N-gram の N
    ​string $encoding = "UTF-8" マルチバイトエンコーディング
    Return
    type summary
    ​array N-gram 配列

    [F] pascal_case PascalCase に変換する

    PascalCase に変換する
    function pascal_case( ?string|string $string, ?string|string $delimiter = "_" ): string

    Example:

    that(pascal_case('this_is_a_pen'))->isSame('ThisIsAPen'); that(pascal_case('this_is-a-pen', '-_'))->isSame('ThisIsAPen');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $delimiter = "_" デリミタ(複数可)
    Return
    type summary
    ​string 変換した文字列

    [F] quoteexplode エスケープやクオートに対応した explode

    エスケープやクオートに対応した explode
    function quoteexplode( string|array $delimiter, ?string|string $string, ?int $limit = null, array|string $enclosures = "'\"", string $escape = "\\", array $options = [] ): array

    $enclosures は配列で開始・終了文字が別々に指定できるが、実装上の都合で今のところ1文字ずつのみ。

    Example:

    // シンプルな例 that(quoteexplode(',', 'a,b,c\\,d,"e,f"'))->isSame([ 'a', // 普通に分割される 'b', // 普通に分割される 'c\\,d', // \\ でエスケープしているので区切り文字とみなされない '"e,f"', // "" でクオートされているので区切り文字とみなされない ]); // $enclosures で囲い文字の開始・終了文字を明示できる that(quoteexplode(',', 'a,b,{e,f}', null, ['{' => '}']))->isSame([ 'a', // 普通に分割される 'b', // 普通に分割される '{e,f}', // { } で囲まれているので区切り文字とみなされない ]);
    Parameter
    type name summary
    ​string|​array $delimiter 分割文字列
    ​?string|​string $string 対象文字列
    ​?int $limit = null 分割数。負数未対応
    ​array|​string $enclosures = "'\"" 囲い文字。 ["start" => "end"] で開始・終了が指定できる
    ​string $escape = "\\" エスケープ文字
    ​array $options = [] オプション
    Return
    type summary
    ​array 分割された配列

    [F] render_file "hoge $hoge" 形式のレンダリングのファイル版

    "hoge $hoge" 形式のレンダリングのファイル版
    function render_file( ?string|string $template_file, array $array ): string
    Parameter
    type name summary
    ​?string|​string $template_file レンダリングするファイル名
    ​array $array レンダリング変数
    Return
    type summary
    ​string レンダリングされた文字列
    See
    type summary
    render_string()

    [F] render_string "hoge $hoge" 形式のレンダリング

    "hoge $hoge" 形式のレンダリング
    function render_string( ?string|string $template, array $array ): string

    文字列を eval して "hoge $hoge" 形式の文字列に変数を埋め込む。 基本処理は eval("return '" . addslashes($template) . "';"); と考えて良いが、下記が異なる。

    • 数値キーが参照できる
    • クロージャは呼び出し結果が埋め込まれる。引数は (変数配列, 自身のキー文字列)
    • 引数をそのまま返すだけの特殊な変数 $_ が宣言される
    • シングルクォートのエスケープは外される

    $ が宣言されるのは変数配列に '' を含んでいないときのみ(上書きを防止するため)。 この $_ は php の埋め込み変数の闇を利用するととんでもないことが出来たりする(サンプルやテストコードを参照)。

    ダブルクオートはエスケープされるので文字列からの脱出はできない。 また、 {$_(syntax(""))} のように $_() 構文で " も使えなくなるので \' を使用しなければならない。

    Example:

    // クロージャは呼び出し結果が埋め込まれる that(render_string('$c', ['c' => fn($vars, $k) => $k . '-closure']))->isSame('c-closure'); // 引数をそのまま返すだけの特殊な変数 $_ が宣言される that(render_string('{$_(123 + 456)}', []))->isSame('579'); // 要するに '$_()' の中に php の式が書けるようになる that(render_string('{$_(implode(\',\', $strs))}', ['strs' => ['a', 'n', 'z']]))->isSame('a,n,z'); that(render_string('{$_(max($nums))}', ['nums' => [1, 9, 3]]))->isSame('9');
    Parameter
    type name summary
    ​?string|​string $template レンダリング文字列
    ​array $array レンダリング変数
    Return
    type summary
    ​string レンダリングされた文字列

    [F] render_template "hoge $hoge" 形式のレンダリング

    "hoge $hoge" 形式のレンダリング
    function render_template( ?string|string $template, array|object $vars, callable $tag = null ): string

    ES6 のテンプレートリテラルのようなもの。

    • 埋め込みは $var のみで、$var は無効
    • $expression は「評価結果の変数名」ではなく「評価結果」が埋め込まれる

    この関数は実験的機能のため、互換性を維持せず変更される可能性がある。 また、 token_get_all に頼っているため php9 で ${var} 構文が廃止されたらおそらく動かない。

    Example:

    that(render_template('${max($nums)}', ['nums' => [1, 9, 3]]))->isSame('9');
    Parameter
    type name summary
    ​?string|​string $template レンダリングする文字列
    ​array|​object $vars レンダリング変数
    ​callable $tag = null ブロックと変数値が渡ってくるクロージャ(タグ付きテンプレートリテラルのようなもの)
    Return
    type summary
    ​string レンダリングされた文字列

    [F] snake_case snake_case に変換する

    snake_case に変換する
    function snake_case( ?string|string $string, ?string|string $delimiter = "_", bool $keep_abbr = false ): string

    Example:

    that(snake_case('ThisIsAPen'))->isSame('this_is_a_pen'); that(snake_case('URLEncode', '-'))->isSame('u-r-l-encode'); // デフォルトでは略語も分割される that(snake_case('URLEncode', '-', true))->isSame('url-encode'); // 第3引数 true で略語は維持される
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $delimiter = "_" デリミタ
    ​bool $keep_abbr = false すべて大文字の単語を1単語として扱うか
    Return
    type summary
    ​string 変換した文字列

    [F] split_noempty 空文字を除外する文字列分割

    空文字を除外する文字列分割
    function split_noempty( ?string|string $delimiter, ?string|string $string, string|bool $trimchars = true ): array
    • 空文字を任意の区切り文字で分割しても常に空配列
    • キーは連番で返す(歯抜けがないただの配列)

    $triming を指定した場合、結果配列にも影響する。 つまり「除外は trim したいが結果配列にはしたくない」はできない。

    Example:

    that(split_noempty(',', 'a, b, c'))->isSame(['a', 'b', 'c']); that(split_noempty(',', 'a, , , b, c'))->isSame(['a', 'b', 'c']); that(split_noempty(',', 'a, , , b, c', false))->isSame(['a', ' ', ' ', ' b', ' c']);
    Parameter
    type name summary
    ​?string|​string $delimiter 区切り文字
    ​?string|​string $string 対象文字
    ​string|​bool $trimchars = true 指定した文字を trim する。true を指定すると trim する
    Return
    type summary
    ​array 指定文字で分割して空文字を除いた配列

    [F] starts_with 指定文字列で始まるか調べる

    指定文字列で始まるか調べる
    function starts_with( ?string|string $string, string|string[] $with, bool $case_insensitivity = false ): bool

    $with に配列を渡すといずれかで始まるときに true を返す。

    Example:

    that(starts_with('abcdef', 'abc'))->isTrue(); that(starts_with('abcdef', 'ABC', true))->isTrue(); that(starts_with('abcdef', 'xyz'))->isFalse(); that(starts_with('abcdef', ['a', 'b', 'c']))->isTrue(); that(starts_with('abcdef', ['x', 'y', 'z']))->isFalse();
    Parameter
    type name summary
    ​?string|​string $string 探される文字列
    ​string|​string[] $with 探す文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​bool 指定文字列で始まるなら true を返す

    [F] str_anyof 文字列が候補の中にあるか調べる

    文字列が候補の中にあるか調べる
    function str_anyof( ?string $needle, iterable $haystack, bool $case_insensitivity = false ): bool

    候補配列の中に対象文字列があるならそのキーを返す。ないなら null を返す。

    あくまで文字列としての比較に徹する(in_array/array_search の第3引数は厳密すぎて使いにくいことがある)。 ので array_search の文字列特化版とも言える。 動作的には array_flip($haystack)[$needle] ?? null と同じ(大文字小文字オプションはあるけど)。 ただ array_flip は巨大配列に弱いし、大文字小文字などの融通が効かないので foreach での素朴な実装になっている。

    Example:

    that(str_anyof('b', ['a', 'b', 'c']))->isSame(1); // 見つかったキーを返す that(str_anyof('x', ['a', 'b', 'c']))->isSame(null); // 見つからないなら null を返す that(str_anyof('C', ['a', 'b', 'c'], true))->isSame(2); // 大文字文字を区別しない that(str_anyof('1', [1, 2, 3]))->isSame(0); // 文字列の比較に徹する that(str_anyof(2, ['1', '2', '3']))->isSame(1); // 同上
    Parameter
    type name summary
    ​?string $needle 調べる文字列
    ​iterable $haystack 候補配列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​bool 候補の中にあるならそのキー。無いなら null

    [F] str_array 文字列を区切り文字で区切って配列に変換する

    文字列を区切り文字で区切って配列に変換する
    function str_array( string|array $string, ?string|string $delimiter, bool $hashmode, bool $strict = true ): array

    典型的には http ヘッダとか sar の結果とかを配列にする。

    Example:

    // http response header を ":" 区切りで連想配列にする that(str_array(" HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Connection: Keep-Alive ", ':', true))->isSame([ 'HTTP/1.1 200 OK', 'Content-Type' => 'text/html; charset=utf-8', 'Connection' => 'Keep-Alive', ]); // sar の結果を " " 区切りで連想配列の配列にする that(str_array(" 13:00:01 CPU %user %nice %system %iowait %steal %idle 13:10:01 all 0.99 0.10 0.71 0.00 0.00 98.19 13:20:01 all 0.60 0.10 0.56 0.00 0.00 98.74 ", ' ', false))->isSame([ 1 => [ '13:00:01' => '13:10:01', 'CPU' => 'all', '%user' => '0.99', '%nice' => '0.10', '%system' => '0.71', '%iowait' => '0.00', '%steal' => '0.00', '%idle' => '98.19', ], 2 => [ '13:00:01' => '13:20:01', 'CPU' => 'all', '%user' => '0.60', '%nice' => '0.10', '%system' => '0.56', '%iowait' => '0.00', '%steal' => '0.00', '%idle' => '98.74', ], ]); // strict:false だと列数が一致していなくてもよい(null で埋められる) that(str_array(" 13:00:01 CPU %user %nice %system %iowait 13:10:01 all 0.99 0.10 0.71 0.00 0.00 98.19 13:20:01 all 0.60 0.10 ", ' ', false, false))->isSame([ 1 => [ '13:00:01' => '13:10:01', 'CPU' => 'all', '%user' => '0.99', '%nice' => '0.10', '%system' => '0.71', '%iowait' => '0.00', '6' => '0.00', '7' => '98.19', ], 2 => [ '13:00:01' => '13:20:01', 'CPU' => 'all', '%user' => '0.60', '%nice' => '0.10', '%system' => null, '%iowait' => null, '6' => null, '7' => null, ], ]);
    Parameter
    type name summary
    ​string|​array $string 対象文字列。配列を与えても動作する
    ​?string|​string $delimiter 区切り文字
    ​bool $hashmode 連想配列モードか
    ​bool $strict = true true にすると列数が一致しない場合に null になる
    Return
    type summary
    ​array 配列

    [F] str_between 指定文字で囲まれた文字列を取得する

    指定文字で囲まれた文字列を取得する
    function str_between( ?string|string $string, ?string|string $from, ?string|string $to, int &$position = 0, string $enclosure = "'\"", string $escape = "\\" ): ?string

    $from, $to で指定した文字間を取得する($from, $to 自体は結果に含まれない)。 ネストしている場合、一番外側の文字間を返す。

    $enclosure で「特定文字に囲まれている」場合を無視することができる。 $escape で「特定文字が前にある」場合を無視することができる。

    $position を与えた場合、その場所から走査を開始する。 さらに結果があった場合、 $position には「次の走査開始位置」が代入される。 これを使用すると連続で「次の文字, 次の文字, ...」と言った動作が可能になる。

    Example:

    // $position を利用して "first", "second", "third" を得る("で囲まれた "blank" は返ってこない)。 that(str_between('{first} and {second} and "{blank}" and {third}', '{', '}', $n))->isSame('first'); that(str_between('{first} and {second} and "{blank}" and {third}', '{', '}', $n))->isSame('second'); that(str_between('{first} and {second} and "{blank}" and {third}', '{', '}', $n))->isSame('third'); // ネストしている場合は最も外側を返す that(str_between('{nest1{nest2{nest3}}}', '{', '}'))->isSame('nest1{nest2{nest3}}');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $from 開始文字列
    ​?string|​string $to 終了文字列
    ​int &$position = 0 開始位置。渡した場合次の開始位置が設定される
    ​string $enclosure = "'\"" 囲い文字。この文字中にいる $from, $to 文字は走査外になる
    ​string $escape = "\\" エスケープ文字。この文字が前にある $from, $to 文字は走査外になる
    Return
    type summary
    ​?string $from, $to で囲まれた文字。見つからなかった場合は null

    [F] str_bytes 文字列のバイト配列を得る

    文字列のバイト配列を得る
    function str_bytes( ?string|string $string, int $base = 10 ): array

    $base 引数で基数を変更できる。

    Example:

    // 10進配列で返す that(str_bytes('abc'))->isSame([97, 98, 99]); // 16進配列で返す that(str_bytes('abc', 16))->isSame(["61", "62", "63"]); // マルチバイトで余計なことはしない(php としての文字列のバイト配列をそのまま返す) that(str_bytes('あいう', 16))->isSame(["e3", "81", "82", "e3", "81", "84", "e3", "81", "86"]);
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int $base = 10 基数
    Return
    type summary
    ​array 文字のバイト配列

    [F] str_chop 先頭・末尾の指定文字列を削ぎ落とす

    先頭・末尾の指定文字列を削ぎ落とす
    function str_chop( ?string|string $string, ?string|string $prefix = "", ?string|string $suffix = "", bool $case_insensitivity = false ): string

    Example:

    // 文字列からパス文字列と拡張子を削ぎ落とす $PATH = '/path/to/something'; that(str_chop("$PATH/hoge.php", "$PATH/", '.php'))->isSame('hoge');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $prefix = "" 削ぎ落とす先頭文字列
    ​?string|​string $suffix = "" 削ぎ落とす末尾文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​string 削ぎ落とした文字列

    [F] str_chunk 文字列を可変引数の数で分割する

    文字列を可変引数の数で分割する
    function str_chunk( ?string|string $string, int ...$chunks ): string[]

    str_split の $length を個別に指定できるイメージ。 長さ以上を指定したりしても最後の要素は必ずついてくる(指定数で分割した後のあまり文字が最後の要素になる)。 これは最後が空文字でも同様で、 list での代入を想定しているため。

    Example:

    // 1, 2, 3 文字に分割(ぴったりなので変わったことはない) that(str_chunk('abcdef', 1, 2, 3))->isSame(['a', 'bc', 'def', '']); // 2, 3 文字に分割(余った f も最後の要素として含まれてくる) that(str_chunk('abcdef', 2, 3))->isSame(['ab', 'cde', 'f']); // 1, 10 文字に分割 that(str_chunk('abcdef', 1, 10))->isSame(['a', 'bcdef', '']);
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int ...$chunks 分割の各文字数(可変引数)
    Return
    type summary
    ​string[] 分割された文字列配列

    [F] str_common_prefix 文字列群の共通のプレフィックスを返す

    文字列群の共通のプレフィックスを返す
    function str_common_prefix(?string|string[] ...$strings): ?string

    共通部分がない場合は空文字を返す。 引数は2個以上必要で足りない場合は null を返す。

    Example:

    // 共通プレフィックスを返す that(str_common_prefix('ab', 'abc', 'abcd'))->isSame('ab'); that(str_common_prefix('あ', 'あい', 'あいう'))->isSame('あ'); // 共通部分がない場合は空文字を返す that(str_common_prefix('xab', 'yabc', 'zabcd'))->isSame(''); that(str_common_prefix('わあ', 'をあい', 'んあいう'))->isSame(''); // 引数不足の場合は null を返す that(str_common_prefix('a'))->isSame(null);
    Parameter
    type name summary
    ​?string|​string[] ...$strings
    Return
    type summary
    ​?string 共通部分(共通がない場合は空文字)

    [F] str_control_apply 制御文字を実際に適用させる

    制御文字を実際に適用させる
    function str_control_apply( string $string, string $characters = <<<TEXT \\b\10\\d\177 TEXT ): string

    とはいえ文字列として適用できる制御文字はそう多くはなく、実質的に \b くらいだろう。

    \b
    前の1文字を消して自身も消える
    \d
    次の\d以外の1文字を消して自身も消える
    • 前方探索なので利便性のため「\d以外の」という条件がついている
    \r
    直前の \n から直後の \n までを消す
    • \r に「消す」という意味はないが、実用上はその方が便利なことが多い

    なお、 \b と書いてあるが php のエスケープシーケンスに \b は存在しないため \x08 と記述する必要がある(\d も同様)。 それはそれで不便なので \b, \d は特別扱いとしてある($characters のデフォルト値に潜ませてあるが、それを外せば特別扱いは無くなる)。

    Example:

    // 最初の X は [BS] で消える・次の X は [DEL] で消える、XXX は [CR] で消える。結果 text\nzzz になる that(str_control_apply("X\b\dXtext\nXXX\rzzz"))->isSame("text\nzzz");
    Parameter
    type name summary
    ​string $string 対象文字列
    ​string $characters = <<<TEXT<br>\\b\10\\d\177<br>TEXT 対象とする制御文字
    Return
    type summary
    ​string 制御文字が適用された文字列

    [F] str_diff テキストの diff を得る

    テキストの diff を得る
    function str_diff( string|array|resource $xstring, string|array|resource $ystring, array $options = [] ): string|array|null

    $options['allow-binary'] でバイナリ文字列の扱いを指定する(false: 例外, null: null を返す)。 $options['ignore-case'] = true で大文字小文字を無視する。 $options['ignore-space-change'] = true で空白文字の数を無視する。 $options['ignore-all-space'] = true ですべての空白文字を無視する $options['stringify'] で差分データを文字列化するクロージャを指定する。

    normal
    標準形式(diff のオプションなしに相当する)
    context
    コンテキスト形式(context=3 のような形式で diff の -C 3 に相当する)
    unified
    ユニファイド形式(unified=3 のような形式で diff の -U 3 に相当する)
    • unified のみを指定するとヘッダを含まない +- のみの差分を出す
    split
    サイドバイサイド形式(split=3,120 のような形式で diff の -y -W 120 に相当する)
    • diff -y と互換性はなく、あくまでそれっぽくしているのみ
    html
    ins, del の html タグ形式
    • html=perline とすると行レベルでの差分も出す

    Example:

    // 前文字列 $old = 'same delete same same change '; // 後文字列 $new = 'same same append same this is changed line '; // シンプルな差分テキストを返す that(str_diff($old, $new))->isSame(' same -delete same +append same -change +this is changed line '); // html で差分を返す that(str_diff($old, $new, ['stringify' => 'html']))->isSame('same <del>delete</del> same <ins>append</ins> same <del>change</del> <ins>this is changed line</ins> '); // 行レベルの html で差分を返す that(str_diff($old, $new, ['stringify' => 'html=perline']))->isSame('same <del>delete</del> same <ins>append</ins> same <ins>this is </ins>chang<ins>ed lin</ins>e '); // raw な配列で差分を返す that(str_diff($old, $new, ['stringify' => null]))->isSame([ // 等価行('=' という記号と前後それぞれの文字列を返す(キーは行番号)) ['=', [0 => 'same'], [0 => 'same']], // 削除行('-' という記号と前の文字列を返す(キーは行番号)、後は int で行番号のみ) ['-', [1 => 'delete'], 0], // 等価行 ['=', [2 => 'same'], [1 => 'same']], // 追加行('+' という記号と後の文字列を返す(キーは行番号)、前は int で行番号のみ) ['+', 2, [2 => 'append']], // 等価行 ['=', [3 => 'same'], [3 => 'same']], // 変更行('*' という記号と前後それぞれの文字列を返す(キーは行番号)) ['*', [4 => 'change'], [4 => 'this is changed line']], ]);
    Parameter
    type name summary
    ​string|​array|​resource $xstring 元文字列
    ​string|​array|​resource $ystring 比較文字列
    ​array $options = [] オプション配列
    Return
    type summary
    ​string|​array|​null 差分テキスト。 stringify が null の場合は raw な差分配列

    [F] str_ellipsis 文字列を指定数に丸める

    文字列を指定数に丸める
    function str_ellipsis( ?string|string $string, int $width, string $trimmarker = "...", int|null $pos = null ): string

    mb_strimwidth と似ているが、省略文字の差し込み位置を $pos で指定できる。 $pos は負数が指定できる。負数の場合後ろから数えられる。 省略した場合は真ん中となる。

    Example:

    // 8文字に丸める($pos 省略なので真ん中が省略される) that(str_ellipsis('1234567890', 8, '...'))->isSame('12...890'); // 8文字に丸める($pos=1 なので1文字目から省略される) that(str_ellipsis('1234567890', 8, '...', 1))->isSame('1...7890'); // 8文字に丸める($pos=-1 なので後ろから1文字目から省略される) that(str_ellipsis('1234567890', 8, '...', -1))->isSame('1234...0');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​int $width 丸める幅
    ​string $trimmarker = "..." 省略文字列
    ​int|​null $pos = null 省略記号の差し込み位置
    Return
    type summary
    ​string 丸められた文字列

    [F] str_embed エスケープ付きで文字列を置換する

    エスケープ付きで文字列を置換する
    function str_embed( ?string|string $string, array $replacemap, string|array $enclosure = "'\"", string $escape = "\\", ?array &$replaced = null ): string

    $replacemap で from -> to 文字列を指定する。 to は文字列と配列とクロージャを受け付ける。 文字列の場合は普通に想起される動作で単純な置換となる。 配列の場合は順次置換していく。要素が足りなくなったら例外を投げる。 クロージャの場合は(from, from 内の連番, トータル置換回数)でコールバックされる。null を返すと置換されない。

    strtr と同様、最も長いキーから置換を行い、置換後の文字列は対象にならない。

    $enclosure で「特定文字に囲まれている」場合を無視することができる。 $escape で「特定文字が前にある」場合を無視することができる。

    Example:

    // 最も単純な置換 that(str_embed('a, b, c', ['a' => 'A', 'b' => 'B', 'c' => 'C']))->isSame('A, B, C'); // 最も長いキーから置換される that(str_embed('abc', ['a' => 'X', 'ab' => 'AB']))->isSame('ABc'); // 配列を渡すと「N番目の置換」が実現できる(文字列の場合は再利用される) that(str_embed('a, a, b, b', [ 'a' => 'A', // 全ての a が A になる 'b' => ['B1', 'B2'], // 1番目の b が B1, 2番目の b が B2 になる ]))->isSame('A, A, B1, B2'); // クロージャを渡すと(from, from 内の連番, トータル置換回数)でコールバックされる that(str_embed('a, a, b, b', [ 'a' => fn($src, $n, $l) => "$src,$n,$l", 'b' => fn($src, $n, $l) => null, ]))->isSame('a,0,0, a,1,1, b, b'); // 最も重要な性質として "' で囲まれていると対象にならない that(str_embed('a, "a", b, "b", b', [ 'a' => 'A', 'b' => ['B1', 'B2'], ]))->isSame('A, "a", B1, "b", B2');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​array $replacemap 置換文字列
    ​string|​array $enclosure = "'\"" 囲い文字。この文字中にいる $from, $to 文字は走査外になる
    ​string $escape = "\\" エスケープ文字。この文字が前にある $from, $to 文字は走査外になる
    ​?array &$replaced = null 置換されたペアがタプルで格納される
    Return
    type summary
    ​string 置換された文字列

    [F] str_equals 文字列比較の関数版

    文字列比較の関数版
    function str_equals( string $str1, string $str2, bool $case_insensitivity = false ): bool

    文字列以外が与えられた場合は常に false を返す。ただし __toString を実装したオブジェクトは別。

    Example:

    that(str_equals('abc', 'abc'))->isTrue(); that(str_equals('abc', 'ABC', true))->isTrue(); that(str_equals('\0abc', '\0abc'))->isTrue();
    Parameter
    type name summary
    ​string $str1 文字列1
    ​string $str2 文字列2
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​bool 同じ文字列なら true

    [F] str_exists 指定文字列を含むか返す

    指定文字列を含むか返す
    function str_exists( ?string|string $haystack, string|array $needle, bool $case_insensitivity = false, bool $and_flag = false ): bool

    Example:

    that(str_exists('abc', 'b'))->isTrue(); that(str_exists('abc', 'B', true))->isTrue(); that(str_exists('abc', ['b', 'x'], false, false))->isTrue(); that(str_exists('abc', ['b', 'x'], false, true))->isFalse();
    Parameter
    type name summary
    ​?string|​string $haystack 対象文字列
    ​string|​array $needle 調べる文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    ​bool $and_flag = false すべて含む場合に true を返すか
    Return
    type summary
    ​bool $needle を含むなら true

    [F] str_guess $string に最も近い文字列を返す

    $string に最も近い文字列を返す
    function str_guess( ?string|string $string, array $candidates, ?float &$percent = null ): string|array

    N-gram 化して類似度の高い結果を返す。 $percent で一致度を受けられる。 予め値が入った変数を渡すとその一致度以上の候補を高い順で配列で返す。

    この関数の結果(内部実装)は互換性を考慮しない。

    Example:

    // 「あいうえお」と最も近い文字列は「あいゆえに」である that(str_guess("あいうえお", [ 'かきくけこ', // マッチ度 0%(1文字もかすらない) 'ぎぼあいこ', // マッチ度約 13.1%("あい"はあるが位置が異なる) 'あいしてる', // マッチ度約 13.8%("あい"がマッチ) 'かとうあい', // マッチ度約 16.7%("あい"があり"う"の位置が等しい) 'あいゆえに', // マッチ度約 17.4%("あい", "え"がマッチ) ]))->isSame('あいゆえに'); // マッチ度30%以上を高い順に配列で返す $percent = 30; that(str_guess("destory", [ 'describe', 'destroy', 'destruct', 'destiny', 'destinate', ], $percent))->isSame([ 'destroy', 'destiny', 'destruct', ]);
    Parameter
    type name summary
    ​?string|​string $string 調べる文字列
    ​array $candidates 候補文字列配列
    ​?float &$percent = null マッチ度(%)を受ける変数
    Return
    type summary
    ​string|​array 候補の中で最も近い文字列

    [F] str_lchop 先頭の指定文字列を削ぎ落とす

    先頭の指定文字列を削ぎ落とす
    function str_lchop( ?string|string $string, ?string|string $prefix, bool $case_insensitivity = false ): string

    Example:

    // 文字列からパス文字列を削ぎ落とす $PATH = '/path/to/something'; that(str_lchop("$PATH/hoge.php", "$PATH/"))->isSame('hoge.php');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $prefix 削ぎ落とす先頭文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​string 削ぎ落とした文字列

    [F] str_patch テキストに patch を当てる

    テキストに patch を当てる
    function str_patch( ?string|string $string, ?string|string $patch, array $options = [] ): string

    diff 形式は現在のところ unified のみ。 reject の仕様はなく、ハンクが一つでも検出・適用不可なら例外を投げる。

    Example:

    $xstring = <<<HERE equal line delete line equal line HERE; $ystring = <<<HERE equal line equal line append line HERE; // xstring から ystring へのパッチ $patch = str_diff($xstring, $ystring, ['stringify' => 'unified=3']); // xstring に適用すれば ystring になる that(str_patch($xstring, $patch))->isSame($ystring); // ystring に reverse で適用すれば xstring に戻る that(str_patch($ystring, $patch, ['reverse' => true]))->isSame($xstring);
    Parameter
    type name summary
    ​?string|​string $string 文字列
    ​?string|​string $patch パッチ文字列
    ​array $options = [] オプション配列
    Return
    type summary
    ​string パッチ適用結果

    [F] str_putcsv fputcsv の文字列版(str_getcsv の put 版)

    fputcsv の文字列版(str_getcsv の put 版)
    function str_putcsv( iterable $array, string $delimiter = ",", string $enclosure = "\"", string $escape = "\\" ): string

    普通の配列を与えるとシンプルに "a,b,c" のような1行を返す。 多次元配列(2次元のみを想定)や Traversable を与えるとループして "a,b,c\nd,e,f" のような複数行を返す。

    Example:

    // シンプルな1行を返す that(str_putcsv(['a', 'b', 'c']))->isSame("a,b,c"); that(str_putcsv(['a', 'b', 'c'], "\t"))->isSame("a\tb\tc"); that(str_putcsv(['a', ' b ', 'c'], " ", "'"))->isSame("a ' b ' c"); // 複数行を返す that(str_putcsv([['a', 'b', 'c'], ['d', 'e', 'f']]))->isSame("a,b,c\nd,e,f"); that(str_putcsv((function() { yield ['a', 'b', 'c']; yield ['d', 'e', 'f']; })()))->isSame("a,b,c\nd,e,f");
    Parameter
    type name summary
    ​iterable $array 値の配列 or 値の配列の配列
    ​string $delimiter = "," フィールド区切り文字
    ​string $enclosure = "\"" フィールドを囲む文字
    ​string $escape = "\\" エスケープ文字
    Return
    type summary
    ​string CSV 文字列

    [F] str_quote 文字列をダブルクォート文字列に変換する

    文字列をダブルクォート文字列に変換する
    function str_quote( string $string, array $options = [] ): string

    文字ではうまく表現できないが、例えば「本当の改行」が \n になり、「本当のタブ文字」が \t になる。 コントロール文字は "\code" 形式のようになる。 「得られた文字列は eval すると元に戻る」とでも言えばいいか。

    制御文字をそのまま出力するとまずい状況が稀によくある(特に行指向媒体への改行文字)。 この関数を通せば php の文字列の体裁を保ったまま1行化できる。 端的に言えば var_export の文字列特化版。

    挙動は $options である程度制御可能。 各 $options は原則的に文字のマップか true を渡す(true の場合はデフォルトが使用される)。 一部、それ以外の値・型に対応しているものもある。

    escape-character
    制御文字のうち、明確なエスケープシーケンスが存在する場合はそれを使用する
    • control-character にオーバーラップするがこちらが優先される
    control-character
    00 ~ 1F+7F の制御文字を \code 形式にする
    • 文字列で "oct", "hex", "HEX" も指定できる。その場合それぞれ \oct, \xhex, \xHEX 形式になる
    special-character
    ダブルクオート内の文字列が文字列であるための変換を行う
    • 原則的にデフォルトに任せて指定すべきではない

    Example:

    // (非常に分かりにくいが)下記のように変換される that(str_quote("\$a\nb\rc\x00"))->isSame("\"\\\$a\\nb\\rc\\0\""); // 文字としての意味は一緒であり要するに表現形式の違いなので、php の世界で eval すれば元の文字列に戻る that(eval('return ' . str_quote("\$a\nb\rc\x00") . ';'))->isSame("\$a\nb\rc\x00");
    Parameter
    type name summary
    ​string $string 対象文字列
    ​array $options = [] オプション配列
    Return
    type summary
    ​string クォート文字列

    [F] str_rchop 末尾の指定文字列を削ぎ落とす

    末尾の指定文字列を削ぎ落とす
    function str_rchop( ?string|string $string, ?string|string $suffix, bool $case_insensitivity = false ): string

    Example:

    // 文字列から .php を削ぎ落とす $PATH = '/path/to/something'; that(str_rchop("$PATH/hoge.php", ".php"))->isSame("$PATH/hoge");
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​?string|​string $suffix 削ぎ落とす末尾文字列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​string 削ぎ落とした文字列

    [F] str_submap 指定文字列を置換する

    指定文字列を置換する
    function str_submap( ?string|string $subject, array $replaces, bool $case_insensitivity = false ): string

    $subject を $replaces に従って置換する。 具体的には「$replaces を 複数指定できる str_subreplace」に近い。

    strtr とは「N 番目のみ置換できる」点で異なる。 つまり、$replaces=['hoge' => [2 => 'fuga']] とすると「2 番目の 'hoge' が 'fuga' に置換される」という動作になる(0 ベース)。

    $replaces の要素に非配列を与えた場合は配列化される。 つまり $replaces = ['hoge' => 'fuga']$replaces = ['hoge' => ['fuga']] と同じ(最初のマッチを置換する)。

    $replace に空配列を与えると何もしない。 負数キーは後ろから数える動作となる。 また、置換後の文字列は置換対象にはならない。

    N 番目の検索文字列が見つからない場合は例外を投げる。 ただし、文字自体が見つからない場合は投げない。

    Example:

    // "hello, world" の l と o を置換 that(str_submap('hello, world', [ // l は0番目と2番目のみを置換(1番目は何も行われない) 'l' => [ 0 => 'L1', 2 => 'L3', ], // o は後ろから数えて1番目を置換 'o' => [ -1 => 'O', ], ]))->isSame('heL1lo, wOrL3d');
    Parameter
    type name summary
    ​?string|​string $subject 対象文字列
    ​array $replaces 読み換え配列
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​string 置換された文字列

    [F] str_subreplace 指定文字列を置換する

    指定文字列を置換する
    function str_subreplace( ?string|string $subject, ?string|string $search, array|string $replaces, bool $case_insensitivity = false ): string

    $subject 内の $search を $replaces に置換する。 str_replace とは「N 番目のみ置換できる」点で異なる。 つまり、$search='hoge', $replace=[2 => 'fuga'] とすると「2 番目の 'hoge' が 'fuga' に置換される」という動作になる(0 ベース)。

    $replace に 非配列を与えた場合は配列化される。 つまり $replaces = 'hoge'$replaces = [0 => 'hoge'] と同じ(最初のマッチを置換する)。

    $replace に空配列を与えると何もしない。 負数キーは後ろから数える動作となる。 また、置換後の文字列は置換対象にはならない。

    N 番目の検索文字列が見つからない場合は例外を投げる。 ただし、文字自体が見つからない場合は投げない。

    Example:

    // 1番目(0ベースなので2番目)の x を X に置換 that(str_subreplace('xxx', 'x', [1 => 'X']))->isSame('xXx'); // 0番目(最前列)の x を Xa に、-1番目(最後尾)の x を Xz に置換 that(str_subreplace('!xxx!', 'x', [0 => 'Xa', -1 => 'Xz']))->isSame('!XaxXz!'); // 置換結果は置換対象にならない that(str_subreplace('xxx', 'x', [0 => 'xxx', 1 => 'X']))->isSame('xxxXx');
    Parameter
    type name summary
    ​?string|​string $subject 対象文字列
    ​?string|​string $search 検索文字列
    ​array|​string $replaces 置換文字列配列(単一指定は配列化される)
    ​bool $case_insensitivity = false 大文字小文字を無視するか
    Return
    type summary
    ​string 置換された文字列

    [F] strcat 文字列結合の関数版

    文字列結合の関数版
    function strcat(?string|mixed ...$variadic): string

    Example:

    that(strcat('a', 'b', 'c'))->isSame('abc');
    Parameter
    type name summary
    ​?string|​mixed ...$variadic 結合する文字列(可変引数)
    Return
    type summary
    ​string 結合した文字列

    [F] strpos_array 複数の文字列で strpos する

    複数の文字列で strpos する
    function strpos_array( ?string|string $haystack, iterable $needles, int $offset = 0 ): array

    $needles のそれぞれの位置を配列で返す。 ただし、見つからなかった文字は結果に含まれない。

    Example:

    // 見つかった位置を返す that(strpos_array('hello world', ['hello', 'world']))->isSame([ 0 => 0, 1 => 6, ]); // 見つからない文字は含まれない that(strpos_array('hello world', ['notfound', 'world']))->isSame([ 1 => 6, ]);
    Parameter
    type name summary
    ​?string|​string $haystack 対象文字列
    ​iterable $needles 位置を取得したい文字列配列
    ​int $offset = 0 開始位置
    Return
    type summary
    ​array $needles それぞれの位置配列

    [F] strpos_closest 指定位置から左右どちらかの探索を行う

    指定位置から左右どちらかの探索を行う
    function strpos_closest( string $haystack, string $needle, ?int $offset = null, int $nth = 1 ): ?int

    一つ前/後の指定文字の位置を返す 端的に言えば strpo/strposr の自動使い分け+範囲外でもエラーにならない版。

    $nth は負数で左探索、正数で右探索だが、スキップ数も兼ねる。

    Example:

    // +0123456789A1234567 // -7654321A9876543210 $string = 'hello hello hello'; // 0文字目から右探索すれば0文字目が引っかかる that(strpos_closest($string, 'hello', 0, +1))->isSame(0); // ↑のスキップ(2つ目)版 that(strpos_closest($string, 'hello', 0, +2))->isSame(6); // 5文字目から右探索すれば6文字目が引っかかる that(strpos_closest($string, 'hello', 5, +1))->isSame(6); // ↑のスキップ(2つ目)版 that(strpos_closest($string, 'hello', 5, +2))->isSame(12); // 8文字目から右探索すれば12文字目が引っかかる(2個目の hello の途中なので3個目の hello から引っかかる) that(strpos_closest($string, 'hello', 8, +1))->isSame(12); // ↑のスキップ(2つ目)版 that(strpos_closest($string, 'hello', 8, +2))->isSame(null); // 0文字目から左探索しても見つからない that(strpos_closest($string, 'hello', 0, -1))->isSame(null); // 5文字目から左探索すれば0文字目が引っかかる that(strpos_closest($string, 'hello', 5, -1))->isSame(0); // 8文字目から左探索すれば0文字目が引っかかる(2個目の hello の途中なので1個目の hello から引っかかる) that(strpos_closest($string, 'hello', 8, -1))->isSame(0); // 11文字目から左探索すれば6文字目が引っかかる that(strpos_closest($string, 'hello', 11, -1))->isSame(6); // ↑のスキップ(2つ目)版 that(strpos_closest($string, 'hello', 11, -2))->isSame(0); // 範囲外でもエラーにならない that(strpos_closest($string, 'hello', -999, +1))->isSame(0); // 「数直線のはるか左方から探索を始めて 0 文字目で見つかった」のようなイメージ that(strpos_closest($string, 'hello', +999, -1))->isSame(12); // 「数直線のはるか右方から探索を始めて 12 文字目で見つかった」のようなイメージ
    Parameter
    type name summary
    ​string $haystack 対象文字列
    ​string $needle 位置を取得したい文字列
    ​?int $offset = null 開始位置。 null を渡すと $nth の方向に応じて自動で定まる
    ​int $nth = 1 左右指定兼スキップ数(正数で右探索、負数で左探索)
    Return
    type summary
    ​?int 見つかった位置

    [F] strpos_escaped エスケープを考慮して strpos する

    エスケープを考慮して strpos する
    function strpos_escaped( ?string|string $haystack, string|array $needle, int $offset = 0, string $escape = "\\", ?string &$found = null ): ?int

    文字列中のエスケープ中でない生の文字を検索する。 例えば "abc\nxyz" という文字列で "n" という文字は存在しないとみなす。 "\n" は改行のエスケープシーケンスであり、 "n" という文字ではない(エスケープシーケンスとして "n" を流用しているだけ)。 逆に "\\n" はバックスラッシュと "n" という文字であり "n" が存在する。 簡単に言えば「直前にバックスラッシュがある場合はヒットしない strpos」である。 バックスラッシュは $escape 引数で指定可能。

    $needle 自体にエスケープ文字を含む場合、反対の意味で検索する。 つまり、「直前にバックスラッシュがある場合のみヒットする strpos」になる。

    $offset 引数を指定するとその位置から探索を開始するが、戻り読みはしないのでエスケープ文字の真っ只中を指定する場合は注意。 例えば "\n" は改行文字だけであるが、offset に 1 に指定して "n" を探すとマッチする。

    Example:

    # 分かりにくいので \ ではなく % をエスケープ文字とする $defargs = [0, '%']; // これは null である("%d" という文字の列であるため "d" という文字は存在しない) that(strpos_escaped('%d', 'd', ...$defargs))->isSame(null); // これは 2 である("%" "d" という文字の列であるため(d の前の % は更にその前の % に呑まれておりメタ文字ではない)) that(strpos_escaped('%%d', 'd', ...$defargs))->isSame(2); // これは 0 である(% をつけて検索するとそのエスケープシーケンス的なものそのものを探すため) that(strpos_escaped('%d', '%d', ...$defargs))->isSame(0); // これは null である("%" "d" という文字の列であるため "%d" という文字は存在しない) that(strpos_escaped('%%d', '%d', ...$defargs))->isSame(null); // これは 2 である("%" "%d" という文字の列であるため) that(strpos_escaped('%%%d', '%d', ...$defargs))->isSame(2);
    Parameter
    type name summary
    ​?string|​string $haystack 対象文字列
    ​string|​array $needle 探す文字
    ​int $offset = 0 開始位置
    ​string $escape = "\\" エスケープ文字
    ​?string &$found = null 見つかった文字が格納される
    Return
    type summary
    ​?int 見つかった位置

    [F] strpos_quoted クオートを考慮して strpos する

    クオートを考慮して strpos する
    function strpos_quoted( ?string|string $haystack, string|iterable $needle, int $offset = 0, string|array $enclosure = "'\"", string $escape = "\\", ?string &$found = null ): ?int

    Example:

    // クオート中は除外される that(strpos_quoted('hello "this" is world', 'is'))->isSame(13); // 開始位置やクオート文字は指定できる(5文字目以降の \* に囲まれていない hoge の位置を返す) that(strpos_quoted('1:hoge, 2:*hoge*, 3:hoge', 'hoge', 5, '*'))->isSame(20);
    Parameter
    type name summary
    ​?string|​string $haystack 対象文字列
    ​string|​iterable $needle 位置を取得したい文字列
    ​int $offset = 0 開始位置
    ​string|​array $enclosure = "'\"" 囲い文字。この文字中にいる $from, $to 文字は走査外になる
    ​string $escape = "\\" エスケープ文字。この文字が前にある $from, $to 文字は走査外になる
    ​?string &$found = null $needle の内、見つかった文字列が格納される
    Return
    type summary
    ​?int $needle の位置

    [F] strposr デフォで左探索な strrpos

    デフォで左探索な strrpos
    function strposr( string $haystack, string $needle, ?int $offset = null ): int|false

    個人的に strrpos の offset の挙動が分かりにくい。

    offset>=0
    先頭から offset スキップ+右探索
    offset< 0
    末尾から -offset スキップ+左探索

    r と銘打っているんだから offset に依らずにデフォで左探索して欲しい。 特に offset 未指定だと 0 なので「先頭から末尾まで読んで最後に見つかった場所を返す」という非常に非効率的なことになっている(実際の実装は違うようだけど)。 さらに「単純に50文字目から左探索したい」だけなのに offset が気持ち悪いことになる(50 - strlen($string))。 要するに https://www.php.net/manual/ja/function.strrpos.php#76447 これ。

    offset で検索文字列の途中に飛び込んだ場合は見つからないとみなす(strrpos は見つかったとみなす)。 この挙動は説明しにくいので Example を参照。

    Example:

    // +0123456789A1234567 // -7654321A9876543210 $string = 'hello hello hello'; // offset を省略すると末尾から左探索になる that(strposr($string, 'hello'))->isSame(12); // 標準の文字列関数のように負数で後ろからの探索になる that(strposr($string, 'hello', -1))->isSame(6); // 0文字目から左探索しても見つからない that(strposr($string, 'hello', 0))->isSame(false); // 5文字目から左探索すれば0文字目が引っかかる that(strposr($string, 'hello', 5))->isSame(0); // 13文字目から左探索すれば6文字目が引っかかる(13文字目は3個目の hello の途中なので2個目の hello から引っかかる) that(strposr($string, 'hello', 13))->isSame(6); // この動作は strrpos だと異なる(途中文字列も拾ってしまう) that(strrpos($string, 'hello', -4))->isSame(12); // そもそも strrpos は負数指定しないと右探索なので使いにくくてしょうがない that(strrpos($string, 'hello', 5))->isSame(12);
    Parameter
    type name summary
    ​string $haystack 対象文字列
    ​string $needle 位置を取得したい文字列
    ​?int $offset = null 開始位置。 null を渡すと末尾からの探索になる
    Return
    type summary
    ​int|​false 見つかった位置

    [F] strrstr 文字列が最後に現れる位置以前を返す

    文字列が最後に現れる位置以前を返す
    function strrstr( ?string|string $haystack, ?string|string $needle, bool $after_needle = true ): ?string

    strstr の逆のイメージで文字列を後ろから探索する動作となる。 strstr の動作は「文字列を前から探索して指定文字列があったらそれ以後を返す」なので、 その逆の動作の「文字列を後ろから探索して指定文字列があったらそれ以前を返す」という挙動を示す。

    strstr の「needle が文字列でない場合は、 それを整数に変換し、その番号に対応する文字として扱います」は直感的じゃないので踏襲しない。 (全体的にこの動作をやめよう、という RFC もあったはず)。

    第3引数の意味合いもデフォルト値も逆になるので、単純に呼べばよくある「指定文字列より後ろを(指定文字列を含めないで)返す」という動作になる。

    Example:

    // パス中の最後のディレクトリを取得 that(strrstr("path/to/1:path/to/2:path/to/3", ":"))->isSame('path/to/3'); // $after_needle を false にすると逆の動作になる that(strrstr("path/to/1:path/to/2:path/to/3", ":", false))->isSame('path/to/1:path/to/2:'); // (参考)strrchr と違い、文字列が使えるしその文字そのものは含まれない that(strrstr("A\r\nB\r\nC", "\r\n"))->isSame('C');
    Parameter
    type name summary
    ​?string|​string $haystack 調べる文字列
    ​?string|​string $needle 検索文字列
    ​bool $after_needle = true $needle より後ろを返すか
    Return
    type summary
    ​?string

    [F] strtr_escaped エスケープを考慮して strtr する

    エスケープを考慮して strtr する
    function strtr_escaped( ?string|string $string, array $replace_pairs, string $escape = "\\" ): string

    「エスケープ」についての詳細は strpos_escaped を参照。

    $replace_pairs は [from => to] な配列を指定する。 to がクロージャの場合はキーとオフセットでコールバックされる。

    strtr と同様、最も長いキーから置換を行い、置換後の文字列は対象にならない。

    Example:

    # 分かりにくいので \ ではなく % をエスケープ文字とする that(strtr_escaped('XYZ ab %% %s', [ 'ab' => 'AB', // 2. 1 で置換された文字は対象にならない 'A' => '%a', // 使われない 'Z' => '%z', // 使われない '%%' => 'p', // 普通に置換される 's' => 'S', // エスケープが対象なので置換されない(%s は文字 "s" ではない(\n が文字 "n" ではないのと同じ)) 'XYZ' => 'abc', // 1. 後ろにあるがまず置換される ], '%'))->isSame('abc AB p %s');
    Parameter
    type name summary
    ​?string|​string $string 対象文字列
    ​array $replace_pairs 置換するペア
    ​string $escape = "\\" エスケープ文字
    Return
    type summary
    ​string 置換された文字列

    [N] ryunosuke\Functions\Package\syntax\

    [F] functions

    [F] blank_if 値が空なら null を返す

    値が空なら null を返す
    function blank_if( mixed $var, mixed $default = null ): mixed

    is_empty($value) ? $value : null とほぼ同じ。 言ってしまえば「falsy な値を null に変換する」とも言える。

    ここでいう falsy とは php 標準の empty ではなく本ライブラリの is_empty であることに留意("0" は空ではない)。 さらに利便性のため 0, 0.0 も空ではない判定をする(strpos や array_search などで「0 は意味のある値」という事が多いので)。 乱暴に言えば「仮に文字列化したとき、情報量がゼロ」が falsy になる。

    • $var ?: 'default' で十分なんだけど "0" が…」
    • $var ?? 'default' で十分なんだけど false が…」

    という状況はまれによくあるはず。

    ?? との親和性のため null を返す動作がデフォルトだが、そのデフォルト値は引数で渡すこともできる。 用途は Example を参照。

    Example:

    // falsy な値は null を返すので null 合体演算子でデフォルト値が得られる that(blank_if(null) ?? 'default')->isSame('default'); that(blank_if('') ?? 'default')->isSame('default'); // falsy じゃない値の場合は引数をそのまま返すので null 合体演算子には反応しない that(blank_if(0) ?? 'default')->isSame(0); // 0 は空ではない that(blank_if('0') ?? 'default')->isSame('0'); // "0" は空ではない that(blank_if(1) ?? 'default')->isSame(1); that(blank_if('X') ?? 'default')->isSame('X'); // 第2引数で返る値を指定できるので下記も等価となる。ただし、php の仕様上第2引数が必ず評価されるため、関数呼び出しなどだと無駄な処理となる that(blank_if(null, 'default'))->isSame('default'); that(blank_if('', 'default'))->isSame('default'); that(blank_if(0, 'default'))->isSame(0); that(blank_if('0', 'default'))->isSame('0'); that(blank_if(1, 'default'))->isSame(1); that(blank_if('X', 'default'))->isSame('X'); // 第2引数の用途は少し短く書けることと演算子の優先順位のつらみの回避程度(`??` は結構優先順位が低い。下記を参照) that(0 < blank_if(null) ?? 1)->isFalse(); // (0 < null) ?? 1 となるので false that(0 < blank_if(null, 1))->isTrue(); // 0 < 1 となるので true that(0 < (blank_if(null) ?? 1))->isTrue(); // ?? で同じことしたいならこのように括弧が必要 # ここから下は既存言語機構との比較(愚痴っぽいので読まなくてもよい) // エルビス演算子は "0" にも反応するので正直言って使いづらい(php における falsy の定義は広すぎる) that(null ?: 'default')->isSame('default'); that('' ?: 'default')->isSame('default'); that(1 ?: 'default')->isSame(1); that('0' ?: 'default')->isSame('default'); // こいつが反応してしまう that('X' ?: 'default')->isSame('X'); // 逆に null 合体演算子は null にしか反応しないので微妙に使い勝手が悪い(php の標準関数が false を返したりするし) that(null ?? 'default')->isSame('default'); // こいつしか反応しない that('' ?? 'default')->isSame(''); that(1 ?? 'default')->isSame(1); that('0' ?? 'default')->isSame('0'); that('X' ?? 'default')->isSame('X'); // 恣意的な例だが、 array_search は false も 0 も返し得るので ?: は使えない。 null を返すこともないので ?? も使えない(エラーも吐かない) that(array_search('a', ['a', 'b', 'c']) ?: 'default')->isSame('default'); // 見つかったのに 0 に反応するので 'default' になってしまう that(array_search('x', ['a', 'b', 'c']) ?? 'default')->isSame(false); // 見つからないので 'default' としたいが false になってしまう // 要するに単に「見つからなかった場合に 'default' としたい」だけなんだが、下記のようにめんどくさいことをせざるを得ない that(array_search('x', ['a', 'b', 'c']) === false ? 'default' : array_search('x', ['a', 'b', 'c']))->isSame('default'); // 3項演算子で2回呼ぶ that(($tmp = array_search('x', ['a', 'b', 'c']) === false) ? 'default' : $tmp)->isSame('default'); // 一時変数を使用する(あるいは if 文) // このように書きたかった that(blank_if(array_search('x', ['a', 'b', 'c'])) ?? 'default')->isSame('default'); // null 合体演算子版 that(blank_if(array_search('x', ['a', 'b', 'c']), 'default'))->isSame('default'); // 第2引数版
    Parameter
    type name summary
    ​mixed $var 判定する値
    ​mixed $default = null 空だった場合のデフォルト値
    Return
    type summary
    ​mixed 空なら $default, 空じゃないなら $var をそのまま返す

    [F] cast php の型変換に準じてキャストする

    php の型変換に準じてキャストする
    function cast( mixed $value, string $type, mixed $default = null ): mixed

    「php の型変換」とは strict_type=0 の時の暗黙の変換を指す((type)等のキャストではない)。 eval で呼び出して判定するため、決して $type に外部入力を渡してはならない。

    この関数を使うシチュエーションはほぼない。 呼び先のためならそれを普通に呼べば同じエラーになるし、用途が分かっているなら通常のキャストで十分。 「呼び先が型宣言されていない」とか「numeric であることを担保したい」とか、限られた状況でしか使えないし使うべきではない。 通常の(type)キャストが強すぎる(特に int)のため、「エラーになってくれる弱いキャスト」のようなイメージ。

    Example:

    # 下記のように変換される that(cast("1", 'int'))->isSame(1); that(cast(1, 'string'))->isSame('1'); that(cast(1, 'int|string'))->isSame(1); that(cast([], 'array|ArrayAccess'))->isSame([]); that(cast($ao = new \ArrayObject(), 'ArrayAccess&Countable'))->isSame($ao); # 下記はすべて TypeError になる // cast("hoge", 'int'); // 非数値文字列 は int に変換できない // cast([], 'string'); // array は string に変換できない // cast(new \stdClass(), 'bool'); // object は bool に変換できない
    Parameter
    type name summary
    ​mixed $value 取得される配列・オブジェクト
    ​string $type 型文字列
    ​mixed $default = null 失敗したときのデフォルト値(null も立派な値なので例外を飛ばすためには未指定時にしなければならない)
    Return
    type summary
    ​mixed キャストされた値

    [F] instance_of instanceof 構文の関数版

    instanceof 構文の関数版
    function instance_of( T $object, string|object $class ): ?T

    ただし、bool ではなく ?object を返す。 つまり「instanceof が true ならそのまま、false なら null」を返す。 これは ?-> との親和性を考慮している。

    Example:

    // 実質的に下記は同じ $object = new \Exception('message'); that(($object instanceof \Exception ? $object : null)?->getMessage())->is('message'); that(instance_of($object, \Exception::class)?->getMessage())->is('message'); $object = new \stdClass(); that(($object instanceof \Exception ? $object : null)?->getMessage())->isNull(); that(instance_of($object, \Exception::class)?->getMessage())->isNull(); // Exception ではないが null でもないので下記のようにはできない // $object?->getMessage();
    Parameter
    type name summary
    T $object 調べるオブジェクト
    ​string|​object $class クラス名
    Return
    type summary
    ​?T $object が $class のインスタンスなら $object, そうでなければ null

    [F] try_catch try ~ catch 構文の関数版

    try ~ catch 構文の関数版
    function try_catch( callable $try, ?callable $catch = null, mixed ...$variadic ): \Exception|mixed

    例外機構構文が冗長なことがまれによくあるはず。

    Example:

    // 例外が飛ばない場合は平和極まりない $try = function ($a, $b, $c) {return [$a, $b, $c];}; that(try_catch($try, null, 1, 2, 3))->isSame([1, 2, 3]); // 例外が飛ぶ場合は特殊なことをしなければ例外オブジェクトが返ってくる $try = function () {throw new \Exception('tried');}; that(try_catch($try)->getMessage())->isSame('tried');
    Parameter
    type name summary
    ​callable $try try ブロッククロージャ
    ​?callable $catch = null catch ブロッククロージャ
    ​mixed ...$variadic $try に渡る引数
    Return
    type summary
    Exception|​mixed 例外が飛ばなかったら $try ブロックの返り値、飛んだなら $catch の返り値(デフォルトで例外オブジェクト)

    [F] try_catch_finally try ~ catch ~ finally 構文の関数版

    try ~ catch ~ finally 構文の関数版
    function try_catch_finally( callable $try, ?callable $catch = null, ?callable $finally = null, mixed ...$variadic ): \Exception|mixed

    例外機構構文が冗長なことがまれによくあるはず。

    Example:

    $finally_count = 0; $finally = function () use (&$finally_count) {$finally_count++;}; // 例外が飛ぼうと飛ぶまいと $finally は実行される $try = function ($a, $b, $c) {return [$a, $b, $c];}; that(try_catch_finally($try, null, $finally, 1, 2, 3))->isSame([1, 2, 3]); that($finally_count)->isSame(1); // 呼ばれている // 例外を投げるが、 $catch で握りつぶす $try = function () {throw new \Exception('tried');}; that(try_catch_finally($try, null, $finally, 1, 2, 3)->getMessage())->isSame('tried'); that($finally_count)->isSame(2); // 呼ばれている
    Parameter
    type name summary
    ​callable $try try ブロッククロージャ
    ​?callable $catch = null catch ブロッククロージャ
    ​?callable $finally = null finally ブロッククロージャ
    ​mixed ...$variadic $try に渡る引数
    Return
    type summary
    Exception|​mixed 例外が飛ばなかったら $try ブロックの返り値、飛んだなら $catch の返り値(デフォルトで例外オブジェクト)

    [F] try_close try ~ finally 構文の close 特化版

    try ~ finally 構文の close 特化版
    function try_close( $callback, object|resource|array ...$resources ): mixed

    $try の呼び出し完了後に必ず close するようにする。 C# の using, java の try-with-resource みたいなもの。

    $resources 引数は [filename, mode], [filename => mode] のような配列を受け付け、中で fopen される。

    resource 型を想定しているが、オブジェクトの場合は close, free, dispose あたりを試みる。 このメソッド候補は互換性を問わず変更されることがある。 そもそも、stream 以外のリソースや完全不透明クラスはオマケのようなもので完全サポートはしないし互換性も考慮しない。

    close で例外が飛んだ場合は握りつぶされる。 この握りつぶした例外を取得する方法は今のところ存在しない。

    Example:

    that(try_close(fn($fp, $string) => fwrite($fp, $string), $tmpfile = tmpfile(), 'test'))->is(4); that(gettype($tmpfile))->is('resource (closed)'); // 閉じている
    Parameter
    type name summary
    $callback
    ​object|​resource|​array ...$resources $try に渡る引数
    Return
    type summary
    ​mixed $try ブロックの返り値

    [F] try_finally try ~ finally 構文の関数版

    try ~ finally 構文の関数版
    function try_finally( callable $try, ?callable $finally = null, mixed ...$variadic ): \Exception|mixed

    例外は投げっぱなす。例外機構構文が冗長なことがまれによくあるはず。

    Example:

    $finally_count = 0; $finally = function () use (&$finally_count) {$finally_count++;}; // 例外が飛ぼうと飛ぶまいと $finally は実行される $try = function ($a, $b, $c) {return [$a, $b, $c];}; that(try_finally($try, $finally, 1, 2, 3))->isSame([1, 2, 3]); that($finally_count)->isSame(1); // 呼ばれている // 例外は投げっぱなすが、 $finally は実行される $try = function () {throw new \Exception('tried');}; try {try_finally($try, $finally, 1, 2, 3);} catch(\Exception $e){} that($finally_count)->isSame(2); // 呼ばれている
    Parameter
    type name summary
    ​callable $try try ブロッククロージャ
    ​?callable $finally = null finally ブロッククロージャ
    ​mixed ...$variadic $try に渡る引数
    Return
    type summary
    Exception|​mixed 例外が飛ばなかったら $try ブロックの返り値、飛んだなら $catch の返り値(デフォルトで例外オブジェクト)

    [F] try_null 例外を握りつぶす try 構文

    例外を握りつぶす try 構文
    function try_null( callable $try, mixed ...$variadic ): mixed

    例外機構構文が冗長なことがまれによくあるはず。

    Example:

    // 例外が飛ばない場合は平和極まりない $try = function ($a, $b, $c) {return [$a, $b, $c];}; that(try_null($try, 1, 2, 3))->isSame([1, 2, 3]); // 例外が飛ぶ場合は null が返ってくる $try = function () {throw new \Exception('tried');}; that(try_null($try))->isSame(null);
    Parameter
    type name summary
    ​callable $try try ブロッククロージャ
    ​mixed ...$variadic $try に渡る引数
    Return
    type summary
    ​mixed 例外が飛ばなかったら $try ブロックの返り値、飛んだなら null

    [F] try_return 例外が飛んだら例外オブジェクトを返す

    例外が飛んだら例外オブジェクトを返す
    function try_return( callable $try, mixed ...$variadic ): mixed

    例外機構構文が冗長なことがまれによくあるはず。

    Example:

    // 例外が飛ばない場合は平和極まりない $try = function ($a, $b, $c) {return [$a, $b, $c];}; that(try_return($try, 1, 2, 3))->isSame([1, 2, 3]); // 例外が飛ぶ場合は例外オブジェクトが返ってくる $try = function () {throw new \Exception('tried');}; that(try_return($try))->IsInstanceOf(\Exception::class);
    Parameter
    type name summary
    ​callable $try try ブロッククロージャ
    ​mixed ...$variadic $try に渡る引数
    Return
    type summary
    ​mixed 例外が飛ばなかったら $try ブロックの返り値、飛んだなら null

    [N] ryunosuke\Functions\Package\url\

    [F] functions

    [F] base62_decode 数値とアルファベットで base62 デコードする

    数値とアルファベットで base62 デコードする
    function base62_decode(string $string): string

    対で使うと思うので base62_encode を参照。

    Parameter
    type name summary
    ​string $string base62 文字列
    Return
    type summary
    ​string 変換元文字列

    [F] base62_encode 数値とアルファベットで base62 エンコードする

    数値とアルファベットで base62 エンコードする
    function base62_encode(string $string): string

    あくまでエンコードであって引数は文字列として扱う。つまり数値の基数変換ではない。

    base64 と違い、下記の特徴がある。

    • =パディングなし
    • 記号が入らない(完全に URL セーフ)
    • ASCII 順(元の推移律が維持される)

    変換効率もあまり変わらないため、文字列が小さい間はほぼ base64_encode の上位互換に近い。 ただし gmp が入っていないと猛烈に遅い。

    Example:

    that(base62_encode('abcdefg'))->isSame('21XiSSifQN'); that(base62_decode('21XiSSifQN'))->isSame('abcdefg');
    Parameter
    type name summary
    ​string $string 変換元文字列
    Return
    type summary
    ​string base62 文字列

    [F] base64url_decode url safe な base64_decode

    url safe な base64_decode
    function base64url_decode(string $string): string

    対で使うと思うので base64_encode を参照。

    Parameter
    type name summary
    ​string $string base64url 文字列
    Return
    type summary
    ​string 変換元文字列

    [F] base64url_encode url safe な base64_encode

    url safe な base64_encode
    function base64url_encode(string $string): string

    れっきとした RFC があるのかは分からないが '+' => '-', '/' => '_' がデファクトだと思うのでそのようにしてある。 パディングの = も外す。

    Parameter
    type name summary
    ​string $string 変換元文字列
    Return
    type summary
    ​string base64url 文字列

    [F] dataurl_decode DataURL をデコードする

    DataURL をデコードする
    function dataurl_decode( string $url, array &$metadata = [] ): ?string

    Example:

    that(dataurl_decode("data:text/plain;charset=US-ASCII,hello%2C%20world"))->isSame('hello, world'); that(dataurl_decode("data:text/plain;charset=US-ASCII;base64,aGVsbG8sIHdvcmxk", $metadata))->isSame('hello, world'); that($metadata)->is([ "mimetype" => "text/plain", "charset" => "US-ASCII", "base64" => true, ]);
    Parameter
    type name summary
    ​string $url DataURL
    ​array &$metadata = [] スキームのメタ情報が格納される
    Return
    type summary
    ​?string 元データ。失敗時は null

    [F] dataurl_encode DataURL をエンコードする

    DataURL をエンコードする
    function dataurl_encode( string $data, array $metadata = [] ): string

    $metadata で mimetype や エンコード等を指定できる。 指定されていない場合、自動検出して埋め込まれる。

    mimetype(?string)
    特筆無し
    charset(?string)
    自動検出は mime 名になる。明示指定はそのまま埋め込まれる
    base64(?bool)
    true:base64encode, false:urlencode, null: raw
    • null の raw はスキームとしては base64 となる。つまり既に base64 の文字列が手元にある場合(変換したくない場合)に指定する

    Example:

    that(dataurl_encode("hello, world", ['base64' => false]))->isSame("data:text/plain;charset=US-ASCII,hello%2C%20world"); that(dataurl_encode("hello, world", ['mimetype' => 'text/csv', 'charset' => 'hoge']))->isSame("data:text/csv;charset=hoge;base64,aGVsbG8sIHdvcmxk");
    Parameter
    type name summary
    ​string $data エンコードするデータ
    ​array $metadata = [] エンコードオプション
    Return
    type summary
    ​string DataURL

    [F] formdata_build multipart/formdata の構築

    multipart/formdata の構築
    function formdata_build( array $formdata, ?string &$boundary = null, ?\Closure $encoder = null ): string

    $boundary 未指定時はランダム文字列が生成され、衝突した場合は無限にリトライされる。 SplFileInfo はファイルとみなされるが $encoder を指定すれば CURLFile なども活用可能。

    Example:

    $file = sys_get_temp_dir() . '/upload.txt'; file_put_contents($file, 'plain'); $boundary = 'hogefugapiyo'; that(formdata_build([ 'n' => ['e' => ['s' => ['t' => 'nest']]], 'f' => new \SplFileInfo($file), ], $boundary))->is(strtr(<<<FORMDATA --hogefugapiyo Content-Disposition: form-data; name="n[e][s][t]" nest --hogefugapiyo Content-Disposition: form-data; name="f"; filename="upload.txt" Content-Type: text/plain plain --hogefugapiyo-- FORMDATA, ["\n" => "\r\n"]));
    Parameter
    type name summary
    ​array $formdata フォームデータ配列
    ​?string &$boundary = null バウンダリ文字列初期値兼レシーバ引数
    ​?Closure $encoder = null 値のエンコーダだが実質的にファイルの検出に使う(デフォルトでは SplFileInfo がファイルと認識される)
    Return
    type summary
    ​string フォームデータ文字列

    [F] formdata_parse multipart/formdata のパース

    multipart/formdata のパース
    function formdata_parse( string $formdata, ?string $boundary = null, ?\Closure $decoder = null ): array

    Example:

    $data = formdata_parse(<<<FORMDATA --hogefugapiyo Content-Disposition: form-data; name="n[e][s][t]" nest --hogefugapiyo Content-Disposition: form-data; name="f"; filename="upload.txt" Content-Type: text/plain plain --hogefugapiyo-- FORMDATA); that($data['n']['e']['s']['t'])->is('nest'); that($data['f'])->isInstanceOf(\SplFileInfo::class);
    Parameter
    type name summary
    ​string $formdata フォームデータ文字列
    ​?string $boundary = null バウンダリ文字列。省略時は1行目から推測する
    ​?Closure $decoder = null 値のデコーダだが実質的にファイルの検出に使う(デフォルトでは一時ファイルの SplFileInfo で返す)
    Return
    type summary
    ​array フォームデータ配列

    [F] query_build 数値キーを削除する http_build_query

    数値キーを削除する http_build_query
    function query_build( array|object $data, string|int|null $numeric_prefix = null, string|null $arg_separator = null, int $encoding_type = PHP_QUERY_RFC1738, string[]|string|null $brackets = null ): string

    php の世界において配列のクエリ表現は var[]=1&var[]=2 で事足りる。 しかし http_build_query では数値キーでも必ず var[0]=1&var[1]=2 になる。 それはそれで正しいし、他言語との連携が必要な場合はそうせざるを得ない状況もあるが、単純に php だけで配列を表したい場合は邪魔だし文字長が長くなる。 この関数を使うと数値キーを削除し、var[]=1&var[]=2 のようなクエリ文字列を生成できる。

    シグネチャは http_build_query と同じで、 $numeric_prefix に数値的文字列を与えたときのみ動作が変化する。 ($numeric_prefix の意味を考えればこの引数に数値的文字列を与える意味は皆無だろうので流用している)。

    • 1 を与えると最前列を残して [] (%5B%5D) が置換される
    • 2 を与えると最前列とその右を残して [] (%5B%5D) が置換される
    • 要するに正数を与えると「abs(n) 個を残して [] (%5B%5D) を置換する」という指定になる
    • -1 を与えると最後尾の [] (%5B%5D) が置換される
    • -2 を与えると最後尾とその左の [] (%5B%5D) が置換される
    • 要するに負数を与えると「右から abs(n) 個の [] (%5B%5D) を置換する」という指定になる

    この仕様は v[][]=1&v[][]=2 のようなときにおいしくないためである。 これは $v=[[1], [2]] のような値になるが、この場合 $v=[[1, 2]] という値が欲しい、という事が多い。 そのためには v[0][]=1&v[0][]=2 のようにする必要があるための数値指定である。

    $brackets で配列ブラケット文字を指定できるが、現実的には下記の3択だろう。

    ['%5B','%5D']
    デフォルトのパーセントエンコーディング文字
    ['[', ']']
    [] のままにする(ブラケットは必ずしもパーセントエンコーディングが必須ではない)
    ['', '']
    ブラケットを削除する(他言語のために配列パラメータを抑止したいことがある)
    Parameter
    type name summary
    ​array|​object $data クエリデータ
    ​string|​int|​null $numeric_prefix = null 数値キープレフィックス
    ​string|​null $arg_separator = null クエリセパレータ
    ​int $encoding_type = PHP_QUERY_RFC1738 エンコードタイプ
    ​string[]|​string|​null $brackets = null 配列ブラケット文字
    Return
    type summary
    ​string クエリ文字列

    [F] query_parse parse_str の返り値版

    parse_str の返り値版
    function query_parse( string $query, ?string $arg_separator = null, ?int $encoding_type = null ): array

    標準の parse_str は参照で受ける謎シグネチャなのでそれを返り値に変更したもの。 と同時に parse_str はドットやスペースをアンダースコアに置換するため、それを避ける独自実装がある。 $arg_separator や $encoding_type を指定すると独自実装で動きかつその引数の挙動でパースされる。

    Example:

    // 普通に使えばネイティブの返り値版 that(query_parse('a.b=ab&x[y][z]=xyz'))->is([ 'a_b' => 'ab', 'x' => ['y' => ['z' => 'xyz']], ]); // パラメータを渡せば独自実装(& 以外を指定できたり . を維持できたりする) that(query_parse('a.b=ab|x[y][z]=xyz', '|'))->is([ 'a.b' => 'ab', 'x' => ['y' => ['z' => 'xyz']], ]);
    Parameter
    type name summary
    ​string $query クエリ文字列
    ​?string $arg_separator = null クエリ文字列
    ​?int $encoding_type = null クエリ文字列
    Return
    type summary
    ​array クエリのパース結果配列

    [F] uri_build parse_uri の逆

    parse_uri の逆
    function uri_build( array $parts, array $options = [] ): string

    URI のパーツを与えると URI として構築する。 パーツは不完全でも良い。例えば scheme を省略すると "://" すら付かない URI が生成される。

    "query" パートだけは配列が許容される。その場合クエリ文字列に変換される。

    Example:

    // 完全指定 that(uri_build([ 'scheme' => 'http', 'user' => 'user', 'pass' => 'pass', 'host' => 'localhost', 'port' => '80', 'path' => '/path/to/file', 'query' => ['id' => 1], 'fragment' => 'hash', ]))->isSame('http://user:pass@localhost:80/path/to/file?id=1#hash'); // 一部だけ指定 that(uri_build([ 'scheme' => 'http', 'host' => 'localhost', 'path' => '/path/to/file', 'fragment' => 'hash', ]))->isSame('http://localhost/path/to/file#hash');
    Parameter
    type name summary
    ​array $parts URI の各パーツ配列
    ​array $options = [] オプション
    Return
    type summary
    ​string URI 文字列

    [F] uri_parse parse_url の仕様を少しいじったもの

    parse_url の仕様を少しいじったもの
    function uri_parse( string $uri, array|string $default = [] ): array

    parse_url とは下記が異なる。

    • "単一文字列" はホスト名とみなされる(parse_url はパスとみなされる)
    • パートがなくてもキー自体は生成される(そしてその値は $default で指定したもの)
    • query は配列で返す(parse_str される)
    • パート値をスカラー値で返すことはできない(必ず8要素の配列を返す)

    Example:

    // 完全指定 that(uri_parse('http://user:pass@localhost:80/path/to/file?id=1#hash'))->is([ 'scheme' => 'http', 'user' => 'user', 'pass' => 'pass', 'host' => 'localhost', 'port' => '80', 'path' => '/path/to/file', 'query' => ['id' => 1], 'fragment' => 'hash', ]); // デフォルト値つき that(uri_parse('localhost/path/to/file', [ 'scheme' => 'http', // scheme のデフォルト値 'user' => 'user', // user のデフォルト値 'port' => '8080', // port のデフォルト値 'host' => 'hoge', // host のデフォルト値 ]))->is([ 'scheme' => 'http', // scheme はないのでデフォルト値が使われている 'user' => 'user', // user はないのでデフォルト値が使われている 'pass' => '', 'host' => 'localhost', // host はあるのでデフォルト値が使われていない 'port' => '8080', // port はないのでデフォルト値が使われている 'path' => '/path/to/file', 'query' => [], 'fragment' => '', ]);
    Parameter
    type name summary
    ​string $uri パースする URI
    ​array|​string $default = [] $uri に足りないパーツがあった場合のデフォルト値。文字列を与えた場合はそのパース結果がデフォルト値になる
    Return
    type summary
    ​array URI の各パーツ配列

    [N] ryunosuke\Functions\Package\utility\

    [F] functions

    [F] benchmark 簡易ベンチマークを取る

    簡易ベンチマークを取る
    function benchmark( array|callable $suite, array $args = [], int $millisec = 1000, bool $output = true ): array

    「指定ミリ秒内で何回コールできるか?」でベンチする。 メモリ使用量も取れるが ticks を利用しているのであまり正確ではないし、モノによっては計測できない(バージョンアップで memory_reset_peak_usage に変更される)。

    $suite は ['表示名' => $callable] 形式の配列。 表示名が与えられていない場合、それらしい名前で表示する。

    Example:

    // intval と int キャストはどちらが早いか調べる benchmark([ 'intval', 'intcast' => fn($v) => (int) $v, ], ['12345'], 10);
    Parameter
    type name summary
    ​array|​callable $suite ベンチ対象処理
    ​array $args = [] 各ケースに与えられる引数
    ​int $millisec = 1000 呼び出しミリ秒
    ​bool $output = true true だと標準出力に出力される
    Return
    type summary
    ​array ベンチ結果の配列

    [F] built_in_server ビルトインサーバーを起動する

    ビルトインサーバーを起動する
    function built_in_server( string $document_root, string|callable $router = null, array $options = [] ): ProcessAsync|object

    単に別プロセスで php -S するだけなので本番では使用してはならない。 少し小細工として下記を実装してある。

    $router
    クロージャを渡せる
    $options['last-modified']
    true にすると存在するファイルの 304 キャッシュが有効になる
    Parameter
    type name summary
    ​string $document_root 公開ディレクトリ兼カレントディレクトリ
    ​string|​callable $router = null ルータースクリプト or クロージャ
    ​array $options = [] オプション配列
    Return
    type summary
    ProcessAsync|​object プロセスオブジェクト

    [F] cache シンプルにキャッシュする

    シンプルにキャッシュする
    function cache( string $key, ?callable $provider, ?string $namespace = null ): mixed

    この関数は get/set/delete を兼ねる。 キャッシュがある場合はそれを返し、ない場合は $provider を呼び出してその結果をキャッシュしつつそれを返す。

    $provider に null を与えるとキャッシュの削除となる。

    Example:

    $provider = fn() => rand(); // 乱数を返す処理だが、キャッシュされるので同じ値になる $rand1 = cache('rand', $provider); $rand2 = cache('rand', $provider); that($rand1)->isSame($rand2); // $provider に null を与えると削除される cache('rand', null); $rand3 = cache('rand', $provider); that($rand1)->isNotSame($rand3);
    Parameter
    type name summary
    ​string $key キャッシュのキー
    ​?callable $provider キャッシュがない場合にコールされる callable
    ​?string $namespace = null 名前空間
    Return
    type summary
    ​mixed キャッシュ

    [F] cache_fetch psr-16 cache で「無かったらコールバックを実行して set」する

    psr-16 cache で「無かったらコールバックを実行して set」する
    function cache_fetch( Psr\SimpleCache\CacheInterface $cacher, string $key, callable $provider, ?int $ttl = null ): mixed
    Parameter
    type name summary
    CacheInterface $cacher キャッシュオブジェクト
    ​string $key キャッシュキー
    ​callable $provider データプロバイダ
    ​?int $ttl = null キャッシュ時間
    Return
    type summary
    ​mixed キャッシュデータ

    [F] cacheobject psr-16 を実装したキャッシュオブジェクトを返す

    psr-16 を実装したキャッシュオブジェクトを返す
    function cacheobject( ?string $directory = null, float $clean_probability = 0, ?float $clean_execution_time = null ): Cacheobject

    このオブジェクトはあくまで「他のパッケージに依存したくない」場合のデフォルト実装としての使用を想定している。

    • キャッシュはファイルシステムに保存される
    • キャッシュキーの . はディレクトリ区切りとして使用される
    • TTL を指定しなかったときのデフォルト値は約100年(実質無期限だろう)
    • psr-16 にはない getOrSet(fetch) が生えている(利便性が非常に高く使用頻度が多いため)

    性質上、参照されない期限切れキャッシュが溜まり続けるが $clean_probability を渡すと一定確率で削除される。 さらに $clean_execution_time を指定すると削除の実行時間が制限される。 $clean_probability は 1 が 100%(必ず削除)、 0 が 0%(削除しない)である。 削除処理は軽くはないため高頻度な実行は避けなければならない。 clean メソッドが生えているので明示的に呼ぶことも可能。

    psr/simple-cache (\Psr\SimpleCache\CacheInterface)が存在するなら implement される。 存在しないなら素の無名クラスで返す。 動作に違いはないが instanceoof や class_implements に違いが出てくるので注意。

    Parameter
    type name summary
    ​?string $directory = null キャッシュ保存ディレクトリ
    ​float $clean_probability = 0 不要キャッシュの削除確率
    ​?float $clean_execution_time = null 不要キャッシュの最大実行時間
    Return
    type summary
    Cacheobject psr-16 実装オブジェクト

    [F] function_configure 本ライブラリの設定を行う

    本ライブラリの設定を行う
    function function_configure(array|?string $option): array|string

    各関数の挙動を変えたり、デフォルトオプションを設定できる。

    Parameter
    type name summary
    ​array|​?string $option 設定。文字列指定時はその値を返す
    Return
    type summary
    ​array|​string 設定値

    [F] function_resolve 本ライブラリの関数名を解決する

    本ライブラリの関数名を解決する
    function function_resolve(string $funcname): ?string

    ※ 内部向け

    Parameter
    type name summary
    ​string $funcname 関数名
    Return
    type summary
    ​?string FQSEN 名

    [F] json_storage キーが json 化されてファイルシステムに永続化される ArrayAccess を返す

    キーが json 化されてファイルシステムに永続化される ArrayAccess を返す
    function json_storage( string $prefix = "global", int $ttl = ryunosuke\Functions\Package\PHP_INT_MAX ): \ArrayObject

    非常にシンプルで PSR-16 も実装せず、クリア手段も(基本的には)存在しない。 ArrayAccess なので $storage['hoge'] ??= something() として使うのがほぼ唯一の利用法。 その仕様・利用上、値として null を使用することはできない(使用した場合の動作は未定義とする)。

    キーに指定できるのは json_encode 可能なもののみ。 値に指定できるのは var_export 可能なもののみ。 上記以外を与えたときの動作は未定義。 TTL を指定すると次回読み込み時に期限切れをチェックし、切れていた場合 null を返す。 一度読み込まれればそのリクエスト中は期限切れになることはない。

    得てして簡単な関数・メソッドのメモ化や内部的なキャッシュに使用する。

    Example:

    // ??= を使えば「無かったら値を、有ったらそれを」を単純に実現できる $storage = json_storage(); that($storage['key'] ??= (fn() => 123)())->is(123); that($storage['key'] ??= (fn() => 456)())->is(123); // 引数に与えた prefix で別空間になる $storage = json_storage('other'); that($storage['key'] ??= (fn() => 789)())->is(789);
    Parameter
    type name summary
    ​string $prefix = "global"
    ​int $ttl = ryunosuke\Functions\Package\PHP_INT_MAX TTL
    Return
    type summary
    ArrayObject

    [F] number_serial 連続した数値の配列を縮めて返す

    連続した数値の配列を縮めて返す
    function number_serial( iterable|array $numbers, int|float $step = 1, string|null|\Closure $separator = null, bool $doSort = true ): array

    例えば [1, 2, 4, 6, 7, 9]['1~2', 4, '6~7', 9] になる。 結合法則は指定可能(上記は "~" を指定したもの)。 null を与えると配列の配列で返すことも可能。

    Example:

    // 単純に文字列指定 that(number_serial([1, 2, 4, 6, 7, 9], 1, '~'))->is(['1~2', 4, '6~7', 9]); // null を与えると from, to の配列で返す that(number_serial([1, 2, 4, 6, 7, 9], 1, null))->is([[1, 2], [4, 4], [6, 7], [9, 9]]); // $step は負数・小数・逆順も対応している(正負でよしなにソートされる) that(number_serial([-9, 0.2, 0.5, -0.3, 0.1, 0, -0.2, 9], -0.1, '~'))->is([9, 0.5, '0.2~0', '-0.2~-0.3', -9]);
    Parameter
    type name summary
    ​iterable|​array $numbers 数値配列
    ​int|​float $step = 1 連続とみなすステップ。負数を指定すれば逆順指定にも使える
    ​string|​null|​Closure $separator = null 連続列を結合する文字列(string: 文字結合、null: 配列、Closure: 2引数が渡ってくる)
    ​bool $doSort = true ソートをするか否か。事前にソート済みであることが明らかであれば false の方が良い
    Return
    type summary
    ​array 連続値をまとめた配列

    [N] ryunosuke\Functions\Package\var\

    [F] functions

    [F] arrayable_key_exists 配列・ArrayAccess にキーがあるか調べる

    配列・ArrayAccess にキーがあるか調べる
    function arrayable_key_exists( string|int $key, array|\ArrayAccess $arrayable ): bool

    配列が与えられた場合は array_key_exists と同じ。 ArrayAccess は一旦 isset で確認した後 null の場合は実際にアクセスして試みる。

    Example:

    $array = [ 'k' => 'v', 'n' => null, ]; // 配列は array_key_exists と同じ that(arrayable_key_exists('k', $array))->isTrue(); // もちろん存在する that(arrayable_key_exists('n', $array))->isTrue(); // isset ではないので null も true that(arrayable_key_exists('x', $array))->isFalse(); // 存在しないので false that(isset($array['n']))->isFalse(); // isset だと null が false になる(参考) $object = new \ArrayObject($array); // ArrayAccess は isset + 実際に取得を試みる that(arrayable_key_exists('k', $object))->isTrue(); // もちろん存在する that(arrayable_key_exists('n', $object))->isTrue(); // isset ではないので null も true that(arrayable_key_exists('x', $object))->isFalse(); // 存在しないので false that(isset($object['n']))->isFalse(); // isset だと null が false になる(参考)
    Parameter
    type name summary
    ​string|​int $key キー
    ​array|​ArrayAccess $arrayable 調べる値
    Return
    type summary
    ​bool キーが存在するなら true

    [F] arrayval array キャストの関数版

    array キャストの関数版
    function arrayval( mixed $var, bool $recursive = true ): array

    intval とか strval とかの array 版。 ただキャストするだけだが、関数なのでコールバックとして使える。

    $recursive を true にすると再帰的に適用する(デフォルト)。 入れ子オブジェクトを配列化するときなどに使える。

    Example:

    // キャストなので基本的には配列化される that(arrayval(123))->isSame([123]); that(arrayval('str'))->isSame(['str']); that(arrayval([123]))->isSame([123]); // 配列は配列のまま // $recursive = false にしない限り再帰的に適用される $stdclass = (object) ['key' => 'val']; that(arrayval([$stdclass], true))->isSame([['key' => 'val']]); // true なので中身も配列化される that(arrayval([$stdclass], false))->isSame([$stdclass]); // false なので中身は変わらない
    Parameter
    type name summary
    ​mixed $var array 化する値
    ​bool $recursive = true 再帰的に行うなら true
    Return
    type summary
    ​array array 化した配列

    [F] attr_exists 配列・オブジェクトを問わずキーやプロパティの存在を確認する

    配列・オブジェクトを問わずキーやプロパティの存在を確認する
    function attr_exists( int|string $key, array|object $value ): bool

    配列が与えられた場合は array_key_exists と同じ。 オブジェクトは一旦 isset で確認した後 null の場合は実際にアクセスして試みる。

    Example:

    $array = [ 'k' => 'v', 'n' => null, ]; // 配列は array_key_exists と同じ that(attr_exists('k', $array))->isTrue(); // もちろん存在する that(attr_exists('n', $array))->isTrue(); // isset ではないので null も true that(attr_exists('x', $array))->isFalse(); // 存在しないので false $object = (object) $array; // オブジェクトでも使える that(attr_exists('k', $object))->isTrue(); // もちろん存在する that(attr_exists('n', $object))->isTrue(); // isset ではないので null も true that(attr_exists('x', $object))->isFalse(); // 存在しないので false
    Parameter
    type name summary
    ​int|​string $key 調べるキー
    ​array|​object $value 調べられる配列・オブジェクト
    Return
    type summary
    ​bool $key が存在するなら true

    [F] attr_get 配列・オブジェクトを問わずキーやプロパティの値を取得する

    配列・オブジェクトを問わずキーやプロパティの値を取得する
    function attr_get( int|string $key, array|object $value, mixed $default = null ): mixed

    配列が与えられた場合は array_key_exists でチェック。 オブジェクトは一旦 isset で確認した後 null の場合は実際にアクセスして取得する。

    Example:

    $array = [ 'k' => 'v', 'n' => null, ]; that(attr_get('k', $array))->isSame('v'); // もちろん存在する that(attr_get('n', $array))->isSame(null); // isset ではないので null も true that(attr_get('x', $array, 'default'))->isSame('default'); // 存在しないのでデフォルト値 $object = (object) $array; // オブジェクトでも使える that(attr_get('k', $object))->isSame('v'); // もちろん存在する that(attr_get('n', $object))->isSame(null); // isset ではないので null も true that(attr_get('x', $object, 'default'))->isSame('default'); // 存在しないのでデフォルト値
    Parameter
    type name summary
    ​int|​string $key 取得するキー
    ​array|​object $value 取得される配列・オブジェクト
    ​mixed $default = null なかった場合のデフォルト値
    Return
    type summary
    ​mixed $key の値

    [F] cipher_metadata 暗号化アルゴリズムのメタデータを返す

    暗号化アルゴリズムのメタデータを返す
    function cipher_metadata(string $cipher): array

    ※ 内部向け

    Parameter
    type name summary
    ​string $cipher 暗号化方式(openssl_get_cipher_methods で得られるもの)
    Return
    type summary
    ​array 暗号化アルゴリズムのメタデータ

    [F] decrypt 指定されたパスワードで復号化する

    指定されたパスワードで復号化する
    function decrypt( string $cipherdata, string|array $password, string|array $ciphers = "aes-256-cbc", string $tag = "" ): mixed

    $password は配列で複数与えることができる。 複数与えた場合、順に試みて複合できた段階でその値を返す。

    $ciphers は配列で複数与えることができる。 複数与えた場合、順に試みて複合できた段階でその値を返す。 v2 以降は生成文字列に $cipher が含まれているため指定不要(今後指定してはならない)。

    復号に失敗すると null を返す。 単体で使うことはないと思うので詳細は encrypt を参照。

    Parameter
    type name summary
    ​string $cipherdata 復号化するデータ
    ​string|​array $password パスワード
    ​string|​array $ciphers = "aes-256-cbc" 暗号化方式(openssl_get_cipher_methods で得られるもの)
    ​string $tag = "" 認証タグ
    Return
    type summary
    ​mixed 復号化されたデータ

    [F] encrypt 指定されたパスワードで暗号化する

    指定されたパスワードで暗号化する
    function encrypt( mixed $plaindata, string|array $password, string $cipher = null, string &$tag = "" ): string

    データは json を経由して base64(URL セーフ) して返す。

    $tag を与えると認証タグが設定される(非推奨)。 $cipher で暗号化メソッドを指定できる(非推奨)。 v4 から $cipher は aes-256-gcm 決め打ち、結果に $tag が含まれるようになったので引数は2つだけを推奨。 要するに sodium_crypto_aead_aes256gcm_encrypt+iv と等しくなった。

    $password は配列で複数与えることができる。 複数与えた場合、先頭の要素が使用される。 これは decrypt との親和性のため(password の変更・移行期間は複数を扱いたいことがある)であり、決して「複数のパスワード対応」ではない。

    データ末尾には v が付与される。 これによって処理が変わり、バージョン違いの暗号化文字列を与えたとしても複合することができる。

    v0
    バージョンのない無印。json -> encrypt -> base64
    v1
    上記に圧縮処理を加えたもの。json -> deflate -> encrypt -> base64
    v2
    生成文字列に $cipher, $iv, $tag を加えたもの。json -> deflate -> cipher+iv+tag+encrypt -> base64
    v3
    生成文字列に $iv, $tag, $cipher を加えたもの。json -> deflate -> encrypt+cipher+iv+tag -> base64
    v4
    生成文字列に $tag, $iv を加えたもの。json -> deflate -> encrypt+tag+iv -> base64

    Example:

    $plaindata = ['a', 'b', 'c']; $encrypted = encrypt($plaindata, 'password'); $decrypted = decrypt($encrypted, 'password'); // 暗号化されて base64 の文字列になる that($encrypted)->isString(); // 復号化されて元の配列になる that($decrypted)->isSame(['a', 'b', 'c']); // password が異なれば失敗して null を返す that(decrypt($encrypted, 'invalid'))->isSame(null);
    Parameter
    type name summary
    ​mixed $plaindata 暗号化するデータ
    ​string|​array $password パスワード。十分な長さでなければならない
    ​string $cipher = null 暗号化方式(openssl_get_cipher_methods で得られるもの)
    ​string &$tag = "" 認証タグ
    Return
    type summary
    ​string 暗号化された文字列

    [F] flagval falsy の範囲を少し拡張した bool キャスト

    falsy の範囲を少し拡張した bool キャスト
    function flagval( mixed $var, bool $trim = false ): bool

    例えば ajax 等で とすると "false" が飛んできてしまうが、その場合も false 判定されるようになる。 この処理は FILTER_VALIDATE_BOOLEAN で行うので "off", "no", 等も false を返す。

    あとドキュメントには空白文字について言及がないが、どうも trim される模様。 trim するかどうかは呼び元で判断すべきだと思う(" true " が true, " " が false になるのは果たして正しいのか)ので、第2引数で分岐できるようにしてある。 boolval やキャストでは trim されないようなのでデフォルト false にしてある。

    Example:

    // こういう文字列も false になる that(flagval('false'))->isFalse(); that(flagval('off'))->isFalse(); that(flagval('no'))->isFalse();
    Parameter
    type name summary
    ​mixed $var bool 化する値
    ​bool $trim = false $var が文字列の場合に trim するか
    Return
    type summary
    ​bool bool 化した値

    [F] hashvar 変数指定をできるようにした compact

    変数指定をできるようにした compact
    function hashvar(mixed ...$vars): array

    名前空間指定の呼び出しは未対応。use して関数名だけで呼び出す必要がある。

    Example:

    $hoge = 'HOGE'; $fuga = 'FUGA'; that(hashvar($hoge, $fuga))->isSame(['hoge' => 'HOGE', 'fuga' => 'FUGA']);
    Parameter
    type name summary
    ​mixed ...$vars 変数(可変引数)
    Return
    type summary
    ​array 引数の変数を変数名で compact した配列

    [F] is_arrayable 変数が配列アクセス可能か調べる

    変数が配列アクセス可能か調べる
    function is_arrayable(mixed $var): bool

    Example:

    that(is_arrayable([]))->isTrue(); that(is_arrayable(new \ArrayObject()))->isTrue(); that(is_arrayable(new \stdClass()))->isFalse();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool 配列アクセス可能なら true

    [F] is_decimal 値が10進的か調べる

    値が10進的か調べる
    function is_decimal( mixed $var, bool $allow_float = true ): bool

    is_numeric を少しキツめにしたような関数で、なるべく「一般人の感覚」で数値とみなせるものを true で返す。

    なお、 0 始まりは false だが 0 終わり小数は許容される。 1.20000 を false にするような動作だと 1.0 も false にしなければ一貫性がない。 しかし 1.0 が false になるのはあまり一般的とはいえない。

    空白込みが false なのは空白許可は呼び元に委ねたいため(trim すればいいだけなので)。

    Example:

    // 以下は is_numeric と違い false を返す that(is_decimal('.12'))->isFalse(); // 整数部省略 that(is_decimal('12.'))->isFalse(); // 小数部省略 that(is_decimal('1e2'))->isFalse(); // 指数記法 that(is_decimal(' 12 '))->isFalse(); // 空白あり that(is_decimal('012'))->isFalse(); // 0 始まり
    Parameter
    type name summary
    ​mixed $var 判定する値
    ​bool $allow_float = true false にすると整数のみを許可する
    Return
    type summary
    ​bool 数値なら true

    [F] is_empty 値が空か検査する

    値が空か検査する
    function is_empty( mixed $var, bool $empty_stdClass = false ): bool

    empty とほぼ同じ。ただし

    string
    "0"
  • countable でない object
  • countable である object で count() > 0
  • は false 判定する。 ただし、 $empty_stcClass に true を指定すると「フィールドのない stdClass」も true を返すようになる。 これは stdClass の立ち位置はかなり特殊で「フィールドアクセスできる組み込み配列」のような扱いをされることが多いため。 (例えば json_decode('{}') は stdClass を返すが、このような状況は空判定したいことが多いだろう)。

    なお、関数の仕様上、未定義変数を true 判定することはできない。 未定義変数をチェックしたい状況は大抵の場合コードが悪いが $array['key1']['key2'] を調べたいことはある。 そういう時には使えない(?? する必要がある)。

    if ($var) {} で十分なんだけど "0" が…」という状況はまれによくあるはず。

    Example:

    // この辺は empty と全く同じ that(is_empty(null))->isTrue(); that(is_empty(false))->isTrue(); that(is_empty(0))->isTrue(); that(is_empty(''))->isTrue(); // この辺だけが異なる that(is_empty('0'))->isFalse(); // 第2引数に true を渡すと空の stdClass も empty 判定される $stdclass = new \stdClass(); that(is_empty($stdclass, true))->isTrue(); // フィールドがあれば empty ではない $stdclass->hoge = 123; that(is_empty($stdclass, true))->isFalse();
    Parameter
    type name summary
    ​mixed $var 判定する値
    ​bool $empty_stdClass = false 空の stdClass を空とみなすか
    Return
    type summary
    ​bool 空なら true

    [F] is_empty_recursive 値が空か再帰的に検査する

    値が空か再帰的に検査する
    function is_empty_recursive( mixed $var, bool $empty_stdClass = false ): bool

    is_empty の再帰版。

    クエリパラメータやオプション配列等で「実質値を持っていない」を判定したいことが稀によくある。 Example を参照。

    Example:

    // このような値を空判定したい that(is_empty_recursive([ 'query' => [ 'param1' => '', 'param2' => '', ], 'opt' => [ 'key1' => '', 'key2' => null, ], ]))->isTrue();
    Parameter
    type name summary
    ​mixed $var 判定する値
    ​bool $empty_stdClass = false 空の stdClass を空とみなすか
    Return
    type summary
    ​bool 空なら true

    [F] is_exportable 値が var_export で出力可能か検査する

    値が var_export で出力可能か検査する
    function is_exportable(mixed $var): bool

    「出力可能」とは「意味のある出力」を意味する。 例えば set_state のないオブジェクトはエラーなく set_state コール形式で出力されるが意味のある出力ではない。 リソース型はエラーなく NULL で出力されるが意味のある出力ではない。 循環参照は出力できるものの warning が出てかつ循環は切れるため意味のある出力ではない。

    Example:

    that(is_primitive(null))->isTrue(); that(is_primitive(false))->isTrue(); that(is_primitive(123))->isTrue(); that(is_primitive(STDIN))->isTrue(); that(is_primitive(new \stdClass))->isFalse(); that(is_primitive(['array']))->isFalse();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool 出力可能なら true

    [F] is_primitive 値が複合型でないか検査する

    値が複合型でないか検査する
    function is_primitive(mixed $var): bool

    「複合型」とはオブジェクトと配列のこと。 つまり

    • is_scalar($var) || is_null($var) || is_resource($var)

    と同義(!is_array($var) && !is_object($var) とも言える)。

    Example:

    that(is_primitive(null))->isTrue(); that(is_primitive(false))->isTrue(); that(is_primitive(123))->isTrue(); that(is_primitive(STDIN))->isTrue(); that(is_primitive(new \stdClass))->isFalse(); that(is_primitive(['array']))->isFalse();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool 複合型なら false

    [F] is_recursive 変数が再帰参照を含むか調べる

    変数が再帰参照を含むか調べる
    function is_recursive(mixed $var): bool

    Example:

    // 配列の再帰 $array = []; $array['recursive'] = &$array; that(is_recursive($array))->isTrue(); // オブジェクトの再帰 $object = new \stdClass(); $object->recursive = $object; that(is_recursive($object))->isTrue();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool 再帰参照を含むなら true

    [F] is_resourcable 閉じたリソースでも true を返す is_resource

    閉じたリソースでも true を返す is_resource
    function is_resourcable(mixed $var): bool

    マニュアル( https://www.php.net/manual/ja/function.is-resource.php )に記載の通り、 isresource は閉じたリソースで false を返す。 リソースはリソースであり、それでは不便なこともあるので、閉じていようとリソースなら true を返す関数。

    Example:

    // 閉じたリソースを用意 $resource = tmpfile(); fclose($resource); // is_resource は false を返すが・・・ that(is_resource($resource))->isFalse(); // is_resourcable は true を返す that(is_resourcable($resource))->isTrue();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool リソースなら true

    [F] is_stringable 変数が文字列化できるか調べる

    変数が文字列化できるか調べる
    function is_stringable(mixed $var): bool

    「配列」「__toString を持たないオブジェクト」が false になる。 (厳密に言えば配列は "Array" になるので文字列化できるといえるがここでは考えない)。

    Example:

    // こいつらは true that(is_stringable(null))->isTrue(); that(is_stringable(true))->isTrue(); that(is_stringable(3.14))->isTrue(); that(is_stringable(STDOUT))->isTrue(); that(is_stringable(new \Exception()))->isTrue(); // こいつらは false that(is_stringable(new \ArrayObject()))->isFalse(); that(is_stringable([1, 2, 3]))->isFalse();
    Parameter
    type name summary
    ​mixed $var 調べる値
    Return
    type summary
    ​bool 文字列化できるなら true

    [F] is_typeof 変数が型に合致するか調べる

    変数が型に合致するか調べる
    function is_typeof( mixed $var, string $typestring, null|object|string $context = null ): bool

    is_a のビルトイン+DNF 対応版。

    DNF の場合、括弧は必須だしネストも不可。 実質的には ReflectionType の文字列表現を与えるのみ。

    いわゆる strict_types=1 で、型の変換は伴わない。 それはそれで不便なことがある(stringable とか)ので対応するかもしれない。

    Example:

    that(is_typeof(null, 'null'))->isTrue(); that(is_typeof(null, '?int'))->isTrue(); that(is_typeof(1234, '?int'))->isTrue(); that(is_typeof(1234, 'int|string'))->isTrue(); that(is_typeof('ss', 'int|string'))->isTrue(); that(is_typeof(null, 'int|string'))->isFalse(); that(is_typeof([], 'array|(ArrayAccess&Countable&iterable)'))->isTrue(); that(is_typeof(new \ArrayObject(), 'array|(ArrayAccess&Countable&iterable)'))->isTrue();
    Parameter
    type name summary
    ​mixed $var 調べる値
    ​string $typestring 型文字列
    ​null|​object|​string $context = null self,static のコンテキスト
    Return
    type summary
    ​bool $typestring に合致するなら true

    [F] numberify 値を何とかして数値化する

    値を何とかして数値化する
    function numberify( mixed $var, bool $decimal = false ): int|float
    • 配列は要素数
    • int/float はそのまま(ただし $decimal に応じた型にキャストされる)
    • resource はリソースID(php 標準の int キャスト)
    • null/bool はその int 値(php 標準の int キャストだが $decimal を見る)
    • それ以外(文字列・オブジェクト)は文字列表現から数値以外を取り除いたもの

    文字列・オブジェクト以外の変換は互換性を考慮しない。頻繁に変更される可能性がある(特に配列)。

    -記号は受け入れるが+記号は受け入れない。

    Example:

    // 配列は要素数となる that(numberify([1, 2, 3]))->isSame(3); // int/float は基本的にそのまま that(numberify(123))->isSame(123); that(numberify(123.45))->isSame(123); that(numberify(123.45, true))->isSame(123.45); // 文字列は数値抽出 that(numberify('a1b2c3'))->isSame(123); that(numberify('a1b2.c3', true))->isSame(12.3);
    Parameter
    type name summary
    ​mixed $var 対象の値
    ​bool $decimal = false 小数として扱うか
    Return
    type summary
    ​int|​float 数値化した値

    [F] numval 値を数値化する

    値を数値化する
    function numval( mixed $var, int $base = 10 ): int|float

    int か float ならそのまま返す。 文字列の場合、一言で言えば「.を含むなら float、含まないなら int」を返す。 int でも float でも stringable でもない場合は実装依存(ただの int キャスト)。

    Example:

    that(numval(3.14))->isSame(3.14); // int や float はそのまま返す that(numval('3.14'))->isSame(3.14); // . を含む文字列は float を返す that(numval('11', 8))->isSame(9); // 基数が指定できる
    Parameter
    type name summary
    ​mixed $var 数値化する値
    ​int $base = 10 基数。int 的な値のときしか意味をなさない
    Return
    type summary
    ​int|​float 数値化した値

    [F] phpval 文字列を php の式として評価して値を返す

    文字列を php の式として評価して値を返す
    function phpval( mixed $var, array $contextvars = [] ): mixed

    実質的には eval("return $var;") とほぼ同義。 ただ、 eval するまでもない式はそのまま返し、bare な文字列はそのまま文字列として返す(7.2 以前の未定義定数のような動作)。

    Example:

    that(phpval('strtoupper($var)', ['var' => 'string']))->isSame('STRING'); that(phpval('bare string'))->isSame('bare string');
    Parameter
    type name summary
    ​mixed $var 評価する式
    ​array $contextvars = [] eval される場合のローカル変数
    Return
    type summary
    ​mixed 評価した値

    [F] si_prefix 数値に SI 接頭辞を付与する

    数値に SI 接頭辞を付与する
    function si_prefix( mixed $var, int $unit = 1000, string|\Closure $format = "%.3f %s" ): string|array

    値は 1 <= $var < 1000(1024) の範囲内に収められる。 ヨクト(10^24)~ヨタ(1024)まで。整数だとしても 64bit の範囲を超えるような値の精度は保証しない。

    Example:

    // シンプルに k をつける that(si_prefix(12345))->isSame('12.345 k'); // シンプルに m をつける that(si_prefix(0.012345))->isSame('12.345 m'); // 書式フォーマットを指定できる that(si_prefix(12345, 1000, '%d%s'))->isSame('12k'); that(si_prefix(0.012345, 1000, '%d%s'))->isSame('12m'); // ファイルサイズを byte で表示する that(si_prefix(12345, 1000, '%d %sbyte'))->isSame('12 kbyte'); // ファイルサイズを byte で表示する(1024) that(si_prefix(10240, 1024, '%.3f %sbyte'))->isSame('10.000 kbyte'); // フォーマットに null を与えると sprintf せずに配列で返す that(si_prefix(12345, 1000, null))->isSame([12.345, 'k']); // フォーマットにクロージャを与えると実行して返す that(si_prefix(12345, 1000, fn($v, $u) => number_format($v, 2) . $u))->isSame('12.35k');
    Parameter
    type name summary
    ​mixed $var 丸める値
    ​int $unit = 1000 桁単位。実用上は 1000, 1024 の2値しか指定することはないはず
    ​string|​Closure $format = "%.3f %s" 書式フォーマット。 null を与えると sprintf せずに配列で返す
    Return
    type summary
    ​string|​array 丸めた数値と SI 接頭辞で sprintf した文字列($format が null の場合は配列)

    [F] si_unprefix SI 接頭辞が付与された文字列を数値化する

    SI 接頭辞が付与された文字列を数値化する
    function si_unprefix( mixed $var, int $unit = 1000, $format = "%d%s" ): int|float

    典型的な用途は ini_get で得られた値を数値化したいとき。 ただし、 ini は 1m のように小文字で指定することもあるので大文字化する必要はある。

    Example:

    // 1k = 1000 that(si_unprefix('1k'))->isSame(1000); // 1k = 1024 that(si_unprefix('1k', 1024))->isSame(1024); // m はメガではなくミリ that(si_unprefix('1m'))->isSame(0.001); // M がメガ that(si_unprefix('1M'))->isSame(1000000); // K だけは特別扱いで大文字小文字のどちらでもキロになる that(si_unprefix('1K'))->isSame(1000);
    Parameter
    type name summary
    ​mixed $var 数値化する値
    ​int $unit = 1000 桁単位。実用上は 1000, 1024 の2値しか指定することはないはず
    $format = "%d%s"
    Return
    type summary
    ​int|​float SI 接頭辞を取り払った実際の数値

    [F] strdec 0xff,0777 などを10進数値化する

    0xff,0777 などを10進数値化する
    function strdec($var): int|float

    php のリテラル形式の数値文字列を int に変換すると考えればよい。 intval でも似たようなことはできるが、エラーも例外も発生せず静かに 0 を返すので使い勝手が悪い。 この関数は変換できない場合は例外を投げる。

    Example:

    // 数値を与えれば数値のまま that(strdec(12345))->isSame(12345); // 通常の10進数字 that(strdec('12345'))->isSame(12345); // 16進数字 that(strdec('0xff'))->isSame(255); // 8進数字 that(strdec('077'))->isSame(63); that(strdec('0o77'))->isSame(63); // 2進数字 that(strdec('0b11'))->isSame(3);
    Parameter
    type name summary
    $var
    Return
    type summary
    ​int|​float

    [F] stringify 値を何とかして文字列化する

    値を何とかして文字列化する
    function stringify(mixed $var): string

    この関数の出力は互換性を考慮しない。頻繁に変更される可能性がある。

    Parameter
    type name summary
    ​mixed $var 文字列化する値
    Return
    type summary
    ​string $var を文字列化したもの

    [F] var_apply 値にコールバックを適用する

    値にコールバックを適用する
    function var_apply( mixed $var, callable $callback, mixed ...$args ): mixed|array

    普通のスカラー値であれば $callback($var) と全く同じ。 この関数は「$var が配列だったら中身に適用して返す(再帰)」という点で上記とは異なる。

    「配列が与えられたら要素に適用して配列で返す、配列じゃないなら直に適用してそれを返す」という状況はまれによくあるはず。

    Example:

    // 素の値は素の呼び出しと同じ that(var_apply(' x ', 'trim'))->isSame('x'); // 配列は中身に適用して配列で返す(再帰) that(var_apply([' x ', ' y ', [' z ']], 'trim'))->isSame(['x', 'y', ['z']]); // 第3引数以降は残り引数を意味する that(var_apply(['!x!', '!y!'], 'trim', '!'))->isSame(['x', 'y']); // 「まれによくある」の具体例 that(var_apply(['<x>', ['<y>']], 'htmlspecialchars', ENT_QUOTES, 'utf-8'))->isSame(['&lt;x&gt;', ['&lt;y&gt;']]);
    Parameter
    type name summary
    ​mixed $var $callback を適用する値
    ​callable $callback 値変換コールバック
    ​mixed ...$args $callback の残り引数(可変引数)
    Return
    type summary
    ​mixed|​array $callback が適用された値。元が配列なら配列で返す

    [F] var_applys 配列にコールバックを適用する

    配列にコールバックを適用する
    function var_applys( mixed $var, callable $callback, mixed ...$args ): mixed|array

    配列であれば $callback($var) と全く同じ。 この関数は「$var がスカラー値だったら配列化して適用してスカラーで返す」という点で上記とは異なる。

    「配列を受け取って配列を返す関数があるが、手元にスカラー値しか無い」という状況はまれによくあるはず。

    Example:

    // 配列を受け取って中身を大文字化して返すクロージャ $upper = fn($array) => array_map('strtoupper', $array); // 普通はこうやって使うが・・・ that($upper(['a', 'b', 'c']))->isSame(['A', 'B', 'C']); // 手元に配列ではなくスカラー値しか無いときはこうせざるをえない that($upper(['a'])[0])->isSame('A'); // var_applys を使うと配列でもスカラーでも統一的に記述することができる that(var_applys(['a', 'b', 'c'], $upper))->isSame(['A', 'B', 'C']); that(var_applys('a', $upper))->isSame('A'); # 要するに「大文字化したい」だけなわけだが、$upper が配列を前提としているので、「大文字化」部分を得るには配列化しなければならなくなっている # 「strtoupper だけ切り出せばよいのでは?」と思うかもしれないが、「(外部ライブラリなどで)手元に配列しか受け取ってくれない処理しかない」状況がまれによくある
    Parameter
    type name summary
    ​mixed $var $callback を適用する値
    ​callable $callback 値変換コールバック
    ​mixed ...$args $callback の残り引数(可変引数)
    Return
    type summary
    ​mixed|​array $callback が適用された値。元が配列なら配列で返す

    [F] var_export2 組み込みの var_export をいい感じにしたもの

    組み込みの var_export をいい感じにしたもの
    function var_export2( mixed $value, bool|array $options = [] ): string|null

    下記の点が異なる。

    • 配列は 5.4 以降のショートシンタックス([])で出力
    • ただの配列は1行([1, 2, 3])でケツカンマなし、連想配列は桁合わせインデントでケツカンマあり
    • 文字列はダブルクオート
    • null は null(小文字)
    • 再帰構造を渡しても警告がでない(さらに NULL ではなく '*RECURSION*' という文字列になる)
    • 配列の再帰構造の出力が異なる(Example参照)

    Example:

    // 単純なエクスポート that(var_export2(['array' => [1, 2, 3], 'hash' => ['a' => 'A', 'b' => 'B', 'c' => 'C']], true))->isSame('[ "array" => [1, 2, 3], "hash" => [ "a" => "A", "b" => "B", "c" => "C", ], ]'); // 再帰構造を含むエクスポート(標準の var_export は形式が異なる。 var_export すれば分かる) $rarray = []; $rarray['a']['b']['c'] = &$rarray; $robject = new \stdClass(); $robject->a = new \stdClass(); $robject->a->b = new \stdClass(); $robject->a->b->c = $robject; that(var_export2(compact('rarray', 'robject'), true))->isSame('[ "rarray" => [ "a" => [ "b" => [ "c" => "*RECURSION*", ], ], ], "robject" => (object) [ "a" => (object) [ "b" => (object) [ "c" => "*RECURSION*", ], ], ], ]');
    Parameter
    type name summary
    ​mixed $value 出力する値
    ​bool|​array $options = [] オプション配列(var_export に寄せるため bool も受け付ける)
    Return
    type summary
    ​string|​null $return=true の場合は出力せず結果を返す

    [F] var_export3 var_export を色々と出力できるようにしたもの

    var_export を色々と出力できるようにしたもの
    function var_export3( mixed $value, bool|array $return = false ): string

    php のコードに落とし込むことで serialize と比較してかなり高速に動作する。

    各種オブジェクトやクロージャ、循環参照を含む配列など様々なものが出力できる。 ただし、下記は不可能あるいは復元不可(今度も対応するかは未定)。

    • 特定の内部クラス(PDO など)
    • 大部分のリソース

    オブジェクトは「リフレクションを用いてコンストラクタなしで生成してプロパティを代入する」という手法で復元する。 ただしコンストラクタが必須引数無しの場合はコールされる。 のでクラスによってはおかしな状態で復元されることがある(大体はリソース型のせいだが…)。 sleep, wakeup, Serializable などが実装されているとそれはそのまま機能する。 set_state だけは呼ばれないので注意。

    Generator は元となった関数/メソッドを再コールすることで復元される。 その仕様上、引数があると呼べないし、実行位置はリセットされる。

    クロージャはコード自体を引っ張ってきて普通に function (){} として埋め込む。 クラス名のエイリアスや use, $this バインドなど可能な限り復元するが、おそらくあまりに複雑なことをしてると失敗する。

    リソースはファイル的なリソースであればメタ情報を出力して復元時に再オープンする。

    軽くベンチを取ったところ、オブジェクトを含まない純粋な配列の場合、serialize の 200 倍くらいは速い(それでも var_export の方が速いが…)。 オブジェクトを含めば含むほど遅くなり、全要素がオブジェクトになると serialize と同程度になる。 大体 var_export:var_export3:serialize が 1:5:1000 くらい。

    Parameter
    type name summary
    ​mixed $value エクスポートする値
    ​bool|​array $return = false 返り値として返すなら true. 配列を与えるとオプションになる
    Return
    type summary
    ​string エクスポートされた文字列

    [F] var_hash 値に複数のハッシュアルゴリズムを適用させて結合して返す

    値に複数のハッシュアルゴリズムを適用させて結合して返す
    function var_hash( mixed $var, string[] $algos = ["md5", "sha1"], ?bool $base64 = true ): string

    $data は何らかの方法で文字列化される(この「何らかの方法」は互換性を担保しない)。 文字長がかなり増えるため、 $base64 に true を与えるとバイナリ変換してその結果を base64(url セーフ)して返す。 さらに false を与えると 16進数文字列で返し、 null を与えるとバイナリ文字列で返す。

    Example:

    // 配列をハッシュ化する that(var_hash(['a', 'b', 'c']))->isSame('7BDgx6NE2hkXAKtKzhpeJm6-mheMOQWNgrCe7768OiFeoWgA'); // オブジェクトをハッシュ化する that(var_hash(new \ArrayObject(['a', 'b', 'c'])))->isSame('-zR2rZ58CzuYhhdHn1Oq90zkYSaxMS-dHUbmb0MTRM4gBpj2');
    Parameter
    type name summary
    ​mixed $var ハッシュ化する値
    ​string[] $algos = ["md5", "sha1"] ハッシュアルゴリズム
    ​?bool $base64 = true 結果を base64 化するか
    Return
    type summary
    ​string ハッシュ文字列

    [F] var_html var_export2 を html コンテキストに特化させたようなもの

    var_export2 を html コンテキストに特化させたようなもの
    function var_html(mixed $value)

    下記のような出力になる。

    • <pre class='var_html'> ~ </pre> で囲まれる
    • php 構文なのでハイライトされて表示される
    • Content-Type が強制的に text/html になる

    この関数の出力は互換性を考慮しない。頻繁に変更される可能性がある。

    Parameter
    type name summary
    ​mixed $value 出力する値

    [F] var_mimetype 値の mimetype を返す

    値の mimetype を返す
    function var_mimetype( $var, ?array &$parameters = null ): ?string

    追加の引数で ; 区切りのパラメータを受け取れる。 mimetype は タイプ/サブタイプ;引数=値 と規約されているので 引数=>値 の連想配列で受け取る。 したがって返り値は「タイプ/サブタイプ」の文字列で固定となる(ただし失敗時は null を返す)。 とは言っても finfo の仕様上、現状では charset しか返さない。

    Example:

    // 普通の文字列は text/plain that(var_mimetype('plain text', $parameters))->isSame('text/plain'); // $parameters で引数を受け取れる that($parameters)->is(['charset' => 'us-ascii']);
    Parameter
    type name summary
    $var
    ​?array &$parameters = null
    Return
    type summary
    ​?string

    [F] var_pretty var_dump の出力を見やすくしたもの

    var_dump の出力を見やすくしたもの
    function var_pretty( mixed $value, array $options = [] ): string

    var_dump はとても縦に長い上見づらいので色や改行・空白を調整して見やすくした。 sapi に応じて自動で色分けがなされる($context で指定もできる)。 また、 xdebug のように呼び出しファイル:行数が先頭に付与される。

    この関数の出力は互換性を考慮しない。頻繁に変更される可能性がある。

    Example:

    // 下記のように出力される(実際は色付きで出力される) $using = 123; var_pretty([ "array" => [1, 2, 3], "hash" => [ "a" => "A", "b" => "B", "c" => "C", ], "object" => new \Exception(), "closure" => function () use ($using) { }, ]); ?> { array: [1, 2, 3], hash: { a: 'A', b: 'B', c: 'C', }, object: Exception#1 { message: '', string: '', code: 0, file: '...', line: 19, trace: [], previous: null, }, closure: Closure#0(static) use { using: 123, }, } <?php
    Parameter
    type name summary
    ​mixed $value 出力する値
    ​array $options = [] 出力オプション
    Return
    type summary
    ​string return: true なら値の出力結果

    [F] var_type 値の型を取得する

    値の型を取得する
    function var_type( mixed $var, array $options = [] ): string

    get_debug_type を少しだけ特殊化したもの。 「デバッグ用の型」ではなく「コード化したときに埋め込みやすい型」が主目的。

    • object の場合は必ず \ が付く
    • resource の場合はカッコ書き無しで 'resource'

    無名クラスの場合は extends, implements の優先順位でその名前を使う。 継承も実装もされていない場合は標準の get_class の結果を返す。

    phpdoc に true を渡すと array-shape 記法も有効になり下記のようになる。

    • 連想配列は想起した通り
    • 連番配列は中身の和集合を取る
      • 中身がさらに配列なら再帰的に処理する
        • 有り無し混在は null 扱いになる
    • 無名クラスは親クラス+インターフェースを取る
      • 完全無名クラスは object になる

    phpdoc:true の場合の結果は互換性を考慮しない。

    Example:

    // プリミティブ型は get_debug_type と同義 that(var_type(false))->isSame('bool'); that(var_type(123))->isSame('int'); that(var_type(3.14))->isSame('float'); that(var_type([1, 2, 3]))->isSame('array'); // リソースはなんでも resource that(var_type(STDOUT))->isSame('resource'); // オブジェクトは型名を返す that(var_type(new \stdClass))->isSame('\\stdClass'); that(var_type(new \Exception()))->isSame('\\Exception'); // 無名クラスは継承元の型名を返す(インターフェース実装だけのときはインターフェース名) that(var_type(new class extends \Exception{}))->isSame('\\Exception'); that(var_type(new class implements \JsonSerializable{ public function jsonSerialize(): string { return ''; } }))->isSame('\\JsonSerializable'); // phpdoc 形式 that(var_type([ 'scalar' => 123, 'lish-hash' => [ ['id' => 1, 'name' => 'a'], ['id' => 2, 'name' => 'b'], ], 'nest' => [ 'a' => [ 'b' => [ 'c' => ['a', 'b', 'c'], ], ], ], ], ['phpdoc' => true, 'format' => 9]))->is(<<<ARRAYSHAPE array{ "lish-hash": array<array{ id: int, name: string }>, nest: array{ a: array{ b: array{ c: array<string> } } }, scalar: int } ARRAYSHAPE);
    Parameter
    type name summary
    ​mixed $var 型を取得する値
    ​array $options = [] オプション配列
    Return
    type summary
    ​string 型名

    [F] varcmp php7 の &lt;=&gt; の関数版

    php7 の <=> の関数版
    function varcmp( mixed $a, mixed $b, ?int $mode = null, ?int $precision = null ): int

    引数で大文字小文字とか自然順とか型モードとかが指定できる。 さらに追加で SORT_STRICT という厳密比較フラグを渡すことができる。

    Example:

    // 'a' と 'z' なら 'z' の方が大きい that(varcmp('z', 'a') > 0)->isTrue(); that(varcmp('a', 'z') < 0)->isTrue(); that(varcmp('a', 'a') === 0)->isTrue(); // 'a' と 'Z' なら 'a' の方が大きい…が SORT_FLAG_CASE なので 'Z' のほうが大きい that(varcmp('Z', 'a', SORT_FLAG_CASE) > 0)->isTrue(); that(varcmp('a', 'Z', SORT_FLAG_CASE) < 0)->isTrue(); that(varcmp('a', 'A', SORT_FLAG_CASE) === 0)->isTrue(); // '2' と '12' なら '2' の方が大きい…が SORT_NATURAL なので '12' のほうが大きい that(varcmp('12', '2', SORT_NATURAL) > 0)->isTrue(); that(varcmp('2', '12', SORT_NATURAL) < 0)->isTrue(); // SORT_STRICT 定数が使える(下記はすべて宇宙船演算子を使うと 0 になる) that(varcmp(['a' => 'A', 'b' => 'B'], ['b' => 'B', 'a' => 'A'], SORT_STRICT) < 0)->isTrue(); that(varcmp((object) ['a'], (object) ['a'], SORT_STRICT) < 0)->isTrue();
    Parameter
    type name summary
    ​mixed $a 比較する値1
    ​mixed $b 比較する値2
    ​?int $mode = null 比較モード(SORT_XXX)。省略すると型でよしなに選択
    ​?int $precision = null 小数比較の際の誤差桁
    Return
    type summary
    ​int 等しいなら 0、 $a のほうが大きいなら > 0、 $bのほうが大きいなら < 0

    © Copyright Dave Snider, Read the Docs, Inc. & contributors.

    Built with htmarkdown Using a theme provided by Read the Docs.
    コントロールパネル
    View
    Head
    Body