カテゴリーアーカイブ PHP

著者:杉浦

PHPDocの内容を継承する@inheritdoc

Inheritance — phpDocumentor
 @inheritdocは継承元(implements元やextends元やtrait元)のPHPDocの説明を継承するタグです。@inheritdocを使うと次の様に継承元のPHPDocの説明文を受け継げます。
 
 元の振る舞いと継承後の振る舞いに大した違いが無い場合、同じ説明を記述することは面倒です。@inheritdocを使うことで何度も同じ記述を行わず済み、楽が出来ます。
 @inheritdocは継承元の説明をインラインに埋め込むコメントとして扱えます。冒頭の説明文の部分に関しては次の様にけっこう自由がききます。

  • この記事いいね! (0)
著者:杉浦

PHPの引数順と最初の開発者のインタビュー

 PHPStormには次の画像の様な機能、呼び出し先の関数の引数名を表示するという機能が備わっています。

 ことPHPにおいてこの機能は非常に役立ってくれています。というのもPHPに備わっている関数の引数はその順番と役割が関数によって変わることがしばしばあるためです。例えば、array_filter,array_map,array_walkという関数があります。これらはコールバックを配列に適用する関数です。これらをPHPStorm上で呼び出すと次の様な画面が見えます。

 各引数の名前で察する通り、array_mapのみcallbackを第一引数で指定する必要があります。これはいささか極端な例ですが、同じように似た役割、挙動の関数において引数の順番と役割が異なるということが起こることがあります。このため生のPHPを記述するためにはたびたびリファレンスなりPHPDocなりの何かを参照する必要があります。
 この関数の引数の順番のようなPHPの整合性についてPHPの生みの親であるラスマス・ラードフ氏は次のように述べています。
PHPの生みの親,ラスマス・ラードフ氏インタビュー:レポート|gihyo.jp … 技術評論社

私たちがPHPの整合性のなさを直さないと言ってよく非難する人がいますが,PHPはそもそもそれが問題になるようなものではないのです。PHPは結局,ライブラリやその他基盤となっているテクノロジーへのショートカットにすぎません。それなのに,どうして誰もがPHPレベルでの整合性を求めるのか,私には分かりません。整合性をとるのはフレームワークの役割です。フレームワークは問題を解決するための全体的なアプローチを定める場所ですから。PHPはその下のレベルにあって,ただ低レベルにあるライブラリとか関数とかへのアクセスを提供するだけの存在です。

 この発言の前にはPHPよりも下位層の関数と1対1で対応した何らかの関数をPHPは持っている、PHPは垂直的に整合性を持っている、という趣旨の発言があります。PHPは中間言語に近いもので更に上級言語のフレームワークを使えと言っているわけです。最近自分はLaravelばかり触っていますが、実際生のPHPに比べて格段に書きやすくシンプルにまとまっています。

  • この記事いいね! (0)
takahashi 著者:takahashi

Laravel環境をインポートしたときに”artisan package:discover handling the post-autoload-dump event returned with error code 1″エラーが出た時に確認したいこと

いろいろあって、開発中のLaravel環境をgit経由で再インポートしたのですが、再度npm installしてからcomposer installしたところ、こんなエラーが発生しました。

Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1

エラーで調べるとこんな記事が。

[Laravel] プロダクション環境にはLaravel Duskをインストールしない – 端くれプログラマの備忘録

Laravel Duskはブラウザでの自動テストAPIを提供するプラグインで、製品版(production)モードではセキュリティ上の理由で無効化しなければいけないため、productionモードで有効化されているとエラーになります。

記事の通りにcomposer.jsonを確認すると

...
"require": {
        "php": "^7.1.3",
        "ext-json": "*",
        "fideloper/proxy": "^4.0",
        "intervention/image": "^2.4",
        "lampager/lampager-laravel": "^0.3.0",
        "laravel/framework": "5.6.*",
        "laravel/tinker": "^1.0",
        "league/flysystem-aws-s3-v3": "^1.0",
        "maatwebsite/excel": "^3.1",
        "simplesoftwareio/simple-qrcode": "^2.0"
    },
    "require-dev": {
        "beyondcode/laravel-dump-server": "^1.0",
        "filp/whoops": "^2.0",
        "friendsofphp/php-cs-fixer": "^2.13",
        "fzaninotto/faker": "^1.4",
        "laravel/dusk": "^4.0",
        "mockery/mockery": "^1.0",
        "nunomaduro/collision": "^2.0",
        "phpunit/phpunit": "^7.0",
        "squizlabs/php_codesniffer": "^3.3"
    },
