PHP – 関数 – 無名関数/クロージャ―/コールバック

基本

無名関数(anonymous function)はfunction()で定義。無名関数はクロージャ―(closure)とも呼ばれる。

戻り値を返す例。

引数をとる例。

use~エンクロージャーの変数

クロージャ―の外側(エンクロージャー)のスコープの変数をクロージャ―内で使う場合は、useを加える。

useで持ち込まれるエンクロージャーの変数の値は、関数定義前に定義された内容。上の例では$greetに関数が定義される前の'Hello'が使われていて、実行前の'Hi'は使われない。

コールバック

無名関数を使ったコールバック

以下の関数は、引数$callbackを関数として想定し、その引数に$nameを与えた結果を表示する。

$nameに文字列'Jane'を与え、$callbackに無名関数を与えて実行。この無名関数は引数の$nameを使って"Hi, $name"とい文字列を返す。

$callbackに違う動作をする無名関数を与えて実行。この無名関数は引数の$nameを使って"Hello $name. How arey you?"という文字列を返す。

変数に定義した無名関数のコールバック

コールバックを都度無名関数で定義するのではなく、あらかじめ変数に定義しておいた関数を渡すことができる。

これによって、メッセージのディスパッチなどが柔軟に行えるようになる。

可変関数によるコールバック

コールバックを可変関数で実装した例が以下の通り。

なお、無名関数を変数に代入した場合の変数名とfunctionで定義した関数名は重複してもエラーとならない。

array_map()の例

コールバックを使った内部関数にarray_map()がある。

array_map($callback, $array);

コールバックで直接無名関数を定義し、配列の各要素の自乗を要素とする配列を計算させる。

もちろん引数のコールバックに、変数に定義した関数や可変関数を用いることもできる。

 

PHP – 関数 – 可変関数

基本

可変関数は、変数に格納された文字列を関数名として解釈して実行するもの。

変数の後に()がある場合、PHPは変数の値の文字列を関数名として実行しようとする。

文字列に対応する関数が存在しない場合や値が文字列でない場合にはエラーとなる。

言語構造の場合

echoevalといった言語構造については、可変関数として使えない。これらを使う場合にはラッパー関数を定義する。

ビルトイン関数

ビルトイン関数は使用可能。

メソッド

クラスのstaticメソッドやインスタンスメソッドに対しても使用可能。

 

PHP – 関数 – 可変長引数

…$args

関数の引数を...変数名のように指定すると、任意の個数の変数を渡すことができる。

受け取った関数では、配列$argsの各要素に引数の値がセットされている(要素数が0や1の場合も配列)。

型の混在

上の例ではstring型を指定して全ての引数を文字列として扱っていて、すべての値がstring型である(あるいはキャスト可能である)必要がある。

型を指定しない場合は、与えられたそれぞれの引数の型のままで配列に格納される。

単純引数との混在

最後尾は可能

通常の引数が並んだその後、最後尾に可変長引数を置くことができる。

可変長以外の引数はデフォルト値がなければ省略できないが、可変長引数は0個でもよい。

先頭や中間は不可

可変長引数を先頭や単純引数の間に置くことはできない。

逆の使い方

複数の引数を...$arrayで受けると、それらを要素とした$arrayが得られる。逆に$arrayが配列の時...$arrayは分解された各要素を返す

これらはコレクションでもないばらばらの値なので、echoで出力したりforeachで使うことはできない。

以下の例では、配列を1つ1つの要素に分解して、それらを引数に与えて最大値・最小値を得ている。

以下の例では、2つの引数を配列の要素として準備して関数に与えている。

 

PHP – 関数 – デフォルト引数

概要

  • 関数定義時に引数のデフォルト値を指定することができる
  • デフォルト値を指定した引数は、関数呼び出し時に省略することができる
  • 関数呼び出し時に指定された引数は、定義された引数の順番に充てられる
    • PHP8.0以降は名前付きを使うことができるため、必要な引数のみ値をセットできる

最後の関数呼び出しでは、int $dices2を、int $trials','をセットすることになるので、2つ目の引数の型が合わずにエラーとなる。

2つ目の引数$trialsの型指定をしていなければ、','は数値表現ではないので0とみなされ、以下のような結果になる(各試行の際に必ず1度サイコロが降られる)。

 

PHP – 関数 – 引数の型指定

概要

  • 新しいバージョンのPHPでは関数の引数の型を指定できる
  • ただし型チェックの前にキャストが試みられて、その結果が指定された型に合致しない場合はTypeErrorが出る
  • なので以下のような場合は(少なくとも型チェックでは)エラーにならない
    • 文字列型の引数に数値を与える場合
    • 数値型のタイプが異なる場合
    • 数値型の引数に与える文字列が数値として解釈可能な場合
  • これらの挙動にdeclare(strict_types=1)は影響しない

