[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']);
type | name | summary |
---|---|---|
array | ...$variadic |
足す配列(可変引数) |
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, ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable|callable[] | $columns |
集計関数 |
string|array|null | $key = null |
集約列。クロージャを与えると返り値がキーになる |
type | summary |
---|---|
array | 集約配列 |
[F] array_all 全要素が true になるなら true を返す(1つでも false なら false を返す)
全要素が true になるなら true を返す(1つでも false なら false を返す)
function array_all( $array, $callback = null, $default = true )
type | name | summary |
---|---|---|
| $array |
|
| $callback = null |
|
| $default = true |
|
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();
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
?callable | $callback = null |
評価クロージャ。 null なら値そのもので評価 |
bool|mixed | $default = true |
空配列の場合のデフォルト値 |
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 )
type | name | summary |
---|---|---|
| $array |
|
| $callback = null |
|
| $default = false |
|
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
| $value |
|
| $key = null |
|
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'], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable[] | $rules |
分類ルール。[key => callable] 形式 |
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']]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
string|array|null | $column_keys = null |
引っ張ってくるキー名 |
mixed | $index_key = null |
新しい配列のキーとなるキー名 |
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', ], ]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
callable | $callback |
適用するコールバック |
bool | $apply_array = false |
配列要素にもコールバックを適用するか |
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, // 配列の数 ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
カウントルール。配列も渡せる |
bool | $recursive = false |
再帰フラグ |
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]]);
type | name | summary |
---|---|---|
array | ...$arrays |
対象配列(可変引数) |
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);
type | name | summary |
---|---|---|
array | $array |
調べる配列 |
int|null | $max_depth = null |
最大階層数 |
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'], ]);
type | name | summary |
---|---|---|
iterable | $array1 |
対象配列1 |
iterable | $array2 |
対象配列2 |
string | $delimiter = "." |
差分配列のキー区切り文字 |
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]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable|int|string|null | $comparator = null |
比較関数 |
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');
type | name | summary |
---|---|---|
array|ArrayAccess | $array |
調べる配列 |
string|array | $path |
パス文字列。配列も与えられる |
mixed | $default = null |
無かった場合のデフォルト値 |
string | $delimiter = "." |
パスの区切り文字。大抵は '.' か '/' |
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; }, []) // 個人的に↑のようなぶら下がり引数があまり好きではない(クロージャを最後の引数にしたい) );
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
評価クロージャ。(&$carry, $key, $value) を受ける |
mixed | $default = null |
ループの最初や空の場合に適用される値 |
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']]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
mixed | $condition |
分割条件 |
int | $limit = PHP_INT_MAX |
最大分割数 |
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], ]);
type | name | summary |
---|---|---|
array|bool | $default = [] |
基準となる配列 |
iterable|Closure | ...$arrays |
マージする配列 |
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', ]);
type | name | summary |
---|---|---|
iterable | $keys |
キーとなる配列 |
callable | $callback |
要素のコールバック(引数でキーが渡ってくる) |
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', ]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
mixed | ...$values |
詰める値(可変引数) |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
評価クロージャ |
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", ], ], ]);
type | name | summary |
---|---|---|
iterable|T | $array |
対象配列 |
callable | $callback |
評価クロージャ(値, キー, 親キー配列) |
bool | $unset_empty = true |
再帰の際に空になった要素も伏せるか |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | ...$callbacks |
評価クロージャ配列 |
type | summary |
---|---|
array | 評価クロージャでフィルタした新しい配列 |
[F] array_find array_search のクロージャ版のようなもの
array_search のクロージャ版のようなもの
function array_find( $array, $callback, $is_key = true )
type | name | summary |
---|---|---|
| $array |
|
| $callback |
|
| $is_key = true |
|
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);
type | name | summary |
---|---|---|
iterable | $array |
調べる配列 |
callable | $callback |
評価コールバック |
bool | $is_key = true |
キーを返すか否か |
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);
type | name | summary |
---|---|---|
iterable | $array |
調べる配列 |
callable | $callback |
評価コールバック |
bool | $is_key = true |
キーを返すか否か |
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]);
type | name | summary |
---|---|---|
iterable | $array |
調べる配列 |
callable | $callback |
評価コールバック |
bool | $is_key = true |
キーを返すか否か |
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, ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string|Closure|null | $delimiter = null |
キーの区切り文字。 null を与えると連番になる |
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']);
type | name | summary |
---|---|---|
array|ArrayAccess | $array |
配列 |
string|int|array|Closure | $key |
取得したいキー。配列を与えると全て返す。クロージャの場合は true 相当を返す |
mixed | $default = null |
無かった場合のデフォルト値 |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string | $regex |
正規表現 |
bool | $not = false |
true にすると「マッチしない」でフィルタする |
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, ], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
?callable|string|array | $callback = null |
評価クロージャ。 null なら値そのもので評価 |
bool | $preserve_keys = false |
キーを保存するか。 false の場合数値キーは振り直される |
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']);
type | name | summary |
---|---|---|
iterable|string | $array |
対象配列 |
string | $glue |
差し込む要素 |
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
mixed | $value |
挿入値 |
int|null | $position = null |
挿入位置 |
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'], ]);
type | name | summary |
---|---|---|
array|ArrayAccess | $from |
駆動表 |
array|ArrayAccess | $join |
結合表 |
array|callable | $on |
結合条件。原則として callable で、ある程度の前提制約が置けるときのみ配列を指定する |
bool | $outer = false |
true だと OUTER, false だと INNER 的挙動になる |
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();
type | name | summary |
---|---|---|
array|string | $keys |
調べるキー |
array|ArrayAccess | $array |
調べる配列 |
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", ], ], ], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
適用するコールバック |
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件(完全に範囲外なので空)
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
int | $limit |
切り詰めるサイズ |
?int | $offset = null |
開始位置 |
?bool | $preserve_keys = null |
キーの保存フラグ(null にすると連想配列の時のみ保存される) |
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], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
array|string|null | $column_key = null |
値となるキー |
string|Closure|null | $index_key = null |
キーとなるキー |
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', '']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
評価クロージャ |
bool | $strict = false |
厳密比較フラグ。 true だと null のみが偽とみなされる |
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([]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
評価クロージャ |
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', ], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $callback |
評価クロージャ |
bool | $iterable = true |
is_iterable で判定するか |
bool | $apply_array = false |
配列要素にもコールバックを適用するか |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | ...$callbacks |
評価クロージャ配列 |
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']);
type | name | summary |
---|---|---|
array | ...$arrays |
マージする配列 |
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]);
type | name | summary |
---|---|---|
array | ...$variadic |
対象配列(可変引数) |
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); }
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string | $delimiter = "." |
キーの区切り文字 |
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');
type | name | summary |
---|---|---|
string|int|array | $key |
取得したいキー |
mixed | $default = null |
デフォルト値 |
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();
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
?callable | $callback = null |
評価クロージャ。 null なら値そのもので評価 |
bool|mixed | $default = false |
空配列の場合のデフォルト値 |
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
mixed | $orders |
ソート順 |
bool | $preserve_keys = false |
キーを保存するか。 false の場合数値キーは振り直される |
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']);
type | name | summary |
---|---|---|
iterable|object | $array |
対象配列 |
array | $keys |
取り出すキー |
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');
type | name | summary |
---|---|---|
array | $array |
対象配列 |
int | $position |
取得する位置 |
bool | $return_key = false |
true にすると値ではなくキーを返す |
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
string|int|array | $key |
取得したい位置のキー |
mixed | $default = null |
見つからなかったときのデフォルト値。指定しないと例外。$key が配列の場合は見つからなかったキー全てに代入される |
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
| $value |
|
| $key = null |
|
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']);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
?int | $count = null |
取り出す個数 |
| $preserve_keys = false |
|
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([]);
type | name | summary |
---|---|---|
int|float|string|DateTimeInterface | $start |
最初の値 |
int|float|string|DateTimeInterface | $end |
最後の値 |
int|float|string|null|DateInterval | $step = null |
増分 |
| $options = [] |
|
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
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
int | $length |
取り出す件数 |
?callable | $rankfunction = null |
ランク付けクロージャ |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
array|callable | $keymap |
マップ配列かキーを返すクロージャ |
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']);
type | name | summary |
---|---|---|
array|Traversable | $array |
対象配列 |
array|int|string | $keys |
伏せるキー |
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, ]);
type | name | summary |
---|---|---|
iterable | $array |
|
array | ...$maps |
|
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 を使えという内なる声が聞こえなくもない。
type | name | summary |
---|---|---|
array | $schema |
スキーマ配列 |
mixed | ...$arrays |
検証する配列(可変引数。マージされる) |
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'], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string|iterable|Closure | $columns |
抽出項目 |
int|string|null | $index = null |
キーとなるキー |
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);
type | name | summary |
---|---|---|
array | &$array |
配列 |
mixed | $value |
設定する値 |
array|string|int|null | $key = null |
設定するキー |
callable|null | $condition = null |
追加する条件 |
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']);
type | name | summary |
---|---|---|
iterable|array|object | ...$variadic |
共通項を取る配列(可変引数) |
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']);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
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');
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string|callable|null | $format = null |
書式文字列あるいはクロージャ |
?string | $glue = null |
結合文字列。未指定時は implode しない |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string|array | $key_prefix |
キー側の付加文字列 |
string|array | $val_prefix = "" |
値側の付加文字列 |
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'], ]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
?array | $template = null |
抽出要素とそのデフォルト値 |
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']);
type | name | summary |
---|---|---|
array|ArrayAccess | &$array |
配列 |
string|int|array|callable | $key |
伏せたいキー。配列を与えると全て伏せる。クロージャの場合は true 相当を伏せる |
mixed | $default = null |
無かった場合のデフォルト値 |
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」のように振る舞う。
type | name | summary |
---|---|---|
array | $array |
対象配列 |
callable | $callback |
コールバック |
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], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
string|array|null | $column = null |
キー名 |
?callable | $callback = null |
評価クロージャ |
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']]);
type | name | summary |
---|---|---|
array | ...$arrays |
対象配列(可変引数) |
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]);
type | name | summary |
---|---|---|
mixed | ...$variadic |
生成する要素(可変引数) |
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']);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
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);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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);
type | name | summary |
---|---|---|
iterable|object | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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"], ]);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
callable | $grouper |
グループ導出関数 |
callable | $comparator |
部分配列の比較関数 |
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();
type | name | summary |
---|---|---|
array|mixed | $needle |
調べる値 |
array | $haystack |
調べる配列 |
bool | $strict = false |
厳密フラグ |
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();
type | name | summary |
---|---|---|
array|mixed | $needle |
調べる値 |
array | $haystack |
調べる配列 |
bool | $strict = false |
厳密フラグ |
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();
type | name | summary |
---|---|---|
array | $array |
調べる配列 |
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();
type | name | summary |
---|---|---|
array | $array |
調べる配列 |
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, ]);
type | name | summary |
---|---|---|
T | $array |
対象配列 |
callable|int|null | $comparator = null |
比較関数。SORT_XXX も使える |
callable|callable[] | $schwartzians = [] |
シュワルツ変換に使用する仮想列 |
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);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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);
type | name | summary |
---|---|---|
iterable|object | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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);
type | name | summary |
---|---|---|
iterable | $array |
対象配列 |
mixed | $default = null |
無かった場合のデフォルト値 |
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);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
string|int|null | $key = null |
調べるキー |
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);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
string|int | $key |
調べるキー |
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');
type | name | summary |
---|---|---|
?string | $startdir = null |
高速化用の検索開始ディレクトリを指定するが、どちらかと言えばテスト用 |
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(); // オートロードを走らせなければ定義されている
type | name | summary |
---|---|---|
array | $aliases |
|
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', ]);
type | name | summary |
---|---|---|
string|object | $class |
クラス名 or オブジェクト |
?int | $filter = null |
アクセスレベル定数 |
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);
type | name | summary |
---|---|---|
T | $object |
対象オブジェクト |
Closure[] | $methods |
注入するメソッド |
array | $fields = [] |
注入するフィールド |
array | $implements = [] |
実装するインターフェース |
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);
type | name | summary |
---|---|---|
?string | $startdir = null |
高速化用の検索開始ディレクトリを指定するが、どちらかと言えばテスト用 |
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 相当を返す。
ファイル名からクラス名を逆引きする都合上、猛烈に遅いので注意。
type | name | summary |
---|---|---|
?object|?ClassLoader | $loader = null |
オートローダオブジェクト |
?string | $basePath = null |
パスが相対パスだった場合の基底ディレクトリ |
bool | $cache = true |
キャッシュを使用するか |
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');
type | name | summary |
---|---|---|
string|object | $class |
対象クラス・オブジェクト |
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');
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');
type | name | summary |
---|---|---|
string|object | $class |
対象クラス・オブジェクト |
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 している ]);
type | name | summary |
---|---|---|
string|object | $class |
|
bool | $autoload = true |
オートロードを呼ぶか |
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();
type | name | summary |
---|---|---|
string|object | $classname |
調べるクラス |
string | $constname = "" |
調べるクラス定数 |
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");
type | name | summary |
---|---|---|
string | $location |
配置パス。ファイル名を与えるとそのファイルを配置すべきクラス名を返す |
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');
type | name | summary |
---|---|---|
object | $object |
調べるオブジェクト |
string|array | $path |
パス文字列。配列も与えられる |
mixed | $default = null |
無かった場合のデフォルト値 |
string | $delimiter = "." |
パスの区切り文字。大抵は '.' か '/' |
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();
type | name | summary |
---|---|---|
null|object|int | $objectOrId |
対象オブジェクト or オブジェクトID |
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, ]);
type | name | summary |
---|---|---|
object | $object |
オブジェクト |
array | &$privates = [] |
継承ツリー上の private が格納される |
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);
type | name | summary |
---|---|---|
string | $namespace = "global" |
名前空間 |
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 が呼ばれて・・・)。
ローダーオブジェクトを返すが特に意味はなく、使うべきではない。
type | name | summary |
---|---|---|
?callable | $before = null |
読み込み前コールバック |
?callable | $after = null |
読み込み後コールバック |
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);
type | name | summary |
---|---|---|
mixed | ...$fields |
メンバー配列 |
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();
type | name | summary |
---|---|---|
string | $typename |
調べる型名 |
bool | $autoload = true |
オートロードを行うか |
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'");
type | name | summary |
---|---|---|
string | $sql |
値を埋め込む SQL |
array|mixed | $values |
埋め込む値 |
?callable | $quote = null |
値をクォートするクロージャ |
type | summary |
---|---|
mixed | 値が埋め込まれた SQL |
[F] sql_format ものすごく雑に SQL を整形する
ものすごく雑に SQL を整形する
function sql_format( string $sql, array $options = [] ): string
非常に荒くアドホックに実装しているのでこの関数で得られた SQL を実際に実行してはならない。 あくまでログ出力やデバッグ用途で視認性を高める目的である。
JOIN 句は FROM 句とみなさず、別句として処理する。 AND と && は微妙に処理が異なる。 AND は改行されるが && は改行されない(OR と || も同様)。
type | name | summary |
---|---|---|
string | $sql |
整形する SQL |
array | $options = [] |
整形オプション |
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");
type | name | summary |
---|---|---|
mixed | $value |
クオートする値 |
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', ], ]);
type | name | summary |
---|---|---|
string | $selector |
CSS セレクタ |
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 ");
type | name | summary |
---|---|---|
iterable | $csvarrays |
連想配列の配列 |
array | $options = [] |
オプション配列。fputcsv の第3引数以降もここで指定する |
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']], ]);
type | name | summary |
---|---|---|
string|resource | $csvstring |
CSV 的文字列。ファイルポインタでも良いが終了後に必ず閉じられる |
array | $options = [] |
オプション配列。fgetcsv の第3引数以降もここで指定する |
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="<value>" 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="{"a":"A"}"');
type | name | summary |
---|---|---|
iterable | $array |
属性配列 |
string|array|null | $options = [] |
オプション配列 |
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>');
type | name | summary |
---|---|---|
string | $html |
html 文字列 |
array | $options = [] |
オプション配列 |
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's content</a>' ); // ネストした配列を与えると再帰される that( html_tag([ 'div#wrapper' => [ 'b.class1' => [ '<plain>', ], 'b.class2' => [ '<plain1>', 's' => '<strike>', '<plain2>', ], ], ])) ->isSame('<div id="wrapper"><b class="class1"><plain></b><b class="class2"><plain1><s><strike></s><plain2></b></div>' );
type | name | summary |
---|---|---|
string|array | $selector |
|
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" ');
type | name | summary |
---|---|---|
array | $iniarray |
ini 化する配列 |
array | $options = [] |
オプション配列 |
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]);
type | name | summary |
---|---|---|
string | $inistring |
ini 文字列 |
array | $options = [] |
オプション配列 |
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", }');
type | name | summary |
---|---|---|
mixed | $value |
encode する値 |
array | $options = [] |
JSON_*** をキーにした連想配列。 値が false は指定されていないとみなされる |
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");
type | name | summary |
---|---|---|
string | $value |
JSON 文字列 |
array | $options = [] |
JSON_*** をキーにした連想配列。値が false は指定されていないとみなされる |
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]`");
type | name | summary |
---|---|---|
array | $ltsvarray |
配列 |
array | $options = [] |
オプション配列 |
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], ]);
type | name | summary |
---|---|---|
string | $ltsvstring |
LTSV 的文字列 |
array | $options = [] |
オプション配列 |
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 ");
type | name | summary |
---|---|---|
array | $array |
配列 |
array | $option = [] |
オプション配列 |
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 | ");
type | name | summary |
---|---|---|
array | $array |
連想配列の配列 |
array | $option = [] |
オプション配列 |
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"');
type | name | summary |
---|---|---|
array | $pamlarray |
配列 |
array | $options = [] |
オプション配列 |
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, ]);
type | name | summary |
---|---|---|
string | $pamlstring |
PAML 文字列 |
array | $options = [] |
オプション配列 |
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,);
type | name | summary |
---|---|---|
iterable | $xmlssarrays |
連想配列の配列 |
array | $options = [] |
オプション配列 |
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], ]);
type | name | summary |
---|---|---|
string|resource | $xmlssstring |
XML SpreadSheet 的文字列。ファイルポインタでも良い |
array | $options = [] |
オプション配列 |
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 を返す
type | name | summary |
---|---|---|
string|int|DateTimeInterface | $datetime |
調べる日付 |
array | $excluded_dates |
除外日(いわゆる祝休日リスト) |
?int | $follow_count |
ずらす範囲 |
string | $format = "Y-m-d" |
日付フォーマット($excluded_dates の形式+返り値の形式) |
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);
type | name | summary |
---|---|---|
?string | $format |
フォーマット |
string|int|float|DateTimeInterface|null | $datetimedata = null |
日時データ。省略時は microtime |
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"]);
type | name | summary |
---|---|---|
string | $format |
フォーマット。 null を与えるとタイムスタンプで返す |
string | $datetimestring |
日時データ |
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');
type | name | summary |
---|---|---|
string | $interval |
ISO8601継続時間文字列か相対表記 |
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);
type | name | summary |
---|---|---|
DateInterval|string|float|int | $interval |
DateInterval インスタンスか間隔を表す ISO8601 文字列 |
string|float|int | $basetime = 0 |
基準日時(省略時 1970/01/01 00:00:00) |
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秒');
type | name | summary |
---|---|---|
DateInterval|string|float|int | $interval |
DateInterval インスタンスか間隔を表す ISO8601 文字列 |
string|array|Closure | $format = null |
時刻フォーマット |
string|int | $limit_type = "y" |
どこまで換算するか([c |
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();
type | name | summary |
---|---|---|
mixed | $datetime |
日時を表す引数 |
string | $cronlike |
マッチパターン |
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');
type | name | summary |
---|---|---|
string | $datetimedata |
日時文字列 |
int|string|DateInterval | $modify |
加減算値 |
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');
type | name | summary |
---|---|---|
string | $datetimestring |
日時文字列 |
array | &$parsed = [] |
パースの参考情報が格納される(内部向け) |
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'));
type | name | summary |
---|---|---|
string|int|float|DateTimeInterface | $datetimedata |
日時データ |
int|null | $baseTimestamp = null |
日時データ |
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 で取得可能だが、行儀の悪い(@抑制を見ない)エラーハンドラが設定されていると例外として送出されることがあるので注意。
type | name | summary |
---|---|---|
string | $datetime_string |
日付形式の文字列 |
string | $format = "Y/m/d H:i:s" |
フォーマット文字列 |
int | $overhour = 0 |
24時以降をどこまで許すか |
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);
type | name | summary |
---|---|---|
bool | $persistence = true |
固定化フラグ |
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();
type | name | summary |
---|---|---|
callable | $handler |
エラーハンドラ |
int | $error_types = 32767 |
エラータイプ |
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' ]);
type | name | summary |
---|---|---|
int | $flags = DEBUG_BACKTRACE_PROVIDE_OBJECT |
debug_backtrace の引数 |
array | $options = [] |
フィルタ条件 |
type | summary |
---|---|
array | バックトレース |
[F] error エラー出力する
エラー出力する
function error( string|mixed $message, resource|string|mixed $destination = null ): int
第1引数 $message はそれらしく文字列化されて出力される。基本的にはあらゆる型を与えて良い。
第2引数 $destination で出力対象を指定する。省略すると error_log 設定に従う。 文字列を与えるとファイル名とみなし、ファイルに追記される。 ファイルを開くが、ファイルは閉じない。閉じ処理は php の終了処理に身を任せる。 したがって閉じる必要がある場合はファイルポインタを渡す必要がある。
type | name | summary |
---|---|---|
string|mixed | $message |
出力メッセージ |
resource|string|mixed | $destination = null |
出力先 |
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 を返すと設定前(ない場合は標準)のハンドラがコールされる。 実用上は「ログるかログらないか」くらいの差でしかない。
type | name | summary |
---|---|---|
Closure | $handler |
実行されるクロージャ |
bool | $atmark_error = false |
エラー抑制演算子をハンドリングするか |
int | $reserved_byte = 0 |
fatal 用に予約するサイズ |
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(); }
type | name | summary |
---|---|---|
int | $error_levels = E_ALL |
エラーレベル |
bool | $handle_atmark_error = false |
エラー抑制時もハンドリングするか |
type | summary |
---|---|
callable | restore するコールバック |
[F] set_trace_logger メソッド呼び出しロガーを仕込む
メソッド呼び出しロガーを仕込む
function set_trace_logger( Psr\Log\LoggerInterface $logger, string $target ): mixed
この関数はかなり実験的なもので、互換性を考慮しない。
type | name | summary |
---|---|---|
LoggerInterface | $logger |
書き出すファイル名 |
string | $target |
|
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 は比較的指定頻度が高いかつ互換性維持のため配列オプションではなく直に渡すことが可能になっている。
type | name | summary |
---|---|---|
?array | $traces = null |
debug_backtrace 的な配列 |
int|string|array | $option = [] |
オプション |
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" が格納される
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 |
その他の追加オプション |
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
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 |
その他の追加オプション |
type | summary |
---|---|
ProcessAsync|object | プロセスオブジェクト |
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);
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 |
その他の追加オプション |
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, ], ]);
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 |
その他の追加オプション |
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');
type | name | summary |
---|---|---|
string | $src |
コピー元パス |
string | $dst |
コピー先パス。末尾/でディレクトリであることを明示できる |
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, );
type | name | summary |
---|---|---|
string | $directory |
対象ディレクトリ |
int | $atime = 0 |
対象アクセス日時秒数 |
int | $mtime = 0 |
対象更新日時秒数 |
array|string | $excludePattern = [] |
除外パターン |
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 にのみ存在する ]);
type | name | summary |
---|---|---|
string | $path1 |
パス1 |
string | $path2 |
パス2 |
array | $options = [] |
オプション |
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);
type | name | summary |
---|---|---|
string | $dirname |
ディレクトリ名 |
bool | $recursive = true |
再帰フラグ |
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"));
type | name | summary |
---|---|---|
string | $path |
パス名 |
callable | $callback |
コールバック |
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();
type | name | summary |
---|---|---|
string | $file1 |
ファイル名1 |
string | $file2 |
ファイル名2 |
?int | $chunk_size = null |
読み込みチャンクサイズ |
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');
type | name | summary |
---|---|---|
string | $filename |
調べるファイル名 |
string | $extension = "" |
拡張子。nullや空文字なら拡張子削除 |
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], ]);
type | name | summary |
---|---|---|
string | $filename |
読み込むファイル名 |
array | $options = [] |
各種オプション |
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", ]);
type | name | summary |
---|---|---|
string | $dirname |
調べるディレクトリ名 |
array | $filter_condition = [] |
フィルタ条件 |
type | summary |
---|---|
?array | ファイルの配列 |
[F] file_matcher 各種属性を指定してファイルのマッチングを行うクロージャを返す
各種属性を指定してファイルのマッチングを行うクロージャを返す
function file_matcher(array $filter_condition): \Closure
※ 内部向け
type | name | summary |
---|---|---|
array | $filter_condition |
マッチャーコンディション配列(ソースを参照) |
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');
type | name | summary |
---|---|---|
string | $filename |
ファイル名(URL) |
array|bool | $prefer_extension = [] |
extension => mimetype のマップ(true を与えると組み込みを使用する) |
?array | &$parameters = null |
引数=値 の連想配列 |
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);
type | name | summary |
---|---|---|
string | $filename |
ファイル名 |
string|array | $needle |
探す文字列 |
int | $start = 0 |
読み込み位置 |
int|null | $end = null |
読み込むまでの位置。省略時は指定なし(最後まで)。負数は後ろからのインデックス |
int|null | $chunksize = null |
読み込みチャンクサイズ。省略時は 4096 の倍数に正規化 |
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');
type | name | summary |
---|---|---|
string | $filename |
読み書きするファイル名 |
callable | $callback |
書き込む内容。引数で $contents, $fp が渡ってくる |
int | $operation = 0 |
ロック定数(LOCL_SH, LOCK_EX, LOCK_NB) |
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", ]);
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世代目から圧縮される) |
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');
type | name | summary |
---|---|---|
string | $filename |
書き込むファイル名 |
string | $data |
書き込む内容 |
int | $umask = 2 |
ディレクトリを掘る際の umask |
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');
type | name | summary |
---|---|---|
array | $contents_tree |
コンテンツツリー |
?int | $umask = null |
umask |
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 を指定しても空行が飛ばされる動作になっている
- file における FILE_SKIP_EMPTY_LINES の単独指定は意味を成さないっぽい
- $end_line に負数を指定すると行番号の直指定となる
file_slice($filename, 120, -150)
で 120行目から150行目までを読む- 負数なのは気持ち悪いが、範囲指定のハイフン(120-150)だと思えば割と自然
使用用途としては
- 巨大ファイルの前半だけ読みたい
- 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", ]);
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 指定 |
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');
type | name | summary |
---|---|---|
string | $filename |
パス・ファイル名 |
string | $suffix |
付与するサフィックス |
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", ], ], ], ]);
type | name | summary |
---|---|---|
string | $dirname |
調べるディレクトリ名 |
array | $filter_condition = [] |
フィルタ条件 |
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();
type | name | summary |
---|---|---|
array|string | $patterns |
パターン配列(単一文字列可) |
string | $string |
調べる文字列 |
int | $flags = 0 |
FNM_*** |
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();
type | name | summary |
---|---|---|
array|string | $patterns |
パターン配列(単一文字列可) |
string | $string |
調べる文字列 |
int | $flags = 0 |
FNM_*** |
type | summary |
---|---|
bool | どれかにマッチしたら true |
[F] globstar globstar(再帰パターン有効)な glob
globstar(再帰パターン有効)な glob
function globstar( string $pattern, int $flags = 0 ): ?array
file_list でも代替可能だが、もっと手軽にササっとファイル一覧が欲しいこともある。
type | name | summary |
---|---|---|
string | $pattern |
glob パターン。** が使えること以外は glob と同じ |
int | $flags = 0 |
glob フラグ |
type | summary |
---|---|
?array | マッチしたファイル名配列 |
[F] mkdir_p ディレクトリを再帰的に掘る
ディレクトリを再帰的に掘る
function mkdir_p( string $dirname, int $umask = 2 ): bool
既に存在する場合は何もしない(エラーも出さない)。
type | name | summary |
---|---|---|
string | $dirname |
ディレクトリ名 |
int | $umask = 2 |
ディレクトリを掘る際の umask |
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(); }
type | name | summary |
---|---|---|
string | $path |
パス文字列 |
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");
type | name | summary |
---|---|---|
string | $path |
パス文字列 |
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"], ]);
type | name | summary |
---|---|---|
string | $path |
パス |
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("");
type | name | summary |
---|---|---|
string | $from |
元パス |
string | $to |
対象パス |
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");
type | name | summary |
---|---|---|
string | ...$paths |
パス文字列(可変引数) |
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);
type | name | summary |
---|---|---|
string | $dirname |
削除するディレクトリ名。glob パターンが使える |
bool | $self = true |
自分自身も含めるか。false を与えると中身だけを消す |
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 される文字列は互換性を担保しない。
type | name | summary |
---|---|---|
string | $src |
送信側ディレクトリ |
string | $dst |
受信側ディレクトリ |
array | $options = [] |
オプション |
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---');
type | name | summary |
---|---|---|
int | $octet |
8進数 |
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);
type | name | summary |
---|---|---|
string | $perms |
パーミッション文字列(ls -l 準拠) |
type | summary |
---|---|
int | 8進数 |
[F] tmpname 終了時に削除される一時ファイル名を生成する
終了時に削除される一時ファイル名を生成する
function tmpname( string $prefix = "rft", ?string $dir = null ): string
tempnam とほぼ同じで違いは下記。
- 引数が逆
- 終了時に削除される
- 失敗時に false を返すのではなく例外を投げる
type | name | summary |
---|---|---|
string | $prefix = "rft" |
ファイル名プレフィックス |
?string | $dir = null |
生成ディレクトリ。省略時は sys_get_temp_dir() |
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);
のように判定できる。
type | name | summary |
---|---|---|
object|string | $class |
|
string | $function |
|
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);
type | name | summary |
---|---|---|
mixed | $source = null |
元データ |
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);
type | name | summary |
---|---|---|
string | $expression |
eval コード |
mixed | ...$variadic |
引数名(可変引数) |
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');
type | name | summary |
---|---|---|
string | $methodname |
メソッド名 |
mixed | ...$defaultargs |
メソッドのデフォルト引数 |
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');
type | name | summary |
---|---|---|
string | $classname |
クラス名 |
mixed | ...$defaultargs |
コンストラクタのデフォルト引数 |
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]);
type | name | summary |
---|---|---|
string | $operator |
演算子 |
mixed | ...$operands |
右オペランド |
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);
type | name | summary |
---|---|---|
callable|null | $callback |
呼び出すクロージャ |
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']); // 呼び出し時の引数優先
type | name | summary |
---|---|---|
callable | $callable |
対象 callable |
array|ArrayAccess | $dependency |
引数候補配列 |
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');
type | name | summary |
---|---|---|
callable | $original |
元となる関数 |
string | $alias |
関数のエイリアス名 |
[F] function_shorten 関数の名前空間部分を除いた短い名前を取得する
関数の名前空間部分を除いた短い名前を取得する
function function_shorten(string $function): string
type | name | summary |
---|---|---|
string | $function |
短くする関数名 |
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();
type | name | summary |
---|---|---|
Closure | $closure |
調べるクロージャ |
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_splice
や array_sprintf
などで頻出しているので関数として定義する。
副作用はなく、クラスのロードや関数の存在チェックなどは行わない。あくまで型と形式で判定する。 引数は callable でなくても構わない。その場合単に false を返す。
type | name | summary |
---|---|---|
mixed | $callable |
対象 callable |
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
- 文字青・背景白・太字・斜体
という動作になる(記号で区切られていれば形式はどうでも良いということ)。 ただ、この指定方法は変更が入る可能性が高いのでスペースあたりで区切っておくのがもっとも無難。
type | name | summary |
---|---|---|
string | $string |
対象文字列 |
string | $color |
色とスタイル文字列 |
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');
type | name | summary |
---|---|---|
string | $string |
対象文字列 |
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 のオプション値に見えるが、ただの引数 ]);
type | name | summary |
---|---|---|
array | $rule |
オプションルール |
array|string|null | $argv = null |
パースするコマンドライン引数。未指定時は $argv が使用される |
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, //}
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); // が、一度しか呼ばれないので呼ばれない
type | name | summary |
---|---|---|
callable | $finalizer |
実行する php コード |
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);
type | name | summary |
---|---|---|
string|array | $target_pattern = "*.php" |
対象ファイルパターン(マッチしないものは無視される) |
string|array | $ignore_pattern = "*.phtml" |
除外ファイルパターン(マッチしたものは無視される) |
type | summary |
---|---|
array | 変更のあったファイル名配列 |
[F] get_uploaded_files $_FILES の構造を組み替えて $_POST などと同じにする
$_FILES の構造を組み替えて $_POST などと同じにする
function get_uploaded_files(?array $files = null): array
$_FILES の配列構造はバグとしか思えないのでそれを是正する関数。 第1引数 $files は指定可能だが、大抵は $_FILES であり、指定するのはテスト用。
サンプルを書くと長くなるので例はテストファイルを参照。
type | name | summary |
---|---|---|
?array | $files = null |
$_FILES の同じ構造の配列。省略時は $_FILES |
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 ]);
type | name | summary |
---|---|---|
iterable | $env_vars |
[キー => 環境変数名] |
type | summary |
---|---|
array | 環境変数 |
[F] ini_sets 複数の php.ini の設定をまとめて設定する
複数の php.ini の設定をまとめて設定する
function ini_sets(array $values): callable
返り値として「もとに戻すためのクロージャ」を返すので、復元するためにはそのクロージャを呼ぶだけで良い。
type | name | summary |
---|---|---|
array | $values |
ini のエントリ名と値の配列 |
type | summary |
---|---|
callable | ini を元に戻す callable |
[F] is_ansi リソースが ansi color に対応しているか返す
リソースが ansi color に対応しているか返す
function is_ansi(resource $stream): bool
パイプしたりリダイレクトしていると false を返す。
type | name | summary |
---|---|---|
resource | $stream |
調べるリソース |
type | summary |
---|---|
bool | ansi color に対応しているなら true |
type | summary |
---|---|
https://github.com/symfony/console/blob/v4.2.8/Output/StreamOutput.php#L98 | |
[F] php_binary php-cli のパスを返す
php-cli のパスを返す
function php_binary(): string|null
見つからない場合は null を返す。 実質的には PHP_BINARY と同じと考えてよい。 ただ PHP_BINARY は SAPI によって異なるので fpm 時にはこの関数を用いて php-cli のパスを得る必要がある。
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();
type | name | summary |
---|---|---|
iterable | $env_vars |
[環境変数名 => 値] |
type | summary |
---|---|
array | 成否配列 |
[F] sys_get_memory システムのメモリを取得する
システムのメモリを取得する
function sys_get_memory(int $cacheSecond = 0): array
php にはメモリ情報を返す関数が存在しないので共通のために作成。 Windows 版はかなりやっつけなので過度に呼んではならない。
$cacheSecond を指定するとその秒数分はキャッシュを返すようになる。
type | name | summary |
---|---|---|
int | $cacheSecond = 0 |
キャッシュ秒数 |
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 はデバッグ用なので運用では使わないこと。
type | name | summary |
---|---|---|
string | $directory |
一時ディレクトリ |
bool | $creates = true |
設定すると同時に作成するか |
| $check_settled = true |
|
type | summary |
---|---|
bool | 成功時に true |
type | summary |
---|---|
https://github.com/php/php-src/blob/af6c11c5f060870d052a2b765dc634d9e47d0f18/main/php_open_temporary_file.c | Example: |
[F] system_status システムの各種情報配列を返す
システムの各種情報配列を返す
function system_status( string $siunit = "", string $datetime_format = DateTime::RFC3339 ): array
Windows 版はオマケ実装。 この関数の結果は互換性を考慮しない。
type | name | summary |
---|---|---|
string | $siunit = "" |
バイト系数値の単位 |
string | $datetime_format = DateTime::RFC3339 |
日時系のフォーマット |
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']);
type | name | summary |
---|---|---|
iterable | $iterator |
イテレータ |
int|Closure | $length |
チャンクサイズ。クロージャを渡すと毎ループ(値, キー, ステップ, チャンク番号, イテレータ)でコールされて false を返すと1チャンク終了となる |
bool | $preserve_keys = false |
キーの保存フラグ |
[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]);
type | name | summary |
---|---|---|
iterable | $keys |
キー |
iterable | $values |
値 |
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']);
type | name | summary |
---|---|---|
iterable | $iterables |
結合する iterable |
| $preserve_keys = true |
|
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引数が渡ってくる
- 例えば $iterables が2つの場合
数が不一致の場合、(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"]);
type | name | summary |
---|---|---|
?callable | $callback |
コールバック |
iterable | ...$iterables |
iterable |
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]);
type | name | summary |
---|---|---|
iterable | $iterable |
iterable |
callable | ...$callbacks |
コールバック |
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);
type | name | summary |
---|---|---|
iterable | $iterable |
対象 iterator |
array | $chunk_sizes |
各チャンクの数を指定する |
bool | $preserve_keys = false |
キーを保存するか |
type | summary |
---|---|
array | $chunk_sizes の数+1 の iterable 配列 |
[N] ryunosuke\Functions\Package\math\
[F] functions
[F] average 引数の意味平均値を返す
引数の意味平均値を返す
function average(mixed ...$variadic): mixed
- 3座標の重心座標とか日付の平均とかそういうもの
- 配列は個数ではなくフラット展開した要素を対象にする
- 候補がない場合はエラーではなく例外を投げる
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
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]);
type | name | summary |
---|---|---|
array | $array |
対象配列 |
int | $from_base |
変換元基数 |
int | $to_base |
変換先基数 |
type | summary |
---|---|
array | 基数変換後の配列 |
type | summary |
---|---|
https://github.com/tuupola/base62/blob/2.x/LICENSE | |
[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);
type | name | summary |
---|---|---|
string | $formula |
計算式 |
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 になる
type | name | summary |
---|---|---|
int|mixed | $value |
対象の値 |
int|mixed | $min |
最小値 |
int|mixed | $max |
最大値 |
bool | $circulative = false |
true だと切り詰めるのではなく循環する |
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); // 正の無限大の方向
type | name | summary |
---|---|---|
int|float | $value |
丸める値 |
int | $precision = 0 |
有効桁数 |
mixed | $mode = 0 |
丸めモード(0 |
type | summary |
---|---|
float | 丸めた値 |
[F] maximum 引数の最大値を返す
引数の最大値を返す
function maximum(mixed ...$variadic): mixed
- 配列は個数ではなくフラット展開した要素を対象にする
- 候補がない場合はエラーではなく例外を投げる
Example:
that(maximum(-1, 0, 1))->isSame(1);
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
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);
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
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');
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
type | summary |
---|---|
mixed | 中央値 |
[F] minimum 引数の最小値を返す
引数の最小値を返す
function minimum(mixed ...$variadic): mixed
- 配列は個数ではなくフラット展開した要素を対象にする
- 候補がない場合はエラーではなく例外を投げる
Example:
that(minimum(-1, 0, 1))->isSame(-1);
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
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);
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
type | summary |
---|---|
mixed | 最頻値 |
[F] sum 引数の合計値を返す
引数の合計値を返す
function sum(mixed ...$variadic): mixed
- is_numeric でない値は除外される(計算結果に影響しない)
- 配列は個数ではなくフラット展開した要素を対象にする
- 候補がない場合はエラーではなく例外を投げる
Example:
that(sum(1, 2, 3, 4, 5, 6))->isSame(21);
type | name | summary |
---|---|---|
mixed | ...$variadic |
対象の変数・配列・リスト |
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 であるとする)。
- ただし行末が
({[
のいずれかであれば次の]})
までブロックを記載する機会が与えられる - ブロックを見つけたときは本来値となるべき値がキーに、ブロックが値となり、結果は必ず配列化される
@hogera
@hogera v1 "v2 v3"
@hogera {key: 123}
@hogera [123, 456]
@hogera ("2019/12/23")
$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'], ], ]);
type | name | summary |
---|---|---|
string|Reflector | $annotation |
アノテーション文字列 |
array|mixed | $schema = [] |
スキーマ定義 |
string|array | $nsfiles = [] |
ファイル名 or [ファイル名 => 名前空間名] |
type | summary |
---|---|
array | アノテーション配列 |
[F] console_log js の console に値を吐き出す
js の console に値を吐き出す
function console_log(mixed ...$values)
script タグではなく X-ChromeLogger-Data を使用する。 したがってヘッダ送信前に呼ぶ必要がある。
type | name | summary |
---|---|---|
mixed | ...$values |
出力する値(可変引数) |
type | summary |
---|---|
https://craig.is/writing/chrome-logger/techspecs | |
[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);
type | name | summary |
---|---|---|
string | $phpcode |
実行する php コード |
array | $contextvars = [] |
コンテキスト変数配列 |
int | $cachesize = 256 |
キャッシュするサイズ |
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', // 同一名前空間として返される ], ], ]);
type | name | summary |
---|---|---|
string | $filename |
ファイル名 |
array | $options = [] |
オプション配列 |
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;
してるときの Piyo
を Hoge\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');
type | name | summary |
---|---|---|
string | $shortname |
エイリアス名 |
string|array | $nsfiles |
ファイル名 or [ファイル名 => 名前空間名] |
array | $targets = ["const", "function", "alias"] |
エイリアスタイプ('const', 'function', 'alias' のいずれか) |
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 対応版とも言える。
type | name | summary |
---|---|---|
string | $phpcode |
ハイライトする php コード |
array|int | $options = [] |
オプション |
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; } ');
type | name | summary |
---|---|---|
string | $phpcode |
インデントする php コード |
array|int|string | $options = [] |
オプション |
type | summary |
---|---|
string | インデントされた php コード |
[F] php_opcode コード断片の opcode を返す
コード断片の opcode を返す
function php_opcode( string $phpcode, int $level = 131072 ): string
php レベルでサクッと取りたいことはあるし、ini 設定がややややこしく、簡単に取れない場合があるので関数化した。 phpdbg 等ではなく opcache で出すため、与えたコードは実行されることに注意。
type | name | summary |
---|---|---|
string | $phpcode |
php コード |
int | $level = 131072 |
opt_debug_level に渡される(URL 参照だが、正味使うのは 0x10000:最適化前, 0x20000:最適化後 くらいだろう) |
type | summary |
---|---|
string | opcode |
type | summary |
---|---|
https://www.npopov.com/2022/05/22/The-opcache-optimizer.html | |
[F] php_parse php のコード断片をパースする
php のコード断片をパースする
function php_parse( string $phpcode, array|int $option = [] ): \PhpToken[]
type | name | summary |
---|---|---|
string | $phpcode |
パースする php コード |
array|int | $option = [] |
パースオプション |
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);
type | name | summary |
---|---|---|
string | $phtml |
php コードを含む文字列 |
array | $option = [] |
オプション配列 |
array | &$mapping = [] |
変換表が格納される参照変数 |
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');
type | name | summary |
---|---|---|
string | $code |
|
int | $flags = 0 |
パースオプション |
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 らしい)
- 逐次ロックなので大量生成には全く向かない
type | name | summary |
---|---|---|
array | &$id_info = [] |
元になった生成データのレシーバ引数 |
array | $debug = [] |
デバッグ用引数(配列で内部の動的な値を指定できる) |
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']);
type | name | summary |
---|---|---|
string | $cidr |
cidr |
type | summary |
---|---|
array | IP アドレス |
[F] cidr_parse cidr を分割する
cidr を分割する
function cidr_parse(string $cidr): array
※ 内部向け
type | name | summary |
---|---|---|
string | $cidr |
|
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 してみれば一発で分かる
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 を温めたいような限定的なケースでしか使わないし使うべきでない。
type | name | summary |
---|---|---|
string | $url |
URL |
array | $params = [] |
FCGI パラメータ |
array|string | $stdin = "" |
FCGI ボディ |
array | $options = [] |
その他のオプション |
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');
type | name | summary |
---|---|---|
string|int|null | $target = null |
接続先 |
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倍になったとしてもそれは外れ値ではないだろう。 と考えるとそもそも「外れ値」の定義自体が不可能であり、余計なことは一切しない。
type | name | summary |
---|---|---|
array|string | $urls |
URLs |
int | $requests = 10 |
合計リクエスト |
int | $concurrency = 3 |
同時接続数 |
| $output = null |
|
type | summary |
---|---|
array | 結果配列 |
[F] http_delete http_request の DELETE 特化版
http_request の DELETE 特化版
function http_delete( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
type | summary |
---|---|
mixed | レスポンスボディ |
[F] http_get http_request の GET 特化版
http_request の GET 特化版
function http_get( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
type | summary |
---|---|
mixed | レスポンスボディ |
[F] http_head http_request の HEAD 特化版
http_request の HEAD 特化版
function http_head( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): array
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
type | summary |
---|---|
array | レスポンスヘッダ |
[F] http_patch http_request の PATCH 特化版
http_request の PATCH 特化版
function http_patch( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
type | summary |
---|---|
mixed | レスポンスボディ |
[F] http_post http_request の POST 特化版
http_request の POST 特化版
function http_post( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
type | summary |
---|---|
mixed | レスポンスボディ |
[F] http_put http_request の PUT 特化版
http_request の PUT 特化版
function http_put( string $url, mixed $data = [], $options = [], &$response_header = [], &$info = [] ): mixed
type | name | summary |
---|---|---|
string | $url |
対象 URL |
mixed | $data = [] |
パラメータ |
| $options = [] |
|
| &$response_header = [] |
|
| &$info = [] |
|
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', ]);
type | name | summary |
---|---|---|
array | $options = [] |
curl_setopt_array に渡される |
array | &$response_header = [] |
レスポンスヘッダが連想配列で格納される |
array | &$info = [] |
curl_getinfo が格納される |
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']], ];
type | name | summary |
---|---|---|
array | $urls |
実行する curl オプション |
array | $single_options = [] |
全 $urls に適用されるデフォルトオプション |
array | $multi_options = [] |
並列リクエストとしてのオプション |
array | &$infos = [] |
curl 情報やヘッダなどが格納される受け変数 |
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();
type | name | summary |
---|---|---|
string | $ipaddr |
調べられる IP/cidr アドレス |
string|array | $cidr |
調べる cidr アドレス |
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', ]);
type | name | summary |
---|---|---|
string | $fromipaddr |
ipaddrs |
string | $toipaddr |
ipaddrs |
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');
type | name | summary |
---|---|---|
string | $ipaddr |
調べる IP アドレス |
array | $options = [] |
オプション配列 |
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');
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 はスキームを省略できる
type | name | summary |
---|---|---|
string | $host |
ホスト名(プロトコルも指定できる) |
int|null | $port = null |
ポート番号。指定しないと ICMP になる |
int | $timeout = 1 |
タイムアウト秒 |
string | &$errstr = "" |
エラー文字列が格納される |
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" は全く別の意味になる)。
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 の前段でコールして無用なキャッシュを保存されないようにする」くらいの用途しかない。
この関数は互換性を考慮しない。
type | name | summary |
---|---|---|
int | $thresholdLifetime = 86400 |
生存期間が指定以上を対象にする |
int | $thresholdHits = 0 |
ヒット数が指定以下を対象にする |
bool | $deletedFile = true |
元ファイルが存在しないものを対象にする |
bool | $modifiedFile = true |
元ファイルが変更されたものを対象にする |
?Closure | $includeCondition = null |
強制的に維持する条件クロージャ |
?Closure | $excludeCondition = null |
強制的に除去する条件クロージャ |
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 に設定してもよい。少なくともエラーにはならないようにしてある。
この関数は互換性を考慮しない。
type | name | summary |
---|---|---|
array | $includePatterns = [] |
対象パターン |
array | $excludePatterns = [] |
除外パターン |
bool | $reset = false |
reset を伴うか |
?bool | $ignoreErrors = null |
エラーを無視するか(null なら SAPI に応じて自動) |
?string | $cachefile = null |
キャッシュファイル名(原則としてテスト用) |
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");
type | name | summary |
---|---|---|
callable | $callback |
実行するコールバック |
mixed | ...$variadic |
$callback に渡される引数(可変引数) |
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. ');
type | name | summary |
---|---|---|
string | $include_file |
include するファイル名 |
array | $array = [] |
extract される連想変数 |
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 であること以外はいかなる前提も置いてはならない)。
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', ]);
type | name | summary |
---|---|---|
string | $pattern |
glob パターン文字列 |
int | $flags = 0 |
glob フラグ |
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']);
type | name | summary |
---|---|---|
string | $pattern |
正規表現 |
string | $subject |
対象文字列 |
array | $default |
デフォルト値 |
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'], ]);
type | name | summary |
---|---|---|
string | $pattern |
正規表現 |
string | $subject |
対象文字列 |
int | $flags = 0 |
PREG 定数 |
int | $offset = 0 |
開始位置 |
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>');
type | name | summary |
---|---|---|
string | $pattern |
正規表現 |
array|string|callable | $replacements |
置換文字列 |
string | $subject |
対象文字列 |
int | $limit = -1 |
置換回数 |
null | &$count = null |
置換回数格納変数 |
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']);
type | name | summary |
---|---|---|
string | $pattern |
正規表現 |
string|callable | $replacement |
置換文字列 |
string | $subject |
対象文字列 |
array | &$matches = [] |
キャプチャ配列が格納される |
int | $limit = -1 |
置換回数 |
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"; }
type | name | summary |
---|---|---|
int | $probability |
分子 |
int | $divisor = 100 |
分母 |
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');
type | name | summary |
---|---|---|
array | $array |
配列 |
?int | $divisor = 100 |
分母 |
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]);
type | name | summary |
---|---|---|
mixed | ...$args |
候補 |
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);
type | name | summary |
---|---|---|
float | $min |
最小値 |
float | $max |
最大値 |
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);
type | name | summary |
---|---|---|
float | $average = 0.0 |
平均 |
float | $std_deviation = 1.0 |
標準偏差 |
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);
type | name | summary |
---|---|---|
int | $min |
最小値 |
int | $max |
最大値 |
?int | $count = null |
返す個数 |
type | summary |
---|---|
array | min~maxの数値の配列 |
[F] random_string 安全な乱数文字列を生成する
安全な乱数文字列を生成する
function random_string( int $length = 8, string $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ): string
type | name | summary |
---|---|---|
int | $length = 8 |
生成文字列長 |
string | $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" |
使用する文字セット |
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');
type | name | summary |
---|---|---|
string | $source |
元文字列 |
string|int | $initial = null |
初期文字列あるいは文字数 |
string|array | $charlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" |
使用する文字セット |
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;}');
type | name | summary |
---|---|---|
callable|ReflectionFunctionAbstract | $callable |
コードを取得する callable |
bool | $return_token = false |
true にすると生のトークン配列で返す |
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 は必ず型宣言の直前(ない場合は{の直前)に記述しなければならない。
type | name | summary |
---|---|---|
ReflectionFunctionAbstract|callable | $callable |
対象 callable |
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 で、他はグローバル」という使い分けの利便性のためだが、出力数が膨大になるので留意。
type | name | summary |
---|---|---|
string | $namespace |
吐き出す名前空間 |
bool | $false_only = true |
false 返しのみか。false を与えると全関数を返す |
type | summary |
---|---|
string | 吐き出された関数定義を含むファイル内容 |
[F] function_parameter 関数/メソッドの引数定義を取得する
関数/メソッドの引数定義を取得する
function function_parameter(\ReflectionFunctionAbstract|callable $eitherReffuncOrCallable): array
ほぼ内部向けで外から呼ぶことはあまり想定していない。
type | name | summary |
---|---|---|
ReflectionFunctionAbstract|callable | $eitherReffuncOrCallable |
関数/メソッドリフレクション or callable |
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']);
type | name | summary |
---|---|---|
callable | $callable |
対象 callable |
iterable|array | $arguments = [] |
デフォルト引数 |
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);
type | name | summary |
---|---|---|
callable | $callable |
対象 callable |
bool | $require_only = false |
true を渡すと必須パラメータの数を返す |
bool | $thought_variadic = false |
可変引数を考慮するか。 true を渡すと可変引数の場合に無限長を返す |
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', ]);
type | name | summary |
---|---|---|
callable | $callable |
対象 callable |
array|ArrayAccess | $dependency |
引数候補配列 |
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]);
type | name | summary |
---|---|---|
callable | $callable |
対象 callable |
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');
type | name | summary |
---|---|---|
?string | $type |
string だが実用上は getType 等で得られるインスタンスでよい |
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);
type | name | summary |
---|---|---|
ReflectionFunctionAbstract|ReflectionType|ReflectionType[]|null | $reflection_type = null |
getType 等で得られるインスタンス |
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:// の登録は全て解除されるので注意。
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);
type | name | summary |
---|---|---|
string | $path = "" |
パス名(実質的に一意なファイル名) |
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 で回せばコールスタック・回数・時間などが取得できる。 配列で欲しい場合は直に呼べば良い。
type | name | summary |
---|---|---|
array | $options = [] |
オプション配列 |
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('');
type | name | summary |
---|---|---|
string|null | &$var |
対象の変数 |
string | $initial = "" |
初期値。与えたときのみ初期化される |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $delimiter = "_" |
デリミタ |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $delimiter = "-" |
デリミタ |
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);
type | name | summary |
---|---|---|
?string | ...$variadic |
結合する文字列(可変引数) |
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);
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 |
転置のコスト |
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();
type | name | summary |
---|---|---|
?string|string | $string |
探される文字列 |
string|string[] | $with |
探す文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
type | summary |
---|---|
bool | 対象文字列で終わるなら true |
[F] include_string 変数を extract して include する(文字列指定)
変数を extract して include する(文字列指定)
function include_string( ?string|string $template, array $array = [] ): string
type | name | summary |
---|---|---|
?string|string | $template |
テンプレート文字列 |
array | $array = [] |
extract される連想変数 |
type | summary |
---|---|
string | レンダリングされた文字列 |
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');
type | name | summary |
---|---|---|
?string|string | $format |
フォーマット文字列 |
array | $array |
フォーマット引数 |
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'); }
type | name | summary |
---|---|---|
?string|string | $from |
変換元エンコーディング |
?string|string | $to |
変換先エンコーディング |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | $width |
丸める幅 |
string | $trimmarker = "..." |
省略文字列 |
int|null | $pos = null |
省略記号の差し込み位置 |
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 をコールすると戻る
type | name | summary |
---|---|---|
array | $options |
オプション配列 |
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']);
type | name | summary |
---|---|---|
?string|string | $pattern |
パターン文字列 |
?string|string | $subject |
対象文字列 |
int | $limit = -1 |
分割数 |
int | $flags = 0 |
フラグ |
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);
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
array | $codepoints = [] |
コードポイント配列 |
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'); // マルチバイトでも同じ
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | $width |
埋める幅 |
string | $pad_string = " " |
埋める文字列 |
int | $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT |
埋める位置 |
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 )
type | name | summary |
---|---|---|
?string | $string |
|
| $width |
|
| $pad_string = " " |
|
| $pad_type = ryunosuke\Functions\Package\STR_PAD_RIGHT |
|
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $replacement |
置換文字列 |
int | $start |
開始位置 |
?int | $length = null |
置換長 |
type | summary |
---|---|
string | 置換した文字列 |
[F] mb_trim マルチバイト対応 trim
マルチバイト対応 trim
function mb_trim(?string $string)
type | name | summary |
---|---|---|
?string | $string |
|
type | summary |
---|---|
https://github.com/symfony/polyfill-php84/ | |
[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は晴', '天なり', ]);
type | name | summary |
---|---|---|
?string|string | $string |
分割する文字列 |
int | $width |
分割する最大幅 |
?string | $break = "\n" |
分割文字 |
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']);
type | name | summary |
---|---|---|
string|array | $delimiter |
分割文字列。配列可 |
?string|string | $string |
対象文字列 |
int | $limit = PHP_INT_MAX |
分割数 |
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']);
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
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(["あいう", "いうえ", "うえお", "えお", "お"]);
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | $N |
N-gram の N |
string | $encoding = "UTF-8" |
マルチバイトエンコーディング |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $delimiter = "_" |
デリミタ(複数可) |
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}', // { } で囲まれているので区切り文字とみなされない ]);
type | name | summary |
---|---|---|
string|array | $delimiter |
分割文字列 |
?string|string | $string |
対象文字列 |
?int | $limit = null |
分割数。負数未対応 |
array|string | $enclosures = "'\"" |
囲い文字。 ["start" => "end"] で開始・終了が指定できる |
string | $escape = "\\" |
エスケープ文字 |
array | $options = [] |
オプション |
type | summary |
---|---|
array | 分割された配列 |
[F] render_file "hoge $hoge" 形式のレンダリングのファイル版
"hoge $hoge" 形式のレンダリングのファイル版
function render_file( ?string|string $template_file, array $array ): string
type | name | summary |
---|---|---|
?string|string | $template_file |
レンダリングするファイル名 |
array | $array |
レンダリング変数 |
type | summary |
---|---|
string | レンダリングされた文字列 |
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');
type | name | summary |
---|---|---|
?string|string | $template |
レンダリング文字列 |
array | $array |
レンダリング変数 |
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');
type | name | summary |
---|---|---|
?string|string | $template |
レンダリングする文字列 |
array|object | $vars |
レンダリング変数 |
callable | $tag = null |
ブロックと変数値が渡ってくるクロージャ(タグ付きテンプレートリテラルのようなもの) |
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 で略語は維持される
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $delimiter = "_" |
デリミタ |
bool | $keep_abbr = false |
すべて大文字の単語を1単語として扱うか |
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']);
type | name | summary |
---|---|---|
?string|string | $delimiter |
区切り文字 |
?string|string | $string |
対象文字 |
string|bool | $trimchars = true |
指定した文字を trim する。true を指定すると trim する |
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();
type | name | summary |
---|---|---|
?string|string | $string |
探される文字列 |
string|string[] | $with |
探す文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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); // 同上
type | name | summary |
---|---|---|
?string | $needle |
調べる文字列 |
iterable | $haystack |
候補配列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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, ], ]);
type | name | summary |
---|---|---|
string|array | $string |
対象文字列。配列を与えても動作する |
?string|string | $delimiter |
区切り文字 |
bool | $hashmode |
連想配列モードか |
bool | $strict = true |
true にすると列数が一致しない場合に null になる |
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}}');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $from |
開始文字列 |
?string|string | $to |
終了文字列 |
int | &$position = 0 |
開始位置。渡した場合次の開始位置が設定される |
string | $enclosure = "'\"" |
囲い文字。この文字中にいる $from, $to 文字は走査外になる |
string | $escape = "\\" |
エスケープ文字。この文字が前にある $from, $to 文字は走査外になる |
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"]);
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | $base = 10 |
基数 |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $prefix = "" |
削ぎ落とす先頭文字列 |
?string|string | $suffix = "" |
削ぎ落とす末尾文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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', '']);
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | ...$chunks |
分割の各文字数(可変引数) |
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);
type | name | summary |
---|---|---|
?string|string[] | ...$strings |
|
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");
type | name | summary |
---|---|---|
string | $string |
対象文字列 |
string | $characters = <<<TEXT<br>\\b\10\\d\177<br>TEXT |
対象とする制御文字 |
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']], ]);
type | name | summary |
---|---|---|
string|array|resource | $xstring |
元文字列 |
string|array|resource | $ystring |
比較文字列 |
array | $options = [] |
オプション配列 |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
int | $width |
丸める幅 |
string | $trimmarker = "..." |
省略文字列 |
int|null | $pos = null |
省略記号の差し込み位置 |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
array | $replacemap |
置換文字列 |
string|array | $enclosure = "'\"" |
囲い文字。この文字中にいる $from, $to 文字は走査外になる |
string | $escape = "\\" |
エスケープ文字。この文字が前にある $from, $to 文字は走査外になる |
?array | &$replaced = null |
置換されたペアがタプルで格納される |
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();
type | name | summary |
---|---|---|
string | $str1 |
文字列1 |
string | $str2 |
文字列2 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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();
type | name | summary |
---|---|---|
?string|string | $haystack |
対象文字列 |
string|array | $needle |
調べる文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
bool | $and_flag = false |
すべて含む場合に true を返すか |
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', ]);
type | name | summary |
---|---|---|
?string|string | $string |
調べる文字列 |
array | $candidates |
候補文字列配列 |
?float | &$percent = null |
マッチ度(%)を受ける変数 |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $prefix |
削ぎ落とす先頭文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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);
type | name | summary |
---|---|---|
?string|string | $string |
文字列 |
?string|string | $patch |
パッチ文字列 |
array | $options = [] |
オプション配列 |
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");
type | name | summary |
---|---|---|
iterable | $array |
値の配列 or 値の配列の配列 |
string | $delimiter = "," |
フィールド区切り文字 |
string | $enclosure = "\"" |
フィールドを囲む文字 |
string | $escape = "\\" |
エスケープ文字 |
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");
type | name | summary |
---|---|---|
string | $string |
対象文字列 |
array | $options = [] |
オプション配列 |
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");
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
?string|string | $suffix |
削ぎ落とす末尾文字列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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');
type | name | summary |
---|---|---|
?string|string | $subject |
対象文字列 |
array | $replaces |
読み換え配列 |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
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');
type | name | summary |
---|---|---|
?string|string | $subject |
対象文字列 |
?string|string | $search |
検索文字列 |
array|string | $replaces |
置換文字列配列(単一指定は配列化される) |
bool | $case_insensitivity = false |
大文字小文字を無視するか |
type | summary |
---|---|
string | 置換された文字列 |
[F] strcat 文字列結合の関数版
文字列結合の関数版
function strcat(?string|mixed ...$variadic): string
Example:
that(strcat('a', 'b', 'c'))->isSame('abc');
type | name | summary |
---|---|---|
?string|mixed | ...$variadic |
結合する文字列(可変引数) |
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, ]);
type | name | summary |
---|---|---|
?string|string | $haystack |
対象文字列 |
iterable | $needles |
位置を取得したい文字列配列 |
int | $offset = 0 |
開始位置 |
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 文字目で見つかった」のようなイメージ
type | name | summary |
---|---|---|
string | $haystack |
対象文字列 |
string | $needle |
位置を取得したい文字列 |
?int | $offset = null |
開始位置。 null を渡すと $nth の方向に応じて自動で定まる |
int | $nth = 1 |
左右指定兼スキップ数(正数で右探索、負数で左探索) |
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);
type | name | summary |
---|---|---|
?string|string | $haystack |
対象文字列 |
string|array | $needle |
探す文字 |
int | $offset = 0 |
開始位置 |
string | $escape = "\\" |
エスケープ文字 |
?string | &$found = null |
見つかった文字が格納される |
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);
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 の内、見つかった文字列が格納される |
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);
type | name | summary |
---|---|---|
string | $haystack |
対象文字列 |
string | $needle |
位置を取得したい文字列 |
?int | $offset = null |
開始位置。 null を渡すと末尾からの探索になる |
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');
type | name | summary |
---|---|---|
?string|string | $haystack |
調べる文字列 |
?string|string | $needle |
検索文字列 |
bool | $after_needle = true |
$needle より後ろを返すか |
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');
type | name | summary |
---|---|---|
?string|string | $string |
対象文字列 |
array | $replace_pairs |
置換するペア |
string | $escape = "\\" |
エスケープ文字 |
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引数版
type | name | summary |
---|---|---|
mixed | $var |
判定する値 |
mixed | $default = null |
空だった場合のデフォルト値 |
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 に変換できない
type | name | summary |
---|---|---|
mixed | $value |
取得される配列・オブジェクト |
string | $type |
型文字列 |
mixed | $default = null |
失敗したときのデフォルト値(null も立派な値なので例外を飛ばすためには未指定時にしなければならない) |
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();
type | name | summary |
---|---|---|
T | $object |
調べるオブジェクト |
string|object | $class |
クラス名 |
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');
type | name | summary |
---|---|---|
callable | $try |
try ブロッククロージャ |
?callable | $catch = null |
catch ブロッククロージャ |
mixed | ...$variadic |
$try に渡る引数 |
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); // 呼ばれている
type | name | summary |
---|---|---|
callable | $try |
try ブロッククロージャ |
?callable | $catch = null |
catch ブロッククロージャ |
?callable | $finally = null |
finally ブロッククロージャ |
mixed | ...$variadic |
$try に渡る引数 |
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)'); // 閉じている
type | name | summary |
---|---|---|
| $callback |
|
object|resource|array | ...$resources |
$try に渡る引数 |
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); // 呼ばれている
type | name | summary |
---|---|---|
callable | $try |
try ブロッククロージャ |
?callable | $finally = null |
finally ブロッククロージャ |
mixed | ...$variadic |
$try に渡る引数 |
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);
type | name | summary |
---|---|---|
callable | $try |
try ブロッククロージャ |
mixed | ...$variadic |
$try に渡る引数 |
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);
type | name | summary |
---|---|---|
callable | $try |
try ブロッククロージャ |
mixed | ...$variadic |
$try に渡る引数 |
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 を参照。
type | name | summary |
---|---|---|
string | $string |
base62 文字列 |
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');
type | name | summary |
---|---|---|
string | $string |
変換元文字列 |
type | summary |
---|---|
string | base62 文字列 |
[F] base64url_decode url safe な base64_decode
url safe な base64_decode
function base64url_decode(string $string): string
対で使うと思うので base64_encode を参照。
type | name | summary |
---|---|---|
string | $string |
base64url 文字列 |
type | summary |
---|---|
string | 変換元文字列 |
[F] base64url_encode url safe な base64_encode
url safe な base64_encode
function base64url_encode(string $string): string
れっきとした RFC があるのかは分からないが '+' => '-', '/' => '_' がデファクトだと思うのでそのようにしてある。 パディングの = も外す。
type | name | summary |
---|---|---|
string | $string |
変換元文字列 |
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, ]);
type | name | summary |
---|---|---|
string | $url |
DataURL |
array | &$metadata = [] |
スキームのメタ情報が格納される |
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");
type | name | summary |
---|---|---|
string | $data |
エンコードするデータ |
array | $metadata = [] |
エンコードオプション |
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"]));
type | name | summary |
---|---|---|
array | $formdata |
フォームデータ配列 |
?string | &$boundary = null |
バウンダリ文字列初期値兼レシーバ引数 |
?Closure | $encoder = null |
値のエンコーダだが実質的にファイルの検出に使う(デフォルトでは SplFileInfo がファイルと認識される) |
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);
type | name | summary |
---|---|---|
string | $formdata |
フォームデータ文字列 |
?string | $boundary = null |
バウンダリ文字列。省略時は1行目から推測する |
?Closure | $decoder = null |
値のデコーダだが実質的にファイルの検出に使う(デフォルトでは一時ファイルの SplFileInfo で返す) |
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']
- デフォルトのパーセントエンコーディング文字
- ['[', ']']
- [] のままにする(ブラケットは必ずしもパーセントエンコーディングが必須ではない)
- ['', '']
- ブラケットを削除する(他言語のために配列パラメータを抑止したいことがある)
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 |
配列ブラケット文字 |
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']], ]);
type | name | summary |
---|---|---|
string | $query |
クエリ文字列 |
?string | $arg_separator = null |
クエリ文字列 |
?int | $encoding_type = null |
クエリ文字列 |
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');
type | name | summary |
---|---|---|
array | $parts |
URI の各パーツ配列 |
array | $options = [] |
オプション |
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' => '', ]);
type | name | summary |
---|---|---|
string | $uri |
パースする URI |
array|string | $default = [] |
$uri に足りないパーツがあった場合のデフォルト値。文字列を与えた場合はそのパース結果がデフォルト値になる |
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);
type | name | summary |
---|---|---|
array|callable | $suite |
ベンチ対象処理 |
array | $args = [] |
各ケースに与えられる引数 |
int | $millisec = 1000 |
呼び出しミリ秒 |
bool | $output = true |
true だと標準出力に出力される |
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 キャッシュが有効になる
type | name | summary |
---|---|---|
string | $document_root |
公開ディレクトリ兼カレントディレクトリ |
string|callable | $router = null |
ルータースクリプト or クロージャ |
array | $options = [] |
オプション配列 |
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);
type | name | summary |
---|---|---|
string | $key |
キャッシュのキー |
?callable | $provider |
キャッシュがない場合にコールされる callable |
?string | $namespace = null |
名前空間 |
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
type | name | summary |
---|---|---|
CacheInterface | $cacher |
キャッシュオブジェクト |
string | $key |
キャッシュキー |
callable | $provider |
データプロバイダ |
?int | $ttl = null |
キャッシュ時間 |
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 に違いが出てくるので注意。
type | name | summary |
---|---|---|
?string | $directory = null |
キャッシュ保存ディレクトリ |
float | $clean_probability = 0 |
不要キャッシュの削除確率 |
?float | $clean_execution_time = null |
不要キャッシュの最大実行時間 |
type | summary |
---|---|
Cacheobject | psr-16 実装オブジェクト |
[F] function_configure 本ライブラリの設定を行う
本ライブラリの設定を行う
function function_configure(array|?string $option): array|string
各関数の挙動を変えたり、デフォルトオプションを設定できる。
type | name | summary |
---|---|---|
array|?string | $option |
設定。文字列指定時はその値を返す |
type | summary |
---|---|
array|string | 設定値 |
[F] function_resolve 本ライブラリの関数名を解決する
本ライブラリの関数名を解決する
function function_resolve(string $funcname): ?string
※ 内部向け
type | name | summary |
---|---|---|
string | $funcname |
関数名 |
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);
type | name | summary |
---|---|---|
string | $prefix = "global" |
|
int | $ttl = ryunosuke\Functions\Package\PHP_INT_MAX |
TTL |
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]);
type | name | summary |
---|---|---|
iterable|array | $numbers |
数値配列 |
int|float | $step = 1 |
連続とみなすステップ。負数を指定すれば逆順指定にも使える |
string|null|Closure | $separator = null |
連続列を結合する文字列(string: 文字結合、null: 配列、Closure: 2引数が渡ってくる) |
bool | $doSort = true |
ソートをするか否か。事前にソート済みであることが明らかであれば false の方が良い |
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 になる(参考)
type | name | summary |
---|---|---|
string|int | $key |
キー |
array|ArrayAccess | $arrayable |
調べる値 |
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 なので中身は変わらない
type | name | summary |
---|---|---|
mixed | $var |
array 化する値 |
bool | $recursive = true |
再帰的に行うなら true |
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
type | name | summary |
---|---|---|
int|string | $key |
調べるキー |
array|object | $value |
調べられる配列・オブジェクト |
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'); // 存在しないのでデフォルト値
type | name | summary |
---|---|---|
int|string | $key |
取得するキー |
array|object | $value |
取得される配列・オブジェクト |
mixed | $default = null |
なかった場合のデフォルト値 |
type | summary |
---|---|
mixed | $key の値 |
[F] cipher_metadata 暗号化アルゴリズムのメタデータを返す
暗号化アルゴリズムのメタデータを返す
function cipher_metadata(string $cipher): array
※ 内部向け
type | name | summary |
---|---|---|
string | $cipher |
暗号化方式(openssl_get_cipher_methods で得られるもの) |
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 を参照。
type | name | summary |
---|---|---|
string | $cipherdata |
復号化するデータ |
string|array | $password |
パスワード |
string|array | $ciphers = "aes-256-cbc" |
暗号化方式(openssl_get_cipher_methods で得られるもの) |
string | $tag = "" |
認証タグ |
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);
type | name | summary |
---|---|---|
mixed | $plaindata |
暗号化するデータ |
string|array | $password |
パスワード。十分な長さでなければならない |
string | $cipher = null |
暗号化方式(openssl_get_cipher_methods で得られるもの) |
string | &$tag = "" |
認証タグ |
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();
type | name | summary |
---|---|---|
mixed | $var |
bool 化する値 |
bool | $trim = false |
$var が文字列の場合に trim するか |
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']);
type | name | summary |
---|---|---|
mixed | ...$vars |
変数(可変引数) |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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 始まり
type | name | summary |
---|---|---|
mixed | $var |
判定する値 |
bool | $allow_float = true |
false にすると整数のみを許可する |
type | summary |
---|---|
bool | 数値なら true |
[F] is_empty 値が空か検査する
値が空か検査する
function is_empty( mixed $var, bool $empty_stdClass = false ): bool
empty
とほぼ同じ。ただし
- string
- "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();
type | name | summary |
---|---|---|
mixed | $var |
判定する値 |
bool | $empty_stdClass = false |
空の stdClass を空とみなすか |
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();
type | name | summary |
---|---|---|
mixed | $var |
判定する値 |
bool | $empty_stdClass = false |
空の stdClass を空とみなすか |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
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();
type | name | summary |
---|---|---|
mixed | $var |
調べる値 |
string | $typestring |
型文字列 |
null|object|string | $context = null |
self,static のコンテキスト |
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);
type | name | summary |
---|---|---|
mixed | $var |
対象の値 |
bool | $decimal = false |
小数として扱うか |
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); // 基数が指定できる
type | name | summary |
---|---|---|
mixed | $var |
数値化する値 |
int | $base = 10 |
基数。int 的な値のときしか意味をなさない |
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');
type | name | summary |
---|---|---|
mixed | $var |
評価する式 |
array | $contextvars = [] |
eval される場合のローカル変数 |
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');
type | name | summary |
---|---|---|
mixed | $var |
丸める値 |
int | $unit = 1000 |
桁単位。実用上は 1000, 1024 の2値しか指定することはないはず |
string|Closure | $format = "%.3f %s" |
書式フォーマット。 null を与えると sprintf せずに配列で返す |
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);
type | name | summary |
---|---|---|
mixed | $var |
数値化する値 |
int | $unit = 1000 |
桁単位。実用上は 1000, 1024 の2値しか指定することはないはず |
| $format = "%d%s" |
|
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);
type | name | summary |
---|---|---|
| $var |
|
type | summary |
---|---|
int|float | |
[F] stringify 値を何とかして文字列化する
値を何とかして文字列化する
function stringify(mixed $var): string
この関数の出力は互換性を考慮しない。頻繁に変更される可能性がある。
type | name | summary |
---|---|---|
mixed | $var |
文字列化する値 |
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(['<x>', ['<y>']]);
type | name | summary |
---|---|---|
mixed | $var |
$callback を適用する値 |
callable | $callback |
値変換コールバック |
mixed | ...$args |
$callback の残り引数(可変引数) |
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 だけ切り出せばよいのでは?」と思うかもしれないが、「(外部ライブラリなどで)手元に配列しか受け取ってくれない処理しかない」状況がまれによくある
type | name | summary |
---|---|---|
mixed | $var |
$callback を適用する値 |
callable | $callback |
値変換コールバック |
mixed | ...$args |
$callback の残り引数(可変引数) |
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*", ], ], ], ]');
type | name | summary |
---|---|---|
mixed | $value |
出力する値 |
bool|array | $options = [] |
オプション配列(var_export に寄せるため bool も受け付ける) |
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 くらい。
type | name | summary |
---|---|---|
mixed | $value |
エクスポートする値 |
bool|array | $return = false |
返り値として返すなら true. 配列を与えるとオプションになる |
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');
type | name | summary |
---|---|---|
mixed | $var |
ハッシュ化する値 |
string[] | $algos = ["md5", "sha1"] |
ハッシュアルゴリズム |
?bool | $base64 = true |
結果を base64 化するか |
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 になる
この関数の出力は互換性を考慮しない。頻繁に変更される可能性がある。
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']);
type | name | summary |
---|---|---|
| $var |
|
?array | &$parameters = null |
|
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
type | name | summary |
---|---|---|
mixed | $value |
出力する値 |
array | $options = [] |
出力オプション |
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);
type | name | summary |
---|---|---|
mixed | $var |
型を取得する値 |
array | $options = [] |
オプション配列 |
type | summary |
---|---|
string | 型名 |
[F] varcmp php7 の <=>
の関数版
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();
type | name | summary |
---|---|---|
mixed | $a |
比較する値1 |
mixed | $b |
比較する値2 |
?int | $mode = null |
比較モード(SORT_XXX)。省略すると型でよしなに選択 |
?int | $precision = null |
小数比較の際の誤差桁 |
type | summary |
---|---|
int | 等しいなら 0、 $a のほうが大きいなら > 0、 $bのほうが大きいなら < 0 |