...

あれ、含まれてない(汗

ちゃんとrequire-devにしか”laravel/dusk”の項目は入っていません。
もしやと思ってcomposer.lockの方を確認すると…

...
"name": "laravel/dusk",
            "version": "v4.0.2",
            "source": {
                "type": "git",
                "url": "https://github.com/laravel/dusk.git",
                "reference": "9810f8609c8b53d9a3bac7d38c56530e0d77a6bb"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/laravel/dusk/zipball/9810f8609c8b53d9a3bac7d38c56530e0d77a6bb",
                "reference": "9810f8609c8b53d9a3bac7d38c56530e0d77a6bb",
                "shasum": ""
            },
...

含まれていました。
composer installすると
composer.lock
の記述が優先されます。

今回の原因は、composer.lockにその記述が入っていることが原因か…
と思いましたが、ここでふと気づきます。

(まてよ、そもそも開発を始めるときにdevelopモードにしていたはずじゃなかったっけ…)

そもそも今回のエラーはproductionモードにしていなければ出ないはずのエラーなのですが、それにもかかわらず出てしまっているので、よく考えたらおかしな状況です。
そこで調べたところ、.envファイルが入っていないことに気づきました。

前に使っていたLaravelのプロジェクトから.envファイルを書き戻してみると、

composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Nothing to install or update
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: fideloper/proxy
Discovered Package: intervention/image
Discovered Package: lampager/lampager-laravel
Discovered Package: laravel/dusk
Discovered Package: laravel/tinker
Discovered Package: maatwebsite/excel
Discovered Package: nunomaduro/collision
Discovered Package: simplesoftwareio/simple-qrcode
Package manifest generated successfully.

となり、先ほどのDuskのエラーは出なくなりました。

.envファイル内の
APP_ENV
の設定が
APP_ENV=local
または
APP_ENV=development
になっていないと、自動でproductionモードとして動作してしまい、Duskの部分でエラーになるようです。

つまり開発の際は.envファイルに
APP_ENV=local
または
APP_ENV=development
を記述するのが必須になってきますので、最初にLaravel環境を展開したときは要注意です。

  • この記事いいね! (0)
著者:杉浦

trait中の関数を差分プログラミングをする

 traitはクラス中に現れる共通のプロパティ、メソッドをまとめて記述するための仕組みです。コード中におけるその扱いは継承に似ています。継承を行った場合、スコープ定義演算子をもちいることによってコードをコピペせずにちょっとだけ処理を付け足すことが出来ます。

class ChildHoge extends ParentHoge
{
  public function __construct()
  {
    //親のコードの処理前に付け足す処理の部分
    parent::__construct();
    //親のコードの処理後に付け足す処理の部分
  }
}

 このように便利なスコープ定義演算子ですが、traitを指すことはできません。traitでこれに似たことをしたい時、名前の変換を行うことによって実現ができます。元のコードを別名として扱い、traitを読み込んだクラスで元々の名前のメソッドを実装することによってクラス外のふるまいを変えることなくちょっと付け足しただけの差分プログラミングができます。

class ChildHoge
{
  use FugaTrait {__construct as traitConstruct;}
  
  public function __construct()
  {
    //トレイトのコードの処理前に付け足す処理の部分
    $this->traitConstruct();
    //トレイトのコードの処理後に付け足す処理の部分
  }
}
  • この記事いいね! (0)
著者:杉浦

foreachとPHPDocとIDE

 IDEの補完機能は便利です。複雑なオブジェクトのプロパティ、メソッドを網羅し、入力をサポートしてくれます。
 
 これは変数の型を推論することと型の内容を把握することによって実現されています。ここでforeachを使った場合、次の様になる時があります。

 静止画のためわかりにくいですが、予測が出来なくなっています。これは推論が不能になったため起こっています。型推論の主な道筋はそれを導くために用いられた関数、演算子などによる型の連想です。ここで起こっていることはarrayの中に入っている値の型が推論できない、ということです。これを解決するためにPHPDocによる型の説明を用いることが出来ます。

/** @var Members $member */

 このPHPDocコメントを書くことによって$memberはMembers型であるとIDEに示すことができ、IDEは型による補完機能を動かしてくれました。

  • この記事いいね! (0)
著者:杉浦

phpの三項演算子の一種エルビス演算子

PHP: 比較演算子 – Manual

PHP 5.3 以降では、三項演算子のまんなかの部分をなくすこともできるようになりました。 式 expr1 ?: expr3 の結果は、expr1 が TRUE と同等の場合は expr1、 それ以外の場合は expr3 となります。

 phpの三項演算子は真ん中の部分を省略して記述することが出来ます。この記述?:はエルビス・プレスリーの顔文字にならってエルビス演算子と通称されています。google画像検索などではエルビス・プレスリーの写真に?:を当てはめた画像が大量にヒットします。エルビス演算子による記述を用いたコードは次の様になります。

$c = $a ? $a : $b;
$c = $a ?: $b;

 {}もelseも必要にならないが条件分岐が欲しいときはよくあります。特定の条件――次の例では投稿された記事の編集権限の有無――によって早期returnを促すガード節はエルビス演算子を用いることによって簡単に記述できます。

auth()->user()->hasAccess($edit_post) ?: abort('403');

 null合体演算子??は$hoge ?? $fugaでisset($hoge) ? $hoge : $fugaという式を表していたのに対し、エルビス演算子は緩やかな比較を用いたTRUEであるか否かの式を表します。$hoge ?: $fugaならば $hoge == true ? $hoge : $fugaといった具合です。これにより次の画像の様な違いが現れます。

  • この記事いいね! (0)
著者:杉浦

laravelのEloquentクラスのbootメソッド

 laravelはモデルの基盤をEloquentというクラスで定義しています。boot()はこのEloquentが呼び出されたときに走ることになる関数です。laravel5.6のソースコード(vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php)には次の様にあります。

    /**
     * The "booting" method of the model.
     * モデルの「初期起動」メソッド
     *
     * @return void
     */
    protected static function boot()
    {
        static::bootTraits();
    }

    /**
     * Boot all of the bootable traits on the model.
     * モデル上の起動可能なすべてのtraitを起動します
     *
     * @return void
     */
    protected static function bootTraits()
    {
        $class = static::class;

        $booted = [];

        foreach (class_uses_recursive($class) as $trait) {
            $method = 'boot'.class_basename($trait);

            if (method_exists($class, $method) && ! in_array($method, $booted)) {
                forward_static_call([$class, $method]);

                $booted[] = $method;
            }
        }
    }

 これにある様にbootはbootTraitsを呼びだしbootTraitsは頭にbootクラス名とあるtraitのメソッドを呼び出します。bootメソッドに追記する事、bootトレイトを作ることでモデル呼び出し時に様々なことが出来ます。特に便利な使い方はグローバルスコープです。グローバルスコープは次の様に記述する事でクエリビルダを呼び出す際に必ず記述したクエリがつくというものです。

    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope('auth_post', function (Builder $builder) {
            $post_key = Users::findOrFail(auth()->id())->post_key;

            $builder->where('post.primary_key', $post_key);
        });
    }

 上記のコードでは今ログインしているuserに関連するpostでwhereをかけています。ログインしているユーザに関してのみのデータを扱う、という処理は頻繁に現れます。このようなほぼ必ずするべき記述をモデルクラス内の各メソッドで何度も記述することは無駄であり、bootメソッドの利用はこれを解決します。

  • この記事いいね! (0)
