「PHP」の版間の差分
提供:senooken JP Wiki
(連想配列) |
細 (→new) |
||
(同じ利用者による、間の209版が非表示) | |||
1行目: | 1行目: | ||
== | == About== | ||
=== | === About === | ||
PHPはプログラミング言語だ。汎用的なプログラミング言語だが、主にウェブサーバー上のソフトウェアで使用される。 | |||
GNU socialはPHPで記述されている。他にもWordPress・NextCloudなどがPHPで記述されている。これらのPHP製ソフトウェアはVPSだけではなく安価なレンタルサーバーでも動作するため、低コストで運用することができる。 | |||
PHPの公式リファレンスは日本語版があり、わかりやすくまとまっている。 | |||
*[https://www.php.net/manual/ja/langref.php PHP: 言語リファレンス - Manual] | |||
*[https://museum.php.net/ PHP: Release Archives (museum)]: PHP1からのソースコードの保管場所。 | |||
ウェブ上にはPHPに関するTipsが多く公開されており、大抵の疑問はウェブ検索で解決できる。 | |||
=== Version === | |||
PHPは言語の版数が上がる際、過去の版と互換性の無い破壊的変更がなされることがある。 | |||
開発者はこのリスクを軽減するために、非推奨の言語機能を避け、実行時の警告 (warning) を適切に処理するべきだ。 | |||
GNU socialは現在PHP 7系で動作する様に記述されており、PHP 8系への対応は作業途中だ。 | |||
==== PHP v8 ==== | |||
PHP v8になっていろいろ更新が入った。特にPHP v7.4からv8に更新する際のポイントがあるので整理する ([https://web.gnusocial.jp/post/2023/12/08/9535/ 行事: 「12月にPHP8.3が出るので、PHP8で増えた文法をおさらいしましょうセミナー」参加報告 | PHP8対応の肝は型とエラーレベル | GNU social JP Web])。 | |||
大きく以下2点がある。 | |||
#エラーレベルの上昇。 | |||
#型の厳格化。 | |||
エラーレベルが1段階上がったため、今までWarningで問題なかったものがFatal Errorになって動作しなくなる。他に、型が厳格になっている。 | |||
具体的には、php.ini/.user.iniで以下を指定して、PHP v7.4時点で警告にできるだけ対応しておく。 | |||
* | error_reporting=E_ALL ; -1 | ||
* | 続いて、phpソースファイルに以下を記入して型を厳密にしておく。 | ||
* | declare(strict_types=1); | ||
チェックツールがあるのでこれを使うと問題箇所などがわかる。 | |||
*PHP CodeSniffer: コーディング規約の準拠確認ツール。PHPのバージョンアップグレード可否チェックもできる。 | |||
*PHPStan: 静的解析ツール。引数の数、型不一致など、潜在的な問題を検出する。 | |||
*Rector | |||
まず上記2個を試して、おまけでRectorも試すとよい。 | |||
=== Tool === | |||
* [https://creatopia.jp/media/12102 PHPをブラウザで実行、動作確認できるおすすめツール4つ|Creatopia Media] | |||
PHPをWebブラウザーで実行、動作確認のツールがいくつかある。 | |||
* paiza.IO: [https://paiza.io/ja/projects/new Online PHP Editor | ブラウザでプログラミング・実行ができる「オンライン実行環境」| paiza.IO]: パーマリンク。 | |||
* phpsansbox.io [https://phpsandbox.io/ Write PHP online from your browser - PHPSandbox]: フレームワークも確認可能。 | |||
* OnlinePHP.io: [https://onlinephp.io/ PHP Sandbox - Execute PHP code online through your browser]: 複数バージョンの同時実行できる。 | |||
* 3v4l.org: [https://3v4l.org/ Online PHP editor | Test code in 250+ PHP versions]: パーマリンク。 | |||
=== | 3v4l.orgがパーマリンクがあって、複数バージョンの動作確認できるので、これがいいと思う。 | ||
=== Guide === | |||
*[https://notabug.org/gnusocialjp/gnusocial/src/main/DOCUMENTATION/DEVELOPERS/CONTRIBUTING/coding_standards.md DOCUMENTATION/DEVELOPERS/CONTRIBUTING/coding_standards.md] | |||
*[https://www.php-fig.org/psr/psr-12/ PSR-12: Extended Coding Style - PHP-FIG] | |||
PHPのコーディングの推奨規約がある。PSR-12というのがメジャーな模様。GNU socialでも採用されている。 | |||
「[https://stackoverflow.com/questions/1209720/what-should-i-name-my-php-class-file What should I name my PHP class file? - Stack Overflow]」にあるように、PSRではファイル名には記載がない。PSR-4や「[https://www.php-fig.org/bylaws/psr-naming-conventions/ PSR Naming Conventions - PHP-FIG]」に記載がある程度。 | |||
ただ、「[https://framework.zend.com/manual/1.12/en/coding-standard.naming-conventions.html Manual - Documentation - Zend Framework]」、「[https://book.cakephp.org/4/en/intro/conventions.html#file-and-class-name-conventions CakePHP Conventions - 4.x]」など、他の規約があり、クラス名と同じになっている。 | |||
PHPのクラス名は大文字小文字を区別しないが、わかりにくいので大文字小文字で、クラス名と一致させておくとよさそう。 | |||
ただし、viewなど、表示に直接結びついているものは、小文字でもいいかも。ファイル名とURLパスが同じほうが分かりやすい。 | |||
==== Naming ==== | |||
* [https://qiita.com/aaari95/items/8325131ce328e42e078c リーダブルコード・PSRから学ぶ 命名規則 #PHP - Qiita] | |||
* [https://www.php.net/manual/ja/function.disk-free-space.php PHP: disk_free_space - Manual] | |||
* [https://www.php.net/manual/ja/language.oop5.properties.php PHP: プロパティ - Manual] | |||
変数名やシンボルの命名規則。 | |||
* クラス名: CamelCase | |||
* | * メソッド名: mixedCase | ||
* | * 定数: UPPER_SNAKE_CASE | ||
* | * プロパティー: PSRでの規定はない。公式文書だとmixedCase | ||
* | * 変数名: PSRでの規定はない。公式文書だとlower_snake_case | ||
* | |||
上記で揃えるとよいだろう。 | |||
=== | === Performance === | ||
==== About ==== | |||
PHPのコーディング時に、速度に影響のある書き方がいろいろある。 | |||
* [https://thk.kanzae.net/net/itc/t2379/ PHP 高速化に関するメモ書き | Thought is free] | |||
* [https://qiita.com/kapitan/items/47d89449f23bc7f7c9bb PHPを少しでも速く動かしたい #PHP - Qiita] | |||
* [https://qiita.com/Hiraku/items/190443b33ee7a2167ade PHPで高速オシャレな配列操作を求めて #PHP - Qiita] | |||
一般論。 | |||
* | * 言語構造>組込関数>関数の順に速い。 | ||
* 複雑な方法より単純な方法のほうが速い。 | |||
* 特に配列関係の関数、呼び出しが頻発する処理などで重要。 | |||
=== | ==== Time ==== | ||
速度の話をするにあたって計測方法。 | |||
<?php | |||
/** | |||
* Time target function. | |||
* @param callable $callback Target function. | |||
* @return int|float Run time [ns]. | |||
*/ | |||
function timeit(callable $callback) | |||
{ | |||
$time = 'microtime'; | |||
$nanoFactor = 1000; | |||
if (function_exists('hrtime')) { | |||
$time = 'hrtime'; | |||
$nanoFactor = 1; | |||
} | |||
$start = $time(true); | |||
$callback(); | |||
$stop = $time(true); | |||
return ($stop - $start) * $nanoFactor; | |||
} | |||
echo timeit(function(){sleep(1);}); | |||
// one liner. | |||
echo (function($c){$s=hrtime(1);$c();return hrtime(1)-$s;})(function(){;}), " ns\n"; | |||
// arrow function | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function(){;}), " ns\n"; | |||
(function($c){$s=hrtime(true);$c();return hrtime(true)-$s;})(function(){sleep(1);}); | |||
?> | |||
ワンライナーか上記のtimeit関数で計測する。 | |||
==== Array ==== | |||
配列が特に重要。基本的に、array_mapなどの一括操作系関数を使うと遅い。foreachでやったほうが速い。可読性などの問題はある。 | |||
===== in_array/array_search ===== | |||
[https://thk.kanzae.net/net/itc/t2379/ PHP 高速化に関するメモ書き | Thought is free] | |||
in_array/array_searchは遅いらしい。書き方を変えたほうがいい。 | |||
PHPの言語構造の中で、isset/emptyが非常に重要。これらで値の有無判定ができる。 | |||
ただし、もともと単純配列になっているのを、連想配列に変換するくらいならば、forなどを使ってもその変換に時間がかかる。 | |||
=== | そして、in_arrayじゃなくて、for/ifで比較するくらいなら、in_arrayのほうが速い。実装の段階で工夫してキーに値を入れられるならそちら。そうでなければ、そのままin_arrayでよさそう。特に要素数が多い場合。 | ||
$a = range(0, 1000); | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){isset(array_flip($a)[500]);}), " ns\n"; | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){in_array(500, $a);}), " ns\n"; | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){isset(array_flip($a)[500]);}), " ns\n"; | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){isset(array_combine($a, range(1,count($a)))[500]);}), " ns\n"; | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){ | |||
foreach ($a as $k => $v){ | |||
$a[$v]=$k; | |||
unset($a[$k]); | |||
} | |||
isset($a[500]); | |||
}), " ns\n"; | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function()use($a){ | |||
foreach ($a as $v){ | |||
if ($v === 500) return true; | |||
} | |||
return false; | |||
}), " ns\n"; | |||
==Language Reference== | |||
===Types=== | |||
====Introduction==== | |||
Ref: [https://www.php.net/manual/en/language.types.intro.php PHP: Introduction - Manual]. | |||
PHPの変数は以下の型のいずれかの値となる。 | |||
* | *null | ||
*bool | |||
*int | |||
*float (floating-point number) | |||
*string | |||
*array | |||
*object | |||
*callable | |||
*resource | |||
C言語のようなlong/doubleのような精度ごとの型はない。 | |||
=== | ==== System ==== | ||
[https://www.php.net/manual/ja/language.types.type-system.php PHP: 型システム - Manual] | |||
組込型の他に、ユーザー定義の型、aliasなどいくつかの型がある。 | |||
===== 基本型 ===== | |||
言語に統合されていて、ユーザー定義で再現不能。 | |||
* 組込 | |||
** null | |||
** スカラー型: bool/int/float/string | |||
** array | |||
** object | |||
** resource | |||
** never | |||
** void | |||
** クラス内の相対型: self/parent/static | |||
* Value型: false/true | |||
* ユーザー定義型/クラス型 | |||
** インターフェイス | |||
** クラス | |||
** 列挙型 | |||
* callable | |||
===== 複合型 ===== | |||
複数の基本型を組み合わせた型。交差型とunion型で作れる。 | |||
* 交差型: 宣言した複数のクラス型をすべて満たす型。&で表現。T/U/Vの交差型はT&U&Vと書く。 | |||
* union型: 複数の型を受け入れる型。|で表現。T/U/Vのunion型はT|U|Vと書く。交差型を含む場合、T|(X&U)と丸括弧で囲む必要がある。 | |||
* | |||
* | |||
===== alias ===== | |||
PHPはmixedとiterableの2個の型のエイリアスに対応している。 | |||
* mixed=object|resource|array|string|float|int|bool|null: PHP 8.0.0で導入。mixedは型のトップ。他の全部の型はこの型の部分になる。 | |||
* iterable=Traversable|array: PHP 7.1.0で導入。foreachで反復可能でジェネレーター内でyield from可能。 | |||
ただし、ユーザー定義のエイリアスは未対応。 | |||
====Boolean==== | |||
*[https://www.php.net/manual/ja/language.types.boolean.php PHP: 論理型 (boolean) - Manual] | |||
*[https://qiita.com/minato-naka/items/50645a45998e91c83e2b PHP trueになるもの・falseになるもの確認 falseを入れた配列はどっちに? #初心者 - Qiita] | |||
条件判定にかかってくるので非常に重要。 | |||
まずは、下記のfalseになるもの一覧を把握し、それ以外はすべてtrueになるということを把握しておく。 | |||
*booleanのfalse | |||
*intの0 | |||
*floatの0.0 | |||
*stringの空文字列、"0" | |||
*要素数0個のarray | |||
*null (未初期化変数含む) | |||
stringの"0"と要素0のarrayがfalseになる点が重要。注意する。要素0のarrayは包含判定、検索などでよく使う。 | |||
stringの0ははまりどころ。stringは何がくるかわからないなら、strlenで文字数を見たほうが確実。 | |||
====Strings==== | |||
Ref: [https://www.php.net/manual/ja/language.types.string.php PHP: 文字列 - Manual]. | |||
非常に重要。 | |||
=====Literal===== | |||
文字列リテラルとしては4の表現がある。 | |||
*Single quote: <nowiki>''</nowiki> 変数展開されない。エスケープシーケンス無視。 | |||
*Double quote: "" 変数展開される。エスケープシーケンス解釈。 | |||
*Here document: <<<EOT 二重引用符扱いで変数展開される。 | |||
*Nowdoc: <<<'EOT' 一重引用符扱いで変数展開されない。 | |||
引用符内で引用符'を使う場合はバックスラッシュ\でエスケープが必要。バックスラッシュ自体の指定は二重\\。 | |||
Here document/Nowdocは終端IDのインデントで行頭を識別しており、インデントに意味があるので注意する。 | |||
echo <<<END | |||
a | |||
b | |||
c | |||
\n | |||
END; | |||
echo <<<'EOT' | |||
My name is "$name". I am printing some $foo->foo. | |||
Now, I am printing some {$foo->bar[1]}. | |||
This should not print a capital 'A': \x41 | |||
EOT; | |||
===== Escape sequence expansion ===== | |||
{| class="wikitable" | |||
|+エスケープされた文字 | |||
!記述 | |||
!意味 | |||
|- | |||
|<code>\n</code> | |||
|ラインフィード (LF またはアスキーの 0x0A (10)) | |||
|- | |||
|<code>\r</code> | |||
|キャリッジリターン (CR またはアスキーの 0x0D (13)) | |||
|- | |||
|<code>\t</code> | |||
|水平タブ (HT またはアスキーの 0x09 (9)) | |||
|- | |||
|<code>\v</code> | |||
|垂直タブ (VT またはアスキーの 0x0B (11)) | |||
|- | |||
|<code>\e</code> | |||
|エスケープ (ESC あるいはアスキーの 0x1B (27)) | |||
|- | |||
|<code>\f</code> | |||
|フォームフィード (FF またはアスキーの 0x0C (12)) | |||
|- | |||
|<code>\\</code> | |||
|バックスラッシュ | |||
|- | |||
|<code>\$</code> | |||
|ドル記号 | |||
|- | |||
|<code>\"</code> | |||
|二重引用符 | |||
|- | |||
|<code>\[0-7]{1,3}</code> | |||
|8進数: 正規表現 <code>[0-7]{1,3}</code> にマッチする文字シーケンスは、8 進数表記の 1 文字 (例:. <code>"\101" === "A"</code>) です。 正規表現にマッチする文字シーケンスは、8 進数表記の 1 文字です。 1 バイトに収まらない部分は、何もメッセージを出さずにオーバーフローします (例: <code>"\400" === "\000"</code>) 。 | |||
|- | |||
|<code>\x[0-9A-Fa-f]{1,2}</code> | |||
|16進数: 正規表現 <code>[0-9A-Fa-f]{1,2}</code> にマッチする文字シーケンスは、16 進数表記の 1 文字(例: <code>"\x41" === "A"</code>)です。 | |||
|- | |||
|<code>\u{[0-9A-Fa-f]+}</code> | |||
|Unicode: 正規表現 <code>[0-9A-Fa-f]+</code> にマッチする文字シーケンスは、Unicode のコードポイントです。 そのコードポイントの UTF-8 表現を文字列として出力します。 シーケンスを波括弧で囲む必要があります。例 <code>"\u{41}" === "A"</code> | |||
|} | |||
繰り返しますが、この他の文字をエスケープしようとした場合には、 バックスラッシュも出力されます! | |||
=====Variable expansion===== | |||
二重引用符とヒアドキュメントではエスケープシーケンスが解釈され、変数が展開される。<syntaxhighlight lang="php"> | |||
<?php | |||
$juice = "apple"; | |||
echo "He drank some $juice juice." . PHP_EOL; | |||
// 意図しない動作をします。"s" は、変数名として有効な文字です。よって、変数は $juices を参照しています。$juice ではありません。 | |||
echo "He drank some juice made of $juices." . PHP_EOL; | |||
// 参照する変数名を波括弧で囲むことで、変数名の終端を明示的に指定しています。 | |||
echo "He drank some juice made of {$juice}s."; | |||
// | |||
$juices = array("apple", "orange", "koolaid1" => "purple"); | |||
echo "He drank some $juices[0] juice.".PHP_EOL; | |||
echo "He drank some $juices[1] juice.".PHP_EOL; | |||
echo "He drank some $juices[koolaid1] juice.".PHP_EOL; | |||
// 複雑な例1 | |||
// これが動作しない理由は、文字列の外で $foo[bar] | |||
// が動作しない理由と同じです。 | |||
// PHP はまず最初に foo という名前の定数を探し、 | |||
// 見つからない場合はエラーをスローします。 | |||
// 定数が見つかった場合は、その値('foo' そのものではない) | |||
// を配列のインデックスとして使います。 | |||
echo "This is wrong: {$arr[foo][3]}"; | |||
// 動作します。多次元配列を使用する際は、 | |||
// 文字列の中では必ず配列を波括弧で囲むようにします。 | |||
echo "This works: {$arr['foo'][3]}"; | |||
// 複雑な例。二重展開で変数になる場合だけ式が使える模様。 | |||
$var1=9; | |||
echo "{${mb_strtolower('VAR1')}}"; // 9 | |||
?> | |||
</syntaxhighlight>波括弧はなくてもいいが、文字列が連結するなどして変数名の終端を区別できない場合に必須になる。 | |||
特に重要な挙動は以下。 | |||
* ""内だと、連想配列添字の引用符不能。 | |||
* ${}内だと、連想配列添字の引用符必要。 | |||
複雑な形式は{$ ... }がセット。{$ } 部分で変数が式扱いになる。 | |||
<nowiki>さらに複雑なことができる。{${}}を指定すると、内側の波括弧内で、${}部分が変数評価になる場合にだけ式を指定できる。動きがトリッキーすぎる。フォーマット文字列的なことには使えない。バグのもとになりそうなので使用を控えたほうがよさそう。</nowiki> | |||
=====Format===== | |||
Ref: | |||
*[https://chaika.hatenablog.com/entry/2023/02/14/120000 PHP 文字列中に変数で値を埋め込むやつのメモ - かもメモ] | |||
*[https://qiita.com/iez/items/8a36e5cba8bf6af21f45 python の string.format が便利だからPHPでも使いたい #PHP - Qiita] | |||
PHPにはPythonのformatメソッド相当はない。が似たような目的の関数がある。 | |||
*sprintf/vprintf | |||
*strtr | |||
strtrは第2引数にold => newの置換のペアの配列を渡す。やることは同じようなものだけどちょっと違う。 | |||
vsprintfは置換対象が可変長引数ではなく配列なだけ。 | |||
=====sprintf===== | |||
Ref: [https://www.php.net/manual/ja/function.sprintf.php PHP: sprintf - Manual] | |||
今後何度も使う。 | |||
== | C言語のprintfといろいろ違うところがある。<syntaxhighlight lang="php"> | ||
<?php | |||
$format = 'The %2$s contains %1$d monkeys. | |||
That\'s a nice %2$s full of %1$d monkeys.'; | |||
echo sprintf($format, $num, $location); | |||
echo sprintf("%'.9d\n", 123); // ......123 | |||
echo sprintf("%'.09d\n", 123); // 000000123 | |||
?> | |||
</syntaxhighlight>特徴的なのが`%数$指定子`で引数の番号を選べるところ。Pythonの`{数:指定子}`に似ている。 | |||
===== | 後は埋める文字を指定する際は'を前置。 | ||
. | =====文字列の切り出し===== | ||
いくつか方法がある。 | |||
*substr/mb_substr | |||
*strpos/mb_strpos/strrpos/mb_strrpos ([https://www.php.net/manual/ja/function.strpos.php PHP: strpos - Manual]) | |||
*split | |||
*preg_match ([https://www.php.net/manual/ja/function.preg-match.php PHP: preg_match - Manual]) | |||
preg_matchの自由度が高い。速度を気にしなくていいならこれでいいと思われる。ただ、引数の配列に入ってくるのがいまいち。関数の戻り値でほしい。 | |||
strposとsubstrを組み合わせると端の文字列を切り出せる。 | |||
=====文字列置換===== | |||
*[https://www.php.net/manual/ja/function.str-replace.php PHP: str_replace - Manual]: 日本語不能。記号の置換などで便利。 | |||
*[https://www.php.net/manual/ja/function.substr-replace.php PHP: substr_replace - Manual]: 日本語不能。指定した文字数の位置で置換する。 | |||
*preg_replace | |||
*explode/implode: 日本語OK。 | |||
str_replace( | |||
array|string $search, | |||
array|string $replace, | |||
string|array $subject, | |||
int &$count = null | |||
): string|array | |||
$search/$replaceが配列の場合、それぞれ前から順番に対応する。$searchの要素数が多い場合、$replaceは空文字が適用される。これでまとめて置換できる。 | |||
// <body text='black'> となります | |||
$bodytag = str_replace("%body%", "black", "<body text='%body%'>"); | |||
==== | substr_replace($text, <nowiki>''</nowiki>, -1); // 末尾1文字の削除。 | ||
// substr_replace($text, '.', mb_strrpos('_')); | |||
1文字などの置換ならmb_strrposとの組み合わせ。 | |||
$query = $request->query(); | |||
foreach ($query as $key => $value) { | |||
unset($query[$key]); | |||
$keys = explode('_', $key); | |||
$key = implode('_', array_slice($keys, 0, -1)) . '.' . $keys[count($keys)-1]; | |||
$query[$key] = $value; | |||
} | |||
日本語はexplode/implodeが無難で確実。 | |||
===== | =====startsWith/endsWith===== | ||
[https:// | PHP 8なら「[https://www.php.net/manual/en/function.str-starts-with.php PHP: str_starts_with - Manual]/[https://www.php.net/manual/en/function.str-ends-with.php PHP: str_ends_with - Manual]」がある。 | ||
PHP 8未満なら以下のようなコード。 | |||
function startsWith( $haystack, $needle ) { | |||
$length = strlen( $needle ); | |||
return substr( $haystack, 0, $length ) === $needle; | |||
} | |||
function endsWith( $haystack, $needle ) { | |||
$length = strlen( $needle ); | |||
if( !$length ) { | |||
return true; | |||
} | |||
return substr( $haystack, -$length ) === $needle; | |||
} | |||
mb_strlen/mb_substrでマルチバイト対応。 | |||
===== 改行分割 ===== | |||
* [https://stackoverflow.com/questions/3997336/explode-php-string-by-new-line Explode PHP string by new line - Stack Overflow] | |||
* [https://stackoverflow.com/questions/1483497/split-string-by-new-line-characters php - Split string by new line characters - Stack Overflow] | |||
* [https://stackoverflow.com/questions/7836632/how-to-replace-different-newline-styles-in-php-the-smartest-way How to replace different newline styles in PHP the smartest way? - Stack Overflow] | |||
===== | |||
<code>explode('\n', $csv)</code> のようなことをしたくなるが、改行が\nとは限らない。 | |||
$array = preg_split('/\R/u', $string); | |||
上記がいい。\Rが\r \n \n\rなどにマッチ。uで入力がUTF-8の場合を考慮。例えば、「腰」がuをつけないと分割されてしまう。 | |||
== | ===== trim ===== | ||
[https://www.php.net/manual/ja/function.trim.php PHP: trim - Manual] | |||
文字列の両端のホワイトスペースを除去する。 | |||
===== 文字列反復 ===== | |||
[https://www.php.net/manual/ja/function.str-repeat.php PHP: str_repeat - Manual] | |||
str_repeat(string $string, int $times): string | |||
文字列に対する乗算はstr_repeatで行う。他にarray_fillを使った方法もある。 | |||
プリペアードステートメントで(?,?)を作るときとかで使う。 | |||
function timeit(callable $callback) | |||
function | |||
{ | { | ||
} | <nowiki> </nowiki> $time = 'microtime'; | ||
</ | <nowiki> </nowiki> $nanoFactor = 1000; | ||
* | <nowiki> </nowiki> if (function_exists('hrtime')) { | ||
<nowiki> </nowiki> $time = 'hrtime'; | |||
<nowiki> </nowiki> $nanoFactor = 1; | |||
<nowiki> </nowiki> } | |||
<nowiki> </nowiki> $start = $time(true); | |||
* | <nowiki> </nowiki> $callback(); | ||
* | <nowiki> </nowiki> $stop = $time(true); | ||
<nowiki> </nowiki><nowiki> return ($stop - $start) * $nanoFactor; | |||
} | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){rtrim(str_repeat('?,', 5),',');}}) . '=rtrim'. PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){substr(str_repeat('?,', 5), 0, -1);}}) . '=substr' . PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i) {implode(',', array_fill(0, 5, '?'));}}) . '=array' . PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){rtrim(str_repeat('?,', 10000),',');}}) . '=rtrim'. PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){substr(str_repeat('?,', 10000), 0, -1);}}) . '=substr' . PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i) {implode(',', array_fill(0, 10000, '?'));}}) . '=array' . PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){substr(str_repeat('?,', 10000), 0, -1);}}) . '=substr' . PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i){rtrim(str_repeat('?,', 10000),',');}}) . '=rtrim'. PHP_EOL; | |||
echo timeit(function(){for ($i = 0; $i<10000; ++$i) {implode(',', array_fill(0, 10000, '?'));}}) . '=array' . PHP_EOL; | |||
/* | |||
552413=rtrim | |||
565660=substr | |||
997959=array | |||
6853087=rtrim | |||
6411850=substr | |||
755294953=array | |||
6507484=substr | |||
6451837=rtrim | |||
770350600=array</nowiki> | |||
<nowiki>*</nowiki>/ | |||
常に速いのはrtim。 | |||
===== 文字数カウント ===== | |||
[https://www.php.net/manual/ja/function.substr-count.php PHP: substr_count - Manual] | |||
行数カウントなどで文字列をカウントしたいことがそれなりにある。 | |||
substr_countでできる。 | |||
substr_count( | |||
string $haystack, | |||
string $needle, | |||
int $offset = 0, | |||
?int $length = null | |||
): int | |||
$text = 'This is a test'; | |||
echo substr_count($text, 'is'); // 2 | |||
substr_count($str,"\n"); | |||
===== BOMの判定 ===== | |||
* [https://www.fourier.jp/blog/php-read-csv-utf8-with-bom UTF-8BOM有無両対応のCSVファイル読み込み(PHP) | 株式会社フーリエ | Web戦略・システム開発[東京/浜松]] | |||
* [https://qiita.com/tf_okrt/items/be81cd66e38fc38d3aeb php で csv を読み込む上での備忘録 #PHP - Qiita] | |||
* [https://stackoverflow.com/questions/26679980/csv-upload-parsing-with-splfileobject-remove-bom php - CSV upload - parsing with SplFileObject - Remove BOM - Stack Overflow] | |||
*[https:// | |||
読み込んだファイルにUTF-8のBOMがあって、データ処理としてはBOMを除外したいことがある。 | |||
いくつか方法がある。 | |||
= | $header[0] = preg_replace('/^\xEF\xBB\xBF/', <nowiki>''</nowiki>, $header[0]); | ||
if ($file->fread(3) !== pack('C*', 0xEF, 0xBB, 0xBF)) { | |||
$bom = pack('CCC', 0xEF, 0xBB, 0xBF); | |||
$first = true; | |||
foreach ($file as $line) { | |||
if ($first && substr($line, 0, 3) === $bom) { | |||
$line = substr($line, 3); | |||
} | |||
$first = false; | |||
// your lines don't have a BOM, do your stuff | |||
} | } | ||
==== | 最後の方法がよいと思う。 | ||
$line = (substr($line, 0, 3) === "\xEF\xBB\xBF") ? trim(substr($line, 3), '"') : $line; | |||
SplFileObjectだと$csvObj->setFlags(SplFileObject::READ_CSV); でCSV扱いにしてしまうと、1列目はBOMつきでセルの解釈をしてしまうので、二重引用符もデータ扱いになる。BOM除去後にそれも除去しておく。 | |||
$current = $this->file->current(); | |||
if (count($current)) { | |||
$line = $current[0]; | |||
$current[0] = (substr($line, 0, 3) === "\xEF\xBB\xBF") ? trim(substr($line, 3), '"') : $line; | |||
} | |||
こういう | |||
====Array==== | |||
==== | |||
===== About ===== | |||
[https://www.php.net/manual/ja/language.types.array.php PHP: 配列 - Manual] | |||
PHPの配列は、順序マップ。 | |||
=== | array( | ||
key => value, | |||
key2 => value2, | |||
key3 => value3, | |||
... | |||
) | |||
全て連想配列。キーはint|string。キーを省略したら、登場したキーの数+1の添え字のキーに自動で採番される。ただし、先頭は0。 | |||
=====Create===== | |||
====== Basic ====== | |||
配列の作成方法がいくつかある。 | |||
*array()/[] | |||
==== | *explode ([https://www.php.net/manual/ja/function.explode.php PHP: explode - Manual]) | ||
*array_merge | |||
*[ | *array_map | ||
*[https:// | $arr[キー] = 値; | ||
$arr[] = 値; | |||
// キーは文字列か整数。 | |||
$arr = [ | |||
'key1' => 'value1', | |||
]; | |||
= | |||
array()の他に[]も使える。[]のほうが短いのでこちらがいいだろう。 | |||
$arrが存在しないか、null/falseの場合、新しい配列を作成する。ただし、この方法は万が一既存の変数があったら、追加になるのであまり推奨されない。明示的に初期化したほうがいい。 | |||
2行余分に増えるが、上記の形式が初期化もできるのでいいだろう。 | |||
explode(',', '物件コード,オーナーコード,棟数,M数,実戸数,a,b,c') | |||
['物件コード','オーナーコード','棟数','M数','実戸数','a','b','c'] | |||
explode(',', '物件コード,オーナーコード,棟数,M数,実戸数) | |||
['物件コード', 'オーナーコード', '棟数', 'M数', '実戸数'] | |||
explodeで配列を作ると短いのは、要素数8以上。詰めずに書いたら5以上。 | |||
ただ、余計な関数呼び出しが発生するから、あまりしないほうがいいかも。 | |||
====== Serial ====== | |||
* [https://www.php.net/manual/ja/function.array-fill.php PHP: array_fill - Manual] | |||
* [https://www.php.net/manual/ja/function.array-fill-keys.php PHP: array_fill_keys - Manual] | |||
* [https://www.php.net/manual/ja/function.range.php PHP: range - Manual] | |||
* [https://www.php.net/manual/ja/function.str-repeat.php PHP: str_repeat - Manual] | |||
同じ値の複数要素、連番データの作成方法がある。 | |||
array_fill(int $start_index, int $count, mixed $value): array | |||
$a = array_fill(5, 6, 'banana'); | |||
print_r($a); | |||
Array | |||
( | |||
[5] => banana | |||
[6] => banana | |||
[7] => banana | |||
[8] => banana | |||
[9] => banana | |||
[10] => banana | |||
) | |||
array_fill_keys(array $keys, mixed $value): array | |||
$keys = array('foo', 5, 10, 'bar'); | |||
$a = array_fill_keys($keys, 'banana'); | |||
array_fill_keys(['a', 'b'], 'ab'); | |||
print_r($a); | |||
Array | |||
( | |||
[foo] => banana | |||
[5] => banana | |||
[10] => banana | |||
[bar] => banana | |||
) | |||
range(0, 12) | |||
explode(',', str_repeat(",", 10)); | |||
連続データを作成出来たら、array_combine/array_keys/array_valuesなどの組み合わせで、キーと値は調整できる。 | |||
* range: 指定要素数配列 | |||
* array_fill/array_fill_keys: 指定値の指定要素数配列。連想配列で複数キーに同じ値を設定したい場合に使う。 | |||
* | |||
* | |||
==== | ====== Merge ====== | ||
[ | 配列の追加、結合。 | ||
$arr[キー] = 値; | |||
$arr[] = 値; | |||
[0]+[1]; // 右の配列を左の配列に追加したものを返す。同じキーは左優先。 | |||
array_push($arr, 'a', 'b'); // array_pushだと一度に複数追加できる。 | |||
array_unshift($arr, 'a', 'b'); // 先頭に追加。 | |||
array_merge($arr, [0, 1]); // 配列同士の追加。 | |||
$arr = [...$arr, ...[0, 1]] // PHP7.4以上。...演算子。性能はarray_mergeのほうが高い。 | |||
array_combine(['k1', 'k2'], [0, 1]); // ['k1' => 0, 'k2' => 1] | |||
基本は$arr[キー] $arr[]でいいだろう。キーと値の配列から連想配列を作るには、array_combine。重宝する。 | |||
+演算子の結合は注意が必要。同じキーだと追加されない。基本はarray_merge。 | |||
配列ではなく、配列要素の結合は以下が使える。 | |||
implode($arr); | |||
array_reduce($arr, function($c, $v){return $c.$v;}); | |||
単に文字列結合するならimplodeがシンプル。 | |||
指定した要素を全部の行に追加する場合。きれいな方法はない。 | |||
# foreach | |||
# array_map | |||
foreach ($array as &$row) { | |||
$row[] = $newElement; // 各行の末尾に要素を追加 | |||
} | |||
===== | $array = array_map(function($row) use ($newElement) { | ||
$row[] = $newElement; // 各行の末尾に要素を追加 | |||
return $row; | |||
}, $array); | |||
===== Read ===== | |||
====== 角括弧構文による配列要素へのアクセス ====== | |||
配列要素へのアクセスはarray[key]構文を使う。 | |||
[]を角括弧構文 (square bracket syntax) と呼んでいる ([https://www.php.net/manual/en/language.types.array.php PHP: Arrays - Manual])。 | |||
なお、これとは別でstring access operator (文字列アクセス演算子)、インデックス演算子と呼ぶこともあるが、演算子ではないので「[https://www.php.net/manual/en/language.operators.php PHP: Operators - Manual]」に記載はない。 | |||
未定義キーへのアクセスは、未定義変数へのアクセスと同じ扱い。つまり、E_WARNING レベルの警告 (PHP 8.0.0 より前のバージョンでは E_NOTICE) が発生し、結果が null。 | |||
https://chatgpt.com/c/67a405d2-7614-800b-9f41-811812718dac | |||
なお、配列以外の型に角括弧構文を使った文字列キーアクセスの挙動には注意が必要基本はFatal error (isset/emptyでのガードも不能) になる。 | |||
* null: Warning | |||
* object: Fatal error | |||
* int|float: Warning | |||
* string: PHP 7.3以下=OK、PHP 7.4以上=Warning、PHP 8.0以上=Fatal error | |||
stringはキーではなくて、数値アクセスなら許容される。PHP 7.1から負のインデックスも可能。 | |||
====== 末尾要素 ====== | |||
[https://hishikiryu.com/php-get-last-array-value/ 【PHP】配列の最後(末尾)の要素を取得まとめ array_key_last, count, end関数 | ヒシキリュウ.com] | |||
* array_key_last: PHP v7.3.0+ ($arr[array_key_last($arr)];)。 | |||
* count: 昔ながら ($arr[count($arr) - 1];)。 | |||
* end: 非推奨。 | |||
$ | |||
====== 指定要素の取得 ====== | |||
[https://gen0e0.hatenablog.com/entry/2018/03/16/164714 PHPの連想配列から一部を切り出す話 - あしたにっき] | |||
// | * []: | ||
* array_slice: 範囲取得。 | |||
* array_intersect/array_intersect_key: 指定したキーの配列だけ取得。 | |||
* array_diff/array_diff_key: 指定したキー以外の配列を取得。 | |||
* array_filter: 複雑な場合。 | |||
/ | 連想配列で指定キー/指定キー以外の一括取得でよく使う。 | ||
$needles = ['t1', 't2']; | |||
$haystack = ['t1' => 1, 't2' => 2]; | |||
array_intersect_key($haystack, array_flip($needles)); | |||
array_diff_key($haystack, array_flip($needles)); | |||
array_sliceは添え字がなかったら空配列を返してくれるので、添え字アクセスより安全。 | |||
====== 連想配列の先頭・末尾 ====== | |||
// | * [https://stackoverflow.com/questions/1028668/get-first-key-in-a-possibly-associative-array php - Get first key in a (possibly) associative array? - Stack Overflow] | ||
* [https://www.php.net/manual/ja/function.array-key-first.php PHP: array_key_first - Manual] | |||
* [http://taustation.com/php-head-tail-and-subarray/ PHP – 配列の先頭・末尾・部分配列の取出し(非破壊的) – TauStation] | |||
* [https://bashalog.c-brains.jp/15/03/10-172501.php PHP で配列の先頭要素の値を取得するきれいな方法を考える | バシャログ。] | |||
PHP 7.3からarray_key_firstがある。これを使う。7.3以前はreset。 | |||
/ | 他に、元配列を破壊していいなら、array_shift/array_popもある。 | ||
array_sliceで部分配列を取得して変数に格納して、array_shiftもある。 | |||
$ | $t = ['a' => 0, 'b' => 1]; | ||
$t2 = array_slice($t, 0, 1); | |||
var_export(array_shift($t2)); | |||
他にきれいなのはarray_keys/array_values[0]。これがいい。 | |||
====== 抽出 ====== | |||
* [https://www.php.net/manual/ja/function.array-splice.php PHP: array_splice - Manual] | |||
* [https://www.php.net/manual/ja/function.array-pop.php PHP: array_pop - Manual] | |||
* [https://www.php.net/manual/ja/function.array-shift.php PHP: array_shift - Manual] | |||
配列の分割などで、重複をなくすために、取得後削除したいことがある。 | |||
先頭と末尾ならarray_shift/array_pop。それ以外はarray_spliceを使う ([https://chatgpt.com/c/67344cfe-dc20-800b-9cd3-1dee66a4deab ChatGPT])。 | |||
$array = [1, 2, 3, 4, 5]; | |||
$index = 2; // 3番目の要素を取得したい (0から始まるインデックス) | |||
// 取得と削除を同時に行う | |||
$removedElement = array_splice($array, $index, 1); | |||
echo $removedElement[0]; // 3 | |||
print_r($array); // [1, 2, 4, 5] | |||
ただ、array_spliceは連想配列に使うと、キーが番号になる。 | |||
取得後unsetするのが無難。 | |||
====== array_column ====== | |||
===== | [https://www.php.net/manual/ja/function.array-column.php PHP: array_column - Manual] | ||
テーブルの取得結果の整形に非常に便利。 | |||
array_column(array $array, int|string|null $column_key, int|string|null $index_key = null): array | |||
* column_key: 抽出したいカラム。nullにすると全部の列。index_keyを指定しなかったら元の配列と同じ。 | |||
* index_key: 取得後の配列のキーにしたいカラム。 | |||
index_keyを指定しなければ、column_keyの単純配列。 | |||
=====Remove===== | |||
配列要素の削除方法がいくつかある。 | |||
*unset($arr[$key]); | |||
*array_shift($arr): 先頭要素を削除。削除済み要素を返す。破壊的な処理。 | |||
*array_pop($arr): 末尾要素を削除。削除済み要素を返す。破壊的な処理。 | |||
*array_slice ([https://www.php.net/manual/ja/function.array-slice.php PHP: array_slice - Manual]): 先頭・末尾の要素を除去した要素を返す。 | |||
$ar = [0, 1, 2]; | |||
foreach($ar as $e) { | |||
echo $e; | |||
if ($e === 1) { | |||
array_shift($ar); | |||
} | |||
} | |||
print_r($ar); | |||
012Array | |||
===== | ( | ||
[0] => 1 | |||
[1] => 2 | |||
* | ) | ||
途中で削除しても、foreachは詰めたりしない。 | |||
* | |||
===== Rename ===== | |||
* [https://fellowtuts.com/php/change-array-key-without-changing-order/ 3 Ways to Change Array Key without Changing the Order in PHP] | |||
* [https://stackoverflow.com/questions/9605143/how-to-rename-sub-array-keys-in-php How to rename sub-array keys in PHP? - Stack Overflow] | |||
連想配列のキーの置換、キーの更新、キー名の置換、キー名の更新をしたいことがある。 | |||
いくつか方法がある。 | |||
サブ配列の場合はarray_mapでやればいい。 | |||
function | $tags = array_map(function($tag) { | ||
return array( | |||
'name' => $tag['name'], | |||
'value' => $tag['url'] | |||
); | |||
}, $tags); | |||
シンプルな方法は配列で設定してunset | |||
foreach($tags as &$val){ | |||
$val['value'] = $val['url']; | |||
unset($val['url']); | |||
} | } | ||
他にはjsonを経由したり。array_keys/array_combineを使ったり。 | |||
$ | |||
=====Copy===== | |||
[https://stackoverflow.com/questions/6418903/how-to-clone-an-array-of-objects-in-php How to clone an array of objects in PHP? - Stack Overflow] | |||
配列変数を代入すると通常はそれでコピーになる。ただし、配列にオブジェクトがあると、そのオブジェクトはシャローコピーになる。 | |||
$new = array(); | |||
foreach ($old as $k => $v) { | |||
$new[$k] = clone $v; | |||
} | |||
上記のように配列要素をcloneでコピーして作る必要がある模様 ([https://www.php.net/manual/ja/language.oop5.cloning.php PHP: オブジェクトのクローン作成 - Manual])。 | |||
=====Comma===== | |||
*[https://www.php.net/manual/ja/language.types.array.php PHP: 配列 - Manual] | |||
*[https://kinsta.com/jp/blog/php-7-3/#trailing-comma-in-function-calls PHP 7.3の新機能(Kinstaで利用可能)] | |||
PHPでは配列の終端カンマは許容される。 | |||
他にも、名前空間のグループ指定はPHP7.2以上、関数の引数はPHP7.3以上で可能になった。 | |||
=====連想配列判定===== | |||
[https://qiita.com/Hiraku/items/721cc3a385cb2d7daebd 配列か連想配列か判定する #PHP - Qiita] | |||
<?php | |||
if (array_values($arr) === $arr) { | |||
echo '$arrは配列'; | |||
} else { | |||
echo '$arrは連想配列'; | |||
} | } | ||
これで添え字が、数字かどうかをみるのがいい模様。 | |||
=====Convert===== | |||
===== | ====== 2次元配列→1次元配列 ====== | ||
* [https:// | * [https://zenn.dev/akido_/articles/833232e489137f PHPで二次元配列を一次元配列に変換する方法] | ||
* [https:// | * [https://www.php.net/manual/ja/function.array-column.php PHP: array_column - Manual] | ||
* [https:// | * [https://qiita.com/harukasan/items/a0773aef27d838852e44 PHPのarray_columnが便利 #PHP - Qiita] | ||
$array = [ | |||
$array | [1, 2, 3], | ||
[4, 5, 6], | |||
[7, 8] | |||
]; | |||
array_reduce($array, 'array_merge', []); | |||
// Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8 ) | |||
===== | $array = [ | ||
[ | [ | ||
'staff' => [ | |||
'name1', | |||
'name2', | |||
'name3', | |||
], | |||
], | |||
[ | |||
'staff' => [ | |||
'name4', | |||
'name5', | |||
'name6', | |||
], | |||
], | |||
[ | |||
'staff' => [ | |||
'name7', | |||
'name8', | |||
'name9', | |||
], | |||
], | |||
[ | |||
'staff' => [ | |||
'name10', | |||
'name11', | |||
'name12', | |||
], | |||
], | |||
]; | |||
array_reduce(array_column($array, 'staff'), 'array_merge', []); | |||
// Array ( [0] => name1 [1] => name2 [2] => name3 [3] => name4 [4] => name5 [5] => name6 [6] => name7 [7] => name8 [8] => name9 [9] => name10 [10] => name11 [11] => name12 ) | |||
array_columnが非常に便利。 | |||
$rows = [ | |||
0 => [ 'id' => 40, 'title' => 'dave', 'comment' => 'Hello, world!'], | |||
1 => [ 'id' => 10, 'title' => 'alice', 'comment' => '你好,世界!'], | |||
]; | |||
var_export(array_column($rows, 'title', 'id')); | |||
// => | |||
// array ( | |||
// 40 => 'dave', | |||
// 10 => 'alice', | |||
// ) | |||
$rows = [ | |||
0 => [ 'id' => 40, 'title' => 'dave', 'comment' => 'Hello, world!'], | |||
1 => [ 'id' => 10, 'title' => 'alice', 'comment' => '你好,世界!'], | |||
]; | |||
var_export(array_column($rows, null, 'id')); | |||
// => | |||
// array ( | |||
// 40 => | |||
// array ( | |||
// 'id' => 40, | |||
// 'title' => 'dave', | |||
// 'comment' => 'Hello, world!', | |||
// ), | |||
// 10 => | |||
// array ( | |||
// 'id' => 10, | |||
// 'title' => 'alice', | |||
// 'comment' => '你好,世界!', | |||
// ), | |||
// ) | |||
= | $map = []; | ||
foreach ($table as $row) { | |||
$map[$row['括りオーナーコード']] = $row['オーナーコード']; | |||
} | |||
DBテーブルからの取得結果が2次元の連想配列になっている。ここから、IDをキーにして、特定の値を取得するmapを作ったり、レコード行を取得できる。 | |||
自前でfor文で数行のコードでできるが、関数だと楽。 | |||
====== | ====== 多次元連想配列→一次元連想配列 ====== | ||
https://chatgpt.com/c/673fd301-45c4-800b-bec8-02302ad01383 | |||
再帰関数で処理する。 | |||
function flattenArray(array $array, string $prefix = <nowiki>''</nowiki>): array { | |||
<nowiki> </nowiki> $result = []; | |||
<nowiki> </nowiki> foreach ($array as $key => $value) { | |||
===== | <nowiki> </nowiki> $newKey = $prefix === <nowiki>''</nowiki> ? $key : $prefix . '.' . $key; | ||
<nowiki> </nowiki> if (is_array($value)) { | |||
<nowiki> </nowiki> // 再帰的に呼び出して配列をフラットにする | |||
<nowiki> </nowiki> $result += flattenArray($value, $newKey); | |||
<nowiki> </nowiki> } else { | |||
<nowiki> </nowiki> // フラット化した結果にキーと値を追加 | |||
<nowiki> </nowiki> $result[$newKey] = $value; | |||
<nowiki> </nowiki> } | |||
<nowiki> </nowiki> } | |||
<nowiki> </nowiki> return $result; | |||
} | |||
// 使用例 | |||
$nestedArray = [ | |||
<nowiki> </nowiki> 'user' => [ | |||
<nowiki> </nowiki> 'name' => 'Alice', | |||
<nowiki> </nowiki> 'details' => [ | |||
<nowiki> </nowiki> 'age' => 25, | |||
<nowiki> </nowiki> 'address' => [ | |||
<nowiki> </nowiki> 'city' => 'New York', | |||
<nowiki> </nowiki> 'zip' => '10001' | |||
<nowiki> </nowiki> ] | |||
<nowiki> </nowiki> ] | |||
<nowiki> </nowiki> ], | |||
<nowiki> </nowiki> 'status' => 'active' | |||
]; | |||
$flattenedArray = flattenArray($nestedArray); | |||
print_r($ | print_r($flattenedArray); | ||
Array | |||
( | ( | ||
[ | [user.name] => Alice | ||
[ | [user.details.age] => 25 | ||
[user.details.address.city] => New York | |||
[user.details.address.zip] => 10001 | |||
[status] => active | |||
) | ) | ||
連想配列なのでarray_mergeではなく+=でOK。 | |||
= | |||
======連想配列→単純配列====== | |||
associative arrayをsimple arrayに変換する。 | |||
======連想配列→単純配列====== | |||
associative arrayをsimple arrayに変換する。 | |||
[https://stackoverflow.com/questions/15191903/convert-an-associative-array-to-a-simple-array-of-its-values-in-php Convert an associative array to a simple array of its values in php - Stack Overflow] | [https://stackoverflow.com/questions/15191903/convert-an-associative-array-to-a-simple-array-of-its-values-in-php Convert an associative array to a simple array of its values in php - Stack Overflow] | ||
965行目: | 982行目: | ||
*array_map(null, array_keys($a1), array_values($a1));: 連想配列の[[key,value], [key2, valu2]] 形式。 | *array_map(null, array_keys($a1), array_values($a1));: 連想配列の[[key,value], [key2, valu2]] 形式。 | ||
後者のパターンはそれなりに使う気がする。 | 後者のパターンはそれなりに使う気がする。 | ||
いくつか方法がある。 | DBテーブルから結果を取得後、必要なカラムの単純配列が欲しい場合もarray_mapを使う。 | ||
#array_combine | $ar = [ | ||
#array_fill_keys | ['k1' => 'v11', 'k2' => 'v12'], | ||
['k1' => 'v21', 'k2' => 'v22'], | |||
]; | |||
var_export(array_map(function($e){return $e['k1'];}, $ar)); | |||
/* | |||
array ( | |||
0 => 'v11', | |||
1 => 'v21', | |||
) | |||
*/ | |||
// mapが欲しければarray_combineを併用する。 | |||
var_export(array_combine(array_map(function($e){return $e['k2'];}, $ar), array_map(function($e){return $e['k1'];}, $ar))); | |||
/* | |||
array ( | |||
'v12' => 'v11', | |||
'v22' => 'v21', | |||
) | |||
*/ | |||
======単純配列→連想配列====== | |||
[https://www.techiedelight.com/ja/convert-regular-array-to-associative-array-php/ PHP で通常の配列を連想配列に変換する] | |||
いくつか方法がある。 | |||
#array_combine | |||
#array_fill_keys | |||
#foreach | #foreach | ||
#array_flip | #array_flip | ||
976行目: | 1,019行目: | ||
$ar2 = array_combine($ar, $ar); | $ar2 = array_combine($ar, $ar); | ||
var_dump($ar2); | var_dump($ar2); | ||
/* | |||
array ( | |||
'a' => 'a', | |||
'b' => 'b', | |||
) | |||
*/ | |||
array_combineがシンプル。array_fill_keysは0初期化などしたい場合。 | array_combineがシンプル。array_fill_keysは0初期化などしたい場合。 | ||
998行目: | 1,047行目: | ||
$csv[] = array_combine($header, $row); | $csv[] = array_combine($header, $row); | ||
} | } | ||
1番目の方法がシンプル。これよりSplFileObjectのほうがいい。 | |||
[https://blog.fenrir-inc.com/jp/2014/07/php-csv.html 【PHP】その CSV 変換、本当に「fgetcsv」でいいの? (フェンリル | デベロッパーズブログ)] | |||
======反転|array_flip====== | |||
[https://www.php.net/manual/ja/function.array-flip.php PHP: array_flip - Manual] | |||
配列のキーと値を反転した配列を返す。元のarrayの値は有効なキーを必要とする。つまり、intかstring。型が違う場合、警告が出て無視される。 | |||
また、同じ値が複数ある場合、最後のみが有効になる。 | |||
====== 分割 ====== | |||
* [https://stackoverflow.com/questions/29792685/php-split-array-in-subarrays PHP Split array in subarrays - Stack Overflow] | |||
* [https://www.php.net/manual/ja/function.array-chunk.php PHP: array_chunk - Manual] | |||
1個の大きな配列をそのまま反復させると大きいので、指定要素数ずつに分割して、処理したいことがある。 | |||
一括INSERTを分割する場合など。array_chunkで配列を分割できるのでこれを使う。 | |||
implodeで文字列にマージして、explodeで分割というのもある。 | |||
====== String ====== | |||
[https://www.php.net/manual/ja/function.implode.php PHP: implode - Manual] | |||
implode(array|string $separator = "", ?array $array): string | |||
配列だけ指定した場合、空文字で結合する。 | |||
var_dump(implode(['a', 'b', 'c'])); // string(3) "abc" | |||
=====Search===== | =====Search===== | ||
1,005行目: | 1,079行目: | ||
いくつか方法がある。 | いくつか方法がある。 | ||
基本はin_array。複雑な検索はarray_filter/array_intersect。 | |||
======array_key_exists/キー確認====== | ======array_key_exists/キー確認====== | ||
[https://www.php.net/manual/ja/function.array-key-exists.php PHP: array_key_exists - Manual] | [https://www.php.net/manual/ja/function.array-key-exists.php PHP: array_key_exists - Manual] | ||
1,015行目: | 1,089行目: | ||
*??: キー不在だとnullになるのでこれでない場合に対応できる。 | *??: キー不在だとnullになるのでこれでない場合に対応できる。 | ||
基本はarray_key_exitsか??。$ar ?? nullでWARNINGを回避しながら手短にかける。 | 基本はarray_key_exitsか??。$ar ?? nullでWARNINGを回避しながら手短にかける。 | ||
変数が配列か保証できていない場合、キーの確認と同時に行う場合、以下のいずれかになる。 | |||
if (!is_array($ar) || $ar['key'] ?? null) | |||
if (!is_array($ar) || !isset($ar['key])) // 要素がnullの場合もなしあつかい。 | |||
if (!is_array($ar) || array_key_exists('key', $ar)) // 要素がnullの場合はありとみなす。 | |||
2番目のissetを使うのがいい。 | |||
$arがobjectの場合、isset/emptyだと$ar['key']はarray_key_exists含む配列要素アクセス関係でエラーになるので、is_arrayは必須。 | |||
======empty====== | |||
* [https://qiita.com/miriwo/items/c4760cbb2807ee84ef2d PHP 配列が空かどうかを判定する #初心者 - Qiita] | |||
* [https://www.php.net/manual/ja/function.count.php PHP: count - Manual] | |||
* [https://www.php.net/manual/ja/function.array-filter.php PHP: array_filter - Manual] | |||
emptyで確認できる。が、単に配列変数がnullなどの場合も判定してしまう。null or emptyという意味ならemptyでもOK。 | |||
配列変数があって、空かどうかを見たければis_array && empty | |||
逆に、issetであることと、nullではないことを確認できる。 | |||
countで配列要素数をカウントできるのでこれでも確認できるが、配列変数自体がnullの場合エラーになるのでis_arrayのチェックが必要。面倒だからemptyでいいだろう。 | |||
ただ、配列の要素の値が全部nullで実質空というような場合は工夫が必要。array_filterを使う。コールバックを指定しなかったら、emptyの判定をする。これがスマート。 | |||
$a = ['a' => null, 'b' => null]; | |||
var_export(array_filter($a)); | |||
var_export(empty(array_filter($a))); | |||
空の要素を削除する場合もarray_filterを使う。不要データの削除などでよく使いそう。 | |||
======array_search====== | ======array_search====== | ||
array_search() - 指定した値を配列で検索し、見つかった場合に対応する最初のキーを返す | array_search() - 指定した値を配列で検索し、見つかった場合に対応する最初のキーを返す | ||
1,088行目: | 1,184行目: | ||
array_intersectが実行結果とboolが同じ向きなので、これを使うとわかりやすいだろう。 | array_intersectが実行結果とboolが同じ向きなので、これを使うとわかりやすいだろう。 | ||
====== 重複削除 ====== | |||
いくつか方法がある。 | |||
* 連想配列 | |||
* array_diff | |||
* array_unique | |||
* array_keys(array_flip()): array_uniqueより少し早い ([https://zenn.dev/umeso/articles/280e268e196390 PHPの配列から重複を削除するにはarray_unique()よりarray_keys(array_flip())が速いのか])。 | |||
array_uniqueはデフォルトではvalueだけで判断する。 | |||
array_uniqueはデフォルトで文字列として比較する。配列などの場合はSORT_REGULARのフラグを指定する ([https://stackoverflow.com/questions/13857775/remove-duplicated-elements-of-associative-array-in-php Remove duplicated elements of associative array in PHP - Stack Overflow])。 | |||
ただし、型混在など複雑な場合は比較が失敗することがあるので、自前で行ったほうがいいらしい ([https://qiita.com/y-encore/items/40ba694a8899ad1e9416 PHP: array_uniqueについて #PHP - Qiita])。 | |||
array_unique重複は最初の要素を残す。最後の要素を残したければ、array_reverseを2併用する ([https://stackoverflow.com/questions/16777363/keep-unique-values-of-array-preserving-order-retaining-last-occurrence-of-each php - Keep unique values of array, preserving order, retaining last occurrence of each - Stack Overflow])。 | |||
array_reverse(array_unique(array_reverse($array))); | |||
但し、配列が大きいとarray_reverseの2回は遅い。 | |||
[https://vijayasankarn.wordpress.com/2017/02/20/array_unique-for-multidimensional-array/ array_unique for multidimensional array – James' Desk] | |||
array_uniqueとarray_intersect_keyをうまく使う方法がある。 | |||
連想配列であるプロパティー (例: value) だけに固有条件を入れたい場合、 | |||
$tempArr = array_unique(array_column($array, 'value')); | |||
print_r(array_intersect_key($array, $tempArr)); | |||
一度valueだけarray_uniqueで取得して、その後array_intersect_keyで交差を取得。 | |||
====== 列の一致判定 ====== | |||
重複削除判定時などで、複数配列の同じ列・キーで処理したいことがある。foreach文と判定用変数を使わずに行うには、array_filterを使う。これくらいしか逆に方法がない。 | |||
$needle = ['a', 'b']; | |||
$h1 = ['a' => '1', 'b' => 2]; | |||
$h2 = ['a' => '1', 'b' => 2, 'c' => 3]; | |||
$h3 = ['a' => '0', 'b' => 2, 'c' => 3]; | |||
var_dump($same_all = !array_filter($needle, function($n)use($h1, $h2){return $h1[$n] !== $h2[$n];})); // bool(true) | |||
var_dump($same_all = !array_filter($needle, function($n)use($h1, $h3){return $h1[$n] !== $h3[$n];})); // bool(false) | |||
var_dump($same_all = array_filter($needle, function($n)use($h1, $h2){return $h1[$n] !== $h2[$n];})); // [] | |||
var_dump($same_all = array_filter($needle, function($n)use($h1, $h3){return $h1[$n] !== $h3[$n];})); // ['a'] | |||
var_dump($same_all = array_filter($needle, function($n)use($h1, $h2){return $h1[$n] === $h2[$n];})); // ['a', 'b'] | |||
var_dump($same_all = array_filter($needle, function($n)use($h1, $h3){return $h1[$n] === $h3[$n];})); // ['b'] | |||
コールバック内の判定を===にすると、1個でもマッチしたらarray_filterの結果型trueになる。 | |||
ややこしいが、コールバック内を!==にして、結果が空になったら完全一致とみなす。そうしないと、元の要素数の余計な判定が必要になる。 | |||
これを応用して行列の一致判定をする場合。<syntaxhighlight lang="php"> | |||
$records_unique = []; // [0 => ['a' => 0, 'b' => 1], 1 => ['a' => 1, 'b' => 2]] | |||
$unique = ['a', 'b']; | |||
$is_unique = !array_filter($records_unique, function($record_unique)use($needle, $record){ | |||
/** @return bool unique対象列の全一致判定 */ | |||
return !array_filter($unique, function($v)use($record_unique, $record){return $record_unique[$v] !== $record2[$v];}); | |||
}); | |||
</syntaxhighlight>外側に行ループ用の配列をわつぃて、その要素を内側で使うだけ。 | |||
=====Array Functions===== | =====Array Functions===== | ||
======compact====== | ======compact====== | ||
1,099行目: | 1,249行目: | ||
配列のキー・バリューを変数として取り込む。 | 配列のキー・バリューを変数として取り込む。 | ||
===== 一括操作 ===== | |||
配列要素全体に一括処理を行える関数がいくつかある。for/foreach文が不要なのでコンパクト。 | |||
* array_map: 適用結果の配列を取得。 | |||
* array_filter: 適用して絞り込んだ配列を取得。 | |||
* array_reduce: 繰り返し適用して1個にまとめる。 | |||
* array_walk: 要素に適用するだけ。 | |||
====== forとの速度比較 ====== | |||
単に反復させるだけなら、基本的にはfor/foreachを使ったほうが速い模様。 | |||
<nowiki>$a = range(0, 10000); | |||
echo (function($c){$s=hrtime(1);$c();return hrtime(1)-$s;})(function()use($a){foreach($a as $v){$v;}}), " ns\n"; | |||
echo (function($c){$s=hrtime(1);$c();return hrtime(1)-$s;})(function()use($a){array_map(function($v){$a;}, $a);}), " ns\n"; | |||
echo (function($c){$s=hrtime(1);$c();return hrtime(1)-$s;})(function()use($a){array_walk($a, function($v){$a;});}), " ns\n";</nowiki> | |||
116170 ns | |||
372829 ns | |||
713770 ns | |||
可読性やコード量、反復しないところでarray_関数は使うのがよさそう。 | |||
======array_map====== | ======array_map====== | ||
*[https://www.php.net/manual/ja/function.array-map.php PHP: array_map - Manual] | *[https://www.php.net/manual/ja/function.array-map.php PHP: array_map - Manual] | ||
1,111行目: | 1,282行目: | ||
[https://www.danielauener.com/howto-use-array_map-on-associative-arrays-to-change-values-and-keys/ Howto use array_map on associative arrays to change values and keys - Daniel Auener] | [https://www.danielauener.com/howto-use-array_map-on-associative-arrays-to-change-values-and-keys/ Howto use array_map on associative arrays to change values and keys - Daniel Auener] | ||
いや、そういうことをしなくても、array_keysを使えばOK。 | |||
$result = array_map(function($k, $v){return ;}, array_keys($arr), $arr); | |||
Ref: [https://stackoverflow.com/questions/25513361/how-can-i-use-array-map-with-keys-and-values-but-return-an-array-with-the-same php - How can I use array_map with keys and values, but return an array with the same indexes (not int)? - Stack Overflow] | |||
array_mapは単純配列を返す。元々が連想配列の場合、キーが数値に置換される。元のキーを維持したければ、array_combineを併用する。 | |||
$arr = | |||
[ | |||
"id" => 1, | |||
"name" => "Fred", | |||
]; | |||
$result = array_combine( | |||
array_keys($arr), | |||
array_map(function($v){ return $v; }, $arr) | |||
); | |||
====== array_filter ====== | |||
[https://www.php.net/manual/ja/function.array-filter.php PHP: array_filter - Manual] | |||
名前通り配列要素をフィルターリングする。 | |||
array_filter(array $array, ?callable $callback = null, int $mode = 0): array | |||
$modeを指定しなければcallbackにはvalueのみ渡される。 | |||
callbackがtrueを返したら、その要素を残す。callbackを指定しなかったら、!empty($v)相当。なので、array_filter($array) で、キーがある場合の配列要素の空判定にもなる。 | |||
他には応用として、操作対象ののキーの配列を渡して、そのキーを使って複数の配列の同じキーの一致・重複判定などできる。 | |||
/** UPSERTのAI増分対策用に重複削除。 */ | |||
<nowiki> </nowiki> if (!empty($unique)) { | |||
<nowiki> </nowiki> $old_row = <nowiki>''</nowiki>; | |||
<nowiki> </nowiki> foreach ($records as $row => $line) { | |||
<nowiki> </nowiki> // unique対象列が全部一致の場合削除。 | |||
<nowiki> </nowiki> if (array_filter($unique, function($v) use ($line, $records, $old_row) {return $line[$v] !== $records[$old_row][$v];})) { | |||
<nowiki> </nowiki> unset($records[$old_row]); | |||
<nowiki> </nowiki> } | |||
<nowiki> </nowiki> $old_row = $row; | |||
<nowiki> </nowiki> } | |||
<nowiki> </nowiki> } | |||
====== array_reduce ====== | |||
[https://www.php.net/manual/ja/function.array-reduce.php PHP: array_reduce - Manual] | |||
array_reduce(array $array, callable $callback, mixed $initial = null): mixed | |||
callback(mixed $carry, mixed $item): mixed | |||
$carryに前回処理結果。$itemに現在要素。 | |||
配列要素を集計して1要素にまとめる。 | |||
統計処理したり、結合したりできる。 | |||
var_export(array_reduce([0, 1, 2], function($c, $v){return $c.$v;})); // '012' | |||
配列要素の列を結合したいことがある。そういうときにこれを使う。 | |||
==== Enum ==== | |||
[https://www.php.net/manual/ja/language.types.enumerations.php PHP: 列挙型 / Enum - Manual] | |||
PHP 8.1.0から導入。長らくなかった。 | |||
複数の異なる値を1個の集合として取り扱うデータ型。 | |||
終了コードなど、意味がある数字を扱う。 | |||
enumがないと、値の下限、上限など、ただの数字だから保証できない。 | |||
===== Implementation ===== | |||
* [https://tech.designone.jp/entry/2022/01/12/181158 【PHP 8.1】とうとうPHPにもEnumがやってきた - デザインワン・ジャパン Tech Blog] | |||
* [https://zenn.dev/naopusyu/scraps/798f57c5f3dbf1 php-enumのメモ] | |||
* [https://blog.wh-plus.co.jp/entry/2023/07/25/164637 PHP8.1のEnumと独自実装のEnumを比較して移行できるか検討しました - WHITEPLUS TechBlog] | |||
* [https://stackoverflow.com/questions/1528280/how-to-implement-enum-like-functionality-in-php How to implement Enum like functionality in PHP? - Stack Overflow] | |||
長らく言語機能になかったのでクラスやトレイトを使った独自実装が試されている。 | |||
* [https://github.com/BenSampo/laravel-enum GitHub - BenSampo/laravel-enum: Simple, extensible and powerful enumeration implementation for Laravel.] | |||
* [https://github.com/myclabs/php-enum GitHub - myclabs/php-enum: The enum PHP is missing, inspired from SplEnum] | |||
昔はSplEnumという実験モジュールがあったが、Enumの登場でなくなった。 | |||
PHP 7.4以前との互換性のために、独自のクラスで実装して、その内部実装で上記ライブラリー類を使う感じだろう。 | |||
==== Iterable ==== | |||
* [https://www.php.net/manual/ja/language.types.iterable.php PHP: Iterable - Manual] | |||
* [https://www.php.net/manual/ja/class.traversable.php PHP: Traversable - Manual] | |||
* [https://www.php.net/manual/ja/class.iterator.php PHP: Iterator - Manual] | |||
array|Traversable型のエイリアス。PHP 7.1.0で導入。foreachで使用可能で、ジェネレーター内のyield fromでも使える。 | |||
Traversableインターフェイス、Iteratorクラスが特に重要。このメソッドはいろんなところで登場するから。 | |||
* current: 現在の要素を返す。 | |||
* key: | |||
* next | |||
* rewind | |||
* valid | |||
特にcurrentが重要。例えば、ヘッダーをこれで取得などできる。 | |||
====Type declarations/型宣言==== | ====Type declarations/型宣言==== | ||
1,146行目: | 1,406行目: | ||
[https://www.php.net/manual/ja/language.types.type-juggling.php PHP: 型の相互変換 - Manual] | [https://www.php.net/manual/ja/language.types.type-juggling.php PHP: 型の相互変換 - Manual] | ||
型の相互変換。非常に重要。いろいろ方法がある。 | |||
共通なのはキャスト (cast)。<syntaxhighlight lang="php"> | 共通なのはキャスト (cast)。<syntaxhighlight lang="php"> | ||
1,155行目: | 1,413行目: | ||
$bar = (bool) $foo; // $bar は boolean です | $bar = (bool) $foo; // $bar は boolean です | ||
$fst = "$foo"; // to string. | $fst = "$foo"; // to string. | ||
+"+40"; // to int | |||
?> | ?> | ||
</syntaxhighlight>C言語と同じで (型) を前置する。ただし、少々長い。 | </syntaxhighlight>C言語と同じで (型) を前置する。ただし、少々長い。 | ||
文字列への変換は二重引用符囲、数値への変換は算術演算子 (+)。まあ、キャストだけ覚えておくのがシンプル。 | |||
===== 型キャスト ===== | |||
変換先の型を波括弧で囲んで、変換対象の変数に前置することで変換する。 | |||
使用可能なキャストは以下。 | |||
* <code>(int)</code> - 整数([[/www.php.net/manual/ja/language.types.integer.php|int]]) へのキャスト | |||
* <code>(bool)</code> - 論理値([[/www.php.net/manual/ja/language.types.boolean.php|bool]]) へのキャスト | |||
* <code>(float)</code> - [[/www.php.net/manual/ja/language.types.float.php|float]] へのキャスト | |||
* <code>(string)</code> - 文字列([[/www.php.net/manual/ja/language.types.string.php|string]]) へのキャスト | |||
* <code>(array)</code> - 配列([[/www.php.net/manual/ja/language.types.array.php|array]]) へのキャスト | |||
* <code>(object)</code> - オブジェクト([[/www.php.net/manual/ja/language.types.object.php|object]]) へのキャスト | |||
* <code>(unset)</code> - [[/www.php.net/manual/ja/language.types.null.php|NULL]] へのキャスト PHP 8.0.0で削除。単にNULLを代入する。 | |||
言語構造なので、関数よりも高速。丸括弧内のスペースは無視される。 | |||
====Other==== | ====Other==== | ||
=====型判定===== | =====型判定===== | ||
1,169行目: | 1,444行目: | ||
*is_型名: is_array/is_bool/is_callable/is_float/is_int/is_null/is_numeric/is_object/is_resoure/is_scalar/is_string/function_exists/method_exists | *is_型名: is_array/is_bool/is_callable/is_float/is_int/is_null/is_numeric/is_object/is_resoure/is_scalar/is_string/function_exists/method_exists | ||
基本はis_型名だろう。 | 基本はis_型名だろう。 | ||
===== associative array vs stdClass ===== | |||
連想配列とオブジェクトのどちらを使うべきか? | |||
* 総合: 配列のほうが専用関数が多く扱いやすいことが多く無難。 | |||
* 型: 意識したい場合、stdClass。IDEの補完もしやすい。 | |||
* 性能: 配列のほうが速い。オブジェクトはプロパティーとメソッドの管理が必要でやや重い。 | |||
* 再利用を意識するならstdClass | |||
まとめ。 | |||
* その場しのぎ、一時的な利用など、基本は連想配列。 | |||
* いろんな場所で使う構造データはオブジェクト。 | |||
json_decodeもいろんな場所で使わないなら連想配列でよいと思う。 | |||
===Variables=== | ===Variables=== | ||
====Basics==== | ====Basics==== | ||
Ref: [https://www.php.net/manual/en/language.variables.basics.php PHP: Basics - Manual] | Ref: [https://www.php.net/manual/en/language.variables.basics.php PHP: Basics - Manual] | ||
===== 使用可能な文字 ===== | |||
変数名は、PHPの他のラベルと同じルールに従います。 有効な変数名は文字またはアンダースコアから始まり、任意の数の文字、 数字、アンダースコアが続きます。正規表現によれば、これは次の ように表現することができます。 | |||
^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ | |||
ASCIIテキストの範囲だと記号類は_以外変数名に使用不能。 | |||
=====Undefined variable===== | =====Undefined variable===== | ||
1,246行目: | 1,542行目: | ||
* $_REQUEST | * $_REQUEST | ||
* $_ENV | * $_ENV | ||
==== Variable variables/可変変数 ==== | |||
* [https://www.php.net/manual/ja/language.variables.variable.php PHP: 可変変数 - Manual] | |||
* [https://www.php.net/manual/ja/language.oop5.properties.php PHP: プロパティ - Manual] | |||
PHP 7.0から対応した機能とのこと。 | |||
クラスのプロパティーの可変プロパティーアクセスがる。 | |||
$ref->{'ref-type'} = 'Journal Article'; | |||
class foo { | |||
var $bar = 'I am bar.'; | |||
var $arr = array('I am A.', 'I am B.', 'I am C.'); | |||
var $r = 'I am r.'; | |||
} | |||
$foo = new foo(); | |||
$bar = 'bar'; | |||
$baz = array('foo', 'bar', 'baz', 'quux'); | |||
echo $foo->$bar . "\n"; | |||
echo $foo->{$baz[1]} . "\n"; | |||
$start = 'b'; | |||
$end = 'ar'; | |||
echo $foo->{$start . $end} . "\n"; | |||
$arr = 'arr'; | |||
echo $foo->{$arr[1]} . "\n"; | |||
プロパティー名として無効な文字 (-,.()など) を含む場合もアクセスでき便利。例えば、json_decodeの結果など。 | |||
====Variables From External Sources==== | ====Variables From External Sources==== | ||
1,256行目: | 1,582行目: | ||
配列渡しはPHP側の仕様。 | 配列渡しはPHP側の仕様。 | ||
===== HTML Forms (GET and POST) ===== | |||
PHPではフォーム変数のドットとスペースはアンダーバーに変換される。 | |||
たとえば <code><input name="a.b" /></code> は <code>$_REQUEST["a_b"]</code> となります。 | |||
===== 外部変数名のドット ===== | |||
PHPの変数名でドットやスペースは無効。その都合で、それらの文字は_に置換される。フォーム変数も似たような考え方。 | |||
=== Constants === | === Constants === | ||
1,315行目: | 1,649行目: | ||
|- | |- | ||
|スコープ | |スコープ | ||
|名前空間 | |||
|グローバル | |グローバル | ||
|} | |} | ||
defineはブロック内で使えるので、何らかの条件で定義を変更できるのが利点。例えば、環境を本番とデバッグに変えたりなど。 | defineはブロック内で使えるので、何らかの条件で定義を変更できるのが利点。例えば、環境を本番とデバッグに変えたりなど。 | ||
動的に変更したいならdefine、それ以外は名前空間やクラス定数として使えるconstだろうか。関数内のマジックナンバー的な使い方はできない。そういうのは、普通の変数で取り扱う。 | |||
ただ、constはアプリの設定として使うことはない。クラスの固有値の定義。 | ただ、constはアプリの設定として使うことはない。クラスの固有値の定義。 | ||
1,334行目: | 1,668行目: | ||
クラス内に定数を定義できる。デフォルトでpublic。staic変数的な扱い。インスタンスではなく、クラスが保有する。 | クラス内に定数を定義できる。デフォルトでpublic。staic変数的な扱い。インスタンスではなく、クラスが保有する。 | ||
===Operators=== | ==== predefined ==== | ||
[https://www.php.net/manual/en/language.operators.php PHP: Operators - Manual] | |||
====Assignment/代入演算子==== | * [https://www.php.net/manual/ja/language.constants.predefined.php PHP: 自動的に定義される定数 - Manual] | ||
*[https://www.php.net/manual/en/language.operators.assignment.php PHP: Assignment - Manual] | * [https://www.php.net/manual/ja/reserved.constants.php PHP: 定義済みの定数 - Manual] | ||
*[https://qiita.com/rana_kualu/items/e15a9b7c12f175380244 【PHP7.4】PHPの新たな演算子??=ってなんぞ? #NULL合体代入演算子 - Qiita] | |||
??= | 言語で定義済みの定数がいろいろある。true/false/nullなど。 | ||
==== Magic/マジック定数 ==== | |||
使用箇所で値が変化する定数 (マジック定数) が9個ある。C言語のマクロに近い。コンパイル時に解決される。大文字小文字を区別しない。 | |||
{| class="wikitable" | |||
|+PHP の "マジック" 定数 | |||
!名前 | |||
!説明 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.line|__LINE__]]</code> | |||
|ファイル上の現在の行番号。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.file|__FILE__]]</code> | |||
|ファイルのフルパスとファイル名 (シンボリックリンクを解決した後のもの)。 インクルードされるファイルの中で使用された場合、インクルードされるファイルの名前が返されます。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.dir|__DIR__]]</code> | |||
|そのファイルの存在するディレクトリ。include の中で使用すると、 インクルードされるファイルの存在するディレクトリを返します。 つまり、これは <code>dirname(__FILE__)</code> と同じ意味です。 ルートディレクトリである場合を除き、ディレクトリ名の末尾にスラッシュはつきません。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.function|__FUNCTION__]]</code> | |||
|関数名。無名関数の場合は、<code>{closure}</code> | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.class|__CLASS__]]</code> | |||
|クラス名。 クラス名には、そのクラスが宣言されている名前空間も含みます (例 <code>Foo\Bar</code>)。 トレイトのメソッド内で __CLASS__ を使うと、 そのトレイトを use しているクラスの名前を返します。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.trait|__TRAIT__]]</code> | |||
|トレイト名。 トレイト名には、宣言された名前空間も含みます (例 <code>Foo\Bar</code>)。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.method|__METHOD__]]</code> | |||
|クラスのメソッド名。 | |||
|- | |||
|<code>[[/www.php.net/manual/ja/language.constants.magic.php#constant.namespace|__NAMESPACE__]]</code> | |||
|現在の名前空間の名前。 | |||
|- | |||
|<code>ClassName::class</code> | |||
|完全に修飾されたクラス名。 | |||
|} | |||
どれもよく使う。 | |||
===Operators=== | |||
[https://www.php.net/manual/en/language.operators.php PHP: Operators - Manual] | |||
==== precedence/優先順位 ==== | |||
[https://www.php.net/manual/ja/language.operators.precedence.php PHP: 演算子の優先順位 - Manual] | |||
丸括弧をつけるかつけないかが変わる。 | |||
特によく使うもの、注意が必要なものを整理する。 | |||
===== if (!$var = getVar()) ===== | |||
* [https://stackoverflow.com/questions/12453968/why-does-negation-happen-last-in-an-assignment-expression-in-php Why does negation happen last in an assignment expression in PHP? - Stack Overflow] | |||
* [https://qiita.com/itsumoonazicode/items/750778a0e97c85b0dd6f if文の中で変数定義 - PHP #PHP - Qiita] | |||
!は=より優先順位が高いが if (!$var = getVar()) のような式は成立して、変数代入結果の否定が評価される。<blockquote>注意: = は他のほとんどの演算子よりも優先順位が低いはずなのにもかかわらず、 PHP は依然として if (!$a = foo()) のような式も許します。この場合は foo() の戻り値が $a に代入されます。</blockquote>これが成立する理由。=の左辺は変数じゃないといけないから。(!$var) には代入がそもそもできない。そのため、PHPができるだけパース仕様として、以下のように代入部分を丸括弧で囲んだ扱いにしてくれる。 | |||
!$var = getVar() | |||
!($var = getVar()) | |||
だからこれが成立する。関数の処理結果を保存して、判定してその後の流用に短縮できて便利。 | |||
====Assignment/代入演算子==== | |||
*[https://www.php.net/manual/en/language.operators.assignment.php PHP: Assignment - Manual] | |||
*[https://qiita.com/rana_kualu/items/e15a9b7c12f175380244 【PHP7.4】PHPの新たな演算子??=ってなんぞ? #NULL合体代入演算子 - Qiita] | |||
??= | |||
// NULL合体代入演算子 | // NULL合体代入演算子 | ||
$id ??= getId(); | $id ??= getId(); | ||
1,422行目: | 1,819行目: | ||
デフォルト値扱いにしたければ、短縮条件演算子?:や、ヌル合体演算子??を使う。 | デフォルト値扱いにしたければ、短縮条件演算子?:や、ヌル合体演算子??を使う。 | ||
==== | ==== 配列演算子 ==== | ||
[https://www.php.net/manual/ja/language.operators.array.php PHP: 配列演算子 - Manual] | |||
* 関数 | 配列に対する演算子は扱いがやや特殊。 | ||
** 可変長引数リストと関数呼出 [https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list PHP: 関数の引数 - Manual]。PHP 5.6で導入 ([https://www.php.net/manual/ja/migration56.new-features.php PHP: 新機能 - Manual])。名前付き引数に対応したPHP 8.0から連想配列も対応。PHP 8.1から可変長引数と名前付き引数の同時使用 ([https://www.php.net/manual/ja/migration81.new-features.php#migration81.new-features.core.named-arg-after-unpack PHP: 新機能 - Manual])。アンパックと通常名前付き引数がある場合、後のやつで上書き不能で実行時エラー。 | {| class="wikitable" | ||
** 第一級callableを生成する記法: PHP 8.1.0で導入 ([https://www.php.net/manual/ja/functions.first_class_callable_syntax.php#functions.first_class_callable_syntax PHP: 第一級callableを生成する記法 - Manual])。Closure::fromCallableの無名関数作成時の別の方法。 | |+Array Operators | ||
* 配列のアンパック: [https://www.php.net/manual/ja/language.types.array.php#language.types.array.unpacking PHP: 配列 - Manual] | !例 | ||
!名前 | |||
!結果 | |||
|- | |||
|$a + $b | |||
|結合 | |||
|<var>$a</var> および <var>$b</var> を結合する。 | |||
|- | |||
|$a == $b | |||
|同等 | |||
|<var>$a</var> および <var>$b</var> のキー/値のペアが等しい場合に <code>[[/www.php.net/manual/ja/reserved.constants.php#constant.true|true]]</code>。 | |||
|- | |||
|$a === $b | |||
|同一 | |||
|<var>$a</var> および <var>$b</var> のキー/値のペアが等しく、その並び順が等しく、 かつデータ型も等しい場合に <code>[[/www.php.net/manual/ja/reserved.constants.php#constant.true|true]]</code>。 | |||
|- | |||
|$a != $b | |||
|等しくない | |||
|<var>$a</var> が <var>$b</var> と等しくない場合に <code>[[/www.php.net/manual/ja/reserved.constants.php#constant.true|true]]</code>。 | |||
|- | |||
|$a <> $b | |||
|等しくない | |||
|<var>$a</var> が <var>$b</var> と等しくない場合に <code>[[/www.php.net/manual/ja/reserved.constants.php#constant.true|true]]</code>。 | |||
|- | |||
|$a !== $b | |||
|同一でない | |||
|<var>$a</var> が <var>$b</var> と同一でない場合に <code>[[/www.php.net/manual/ja/reserved.constants.php#constant.true|true]]</code>。 | |||
|} | |||
配列の等価演算子はキーと値の両方を比較する。これが重要。後は結合の+。 | |||
==== ...演算子/スプレッド演算子 ==== | |||
他の言語でいうスプレッド (splat) 演算子。PHPでは...演算子。使うときはアンパックという。 | |||
* 関数 | |||
** 可変長引数リストと関数呼出 [https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list PHP: 関数の引数 - Manual]。PHP 5.6で導入 ([https://www.php.net/manual/ja/migration56.new-features.php PHP: 新機能 - Manual])。名前付き引数に対応したPHP 8.0から連想配列も対応。PHP 8.1から可変長引数と名前付き引数の同時使用 ([https://www.php.net/manual/ja/migration81.new-features.php#migration81.new-features.core.named-arg-after-unpack PHP: 新機能 - Manual])。アンパックと通常名前付き引数がある場合、後のやつで上書き不能で実行時エラー。 | |||
** 第一級callableを生成する記法: PHP 8.1.0で導入 ([https://www.php.net/manual/ja/functions.first_class_callable_syntax.php#functions.first_class_callable_syntax PHP: 第一級callableを生成する記法 - Manual])。Closure::fromCallableの無名関数作成時の別の方法。 | |||
* 配列のアンパック: [https://www.php.net/manual/ja/language.types.array.php#language.types.array.unpacking PHP: 配列 - Manual]。array_mergeの代替記法。extractは似ているようで意味が少々違う。 | |||
** 数値キー・単純配列はPHP 7.4で導入 ([https://www.php.net/manual/ja/migration74.new-features.php#migration74.new-features.core.unpack-inside-array PHP: 新機能 - Manual])。 | ** 数値キー・単純配列はPHP 7.4で導入 ([https://www.php.net/manual/ja/migration74.new-features.php#migration74.new-features.core.unpack-inside-array PHP: 新機能 - Manual])。 | ||
** 文字キー・連想配列はPHP 8.1 ([https://www.php.net/manual/ja/migration81.new-features.php#migration81.new-features.core.unpacking-string-keys PHP: 新機能 - Manual]、[https://qiita.com/shigakin/items/5e4a2784bbd6b227f4d3 【PHP8.1】あなたはどっち? array_merge VS unpacking(スプレッド演算子) #PHP - Qiita])。 | ** 文字キー・連想配列はPHP 8.1 ([https://www.php.net/manual/ja/migration81.new-features.php#migration81.new-features.core.unpacking-string-keys PHP: 新機能 - Manual]、[https://qiita.com/shigakin/items/5e4a2784bbd6b227f4d3 【PHP8.1】あなたはどっち? array_merge VS unpacking(スプレッド演算子) #PHP - Qiita])。 | ||
1,477行目: | 1,910行目: | ||
なお、配列のアンパックに関しては、array_mergeのほうが速くてメモリーも少ないとのこと。 | なお、配列のアンパックに関しては、array_mergeのほうが速くてメモリーも少ないとのこと。 | ||
==== in演算子 ==== | |||
==== | [https://stackoverflow.com/questions/33182976/php-equivalent-of-mysql-in-operator php equivalent of mysql "IN" operator? - Stack Overflow] | ||
[https:// | |||
PHPにin演算子はない。代わりに、in_arrayで包含判定できる。 | |||
ある値が、いずれかのどれかであるかの判定はそれなりにある。 | |||
in_array($target, [], true); | |||
==== | 例えば、この比較対象が長い場合、(a===b||a===c|a===d) で何回も書かなくて済む。 | ||
==== Name ==== | |||
誰かに言葉で説明する際に、演算子や構文の名前がほしい。意外と覚えていない。根拠とともに整理する。 | |||
https://chatgpt.com/c/6743ea1b-4a14-800b-b50b-272dd4dbcde0 | |||
{| class="wikitable" | |||
|+ | |||
< | !演算子 | ||
!名前 | |||
!name | |||
!URL | |||
!説明 | |||
|- | |||
|$this->property | |||
|オブジェクト演算子 | |||
|object operator | |||
|[https://www.php.net/manual/ja/language.oop5.properties.php PHP: プロパティ - Manual] | |||
|インスタンスのプロパティーとメソッドにアクセスする。 | |||
|- | |||
|...[] | |||
|...演算子 | |||
| | |||
|[https://www.php.net/manual/ja/migration56.new-features.php PHP: 新機能 - Manual] | |||
|配列を展開する。別名splat演算子。スプレッド演算子。スプレッド演算子を使うことをアンパックという。 | |||
|- | |||
|$ar['key'] | |||
$str[0] | |||
|角括弧構文 | |||
文字列アクセス演算子/インデックス演算子 | |||
| | |||
|[https://www.php.net/manual/ja/language.types.array.php PHP: 配列 - Manual][https://www.php.net/manual/ja/language.types.string.php PHP: 文字列 - Manual] | |||
|演算子ではなく構文。 | |||
|- | |||
|<blockquote><blockquote><blockquote><blockquote>::</blockquote></blockquote></blockquote></blockquote> | |||
|スコープ定義演算子 | |||
| | |||
|[https://www.php.net/manual/ja/language.oop5.static.php PHP: static キーワード - Manual] | |||
| | |||
|- | |||
|?: | |||
|三項演算子 | |||
| | |||
| | |||
| | |||
|- | |||
|?? | |||
|Null合体演算子 | |||
| | |||
| | |||
| | |||
|- | |||
|. | |||
|結合演算子 | |||
| | |||
|[https://www.php.net/manual/ja/language.operators.string.php PHP: 文字列演算子 - Manual] | |||
| | |||
|} | |||
===Control Structures=== | |||
Source: [https://www.php.net/manual/en/language.control-structures.php PHP: Control Structures - Manual]. | |||
====制御構造に関する別の構文==== | |||
[https://www.php.net/manual/ja/control-structures.alternative-syntax.php PHP: 制御構造に関する別の構文 - Manual] | |||
if、 while、for、 foreach、switch に関する別の構文がある。開き波括弧部分を:に、閉じ波括弧部分をendif;,endwhile;, endfor;,endforeach;, endswitch;などにできる。else:とelseif:に注意。 | |||
この構文は存在だけ知っておくだけでいいと思われる。 | |||
====elseif/else if==== | |||
[https://www.php.net/manual/ja/control-structures.elseif.php PHP: elseif/else if - Manual] | |||
1単語で書ける。結果は同じだが、文法的な意味が異なる。 | |||
====foreach==== | |||
==== | ===== About ===== | ||
[https://www.php.net/manual/ja/ | [https://www.php.net/manual/ja/control-structures.foreach.php PHP: foreach - Manual] | ||
foreachは配列の反復処理のための制御構造。 | |||
foreach (iterable_expression as $value) | |||
foreach (iterable_expression as $key => $value) | |||
$keyも使いたい場合、2番目の形式を使う。 | |||
ループ中に$valueの要素を直接変更したい場合、&をつけておく。 | |||
foreach (iterable_expression as &$value) | |||
===== Name ===== | |||
foreachで使う変数の命名。 | |||
foreach (table as $row => $line) | |||
DBからSELECT結果などがkeyに行番号、valueにレコードが入ってくる。こういう場合、rowがややこしい。行番号の意味でrowをキーにしておくといい。 | |||
value部分をどうするかだが、valueやitemだと少々わかりにくい。recordやline。SplFileObjectを扱うこともあるからlineがいいと思う。 | |||
===== Rewind ===== | |||
* [https://github.com/php/php-src/issues/13916 SplFileObject: foreach doesn't start from the correct line after `SplFileObject::seek()` · Issue #13916 · php/php-src · GitHub] | |||
* [https://www.php.net/manual/ja/iterator.rewind.php PHP: Iterator::rewind - Manual] | |||
* [https://www.php.net/manual/ja/class.norewinditerator.php PHP: NoRewindIterator - Manual] | |||
注意の必要な挙動として、foreachは最初にrewindでIteratorのポインターを先頭に毎回戻す。なので、ファイル系Iteratorで先頭を飛ばそうとすると工夫が必要。 | |||
// use SplFileObject; | |||
$path = stream_get_meta_data($fp = tmpfile())['uri']; | |||
file_put_contents($path, <<<'EOT' | |||
id,value | |||
0,1 | |||
EOT | |||
); | |||
$file = new SplFileObject($path); | |||
$file->setFlags(SplFileObject::READ_CSV|\SplFileObject::READ_AHEAD|\SplFileObject::SKIP_EMPTY); | |||
$file->seek(1); | |||
foreach(new NoRewindIterator($file) as $row) { | |||
var_dump($row); | |||
} | |||
ほぼこのために存在する、SPLのNoRewindIteratorでラップする。すると、rewindをオーバーライドして巻き戻さないので維持できる。 | |||
なお、next()はREAD_AHEADありにしていないと機能しないようなので注意する ([https://stackoverflow.com/questions/1504927/splfileobject-next-behavior php - SPLFileObject next() behavior - Stack Overflow])。 | |||
ただ、READ_AHEADにしても、初回がnext()2回呼ばないと2行目にcurrent()でならないので動きがわかりにくい。seek(1)でよい。 | |||
===== first/last ===== | |||
[https://stackoverflow.com/questions/1070244/php-how-to-determine-the-first-and-last-iteration-in-a-foreach-loop PHP How to determine the first and last iteration in a foreach loop? - Stack Overflow] | |||
foreachの中で最初と最後を判定したいことがある。 | |||
foreach ($array as $key => $element) { | |||
if ($key === array_key_first($array)) { | |||
echo 'FIRST ELEMENT!'; | |||
} | |||
if ($key === array_key_last($array)) { | |||
echo 'LAST ELEMENT!'; | |||
} | |||
} | |||
ただ、先頭なら$iterable->current()でいい。末尾ならforeachを抜けた後にcurrentでいい。 | |||
配列だったら、反復外部で簡単に判定できる。余計な処理を反復内に含めないほうがいい。 | |||
===== 反復削除 ===== | |||
* [https://iww.hateblo.jp/entry/20220603/array PHPで、foreachでぐるぐる回ってる最中に要素を削除する - 揮発性のメモ2] | |||
* [https://www.techiedelight.com/ja/remove-array-element-in-foreach-loop-php/ PHP の foreach ループで配列要素を削除する] | |||
キーが維持されるので、逆順反復などしなくても、影響ない。unsetすればいい。 | |||
===== reverse ===== | |||
逆順反復の方法がいくつかある。 | |||
=== | [https://stackoverflow.com/questions/10777597/reverse-order-of-foreach-list-items php - Reverse order of foreach list items - Stack Overflow] | ||
$fruits = ['bananas', 'apples', 'pears']; | |||
for($i = count($fruits)-1; $i >= 0; $i--) { | |||
echo $fruits[$i] . '<nowiki><br></nowiki>'; | |||
} | |||
foreach ( array_reverse($accounts) as $account ) { | |||
echo sprintf("<nowiki><li>%s</li></nowiki>", $account); | |||
} | |||
なお、連想配列は無理。やるとしたら、array_reverse、逆順のキーを取得してそれを使う。 | |||
[https://stackoverflow.com/questions/32613036/how-to-reverse-foreach-key-value-php arrays - How to reverse foreach $key value PHP? - Stack Overflow] | |||
====declare==== | |||
Source: [https://www.php.net/manual/en/control-structures.declare.php PHP: declare - Manual]. | |||
PHPUnitのサンプルコード ([https://phpunit.de/getting-started/phpunit-9.html Getting Started with Version 9 of PHPUnit – The PHP Testing Framework]) などで冒頭に以下の記述がある。 | |||
<?php | <?php declare(strict_types=1); | ||
これの意味が分かっていなかったので整理する。 | |||
< | declare文 (construct) は、コードブロックの実行指令となる。以下の構文となる。 | ||
declare (<directive>) | |||
<statement> | |||
<directive> はdeclareブロックの挙動を指示する。指定可能なものは以下3個だ。 | |||
#ticks | |||
#encoding | |||
#strict_types: =1の指定でPHPの暗黙の型変換を無効にする (ストリクトモード)。ただし、影響するのはスカラー型のみ。型が違う場合、TypeErrorの例外が発生する。 | |||
指令はファイルコンパイル時に処理されるので、リテラル値のみが使用可能で、変数や定数は使用不能。 | |||
declareブロックの <statement> は、<directive> の影響を受ける実行部だ。 | |||
declare文はグローバルスコープで使われる。登場以後のコードに影響する。ただし、他のファイルからincludeされても、親ファイルには影響しない。だから安心して使える。 | |||
= | 型安全にするために、基本的にPHPファイルの冒頭に<code>declare(strict_types=1);</code>を書いておいたほうがよいだろう。 | ||
==== return ==== | |||
[https://www.php.net/manual/ja/function.return.php PHP: return - Manual] | |||
関数を終了させて、結果を呼び出し元に返すというのは他の言語同様の動きだが、いくつか注意すべき挙動・使用方法がある。 | |||
returnで引数を省略すると、戻り値はnullになる。 | |||
呼び出し方法、場所で挙動が変わる。 | |||
* 関数/eval内: 即座に関数を終了し、引数を関数の値として返却。 | |||
* グローバルスコープ: スクリプト自体を終了。 | |||
* include/require内: 呼び出し元のファイルに制御を戻す。includeの場合、引数はincludeの戻り値になる。 | |||
return文は関数ではないので、引数の括弧は不要。紛らわしいのでないほうがいい。 | |||
include内で使えるというのがみそ。config.phpでreturnだけした設定一覧を記述しておいて、includeで変数に取り込むというのをよくやる。 | |||
==== require/include/require_once/include_once ==== | |||
==== | ===== Basic ===== | ||
includeは指定したファイルを読み込み評価する。絶対パスで指定しない場合、include_pathの設定を利用する。include_pathにもなければ現在ディレクトリーも探す。 | |||
絶対パス、相対パスの前置があると、include_pathは無視する。 | |||
ファイルが読み込まれると、ファイル内のコードは、includeが実行された行の変数スコープを継承する。つまり、呼び出し行で利用可能な全変数がファイル内でも使用可能。ファイル内で定義された関数やクラスはすべて、グローバルスコープになる。ただし、includeが関数定義内に配置されたら、コードは関数内で定義されているとみなす。 | |||
ファイルの読込時にはHTMLモードになる。そのため、ファイル内でPHPコードを実行するなら、<?php ?>で囲む必要がある。 | |||
includeに失敗したらFALSEを返し、E_WARNINGを発生させる。成功したら、戻り値は1。ただし、ファイル内でreturnを実行したら、その値を返す。 | |||
includeは特別な言語構造のため、引数に括弧は不要。結果を評価したいならば、全体を括弧で囲む。 | |||
// 動作します。 | |||
if ((include 'vars.php') == TRUE) { | |||
echo 'OK'; | |||
} | |||
===== require/include ===== | |||
requireはincludeとほぼ同じ。違いは、失敗時にE_COMPILE_ERRORが発生して処理を中断する点。includeはE_WARNINGで処理は継続する。 | |||
使い分けとして、変数読込などで読み込めなくても処理を進めて問題ない場合に、include。 | |||
関数定義など、絶対必要なものはrequireなど。 | |||
===== _once ===== | |||
読込済みなら、再読込しない点がinclude/requireとの決定的な違い。関数の複数定義のエラーを回避できたりする。 | |||
読み込めたらtrueを返す。 | |||
===== | ===== config.php ===== | ||
* [https://stackoverflow.com/questions/14752470/creating-a-config-file-in-php Creating a config file in PHP - Stack Overflow] | |||
* [https://blog.websandbag.com/entry/2018/12/04/160218 【PHP】静的なconfigファイルの書き方 - websandbag ブログ] | |||
includeとreturnの組み合わせのconfig.phpの設定ファイルをいろんなアプリで使われている。 | |||
<?php | |||
return [ | |||
'name' => 'hoge', | |||
'value' => 'fuga', | |||
]; | |||
?> | |||
<?php | |||
// configファイルを変数に代入 | |||
$config = include __DIR__ . '/config.php'; | |||
// 呼び出し。 | |||
var_dump($config['name']); | |||
?> | |||
こういう形式。このreturnだけの文は、ほぼinclude前提。 | |||
編集対象のアプリの設定を、既存コードと分離する際に、いい方法。 | |||
config.phpをアプリ内で作りたい場合、「[https://laracasts.com/discuss/channels/laravel/how-to-create-dynamically-create-configcustomphp-config-file How to create Dynamically create config/custom.php config file]」にあるように、var_exportを使うとよい。 | |||
// create the array as a php text string | |||
$text = "<?php\n\nreturn " . var_export($myarray, true) . ";"; | |||
===== config class ===== | |||
==== | config.phpをどう用意するかは議論がある。 | ||
* [https://stackoverflow.com/questions/10987703/is-it-right-to-set-a-config-php-class-to-keep-project-settings Is it right to set a config PHP class to keep project settings? - Stack Overflow] | |||
* [https://docs.php.earth/security/configuration/ Configuration in PHP applications | PHP.earth] | |||
* [https://qiita.com/nishimura/items/a396c999a85fa4cbc4a0 PHPでプログラム全体の設定に使う変数の保持の仕方 #PHP - Qiita] | |||
* [https://php-archive.net/php/config-class/ [PHP]コンフィグファイルから設定情報を読み込むためのConfigクラス | PHP Archive] | |||
* [https://qiita.com/satorunooshie/items/ca41f7c824c7ea747708 PHPでconfigファイルをオートロードで呼び出す方法 #Config - Qiita] | |||
* [https://stackoverflow.com/questions/14659769/using-a-config-file-from-within-a-php-class Using a config file from within a php class - Stack Overflow] | |||
* [https://stackoverflow.com/questions/33742740/php-oop-config-class PHP OOP Config Class - Stack Overflow] | |||
include/returnではなくて、クラスのconst定数にするという。 | |||
* クラスのconst定数 | |||
* iniファイル/parse_ini_file | |||
* | |||
* | |||
他に、configクラスを用意しておいて、シングルトンか、staticメソッドで参照する形。 | |||
どれくらいの頻度で参照するか次第。参照頻度が低いなら、getで毎回設定ファイルを読み込む。参照頻度が高いなら$configをstaticのクラス変数にもたせる。 | |||
/** | |||
* config.phpに記載の設定項目を取得する。 | |||
* @param string $key configのキー。 | |||
* @return mixed configの値かnull。 | |||
*/ | |||
public static function get(string $key) | |||
{ | |||
return (include __DIR__ . '/config.php')[$key] ?? null; | |||
} | |||
===Function=== | |||
==== User defined ==== | |||
[https://www.php.net/manual/ja/functions.user-defined.php PHP: ユーザー定義関数 - Manual] | |||
</ | 関数は以下のような構文で定義する。 | ||
<?php | |||
function foo($arg_1, $arg_2, /* ..., */ $arg_n) | |||
{ | |||
echo "関数の例\n"; | |||
return $retval; | |||
} | |||
?> | |||
関数内では、他の関数やクラス定義を含む、PHPのあらゆるコードを使用可能。関数内で関数を定義できないC言語とは異なる。 | |||
PHPでは、変数と異なり、関数やクラスは全てグローバルスコープ。関数内で定義した関数も外部から呼び出し可能。スコープが欲しければ、無名関数を使う。 | |||
また、関数のオーバーロードもできない。関数をunsetしたり、再定義も不能。 | |||
可変引数と、デフォルト引数もある。 | |||
==== Argument ==== | |||
[https://www.php.net/manual/ja/functions.arguments.php PHP: 関数の引数 - Manual] | |||
==== | ===== Comma ===== | ||
[https://www.php.net/manual/ja/migration73.new-features.php PHP: 新機能 - Manual] | |||
PHP 7.3から、関数呼び出し時の終端カンマを許容。 | |||
my1(1,); my2(2,); // OK | |||
PHP 8.0.0から、関数定義時の引数リストの最後のカンマが許容される。 | |||
<?php | |||
function takes_many_args( | |||
$first_arg, | |||
$second_arg, | |||
$a_very_long_argument_name, | |||
$arg_with_default = 5, | |||
$again = 'a default string', // この最後のカンマは、8.0.0 より前では許されません。 | |||
) | |||
{ | |||
// ... | |||
} | |||
?> | |||
===== Reference ===== | |||
引数はデフォルトで値渡しになる。値がコピーされて渡される。関数内部で引数自体を修正したい場合、リファレンス渡しにする。 | |||
関数定義で変数の前に&をつけると、リファレンス参照になる。 | |||
<?php | |||
function add_some_extra(&$string) | |||
{ | |||
$string .= 'and something extra.'; | |||
} | |||
$str = 'This is a string, '; | |||
add_some_extra($str); | |||
echo $str; // 出力は 'This is a string, and something extra.' となります | |||
?> | |||
===== Default ===== | |||
関数定義時に、引数部分で変数に値を代入するようにして、デフォルト値を定義できる。引数が指定されなかった場合に使われる。なお、nullが渡された場合も、デフォルト値の代入はされないので注意する。 | |||
function makecoffee($type = "cappuccino") | |||
{ | |||
return "Making a cup of $type.\n"; | |||
} | |||
デフォルト値には、定数を指定できる。具体的には、スカラー値、配列、null。PHP 8.1.0から、new ClassName記法でインスタンスも指定できる。 | |||
デフォルト引数は、デフォルト値のない引数の右側の必要がある。そうでない場合、省略できず、指定する意味がなくなく。 | |||
「[https://stackoverflow.com/questions/55587939/default-callable-in-function-definition-in-php-7 php 7 - Default callable in function definition in php 7 - Stack Overflow]」にあるように、$callableのデフォルト引数に匿名関数を指定したりはできない。デフォルトnullを指定しておいて、以下のような匿名関数で設定するとよいだろう。 | |||
$callback = $callback ?: function($e) {return $e}; | |||
[https:// | 関数内で、値の有無を確認する必要がある。 | ||
===== 可変長引数 ===== | |||
引数リストに...を含めることで、可変長の引数を受け取ることを示す。...を前置した変数に配列として入る。 | |||
<?php | <?php | ||
function sum(...$numbers) { | |||
$acc = 0; | |||
foreach ($numbers as $n) { | |||
$acc += $n; | |||
} | |||
return $acc; | |||
} | } | ||
echo sum(1, 2, 3, 4); | |||
...の前に型宣言も付与できるが、その場合配列要素が全部その型が必要になる。 | |||
===== 名前付き引数 ===== | |||
PHP 8.0.0から名前付き引数が導入された。引数の位置、順番ではなく、名前ベースで渡せる。これにより、デフォルト値を持つ引数をスキップできるし、引数の順番を意識しなくてよくなる。 | |||
引数の名前の後にコロン:をつけたものを値の前につけて指定する。引数の名前には予約語も使える。ただし、変数など動的には指定できない。 | |||
位置引数との混在もできる。その場合、名前付き引数は最後にする必要がある。 | |||
<?php | |||
myFunction(paramName: $value); | |||
array_foobar(array: $value); | |||
PHP 8.1.0では、引数を...で展開した後に、名前付き引数も指定できる。ただし、展開済み引数の上書きはだめ。 | |||
function foo($a, $b, $c = 3, $d = 4) { | |||
return $a + $b + $c + $d; | |||
} | } | ||
var_dump(foo(...[1, 2], d: 40)); // 46 | |||
var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46 | |||
var_dump(foo(...[1, 2], b: 20)); // Fatal error. Named parameter $b overwrites previous argument | |||
==== | ==== Return value ==== | ||
==== | Ref: [https://www.php.net/manual/ja/functions.returning-values.php PHP: 戻り値 - Manual]. | ||
*[https:// | |||
*[https://www.php.net/manual/ja/ | 関数はreturn文で値を返せる。そこで処理を終了する。 | ||
*[https:// | |||
*[https:// | returnを省略した場合、nullを返す。 | ||
==== Variable Functions/可変関数/Callable/コールバック ==== | |||
* [https://www.php.net/manual/ja/functions.variable-functions.php PHP: 可変関数 - Manual] | |||
* [https://www.php.net/manual/ja/language.types.callable.php PHP: コールバック / Callable - Manual] | |||
* [https://www.ycomps.co.jp/staffblog/11934 【PHP】コールバック関数サンプル3つをまとめる - ウェブ集客で企業を成功に導くホームページ制作会社|(株)ワイコム・パブリッシングシステムズ(福岡)] | |||
* [https://pisuke-code.com/php-call-func-class-from-string/ PHPで関数やクラスを文字列から呼び出しする方法まとめ | PisukeCode - Web開発まとめ] | |||
* [https://begien.com/article/29/view 【小ネタ】phpで変数でメソッドを実行する | BeginnerEngineerBlog] | |||
* [https://zenn.dev/tanomu/articles/d92721f597e87f call_user_func() と $function() の動きが違った] | |||
PHPで関数を引数で指定したり、変数として扱う仕組みがある。evalを使う必要はない。 | |||
可変関数は、関数を文字列で実行する仕組み。これとは別で、Callableという型がある。 | |||
可変関数は、関数名の文字列の変数に丸括弧を追加したら実行できるというもの。インスタンス変数があれば、メソッドもできる。 | |||
PHP 7.0から、関数のみ"str"()も可能になった。「[https://www.php.net/manual/ja/migration70.php PHP: PHP 5.6.x から PHP 7.0.x への移行 - Manual]」に記載はないが、パース方法が変わったことが由来の模様。 | |||
関数もメソッドも統一的に扱うものとして、Callable型がある。 | |||
CallableはPHPで関数を引数として渡したり、関数名の文字列を渡して、動的に関数を実行する仕組み。 | |||
callable型で表す。関数だけでなく、メソッドやstaticメソッドも対応できる。方法が2種類ある。 | |||
# 関数: 関数名の文字列。 | |||
# メソッド: 配列で指定。0番目の要素に、インスakeタンスやオブジェクト。1番目の要素にメソッド名の文字列で指定する。 | |||
# staticメソッド: 配列で指定。0番目の要素に、クラス名を指定する。'ClassName::methodName' 形式でも指定可能。 | |||
==== anonymous/無名関数 ==== | |||
[https://www.php.net/manual/ja/functions.anonymous.php PHP: 無名関数 - Manual] | |||
2009年頃にPHP 5.3で登場したらしい ([https://hnw.hatenablog.com/entry/20090710 PHP 5.3の無名関数を試してみた - hnwの日記])。 | |||
callableの型。非常に重要。 | |||
$message = "message"; | |||
// "use" がない場合 | |||
$example = function () { | |||
// 未定義変数参照扱い | |||
var_dump($message); | |||
}; | |||
$example(); | |||
// $message を引き継ぎます | |||
$example = function () use ($message) { | |||
var_dump($message); | |||
}; | |||
useを指定した場合だけ、親のスコープから変数を引き継げる。変数は関数定義時の値。 | |||
クラスのコンテキストの場合、$thisは自動で引き継がれる。 | |||
即時関数として使うなら、引数で全部渡せる。が、useを使うと引数に指定しなくていいので短くできる。即時関数なら、useで問題ない。 | |||
==== arrow/アロー関数 ==== | |||
[https://www.php.net/manual/ja/functions.arrow.php PHP: アロー関数 - Manual] | |||
PHP 7.4で追加。無名関数の簡易構文。かなり短く記述できる。特に、デフォルトで全部キャプチャーしてくれるのが楽。 | |||
fn (argument_list) => expr | |||
親の変数を暗黙でキャプチャー (コピー)。参照でキャプチャーしたい場合は無名関数を使うしかない。 | |||
$y = 1; | |||
$fn1 = fn($x) => $x + $y; | |||
// $y を値渡しするのと同じ | |||
$fn2 = function ($x) use ($y) { | |||
return $x + $y; | |||
}; | |||
var_export($fn1(3)); | |||
また、関数本文部分は式。forなどの文を書けない。 | |||
どうしても複数行の処理を書きたいなら、結果を配列にする。 | |||
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function(){;}), " ns\n"; | |||
長くなるなら、無名関数にしたほうがよさそう。 | |||
===Classes and Objects=== | |||
====The Basics==== | |||
[https://www.php.net/manual/ja/language.oop5.basic.php PHP: クラスの基礎 - Manual] | |||
=====class===== | |||
class内には変数 (プロパティー)、定数、関数 (メソッド) を含められる。 | |||
class内の関数などで、これらのプロパティー、メソッド類の参照時は、擬似変数$this->経由で参照できる。$thisは呼び出し元オブジェクトが入っている。 | |||
C系言語であれば、$this->相当は省略できたが、PHPでは指定が必要なので注意する。 | |||
===== new ===== | |||
インスタンス生成に使うキーワード。コンストラクターで例外をスローしていない限り、常にオブジェクトを生成する。 | |||
$instance = new SimpleClass(); | |||
$instance = new SimpleClass; // コンストラクターに引数を渡さない場合、()を省略可能。 | |||
$class_name = 'SimpleClass'; | |||
$instance = new $class_name; // クラス名の変数も使用可能。 | |||
$instance = new ('SimpleClass'); // PHP 8.0.0以上で丸括弧で囲むことで任意の指揮と一緒に使える。 | |||
文字列で動的に生成可能な点は重要。 | |||
https://chatgpt.com/c/67a992f0-41f0-800b-9d6b-7bafb5ffda5f | |||
new self, new parent, new staticもある。new staticは遅延静的束縛。 | |||
new staticで静的束縛する場合、戻り値はstaticにする。ただし、PHP 8.0からしか使えない。その場合、戻り値はなしで、phpdocで@return staticにする。 | |||
=====::class===== | |||
<className>::classでクラス名の完全修飾子の文字列を取得できる。 | |||
例外の試験など、クラス名の情報が必要な時によくみかける。 | |||
PHP 8.0.0からオブジェクトに対しても::classを使用でき、元のクラス名を取得できる。その場合、get_class()と同じ。同じならPHP 7で使えないのでget_class()でいいか。 | |||
====Property==== | |||
Ref: [https://www.php.net/manual/ja/language.oop5.properties.php PHP: プロパティ - Manual]. | |||
クラスのメンバー変数のことをプロパティー (property) とPHPでは呼んでいる。 | |||
クラス内で、1以上のキーワード (アクセス権、static、PHP 8.1.0以後のみreadonly) のあとに、オプション型宣言 (PHP 7.4以後、readonly以外) の後に変数宣言を続ける。 | |||
public $var1 | |||
static $var2 | |||
var $var3 | |||
staticなど、アクセス権を指定しない場合、publicとデフォルトでみなされる。なお、varキーワードを使う方法もある。これはPHP4までのプロパティーの宣言方法。PHP5以後はpublicと同じ意味になる ([https://stackoverflow.com/questions/1206105/what-does-php-keyword-var-do What does PHP keyword 'var' do? - Stack Overflow])。 | |||
が、[[https://www.php-fig.org/psr/psr-12/#43-properties-and-constants PSR-12: Extended Coding Style - PHP-FIG]] などのコーディング規約で禁止されている。古い書き方なのでpublicに書き直せばいいと思う。 | |||
宣言時に初期値を代入もできるが、初期値は定数のみ。関数類は使用不能。 | |||
*[https://stackoverflow.com/questions/40827870/constant-expression-contains-invalid-operations php - Constant expression contains invalid operations - Stack Overflow] | |||
*[https://qiita.com/H40831/items/15ebfbf7d9c05001b6df 【PHP】クラスプロパティの値には、動的な値を代入することができないようです。 #error - Qiita] | |||
以下のエラーが出る。 | |||
PHP Fatal error: Constant expression contains invalid operations in /ぼくのかんがえたさいきょうのクラス.php on line 5 | |||
関数類で動的に代入したい場合、__constructでやる。 | |||
クラスメソッドからstaticでないプロパティーにアクセスするには、-> (オブジェクト演算子/object operator) を使う。 | |||
===== プロパティー内の共有 ===== | |||
https://chatgpt.com/c/67a58b69-45a0-800b-a8a0-057756e0e5b7 | |||
同じ値をプロパティーの配列などで指定したいことがある。ただし、プロパティーで可能な初期値は定数のみ。いくつか方法がある。 | |||
# const: 普遍の場合。 | |||
# static: 変更する場合。 | |||
# define: グローバルなので非推奨。 | |||
基本はcostで宣言した値をself::で流用すればいい。 | |||
===== 型宣言 ===== | |||
====== Fatal error: Type of Bar::$a must not be defined (as in class Foo) ====== | |||
[https://qiita.com/shin1x1/items/12f61c2418e9a8e5bd38 PHP 7.4: 基底クラスのプロパティに型が無いと、継承クラスでは型が付けられない #PHP - Qiita] | |||
基底クラスのプロパティーに型宣言がない場合、派生クラスで型を変更できない。 | |||
そのため、型宣言すると上記のエラーになる。 | |||
====Autoloading Classes==== | |||
*[https://www.php.net/manual/en/language.oop5.autoload.php PHP: Autoloading Classes - Manual] | |||
*[https://www.php.net/manual/en/function.spl-autoload-register.php PHP: spl_autoload_register - Manual] | |||
別のファイルのクラスを使う方法の話。 | |||
#require_once()/require()/include: シンプルなファイル読み込み。PHP 4から。 | |||
#__autoload(): 非推奨。PHP 5.0で登場。 | |||
#spl_autoload_register(): PHP標準。PHP 5.1.0で登場。 | |||
#composer autoload: composer。 | |||
C系言語であれば、includeなどで外部ファイルをそのまま自分のファイルに読み込む。PHPでもrequire_onceなどで似たようなこともできる。が、PHPではこれをクラスごとに記述するのが煩雑だとして、自動で読み込む仕組みがいくつかある。 | |||
GNU socialでも <https://notabug.org/gnusocialjp/gnusocial/src/main/lib/util/framework.php> でspl_autoload_registerを使っている。 | |||
基本的にはcomposerのautoloadかPHP標準のspl_autoload_registerの2択になっている。 | |||
基本的な使用方法。<syntaxhighlight lang="php"> | |||
<?php | |||
spl_autoload_register(function ($class_name) { | |||
include $class_name . '.php'; | |||
}); | |||
$obj = new MyClass1(); | |||
$obj2 = new MyClass2(); | |||
?> | |||
</syntaxhighlight>MyClass1.php MyClass2.phpから該当クラスを自動読み込みする。 | |||
該当クラスを使おうとしたときに、spl_autoload_registerに登録した関数が呼ばれる模様。 | |||
spl_autoload_registerは、指定した関数を__autoload()の実装として登録する。順番に登録する。 | |||
spl_autoload_register(?callable $callback = null, bool $throw = true, bool $prepend = false): bool | |||
callback: <code>callback(string $class): void</code> 。重要。nullを指定するとデフォルトのspl_autload()が登録される。$classにはクラスの完全修飾子が入る。 | |||
このcallback内で独自のrequire_once相当をいろいろ指定する形になる。 | |||
ただ、今はcomposerを使うのが主流。 | |||
https://chatgpt.com/c/67355ddc-166c-800b-93ee-a25843cb2839 | |||
spl_autoloader_registerとcomposerのオートローディングはどちらを使ったほうがいいですか? | |||
spl_autoloader_register | |||
* 利点 | |||
** '''軽量で柔軟''':シンプルなプロジェクトでは、1つの関数で基本的なオートローディングを簡単に設定できます。 | |||
** '''依存関係がない''':Composerのような外部ツールや設定ファイルを必要としないので、PHP単体で実装できます。 | |||
* 欠点 | |||
** '''設定が手動''':名前空間とディレクトリのマッピングを手動で設定する必要があり、大規模なプロジェクトでは管理が複雑になりがちです。 | |||
** '''標準規約に対応するのが難しい''':<code>PSR-4</code>のような標準的なオートローディング規約(ファイル構造と名前空間のマッピングルール)を自分で実装する必要があるため、コードが煩雑になる可能性があります。 | |||
** '''依存ライブラリの管理が難しい''':外部ライブラリを含める場合は、<code>require</code>や<code>include</code>で個別に読み込む必要があり、依存管理が困難です。 | |||
* | |||
* | |||
* | |||
* | |||
* | |||
* | |||
* | |||
* | |||
< | |||
composer | |||
* 利点 | |||
** '''自動設定''':<code>PSR-4</code>対応の名前空間とディレクトリのマッピングを自動的に管理するため、設定が簡単です。 | |||
** '''依存ライブラリの自動管理''':Composerは依存する外部ライブラリもインストールしてオートロードするため、大規模なプロジェクトで便利です。 | |||
** '''メンテナンスが容易''':プロジェクトの規模が大きくなっても、<code>composer.json</code>ファイルで管理できるため、ファイル構成や依存関係の変更に対応しやすいです。 | |||
* 欠点 | |||
** '''Composer依存''':Composerがインストールされていないと使えません。また、プロジェクトに<code>composer.json</code>の設定が必要です。 | |||
** '''追加の学習が必要''':Composerの使い方や設定ファイルの理解が必要ですが、習得すれば特に問題にはなりません。 | |||
==== Visibility ==== | |||
[https://www.php.net/manual/ja/language.oop5.visibility.php PHP: アクセス権 - Manual] | |||
プロパティー、メソッド、定数 (PHP 7.1.0以上) にはpublic/protected/privateのアクセス権 (visibility) を指定できる。 | |||
* public: どこからでもアクセス可能。 | |||
* protected: クラス自身、継承クラス、親クラス。 | |||
* private: クラス自身。 | |||
アクセス権を省略した場合、public扱いになる。 | |||
なお、同じ型のオブジェクト間では、同一インスタンスでなくても、protected/privateにもアクセス可能。オブジェクト内ではオブジェクト実装が既知だから。 | |||
アクセス権の指定は、PHP 5から導入。PHP 4ではvar指定で全部publicだった。Doxygenや命名規則 (先頭_はprivate) などで区別していた (https://chatgpt.com/c/67a031c2-ce24-800b-b1bf-7d4668b58fb4<nowiki/>)。 | |||
==== スコープ定義演算子 (::) ==== | |||
* [https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php PHP: スコープ定義演算子 (::) - Manual] | |||
* [https://www.php.net/manual/ja/language.oop5.late-static-bindings.php PHP: 遅延静的束縛 (Late Static Bindings) - Manual] | |||
* [https://qiita.com/kouki_o9/items/5fd652ce6c7322480089 [PHP]staticメソッドとstatic::に関するメモ #初心者 - Qiita] | |||
スコープ定義演算子 (::) はトークンの一つ。定数、staticプロパティー、staticメソッド、親クラスなどにアクセスできる。 | |||
[Paamayim Nekudotayim] とも呼ぶ。ダブルコロンを意味するヘブライ語らしい。 | |||
staticメソッド/プロパティーは、遅延静的束縛 (Late Static Bindings) でアクセス可能。 | |||
* MyClass::CONST_VALUE/$classname::CONST_VALUE; | |||
* self::$my_static | |||
* parent::CONST_VALUE | |||
* static: 実行時に最初の呼び出しクラスを参照。 | |||
* | |||
* | |||
* | |||
staticは少々ややこしい。基本はself::でよいと思う。 | |||
==== | ====Traits==== | ||
[https://www.php.net/manual/ja/language.oop5.traits.php PHP: トレイト - Manual] | |||
* | |||
* | コード再利用のための仕組み。単一継承言語で、コードを再利用するための仕組み。関数クラス (デリゲート) 的なもの。クラスに関数クラスのメソッドを取り込める。インスタンス生成などはできず、関数を水平方向で構成可能にする。継承しなくても、メンバーに追加できる。 | ||
<?php | |||
trait ezcReflectionReturnInfo { | |||
function getReturnType() { /*1*/ } | |||
==== | function getReturnDescription() { /*2*/ } | ||
} | |||
class ezcReflectionMethod extends ReflectionMethod { | |||
use ezcReflectionReturnInfo; | |||
/* ... */ | |||
} | |||
class ezcReflectionFunction extends ReflectionFunction { | |||
use ezcReflectionReturnInfo; | |||
/* ... */ | |||
} | |||
?> | |||
==== Overloading/オーバーロード ==== | |||
[https://www.php.net/manual/ja/language.oop5.overloading.php#object.get PHP: オーバーロード - Manual] | |||
一般的には多重定義の意味だが、PHPのオーバーロードはプロパティーやメソッドを動的に作る機能。マジックメソッドで実装される (オーバーロードメソッド)。 | |||
オーバーロードメソッドは、未宣言プロパティー・メソッドのアクセス時に発動する。 | |||
オーバーロードメソッドはすべてpublicで定義する。 | |||
===== プロパティーのオーバーロード ===== | |||
public __set(string $name, mixed $value): void | |||
public __get(string $name): mixed | |||
public __isset(string $name): bool | |||
public __unset(string $name): void | |||
setは不在プロパティーへのデータ書き込み時に使用。 | |||
$instance[$name] = $value; | |||
==== Magic/マジックメソッド ==== | |||
[https://www.php.net/manual/ja/language.oop5.magic.php PHP: マジックメソッド - Manual] | |||
PHPのデフォルトの動作を上書きする特別なメソッドをマジックメソッドと呼んでいる。 | |||
どらも__ (アンダーバー2個) から始まる。__始まりの全メソッドはPHPで予約されているのでユーザー定義メソッドとしては非推奨。 | |||
以下がある。 | |||
* __construct | |||
* __destruct | |||
* __call | |||
* __callStatic | |||
* __get | |||
* __set | |||
* __isset | |||
* __unset | |||
* __sleep | |||
* __wakeup | |||
* __serialize | |||
* __unserialize | |||
* __toString | |||
* __invoke | |||
* __set_state | |||
* __clone | |||
* __debugInfo | |||
__construct/__destruct/__clone以外の全マジックメソッドはpublic必須。E_WARNINGの警告が発生する。 | |||
__construct/__desctructは戻り値型を宣言してはいけない。 | |||
===== | ====Other==== | ||
=====クラス名の取得===== | |||
* [https:// | *[https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name php - How do I get an object's unqualified (short) class name? - Stack Overflow] | ||
* [https://www.php.net/manual/ja/function. | *[https://www.php.net/manual/ja/function.get-class.php PHP: get_class - Manual] | ||
*[https://qiita.com/miriwo/items/7972261a710e79dd1fd5 PHP クラス名::classはどういう処理?? #初心者 - Qiita] | |||
* [https://qiita.com/ | *[https://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class.class PHP: クラスの基礎 - Manual] | ||
* [https:// | get_class($object); | ||
クラス名::class | |||
$object::class // PHP 8.0以上 (get_class相当) | |||
(new \ReflectionClass($obj))->getShortName(); // クラス名 | |||
基本は名前空間付きのフルパスでの取得。クラス名だけだとgetShortName()。クラスがなければnewで例外。 | |||
=== Generator === | |||
* [https://www.php.net/manual/ja/language.generators.php PHP: ジェネレータ - Manual] | |||
* [https://www.php.net/manual/ja/class.generator.php PHP: Generator - Manual] | |||
PHP 5.5から導入。 | |||
シンプルなイテレーターの実装のための機能。配列と異なり、大量のメモリーの確保が不要になる。 | |||
関数でreturnの代わりにyieldで値を返すとジェネレーター関数になる。yieldはGeneratorオブジェクトを返す。 | |||
== | PHPがyieldした時点の状態を記憶しており、次呼ばれたらその続きから処理してくれる。 | ||
<?php | |||
function gen_one_to_three() { | |||
for ($i = 1; $i <= 3; $i++) { | |||
// yield を繰り返す間、$i の値が維持されることに注目しましょう | |||
yield $i; | |||
} | |||
} | |||
$generator = gen_one_to_three(); | |||
foreach ($generator as $value) { | |||
echo "$value\n"; | |||
} | |||
?> | |||
yield時。 | |||
= | * yield $id => $fields;でキーバリューで返す。 | ||
* yeildだけだとNULL。 | |||
なお、ジェネレーターの値は前に進むことしかできない。 | |||
[https://qiita.com/Hiraku/items/190443b33ee7a2167ade PHPで高速オシャレな配列操作を求めて #PHP - Qiita] | |||
真価を発揮するのは、巨大配列の処理や、配列処理の分割。 | |||
* | array_関数で配列を一括処理すると遅い。foreachでやると速いが、foreachの処理内容を分割できない。そういうときに、foreachの処理をyieldにすると、分割できる。後続の処理も、generator (配列) を引数に受け取る想定で作ると、連携できる。 | ||
* | echo (new Collection(range(0, 10000))) | ||
->filter('$_ % 2 === 0') | |||
->map('$_ ** 2') | |||
->filter('$_ > 20') | |||
->sum() | |||
; | |||
// 一部だけ切り出す | |||
function my_special_logic($arr) { | |||
return $arr | |||
->filter('$_ % 2 === 0') | |||
->map('$_ ** 2'); | |||
} | |||
// 再利用 | |||
echo my_special_logic(new Collection(range(0, 10000))) | |||
->filter('$_ > 20') | |||
->sum(); | |||
$ | $mapped = []; | ||
for ($v = 0; $v <= 10000; ++$v) { | |||
if ($v % 2) continue; | |||
$v **= 2; | |||
if ($v <= 20) continue; | |||
$mapped[] = $v; | |||
} | |||
echo array_sum($mapped); | |||
// こんな関数は作れない | |||
function my_special_logic($v) { | |||
if ($v % 2) continue; | |||
$v **= 2; | |||
return $v; | |||
} | |||
function my_special_logic($arr) { | |||
foreach ($arr as $v) { | |||
if ($v % 2) continue; | |||
$v **= 2; | |||
yield $v; | |||
} | |||
} | |||
$sum = 0; | |||
< | foreach (my_special_logic(range(0, 10000)) as $v) { | ||
$ | if ($v <= 20) continue; | ||
$sum += $v; | |||
} | |||
) | |||
echo $sum; | |||
後続処理を関数にしたいなら、next_func(my_special_logic(range(0, 10000)))のようにする。きれい。 | |||
=== Namespace === | |||
[https://www.php.net/manual/ja/language.namespaces.php PHP: 名前空間 - Manual] | |||
PHPの名前空間は、以下の2の問題の解決用の仕組み。 | |||
# 自作の関数や変数類の名前がPHPの組込と衝突。 | |||
# 名前衝突回避のために長い名前が必要。 | |||
なお、requie_onceによる別ファイルの読込か、オートロードで他のファイルなどのシンボルにアクセスできることが、前提になっている。 | |||
==== definition ==== | |||
[https://www.php.net/manual/ja/language.namespaces.definition.php PHP: 名前空間 - Manual] | |||
名前空間の影響を受けるのは、以下。 | |||
* クラス | |||
* インターフェイス | |||
* 関数 | |||
* 定数 | |||
以下の構文でファイル先頭で宣言する。 | |||
namespace [Name]; | |||
namespace [Name]\[Sub]; | |||
ただし、declareは例外でnamespaceの前にも書ける。ただ、それ以外だとPHPコード以外も含めて記述不能。 | |||
同じ名前空間を複数のファイルで定義することも可能。これにより、ファイルをまたいで名前空間を共有できる。 | |||
また、名前空間は階層を持つことができる。バックスラッシュで区切る。 | |||
===== Multiple ===== | |||
[https://www.php.net/manual/ja/language.namespaces.definitionmultiple.php PHP: 同一ファイル内での複数の名前空間の定義 - Manual] | |||
1ファイルで複数の名前空間の定義が可能。 | |||
namespace MyProject { | |||
const CONNECT_OK = 1; | |||
class Connection { /* ... */ } | |||
function connect() { /* ... */ } | |||
} | |||
namespace AnotherProject { | |||
const CONNECT_OK = 1; | |||
class Connection { /* ... */ } | |||
function connect() { /* ... */ } | |||
} | |||
namespace { // global code | |||
session_start(); | |||
$a = MyProject\connect(); | |||
echo MyProject\Connection::start(); | |||
} | |||
名前空間とグローバルを分ける場合、グローバルを名前を指定しないnamespaceで囲む。 | |||
===== Basic ===== | |||
ファイルへのアクセスに、相対パスと絶対パスがあるように、名前空間へのアクセス方法がいくつかある。 | |||
# $a = new foo(): 名前空間を指定しない場合。現在の名前空間currentnamespaceがあれば、currentnamespace\foo。なければグローバルのfoo。 | |||
# $a = new subnamespace\foo(): | |||
# $a = new \currentnamespace\foo(): 完全修飾名。グローバルプレフィクス演算子付きのクラス名。 | |||
現在の名前空間に該当シンボルが不在の場合、自動的にグローバル名前空間 (先頭\) も探す。 | |||
余計な検索が発生するので、わかっているならグローバルで最初から指定したほうがいいかも。 | |||
===== Importing ===== | |||
[https://www.php.net/manual/ja/language.namespaces.importing.php PHP: エイリアス/インポート - Manual] | |||
外部の完全修飾名をエイリアスで参照できる。use演算子を使う。namespaceで同じ名前空間に以内なら、useか完全修飾名を使う必要がある。 | |||
// これは use My\Full\NSname as NSname と同じです | |||
use My\Full\NSname; | |||
useで指定する際は、完全修飾形式。 | |||
use文はグループ化できる。 | |||
use some\namespace\ClassA; | |||
use some\namespace\ClassB; | |||
use some\namespace\ClassC as C; | |||
use some\namespace\{ClassA, ClassB, ClassC as C}; | |||
===== Global ===== | |||
[https://www.php.net/manual/ja/language.namespaces.global.php PHP: グローバル空間 - Manual] | |||
名前の先頭に\をつけるとグローバル空間の名前を指定できる。 | |||
$f = \fopen(...) | |||
=== Reserved === | |||
==== keywords ==== | |||
[https://www.php.net/manual/ja/reserved.keywords.php PHP: キーワードのリスト - Manual] | |||
式や関数ではなく、定数、クラス名、関数名として使えず、PHPで予約されている特別なキーワードがいくつかある。 | |||
statement/文に近い扱い。言語構文の一部扱い。 | |||
{| class="wikitable" | |||
|+PHP のキーワード | |||
|[[/www.php.net/manual/ja/function.halt-compiler.php|__halt_compiler()]] | |||
|[[/www.php.net/manual/ja/language.oop5.abstract.php|abstract]] | |||
|[[/www.php.net/manual/ja/language.operators.logical.php|and]] | |||
|[[/www.php.net/manual/ja/function.array.php|array()]] | |||
|[[/www.php.net/manual/ja/control-structures.foreach.php|as]] | |||
|- | |||
|[[/www.php.net/manual/ja/control-structures.break.php|break]] | |||
|[[/www.php.net/manual/ja/language.types.callable.php|callable]] | |||
=== | |[[/www.php.net/manual/ja/control-structures.switch.php|case]] | ||
|[[/www.php.net/manual/ja/language.exceptions.php|catch]] | |||
==== | |[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class|class]] | ||
|- | |||
|[[/www.php.net/manual/ja/language.oop5.cloning.php|clone]] | |||
|[[/www.php.net/manual/ja/language.oop5.constants.php|const]] | |||
|[[/www.php.net/manual/ja/control-structures.continue.php|continue]] | |||
|[[/www.php.net/manual/ja/control-structures.declare.php|declare]] | |||
|[[/www.php.net/manual/ja/control-structures.switch.php|default]] | |||
| | |||
|- | |- | ||
| | |[[/www.php.net/manual/ja/function.die.php|die()]] | ||
| - | |[[/www.php.net/manual/ja/control-structures.do.while.php|do]] | ||
| | |[[/www.php.net/manual/ja/function.echo.php|echo]] | ||
| - | |[[/www.php.net/manual/ja/control-structures.else.php|else]] | ||
| | |[[/www.php.net/manual/ja/control-structures.elseif.php|elseif]] | ||
|- | |- | ||
| | |[[/www.php.net/manual/ja/function.empty.php|empty()]] | ||
| | |[[/www.php.net/manual/ja/control-structures.declare.php|enddeclare]] | ||
| | |[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endfor]] | ||
| - | |[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endforeach]] | ||
| | |[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endif]] | ||
|- | |- | ||
| | |[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endswitch]] | ||
| | |[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endwhile]] | ||
| | |[[/www.php.net/manual/ja/function.eval.php|eval()]] | ||
| | |[[/www.php.net/manual/ja/function.exit.php|exit()]] | ||
| | |[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.extends|extends]] | ||
|- | |- | ||
| | |[[/www.php.net/manual/ja/language.oop5.final.php|final]] | ||
| | |[[/www.php.net/manual/ja/language.exceptions.php|finally]] | ||
| - | |[[/www.php.net/manual/ja/functions.arrow.php|fn]] (PHP 7.4 以降) | ||
| | |[[/www.php.net/manual/ja/control-structures.for.php|for]] | ||
| - | |[[/www.php.net/manual/ja/control-structures.foreach.php|foreach]] | ||
|- | |||
|[[/www.php.net/manual/ja/functions.user-defined.php|function]] | |||
|[[/www.php.net/manual/ja/language.variables.scope.php|global]] | |||
|[[/www.php.net/manual/ja/control-structures.goto.php|goto]] | |||
|[[/www.php.net/manual/ja/control-structures.if.php|if]] | |||
|[[/www.php.net/manual/ja/language.oop5.interfaces.php|implements]] | |||
|- | |||
|[[/www.php.net/manual/ja/function.include.php|include]] | |||
|[[/www.php.net/manual/ja/function.include-once.php|include_once]] | |||
|[[/www.php.net/manual/ja/language.operators.type.php|instanceof]] | |||
|[[/www.php.net/manual/ja/language.oop5.traits.php#language.oop5.traits.conflict|insteadof]] | |||
|[[/www.php.net/manual/ja/language.oop5.interfaces.php|interface]] | |||
|- | |- | ||
| | |[[/www.php.net/manual/ja/function.isset.php|isset()]] | ||
| | |[[/www.php.net/manual/ja/function.list.php|list()]] | ||
| | |[[/www.php.net/manual/ja/control-structures.match.php|match]] (PHP 8.0 以降) | ||
| | |[[/www.php.net/manual/ja/language.namespaces.php|namespace]] | ||
| | |[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.new|new]] | ||
| | |- | ||
|[[/www.php.net/manual/ja/language.operators.logical.php|or]] | |||
|[[/www.php.net/manual/ja/function.print.php|print]] | |||
|[[/www.php.net/manual/ja/language.oop5.visibility.php|private]] | |||
[ | |[[/www.php.net/manual/ja/language.oop5.visibility.php|protected]] | ||
|[[/www.php.net/manual/ja/language.oop5.visibility.php|public]] | |||
|- | |||
|[[/www.php.net/manual/ja/language.oop5.properties.php#language.oop5.properties.readonly-properties|readonly]] (PHP 8.1.0 以降) * | |||
|[[/www.php.net/manual/ja/function.require.php|require]] | |||
[ | |[[/www.php.net/manual/ja/function.require-once.php|require_once]] | ||
|[[/www.php.net/manual/ja/function.return.php|return]] | |||
[ | |[[/www.php.net/manual/ja/language.variables.scope.php|static]] | ||
|- | |||
|[[/www.php.net/manual/ja/control-structures.switch.php|switch]] | |||
|[[/www.php.net/manual/ja/language.exceptions.php|throw]] | |||
|[[/www.php.net/manual/ja/language.oop5.traits.php|trait]] | |||
|[[/www.php.net/manual/ja/language.exceptions.php|try]] | |||
|[[/www.php.net/manual/ja/function.unset.php|unset()]] | |||
[ | |- | ||
|[[/www.php.net/manual/ja/language.namespaces.php|use]] | |||
|[[/www.php.net/manual/ja/language.oop5.properties.php|var]] | |||
|[[/www.php.net/manual/ja/control-structures.while.php|while]] | |||
= | |[[/www.php.net/manual/ja/language.operators.logical.php|xor]] | ||
|[[/www.php.net/manual/ja/language.generators.php|yield]] | |||
|- | |||
|[[/www.php.net/manual/ja/language.generators.syntax.php#control-structures.yield.from|yield from]] | |||
| | |||
| | |||
| | |||
| | |||