通常は型チェックされない

以下のコードをコンソールで実行する。動作確認のため引数範囲のチェックは行っていない。

挙動は以下の通り。

  • 文字列型を想定した引数に数値型の値をセットすると、文字列型にキャストされる
  • 整数型を想定した引数に実数型の値をセットすると、整数型にキャストされる
  • 数値型を想定した引数に文字列型をセットすると、
    • 数値に変換できる場合はその数値にキャストされる
    • 数値に変換できない場合は0として扱われる

引数の型を明示できるが一部キャストされる

以下のように引数の型を明示する。

挙動は以下の通り。

  • string型の引数にint型をセットすると、string型にキャストされる
  • int型の引数にfloat型の値をセットすると、int型にキャストされる
  • int型の引数にstring型の値をセットすると、
    • 数値表現の文字列ならキャストされる
    • 数値表現の文字列でないならTypeErrorになる

なお、ファイルの冒頭にdeclare(strict_types=1)を加えても結果は変わらない。

 

PHP – 変数のブラウザー表示

 

概要

PHPをサーバー上で実行する場合、変数の内容をブラウザーに表示させるいくつかの方法を整理。いずれの方法でも改行にPHP_EOLが使われるので、ブラウザー上では改行として機能せず空白文字になる。

var_export()print_r()については、結果の文字列を表示させるのではなく、戻り値として扱うことができる。

jason_encode()は趣旨が少し違うが一応加えておく。

コンソールに表示する場合に対して、PHP_EOLが改行として機能せず空白文字になる。

準備

以下のような変数・値を表示させる。

var_dump

var_dump()は引数の変数の内容を型まで含めて詳しく表示する。少し冗長。

  • 変数ごとの表示後や配列の要素間に改行コードが入るが、ブラウザー上では空白文字になる
  • 変数を複数指定しても、別々に実行したのと同じ結果
  • キー・値の文字列はダブルクォートで囲まれる
  • 実数はfloat[1]の様に表示される

var_export

var_export()は、var_dump()より簡潔な形で表示する。

  • 改行コードは全て空白文字になる
  • 結果をPHPのソースにコピー・ペーストして使える(改行が空白文字となっていてもok)
  • キー・値の文字列はシングルクォートで囲まれる
  • 実数は小数部がゼロでも1.0のように表示される
  • 配列の各要素末尾にカンマが付く

print_r

print_r()var_export()より更に簡潔に表示する。

  • 改行コードは全て空白文字になる
  • 文字列はクォートで囲まれない
  • 実数の小数部がゼロの時、整数と同じ表示になる
  • 配列の要素末尾にカンマは付かない

json_encode

変数の内容をJSON形式で返す。オプションのJSON_UNESCAPED_UNICODEを指定しないと、マルチバイト文字がHex表示になる。

以下の様にすれば、'\n'が改行としては機能しないが空白にはなる。

 

PHP – 変数の文字列表現

概要

特にデバッグ段階を意図して、変数の内容を文字列化する方法を整理する。

単純な文字列化

単純変数

単純変数はそのまま出力したり文字列連結したりできる。

配列

配列は自動的には文字列化されない。

implode()関数を使えば、指定したセパレーターで配列の文字列化は可能。

print_r

print_r()関数は配列も含めて変数の内容を文字列化して表示するが、第2引数にtrueを指定することで、文字列化された変数の内容を戻り値とするように変更できる。

単純変数のみPHP_EOLが末尾に付加され、配列の末尾にはPHP_EOLは付かない。

var_export

var_export()関数は配列も含めて変数の内容を文字列化して表示するが、第2引数にtrueを指定することで、文字列化された変数の内容を戻り値とするように変更できる。

単純変数・配列に関わらず、末尾にPHP_EOLは付加されない。

 

PHP/Laravel – console.log

ちょっと小回りが利かない

webアプリケーション開発の過程で変数の内容をちょっと確認したいとき、Laravelならdump()dd()が使えるが、エラーの場合は内容が確認できなかったり、確認の都度実行を停止しなければならなかったりする。

実行させながら変数の内容を表示させるには、var_dump()などの関数でブラウザーに内容を表示させるのが一般的だが、表示が崩れ、ページが遷移すると確認できない。

JSを使った簡易な対応

Railsのようにサーバーコンソールへの出力やJavascriptのconsole.logのように、ページとは別のところで変数の内容を確認したいとき、標準出力に振り向けるような設定もあるようだが、以下のコードでとりあえずブラウザーのコンソールへの表示ができる。

文字列や配列の表現中にシングルクォートやダブルクォートが含まれるとエラーが出る場合もあるので、変数を結合するときにjson_encode()を使っている。