著者:杉浦

PHPのNull合体演算子

 PHPはユーザの投げてきた値、DBから取得した値といった、存在するかも怪しい値を扱うことが多いです。この問題への対策の定番は条件文とissetです。issetは変数がセットされていること、そして NULL でないことを検査する関数です。
PHP: isset – Manual
PHP: PHP 型の比較表 – Manual
条件文とissetを用いて真面目に素朴な書き方をするならば次の様になります。

if(isset($_GET['user'])){
    $user = $_GET['user'];
} else {
    $user = 'nobody';
}

 5行になりました。この処理と同じ処理を何度も記述する場合、ソースコードは巻物の様に長くなります。条件文がシンプル、行う処理もシンプル、であるならば三項演算子がきれいに記述をしてくれます。三項演算子を用いた例は次になります。

$user = isset($_GET['user']) ? $_GET['user'] : 'nobody';

 1行に収まりずいぶんましになりました。しかしながら、ある値が存在するならばその値を扱うという処理は定番であり、三項演算子を用いた場合、二重にある値――例ならば$_GET[‘user’]の繰り返し――が冗長です。Null合体演算子はこれを解決します。Null合体演算子による記述は次です。

$user = $_GET['user'] ?? 'nobody';

PHP: 比較演算子 – Manual
 もうこれ以上ないくらいシンプルな記述になりました。最初のif文と同じ働きをしますが行数は1行、横の長さも大して変わりません。長大な参照名がついてしまっていても変数を増やさず二行で書けます。

