- 投稿日:2020-05-25T22:42:06+09:00
PHP インターフェースの検証
インターフェースとはなんなのか、何も知らないところからスタートします。
公式ドキュメントから引用。
オブジェクトインターフェイスにより、あるクラスが実装する必要があるメソッドの種類を、
これらのメソッドの実装を定義することなく、指定するコードを作成できるようになります。
では、実際に書いていきます。
1.基本
理解するのに時間がかかりました。
test_sample.php<?php require_once(__DIR__ . '/classTest.php'); classSample::two(); ?>classTest.php<?php interface Test { public function one(); public function two(); } class classTest implements Test { public function one(){ echo 'TestOne'; } public function two(){ echo 'TestTwo'; } } class classSample implements Test { public function one(){ echo 'SampleTwo'; } public function two(){ echo 'SampleTwo'; } } ?>・実行結果 SampleTwoここまでは普通なんですけど、
インターフェースで指定したメソッドは最低限使わなくちゃいけないやつです。上記ならoneメソッドとtwoメソッドは最低限使わなきゃいけない。
では、classSampleのoneメソッドをコメントアウトして実行したら以下↓
・実行結果 Fatal error: Class classSample contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Test::one) in /・・・・・・/classTest.php on line 222.メソッドが増えた場合
メソッドが増える文には問題ないかな?
test_sample.php<?php require_once(__DIR__ . '/classTest.php'); classSample::three(); ?>classTest.php<?php interface Test { public function one(); public function two(); } class classTest implements Test { public function one(){ echo 'TestOne'; } public function two(){ echo 'TestTwo'; } } class classSample implements Test { public function one(){ echo 'SampleOne'; } public function two(){ echo 'SampleTwo'; } public function three(){ echo 'SampleThree'; } } ?>・実行結果 SampleThree増えたメソッドに関しては使える。
3.継承
継承はできるか・・・・・・
classTest.php<?php interface Test extends classTest { public function one(); public function two(); } class classTest { public function one(){ echo 'TestOne'; } public function two(){ echo 'TestTwo'; } } class classSample implements Test { public function one(){ echo 'SampleOne'; } public function two(){ echo 'SampleTwo'; } } ?>・実行結果 Fatal error: Test cannot implement classTest - it is not an interface in /・・・・・・・/classTest.php on line 3これなら?
classTest.php<?php interface TestA { public function one(); } interface TestB extends TestA{ public function two(); } class classTest { public function one(){ echo 'TestOne'; } public function two(){ echo 'TestTwo'; } } class classSample implements TestA { public function one(){ echo 'SampleOne'; } public function two(){ echo 'SampleTwo'; } } ?>・実行結果 SampleTwoなるほど。
インターフェースとなら継承可能ですね。ちな、カンマ区切りで複数継承可能。
classTest.php<?php interface TestA { public function one(); } interface TestB{ public function two(); } interface TestC extends TestA, TestB{ public function three(); } class classSample implements TestC { public function one(){ echo 'SampleOne'; } public function two(){ echo 'SampleTwo'; } public function three(){ echo 'SampleThree'; } } ?>4.interfaceとabstractの違い
下記の記事↓
https://qiita.com/yukiyohure0923/items/0e49a313b2b8c98d3a29
引用させて頂きます。・interface
1.定数を定義できる
2.アクセス修飾子はpublicのみ指定可能
3.interfaceはクラスじゃないからインスタンス化できない
4.クラスで使うときはimplementsでinterface名を指定する
5.実装を伴うメソッドやプロパティの定義はできない(抽象メソッドのみ)
6.1つのクラスに対し複数のinterfaceを実装できる(多重継承みたいなことができる)
7.interfaceで定義したメソッドをクラス内で必ず使用(オーバーライド)しなければならない・abstract
1.アクセス修飾子は自由につけられる
2.抽象プロパティだけ抽象クラスの中で定義できない
3.抽象クラスはクラスだけど直接インスタンス化できない
4.クラスで使うときは普通の継承のようにextendsを使う
5.普通のクラスみたいに中身があるメソッドやプロパティを定義できる
6.抽象クラスで「定義し、なおかつ未実装のメソッド」を継承したクラス内で必ず使用(オーバーライド)しなければならない
- 投稿日:2020-05-25T20:00:14+09:00
【PHP8.0】例外をcatchしたいけど何もしたくない
例外をcatchしたいけど何もしたくない。
try{ foo(); }catch(Throwable $e){ // 何もしない }何もしないのにわざわざ変数に受け取るのって無駄じゃありませんか?
というわけでnon-capturing catchesというRFCが提出されました。
PHP RFC: non-capturing catches
Introduction
今のところ、PHPは例外を明示的に変数でキャプチャする必要があります。
try { foo(); } catch (SomeException $ex) { die($ex->getMessage()); }しかしながら、ときにはその変数を使わない場合もあります。
try { changeImportantData(); } catch (PermissionException $ex) { echo "You don't have permission to do this"; }これを見た人は、プログラマが意図してこの変数を使わなかったのか、あるいはバグなのかがわかりません。
Proposal
例外を変数に受け取らずcatchできるようにします。
try { changeImportantData(); } catch (PermissionException) { // catchした変数を使わないという意図が明白 echo "You don't have permission to do this"; }Prior art
7年前にあった似たようなRFCは、以下のように例外名も省略可能にするという理由で否定意見が多数でした。
try { foo(); } catch { bar(); }いっぽう、このRFCは肯定的な反応が多く、再検討の余地があります。
Backward Incompatible Changes
後方互換性を壊す変更はありません。
Proposed PHP Version(s)
PHP8.0
RFC Impact
特になし。
Vote
投票は2020/05/24まで、投票者の2/3の賛成で受理されます。
このRFCは賛成48反対1の圧倒的多数で受理されました。Patches and Tests
・https://github.com/php/php-src/pull/5345
References
感想
このRFCが意図しているところは決して例外の握り潰しなどではなく、例外処理のロジックに例外の中身を使わないときに省略できるというものです。
変数に受け取る処理がなくなるので速度も速まることでしょう。しかしですな、こんな機能があったら絶対にこんなコードを書く奴が出てくるわけですよ。
PHP8try{ foo(); }catch(Exception){}いやまあ、こんなの書いてしまう人は今でもやってると思いますけどね。
PHP7try{ foo(); }catch(Exception $e){}なら別にあってもいいか。
劇的に便利になるというわけではないですが、ちょっと気が利く書き方ができるようになりますね。
最後にもう一度言いますが、冒頭のコードは悪い例なので決して真似してはいけません。
- 投稿日:2020-05-25T19:53:13+09:00
初心者がPHPを勉強する方法
まずは他の言語を始める
PHPは色々な言語をごちゃ混ぜにしたような言語である。
なので、勉強をするなら混ざる前のものから手を付ける方が理解しやすい。C言語
C言語は文字列、構造体、ポインタが分かる程度のところまでやる。
関数ポインタまでやる必要はない。JavaやJavaScript等の他の言語をやったあとに戻ってくると何となく理解できるようになっている。https://php.net/fopen
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/fopen.3.htmlhttps://php.net/strcoll
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strcoll.3.htmlPHPはC言語からそのまま持ってきた関数が多いので、このようにC言語のマニュアルそのままの関数も多い。なので、何故このような関数になっているか?というような関数の成り立ちをPHPの中だけで考えても意味がない。PHPとしてファイル操作のための
fopen
を定義したわけではなく、単にC言語のfopen
をそのままPHPに持ってきただけなのだということを頭に隅に置きつつコードを書くべきである。
fopen
のr
やw+
は一見意味不明なインターフェースだが、C言語をやった後だと慣れで書ける。PHPを作ったラスマス・ラードフ氏は
https://gihyo.jp/news/report/2015/12/1401?page=4モジュール間での共通性には欠けるかもしれませんが,OCI拡張モジュールもMySQL拡張モジュールも,下で動いているC APIにほぼ1対1で対応している点において整合性があるといえます。MySQLドキュメントのC APIに関する部分を見てみれば,PHP開発者にとってはなじみ深く感じられるはずです。どの低レベル関数もPHP側の何かと対応しているからです。だからPHPは垂直方向に見れば整合性はあるのですが,多くの人から始終「PHPはまったく整合性がない」と言われます。整合性は確かにあるのです。ただ,人々が思っているような水平方向にはないというだけです。垂直方向に見れば,完璧な整合性があります。
このように言っている。
最近はバイナリセーフな関数が増えてきたとは言えるものの、まだバイナリセーフでない関数もある。初心者はそもそもバイナリセーフでないとはどういうことかと悩むところだが、C言語で文字列をやった後だと「あ〜Cのライブラリにそのまま渡してるからNULL終端なのね」ということがすぐに分かるだろう。
そのうち「あ、なんかC言語っぽい名前のマイナー関数だからバイナリセーフではないのかも?」と勘が働くようになるはずだ。ポインタや構造体は必須ではないが、PHPの参照を理解する手助けになる。あと
->
の記法に慣れる。
その後、C言語のつもりで参照を無駄に使って余計にパフォーマンスが下がるというところまでが1セットである。Java
PHPのオブジェクト指向はJava由来のものが多い。
デザインパターンやDIコンテナ、インターフェースの設計、DDD、これらクラスベースのオブジェクト指向に真面目に取り組むなら、PHPでやるよりもJavaでやった方が良い。
PHPのためのJavaと考えるならJava 1.4で十分なので、最近Javaが取り入れている関数型っぽい機能の部分はスルーしてオブジェクト指向に関するプログラムをやるのが良い。
静的型付け言語特有のAOPや汎用プログラムとしてのスレッド処理などもスルーして、クラスとインターフェースの基本的な部分をやる。共変性と反変性やジェネリクスについては現在のPHPではまず出てこないのでJavaでやっておいた方が良い。
ジェネリクスはPHP本体の機能としては使えないが、PHPStanのような静的解析ツールを使うことを考えると必要になる。それから、SPLの中では比較的よく使うイテレーター https://php.net/spl.iterators もJavaでやっておいた方が良い。
Perl
スクリプト言語での文字列処理と言えば正規表現であり、スクリプト言語での正規表現と言えばPerlである。
https://php.net/pcre
PCREとはPerl Compatible Regular Expressions、つまりPerl互換正規表現のことなので、歴史も例題もPerlで調べた方が早い。
そのためにはある程度は使えるようになっておく必要がある。Ruby
RubyというよりもRuby on Railsをサンプルアプリケーションを作れる程度にやっておく。
PHPに限らずWebアプリケーション用のフレームワークはRailsに影響を受けて作られたフレームワークがいくつもあるので、本家の思想を知っておくと理解が早い。かつ、他の言語のRails風フレームワークを使うときにもすぐに慣れることが出来るようになるメリットがある。JavaScript
Webアプリを作るにはどうせ必要になるのだから先にやっておく。
JavaScriptでクロージャに慣れておき、共通パターンの一部だけ変更するような処理をabstract classで実装するのかインターフェースを使うのかコールバックで実装するのかという選択肢を増やしておく。ちなみにPHPのシンプルな無名関数は
static function(){}
である。static
無しの場合は$this
がbindされる。
PHPの無名関数はbindTo
メソッドでthisの差し替えもできるので、JavaScriptも今風のライブラリは使わずに素のbind
やapply
に慣れておく。
===
演算子にも慣れておきたいところだが、これはJavaScriptではあまり使われないのかもしれない。PHPほど厳密な比較云々という記事を見かけない。その他
TypeScript, Haskell, Go, Rust等。
PHP8からunion型が使えるようになる、かもしれない。
静的型チェックをするなら現在でも使える。
そのためにunion型が使える言語をやる。
100
と100.0
と'100'
を全部受け取りたい といったときに後ろ向きな気持ちでint|float|string
と指定するのも有りと言えば有りだが、代数的データ型を使える言語で積極的に複数の型をアルゴリズムに組み込むのも良い。
public Left|Right $either;
が型レベルで保証されると、手抜きもしつつ安心感も増す。プログラムの習慣としてPHPで使えそうなものもある。
例えばfunction do_something($i) { if (!is_numeric($i)) return [null, 'invalid argument']; return [$i + 1, null]; } [$ret, $err] = do_something($_GET['i']);というようなエラー処理方法でも良いわけである。
使ったことが無ければどちらが良いかという判断もできないので、まずは他言語のパターンというものも知る必要がある。
trait
に関しては、他の言語の経験がむしろ混乱の元になるかもしれない。
PHPでは珍しく正統派の(?)ものなので、mix-inとは若干使い方が異なる。
trait
のサンプルはScalaではなくSmalltalkになるようだ。PHPを始める
ここまで来れば、PHP自体を一度も見たことがなくても大抵のPHPプログラムは読めるようになっている。
むしろPHP初心者を既に脱している。最近は
magic_quotes_gpc
といった致命的な落とし穴も無いので、ハマることも少ない。
PHPの終了タグ?>
は書かないであるとか、static
変数はリクエスト毎に破棄されるとか、そんな程度である。
PHPというプログラミング言語特有の勉強が別途必要だということは無い。
- 投稿日:2020-05-25T16:30:55+09:00
PHP_CodeSniffer で “Missing file doc comment” エラーが消えない場合の対処法
問題
VSCode に phpcs プラグインを導入1して WordPress PHP コーディング規約に PHP コードが準拠しているかのチェックを行っているのだが、以下のようなコードで、“Missing file doc comment” エラーが消えない場合がある。
<?php /** * ファイルのタイトル * * ファイルの説明. * * @category Components * @package WordPress * @subpackage テーマ名 * @author 名前 <foo.bar@example.com> * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 * @link https://example.com * @since 1.0.0 */ require_once ABSPATH . 'wp-admin/includes/plugin.php'; // 以下略確認してみると、どうやら
require_once
2 が原因となっているようである3。File doc comment 直後の関数宣言に doc comment がない場合にも “Missing file doc comment” エラーが出るようで4、
require_once
が関数宣言に類するものとして扱われているのではないかと思われる5。当然ながら
require
include
include_once
でも同様の問題が発生する。対処法 1(PHP 終了タグ & 開始タグを挿入)
File doc Comment と
require once
の間に PHP 終了タグと開始タグを挿入することで、エラーが出なくなる模様6。<?php /** * ファイルのタイトル * * ファイルの説明. * * @category Components * @package WordPress * @subpackage テーマ名 * @author 名前 <foo.bar@example.com> * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 * @link https://example.com * @since 1.0.0 */ ?> <? require_once ABSPATH . 'wp-admin/includes/plugin.php'; // 以下略対処法 2(なんらかのコメント挿入)
上記のように、(関数宣言に類するものとみなされているらしき)
require_once
の前に doc comment がないことが原因なので、なんらかのdoc comment を file doc comment とrequire_once
の間に挿入することでも、エラーは解消される。<?php /** * ファイルのタイトル * * ファイルの説明. * * @category Components * @package WordPress * @subpackage テーマ名 * @author 名前 <foo.bar@example.com> * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 * @link https://example.com * @since 1.0.0 */ /** * なんらかのコメント */ require_once ABSPATH . 'wp-admin/includes/plugin.php'; // 以下略対処法 3(require_once の記述位置を変更する)
require するファイルが file doc comment の直後で必要でないのなら、必要な箇所の直前に記述位置を変更することでも対処可能。
<?php /** * ファイルのタイトル * * ファイルの説明. * * @category Components * @package WordPress * @subpackage テーマ名 * @author 名前 <foo.bar@example.com> * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 * @link https://example.com * @since 1.0.0 */ // なんらかのコード require_once ABSPATH . 'wp-admin/includes/plugin.php'; if ( is_plugin_active( 'some-plugin/some-plugin.php' ) ) { // 略 } // 以下略対処法 4(require_once を即時関数で囲む)
冒頭で require したくて、とくにコメントも思いつかないなら、即時関数で囲むことでもエラーが回避できるし、問題なく動いた。いっそ場合によっては、file doc comment 以下すべてを即時関数で囲んでやってもいいかもしれない。
<?php /** * ファイルのタイトル * * ファイルの説明. * * @category Components * @package WordPress * @subpackage テーマ名 * @author 名前 <foo.bar@example.com> * @license https://www.gnu.org/licenses/gpl-3.0.txt GNU/GPLv3 * @link https://example.com * @since 1.0.0 */ ( function() { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } )(); // 以下略
Cf. VSCodeで「WordPress Coding Standards」に準拠してPHPの自動フォーマットする方法 | HPcode ↩
今回この問題が発生した例では、プラグインが導入されているか否かを
is_plugin_active()
7 で確認するために、pluguin.php を読み込んでいる。 ↩以下のいずれの対処法でも、関数宣言の doc comment 欠如にかんしても “Missing file doc comment” が消えたため(当然 “Missing doc comment for function” エラーは出るが)、この推測はおそらく正しい。 ↩
Cf. Missing file doc comment · Issue #693 · WordPress/WordPress-Coding-Standards ↩
- 投稿日:2020-05-25T15:28:21+09:00
PHPで型宣言
PHP7以降型宣言充実しており
7.4でかなり良くなり、型宣言する機会が増えたので、まとめ1.引数の型(タイプヒンティング)
// int型のみ function hoge(int $num) { echo $num; } // デフォルト値 function hoge(int $num = 0) { echo $num; } // null許容型 function hoge(?int $num)// int $num = nullも可 { echo $num; } // オブジェクト(ex.Laravel) function hoge(Request $request) { echo $request->hoge; }返り値(戻り値)
// int型のみ function hoge(int $num): int { return $num; } // null許容 function hoge(?int $num): ?int { return $num; } // オブジェクト(ex.Laravel) function hoge(Request $request): Request { return $request; } // void(returnしないメソッド) function hoge(Request $request): void { echo $request->hoge; }プロパティ(クラス変数)
class User { // intのみ protected int $age; // デフォルト protected string $name = 'hoge'; // null許容 protected ?string $address; // オブジェクト protected Request $request; }せっかく7.4使ってるのにいまだにタイプヒントすらないところも多々あるので積極的に使っていきたいところ
- 投稿日:2020-05-25T09:47:43+09:00
PHP で列挙型を作りたい
PHP で列挙型を作りたかったのでがんばって作ってみました。
以下が使用例とソースです。
使用例<?php class Size extends Enum { protected string $name; protected string $abbr; protected function __construct(string $name, string $abbr) { parent::__construct(); $this->name = $name; $this->abbr = $abbr; } public function getName(): string { return $this->name; } public function getAbbr(): string { return $this->abbr; } /** * @Enum */ public static function LARGE(): Size { return new static("大", "L"); } /** * @Enum */ public static function MEDIUM(): Size { return new static("中", "M"); } /** * @Enum */ public static function SMALL(): Size { return new static("小", "S"); } public static function TEST(): int { return 1; } } class Size2 extends Size { /** * @Enum */ public static function EXTRA_LARGE(): Size { return new static("超大", "XL"); } } var_dump( Size::values(), // 3つ Size2::values(), // 4つ Size::MEDIUM(), // Size.MEDIUM Size::MEDIUM()->name(), // "MEDIUM" Size::MEDIUM()->ordinal(), // 1 Size::valueOf("MEDIUM"), // Size.MEDIUM Size2::EXTRA_LARGE(), // Size2.EXTRA_LARGE Size2::EXTRA_LARGE()->name(), // "EXTRA_LARGE" Size2::EXTRA_LARGE()->ordinal(), // 0 Size2::valueOf("EXTRA_LARGE"), // "EXTRA_LARGE" Size::MEDIUM()->equals(Size::MEDIUM()), // true Size::MEDIUM()->equals(Size2::MEDIUM()), // false );ソース<?php interface IEnum { public function name(): string; public function equals(IEnum $other): bool; public function ordinal(): int; public static function values(): array; public static function valueOf(string $name): IEnum; } abstract class Enum implements IEnum { /** @var \ArrayObject<string, \ArrayObject<int, string>> */ private static array $methodNameMemo = []; /** @var \ArrayObject<string, \ArrayObject<string, Enum>> */ private static array $enumMemo = []; private int $ordinal; protected function __construct() { $this->init(); } /** * @return string */ public function name(): string { self::memorize(); return (string) array_search($this->ordinal, self::$methodNameMemo[static::class]); } /** * @param self $other * @return bool */ public function equals(IEnum $other): bool { return is_subclass_of($other, Enum::class) && (static::class === get_class($other)) && $this->ordinal === $other->ordinal(); } /** * @return int */ public function ordinal(): int { return $this->ordinal; } private function init(): void { self::memorize(false); $caller = debug_backtrace()[3]["function"]; if (!isset(self::$methodNameMemo[static::class][$caller])) { throw new \Exception(); } $this->ordinal = self::$methodNameMemo[static::class][$caller]; } /** * @return \ArrayObject<Enum> */ public static function values(): array { self::memorize(); return array_values(self::$enumMemo[static::class]); } /** * @param string $name * @return Enum * @throws \InvalidArgumentException */ public static function valueOf(string $name): Enum { self::memorize(); if (!array_key_exists($name, self::$enumMemo[static::class])) { throw new \InvalidArgumentException(); } return self::$enumMemo[static::class][$name]; } private static function memorize($memolizeEnumerators = true): void { $isMethodNameMemorized = !empty(self::$methodNameMemo[static::class]); $isEnumMemorized = !empty(self::$enumMemo[static::class]); if ($memolizeEnumerators && $isMethodNameMemorized && $isEnumMemorized) { return; } $class = get_called_class(); $reflectionClass = new \ReflectionClass($class); $reflectionMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_STATIC); $ordinal = 0; foreach ($reflectionMethods as $reflectionMethod) { $docComment = $reflectionMethod->getDocComment(); if (!self::hasEnumAnnotation($docComment)) { continue; } $methodName = $reflectionMethod->getName(); if (!$isMethodNameMemorized) { self::$methodNameMemo[static::class][$methodName] = $ordinal; $ordinal++; } if ($memolizeEnumerators) { self::$enumMemo[static::class][$methodName] = static::$methodName(); } } } private static function hasEnumAnnotation($docComment): bool { if (!is_string($docComment)) { return false; } foreach (preg_split("/(\r?\n)/", $docComment) as $line) { if (!preg_match("/^(?=\s+?\*[^\/])(.+)/", $line, $matches)) { continue; } $info = $matches[1]; $info = trim($info); $info = preg_replace('/^(\*\s+?)/', '', $info); if (trim($info) === "@Enum") { return true; } } return false; } }
- 投稿日:2020-05-25T08:08:26+09:00
【Laravel】 ネストしたEagerLoadを書いてみる
やりたいこと
以下のテーブル構成があったときに、
マンションテーブルの情報から郵便番号テーブルの住所名を取得したいときに、
ネストしたEagerLoadを使用することができたので、残します。マンションテーブル(apartments)
項目名 カラム名 外部キー id マンションID マンションの基本情報テーブル(apartment_infos)
項目名 カラム名 外部キー id マンションの基本情報のID apartment_id マンションテーブルのID apartments.id postal_code 郵便番号 postal_codes.postal_code 郵便番号テーブル(postal_codes)
項目名 カラム名 外部キー postal_code 郵便番号 name 住所名(愛媛県松山市...) 公式の説明
初歩の初歩なのか、あっさりと書かれていて、
私はぱっと見ただけでは理解出来ませんでした。実際のコード
モデル//マンションモデル class Apartment extends Model{ public function apartmentInfo() { //マンションの基本情報テーブルとの関係を明示 return $this->hasOne(ApartmentInfo::class,"id" , "id")->withDefault(); } } //マンションの基本情報モデル class ApartmentInfo extends Model{ public function postalCode() { //郵便番号テーブルとの関係を明示 return $this->hasOne(PostalCode::class,"postal_code" , "postal_code")->withDefault(); } } //郵便番号のモデル class PostalCode extends Model{ }コントローラpublic function sample(){ $apartments = Apartment::with( ["apartmentInfo", "apartmentInfo.postalCode"])->get(); var_dump($apartments->ApartmentInfo->PostalCode->name); }感想
最初は、郵便番号のモデルに、findするようなメソッドを用意していたが、
N+1クエリ問題が発生してしまうので、どうにかならないかなあと思っていたところ、改善できてよかった。
- 投稿日:2020-05-25T05:58:30+09:00
php(関数と引数)
関数と引数について理解したことを備忘録として記録します。
●ユーザー定義関数の型
function 関数名(引数1、引数2、…){
//処理内容
return値;
}
例)
<?php
function heikin($num1, $num2){
$result = ($num1 + $num2) / 2;
print $num1.'と'.$num2.'の平均は'.$result.'です';
}heikin(10, 8);
heikin(3, 23);・出力結果
PHPのテストです。10と8の平均は9です
3と23の平均は13です