PHP – 関数 – 引数の型指定

概要

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

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

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

挙動は以下の通り。

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

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

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

挙動は以下の通り。

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

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

 

PHP – 変数のコンソール表示

概要

PHPをコマンドラインで実行する場合に、変数の内容をコンソールに表示させるいくつかの方法を整理。JavaScriptのconsole.log()に比べて、何となく帯に短し襷に長し。個人的には使うとしたらview_export()か。

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

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

変数の内容をコンソールではなくブラウザーに表示する場合はこちら

準備

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

var_dump

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

  • 変数ごとの表示後に改行される
  • 変数を複数指定しても、別々に実行したのと同じ結果
  • キー・値の文字列はダブルクォートで囲まれる
  • 実数はfloat[1]の様に表示される

var_export

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

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

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

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

json_encode

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

何とか'\n'を改行として表示するなら、以下の様にするか。

 

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 – RegisterController

概要

RegisterControllerは、Laravelのプロジェクト開始時にapp/Http/Controllers/Authディレクトリー下に生成される。

ルーティングファイルにAuth:routes()を記述することで、このコントローラーの各アクションは以下のようにURLと結び付けられる。

method URL action name
GET /register showRegistrationForm register
POST /register register

ソースコード

内容

コントローラーの機能

ソースコード冒頭のコメントの要旨は以下の通り。

  • このコントローラーは、新規ユーザーの登録、入力バリデーション、データ作成とともに行う
  • デフォルトではこの機能をIlluminate\Foundation\Auth\RegistersUsersトレイトで実装し、追加のコードなしで上記の機能を提供

RegistersUsersトレイトの導入

登録後の遷移先

登録後の遷移先を設定している。

コントローラーの生成

コントローラー生成時のコンストラクター。

入力バリデーション

メールアドレスをユーザーIDとして使うため、入力バリデーションでemailuniqueの制約をかけている。

Userモデルインスタンスの作成

ユーザー登録時、パスワードにHash()メソッドを適用して暗号化している。

 

Laravel – $this->guard()

 

ログイン認証の例

たとえばログイン認証のLoginControllerに使われるIllumination\Foundation\Auth\AuthenticatesUsersトレイトlogin()メソッドは次のような内容。

この中で呼ばれているログイン認証処理のattemptLogin()メソッドは、同じトレイトで定義されている。

ここでguard()というメソッドが出てくる。これは同じAuthenticatesUsersトレイトの最後の方で以下のように定義されているが、そもそもどういうものかよくわからなかったので掘り下げてみたら、どんどん深みにはまってしまった。

guard()が見当たらない

AuthenticatesUsersトレイトでAuth::guard()が呼ばれているので、まずAuthクラスについて調べてみる。

AuthクラスはAuthenticatesUsersトレイトの最初で以下のようにuse定義されている。

そこでIlluminate\Foundation\Authクラスを見てみると、コメントにstaticメソッドらしきものが書かれているが、クラス定義の本体にはguard()メソッドが見当たらない。

Authクラスの継承元のIlluminate\Support\Facades\Facadeクラスも見てみたが、useしているトレイトも含めてguard()メソッドは定義されていない。

マジックメソッド__callStatic()を使っている

__callStatic()の内容

いくつかのサイトを参考にしたところ、__callStatic()というマジックメソッドを使っていることがわかってきた。このマジックメソッドは、クラスに定義されていないstaticメソッドが呼ばれた際に実行される。

Facadeクラスを見てみると、以下のように__callStatic()が定義されていた。

最初にあったAuth::guard()スタティックメソッドを呼ぶと、このマジックメソッドが呼ばれ、引数の$method'guard'$argsは空の配列となる。

以下で、この__callStatic()の内容を読んでいく。

getFacadeRoot()

メソッドの最初でgetFacadeRoot()メソッドが呼ばれている。最終的に$instanceにはAuthManagerのインスタンスが返されるが、その道筋を追ってみた。最後の部分だけがまだ理解できておらず、参考サイトの受け売りだが。

このメソッドは同じFacadeクラスで定義されていて、getFacadeAccessor()を引数に与えたresolveFacadeInstance()の戻り値を返している。

getFacadeAccessor()

ここでstaticメソッドgetFacaceAccessor()は、Facadeクラスとこれを継承したAuthクラスの両方で定義されている。

FacadeクラスでのgetFacadeAccessor()の定義は、単に「メソッドが定義されていない」という例外を返す。

一方、Facadeを継承したAuthクラスで定義されたgetFacadeAccessor()は文字列'auth'を返す。

getFacadeRoot()Facadeを継承したAuthクラスから呼ばれると、AuthクラスのstaticメソッドがFacadeクラスのstaticメソッドをオーバーライドして実行される。