$user_point_total = User::findOrFail($_POST['primary_key'])->userPointTotal->point_total
   ?? UserPointTotal::$default_points;
  • この記事いいね! (0)
著者:杉浦

laravelのモデルであるEloquent ORMのCRUDに使うための便利な機能

Eloquent:利用の開始 5.6 Laravel
 Eloquent ORM(Object Relational Mapping)はPHPのフレームワークlaravelで実装されているSQLによるデータ操作をするモデル部分のことです。laravelでモデルを記述する際、多くの場合で以下の様に継承をします。

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

 このIlluminate\Database\Eloquent\Model;の中にあるCRUDに使える特に便利な機能を少し紹介します。少し詳しくは冒頭のリンクの説明書、とても詳しくはソースコード参照です。laravelのソースコードはとても丁寧で読みやすいためソースコードだけでも理解が進みます。
 全件取得をする時は次の様に記述できます。

$flights = Flight::all();

 これでSELECT * FROM Flights;の結果をCollection(配列の拡張形式)に変換した相当です。カラムを絞ることも簡単です。

$flights = Flight::all('primary_key', 'created_at');

 これでSELECT primary_key,created_at FROM Flights;の結果をCollection(配列の拡張形式)に変換した相当です。この様によく使われるパターンにはシンプルな回答が大体用意されています。
 ある1件を主キーを頼りに取得する時は次の様に記述できます。

$flight = Flight::find(1);

 これでSELECT * FROM Flights WHERE id = 1;の結果をモデルクラスFlightに変換した相当です。findした場合、対象が存在しないことも考えられます。そのためfindOr***の形で処理が用意されています。

$flight = Flight::findOrFail(2);
$flight = Flight::findOrNew(2);

 前者は見つからなかった場合、404エラーがユーザに返されます。後者は見つからなかった場合、新たにレコードを作成します。
 レコードを作成する時は次の様に記述できます。

$new_flight = [
    'name' => 'hoge',
    'mail' => 'fuga@example.com'
];
Flight::create($new_flight);

 create([‘カラム名’=>値の連想配列])これだけで新たにレコードがFlightsテーブルに追加されます。
 更新する時は次の様に記述できます。

$update_data = [
    'name' => 'fuga',
];
$flight = Flight::findOrFail(1);
$flight->fill($update_data)->save();

 これもcreateに似ています。fillでは渡されたカラム名のデータのみを更新します。createと違ってこちらはより複雑な操作を繰り替えすことも考えられており、save()と命令する必要があります。
 論理削除をする時はモデルにtraitを増やす必要があります。増やさない場合は物理削除です。

class Flight extends Model
{
    use SoftDeletes;
}

 実際に削除する時は次です。

$flight = Flight::findOrFail(1);
$flight->delete();

 これで作成、読み取り、更新、削除でCRUD制覇です。いずれも非常にシンプルに記述でき、大変便利です。

  • この記事いいね! (0)
著者:杉浦

php-cs-fixerの設定まとめページphp-cs-fixer-configuratorの紹介

 php-cs-fixer-configuratorphp-cs-fixerの設定をまとめているページです。php-cs-fixerはphpのコードを設定に従って修正してくれるアプリケーションなのですが、大本の設定マニュアルは羅列という言葉が似あうもので読むのが億劫です。php-cs-fixer-configuratorはphp-cs-fixerの設定を読みやすい形でまとめているページです。
 使い方はいたって簡単、次の画像からぱっと見でわかる様に検索して目的のルールを探すだけです。

 各ルールの詳細な説明もあります。次の画像らはblank_line_after_namespaceの説明です。これは名前空間の宣言の後には一行だけ空白行が入る、というルールです。This fixer is not configurableは設定不能というよりも設定不要と読んだ方がいいでしょう。ルールの有効無効だけが選択できます。
 


 プリセットに何が含まれているかを確認することも容易です。

  • この記事いいね! (0)