実行例

以下はPHPでの実行例で、上のコードを関数化している。Laravelならヘルパー、staticクラス、staticメソッドのトレイトなどで準備することが考えられる。

以下は実行の結果生成されたHMTL例で、HTMLドキュメント出力の前段だとhead要素内、出力後だとbody要素内にscript要素が配置される。

以下はコンソールへの出力部分。これで実行を止めずに変数の確認ができる。

Laravelの場合

コントローラーのメソッドに定義

コントローラーでメソッドを定義する方法で可能。

  • コントローラーのstaticメソッドでconsole_log()を定義
  • コントローラー内は$this->console_log()で呼び出し
  • ビューでは{{ namespace\controller::console_log() }}で呼び出し

コントローラーの例

ビューの例

ルーティングからConsoleLogController@show()が実行されると、ブラウザーにビューが表示され、F12キーで以下の表示が確認できる。

ヘルパーコントローラーを作成する方法

以下のようにヘルパーコントローラーをapp/Http/Controllers下に作成し、staticメソッドとしてconsole_log()を定義。

php artisan make:controller HelperController

コントローラーのアクションでは以下の様に呼び出す。

ビューでは以下の様に呼び出す。

 

Vagrant/Linux環境でPHPサーバーを起動する

概要

VagrantのLinux上でPHPのビルトインサーバーを立ち上げる。

ゲスト側IPアドレスの確認

Vagrant環境下でip aifconfig -aを実行してネットワークインタフェイスのアドレスを確認する。

サーバーの起動

ビルトインサーバーの起動コマンドは以下のとおり。

php -S ホスト:ポート -t ドキュメントルート
php --server ホスト:ポート --docroot ドキュメントルート

ホスト部分に、確認したIPアドレスを指定する。ドキュメントルートに、実行ファイルが置かれる場所を指定する。

ブラウザーでの表示

ブラウザーでのURL指定は以下のとおり。

 

Laravel – ログイン処理の流れ

ログインフォーム表示

ルーティング定義

ルーティングファイルにAuth::routes();を記述することでユーザー認証関係のルーティングが自動設定される。その中には、以下のログインフォームへのルーティングが含まれる。

  • メソッド:GET
  • URL:login
  • アクション:Auth\LoginController@showLoginForm
  • ルート名:login

showLoginFormアクション

LoginControllerコントローラーはAuthenticatesUsersトレイトを利用していて、その中でshowLoginFormアクションが定義されている。

AuthenticatesUsersトレイトのshowLoginForm()アクションメソッドは以下の通りで、auth.registerビューをレンダリングしている。

auth.registerビューは初期状態では存在しないのでコマンドで生成するかカスタムで作成する必要がある

フォームからのPOSTとログイン実行

ルーティング定義

ルーティングファイルにAuth::routes();を記述することで、以下のルーティングが定義される。

  • メソッド:POST
  • URI:login
  • アクション:Auth\LoginController@login

loginアクション

showLoginFormアクションと同じく、loginアクションもAuthenticatesUsersトレイトで定義されている。

このアクションの動作は以下の通り。

バリデーション

最初はログインフォームのバリデーション。

  • validateLogin()メソッドは同じAuthenticatesUsersトレイトで定義されている

連続エラー時の抑止処理

次はログインエラーが連続したときのログイン抑止処理。ここで捕捉されなければ後段のログイン認証に移る。

  • ここで呼ばれているhasTooManyLoginAttempts()fireLockoutEvent()sendLockoutResponse()の各メソッドは全てThrottlesLoginsトレイトで定義されている
    • hasTooManyLoginAttempts()はログイントライ回数のがmaxAttempts()(デフォルトでは5回)を越えているかどうかをチェック
    • fireLockoutEvent()Lockoutイベントを発火させる
    • sendLockoutResponse()は一定時間ログインを抑止する

      表示例。秒数は30秒程度で試行ごとに変化した。

      Too many login attempts. Please try again in 37 seconds.

認証チェック

上記で、バリデーションと連続トライ回数以内の確認が通ったなら、attemptLogin()で認証チェックを行う。

  • attemptLogin()は同じAuthenticatesUsersトレイトで定義されている。

    • $this->guard()ファサード関係の複雑な処理を経て、\Illuminate\Auth\SessionGuardクラスのインスタンスとなる
    • $this->credentials()は以下のように定義されていて、ユーザー名とパスワードが認証に使われる
    • SessionGuard::attempt()は以下のように認証・ログインを行う
    • SessionGuard::login()メソッドでは、セッション更新、Rememberトークン処理、ユーザーのセットなどが行われる

ログイン失敗時のカウント

ログイン失敗時には失敗回数をカウントアップする。この結果は連続エラーのチェックで参照される。