この結果、resolveFacadeInstance()の引数には文字列'auth'が与えられる。

resolveFacadeInstance()

引数に'auth'を与えられたresolveFacadeInstance('auth')の挙動を見てみる。

流れは以下の通り。

  • 引数$nameの内容は文字列なので、1つ目のifブロックはスルー。
  • static::$resolvedInstanceFacadeで定義されていて、$nameに対応したインスタンスがあればそのインスタンスが得られる。
  • もしインスタンスが登録されていなければ、最後のifブロックでstatic::$app['auth']の内容が登録されて、そのインスタンスが返される。

この$app['auth']がわからなかった。こちらのサイトの内容をひとまず丸呑みする。

  • static::$appはヘルパーメソッドapp()と同じ動作をする
    • static::$app[‘auth]app()[‘auth’]と同じ意味
    • これはサービスコンテナからauthサービスのインスタンスが起動させる下記のコードと同じことを表す
  • app()->make(‘auth’)で作成されるサービスはIlluminate\Auth\AuthServiceProviderregisterメソッドによって登録されており、Immulminate\Auth\AuthMangaerがインスタンス化されている

__callStatic()の戻り値

$instanceが適正にセットされれば、_callStatic()は以下のように戻り値を返す。

__callStatic()が呼ばれたときの$methodguard()、引数は空だったので、戻り値のメソッドはAuthManager::guard()となる。

guard()はSessionGuard()になる

AuthManager::guard()

結局、attemptLogin()->guard()AuthManager::guard()となり、その内容は以下の通り。

引数なしで呼ばれるので、$nameAuthManager::getDefaultDriver()となる。その内容は以下の通り。

このメソッドは、configディレクトリーにあるauth.phpのdefaultsのキー'guard'に対する値を返す。デフォルトでその内容は'web'となっている。

そして、最後のreturn文にある$this->guards[$name]$this->guards['web']となるが、これが存在しない場合は??演算子の右側が評価されて、AuthManager::resolve('web')が内容としてセットされる。

AuthManager::resolve()

AuthManager::resolve()の内容は以下の通り。

最初の実行文で呼ばれるgetConfig('web')により、config/auth.phpguardsセクションの'web'キーに対応する値が$configとしてセットされる。

auth.phpの該当する部分は以下の通り。

そして、$driverMethodに文字列連結で生成された文字列'createSessionDriver'が格納される。

createSessionDriver()メソッドはAuthManagerに存在するので、ifブロック内の$this->createSessionDriver($name, $config)が実行される。

createSessionDriver()を見ると、$guardとして\Illuminate\Auth\SessionGuardクラスのインスタンスが格納される。

SessionGuardクラスはattempt()メソッドを持つので、最初のattemptLogin()メソッドは以下を実行していることになる。

参考サイト

 

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トークン処理、ユーザーのセットなどが行われる

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

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

 

Laravel – ユーザー登録処理の流れ

登録フォーム表示

ルーティング定義

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

  • メソッド:GET
  • URL:register
  • アクション:Auth\RegisterController@showRegistrationForm
  • ルート名:register

アクション

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

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

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

フォームからのPOSTと登録実行

ルーティング定義

ルーティングファイルへのAuth::routes();の記述によって、以下のルーティングも定義される。

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

アクション

showRegistrationFormアクションと同じく、registerアクションもRegisterControllerでuseされるRegistersUsersトレイトで定義されている。

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

  • マスアサインメント$request->all()のための$fillableapp\User.phpモデルで定義しておく
  • 入力されたフィールド群を検証するvalidator()メソッドで検証条件を定義
    • validator()RegistersUsersトレイトを利用する親元のRegisterControllersで定義
    • 戻り値のValidatorインスタンスのvalidate()メソッドで検証実行

  • RegistersUsersトレイトを利用する親元のRegisterControllercreate()メソッドを呼んでデータ作成
  • そのUserインスタンスを与えて登録済みのイベント発行

  • $this->registered()は登録確認のようだが、同じトレイトで空で定義されている
  • このため三項演算子のfalse側に書かれているredirect($this->redirectPath())returnの戻り値となる
  • redirectPath()RegistersUsersトレイトでさらにuseされたRedirectsUsersトレイトで定義されていて、
    • redirectTo()メソッドが定義されていれば実行
    • $redirectTo属性が定義されていればそこにリダイレクト
    • いずれでもなければ/homeにリダイレクト
  • 初期状態ではこのトレイトの親元RegisterController$redirectToが定義されているので、この内容(RouteServiceProvider::HOME)にリダイレクトされる