「PHP」の版間の差分

提供:senooken JP Wiki
(連想配列)
(→‎new)
 
(同じ利用者による、間の209版が非表示)
1行目: 1行目:
== Library ==
== About==


=== Framework ===
=== About ===
PHPはプログラミング言語だ。汎用的なプログラミング言語だが、主にウェブサーバー上のソフトウェアで使用される。


* Symfony
GNU socialはPHPで記述されている。他にもWordPress・NextCloudなどがPHPで記述されている。これらのPHP製ソフトウェアはVPSだけではなく安価なレンタルサーバーでも動作するため、低コストで運用することができる。
* CakePHP
* FuelPHP: 2010年誕生。
* Codeigniter: シンプル、軽量。
* Zend
* Laravel: 2011年誕生。
* Phalcon
* [https://ja.wikipedia.org/wiki/Yii Yii - Wikipedia]


=== Template ===
PHPの公式リファレンスは日本語版があり、わかりやすくまとまっている。
*[https://www.php.net/manual/ja/langref.php PHP: 言語リファレンス - Manual]
*[https://museum.php.net/ PHP: Release Archives (museum)]: PHP1からのソースコードの保管場所。
ウェブ上にはPHPに関するTipsが多く公開されており、大抵の疑問はウェブ検索で解決できる。


* [https://worktoolsmith.com/php-template-engines-matome/ PHPテンプレートエンジンまとめ 一覧と構文例(随時追加中) | WorkToolSmith]
=== Version ===
PHPは言語の版数が上がる際、過去の版と互換性の無い破壊的変更がなされることがある。


いろいろある。
開発者はこのリスクを軽減するために、非推奨の言語機能を避け、実行時の警告 (warning) を適切に処理するべきだ。
* Blade: Laravel標準。
* DIV: 1ファイルでシンプル。大規模には向かない。
* Smarty: 万能。
* Twig: 拡張はしにくい。
使うとしたら、歴史の長いSmarty。


* [https://hnavi.co.jp/knowledge/blog/smarty/ Smartyとは?基礎知識と具体的なメリットをわかりやすく解説 - システム開発のプロが発注成功を手助けする【発注ラウンジ】]
GNU socialは現在PHP 7系で動作する様に記述されており、PHP 8系への対応は作業途中だ。
* [https://w.atwiki.jp/smarty/pages/161.html Smartyとは? - smarty @Wiki - atwiki(アットウィキ)]


高速らしい。
==== 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になって動作しなくなる。他に、型が厳格になっている。


* [https://www.inworks.jp/archives/103 Smarty って、要らなくない? — INWORKS]
具体的には、php.ini/.user.iniで以下を指定して、PHP v7.4時点で警告にできるだけ対応しておく。
* [https://pasela.hatenablog.com/entry/20120906/php_templates 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ]
error_reporting=E_ALL ; -1
* [https://scrapbox.io/fsubal/%E6%9C%AC%E5%BD%93%E3%81%AB%E5%80%92%E3%81%99%E3%81%B9%E3%81%8D%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%AF_jQuery_%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3%E3%81%A0%E3%81%A3%E3%81%9F 本当に倒すべきだったのは jQuery ではなくテンプレートエンジンだった - fsubal]
続いて、phpソースファイルに以下を記入して型を厳密にしておく。
* [https://codezine.jp/article/detail/9594 サーバサイドHTMLテンプレートからの脱却のススメ (1/4)|CodeZine(コードジン)]
declare(strict_types=1);
チェックツールがあるのでこれを使うと問題箇所などがわかる。
*PHP CodeSniffer: コーディング規約の準拠確認ツール。PHPのバージョンアップグレード可否チェックもできる。
*PHPStan: 静的解析ツール。引数の数、型不一致など、潜在的な問題を検出する。
*Rector
まず上記2個を試して、おまけでRectorも試すとよい。


UI/UXを突き詰めると、JavaScriptを使わざるを得ず、サーバーテンプレートエンジンは初回だけなので、いっそのことJSで全部やろうというのが最近の流れの模様。
=== Tool ===


PHP自体が一種のテンプレートエンジンという主張がある。が、関数をあれこれ書く必要があり、可読性が悪い。
* [https://creatopia.jp/media/12102 PHPをブラウザで実行、動作確認できるおすすめツール4つ|Creatopia Media]


SmartyよりTwigのほうが性能が上とか。
PHPをWebブラウザーで実行、動作確認のツールがいくつかある。


[https://phalcon-docs-ja.readthedocs.io/ja/stable/reference/volt.html Volt: テンプレートエンジン — Phalcon 3.0.2 ドキュメント (Japanese / 日本語)]」。高速フレームワークのPhalconではVoltを使っている。
* 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]: パーマリンク。


==== Twig ====
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://pasela.hatenablog.com/entry/20120906/php_templates 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ]
[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]」に記載がある程度。
* [http://fabien.potencier.org/templating-engines-in-php.html Templating Engines in PHP |Articles - Fabien Potencier]
* [http://akisi.tabiyaku.net/?p=896 PHPテンプレートエンジンを使おう Twig編 | AkisiのWEB制作日記]
* [https://www.decodednode.com/2013/08/smarty-vs-twig-part-1.html Decoded Node: Smarty vs. Twig (a benchmark done poorly)]
* [https://github.com/AliShareei/smarty-vs-twig-benchmark GitHub - AliShareei/smarty-vs-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines]
* [https://github.com/dominics/smarty-twig-benchmark GitHub - dominics/smarty-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines]
* [https://medium.com/@gotzmann/the-fastest-template-engine-for-php-b5b461b46296 The fastest template engine for PHP | by Serge Gotsuliak | Medium]
* [https://stackoverflow.com/questions/9363215/pure-php-html-views-vs-template-engines-views Pure PHP/HTML views VS template engines views - Stack Overflow]


Twig v3のほうが速いらしいが、Smarty v3のほうが速いというデータもある。
ただ、「[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]」など、他の規約があり、クラス名と同じになっている。


==== Smarty ====
PHPのクラス名は大文字小文字を区別しないが、わかりにくいので大文字小文字で、クラス名と一致させておくとよさそう。


* [https://github.com/smarty-php/smarty GitHub - smarty-php/smarty: Smarty is a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic.]
ただし、viewなど、表示に直接結びついているものは、小文字でもいいかも。ファイル名とURLパスが同じほうが分かりやすい。
* [https://smarty-php.github.io/smarty/stable/ Smarty Documentation]


「[https://stackoverflow.com/questions/9363215/pure-php-html-views-vs-template-engines-views Pure PHP/HTML views VS template engines views - Stack Overflow]」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。
==== Naming ====


=== ORM ===
* [https://qiita.com/aaari95/items/8325131ce328e42e078c リーダブルコード・PSRから学ぶ 命名規則 #PHP - Qiita]
Ref:  
* [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]


* [https://stackoverflow.com/questions/108699/good-php-orm-library database - Good PHP ORM Library? - Stack Overflow]
変数名やシンボルの命名規則。
* [https://codezine.jp/article/detail/5858?p=2&anchor=0 PHPでの「ORMライブラリ」機能比較 (2/5)|CodeZine(コードジン)]


いろいろある。Doctrineが有名。
* クラス名: CamelCase
* Doctrine: Symfonyで採用。有名。
* メソッド名: mixedCase
* Eloquent
* 定数: UPPER_SNAKE_CASE
* Propel: Symfony v1.2で採用されていた。
* プロパティー: PSRでの規定はない。公式文書だとmixedCase
* PHP activerecord
* 変数名: PSRでの規定はない。公式文書だとlower_snake_case
* PHPDAO
* PDO: PHP標準。
* Xyster
ただ、速度を優先する場合、PDOが最速になるらしい。


ORMは別になくてもいいか。
上記で揃えるとよいだろう。


=== Migrate ===
=== Performance ===


* Phinx
==== About ====
* Doctrine Migrations
PHPのコーディング時に、速度に影響のある書き方がいろいろある。
「[https://zenn.dev/okranagaimo/articles/fe0427f41296ba PHPで「Doctrine Migrations」を使ってみる]」


CakePHPに採用されているPhinxのほうが人気なのでPhinxを使ったほうがよいだろう。
* [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]


=== Test ===
一般論。


* PHPUnit
* 言語構造>組込関数>関数の順に速い。
* 複雑な方法より単純な方法のほうが速い。
* 特に配列関係の関数、呼び出しが頻発する処理などで重要。


=== Search ===
==== Time ====
検索キーワードをフォームから受信後、DBにSQLで検索をかけて取得結果を返すのが基本。
速度の話をするにあたって計測方法。
<?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でやったほうが速い。可読性などの問題はある。


* [https://laravel.com/docs/9.x/scout Laravel Scout - Laravel 9.x - The PHP Framework For Web Artisans]
===== in_array/array_search =====
* [https://github.com/teamtnt/tntsearch teamtnt/tntsearch: A fully featured full text search engine written in PHP]
[https://thk.kanzae.net/net/itc/t2379/ PHP 高速化に関するメモ書き | Thought is free]
* [https://packagist.org/packages/ruflin/elastica ruflin/elastica - Packagist] ([https://www.cloudways.com/blog/php-libraries/ 40 Best PHP Libraries For Web Applications in 2022])


検索サービスで有名なのは以下。
in_array/array_searchは遅いらしい。書き方を変えたほうがいい。


* ElasticSearch/OpenSearch
PHPの言語構造の中で、isset/emptyが非常に重要。これらで値の有無判定ができる。
* MeiliSearch
* Algolia
* Sphinx Search ([https://sphinxsearch.com/ Sphinx | Open Source Search Engine])
* Apache Solr ([https://solr.apache.org/ Welcome to Apache Solr - Apache Solr])/Apache Lucene ([https://lucene.apache.org/ Apache Lucene - Welcome to Apache Lucene])
Laravel Scoutでtntsearchを使う方法がある ([https://qiita.com/sunny_510/items/ba926dc8d8aa7428f142 Laravel Scout + TNTSearchによる小規模プロジェクトへの全文検索機能の追加 #PHP - Qiita]/[https://helog.jp/laravel/scout-tntsearch/ Laravel ScoutとTNTSearchを使用してサイト全文検索を実装してみる – helog])。


「[https://packagist.org/search/?tags=search Packagist]」の検索結果をみても、tntsearchが特に人気の模様。
ただし、もともと単純配列になっているのを、連想配列に変換するくらいならば、forなどを使ってもその変換に時間がかかる。
== About==


=== About ===
そして、in_arrayじゃなくて、for/ifで比較するくらいなら、in_arrayのほうが速い。実装の段階で工夫してキーに値を入れられるならそちら。そうでなければ、そのままin_arrayでよさそう。特に要素数が多い場合。
PHPはプログラミング言語だ。汎用的なプログラミング言語だが、主にウェブサーバー上のソフトウェアで使用される。
$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";


GNU socialはPHPで記述されている。他にもWordPress・NextCloudなどがPHPで記述されている。これらのPHP製ソフトウェアはVPSだけではなく安価なレンタルサーバーでも動作するため、低コストで運用することができる。
==Language Reference==
===Types===
====Introduction====
Ref: [https://www.php.net/manual/en/language.types.intro.php PHP: Introduction - Manual].


PHPの公式リファレンスは日本語版があり、わかりやすくまとまっている。
PHPの変数は以下の型のいずれかの値となる。
*[https://www.php.net/manual/ja/langref.php PHP: 言語リファレンス - Manual]
*null
ウェブ上にはPHPに関するTipsが多く公開されており、大抵の疑問はウェブ検索で解決できる。
*bool
*int
*float (floating-point number)
*string
*array
*object
*callable
*resource
C言語のようなlong/doubleのような精度ごとの型はない。


=== Version ===
==== System ====
PHPは言語の版数が上がる際、過去の版と互換性の無い破壊的変更がなされることがある。
[https://www.php.net/manual/ja/language.types.type-system.php PHP: 型システム - Manual]


開発者はこのリスクを軽減するために、非推奨の言語機能を避け、実行時の警告 (warning) を適切に処理するべきだ。
組込型の他に、ユーザー定義の型、aliasなどいくつかの型がある。


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])。
** null
 
** スカラー型: bool/int/float/string
大きく以下2点がある。
** array
#エラーレベルの上昇。
** object
#型の厳格化。
** resource
エラーレベルが1段階上がったため、今までWarningで問題なかったものがFatal Errorになって動作しなくなる。他に、型が厳格になっている。
** never
** void
** クラス内の相対型: self/parent/static
* Value型: false/true
* ユーザー定義型/クラス型
** インターフェイス
** クラス
** 列挙型
* callable


具体的には、php.ini/.user.iniで以下を指定して、PHP v7.4時点で警告にできるだけ対応しておく。
===== 複合型 =====
error_reporting=E_ALL ; -1
複数の基本型を組み合わせた型。交差型とunion型で作れる。
続いて、phpソースファイルに以下を記入して型を厳密にしておく。
declare(strict_types=1);
チェックツールがあるのでこれを使うと問題箇所などがわかる。
*PHP CodeSniffer
*PHPStan
*Rector
まず上記2個を試して、おまけでRectorも試すとよい。


=== Guide ===
* 交差型: 宣言した複数のクラス型をすべて満たす型。&で表現。T/U/Vの交差型はT&U&Vと書く。
Ref:
* union型: 複数の型を受け入れる型。|で表現。T/U/Vのunion型はT|U|Vと書く。交差型を含む場合、T|(X&U)と丸括弧で囲む必要がある。
*[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]」に記載がある程度。
===== alias =====
PHPはmixedとiterableの2個の型のエイリアスに対応している。


ただ、「[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]」など、他の規約があり、クラス名と同じになっている。
* mixed=object|resource|array|string|float|int|bool|null: PHP 8.0.0で導入。mixedは型のトップ。他の全部の型はこの型の部分になる。
* iterable=Traversable|array: PHP 7.1.0で導入。foreachで反復可能でジェネレーター内でyield from可能。


PHPのクラス名は大文字小文字を区別しないが、わかりにくいので大文字小文字で、クラス名と一致させておくとよさそう。
ただし、ユーザー定義のエイリアスは未対応。


ただし、viewなど、表示に直接結びついているものは、小文字でもいいかも。ファイル名とURLパスが同じほうが分かりやすい。
====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]
条件判定にかかってくるので非常に重要。


=== Tool ===
まずは、下記のfalseになるもの一覧を把握し、それ以外はすべてtrueになるということを把握しておく。
*booleanのfalse
*intの0
*floatの0.0
*stringの空文字列、"0"
*要素数0個のarray
*null (未初期化変数含む)
stringの"0"と要素0のarrayがfalseになる点が重要。注意する。要素0のarrayは包含判定、検索などでよく使う。


* [https://creatopia.jp/media/12102 PHPをブラウザで実行、動作確認できるおすすめツール4つ|Creatopia Media]
stringの0ははまりどころ。stringは何がくるかわからないなら、strlenで文字数を見たほうが確実。
====Strings====
Ref: [https://www.php.net/manual/ja/language.types.string.php PHP: 文字列 - Manual].


PHPをWebブラウザーで実行、動作確認のツールがいくつかある。
非常に重要。
=====Literal=====
文字列リテラルとしては4の表現がある。
*Single quote: <nowiki>''</nowiki> 変数展開されない。エスケープシーケンス無視。
*Double quote: "" 変数展開される。エスケープシーケンス解釈。
*Here document:  <<<EOT 二重引用符扱いで変数展開される。
*Nowdoc: <<<'EOT' 一重引用符扱いで変数展開されない。
引用符内で引用符'を使う場合はバックスラッシュ\でエスケープが必要。バックスラッシュ自体の指定は二重\\。


* paiza.IO: [https://paiza.io/ja/projects/new Online PHP Editor | ブラウザでプログラミング・実行ができる「オンライン実行環境」| paiza.IO]: パーマリンク。
Here document/Nowdocは終端IDのインデントで行頭を識別しており、インデントに意味があるので注意する。
* phpsansbox.io [https://phpsandbox.io/ Write PHP online from your browser - PHPSandbox]: フレームワークも確認可能。
echo <<<END
* OnlinePHP.io: [https://onlinephp.io/ PHP Sandbox - Execute PHP code online through your browser]: 複数バージョンの同時実行できる。
      a
* 3v4l.org: [https://3v4l.org/ Online PHP editor | Test code in 250+ PHP versions]: パーマリンク。
      b
    c
\n
END;


3v4l.orgがパーマリンクがあって、複数バージョンの動作確認できるので、これがいいと思う。
  echo <<<'EOT'
==Package manager==
  My name is "$name". I am printing some $foo->foo.
===Composer===
Now, I am printing some {$foo->bar[1]}.
PHPのパッケージ管理システム。PHP v5.3.2以上で動作する。
This should not print a capital 'A': \x41
====Install====
EOT;
Ref: [https://senooken.jp/post/2020/01/16/ インストール: Composer | モダンなPHPのパッケージマネージャー – senooken JP].
  [ -e installer ] || wget <nowiki>https://getcomposer.org/installer</nowiki>
  php installer --install-dir="$LOCAL/stow/$PKG-$VER/bin" --filename=$PKG
公式サイトからinstallerをダウンロードしてそれを使って任意の場所に設置する。
====Usage====
Composerを使う場合,<code>composer.json</code>ファイルを用意する。このファイルはプロジェクトの依存関係を記載する。VCSで管理すべきファイルだ。


このファイルに使用するライブラリーを以下のように記入する。
===== Escape sequence expansion =====
<{
{| class="wikitable"
    "require": {
|+エスケープされた文字
        "monolog/monolog": "1.0.*"
!記述
    }
!意味
}
|-
composer.jsonに指定する最初の項目はrequireキーだ。このキーで依存パッケージをComposerに知らせる。パッケージ名とバージョンを指定する。
|<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>
|}
繰り返しますが、この他の文字をエスケープしようとした場合には、 バックスラッシュも出力されます!


新規にパッケージを追加する場合は、以下のコマンドでインストールとcomposer.jsonへの追記を行えます。同時に、composer.lockファイルも作成されます。composer.lockも管理すべきファイル。
=====Variable expansion=====
<code>composer require "monolog/monolog:1.0.*"</code>
二重引用符とヒアドキュメントではエスケープシーケンスが解釈され、変数が展開される。<syntaxhighlight lang="php">
パッケージ名はベンダー名とプロジェクト名から構成される。
<?php
$juice = "apple";


1.0.*は1.0の任意のバージョンを示す。
echo "He drank some $juice juice." . PHP_EOL;


composer.jsonを用意したら,以下のようにcomposerのinstallコマンドを実行する。
// 意図しない動作をします。"s" は、変数名として有効な文字です。よって、変数は $juices を参照しています。$juice ではありません。
<code>php composer.phar install</code>
echo "He drank some juice made of $juices." . PHP_EOL;
これにより,<code>vendor</code>ディレクトリーにパッケージがインストールされる。
 
// 参照する変数名を波括弧で囲むことで、変数名の終端を明示的に指定しています。
echo "He drank some juice made of {$juice}s.";


プロジェクトにgitを使っている場合,.gitignoreにvendorディレクトリーを追加したほうがいい。
//
$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;


Composerによるインストールが完了すると,composer.lockファイルにダウンロードしたパッケージとバージョンを出力する。composer.lockをプロジェクトリポジトリーに追加して,プロジェクトメンバー全員が同じバージョンのパッケージを使用する。
// 複雑な例1


composer.lockが存在するプロジェクトで上記コマンドを実行する場合,composer.jsonの内容に加えて,composer.lockの内容も参照されて,composer.lockと同じバージョンがインストールされる。
// これが動作しない理由は、文字列の外で $foo[bar]
// が動作しない理由と同じです。
// PHP はまず最初に foo という名前の定数を探し、
// 見つからない場合はエラーをスローします。
// 定数が見つかった場合は、その値('foo' そのものではない)
// を配列のインデックスとして使います。
echo "This is wrong: {$arr[foo][3]}";


パッケージを最新バージョンに更新したい場合,<code>composer update</code>コマンドを使う。このコマンドを実行すると,最新バージョンをインストールして,composer.lockも更新する。動作としては,composer.lockを削除後に<code>composer install</code>を実行することと等しい。composer updateは基本的には使わない。
// 動作します。多次元配列を使用する際は、
// 文字列の中では必ず配列を波括弧で囲むようにします。
echo "This works: {$arr['foo'][3]}";


updateやinstallの後にパッケージ名を指定すると,指定したパッケージだけ更新やインストールできる。
// 複雑な例。二重展開で変数になる場合だけ式が使える模様。
<code>composer update monolog/monolog</code>
$var1=9;
Composerでインストール可能な主なパッケージは,Packagistリポジトリーに配置されている。
echo "{${mb_strtolower('VAR1')}}"; // 9
====自動読み込み (Autoloading)====
ライブラリーの自動読み込みのために,Composerは<code>vendor/autoload.php</code>ファイルを生成する。以下のように,ライブラリーのクラスを使用するファイルの先頭でこのファイルを読み込めば使えるようになる。
<require __DIR__ . '/vendor/autoload.php';
$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->addWarning('Foo');
composer.jsonの<code>autoload</code>欄に指定することで,ライブラリー以外の自分のコードも自動読み込みの対象にできる。


composer.jsonを編集した場合、<code>composer dump-autoload</code>を実行して<code>vendor/autolaod.php</code>を必ず更新します。
?>
</syntaxhighlight>波括弧はなくてもいいが、文字列が連結するなどして変数名の終端を区別できない場合に必須になる。


==== Libraries ====
特に重要な挙動は以下。
[https://getcomposer.org/doc/02-libraries.md Libraries - Composer]


自前のライブラリーをComposerでインストール可能な形式にする方法がある。
* ""内だと、連想配列添字の引用符不能。
* ${}内だと、連想配列添字の引用符必要。


===== Every project is a package =====
複雑な形式は{$ ... }がセット。{$ } 部分で変数が式扱いになる。
ディレクトリーにcomposer.jsonがあると、そのディレクトリーはパッケージになる。プロジェクトとパッケージの違いは、名前の有無。プロジェクトは名前のないパッケージという扱いになる。


パッケージをインストール可能にするにあたって、'''composer.jsonに最低限名前'''が必要。
<nowiki>さらに複雑なことができる。{${}}を指定すると、内側の波括弧内で、${}部分が変数評価になる場合にだけ式を指定できる。動きがトリッキーすぎる。フォーマット文字列的なことには使えない。バグのもとになりそうなので使用を控えたほうがよさそう。</nowiki>
{
=====Format=====
    "name": "acme/hello-world",
Ref:
    "require": {
*[https://chaika.hatenablog.com/entry/2023/02/14/120000 PHP 文字列中に変数で値を埋め込むやつのメモ - かもメモ]
        "monolog/monolog": "1.0.*"
*[https://qiita.com/iez/items/8a36e5cba8bf6af21f45 python の string.format が便利だからPHPでも使いたい #PHP - Qiita]
    }
PHPにはPythonのformatメソッド相当はない。が似たような目的の関数がある。
}
*sprintf/vprintf
acme/hello-worldというプロジェクトになる。acmeはベンダー名で、ベンダー名は必須。
*strtr
strtrは第2引数にold => newの置換のペアの配列を渡す。やることは同じようなものだけどちょっと違う。


ベンダー名に迷う場合、GitHubのユーザー名が適している。'''パッケージ名は小文字必須'''。単語区切りは-にするのが慣例。
vsprintfは置換対象が可変長引数ではなく配列なだけ。
=====sprintf=====
Ref: [https://www.php.net/manual/ja/function.sprintf.php PHP: sprintf - Manual]


===== Library Versioning =====
今後何度も使う。
VCSでパッケージを管理している場合、composerはVCSからバージョンを自動で判別する。VCSを使っていない場合だけ、versionプロパティーを追加する。
{
    "version": "1.0.0"
}


===== Publishing to a VCS =====
C言語のprintfといろいろ違うところがある。<syntaxhighlight lang="php">
composer.jsonを用意したらVCSのリモートリポジトリーに公開する。ベンダー名とユーザー名は不一致でも問題ない。
<?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);


公開したパッケージを取り込む場合、requireで指定する。
echo sprintf("%'.9d\n", 123); // ......123
{
echo sprintf("%'.09d\n", 123); // 000000123
    "name": "acme/blog",
    "repositories": [
        {
            "type": "vcs",
            "url": "<nowiki>https://github.com/username/hello-world</nowiki>"
        }
    ],
    "require": {
        "acme/hello-world": "dev-master"
    }
}
パッケージ名hello-worldに必要なリポジトリーの情報をrepositoriesで指定している。たぶん、末尾のパッケージ名とリポジトリー名は一致が必要。


===== Publishing to packagist =====
?>
VCSでの公開のケースは以上。ただ、repositoriesの情報は省略する方法がある。これは、Packagistに登録している場合。composerはpackagitstから同盟パッケージを探す。公開して問題ないなら、Packagistへの登録を検討する。
</syntaxhighlight>特徴的なのが`%数$指定子`で引数の番号を選べるところ。Pythonの`{数:指定子}`に似ている。


===== Light-weight distribution packages =====
後は埋める文字を指定する際は'を前置。
.githubディレクトリーのように、パッケージに不要なファイルがある。
=====文字列の切り出し=====
いくつか方法がある。
*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の自由度が高い。速度を気にしなくていいならこれでいいと思われる。ただ、引数の配列に入ってくるのがいまいち。関数の戻り値でほしい。


.gitattributesでパッケージやzipに含めないファイルを指定できる。
strposとsubstrを組み合わせると端の文字列を切り出せる。
// .gitattributes
=====文字列置換=====
/demo export-ignore
*[https://www.php.net/manual/ja/function.str-replace.php PHP: str_replace - Manual]: 日本語不能。記号の置換などで便利。
phpunit.xml.dist export-ignore
*[https://www.php.net/manual/ja/function.substr-replace.php PHP: substr_replace - Manual]: 日本語不能。指定した文字数の位置で置換する。
/.github/ export-ignore
*preg_replace
以下のコマンドで確認できる。
*explode/implode: 日本語OK。
  git archive branchName --format zip -o file.zip
str_replace(
パッケージに含まれないだけで、Gitリポジトリーには入っている。
    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%'>");


==== Articles ====
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が無難で確実。


===== Versions and constraints =====
=====startsWith/endsWith=====
[https://getcomposer.org/doc/articles/versions.md Versions and constraints - Composer]
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]」がある。


composer requireなどで指定するパッケージのバージョンにはいくつか記法がある。このバージョン部分は、composerではversion constraint (バージョン制約) と呼んでいる。このバージョン制約で、チェックアウト対象を判断する。
PHP 8未満なら以下のようなコード。
~/my-library$ git branch
function startsWith( $haystack, $needle ) {
v1
      $length = strlen( $needle );
v2
      return substr( $haystack, 0, $length ) === $needle;
my-feature
  }
another-feature
  function endsWith( $haystack, $needle ) {
 
    $length = strlen( $needle );
~/my-library$ git tag
    if( !$length ) {
v1.0
        return true;
  v1.0.1
    }
  v1.0.2
    return substr( $haystack, -$length ) === $needle;
v1.1-BETA
}
v1.1-RC1
mb_strlen/mb_substrでマルチバイト対応。
v1.1-RC2
 
v1.1
===== 改行分割 =====
v1.1.1
 
v2.0-BETA
* [https://stackoverflow.com/questions/3997336/explode-php-string-by-new-line Explode PHP string by new line - Stack Overflow]
v2.0-RC1
* [https://stackoverflow.com/questions/1483497/split-string-by-new-line-characters php - Split string by new line characters - Stack Overflow]
v2.0
* [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]
v2.0.1
v2.0.2
 
====== tag ======
基本的に、composerはタグを扱う。
 
上記のようなタグの場合、composerは先頭のvなどのプレフィクスを除外して考える。基本的にはこの中で、一番新しいものを優先的に探す。
 
====== branch ======
タグではなく、ブランチのチェックアウトが必要なら、特別なdev-*プレフィクス/サフィックスを指定してブランチを指定する。
 
上記の例で、my-featureブランチの指定が必要ならば、dev-my-featureを指定する。
 
ブランチ名がバージョン名と似ている場合、記法が変わる。v1.x-devのように指定する。v1タグではなく、v1ブランチを明示するために、.xは必須。あるいは、タグ名とブランチ名を完全に別の名前 (v1ブランチの代わりにv1.xブランチ) にしておけば、.xは不要。


バージョン名によく似たブランチ名を指定する場合だけ、dev-プレイフィクスではなく、-devサフィックスを指定する。
<code>explode('\n', $csv)</code> のようなことをしたくなるが、改行が\nとは限らない。
$array = preg_split('/\R/u', $string);
上記がいい。\Rが\r \n \n\rなどにマッチ。uで入力がUTF-8の場合を考慮。例えば、「腰」がuをつけないと分割されてしまう。


==phpDocumentor==
===== trim =====
Ref: [https://phpdoc.org/ Home | phpDocumentor].
[https://www.php.net/manual/ja/function.trim.php PHP: trim - Manual]


PHPのソースコードにコメントを残す際に、構文に従って記載すると、ツールで表示したり、文書に出力できたりする。ソースコードリーディングにも役立つので、積極的に記載したほうがよさそう。構文を整理しておく。
文字列の両端のホワイトスペースを除去する。


特に記法が大事。
===== 文字列反復 =====
*[https://docs.phpdoc.org/3.0/guide/getting-started/what-is-a-docblock.html phpDocumentor]
[https://www.php.net/manual/ja/function.str-repeat.php PHP: str_repeat - Manual]
*[https://docs.phpdoc.org/3.0/guide/references/phpdoc/ phpDocumentor]
str_repeat(string $string, int $times): string
ファイル冒頭の<?php の直後あたりに書くと、ファイルレベルのDocBlockになる。逆にclassの直前などに書くと、ファイル冒頭でもclassレベルになる。
文字列に対する乗算はstr_repeatで行う。他にarray_fillを使った方法もある。


DocBlockは3部構成。
プリペアードステートメントで(?,?)を作るときとかで使う。
#Summary=短い説明。改行直前の.か空行で終わり。
  function timeit(callable $callback)
#Description=長い説明。アルゴリズムの機能や、使用方法、例など。最初のタグか、改行、DocBlockの終端で終わる。
#Tags/Anntations=要素のメタ情報。新しい行の@から始まる。
具体例。<syntaxhighlight lang="php" line="1">
<?php
/**
* A summary informing the user what the associated element does.
*
* A *description*, that can span multiple lines, to go _in-depth_ into
* the details of this element and to provide some background information
* or textual references.
*
* @param string $myArgument With a *description* of this argument,
*                          these may also span multiple lines.
*
* @return void
*/
  function myFunction($myArgument)
  {
  {
  }
  <nowiki> </nowiki>  $time = 'microtime';
</syntaxhighlight>以下の要素に前置できる。
<nowiki> </nowiki>  $nanoFactor = 1000;
*require(_once)
<nowiki> </nowiki>  if (function_exists('hrtime')) {
*include(_once)
<nowiki> </nowiki>      $time = 'hrtime';
*class
<nowiki> </nowiki>      $nanoFactor = 1;
*interface
<nowiki> </nowiki>  }
*trait
*function (including methods)
<nowiki> </nowiki>   $start = $time(true);
*property
<nowiki> </nowiki>  $callback();
*constant
<nowiki> </nowiki>  $stop = $time(true);
*variables, both local and global scope.
<nowiki> </nowiki><nowiki>  return ($stop - $start) * $nanoFactor;
Inheritance
                                                                                                                                      }
                                                                                                                                     
                                                                                                                                     
                                                                                                                                      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]


DocBlockはSummary/Descriptionを上書きしたり、拡張できる。@inheritdocを使う。
行数カウントなどで文字列をカウントしたいことがそれなりにある。


要素ごとに以下のタグを継承する。
substr_countでできる。
{| class="wikitable"
substr_count(
!Elements
    string $haystack,
!Inherited tags
    string $needle,
|-
    int $offset = 0,
|''Any''
    ?int $length = null
|[[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/author.html|@author]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/version.html|@version]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/copyright.html|@copyright]]
): int
|-
|''Classes and Interfaces''
|[[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/category.html|@category]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/package.html|@package]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/subpackage.html|@subpackage]]
|-
|''Methods''
|[[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/param.html|@param]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/return.html|@return]], [[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/throws.html|@throws]]
|-
|''Properties''
|[[/wiki.gnusocial.jp//docs.phpdoc.org/3.0/guide/references/phpdoc/tags/var.html|@var]]
|}@subpackageタグは同じ@packageの親クラスのときだけ継承される。


よく使う@param/@returnの構文。
$text = 'This is a test';
  <type-expression          = 1*(array-of-type-expression|array-of-type|type ["|"])
  echo substr_count($text, 'is'); // 2
  array-of-type-expression = "(" type-expression ")[]"
  substr_count($str,"\n");
array-of-type            = type "[]"
type                    = class-name|keyword
class-name              = 1*CHAR
keyword                  = "string"|"integer"|"int"|"boolean"|"bool"|"float"
                            |"double"|"object"|"mixed"|"array"|"resource"|"scalar"
                            |"void"|"null"|"callable"|"false"|"true"|"self"


クラス名以外は全小文字。
===== BOMの判定 =====


基本は @<directive> <Type> <name> <description> の書式。スペース区切り。
* [https://www.fourier.jp/blog/php-read-csv-utf8-with-bom UTF-8BOM有無両対応のCSVファイル読み込み(PHP) | 株式会社フーリエ | Web戦略・システム開発[東京/浜松]]
*@property: クラスの注釈部で指定する。メンバー変数の説明。
* [https://qiita.com/tf_okrt/items/be81cd66e38fc38d3aeb php で csv を読み込む上での備忘録 #PHP - Qiita]
==PHPUnit==
* [https://stackoverflow.com/questions/26679980/csv-upload-parsing-with-splfileobject-remove-bom php - CSV upload - parsing with SplFileObject - Remove BOM - Stack Overflow]
PHPUnitを使用すれば自動単体テスト (Unit test) が可能だ。
*[https://web.gnusocial.jp/post/2023/07/07/7428/ 設置: PHPUnit | PHPの定番テストフレームワーク  |  GNU social JP Web]
===Version===
情報源: [https://phpunit.de/supported-versions.html Supported Versions of PHPUnit – The PHP Testing Framework]


PHPUnitのバージョンごとに対応しているPHPのバージョンが決まっている。
読み込んだファイルにUTF-8のBOMがあって、データ処理としてはBOMを除外したいことがある。


PHP v7.4に対応してい最後のバージョンはPHPUnit 9なので、当分はこれを使うのが良い。
いくつか方法がある。
===Basic===
$header[0] = preg_replace('/^\xEF\xBB\xBF/', <nowiki>''</nowiki>, $header[0]);
出典: [https://docs.phpunit.de/en/9.6/writing-tests-for-phpunit.html 2. Writing Tests for PHPUnit — PHPUnit 9.6 Manual]


基本的な使用方法を整理する。
if ($file->fread(3) !== pack('C*', 0xEF, 0xBB, 0xBF)) {
#基本的にはクラス単位で試験コードを記載。<Class>クラスの試験コードは<Class>Testの命名にする。
 
#<Class>Test はPHPUnit\Framwork\TestCaseを継承させる。
  $bom = pack('CCC', 0xEF, 0xBB, 0xBF);
#試験はpublicのtest*メソッドの命名にする。あるいは、@testのアノテーションを付ければ、命名規則に従わなくてもいい。
  $first = true;
#test*メソッド内で、assertSame() などで、期待値との比較で試験を行う。
  foreach ($file as $line) {
例:
    if ($first && substr($line, 0, 3) === $bom) {
<?php declare(strict_types=1);
        $line = substr($line, 3);
use PHPUnit\Framework\TestCase;
    }
final class StackTest extends TestCase
{
<nowiki> </nowiki>  private static $dbh;
<nowiki> </nowiki>  private $instance;
<nowiki> </nowiki> 
<nowiki> </nowiki>  public static function setUpBeforeClass(): void
<nowiki> </nowiki>  {
<nowiki> </nowiki>      // DB接続などクラス全体の初期化処理
  <nowiki> </nowiki>      self::$dbh = new PDO(<nowiki>''</nowiki>);
  <nowiki> </nowiki>  }
<nowiki> </nowiki>  public static function tearDownAfterClass(): void
<nowiki> </nowiki>  {
<nowiki> </nowiki>      self::$dbh = null;
  <nowiki> </nowiki>  }
<nowiki> </nowiki>  protected function setUp(): void
<nowiki> </nowiki>  {
<nowiki> </nowiki>    // 該当インスタンスの生成などメソッド単位の初期化処理。
<nowiki> </nowiki>    $instance = new Stack();
<nowiki> </nowiki>  }
<nowiki> </nowiki>  public function testPushAndPop(): void
<nowiki> </nowiki>  {
<nowiki> </nowiki>      $stack = [];
<nowiki> </nowiki>      $this->assertSame(0, count($stack));
   
   
<nowiki> </nowiki>      array_push($stack, 'foo');
    $first = false;
<nowiki> </nowiki>      $this->assertSame('foo', $stack[count($stack)-1]);
<nowiki> </nowiki>      $this->assertSame(1, count($stack));
   
   
<nowiki> </nowiki>      $this->assertSame('foo', array_pop($stack));
    // your lines don't have a BOM, do your stuff
<nowiki> </nowiki>      $this->assertSame(0, count($stack));
<nowiki> </nowiki>  }
  }
  }
====Depends====
最後の方法がよいと思う。
前回の試験で準備した結果を利用したい場合、@dependsのアノテーションでテスト関数を指定しておくと、指定したテスト関数のreturnを引数に受け継いだテスト関数を記述できる。
$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;
        }
こういう


@dependsは複数指定でき、指定順に事前に試験が実行されて引数に渡される。
====Array====
====Data Provider====
ある関数に対して、テストケースを用意して、複数の引数の組み合わせを試験したいことがよくある。こういうときのために、テスト関数に渡す引数を生成する関数のデータプロバイダーを指定できる。@dataProviderで関数を指定する。データプロバイダーは引数のリストを配列で返すようにする。


データ数が多い場合、名前付き配列にしておくと、どういうデータ項目で失敗したかがわかりやすい。
===== About =====
[https://www.php.net/manual/ja/language.types.array.php PHP: 配列 - Manual]


Iteratorオブジェクトを返してもいい。
PHPの配列は、順序マップ。
====Fixtures====
array(
出典: [https://docs.phpunit.de/en/9.6/fixtures.html 4. Fixtures — PHPUnit 9.6 Manual]。
    key  => value,
    key2 => value2,
    key3 => value3,
    ...
)
全て連想配列。キーはint|string。キーを省略したら、登場したキーの数+1の添え字のキーに自動で採番される。ただし、先頭は0。


テストメソッドの実行前に、テスト対象のインスタンスの生成や、DB接続など準備がいろいろある。これをFixturesと呼んでいる。この準備がけっこう手間になる。これを省力できるのがテストフレームワークの利点。
=====Create=====


テストメソッド実行前後に共通で行える処理がある。
====== Basic ======
*setUp/tearDown: テストメソッド単位の前後処理。テスト対象インスタンスの生成など。tearDownは何もしなくてもいいことが多い。
配列の作成方法がいくつかある。
*setUpBeforeClass/tearDownAfterClass: クラス単位の前後処理。 DB接続など。
*array()/[]
====XML Configuration File====
*explode ([https://www.php.net/manual/ja/function.explode.php PHP: explode - Manual])
出典:
*array_merge
*[https://docs.phpunit.de/en/9.6/organizing-tests.html 5. Organizing Tests — PHPUnit 9.6 Manual]
*array_map
*[https://docs.phpunit.de/en/9.6/configuration.html 3. The XML Configuration File — PHPUnit 9.6 Manual]
  $arr[キー] = 値;
基本はコマンドでテスト対象クラス・ファイルを指定してテストを実行する。他に、XMLの設定ファイル (phpunit.xml) でもテスト対象を指定できる。
$arr[] = 値;
 
// キーは文字列か整数。
testsディレクトリーの全*Test.phpを対象にする最小限の例は以下。
   
  <phpunit bootstrap="src/autoload.php">
  $arr = [
  <testsuites>
  'key1' => 'value1',
    <testsuite name="money">
  ];
      <directory>tests</directory>
    </testsuite>
  </testsuites>
  </phpunit>
以下のように--testsuiteで試験対象を指定して実行する。
  phpunit --bootstrap src/autoload.php --testsuite money
===Assertions===
Ref:
*[https://docs.phpunit.de/en/9.6/assertions.html 1. Assertions — PHPUnit 9.6 Manual].
*[https://docs.phpunit.de/en/9.6/writing-tests-for-phpunit.html?highlight=assertSame#testing-exceptions 2. Writing Tests for PHPUnit — PHPUnit 9.6 Manual]
基本はassertSameでテストすればいいのだが、それ以外にも例外とかいろいろ試験したいケースがあるので、メソッドを整理する。
====Exception====
特に例外の試験がイレギュラー。
  <?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
   
   
final class ExceptionTest extends TestCase
array()の他に[]も使える。[]のほうが短いのでこちらがいいだろう。
{
    public function testException(): void
    {
        $this->expectException(InvalidArgumentException::class);
        // Run test target code following.
    }
}
上記のようにexpectExceptionを使う。
*expectException:
*expectExceptionCode:
*expectExceptionMessage:
*expectExceptionMessageMatches:
例外が発生する処理の前に記述しておく。
====Output====
echoなど標準出力を試験する際も専用のメソッドがある。
*<code>void expectOutputRegex(string $regularExpression)</code>
*<code>void expectOutputString(string $expectedString)</code>
*<code>bool setOutputCallback(callable $callback)</code>
*<code>string getActualOutput()</code>
expectExceptionと同様に事前にセットしておく。
===Command-Line===
Ref: [https://docs.phpunit.de/en/9.6/textui.html 3. The Command-Line Test Runner — PHPUnit 9.6 Manual].


phpunitコマンドでいろいろできる。いくつか重要なオプション、使用方法がある。
$arrが存在しないか、null/falseの場合、新しい配列を作成する。ただし、この方法は万が一既存の変数があったら、追加になるのであまり推奨されない。明示的に初期化したほうがいい。
*phpunit file.php: 指定したファイルのテストを実行。
*--testsuite <name>: テストを指定。
===Test Doubles===
Ref: [https://docs.phpunit.de/en/9.6/test-doubles.html 8. Test Doubles — PHPUnit 9.6 Manual].


テスト時に、依存関係を模擬したもので置換したいことがある。PHPUnitにそういう仕組が用意されている。
2行余分に増えるが、上記の形式が初期化もできるのでいいだろう。
explode(',', '物件コード,オーナーコード,棟数,M数,実戸数,a,b,c')
['物件コード','オーナーコード','棟数','M数','実戸数','a','b','c']
explode(',', '物件コード,オーナーコード,棟数,M数,実戸数)
['物件コード', 'オーナーコード', '棟数', 'M数', '実戸数']
explodeで配列を作ると短いのは、要素数8以上。詰めずに書いたら5以上。


stub=親、mock=子。
ただ、余計な関数呼び出しが発生するから、あまりしないほうがいいかも。


メソッド内で他のクラス・メソッドを使う場合はmockで対象クラスを模擬させる。<syntaxhighlight lang="php">
====== Serial ======
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;


final class SubjectTest extends TestCase
* [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]
    public function testObserversAreUpdated(): void
* [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]
        // Create a mock for the Observer class,
        // only mock the update() method.
        $observer = $this->createMock(Observer::class);


        // Set up the expectation for the update() method
同じ値の複数要素、連番データの作成方法がある。
        // to be called only once and with the string 'something'
array_fill(int $start_index, int $count, mixed $value): array
        // as its parameter.
        $observer->expects($this->once())
                ->method('update')
                ->with($this->equalTo('something'));


        // Create a Subject object and attach the mocked
$a = array_fill(5, 6, 'banana');
        // Observer object to it.
print_r($a);
        $subject = new Subject('My subject');
        $subject->attach($observer);


        // Call the doSomething() method on the $subject object
Array
        // which we expect to call the mocked Observer object's
(
        // update() method with the string 'something'.
    [5]  => banana
        $subject->doSomething();
    [6]  => banana
    }
    [7]  => banana
}
    [8]  => banana
    [9]  => banana
    [10] => banana
)


</syntaxhighlight>基本的な作り。
array_fill_keys(array $keys, mixed $value): array
#createMock(<class>::class)で該当クラスのモックを作成。
#expectsに呼出回数条件のオブジェクトをセット。
#methodで対象メソッドを指定。
#withで、該当メソッドの引数処理を指定。
デフォルトで模擬実装はnullを返す。戻り値を変更したければ、will($this->returnValue())などを指定する。よく使うので短縮記法もある。
{| class="wikitable"
|+Table 8.1 Stubbing short hands
!short hand
!longer syntax
|-
|<code>willReturn($value)</code>
|<code>will($this->returnValue($value))</code>
|-
|<code>willReturnArgument($argumentIndex)</code>
|<code>will($this->returnArgument($argumentIndex))</code>
|-
|<code>willReturnCallback($callback)</code>
|<code>will($this->returnCallback($callback))</code>
|-
|<code>willReturnMap($valueMap)</code>
|<code>will($this->returnValueMap($valueMap))</code>
|-
|<code>willReturnOnConsecutiveCalls($value1, $value2)</code>
|<code>will($this->onConsecutiveCalls($value1, $value2))</code>
|-
|<code>willReturnSelf()</code>
|<code>will($this->returnSelf())</code>
|-
|<code>willThrowException($exception)</code>
|<code>will($this->throwException($exception))</code>
|}willReturnCallbackで呼び出し関数をまるごと別のものに置換できる。これが非常に便利。
===Topic===
====Test private/protected====
Ref:
*[https://stackoverflow.com/questions/249664/best-practices-to-test-protected-methods-with-phpunit php - Best practices to test protected methods with PHPUnit - Stack Overflow]
*[https://zenn.dev/ttskch/articles/c7dcd5c1188cdd PHPUnitでprivateメソッドをテストする]
*[https://qiita.com/ponsuke0531/items/6dc6fc34fff1e9b37901 privateとprotectedメソッドをPHPUnitでテストする方法 #PHP - Qiita]
クラスのprivate/protectedメソッドのテストには工夫が必要となる。<syntaxhighlight lang="php">
    /**
    * privateメソッドを実行する.
    * @param string $methodName privateメソッドの名前
    * @param array $param privateメソッドに渡す引数
    * @return mixed 実行結果
    * @throws \ReflectionException 引数のクラスがない場合に発生.
    */
    private function doMethod(string $methodName, array $param)
    {
        // テスト対象のクラスをnewする.
        $controller = $this->instance;
        // ReflectionClassをテスト対象のクラスをもとに作る.
        $reflection = new \ReflectionClass($controller);
        // メソッドを取得する.
        $method = $reflection->getMethod($methodName);
        // アクセス許可をする.
        $method->setAccessible(true);
        // メソッドを実行して返却値をそのまま返す.
        return $method->invokeArgs($controller, $param);
    }
</syntaxhighlight>ReflectionClassを使って取得できる。上記の関数のFormController部分を試験対象のクラスに差し替えればOK。$this->instanceを指定しておけばそのまま流用できるか。getProperty/getValueでprivateプロパティーも取得可能。
====Test header====
Ref: [https://stackoverflow.com/questions/9745080/test-php-headers-with-phpunit unit testing - Test PHP headers with PHPUnit - Stack Overflow].


header関数を使用する場合、phpunitの標準出力と干渉して以下のエラーが出て試験できない。
$keys = array('foo', 5, 10, 'bar');
  Cannot modify header information - headers already sent by (output started at .../vendor/phpunit/phpunit/src/Util/Printer.php:138)
  $a = array_fill_keys($keys, 'banana');
回避方法が2種類ある。
array_fill_keys(['a', 'b'], 'ab');
#<code>@runInSeparateProcess</code>
print_r($a);
#phpunit --stderr
1個目のアノテーションをテストメソッドに指定すると別プロセスでの実行になる。ただ、プロセス生成は時間がかかるため、試験が多いと効率が悪い。


2個目のphpunitの出力を標準エラーに出力させる方法がシンプルで効率もいい。phpunit.xmlに stderr="true"を指定するとキー入力を省略できる。こちらで対応しよう。
Array
====Test exit====
(
Ref:
    [foo] => banana
*[https://qiita.com/kumagaias/items/5b1d95a897bae11f2a5a PHP でテストコードを意識したコーディング #PHPUnit - Qiita]
    [5] => banana
*[https://qiita.com/tenkoma/items/1ac9625b4233c5893812 echo + exit しているPHPコードをユニットテストで保護しながら改善する #PHP - Qiita]
    [10] => banana
*[https://uzulla.hateblo.jp/entry/2019/06/27/193210 header後にdieするテストのアンチパターン - uzullaがブログ]
    [bar] => banana
*[https://stackoverflow.com/questions/23915434/ignore-exit-and-die-with-phpunit php - Ignore exit() and die() with PHPUnit - Stack Overflow]
)
*[https://stackoverflow.com/questions/1347794/how-do-you-use-phpunit-to-test-a-function-if-that-function-is-supposed-to-kill-p unit testing - How do you use PHPUnit to test a function if that function is supposed to kill PHP? - Stack Overflow]
header()後のexit()など、exit/dieを使用するコードがある。phpunit内でこれらがあると、テストも強制終了になる。


上記の別プロセスで実行していた場合、以下のエラーになる。
  range(0, 12)
  Test was run in child process and ended unexpectedly
対処方法がいくつかある。
#exitを使わないコードに変更。
#isTestのようなフラグを元コードに入れてテスト可否で分岐してexitを回避。
#execで外部プロセスで実行してexitCodeを試験。
#exit/die部分だけ別関数に抽出してmockで置換?
<https://notabug.org/gnusocialjp/gnusocial/src/main/actions/apiaccountregister.php> のclientErrorが内部でexitする。


このclientErrorをwillなどで置換すればよさそう?
explode(',', str_repeat(",", 10));
==Language Reference==
連続データを作成出来たら、array_combine/array_keys/array_valuesなどの組み合わせで、キーと値は調整できる。
===Types===
====Introduction====
Ref: [https://www.php.net/manual/en/language.types.intro.php PHP: Introduction - Manual].


PHPの変数は以下の型のいずれかの値となる。
* range: 指定要素数配列
*null
* array_fill/array_fill_keys: 指定値の指定要素数配列。連想配列で複数キーに同じ値を設定したい場合に使う。
*bool
*int
*float (floating-point number)
*string
*array
*object
*callable
*resource
C言語のようなlong/doubleのような精度ごとの型はない。


==== System ====
====== Merge ======
[https://www.php.net/manual/ja/language.types.type-system.php PHP: 型システム - Manual]
配列の追加、結合。
$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。重宝する。


組込型の他に、ユーザー定義の型、aliasなどいくつかの型がある。
+演算子の結合は注意が必要。同じキーだと追加されない。基本はarray_merge。


===== 基本型 =====
配列ではなく、配列要素の結合は以下が使える。
言語に統合されていて、ユーザー定義で再現不能。
implode($arr);
array_reduce($arr, function($c, $v){return $c.$v;});
単に文字列結合するならimplodeがシンプル。


* 組込
指定した要素を全部の行に追加する場合。きれいな方法はない。
** null
** スカラー型: bool/int/float/string
** array
** object
** resource
** never
** void
** クラス内の相対型: self/parent/static
* Value型: false/true
* ユーザー定義型/クラス型
** インターフェイス
** クラス
** 列挙型
* callable


===== 複合型 =====
# foreach
複数の基本型を組み合わせた型。交差型とunion型で作れる。
# array_map


* 交差型: 宣言した複数のクラス型をすべて満たす型。&で表現。T/U/Vの交差型はT&U&Vと書く。
foreach ($array as &$row) {
* union型: 複数の型を受け入れる型。|で表現。T/U/Vのunion型はT|U|Vと書く。交差型を含む場合、T|(X&U)と丸括弧で囲む必要がある。
    $row[] = $newElement; // 各行の末尾に要素を追加
}


===== alias =====
$array = array_map(function($row) use ($newElement) {
PHPはmixedとiterableの2個の型のエイリアスに対応している。
    $row[] = $newElement; // 各行の末尾に要素を追加
    return $row;
}, $array);
 
 
 
===== Read =====


* mixed=object|resource|array|string|float|int|bool|null: PHP 8.0.0で導入。mixedは型のトップ。他の全部の型はこの型の部分になる。
====== 角括弧構文による配列要素へのアクセス ======
* iterable=Traversable|array: PHP 7.1.0で導入。foreachで反復可能でジェネレーター内でyield from可能。
配列要素へのアクセスはarray[key]構文を使う。


ただし、ユーザー定義のエイリアスは未対応。
[]を角括弧構文 (square bracket syntax) と呼んでいる ([https://www.php.net/manual/en/language.types.array.php PHP: Arrays - Manual])。


====Boolean====
なお、これとは別でstring access operator (文字列アクセス演算子)、インデックス演算子と呼ぶこともあるが、演算子ではないので「[https://www.php.net/manual/en/language.operators.php PHP: Operators - Manual]」に記載はない。
*[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になるということを把握しておく。
未定義キーへのアクセスは、未定義変数へのアクセスと同じ扱い。つまり、E_WARNING レベルの警告 (PHP 8.0.0 より前のバージョンでは E_NOTICE) が発生し、結果が null。
*booleanのfalse
*intの0
*floatの0.0
*stringの空文字列、"0"
*要素数0個のarray
*null (未初期化変数含む)
stringの"0"と要素0のarrayがfalseになる点が重要。注意する。要素0のarrayは包含判定、検索などでよく使う。


stringの0ははまりどころ。stringは何がくるかわからないなら、strlenで文字数を見たほうが確実。
https://chatgpt.com/c/67a405d2-7614-800b-9f41-811812718dac
====Strings====
Ref: [https://www.php.net/manual/ja/language.types.string.php PHP: 文字列 - Manual].


非常に重要。
なお、配列以外の型に角括弧構文を使った文字列キーアクセスの挙動には注意が必要基本はFatal error (isset/emptyでのガードも不能) になる。
=====Literal=====
文字列リテラルとしては4の表現がある。
*Single quote: <nowiki>''</nowiki> 変数展開されない。
*Double quote: "" 変数展開される。
*Here document:  <<<EOT 二重引用符扱いで変数展開される。
*Nowdoc: <<<'EOT' 一重引用符扱いで変数展開されない。
引用符内で引用符'を使う場合はバックスラッシュ\でエスケープが必要。バックスラッシュ自体の指定は二重\\。


Here document/Nowdocは終端IDのインデントで行頭を識別しており、インデントに意味があるので注意する。
* null: Warning
echo <<<END
* object: Fatal error
      a
* int|float: Warning
      b
* string: PHP 7.3以下=OK、PHP 7.4以上=Warning、PHP 8.0以上=Fatal error
    c
 
\n
stringはキーではなくて、数値アクセスなら許容される。PHP 7.1から負のインデックスも可能。
END;


echo <<<'EOT'
====== 末尾要素 ======
My name is "$name". I am printing some $foo->foo.
[https://hishikiryu.com/php-get-last-array-value/ 【PHP】配列の最後(末尾)の要素を取得まとめ array_key_last, count, end関数 | ヒシキリュウ.com]
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41
EOT;


=====Parse=====
* array_key_last: PHP v7.3.0+ ($arr[array_key_last($arr)];)。
二重引用符とヒアドキュメントではエスケープシーケンスが解釈され、変数が展開される。<syntaxhighlight lang="php">
* count: 昔ながら ($arr[count($arr) - 1];)。
<?php
* end: 非推奨。
$juice = "apple";


echo "He drank some $juice juice." . PHP_EOL;
====== 指定要素の取得 ======
[https://gen0e0.hatenablog.com/entry/2018/03/16/164714 PHPの連想配列から一部を切り出す話 - あしたにっき]


// 意図しない動作をします。"s" は、変数名として有効な文字です。よって、変数は $juices を参照しています。$juice ではありません。
* []:
echo "He drank some juice made of $juices." . PHP_EOL;
* array_slice: 範囲取得。
* array_intersect/array_intersect_key: 指定したキーの配列だけ取得。
* array_diff/array_diff_key: 指定したキー以外の配列を取得。
* array_filter: 複雑な場合。


// 参照する変数名を波括弧で囲むことで、変数名の終端を明示的に指定しています。
連想配列で指定キー/指定キー以外の一括取得でよく使う。
echo "He drank some juice made of {$juice}s.";
$needles = ['t1', 't2'];
$haystack = ['t1' => 1, 't2' => 2];
array_intersect_key($haystack, array_flip($needles));
array_diff_key($haystack, array_flip($needles));
array_sliceは添え字がなかったら空配列を返してくれるので、添え字アクセスより安全。


//
====== 連想配列の先頭・末尾 ======
$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
* [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 で配列の先頭要素の値を取得するきれいな方法を考える | バシャログ。]


// これが動作しない理由は、文字列の外で $foo[bar]
PHP 7.3からarray_key_firstがある。これを使う。7.3以前はreset。
// が動作しない理由と同じです。
// PHP はまず最初に foo という名前の定数を探し、
// 見つからない場合はエラーをスローします。
// 定数が見つかった場合は、その値('foo' そのものではない)
// を配列のインデックスとして使います。
echo "This is wrong: {$arr[foo][3]}";


// 動作します。多次元配列を使用する際は、
他に、元配列を破壊していいなら、array_shift/array_popもある。
// 文字列の中では必ず配列を波括弧で囲むようにします。
echo "This works: {$arr['foo'][3]}";


// 複雑な例。二重展開で変数になる場合だけ式が使える模様。
array_sliceで部分配列を取得して変数に格納して、array_shiftもある。
$var1=9;
$t = ['a' => 0, 'b' => 1];
echo "{${mb_strtolower('VAR1')}}"; // 9
$t2 = array_slice($t, 0, 1);
var_export(array_shift($t2));
他にきれいなのはarray_keys/array_values[0]。これがいい。


?>
====== 抽出 ======
</syntaxhighlight>波括弧はなくてもいいが、文字列が連結するなどして変数名の終端を区別できない場合に必須になる。


特に重要な挙動は以下。
* [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は連想配列に使うと、キーが番号になる。


<nowiki>さらに複雑なことができる。{${}}を指定すると、内側の波括弧内で、${}部分が変数評価になる場合にだけ式を指定できる。動きがトリッキーすぎる。フォーマット文字列的なことには使えない。バグのもとになりそうなので使用を控えたほうがよさそう。</nowiki>
取得後unsetするのが無難。
=====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は置換対象が可変長引数ではなく配列なだけ。
====== array_column ======
=====sprintf=====
[https://www.php.net/manual/ja/function.array-column.php PHP: array_column - Manual]
Ref: [https://www.php.net/manual/ja/function.sprintf.php PHP: sprintf - Manual]


今後何度も使う。
テーブルの取得結果の整形に非常に便利。
array_column(array $array, int|string|null $column_key, int|string|null $index_key = null): array


C言語のprintfといろいろ違うところがある。<syntaxhighlight lang="php">
* column_key: 抽出したいカラム。nullにすると全部の列。index_keyを指定しなかったら元の配列と同じ。
<?php
* index_key: 取得後の配列のキーにしたいカラム。
$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
index_keyを指定しなければ、column_keyの単純配列。
echo sprintf("%'.09d\n", 123); // 000000123


?>
=====Remove=====
</syntaxhighlight>特徴的なのが`%数$指定子`で引数の番号を選べるところ。Pythonの`{数:指定子}`に似ている。
配列要素の削除方法がいくつかある。
*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
*substr/mb_substr
    [1] => 2
*strpos/mb_strpos/strrpos/mb_strrpos ([https://www.php.net/manual/ja/function.strpos.php PHP: strpos - Manual])
)
*split
途中で削除しても、foreachは詰めたりしない。
*preg_match ([https://www.php.net/manual/ja/function.preg-match.php PHP: preg_match - Manual])
 
preg_matchの自由度が高い。速度を気にしなくていいならこれでいいと思われる。ただ、引数の配列に入ってくるのがいまいち。関数の戻り値でほしい。
===== 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]
連想配列のキーの置換、キーの更新、キー名の置換、キー名の更新をしたいことがある。


strposとsubstrを組み合わせると端の文字列を切り出せる。
いくつか方法がある。
=====文字列置換=====
*str_replace
*substr_replace: 日本語不能。
*preg_replace
*explode/implode
substr_replace($text, <nowiki>''</nowiki>, -1);
substr_replace($text, '.', nb_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=====
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未満なら以下のようなコード。
サブ配列の場合はarray_mapでやればいい。
  function startsWith( $haystack, $needle ) {
  $tags = array_map(function($tag) {
      $length = strlen( $needle );
    return array(
      return substr( $haystack, 0, $length ) === $needle;
        'name' => $tag['name'],
        'value' => $tag['url']
    );
}, $tags);
シンプルな方法は配列で設定してunset
foreach($tags as &$val){
    $val['value'] = $val['url'];
    unset($val['url']);
  }
  }
  function endsWith( $haystack, $needle ) {
他にはjsonを経由したり。array_keys/array_combineを使ったり。
     $length = strlen( $needle );
 
    if( !$length ) {
=====Copy=====
        return true;
[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]
    }
 
    return substr( $haystack, -$length ) === $needle;
配列変数を代入すると通常はそれでコピーになる。ただし、配列にオブジェクトがあると、そのオブジェクトはシャローコピーになる。
  $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は連想配列';
  }
  }
mb_strlen/mb_substrでマルチバイト対応。
これで添え字が、数字かどうかをみるのがいい模様。
=====Convert=====


===== 改行分割 =====
====== 2次元配列→1次元配列 ======


* [https://stackoverflow.com/questions/3997336/explode-php-string-by-new-line Explode PHP string by new line - Stack Overflow]
* [https://zenn.dev/akido_/articles/833232e489137f PHPで二次元配列を一次元配列に変換する方法]
* [https://stackoverflow.com/questions/1483497/split-string-by-new-line-characters php - Split string by new line characters - Stack Overflow]
* [https://www.php.net/manual/ja/function.array-column.php PHP: array_column - Manual]
* [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]
* [https://qiita.com/harukasan/items/a0773aef27d838852e44 PHPのarray_columnが便利 #PHP - Qiita]


<code>explode('\n', $csv)</code> のようなことをしたくなるが、改行が\nとは限らない。
$array = [
  $array = preg_split('/\R/u', $string);
    [1, 2, 3],
上記がいい。\Rが\r \n \n\rなどにマッチ。uで入力がUTF-8の場合を考慮。例えば、「腰」がuをつけないと分割されてしまう。
    [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 )


===== trim =====
$array = [
[https://www.php.net/manual/ja/function.trim.php PHP: trim - Manual]
    [
        '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' => '你好,世界!',
//  ),
// )


====Array====
            $map = [];
=====Create=====
            foreach ($table as $row) {
配列の作成方法がいくつかある。
                $map[$row['括りオーナーコード']] = $row['オーナーコード'];
*array()/[]
            }
*explode ([https://www.php.net/manual/ja/function.explode.php PHP: explode - Manual])
DBテーブルからの取得結果が2次元の連想配列になっている。ここから、IDをキーにして、特定の値を取得するmapを作ったり、レコード行を取得できる。


===== Read =====
自前でfor文で数行のコードでできるが、関数だと楽。


====== 末尾要素 ======
====== 多次元連想配列→一次元連想配列 ======
[https://hishikiryu.com/php-get-last-array-value/ 【PHP】配列の最後(末尾)の要素を取得まとめ array_key_last, count, end関数 | ヒシキリュウ.com]
https://chatgpt.com/c/673fd301-45c4-800b-bec8-02302ad01383


* array_key_last: PHP v7.3.0+ ($arr[array_key_last($arr)];)
再帰関数で処理する。
* count: 昔ながら ($arr[count($arr) - 1];)。
function flattenArray(array $array, string $prefix = <nowiki>''</nowiki>): array {
* end: 非推奨。
<nowiki> </nowiki>  $result = [];
 
<nowiki> </nowiki>  foreach ($array as $key => $value) {
=====Append=====
<nowiki> </nowiki>      $newKey = $prefix === <nowiki>''</nowiki> ? $key : $prefix . '.' . $key;
配列要素の追加。
<nowiki> </nowiki>      if (is_array($value)) {
  $arr[キー] = 値;
<nowiki> </nowiki>          // 再帰的に呼び出して配列をフラットにする
  $arr[] = 値;
<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'
  ];
   
   
  array_push($arr, 'a', 'b'); // array_pushだと一度に複数追加できる。
  $flattenedArray = flattenArray($nestedArray);
array_unshift($arr, 'a', 'b'); // 先頭に追加。
array_merge($arr, [0, 1]); // 配列同士の追加。
$arr = [...$arr, ...[0, 1]] // PHP7.4以上。...演算子。性能はarray_mergeのほうが高い。
基本は$arr[キー] $arr[]でいいだろう。
=====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);
  print_r($flattenedArray);


  012Array
  Array
  (
  (
     [0] => 1
     [user.name] => Alice
     [1] => 2
     [user.details.age] => 25
    [user.details.address.city] => New York
    [user.details.address.zip] => 10001
    [status] => active
  )
  )
途中で削除しても、foreachは詰めたりしない。
連想配列なのでarray_mergeではなく+=でOK。
=====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();
associative arrayをsimple 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=====
======連想配列→単純配列======
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]] 形式。
後者のパターンはそれなりに使う気がする。
後者のパターンはそれなりに使う気がする。
======単純配列→連想配列======
[https://www.techiedelight.com/ja/convert-regular-array-to-associative-array-php/ PHP で通常の配列を連想配列に変換する]


いくつか方法がある。
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番目の方法がシンプル。
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。
基本は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を回避しながら手短にかける。
======空確認======
[https://qiita.com/miriwo/items/c4760cbb2807ee84ef2d PHP 配列が空かどうかを判定する #初心者 - Qiita]


emptyで確認できる。が、単にnullなどの場合も判定してしまう。null or emptyという意味ならemptyでもOK。
変数が配列か保証できていない場合、キーの確認と同時に行う場合、以下のいずれかになる。
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を使う。不要データの削除などでよく使いそう。


逆に、issetであることと、nullではないことを確認できる。
======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_flip======
[https://www.php.net/manual/ja/function.array-flip.php PHP: array_flip - Manual]


配列のキーと値を反転した配列を返す。元のarrayの値は有効なキーを必要とする。つまり、intかstring。型が違う場合、警告が出て無視される。
いや、そういうことをしなくても、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言語と同じで (型) を前置する。ただし、少々長い。


文字列への変換は二重引用符囲などもOK。まあ、キャストだけ覚えておくのがシンプル。
文字列への変換は二重引用符囲、数値への変換は算術演算子 (+)。まあ、キャストだけ覚えておくのがシンプル。
 
===== 型キャスト =====
変換先の型を波括弧で囲んで、変換対象の変数に前置することで変換する。
 
使用可能なキャストは以下。
 
* <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だろうか。
動的に変更したいなら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]。array_mergeの代替記法。
!例
!名前
!結果
|-
|$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のほうが速くてメモリーも少ないとのこと。
===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:に注意。


この構文は存在だけ知っておくだけでいいと思われる。
==== in演算子 ====
====elseif/else if====
[https://stackoverflow.com/questions/33182976/php-equivalent-of-mysql-in-operator php equivalent of mysql "IN" operator? - Stack Overflow]
[https://www.php.net/manual/ja/control-structures.elseif.php PHP: elseif/else if - Manual]


1単語で書ける。結果は同じだが、文法的な意味が異なる。
PHPにin演算子はない。代わりに、in_arrayで包含判定できる。
====for/foreach====
foreachは配列の反復処理のための制御構造。
foreach(iterable_expression as $value)
foreach(iterable_expression as $key => $value)
$keyも使いたい場合、2番目の形式を使う。


ループ中に$valueの要素を直接変更したい場合、&をつけておく。
ある値が、いずれかのどれかであるかの判定はそれなりにある。
  foreach(iterable_expression as &$value)
  in_array($target, [], true);
====declare====
例えば、この比較対象が長い場合、(a===b||a===c|a===d) で何回も書かなくて済む。
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]) などで冒頭に以下の記述がある。
==== Name ====
<?php declare(strict_types=1);
誰かに言葉で説明する際に、演算子や構文の名前がほしい。意外と覚えていない。根拠とともに整理する。
これの意味が分かっていなかったので整理する。


declare文 (construct) は、コードブロックの実行指令となる。以下の構文となる。
https://chatgpt.com/c/6743ea1b-4a14-800b-b50b-272dd4dbcde0
declare (<directive>)
{| class="wikitable"
  <statement>
|+
<directive> はdeclareブロックの挙動を指示する。指定可能なものは以下3個だ。
!演算子
#ticks
!名前
#encoding
!name
#strict_types: =1の指定でPHPの暗黙の型変換を無効にする (ストリクトモード)。ただし、影響するのはスカラー型のみ。型が違う場合、TypeErrorの例外が発生する。
!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]


declareブロックの <statement> は、<directive> の影響を受ける実行部だ。
if、 while、for、 foreach、switch に関する別の構文がある。開き波括弧部分を:に、閉じ波括弧部分をendif;,endwhile;, endfor;,endforeach;, endswitch;などにできる。else:とelseif:に注意。


declare文はグローバルスコープで使われる。登場以後のコードに影響する。ただし、他のファイルからincludeされても、親ファイルには影響しない。だから安心して使える。
この構文は存在だけ知っておくだけでいいと思われる。
====elseif/else if====
[https://www.php.net/manual/ja/control-structures.elseif.php PHP: elseif/else if - Manual]


型安全にするために、基本的にPHPファイルの冒頭に<code>declare(strict_types=1);</code>を書いておいたほうがよいだろう。
1単語で書ける。結果は同じだが、文法的な意味が異なる。
====foreach====


==== return ====
===== About =====
[https://www.php.net/manual/ja/function.return.php PHP: return - Manual]
[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番目の形式を使う。


returnで引数を省略すると、戻り値はnullになる。
ループ中に$valueの要素を直接変更したい場合、&をつけておく。
foreach (iterable_expression as &$value)


呼び出し方法、場所で挙動が変わる。
===== Name =====
foreachで使う変数の命名。
foreach (table as $row => $line)
DBからSELECT結果などがkeyに行番号、valueにレコードが入ってくる。こういう場合、rowがややこしい。行番号の意味でrowをキーにしておくといい。


* 関数/eval内: 即座に関数を終了し、引数を関数の値として返却。
value部分をどうするかだが、valueやitemだと少々わかりにくい。recordやline。SplFileObjectを扱うこともあるからlineがいいと思う。
* グローバルスコープ: スクリプト自体を終了。
* include/require内: 呼び出し元のファイルに制御を戻す。includeの場合、引数はincludeの戻り値になる。


return文は関数ではないので、引数の括弧は不要。紛らわしいのでないほうがいい。
===== Rewind =====


include内で使えるというのがみそ。config.phpでreturnだけした設定一覧を記述しておいて、includeで変数に取り込むというのをよくやる。
* [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]


==== require/include/require_once/include_once ====
注意の必要な挙動として、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をオーバーライドして巻き戻さないので維持できる。


===== Basic =====
なお、next()はREAD_AHEADありにしていないと機能しないようなので注意する ([https://stackoverflow.com/questions/1504927/splfileobject-next-behavior php - SPLFileObject next() behavior - Stack Overflow])。
includeは指定したファイルを読み込み評価する。絶対パスで指定しない場合、include_pathの設定を利用する。include_pathにもなければ現在ディレクトリーも探す。


絶対パス、相対パスの前置があると、include_pathは無視する。
ただ、READ_AHEADにしても、初回がnext()2回呼ばないと2行目にcurrent()でならないので動きがわかりにくい。seek(1)でよい。


ファイルが読み込まれると、ファイル内のコードは、includeが実行された行の変数スコープを継承する。つまり、呼び出し行で利用可能な全変数がファイル内でも使用可能。ファイル内で定義された関数やクラスはすべて、グローバルスコープになる。ただし、includeが関数定義内に配置されたら、コードは関数内で定義されているとみなす。
===== 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]


ファイルの読込時にはHTMLモードになる。そのため、ファイル内でPHPコードを実行するなら、<?php ?>で囲む必要がある。
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でいい。


includeに失敗したらFALSEを返し、E_WARNINGを発生させる。成功したら、戻り値は1。ただし、ファイル内でreturnを実行したら、その値を返す。
配列だったら、反復外部で簡単に判定できる。余計な処理を反復内に含めないほうがいい。


includeは特別な言語構造のため、引数に括弧は不要。結果を評価したいならば、全体を括弧で囲む。
===== 反復削除 =====
// 動作します。
if ((include 'vars.php') == TRUE) {
    echo 'OK';
}


===== require/include =====
* [https://iww.hateblo.jp/entry/20220603/array PHPで、foreachでぐるぐる回ってる最中に要素を削除する - 揮発性のメモ2]
requireはincludeとほぼ同じ。違いは、失敗時にE_COMPILE_ERRORが発生して処理を中断する点。includeはE_WARNINGで処理は継続する。
* [https://www.techiedelight.com/ja/remove-array-element-in-foreach-loop-php/ PHP の foreach ループで配列要素を削除する]


使い分けとして、変数読込などで読み込めなくても処理を進めて問題ない場合に、include。
キーが維持されるので、逆順反復などしなくても、影響ない。unsetすればいい。


関数定義など、絶対必要なものはrequireなど。
===== reverse =====
逆順反復の方法がいくつかある。


===== _once =====
[https://stackoverflow.com/questions/10777597/reverse-order-of-foreach-list-items php - Reverse order of foreach list items - Stack Overflow]
読込済みなら、再読込しない点がinclude/requireとの決定的な違い。関数の複数定義のエラーを回避できたりする。
$fruits = ['bananas', 'apples', 'pears'];
for($i = count($fruits)-1; $i >= 0; $i--) {
    echo $fruits[$i] . '<nowiki><br></nowiki>';
}


読み込めたらtrueを返す。
foreach ( array_reverse($accounts) as $account ) {
  echo sprintf("<nowiki><li>%s</li></nowiki>", $account);
}
なお、連想配列は無理。やるとしたら、array_reverse、逆順のキーを取得してそれを使う。


===== config.php =====
[https://stackoverflow.com/questions/32613036/how-to-reverse-foreach-key-value-php arrays - How to reverse foreach $key value PHP? - Stack Overflow]


* [https://stackoverflow.com/questions/14752470/creating-a-config-file-in-php Creating a config file in PHP - Stack Overflow]
====declare====
* [https://blog.websandbag.com/entry/2018/12/04/160218 【PHP】静的なconfigファイルの書き方 - websandbag ブログ]
Source: [https://www.php.net/manual/en/control-structures.declare.php PHP: declare - Manual].


includeとreturnの組み合わせのconfig.phpの設定ファイルをいろんなアプリで使われている。
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);
return [
これの意味が分かっていなかったので整理する。
    'name' => 'hoge',
    'value' => 'fuga',
];
?>


  <?php
declare文 (construct) は、コードブロックの実行指令となる。以下の構文となる。
  declare (<directive>)
// configファイルを変数に代入
  <statement>
$config = include __DIR__ . '/config.php';
<directive> はdeclareブロックの挙動を指示する。指定可能なものは以下3個だ。
#ticks
// 呼び出し。
#encoding
var_dump($config['name']);
#strict_types: =1の指定でPHPの暗黙の型変換を無効にする (ストリクトモード)。ただし、影響するのはスカラー型のみ。型が違う場合、TypeErrorの例外が発生する。
指令はファイルコンパイル時に処理されるので、リテラル値のみが使用可能で、変数や定数は使用不能。
?>
こういう形式。このreturnだけの文は、ほぼinclude前提。


編集対象のアプリの設定を、既存コードと分離する際に、いい方法。
declareブロックの <statement> は、<directive> の影響を受ける実行部だ。


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を使うとよい。
declare文はグローバルスコープで使われる。登場以後のコードに影響する。ただし、他のファイルからincludeされても、親ファイルには影響しない。だから安心して使える。
// create the array as a php text string
$text = "<?php\n\nreturn " . var_export($myarray, true) . ";";


===== config class =====
型安全にするために、基本的にPHPファイルの冒頭に<code>declare(strict_types=1);</code>を書いておいたほうがよいだろう。
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]
==== return ====
* [https://docs.php.earth/security/configuration/ Configuration in PHP applications | PHP.earth]
[https://www.php.net/manual/ja/function.return.php PHP: return - Manual]
* [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定数にするという。
returnで引数を省略すると、戻り値はnullになる。


* クラスのconst定数
呼び出し方法、場所で挙動が変わる。
* iniファイル/parse_ini_file


他に、configクラスを用意しておいて、シングルトンか、staticメソッドで参照する形。
* 関数/eval内: 即座に関数を終了し、引数を関数の値として返却。
* グローバルスコープ: スクリプト自体を終了。
* include/require内: 呼び出し元のファイルに制御を戻す。includeの場合、引数はincludeの戻り値になる。


どれくらいの頻度で参照するか次第。参照頻度が低いなら、getで毎回設定ファイルを読み込む。多いならstaticのクラス変数にもたせる。
return文は関数ではないので、引数の括弧は不要。紛らわしいのでないほうがいい。
===Function===
=====Return value=====
Ref: [https://www.php.net/manual/ja/functions.returning-values.php PHP: 戻り値 - Manual].


関数はreturn文で値を返せる。そこで処理を終了する。
include内で使えるというのがみそ。config.phpでreturnだけした設定一覧を記述しておいて、includeで変数に取り込むというのをよくやる。


returnを省略した場合、nullを返す。
==== require/include/require_once/include_once ====


==== Variable Functions/可変関数/Callable/コールバック ====
===== Basic =====
includeは指定したファイルを読み込み評価する。絶対パスで指定しない場合、include_pathの設定を利用する。include_pathにもなければ現在ディレクトリーも探す。


* [https://www.php.net/manual/ja/functions.variable-functions.php PHP: 可変関数 - Manual]
絶対パス、相対パスの前置があると、include_pathは無視する。
* [https://www.php.net/manual/ja/language.types.callable.php PHP: コールバック / Callable - Manual]
 
* [https://www.ycomps.co.jp/staffblog/11934 【PHP】コールバック関数サンプル3つをまとめる - ウェブ集客で企業を成功に導くホームページ制作会社|(株)ワイコム・パブリッシングシステムズ(福岡)]
ファイルが読み込まれると、ファイル内のコードは、includeが実行された行の変数スコープを継承する。つまり、呼び出し行で利用可能な全変数がファイル内でも使用可能。ファイル内で定義された関数やクラスはすべて、グローバルスコープになる。ただし、includeが関数定義内に配置されたら、コードは関数内で定義されているとみなす。
* [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を使う必要はない。
ファイルの読込時にはHTMLモードになる。そのため、ファイル内でPHPコードを実行するなら、<?php ?>で囲む必要がある。


可変関数は、関数を文字列で実行する仕組み。これとは別で、Callableという型がある。
includeに失敗したらFALSEを返し、E_WARNINGを発生させる。成功したら、戻り値は1。ただし、ファイル内でreturnを実行したら、その値を返す。


可変関数は、関数名の文字列の変数に丸括弧を追加したら実行できるというもの。インスタンス変数があれば、メソッドもできる。
includeは特別な言語構造のため、引数に括弧は不要。結果を評価したいならば、全体を括弧で囲む。
// 動作します。
if ((include 'vars.php') == TRUE) {
    echo 'OK';
}


PHP 7.0から、関数のみ"str"()も可能になった。「[https://www.php.net/manual/ja/migration70.php PHP: PHP 5.6.x から PHP 7.0.x への移行 - Manual]」に記載はないが、パース方法が変わったことが由来の模様。
===== require/include =====
requireはincludeとほぼ同じ。違いは、失敗時にE_COMPILE_ERRORが発生して処理を中断する点。includeはE_WARNINGで処理は継続する。


関数もメソッドも統一的に扱うものとして、Callable型がある。
使い分けとして、変数読込などで読み込めなくても処理を進めて問題ない場合に、include。


CallableはPHPで関数を引数として渡したり、関数名の文字列を渡して、動的に関数を実行する仕組み。
関数定義など、絶対必要なものはrequireなど。


callable型で表す。関数だけでなく、メソッドやstaticメソッドも対応できる。方法が2種類ある。
===== _once =====
読込済みなら、再読込しない点がinclude/requireとの決定的な違い。関数の複数定義のエラーを回避できたりする。


# 関数: 関数名の文字列。
読み込めたらtrueを返す。
# メソッド: 配列で指定。0番目の要素に、インスakeタンスやオブジェクト。1番目の要素にメソッド名の文字列で指定する。
# staticメソッド:  配列で指定。0番目の要素に、クラス名を指定する。'ClassName::methodName' 形式でも指定可能。


=====anonymous/無名関数=====
===== config.php =====
callableの型。非常に重要。
// "use" がない場合
$example = function () {
    var_dump($message);
};
$example();
// $message を引き継ぎます
$example = function () use ($message) {
    var_dump($message);
};
useを指定した場合だけ、親のスコープから変数を引き継げる。変数は関数定義時の値。


* [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',
];
?>


===Classes and Objects===
<?php
====The Basics====
Ref: [https://www.php.net/manual/en/language.oop5.basic.php PHP: The Basics - Manual].
// configファイルを変数に代入
=====class=====
$config = include __DIR__ . '/config.php';
class内には変数 (プロパティー)、定数、関数 (メソッド) を含められる。
 
// 呼び出し。
class内の関数などで、これらのプロパティー、メソッド類の参照時は、$this->経由で参照する必要がある。
var_dump($config['name']);
 
C系言語であれば、$this->相当は省略できたが、PHPでは指定が必要なので注意する。
?>
=====::class=====
こういう形式。このreturnだけの文は、ほぼinclude前提。
<className>::classでクラス名の完全修飾子の文字列を取得できる。
 
編集対象のアプリの設定を、既存コードと分離する際に、いい方法。


例外の試験など、クラス名の情報が必要な時によくみかける。
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) . ";";


PHP 8.0.0からオブジェクトに対しても::classを使用でき、元のクラス名を取得できる。その場合、get_class()と同じ。同じならPHP 7で使えないのでget_class()でいいか。
===== config class =====
====Property====
config.phpをどう用意するかは議論がある。
Ref: [https://www.php.net/manual/ja/language.oop5.properties.php PHP: プロパティ - Manual].


クラスのメンバー変数のことをプロパティー (property) と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]


クラス内で、1以上のキーワード (アクセス権、static、PHP 8.1.0以後のみreadonly) のあとに、オプション型宣言 (PHP 7.4以後、readonly以外) の後に変数宣言を続ける。
include/returnではなくて、クラスのconst定数にするという。
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])。


宣言時に初期値を代入もできるが、初期値は定数のみ。関数類は使用不能。
* クラスのconst定数
*[https://stackoverflow.com/questions/40827870/constant-expression-contains-invalid-operations php - Constant expression contains invalid operations - Stack Overflow]
* iniファイル/parse_ini_file
*[https://qiita.com/H40831/items/15ebfbf7d9c05001b6df 【PHP】クラスプロパティの値には、動的な値を代入することができないようです。 #error - Qiita]
以下のエラーが出る。
PHP Fatal error: Constant expression contains invalid operations in /ぼくのかんがえたさいきょうのクラス.php on line 5
関数類で動的に代入したい場合、__constructでやる。
====Autoloading Classes====
Ref:
*[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を使っている。
他に、configクラスを用意しておいて、シングルトンか、staticメソッドで参照する形。


基本的にはcomposerのautoloadかPHP標準のspl_autoload_registerの2択になっている。
どれくらいの頻度で参照するか次第。参照頻度が低いなら、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;
    }


基本的な使用方法。<syntaxhighlight lang="php">
===Function===
<?php
spl_autoload_register(function ($class_name) {
    include $class_name . '.php';
});


$obj  = new MyClass1();
==== User defined ====
$obj2 = new MyClass2();
[https://www.php.net/manual/ja/functions.user-defined.php PHP: ユーザー定義関数 - Manual]
?>


</syntaxhighlight>MyClass1.php MyClass2.phpから該当クラスを自動読み込みする。
関数は以下のような構文で定義する。
<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
    echo "関数の例\n";
    return $retval;
}
?>
関数内では、他の関数やクラス定義を含む、PHPのあらゆるコードを使用可能。関数内で関数を定義できないC言語とは異なる。
 
PHPでは、変数と異なり、関数やクラスは全てグローバルスコープ。関数内で定義した関数も外部から呼び出し可能。スコープが欲しければ、無名関数を使う。


該当クラスを使おうとしたときに、spl_autoload_registerに登録した関数が呼ばれる模様。
また、関数のオーバーロードもできない。関数をunsetしたり、再定義も不能。


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相当をいろいろ指定する形になる。
==== 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]


* [https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php PHP: スコープ定義演算子 (::) - Manual]
PHP 7.3から、関数呼び出し時の終端カンマを許容。
* [https://www.php.net/manual/ja/language.oop5.late-static-bindings.php PHP: 遅延静的束縛 (Late Static Bindings) - Manual]
my1(1,); my2(2,); // OK
* [https://qiita.com/kouki_o9/items/5fd652ce6c7322480089 [PHP]staticメソッドとstatic::に関するメモ #初心者 - Qiita]
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 より前では許されません。
)
{
    // ...
}
?>


スコープ定義演算子 (::) はトークンの一つ。定数、staticプロパティー、staticメソッド、親クラスなどにアクセスできる。
===== Reference =====
引数はデフォルトで値渡しになる。値がコピーされて渡される。関数内部で引数自体を修正したい場合、リファレンス渡しにする。


[Paamayim Nekudotayim] とも呼ぶ。ダブルコロンを意味するヘブライ語らしい。
関数定義で変数の前に&をつけると、リファレンス参照になる。
<?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.' となります
?>


staticメソッド/プロパティーは、遅延静的束縛 (Late Static Bindings) でアクセス可能。
===== Default =====
関数定義時に、引数部分で変数に値を代入するようにして、デフォルト値を定義できる。引数が指定されなかった場合に使われる。なお、nullが渡された場合も、デフォルト値の代入はされないので注意する。
function makecoffee($type = "cappuccino")
{
    return "Making a cup of $type.\n";
}
デフォルト値には、定数を指定できる。具体的には、スカラー値、配列、null。PHP 8.1.0から、new ClassName記法でインスタンスも指定できる。


* MyClass::CONST_VALUE/$classname::CONST_VALUE;
デフォルト引数は、デフォルト値のない引数の右側の必要がある。そうでない場合、省略できず、指定する意味がなくなく。
* self::$my_static
* parent::CONST_VALUE
* static: 実行時に最初の呼び出しクラスを参照。


staticは少々ややこしい。基本はself::でよいと思う。
[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を指定しておいて、以下のような匿名関数で設定するとよいだろう。
====Traits====
                $callback = $callback ?: function($e) {return $e};
[https://www.php.net/manual/ja/language.oop5.traits.php PHP: トレイト - Manual]
関数内で、値の有無を確認する必要がある。


コード再利用のための仕組み。単一継承言語で、コードを再利用するための仕組み。関数クラス (デリゲート) 的なもの。クラスに関数クラスのメソッドを取り込める。インスタンス生成などはできず、関数を水平方向で構成可能にする。継承しなくても、メンバーに追加できる。
===== 可変長引数 =====
引数リストに...を含めることで、可変長の引数を受け取ることを示す。...を前置した変数に配列として入る。
  <?php
  <?php
  trait ezcReflectionReturnInfo {
  function sum(...$numbers) {
    function getReturnType() { /*1*/ }
     $acc = 0;
     function getReturnDescription() { /*2*/ }
    foreach ($numbers as $n) {
        $acc += $n;
    }
    return $acc;
  }
  }
   
   
  class ezcReflectionMethod extends ReflectionMethod {
  echo sum(1, 2, 3, 4);
    use ezcReflectionReturnInfo;
...の前に型宣言も付与できるが、その場合配列要素が全部その型が必要になる。
    /* ... */
 
===== 名前付き引数 =====
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;
  }
  }
   
   
  class ezcReflectionFunction extends ReflectionFunction {
  var_dump(foo(...[1, 2], d: 40)); // 46
    use ezcReflectionReturnInfo;
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
  ?>
 
====Other====
==== Return value ====
=====クラス名の取得=====
Ref: [https://www.php.net/manual/ja/functions.returning-values.php PHP: 戻り値 - Manual].
*[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.get-class.php PHP: get_class - Manual]
関数はreturn文で値を返せる。そこで処理を終了する。
*[https://qiita.com/miriwo/items/7972261a710e79dd1fd5 PHP クラス名::classはどういう処理?? #初心者 - Qiita]
 
*[https://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class.class PHP: クラスの基礎 - Manual]
returnを省略した場合、nullを返す。
get_class($object);
 
クラス名::class
==== Variable Functions/可変関数/Callable/コールバック ====
$object::class // PHP 8.0以上 (get_class相当)
 
(new \ReflectionClass($obj))->getShortName();
* [https://www.php.net/manual/ja/functions.variable-functions.php PHP: 可変関数 - Manual]
基本は名前空間付きのフルパスでの取得。クラス名だけだとgetShortName()
* [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() の動きが違った]


=== Reserved ===
PHPで関数を引数で指定したり、変数として扱う仕組みがある。evalを使う必要はない。


==== keywords ====
可変関数は、関数を文字列で実行する仕組み。これとは別で、Callableという型がある。
[https://www.php.net/manual/ja/reserved.keywords.php PHP: キーワードのリスト - Manual]


式や関数ではなく、定数、クラス名、関数名として使えず、PHPで予約されている特別なキーワードがいくつかある。
可変関数は、関数名の文字列の変数に丸括弧を追加したら実行できるというもの。インスタンス変数があれば、メソッドもできる。


statement/文に近い扱い。言語構文の一部扱い。
PHP 7.0から、関数のみ"str"()も可能になった。「[https://www.php.net/manual/ja/migration70.php PHP: PHP 5.6.x から PHP 7.0.x への移行 - Manual]」に記載はないが、パース方法が変わったことが由来の模様。
{| class="wikitable"
 
|+PHP のキーワード
関数もメソッドも統一的に扱うものとして、Callable型がある。
|[[/www.php.net/manual/ja/function.halt-compiler.php|__halt_compiler()]]
 
|[[/www.php.net/manual/ja/language.oop5.abstract.php|abstract]]
CallableはPHPで関数を引数として渡したり、関数名の文字列を渡して、動的に関数を実行する仕組み。
|[[/www.php.net/manual/ja/language.operators.logical.php|and]]
 
|[[/www.php.net/manual/ja/function.array.php|array()]]
callable型で表す。関数だけでなく、メソッドやstaticメソッドも対応できる。方法が2種類ある。
|[[/www.php.net/manual/ja/control-structures.foreach.php|as]]
 
|-
# 関数: 関数名の文字列。
|[[/www.php.net/manual/ja/control-structures.break.php|break]]
# メソッド: 配列で指定。0番目の要素に、インスakeタンスやオブジェクト。1番目の要素にメソッド名の文字列で指定する。
|[[/www.php.net/manual/ja/language.types.callable.php|callable]]
# staticメソッド:  配列で指定。0番目の要素に、クラス名を指定する。'ClassName::methodName' 形式でも指定可能。
|[[/www.php.net/manual/ja/control-structures.switch.php|case]]
 
|[[/www.php.net/manual/ja/language.exceptions.php|catch]]
==== anonymous/無名関数 ====
|[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class|class]]
[https://www.php.net/manual/ja/functions.anonymous.php PHP: 無名関数 - Manual]
|-
 
|[[/www.php.net/manual/ja/language.oop5.cloning.php|clone]]
2009年頃にPHP 5.3で登場したらしい ([https://hnw.hatenablog.com/entry/20090710 PHP 5.3の無名関数を試してみた - hnwの日記])。
|[[/www.php.net/manual/ja/language.oop5.constants.php|const]]
 
|[[/www.php.net/manual/ja/control-structures.continue.php|continue]]
callableの型。非常に重要。
|[[/www.php.net/manual/ja/control-structures.declare.php|declare]]
$message = "message";
|[[/www.php.net/manual/ja/control-structures.switch.php|default]]
// "use" がない場合
|-
$example = function () {
|[[/www.php.net/manual/ja/function.die.php|die()]]
    // 未定義変数参照扱い
|[[/www.php.net/manual/ja/control-structures.do.while.php|do]]
    var_dump($message);
|[[/www.php.net/manual/ja/function.echo.php|echo]]
};
|[[/www.php.net/manual/ja/control-structures.else.php|else]]
$example();
|[[/www.php.net/manual/ja/control-structures.elseif.php|elseif]]
|-
// $message を引き継ぎます
|[[/www.php.net/manual/ja/function.empty.php|empty()]]
$example = function () use ($message) {
|[[/www.php.net/manual/ja/control-structures.declare.php|enddeclare]]
    var_dump($message);
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endfor]]
};
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endforeach]]
useを指定した場合だけ、親のスコープから変数を引き継げる。変数は関数定義時の値。
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endif]]
 
|-
クラスのコンテキストの場合、$thisは自動で引き継がれる。
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endswitch]]
 
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endwhile]]
即時関数として使うなら、引数で全部渡せる。が、useを使うと引数に指定しなくていいので短くできる。即時関数なら、useで問題ない。
|[[/www.php.net/manual/ja/function.eval.php|eval()]]
 
|[[/www.php.net/manual/ja/function.exit.php|exit()]]
==== arrow/アロー関数 ====
|[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.extends|extends]]
[https://www.php.net/manual/ja/functions.arrow.php PHP: アロー関数 - Manual]
|-
 
|[[/www.php.net/manual/ja/language.oop5.final.php|final]]
PHP 7.4で追加。無名関数の簡易構文。かなり短く記述できる。特に、デフォルトで全部キャプチャーしてくれるのが楽。
|[[/www.php.net/manual/ja/language.exceptions.php|finally]]
fn (argument_list) => expr
|[[/www.php.net/manual/ja/functions.arrow.php|fn]] (PHP 7.4 以降)
親の変数を暗黙でキャプチャー (コピー)。参照でキャプチャーしたい場合は無名関数を使うしかない。
|[[/www.php.net/manual/ja/control-structures.for.php|for]]
$y = 1;
|[[/www.php.net/manual/ja/control-structures.foreach.php|foreach]]
 
|-
$fn1 = fn($x) => $x + $y;
|[[/www.php.net/manual/ja/functions.user-defined.php|function]]
// $y を値渡しするのと同じ
|[[/www.php.net/manual/ja/language.variables.scope.php|global]]
$fn2 = function ($x) use ($y) {
|[[/www.php.net/manual/ja/control-structures.goto.php|goto]]
    return $x + $y;
|[[/www.php.net/manual/ja/control-structures.if.php|if]]
};
|[[/www.php.net/manual/ja/language.oop5.interfaces.php|implements]]
|-
var_export($fn1(3));
|[[/www.php.net/manual/ja/function.include.php|include]]
また、関数本文部分は式。forなどの文を書けない。
|[[/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]]
echo (fn($c)=>[$s=hrtime(1),$c(),hrtime(1)-$s][2])(function(){;}), " ns\n";
|[[/www.php.net/manual/ja/language.oop5.interfaces.php|interface]]
長くなるなら、無名関数にしたほうがよさそう。
|-
 
|[[/www.php.net/manual/ja/function.isset.php|isset()]]
===Classes and Objects===
|[[/www.php.net/manual/ja/function.list.php|list()]]
====The Basics====
|[[/www.php.net/manual/ja/control-structures.match.php|match]] (PHP 8.0 以降)
[https://www.php.net/manual/ja/language.oop5.basic.php PHP: クラスの基礎 - Manual]
|[[/www.php.net/manual/ja/language.namespaces.php|namespace]]
=====class=====
|[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.new|new]]
class内には変数 (プロパティー)、定数、関数 (メソッド) を含められる。
|-
 
|[[/www.php.net/manual/ja/language.operators.logical.php|or]]
class内の関数などで、これらのプロパティー、メソッド類の参照時は、擬似変数$this->経由で参照できる。$thisは呼び出し元オブジェクトが入っている。
|[[/www.php.net/manual/ja/function.print.php|print]]
 
|[[/www.php.net/manual/ja/language.oop5.visibility.php|private]]
C系言語であれば、$this->相当は省略できたが、PHPでは指定が必要なので注意する。
|[[/www.php.net/manual/ja/language.oop5.visibility.php|protected]]
 
|[[/www.php.net/manual/ja/language.oop5.visibility.php|public]]
===== new =====
|-
インスタンス生成に使うキーワード。コンストラクターで例外をスローしていない限り、常にオブジェクトを生成する。
|[[/www.php.net/manual/ja/language.oop5.properties.php#language.oop5.properties.readonly-properties|readonly]] (PHP 8.1.0 以降) *
$instance = new SimpleClass();
|[[/www.php.net/manual/ja/function.require.php|require]]
$instance = new SimpleClass; // コンストラクターに引数を渡さない場合、()を省略可能。
|[[/www.php.net/manual/ja/function.require-once.php|require_once]]
$class_name = 'SimpleClass';
|[[/www.php.net/manual/ja/function.return.php|return]]
$instance = new $class_name; // クラス名の変数も使用可能。
|[[/www.php.net/manual/ja/language.variables.scope.php|static]]
$instance = new ('SimpleClass'); // PHP 8.0.0以上で丸括弧で囲むことで任意の指揮と一緒に使える。
|-
文字列で動的に生成可能な点は重要。
|[[/www.php.net/manual/ja/control-structures.switch.php|switch]]
 
|[[/www.php.net/manual/ja/language.exceptions.php|throw]]
https://chatgpt.com/c/67a992f0-41f0-800b-9d6b-7bafb5ffda5f
|[[/www.php.net/manual/ja/language.oop5.traits.php|trait]]
 
|[[/www.php.net/manual/ja/language.exceptions.php|try]]
new self, new parent, new staticもある。new staticは遅延静的束縛。
|[[/www.php.net/manual/ja/function.unset.php|unset()]]
 
|-
new staticで静的束縛する場合、戻り値はstaticにする。ただし、PHP 8.0からしか使えない。その場合、戻り値はなしで、phpdocで@return staticにする。
|[[/www.php.net/manual/ja/language.namespaces.php|use]]
 
|[[/www.php.net/manual/ja/language.oop5.properties.php|var]]
=====::class=====
|[[/www.php.net/manual/ja/control-structures.while.php|while]]
<className>::classでクラス名の完全修飾子の文字列を取得できる。
|[[/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]]
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では呼んでいる。
|}
 
<nowiki>*</nowiki> <code>readonly</code> は、関数名として使用できます。
クラス内で、1以上のキーワード (アクセス権、static、PHP 8.1.0以後のみreadonly) のあとに、オプション型宣言 (PHP 7.4以後、readonly以外) の後に変数宣言を続ける。
{| class="wikitable"
public $var1
|+コンパイル時の定数
static $var2
|[[/www.php.net/manual/ja/language.constants.predefined.php|__CLASS__]]
var $var3
|[[/www.php.net/manual/ja/language.constants.predefined.php|__DIR__]]
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])。
|[[/www.php.net/manual/ja/language.constants.predefined.php|__FILE__]]
 
|[[/www.php.net/manual/ja/language.constants.predefined.php|__FUNCTION__]]
が、[[https://www.php-fig.org/psr/psr-12/#43-properties-and-constants PSR-12: Extended Coding Style - PHP-FIG]] などのコーディング規約で禁止されている。古い書き方なのでpublicに書き直せばいいと思う。
|[[/www.php.net/manual/ja/language.constants.predefined.php|__LINE__]]
 
|[[/www.php.net/manual/ja/language.constants.predefined.php|__METHOD__]]
宣言時に初期値を代入もできるが、初期値は定数のみ。関数類は使用不能。
|-
*[https://stackoverflow.com/questions/40827870/constant-expression-contains-invalid-operations php - Constant expression contains invalid operations - Stack Overflow]
|[[/www.php.net/manual/ja/language.namespaces.nsconstants.php|__NAMESPACE__]]
*[https://qiita.com/H40831/items/15ebfbf7d9c05001b6df 【PHP】クラスプロパティの値には、動的な値を代入することができないようです。 #error - Qiita]
|[[/www.php.net/manual/ja/language.constants.predefined.php|__TRAIT__]]
以下のエラーが出る。
|
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();
?>


==== Interfaces ====
</syntaxhighlight>MyClass1.php MyClass2.phpから該当クラスを自動読み込みする。
[https://www.php.net/manual/ja/reserved.interfaces.php PHP: 定義済みのインターフェイスとクラス - Manual]


===== stdClass =====
該当クラスを使おうとしたときに、spl_autoload_registerに登録した関数が呼ばれる模様。
[https://www.php.net/manual/ja/class.stdclass.php PHP: stdClass - Manual]


動的なプロパティーが使える、汎用的な空クラス。このクラス自体は、メソッドやプロパティーを持たない。
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にはクラスの完全修飾子が入る。


json_decodeなど一部の関数がこのインスタンスを返す。
このcallback内で独自のrequire_once相当をいろいろ指定する形になる。
// 型変換での作成。連想配列を(object)にキャストすると作れる。
(object) array('foo' => 'bar');
データベースの取得結果が、連想配列の他に、stdClassになっていることがある。


匿名オブジェクトや、動的プロパティーなどが主な利用方法。
ただ、今はcomposerを使うのが主流。


データホルダーとして使う場合、連想配列のキーのほうが、自由度が高いので、そちらのほうが便利だと思われる。たくさんある配列関数も使えるし。
https://chatgpt.com/c/67355ddc-166c-800b-93ee-a25843cb2839


==Features==
spl_autoloader_registerとcomposerのオートローディングはどちらを使ったほうがいいですか?
Ref: [https://www.php.net/manual/en/features.php PHP: Features - Manual].
 
===Handling file uploads===
spl_autoloader_register
Ref: [https://www.php.net/manual/en/features.file-upload.php PHP: Handling file uploads - Manual].


input type="file"などのアップロードファイルのPHPでの処理方法・作法がある<syntaxhighlight lang="html">
* 利点
<!-- データのエンコード方式である enctype は、必ず以下のようにしなければなりません -->
** '''軽量で柔軟''':シンプルなプロジェクトでは、1つの関数で基本的なオートローディングを簡単に設定できます。
<form enctype="multipart/form-data" action="__URL__" method="POST">
** '''依存関係がない''':Composerのような外部ツールや設定ファイルを必要としないので、PHP単体で実装できます。
    <!-- MAX_FILE_SIZE は、必ず "file" input フィールドより前になければなりません -->
* 欠点
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
** '''設定が手動''':名前空間とディレクトリのマッピングを手動で設定する必要があり、大規模なプロジェクトでは管理が複雑になりがちです。
    <!-- input 要素の name 属性の値が、$_FILES 配列のキーになります -->
** '''標準規約に対応するのが難しい''':<code>PSR-4</code>のような標準的なオートローディング規約(ファイル構造と名前空間のマッピングルール)を自分で実装する必要があるため、コードが煩雑になる可能性があります。
    このファイルをアップロード: <input name="userfile" type="file" />
** '''依存ライブラリの管理が難しい''':外部ライブラリを含める場合は、<code>require</code><code>include</code>で個別に読み込む必要があり、依存管理が困難です。
    <input type="submit" value="ファイルを送信" />
</form>
</syntaxhighlight>PHP側では$_FILES['userfile']に必要な情報が格納される。
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['name']]]</var>
*:クライアントマシンの元のファイル名。
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['type']]]</var>
*:ファイルの MIME 型。ただし、ブラウザがこの情報を提供する場合。 例えば、<code>"image/gif"</code> のようになります。 この MIME 型は PHP 側ではチェックされません。そのため、 この値は信用できません。
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['size']]]</var>
*:アップロードされたファイルのバイト単位のサイズ。
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['tmp_name']]]</var>
*:アップロードされたファイルがサーバー上で保存されているテンポラ リファイルの名前。
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['error']]]</var>
*:このファイルアップロードに関する [[/wiki.gnusocial.jp//www.php.net/manual/ja/features.file-upload.errors.php|エラーコード]]
*;<var>[[/wiki.gnusocial.jp//www.php.net/manual/ja/reserved.variables.files.php|$_FILES['userfile']['full_path']]]</var>
*:ブラウザからアップロードされたファイルのフルパス。 この値は実際のディレクトリ構造を反映しているとは必ずしも言えないため、 信用できません。 PHP 8.1.0 以降で利用可能です。
tmp_nameが非常に重要。これをリネームする形で保存する。あとはnameも保存時のファイル名で重要。<syntaxhighlight lang="php">
<?php
$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);


echo '<pre>';
composer
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
 
    echo "File is valid, and was successfully uploaded.\n";
* 利点
} else {
** '''自動設定''':<code>PSR-4</code>対応の名前空間とディレクトリのマッピングを自動的に管理するため、設定が簡単です。
    echo "Possible file upload attack!\n";
** '''依存ライブラリの自動管理''':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]


echo 'Here is some more debugging info:';
プロパティー、メソッド、定数 (PHP 7.1.0以上) にはpublic/protected/privateのアクセス権 (visibility) を指定できる。
print_r($_FILES);


print "</pre>";
* public: どこからでもアクセス可能。
* protected: クラス自身、継承クラス、親クラス。
* private: クラス自身。


?>
アクセス権を省略した場合、public扱いになる。
</syntaxhighlight>上記がイメージ。


DBに保存する場合は「[https://qiita.com/NULLchar/items/7bdc6685be0aa909e8fe PHPとMySQLを利用した画像・動画のアップロード・保存・表示 #PHP - Qiita]」も参考になる。
なお、同じ型のオブジェクト間では、同一インスタンスでなくても、protected/privateにもアクセス可能。オブジェクト内ではオブジェクト実装が既知だから。
===Using PHP from the command line===
Ref: [https://www.php.net/manual/en/features.commandline.php PHP: Command line usage - Manual].


簡単なコードの確認などでPHPをコマンドラインなどから簡単に実行したいことがよくある。いくつか方法がある ([https://www.php.net/manual/en/features.commandline.usage.php PHP: Usage - Manual])。
アクセス権の指定は、PHP 5から導入。PHP 4ではvar指定で全部publicだった。Doxygenや命名規則 (先頭_はprivate) などで区別していた (https://chatgpt.com/c/67a031c2-ce24-800b-b1bf-7d4668b58fb4<nowiki/>)
#phpコマンドの引数にファイルを指定: <code>php file.php</code>/<code>php -f file.php</code>
#phpコマンドの引数にコードを指定: <code>php -r 'print_r(get_defined_constants());'</code>
#phpコマンドに標準入力で読み込み: <code>php <file.php</code>
標準入力が一番使いやすく感じる。
==Function Reference==
===Affecting PHP's Behavior===
====Error Handling====
=====Runtime Configuration=====
PHPのエラー設定を整理する。 PHPのエラー設定は「[https://www.php.net/manual/en/errorfunc.configuration.php PHP: Runtime Configuration - Manual]」で一覧化されている。


xmlrpc_errors, syslog.facility, syslog.ident以外はどこでも設定可能。
==== スコープ定義演算子 (::) ====


特に重要なのが以下の設定。
* [https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php PHP: スコープ定義演算子 (::) - Manual]
{| class="wikitable"
* [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メソッド、親クラスなどにアクセスできる。
|-
|error_reporting
|<code>E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED</code>
|エラー出力レベルを設定する。開発時にはE_ALL (2147483647/-1) にしておくとよい。
|-
|display_errors
|"1"
|エラーのHTML出力への表示を設定する。"stderr"を指定すると,stderrに送る。デフォルトで有効なのでそのままでいい。
|-
|display_startup_errors
|"0"
|PHPの起動シーケンス中のエラー表示を設定する。デバッグ時は有効にしておいたほうがいい。
|-
|log_errors
|"0"
|エラーメッセージのサーバーのエラーログまたはerror_logへの記録を指定する。これを指定しないとログが残らないため,常に指定したほうがいい
|-
|error_log
|NULL
|スクリプトエラーが記録されるファイル名を指定する。syslogが指定されると,ファイルではなくシステムロガーに送られる。Unixではsyslog(3)で,Windowsではイベントログになる。指定されていない場合,SAPIエラーロガーに送信される。ApacheのエラーログかCLIならstderrになる。
|}基本的に以下をphp.ini/.user.iniに設定しておけばよい。<syntaxhighlight lang="ini">
error_reporting = E_ALL
display_startup_errors = on
log_errors = on


; For file
[Paamayim Nekudotayim] とも呼ぶ。ダブルコロンを意味するヘブライ語らしい。
display_errors = stderr
 
</syntaxhighlight>htpd.conf/.htaccessの場合は以下。<syntaxhighlight lang="http">
staticメソッド/プロパティーは、遅延静的束縛 (Late Static Bindings) でアクセス可能。
php_value error_reporting -1
php_flag display_startup_errors on
php_flag log_errors on


# For file
* MyClass::CONST_VALUE/$classname::CONST_VALUE;
php_value display_errors stderr
* self::$my_static
</syntaxhighlight>
* parent::CONST_VALUE
===File system===
* static: 実行時に最初の呼び出しクラスを参照。
Ref: [https://www.php.net/manual/ja/refs.fileprocess.file.php PHP: ファイルシステム - Manual].
====File system====
=====io=====
*[https://www.php.net/manual/ja/function.file-get-contents.php file_get_contents] — ファイルの内容を全て文字列に読み込む
*[https://www.php.net/manual/ja/function.file-put-contents.php file_put_contents]: データをファイルに書き込む。戻り値に書き込みバイト数を返す。失敗したらfalse。成否は完全一致===falseで。
*[https://www.php.net/manual/ja/function.file.php PHP: file - Manual]: ファイル全体を読み込んで改行区切りで配列にする。
上記2個の非常に重要な入出力関数がある。


バイナリーやHTTP GETに対応している。アップロードされたファイルの読み込みなどでお世話になる。
staticは少々ややこしい。基本はself::でよいと思う。
=====check=====
====Traits====
*[https://www.php.net/manual/ja/function.file-exists.php PHP: file_exists - Manual]
[https://www.php.net/manual/ja/language.oop5.traits.php PHP: トレイト - Manual]
*[https://www.php.net/manual/ja/function.is-readable.php PHP: is_readable - Manual]
 
*[https://www.php.net/manual/ja/function.is-writable.php PHP: is_writable - Manual]
コード再利用のための仕組み。単一継承言語で、コードを再利用するための仕組み。関数クラス (デリゲート) 的なもの。クラスに関数クラスのメソッドを取り込める。インスタンス生成などはできず、関数を水平方向で構成可能にする。継承しなくても、メンバーに追加できる。
入出力とセットで使うファイルの不在確認の関数群。
<?php
===International===
trait ezcReflectionReturnInfo {
====mbstring====
    function getReturnType() { /*1*/ }
=====mb_substr=====
    function getReturnDescription() { /*2*/ }
Ref:
}
*[https://www.php.net/manual/ja/function.mb-substr.php PHP: mb_substr - Manual].
*[https://www.php.net/manual/ja/function.substr.php PHP: substr - Manual]
class ezcReflectionMethod extends ReflectionMethod {
mb_substr(
    use ezcReflectionReturnInfo;
    /* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
    use ezcReflectionReturnInfo;
    /* ... */
}
?>
 
==== Overloading/オーバーロード ====
[https://www.php.net/manual/ja/language.oop5.overloading.php#object.get PHP: オーバーロード - Manual]


    string $string,
一般的には多重定義の意味だが、PHPのオーバーロードはプロパティーやメソッドを動的に作る機能。マジックメソッドで実装される (オーバーロードメソッド)。


    int $start,
オーバーロードメソッドは、未宣言プロパティー・メソッドのアクセス時に発動する。


    ?int $length = null,
オーバーロードメソッドはすべてpublicで定義する。


    ?string $encoding = null
===== プロパティーのオーバーロード =====
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;


): string
==== Magic/マジックメソッド ====
[https://www.php.net/manual/ja/language.oop5.magic.php PHP: マジックメソッド - Manual]


substr同様、lengthにはマイナス値を指定可能。その場合、末尾からの文字数になる。
PHPのデフォルトの動作を上書きする特別なメソッドをマジックメソッドと呼んでいる。


省略するかnullを指定すると、全文字。0は0文字。
どらも__ (アンダーバー2個) から始まる。__始まりの全メソッドはPHPで予約されているのでユーザー定義メソッドとしては非推奨。
===Text===
====Strings====
=====strpos=====
Ref: [https://www.php.net/manual/ja/function.strpos.php PHP: strpos - Manual].


strpos([https://www.php.net/manual/ja/language.types.string.php string] <code>$haystack</code>, [[/wiki.gnusocial.jp//www.php.net/manual/ja/language.types.string.php|string]] <code>$needle</code>, [[/wiki.gnusocial.jp//www.php.net/manual/ja/language.types.integer.php|int]] <code>$offset</code> = 0): [[/wiki.gnusocial.jp//www.php.net/manual/ja/language.types.integer.php|int]]|[[/wiki.gnusocial.jp//www.php.net/manual/ja/language.types.value.php|false]]
以下がある。


文字列 <code>haystack</code> の中で、 <code>needle</code> が最初に現れる位置を探します。
* __construct
* __destruct
* __call
* __callStatic
* __get
* __set
* __isset
* __unset
* __sleep
* __wakeup
* __serialize
* __unserialize
* __toString
* __invoke
* __set_state
* __clone
* __debugInfo


include相当。よく使う。
__construct/__destruct/__clone以外の全マジックメソッドはpublic必須。E_WARNINGの警告が発生する。


戻り値に注意。有無の確認時は、strpos() !== falseの厳密一致でチェックする必要がある。
__construct/__desctructは戻り値型を宣言してはいけない。


===== echo/print/printf =====
====Other====
 
=====クラス名の取得=====
* [https://www.php.net/manual/ja/function.echo.php PHP: echo - Manual]
*[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.print.php PHP: print - Manual]
*[https://www.php.net/manual/ja/function.get-class.php PHP: get_class - Manual]
* [https://www.php.net/manual/ja/function.printf.php PHP: printf - Manual]
*[https://qiita.com/miriwo/items/7972261a710e79dd1fd5 PHP クラス名::classはどういう処理?? #初心者 - Qiita]
* [https://qiita.com/tadsan/items/e09475093bc336881b20 echoとprintの違い #PHP - Qiita]
*[https://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class.class PHP: クラスの基礎 - Manual]
* [https://qiita.com/chimayu/items/dcc443c3a99f3379b3da PHP : echoとprintの違い #まとめ - Qiita]
get_class($object);
* [https://inouelog.com/php-echo-print/ 【PHP】echoとprintの違いとは?どっちを使えば良い?]
クラス名::class
$object::class // PHP 8.0以上 (get_class相当)
(new \ReflectionClass($obj))->getShortName(); // クラス名
基本は名前空間付きのフルパスでの取得。クラス名だけだとgetShortName()。クラスがなければnewで例外。


PHPの出力関数群。よく使うが、扱いが特殊なので整理する。
=== Generator ===


まず、echo/printは関数ではなく、if/forなどと同じ言語構造、キーワード扱い。丸括弧はなくてもいい。紛らわしいのでないほうがいい。
* [https://www.php.net/manual/ja/language.generators.php PHP: ジェネレータ - Manual]
* [https://www.php.net/manual/ja/class.generator.php PHP: Generator - Manual]


* 共通: 末尾に改行は付与されない。自分で"\n"を指定必要。関数ではないので丸括弧は不要。
PHP 5.5から導入。
* echo: 戻り値void。式ではないので、if returnなどで使えない。戻り値がない分printよりわずかに速い。文字数が短い。コンマ区切りで複数列挙可能。文字列連結するよりコンマ区切りのほうが.演算子の優先順位など扱いが簡単。HTMLでの埋め込みに便利な <?= ?>の短縮表記 (<?php echo ; ?>相当)もあり。
* print: 戻り値intで常に1を返す。ifや条件演算子の結果部分など、式の文脈で使用可能。


基本はechoでいい。if return/条件演算子など戻り値や式が必要な箇所でだけprintを使う。
シンプルなイテレーターの実装のための機能。配列と異なり、大量のメモリーの確保が不要になる。


printfは関数。書式指定が必要ならこれ。
関数でreturnの代わりにyieldで値を返すとジェネレーター関数になる。yieldはGeneratorオブジェクトを返す。


=== Basic/Vartype/変数・データ型関連 ===
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時。


==== Variable ====
* yield $id => $fields;でキーバリューで返す。
* yeildだけだとNULL。


===== print_r/var_export/var_dump =====
なお、ジェネレーターの値は前に進むことしかできない。


* [https://qiita.com/yasumodev/items/4b0e5a0d2d1ec3b3177b [PHP] print_r、var_dump、var_export のちがい #PHP - Qiita]
[https://qiita.com/Hiraku/items/190443b33ee7a2167ade PHPで高速オシャレな配列操作を求めて #PHP - Qiita]
* [https://laracasts.com/discuss/channels/laravel/how-to-create-dynamically-create-configcustomphp-config-file How to create Dynamically create config/custom.php config file]
* [https://www.php.net/manual/ja/function.print-r.php PHP: print_r - Manual]
* [https://www.php.net/manual/ja/function.var-export.php PHP: var_export - Manual]
* [https://www.php.net/manual/ja/function.var-dump.php PHP: var_dump - Manual]


PHPの変数の内容を、配列やオブジェクトなどの複雑なデータ型でも表示、出力可能なのがprint_r/var_export/var_dump。微妙に違いがある。
真価を発揮するのは、巨大配列の処理や、配列処理の分割。


* var_dump: これのみ型情報がある。
array_関数で配列を一括処理すると遅い。foreachでやると速いが、foreachの処理内容を分割できない。そういうときに、foreachの処理をyieldにすると、分割できる。後続の処理も、generator (配列) を引数に受け取る想定で作ると、連携できる。
* var_export: 変数の文字列表現。config.phpなどファイルに出力すればそのままinclude可能。
echo (new Collection(range(0, 10000)))
* print_r: 基本は閲覧用の文字列。
    ->filter('$_ % 2 === 0')
* print_r/var_export: 第二引数をtrueにすると、画面出力ではなく、戻り値に返す。
    ->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();


  $text = "<?php\n\nreturn " . var_export($myarray, true) . ";";
  $mapped = [];
使うとしたら、var_exportとvar_dumpだと思われる。
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;
}


var_dumpは型の情報が詳しい。詳細な情報が欲しい場合、var_dump。そうでない、単に見たいだけなら、var_exportで十分。
function my_special_logic($arr) {
    foreach ($arr as $v) {
        if ($v % 2) continue;
        $v **= 2;
        yield $v;
    }
}


var_dumpは表示のみ。出力制御関数を使えば、文字列に保存はできるが、基本はデバッグ表示用。
$sum = 0;
  <?php
  foreach (my_special_logic(range(0, 10000)) as $v) {
  $data = array(
    if ($v <= 20) continue;
    "A" => "Apple",
   
    "B" => "Banana",
    $sum += $v;
    "C" => "Cherry"
}
  );
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 {
   
   
  echo "---print_r---\n";
  const CONNECT_OK = 1;
  print_r($data);
  class Connection { /* ... */ }
function connect() { /* ... */  }
}
   
   
echo "---var_export---\n";
var_export($data);
   
   
  echo "---var_dump---\n";
  namespace { // global code
  var_dump($data);
session_start();
  ?>
$a = MyProject\connect();
  echo MyProject\Connection::start();
  }
名前空間とグローバルを分ける場合、グローバルを名前を指定しないnamespaceで囲む。


---print_r---
===== Basic =====
Array
ファイルへのアクセスに、相対パスと絶対パスがあるように、名前空間へのアクセス方法がいくつかある。
(
 
    [A] => Apple
# $a = new foo(): 名前空間を指定しない場合。現在の名前空間currentnamespaceがあれば、currentnamespace\foo。なければグローバルのfoo。
    [B] => Banana
# $a = new subnamespace\foo():
    [C] => Cherry
# $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;
   
   
  ---var_export---
  use some\namespace\{ClassA, ClassB, ClassC as C};
array (
 
  'A' => 'Apple',
===== Global =====
  'B' => 'Banana',
[https://www.php.net/manual/ja/language.namespaces.global.php PHP: グローバル空間 - Manual]
  'C' => 'Cherry',
 
)
名前の先頭に\をつけるとグローバル空間の名前を指定できる。
  $f = \fopen(...)
 
---var_dump---
=== Reserved ===
array(3) {
 
  ["A"]=>
==== keywords ====
  string(5) "Apple"
[https://www.php.net/manual/ja/reserved.keywords.php PHP: キーワードのリスト - Manual]
  ["B"]=>
 
  string(6) "Banana"
式や関数ではなく、定数、クラス名、関数名として使えず、PHPで予約されている特別なキーワードがいくつかある。
  ["C"]=>
 
  string(6) "Cherry"
statement/文に近い扱い。言語構文の一部扱い。
}
{| class="wikitable"
画面出力でデバッグしたいならば、前後でpre要素を出力させる。
|+PHP のキーワード
  <?php
|[[/www.php.net/manual/ja/function.halt-compiler.php|__halt_compiler()]]
function print_r2($val){
|[[/www.php.net/manual/ja/language.oop5.abstract.php|abstract]]
        echo '<nowiki><pre>';</nowiki>
|[[/www.php.net/manual/ja/language.operators.logical.php|and]]
        print_r($val);
|[[/www.php.net/manual/ja/function.array.php|array()]]
        echo  '<nowiki></pre></nowiki>';
|[[/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]]
=== Process ===
|[[/www.php.net/manual/ja/control-structures.switch.php|case]]
 
|[[/www.php.net/manual/ja/language.exceptions.php|catch]]
==== exec ====
|[[/www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.class|class]]
システムプログラムの実行のための関数群がある。Windowsだとcmd.exe経由で実行される。
|-
 
|[[/www.php.net/manual/ja/language.oop5.cloning.php|clone]]
===== exec/shell_exec/system/passthru =====
|[[/www.php.net/manual/ja/language.oop5.constants.php|const]]
 
|[[/www.php.net/manual/ja/control-structures.continue.php|continue]]
* [https://stackoverflow.com/questions/7093860/php-shell-exec-vs-exec shell - PHP shell_exec() vs exec() - Stack Overflow]
|[[/www.php.net/manual/ja/control-structures.declare.php|declare]]
* [https://www.php.net/manual/ja/function.exec.php PHP: exec - Manual]
|[[/www.php.net/manual/ja/control-structures.switch.php|default]]
* [https://www.php.net/manual/ja/function.passthru.php PHP: passthru - Manual]
* [https://www.php.net/manual/ja/function.shell-exec.php PHP: shell_exec - Manual]/[https://www.php.net/manual/ja/language.operators.execution.php PHP: 実行演算子 - Manual]
* [https://www.php.net/manual/ja/function.system.php PHP: system - Manual]
 
外部プログラム実行のための3の関数がある。違いがある。
 
* shell_exec: 標準出力をstringで返す。バッククオート演算子``と同じ。プログラムの成功可否、終了コードは判断不能。
* exec: 既定で標準出力の最後の1行のみ返す。第二引数に配列を指定すれば、行区切りの配列で返すこともできる。
* passthru: Unixコマンドの出力がバイナリーで、ブラウザーに直接バイナリーを返す場合に、exec/systemの代わりに使う。戻り値は?falseで標準出力に直接結果を出力する。
* system: C言語のsystem関数に類似。system()でコマンドを実行して出力する。成功時にコマンド出力の最終行を返す。出力をファイルや別のストリームにリダイレクトしないと、終了までPHPが止まる。
 
{| class="wikitable"
|+
!項目
!exec
!passthru
!shell_exec/``
!system
|-
|-
|出力
|[[/www.php.net/manual/ja/function.die.php|die()]]
| -
|[[/www.php.net/manual/ja/control-structures.do.while.php|do]]
|x
|[[/www.php.net/manual/ja/function.echo.php|echo]]
| -
|[[/www.php.net/manual/ja/control-structures.else.php|else]]
|x
|[[/www.php.net/manual/ja/control-structures.elseif.php|elseif]]
|-
|-
|終了コード
|[[/www.php.net/manual/ja/function.empty.php|empty()]]
|x
|[[/www.php.net/manual/ja/control-structures.declare.php|enddeclare]]
|x
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endfor]]
| -
|[[/www.php.net/manual/ja/control-structures.alternative-syntax.php|endforeach]]
|x
|[[/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]]
|null/false
|[[/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]]
|x
|[[/www.php.net/manual/ja/language.exceptions.php|finally]]
| -
|[[/www.php.net/manual/ja/functions.arrow.php|fn]] (PHP 7.4 以降)
|x
|[[/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]]
|}
|-
基本はshell_exec/`` execで十分。
|[[/www.php.net/manual/ja/language.operators.logical.php|or]]
 
|[[/www.php.net/manual/ja/function.print.php|print]]
===== exec =====
|[[/www.php.net/manual/ja/language.oop5.visibility.php|private]]
[https://www.php.net/manual/ja/function.exec.php PHP: exec - Manual]
|[[/www.php.net/manual/ja/language.oop5.visibility.php|protected]]
exec(string $command, array &$output = null, int &$result_code = null): string|false
|[[/www.php.net/manual/ja/language.oop5.visibility.php|public]]
戻り値は最終行。失敗したらfalseを返す。実行コマンドの終了コードは$result_codeに渡される。
|-
 
|[[/www.php.net/manual/ja/language.oop5.properties.php#language.oop5.properties.readonly-properties|readonly]] (PHP 8.1.0 以降) *
===Other/その他の基本モジュール===
|[[/www.php.net/manual/ja/function.require.php|require]]
[https://www.php.net/manual/ja/refs.basic.other.php PHP: その他の基本モジュール - Manual]
|[[/www.php.net/manual/ja/function.require-once.php|require_once]]
====JSON====
|[[/www.php.net/manual/ja/function.return.php|return]]
[https://www.php.net/manual/ja/book.json.php PHP: JSON - Manual]
|[[/www.php.net/manual/ja/language.variables.scope.php|static]]
 
|-
===== About =====
|[[/www.php.net/manual/ja/control-structures.switch.php|switch]]
json_encode/json_decodeをよく使う。非常に重要。
|[[/www.php.net/manual/ja/language.exceptions.php|throw]]
 
|[[/www.php.net/manual/ja/language.oop5.traits.php|trait]]
json_encodeはオブジェクトをJSON文字列表記にできるのでデバッグなどで便利。
|[[/www.php.net/manual/ja/language.exceptions.php|try]]
 
|[[/www.php.net/manual/ja/function.unset.php|unset()]]
[https://kohkimakimoto.hatenablog.com/entry/2012/05/17/180738 PHPでUnicodeアンエスケープしたJSONを出力する関数 - オープンソースこねこね]
|-
 
|[[/www.php.net/manual/ja/language.namespaces.php|use]]
JSON_UNESCAPED_UNICODE をオプションに指定しないと日本語はユニコードエスケープ表記になる。
|[[/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]]
json_decode(
|[[/www.php.net/manual/ja/language.generators.php|yield]]
    string $json,
|-
    ?bool $associative = null,
|[[/www.php.net/manual/ja/language.generators.syntax.php#control-structures.yield.from|yield from]]
    int $depth = 512,
|
    int $flags = 0
|
): mixed
|
json_decodeは第二引数にtrueを指定しないと、object (stdClass) になる。trueにすると、連想配列になる。基本は連想配列でいいと思う。
|