- 投稿日:2020-01-26T20:29:32+09:00
【徒然なるままに】2020年最新、WEB勢力図を三国志にしてみた
今は昔
一昔前はTwitterのScala/FacebookのReactが猛威を振るっていた時期がありましたが、
イキリ勢力の消耗戦で、最近はVue勢がかなり盛り上がっている印象を受けます。かつてのJQuery、それがVue
そんな予感がひしひしとします。
そこで、最新のWEB勢力図を三国志風にしてみました。
三国志の知識はマンガとゲームとWikiです、すみません。全く知らないキッズは中田さんのYoutube大学をご覧ください。
【三国志】第一話〜英雄たちの夜明け〜ついに授業リクエストNo.1の超大作魏
- 曹操(PHP) … 何でもあり最強、女を侍らせる。
- 夏侯惇(Laravel) … ロマンチスト
- 司馬懿(Vue) … 超現実主義、パクリもありあり
呉
- 孫権(Ruby) … なんとなく
- 陸遜(Rails) … 堅実な感じ
- 周瑜(Angular) … 超優秀だがぱっとしない
蜀
- 劉備(Scala) … 形式(礼)に拘る、前漢(Java)の末裔
- 関羽(Playframework) … RESTを世に広めた貢献者
- 孔明(React) … 革新者、新時代を拓く智彗者
後漢、晋は図の通りです
面白そうなので、図はクリエイティブ・コモンズ(表示—非営利—継承)にしときます。
勝手に改編(罵倒)してCCでお願いします。Reactはreact-routerとhooksでものすごい簡単になったので、
是非、新時代を体験してください。新型Reactとは
簡単に書くと、これだけです。
このアトムを融合していくだけです。const 関数 = () => { ~ステート式ES6スクリプト~ render ( <div>~</div> ) } export default 関数
- 投稿日:2020-01-26T20:29:32+09:00
【徒然なるままに】2020年最新、WEB技術の勢力図を三国志にしてみた
今は昔
一昔前はTwitterのScala/FacebookのReactが猛威を振るっていた時期がありましたが、
イキリ勢力の消耗戦で、最近はVue勢がかなり盛り上がっている印象を受けます。かつてのJQuery、それがVue
そんな予感がひしひしとします。
そこで、最新のWEB勢力図を三国志風にしてみました。
三国志の知識はマンガとゲームとWikiです、すみません。全く知らないキッズは中田さんのYoutube大学をご覧ください。
【三国志】第一話〜英雄たちの夜明け〜ついに授業リクエストNo.1の超大作魏
- 曹操(PHP) … 何でもあり最強、女を侍らせる。
- 夏侯惇(Laravel) … ロマンチスト
- 司馬懿(Vue) … 超現実主義、パクリもありあり
呉
- 孫権(Ruby) … なんとなく
- 陸遜(Rails) … 堅実な感じ
- 周瑜(Angular) … 超優秀だがぱっとしない
蜀
- 劉備(Scala) … 形式(礼)に拘る、前漢(Java)の末裔
- 関羽(Playframework) … RESTを世に広めた貢献者
- 孔明(React) … 革新者、新時代を拓く智彗者
後漢、晋は図の通りです
面白そうなので、図はクリエイティブ・コモンズ(表示—非営利—継承)にしときます。
勝手に改編(罵倒)してCCでお願いします。Reactはreact-routerとhooksでものすごい簡単になったので、
是非、新時代を体験してください。新型Reactとは
簡単に書くと、これだけです。
このアトムを融合していくだけです。const 関数 = () => { ~ステート式ES6スクリプト~ render ( <div>~</div> ) } export default 関数
- 投稿日:2020-01-26T19:03:28+09:00
PHPでお問い合わせフォームを作る その3 入力チェック
はじめに
このお問い合わせフォームを作ろうシリーズですが、第1回は最低限のデータ引き渡しが出来るようにしました。
第2回ではPHPファイルとhtmlファイルを分けるようにしましたなんちゃってテンプレート。で、第3回は何をやるかというと、入力画面で入力されたデータのチェックを行おうと思います。
入力チェックとは
上図のように画面は「入力画面」「確認画面」「完了画面」という風に進んでいきます。
確認画面から入力画面に戻ることも考えなければいけませんが、今時点では考えないものとします。で、どこで入力チェックをするのかというと「確認画面」と「完了画面」です。
第1回第2回で作成したプログラムのファイル名で言うと confirm.php と complete.php です。なぜチェックするの?
プログラムで必要だから
では、なぜ入力チェックをするの?と言うことですが、まず何も入力されなかったらこのプログラムの意味がありません。このプログラムでは最後の完了画面でメール送信しています。メールアドレスが入力画面で入力されなかったらメールの送り先が存在しない事になります。また、メールアドレスが入力されていても正しいメールアドレス形式でないとメール送信されません。
こういった事を防ぐために入力チェックを行うのです。セキュリティ担保のため
また、入力チェックを行う目的として上記よりも大切な事があります。セキュリティ面からの入力チェックです。
簡単に言うと安全なプログラムであるために入力されたデータが正しいものであるか、危険なものでないか確認するために入力されたデータのチェックを行います。
例えば2019年はECサイト等から多くの個人情報が流出しました。攻撃の手段が増えている、巧妙になっていると言う理由もありますが、これらを防ぐための一つの手段としてプログラム内で入力チェックを行うのです。入力チェック以外のセキュリティ担保の方法
セキュリティ担保のためには入力チェック以外の方法がたくさんあります。サイトをSSL対応するとか、二段階認証を行うとか、プログラムでの対応以外にも色々とあります。
ここでは深く踏み込みませんが、そんなものがあるのだと思っていてください。入力チェックの実装
前置きが長くなったので、早速実際にチェック処理を入れていきましょう。
チェックを入れる箇所はまずは「確認画面」です。確認画面での入力チェック
まず、今時点での confirm.php を確認しましょう。
confirm.php<?php $name = $_POST['name']; $mail = $_POST['mail']; $toiawase = $_POST['toiawase']; // confirm.html 読み込み $htmlString = file_get_contents("./confirm.html"); // html内の変換文字列を使ってconfirm.html内の文字列を変換 $printHtml = str_replace("****replace_onamae****", $name, $htmlString); // 名前の変換 $printHtml = str_replace("****replace_mailaddress****", $mail, $printHtml); // メールアドレスの変換 $printHtml = str_replace("****replace_toiawase****", $toiawase, $printHtml); // メールアドレスの変換 echo $printHtml;気になる箇所は・・・・・
- 1 POSTで渡されたデータ
$_POST['name']$_POST['mail']$_POST['toiawase']って空じゃないよね?- 2 メールアドレス形式になってるよね?
- 3 渡されたデータって安全?javascriptとか入ってない??
と言う事で一つずつ対応していきます。
未入力チェック
POSTで渡されたデータが空でないかチェックしましょう。
confirm.php(修正後)<?php $name = is_null($_POST['name']) || empty($_POST['name']) ? false : $_POST['name']; $mail = is_null($_POST['mail']) || empty($_POST['mail']) ? false : $_POST['mail']; $toiawase = is_null($_POST['toiawase']) || empty($_POST['toiawase']) ? false : $_POST['toiawase']; // confirm.html 読み込み $htmlString = file_get_contents("./confirm.html"); // html内の変換文字列を使ってconfirm.html内の文字列を変換 $printHtml = str_replace("****replace_onamae****", $name, $htmlString); // 名前の変換 $printHtml = str_replace("****replace_mailaddress****", $mail, $printHtml); // メールアドレスの変換 $printHtml = str_replace("****replace_toiawase****", $toiawase, $printHtml); // メールアドレスの変換 echo $printHtml;以下が変わりました。
$name = is_null($_POST['name']) || empty($_POST['name']) || !is_string($_POST['name'])) ? false : $_POST['name']; $mail = is_null($_POST['mail']) || empty($_POST['mail'] || !is_string($_POST['name'])) ? false : $_POST['mail']; $toiawase = is_null($_POST['toiawase']) || empty($_POST['toiawase'] || !is_string($_POST['name'])) ? false : $_POST['toiawase'];何をやっているかと言うと
is_null($_POST['name']) || empty($_POST['name'])ここで空じゃないかチェックしています。また、ここでは文字列のみ許可する仕様としてis_string関数を利用して文字列である場合のみ受け取ったデータを使用します。その結果、空であればfalseを空でなければPOSTで投げられたデータを$name$toiawaseにセットしています。
上記では三項演算子と言うものを使っています。これを使わない場合は以下のような書き方もできます。if(is_null($_POST['name']) || empty($_POST['name']) || !is_string($_POST['name'])){ $name = false; }else{ $name = $_POST['name']; }どちらでも良いのですが、三項演算子を使ったら一行ですむので三項演算子を使ってます。
さて、これで未入力チェックがやっと終わりました。メールアドレスチェック
あれ??POSTでメールアドレスが送られてきてるけど空じゃなかったらそのまま使われてる。
メールアドレスとして正しいものでないかチェックしなければいけないんじゃないの?
と言う事でメールアドレスチェックをいかに記します。if(!filter_var($mail, FILTER_VALIDATE_EMAIL)){ $mail = false; }上記メールアドレスチェック処理の参考
こちらの処理でPOSTで受け取ったメールアドレス文字列がメールアドレス形式でない場合は false を入れてます。エスケープ処理
もし入力画面のテキストボックス、テキストエリアに悪意のあるjavascriptが埋め込まれており、エスケープ処理を行っていなくて、以下のような入力がされた場合。
確認ボタンをクリックするとこうなります。
簡単に攻撃されちゃうんですね。
じゃあ、どうしたら良いのかと言うとエスケープ処理を行います。
処理は簡単です。htmlspecialchars関数を使いましょう。$toiawase = htmlspecialchars($toiawase);この関数を使う事によって対象となる文字列と変換後の文字列は以下の通りです。
変換前 変換後 & (アンパサンド) & " (ダブルクォート) ENT_NOQUOTES が指定されていない場合、" ' (シングルクォート) ' (ENT_HTML401 の場合) あるいは ' ( ENT_XML1、ENT_XHTML、 ENT_HTML5 の場合)。ただし ENT_QUOTES が指定されている場合に限る < (小なり) < > (大なり) > 引用元 : php.net htmlspecialchars
終わりに
長くなってきたので以下の3つの入力チェックが終わったところで本記事も終わりにします。
- 1 POSTで渡されたデータ
$_POST['name']$_POST['mail']$_POST['toiawase']って空じゃないよね?- 2 メールアドレス形式になってるよね?
- 3 渡されたデータって安全?javascriptとか入ってない??
次は入力チェックでエラーとなった際に
falseを入れていましたがこれをどうするの?と言うところをやっていきます。
入力チェック結果のエラー処理ですね。修正履歴
2020.01.27 @technote-space さんの指摘を受け受け取るデータを文字列のみに変更
参考ページ
- 投稿日:2020-01-26T18:41:22+09:00
Incoming Webhookを使ったSlack通知ライブラリを作った
Incoming Webhookを使ったSlack通知ライブラリを作った
おはこんばんにちは!和尚です
今日は初めてPHPの自作ライブラリをcomposer化までしてみました!
手順をざっくり書いて、作った自作ライブラリについての使い方を説明していくーーー!経緯
そもそも何故、今更Slack通知のライブラリを作ろうと思ったのかについて軽くお話させていただきたい
今年からチャットサービスをSlackに乗り換えるぞ!という会社の方針で、以前チャットワークAPIを使って問合せ通知やらエラー監視ツールの通知やらをチャットワークに送っていたのですが、それらをSlackに移行しなければいけなくなってしまいました。
簡単に使えて便利なPHPのライブラリがあればよかったのですが、Slackの通知の仕組みが去年変わってしまっていて軒並み使えなさそうなライブラリたち...。とはいえ新しい仕組み自体もチャンネル毎にWebhookを使って通知するといった大したものではなかったのでPHPの自作クラスを作って対応しました。
そんなこんなで先月に移行作業を完了したわけなのですが、会社でPHPを使うことも多くSlack通知しないといけない処理が欲しいと言われたときにわざわざクラスコピーしてくるのも面倒だなーと思っていたのですが、もっと汎用的にしてcomposer化しちゃえば後々便利じゃない?とふと帰り道に思ったので今回作ってみることにしました。
参考サイト
自作ライブラリを作る手順
ざっくりとですが、自作のライブラリを作る手順は以下の順番になります。
- 自作ライブラリ用のディレクトリを作成する
$ composer initでcomposerファイルを作成する- 必要なパッケージをcomposer.jsonに記載し、
$ composer installする- srcディレクトリを作成し、autoloadの設定をcomposer.jsonに記載して
$ composer dump-autoloadする- 自作クラスを作成する
- テストコードを書く
- サンプルファイルを作成する
- ライセンスファイルを作成する
- readme.mdを書く
- githubにpushする
- packagistにgithubリポジトリを登録する
大体の作業は参考記事を見ながら作成したので、自作ライブラリが既に出来てるよ!って方は上記の参考サイトを見ながら登録してみてください。
Slack Notification
Slack Notificationの使い方
ライブラリ自体はかなりシンプルな作りになっています。
※ sendメソッドの第二引数にwebhookのURLが必要になります。Slackの通知を行いたいチャンネルにてアプリ追加ボタンを押し、incoming-webhookを追加してwebhookのURLを控えておいてください。1. インストール
$ composer require osyou84/slack-notification2. SlackNotificationを使用するphpファイルで、autoload.phpを読み込む(既に読み込まれている場合はスキップ)
require_once(__DIR__ . '/vendor/autoload.php');3.インスタンスの作成
use osyou84\SlackNotification\SlackNotification; $slack_notificaiton = new SlackNotification;4.メンションを設定し、送信する
$message = 'スラックに通知する'; $webhook_url = 'https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ'; $slack_notification->targetChannelAll() ->send($message, $webhook_url);メンションの設定方法
Slack Notificationではメンション用に4つのメソッドを用意しています。
1.ワークスペース全員に送る(#generalに向けて)
(new SlackNotification)->targetWorkspace() ->send($message, $webhook_url);2.チャンネル内の全員に送る
(new SlackNotification)->argetChannelAll() ->send($message, $webhook_url);3.チャンネル内でアクティブなメンバーに送る
(new SlackNotification)->targetActive() ->send($message, $webhook_url);4.指定したユーザーに送る
user_idはワークスペースの管理者がユーザー管理にてCSVダウンロードできます。
$user_ids = ['user_id_1', 'user_id_2', 'user_id_3']; (new SlackNotification)->setTargets($user_ids) ->send($message, $webhook_url);まとめ
かなりざっくりですが、incoming webhookを簡単に使えるPHPライブラリ作ってcomposer化したので、使ってみてね!という記事でした
改良案を思い浮かんだら随時アップデートしていきたいところ...
- 投稿日:2020-01-26T18:31:02+09:00
VSCodeにPHP_CodeSnifferを導入する。
VSCodeにPHP_CodeSnifferを導入する。
VSCodeにPHP_CodeSnifferを導入する方法を記事にしていきます。
何ができるようになるのか
PHPのコーディング規約違反(PSR違反)をエディタ(VSCode)で視覚的に確認できるようになる。
下準備
Homebrewをインストール
Homebrew公式サイト手順
- 以下のQiita記事を参考に、
Composerをインストール。
- VScodeのメニューバー > Preference > Setting > Workspace > Extensions > PHP_CodeSniffer configurasionにて、
setting.jsonの項目を見つけ以下のように、編集する。"phpcs.executablePath": "/Users/:自分のmacの名前/.composer/vendor/squizlabs/php_codesniffer/bin/phpcs", "phpcs.standard": "PSR1",※ "phpcs.standard": "PSR1"について
=> ここでは、PSR1に準拠するように設定しています。こちらの左辺を変更することによって、PSR2,PSR12など自由に設定できます。結果
コーディング規約違反がエラーとして表示されるようになりました。
※ こちらのコードの正
if ($hoge) { var__dump('piyo'); } exit;
- 投稿日:2020-01-26T17:44:54+09:00
Laravelのサービスコンテナのバインドと解決の仕組みが知りたい!
はじめに
Laravelについて勉強しているなか、自分はふと
「Laravelでは、bindとかmakeを使って手軽にインスタンスの受け渡しやインスタンスの利用ができて便利だけど、その仕組みはどうなっているんだろ?」
と感じました。
そこで、実際にbindなどのバインドの仕組みとmakeなどの解決の仕組みについてLaravelのコードを見て調べてみました!(個人的な事情によりLaravel5.5のコードを見て調べました。)本題
バインドと解決の大まかな仕組み(というかこの記事の結論)
バインド(というかbindメソッド)の大まかな仕組みは、
bindメソッドの第一引数をキーとして、第二引数をクラスのインスタンスやクロージャとなるキーの値としてLravelであらかじめ用意された配列にセットするというものです。
そして解決(というかmakeメソッド)の大まかな仕組みは、
bindメソッドでセットした配列のキーと同じ値がmakeメソッドの第一引数にセットされたとき、そのキーに対応するインスタンスやクロージャを返すというものです。
今度は実際のLaravelのコードを見て、バインドの仕組みについてみていきたいと思います。バインドの仕組みについて
実際のbindメソッドのコードはこのようになっています。ちなみにbindメソッドは、Laravelのサービスコンテナの実態であるApplication.phpの親クラスであるContainer.phpにあります。
bindメソッド
Container.phppublic function bind($abstract, $concrete = null, $shared = false) { $this->dropStaleInstances($abstract); //.....(1) if (is_null($concrete)) { $concrete = $abstract; } if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } //.....(2) $this->bindings[$abstract] = compact('concrete', 'shared'); //.....(3) if ($this->resolved($abstract)) { $this->rebound($abstract); } //.....(4) }ポイントを(1)~(4)に分けて説明します。
(1)では、bindメソッドの第一引数が、同クラス内のinstanceメソッドやaliasメソッドなどによりセットされた配列のキーとして含まれていた場合、そのキーの要素を消去します。
(2)では、もし
$concreteの値がクロージャでなかった場合(クラスである場合)、同クラス内のgetClousureメソッドによりその値はインスタンス化されます。(getClousureメソッドはメソッド内のbuildメソッドやmakeメソッドによりクラスをインスタンス化していますが、これらのメソッドについては後程取り上げるので説明は省略)(3)がこのbindメソッドでやる最も重要なことかなと思います。なぜなら、このひとつ前の章(バインドと解決の大まかな仕組み)で言っていた、
あらかじめ用意された配列とは$this->bindingsのことだからです。
この配列にbindメソッドの$abstractの値をキーとして、$concreteの値をインスタンスやクロージャとしてセットします。
($sharedはデフォルトではfalseとなり特に何の効果も生みませんが、singletonメソッドでバインドされた際はtrueとなり、ある効果を生み出しますが記事が長くなる本筋と離れるので詳しい説明は省きます。また、PHPのマジックメソッドであるcompactメソッドは変数名をキー、値をそのキーの値として配列を作ります。詳しくはこちら。)(4)では、もし
$this->bindingsのキーに対応するインスタンス又はクロージャが解決済みの場合(resolveメソッドにより解決済みかどうかが判断できる)、同クラス内のrebindingメソッドによりセットされた配列の同名のキーの値である関数が行われる。
(いつrebindingメソッドが行われるか、なぜこのメソッドが必要なのかわかっていません。わかる方、コメントで教えてください!)解決(makeメソッド)の仕組みについて
解決の流れはLaravelのサービスコンテナの実態であるApplication.phpのmakeメソッドを経て親クラスのContainer.phpのmakeメソッドが実行されますが、Application.phpのmakeメソッドについて説明すると
面倒くさい本筋と離れるのでこの記事ではContainer.phpの方だけを取り扱います。makeメソッド
Container.phppublic function make($abstract, array $parameters = []) { return $this->resolve($abstract, $parameters); }makeメソッドの第一引数に解決したい値、第二引数にその値のメソッドの引数(なければnull)を入れ、それらの引数を基に同クラス内のresolveメソッドが動きます。
単純な作りで良いですね。resolveメソッド
解決の機能の大部分を担っているのがresolveメソッドです。
Container.phpprotected function resolve($abstract, $parameters = []) { $abstract = $this->getAlias($abstract); //.....(1) $needsContextualBuild = ! empty($parameters) || ! is_null( $this->getContextualConcrete($abstract) //.....(2) ); if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } //.....(3) $this->with[] = $parameters; //.....(4) $concrete = $this->getConcrete($abstract); //.....(5) if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } //.....(6) foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } //.....(7) if ($this->isShared($abstract) && ! $needsContextualBuild) { $this->instances[$abstract] = $object; } //.....(8) $this->fireResolvingCallbacks($abstract, $object); //.....(9) $this->resolved[$abstract] = true; //.....(10) array_pop($this->with); return $object; }resolveメソッドの機能は、このメソッドの第一引数である
$abstractの値とbindメソッドで作成した$this->bindingsのキーの値が同じとき、そのキーに対応したインスタンスやクロージャが返される、というものです。でもこれを言うだけじゃ全コード載せた意味がないので、(1)~(10)に分けてもう少し細かく見ていきます。(1)では、
$abstractの値がaliasメソッドなどでセットされる$this->alaiasesという配列のキーに含まれていない場合、$abstractの値はそのままです。もし含まれていた場合はエラーを吐きます。(2)では、
$parametersの値が空かつ$abstractの値が同クラス内のaddContextualBindingメソッドでセットされる$this->contextualのキーとしてセットされていない場合以外は$needsContextualBuildの値はtrueになります。(たいていはfalseになります。というかなぜ$this->contextualが存在するのか自分では理解していません。わかる方はぜひ教えてほしいです。)(3)についてですが、ここの機能はsingletonメソッドを使用する際に使われるですので今回は説明を省きます。
(4)では、
$parametersの値が$this->withという配列にセットします。this->withはbuildメソッドで使います。そしてresolveメソッドの最後に$this->withの値はリセットされます。(5)では、
$concreteに同クラス内のgetConcreteメソッドの結果をセットします。getConcreteメソッドの機能は、resolveメソッド内の$abstractの値と同じ$this->bindingsのキーのインスタンス化クロージャを返す、というものです。また、それができなければgetConcreteメソッドの引数(ここでは$abstract)の値をそのまま返します。(6)では、まず同クラス内のisBuildableメソッドによって処理の分岐が行われます。isBuildableでは、もし
$abstractと$concreteの値が同じか$concreteの値がクロージャであった場合、tureを返しそうでない場合はfalseを返します。
もしtrueであった場合は$objectの値がbuildメソッドの結果となります(buildメソッドについては後程取り上げます)。
もしfalseであった場合は第一引数を$concreteの値にしてresolveメソッド(makeメソッドはresolveメソッドを引き起こしているだけ)を行います。これにより今度は$abstractの値と$concreteの値が同じになり、trueの場合と同じことが行われます。(7)では、もし同クラス内のextendメソッドによりセットされた
$this->extendersの配列のキーと$abstractの値が同じ場合は、そのキーに対応したクロージャを$objectの値にセットします(extendメソッドの使われ方は理解できていません。わかる方教えてください)。(8)は、singletonメソッドに関係するところなので、説明を省きます。
(9)では、同クラス内のresolvingメソッドで
$this->globalResolvingCallbacksにクロージャがセットされていたらそのクロージャを実行します。セットされていなければ何もしません。(10)では、
$this->resolvedの配列の$abstractの値がキーとなっている値にtureをセットします。これにより、$abstractの値を解決しようとしたときにすでにその値が解決済みかどうかがわかります。最後に
$this->bindings[$abstract]に対応したインスタンスかクロージャを返します。buildメソッド
buildメソッドはbindメソッドでセットした
$this->bindingのキーに対するクロージャやインスタンスを用意してくれる、サービスコンテナの中で重要な役割を担うメソッドです。
これがなければ解決どころかバインドすらも成立しません。Container.phppublic function build($concrete) { if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } //.....(1) $reflector = new ReflectionClass($concrete); //.....(2) if (! $reflector->isInstantiable()) { return $this->notInstantiable($concrete); } //.....(3) $this->buildStack[] = $concrete; //.....(4) $constructor = $reflector->getConstructor(); //.....(5) if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } //.....(6) $dependencies = $constructor->getParameters(); //.....(7) $instances = $this->resolveDependencies( $dependencies ); //.....(8) array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); //.....(9) }buildメソッドは渡される引数によって中で起こることが変わります。
(1)は、もしbuildメソッドに渡された引数がクロージャである行われます。というかクロージャの場合は(1)で終了です。クロージャが返されるときに引数にとる
$this->getLastParameterOverride()の値はmakeメソッドやresolveメソッドの第二引数であるarray $parametersの値となります。なお、返り値のクロージャの第一引数は絶対$this(Applicationクラスをインスタンスにとるオブジェクト。大抵は$app)となるので、もしクロージャに値を受け渡したいときは、bindメソッドでセットするクロージャの第二引数に受け渡したい値をセットしましょう。(2)の
ReflectionClassはPHP5以降からPHPにあらかじめセットされたクラスで、使用したいクラスにuse ReflectionClass;と入力することで使えるようになります(詳しくはこちら)。
ReflectionClassに組み込まれているメソッドを使うことで、引数に指定されているクラスの様々な情報が分かったり、そのクラスの数値を取り出すこともできます。(3)では
ReflectionClassの引数のクラス(これからは$concrete)がインスタンス化できるものかを調べ、できなければエラーを吐きます。(4)の
$this->buildStackは、buildメソッドが行われている間$concreteの値を他のメソッドに受け渡す際に使われます。buildメソッドの終了時に$this->buildStackの値はリセットされます。(同クラス内のfindInContextualBindingsメソッドなどで使いますが、このメソッドがある意味をよく理解していないため、$this->buildStackの重要性もいまいち理解できていません。)(5)では、もし
$concreteがコンストラクトメソッドを含む場合に、それを取得します。(詳しく言うと、$constructにReflectionMethodのインスタンスをセットします。ReflectionMethodはReflectionClassと同じようにPHP5以降から元からセットされたクラスで、$concreteのメソッドについて調べます。詳しくはこちら。)(6)ではもし
$concreteにコンストラクトメソッドがなければ、$concreteをインスタンスとして返します。(7)では、
$concreteのコンストラクトメソッドの引数の値を$dependenciesにセットします。
(ここでも詳しく言うと、ここでは$dependenciesにReflectionParameterのインスタンスをセットします。ReflectionParameterはReflectionClassと同じように以下略。$concreteのメソッドの引数について調べます。詳しくはこちら。)(8)では、同クラス内のresolveDependenciesメソッドを使用し、もし調べる対象の
$concreteのメソッドに値入りの引数があればその値を、タイプヒント付きの引数があればタイプヒントに表記されたクラスのインスタンスを$instancesにセットします。(9)では
$concreteにコンストラクトメソッドに入る引数(なければ空)をセットし、$concreteのインスタンスを返します。
(newInstanceArgsメソッドはReflectionClassに組み込まれたメソッドです。詳しくはこちら。)実際に作って動かしてみよう!
まず作ろう
ここまで調べてくると、今まで調べたクラス、メソッドをコピペし、うまく組み合わせればフレームワーク要らずでバインドと解決の機能を再現できるんじゃね、と思い作ってみました。
実際に(コピペして)作ったコードがこちらApplication.php<?php namespace App; //ここはどうでも良いよ use ReflectionClass; use Closure; use ReflectionParameter; /** * */ class Application { protected $bindings = []; protected $instances = []; protected $resolved = []; protected $abstractAliases = []; protected $contextual = []; protected $buildStack = []; protected $with; public function bind($abstract, $concrete = null, $shared = false) { if (is_null($concrete)) { $concrete = $abstract; } if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact('concrete', 'shared'); } protected function getClosure($abstract, $concrete) { return function ($container, $parameters = []) use ($abstract, $concrete) { if ($abstract == $concrete) { return $container->build($concrete); } return $container->make($concrete, $parameters); }; } public function make($abstract, array $parameters = []) { return $this->resolve($abstract, $parameters); } public function resolve($abstract, $parameters = []) { $needsContextualBuild = ! empty($parameters); if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; } $this->with[] = $parameters; $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } if ($this->isShared($abstract) && ! $needsContextualBuild) { $this->instances[$abstract] = $object; } $this->resolved[$abstract] = true; array_pop($this->with); return $object; } public function build($concrete) { if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } $reflector = new ReflectionClass($concrete); if (! $reflector->isInstantiable()) { //return $this->notInstantiable(); echo "Error"; } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); } protected function getConcrete($abstract) { if (isset($this->bindings[$abstract])) { return $this->bindings[$abstract]['concrete']; } return $abstract; } protected function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } public function isShared($abstract) { return isset($this->instances[$abstract]) || (isset($this->bindings[$abstract]['shared']) && $this->bindings[$abstract]['shared'] === true); } protected function getLastParameterOverride() { return count($this->with) ? end($this->with) : []; } protected function notInstantiable() { $message = 'Error!! This Class is not instantiable.'; throw new Exception($message); } protected function resolveDependencies(array $dependencies) { $results = []; foreach ($dependencies as $dependency) { if ($this->hasParameterOverride($dependency)) { $results[] = $this->getParameterOverride($dependency); continue; } $results[] = is_null($dependency->getClass()) ? $this->resolvePrimitive($dependency) : $this->resolveClass($dependency); } return $results; } protected function hasParameterOverride($dependency) { return array_key_exists( $dependency->name, $this->getLastParameterOverride() ); } protected function resolvePrimitive(ReflectionParameter $parameter) { if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } $this->unresolvablePrimitive($parameter); } protected function unresolvablePrimitive(ReflectionParameter $parameter) { $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}"; echo $message; } protected function resolveClass(ReflectionParameter $parameter) { return $this->make($parameter->getClass()->name); } } ?>Laravelで実際に書かれているコードと比べるとだいぶ少ない分量でいけました。
動かそう
次に今回のように調べる&コピペで作ったオートローダ(その時の記事はこちら)と共に動かしていきます。
動作の結果は、ブラウザ上で確認します。
ちなみにファイルの構造はこんな感じ(白丸は黒丸の中にあるファイル)<アプリケーション内>
- app
- Application.php(バインドと解決)
- Model(このフォルダは実行したいクラスが入る)
- First.php
- Second.php
- Third.php
- app.php(Laravelのbootstrap\app.phpのポジション)
- index.php(Laravelのpublic\index.phpのポジション。こいつの表示を見て動いているかどうかを見る。)
- autoload_real.php(これ以下のファイルはオートロードの機能を担うがオートロードは今回の本筋ではないため紹介しない)
- autoload_static.php
- autoloas.php
- ClassLoader.php
実行するクラス
First.php<?php namespace Model; /** * */ class First { public function copy() { echo "耳コピでしゅか?".nl2br("\n"); //nl2br()はブラウザ上で文字を改行するため } public function pai() { echo "じゃあだれが一体パイを焼くんだい?"; } } ?>Second.php<?php namespace Model; /** * */ class Second extends First { public function __construct() { $this->comment = "キャー、のび太さんの".nl2br("\n"); } public function comment() { echo $this->comment."エッジのきいたカッティングリフー!".nl2br("\n"); } } ?>Third.php<?php namespace Model; /** * */ class Third { public function __construct(First $first) { $this->first = $first; } public function end() { $this->first->pai(); } } ?>ひたすらバインドを行うクラス
app.php<?php $app = new App\Application(); $app->bind('Teacher', function () { echo "コピーよろしく".nl2br("\n"); }); $app->bind('Nobita', Model\First::class); $app->bind('Shizuka', Model\Second::class); $app->bind('End', Model\Third::class); return $app; ?>結果をブラウザに写すクラス(解決を行うクラス)
index.php<?php require 'autoload.php'; //自作のコピペオートローダの読み込み $app = require_once __DIR__.'/app.php'; //自作のコピペサービスコンテナの読み込み $app->make('Teacher'); $answer = $app->make('Nobita'); $answer->copy(); $comment = $app->make('Shizuka'); $comment->comment(); $ending = $app->make('End'); $ending->end(); ?>結果
コピーよろしく
耳コピでしゅか?
キャー、のび太さんの
エッジのきいたカッティングリフー!
じゃあだれが一体パイを焼くんだい?終わりに
他の人に自分の調べたことを伝えることよりも自分の調べたことを言葉でまとめることに意識がいってしまい、わかりにくいところが多い記事になっていると思います。読んでくれた方ありがとうございます、そしてすいません。
フレームワークについて調べると、そのフレームワークの理解が深まるだけではなく、言語自体の理解も深まることもあり、面白かったです。singletonメソッドなどまだまとめきれなかった項目が残っているので、今後これらも取り上げられたら良いなと思っています。
- 投稿日:2020-01-26T17:33:46+09:00
各言語で、継承の挙動はかなり違うという話
はじめに
この記事は、私が色んな言語でひたすら似たようにクラス継承を書いてみて、実際にどんな値が出力されるのかを調査した結果をまとめたものです。時には既知の言語でも「こんな文法あったんだ」と思いながら、時にはHello Worldから頑張りました。
まとめるのが大変だった割に誰得?という内容ですが、同じことが気になった人のために置いておきます。
いやでも新しい発見があるかもしれないのでとりあえず読んでみてください。
意外と面白い結果になったかもしれません。調べた言語
静的型付け
- Java (Corretto 1.8.0_232)
- C# (3.4.0)
- C++ (11.0.0)
- Scala (2.13.1)
- Kotlin (1.3.61)
- Swift (5.1.3)
動的型付け
- Python (3.7.1)
- Ruby (2.6.5)
- PHP (7.1.32)
- JavaScript (node v12.14.1)
オプショナルな静的型付け
- TypeScript (3.7.2)
- Dart (2.7.0)
調査で使うコード
ある親クラスを子クラスが継承します。
その親クラスと子クラスには、同じ名前のインスタンス変数があります。(クラス変数ではありません)
親クラスにはメソッドがあり、そのメソッドを使うとインスタンス変数をコンソールに出力できます。実行時には、子クラスのインスタンスで、継承した親のメソッドを呼びます。
さぁ親と子どっちのインスタンス変数が出力されるでしょうか……というストーリーです。多分読める人が多いであろうJS(ES6以降)のコードで書くと、こういうコードです。
class SuperClass { constructor() { // ※JSではインスタンス変数は実行時に定義されるので、コンストラクタに書く this.instanceVariable = "SuperClass" // 同じ名前で違う値 } superClassMethod() { console.log(this.instanceVariable) } } class SubClass extends SuperClass { constructor() { super() this.instanceVariable = "SubClass" // 同じ名前で違う値 } } const sub = new SubClass() // 子クラスをインタンス化 sub.superClassMethod() // 継承した親クラスのメソッドを呼ぶ自分が普段使っている言語でどういう値が出力されるかぜひ想像してみてください。
(良し悪しはともかくとして)たまにある書き方なので、自分の得意な言語のものは知っているかもしれません。
でも他の言語でどう動くのかを知っていたら、いつか別の会社や別の案件に入った時、もしくは興味本位で他言語に入門した時に役に立つかもしれません。今回は一応、インスタンス変数のみならず、メソッド(クラスメソッドではないインスタンスのメソッド)で
return "SuperClass"をした時にどうなるのかも見たりしています。
この場合は、親クラスと子クラスに同じ名前のメソッドがあり、親クラスのメソッドでそれを呼ぶ……というシナリオです。どんなコードを書いて調べたかは、
コードを見る(hoge言語)
class Hoge {}こんな風に詳細折りたたみ要素が置いてあるので、クリックして開いてもらえると中身のコードを読むことができます。
何をしているか見やすくするために原則としてコピペを採用したかなりWETなコードですが、それはわざとなのでマサカリを投げないでください。今回のポイント
ご存知の通り、プログラミング言語によって存在する文法は異なりますし、評価の仕方も違います。
今回の結果が変わるポイントは、下記であると思います。
- アクセス修飾子が存在するか。また、どんな種類のものがあるか
- 親クラスと子クラスで同名のインスタンス変数をそもそも定義可能か
- インスタンス変数のオーバーライド、メソッドのオーバーライドがそれぞれ存在するか。
- 継承そのものを、各言語がどう処理しているか
結果発表
Java
エンタープライズシステムの覇者、Java。
Javaにはアクセス修飾子として、privateやprotectedがあります。
privateは、現在のクラスからしか見ることはできません。protectedは、現在のクラスと子クラスからアクセスできます。Javaではインスタンス変数のオーバーライドは存在しません。
なので、メソッドでオーバーライドをした時にどうなるのかも見てみます。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected "SuperClass" インスタンス変数の代わりにprivateなメソッド "SuperClass" インスタンス変数の代わりにprotectedなメソッド(override) "SubClass" オーバーライドは文字通り「上書き」してしまいます。
今回は関係ありませんでしたが、Javaは、子クラス側のメソッドで親クラスのインスタンス変数に
super.instanceVariableのようにしてアクセスすることができます。オーバーライドしないゆえにできる芸当ですね。
コードを見る(Java)
public class JavaSample { public static void main(String[] args) { System.out.println("---- SubClass ----"); SubClass sub = new SubClass(); sub.superClassMethod(); System.out.println("---- SubClassProtected ----"); SubClassProtected subp = new SubClassProtected(); subp.superClassMethod(); System.out.println("---- SubClassGetter ----"); SubClassGetter subg = new SubClassGetter(); subg.superClassMethod(); System.out.println("---- SubClassGetterProtected ----"); SubClassGetterProtected subgp = new SubClassGetterProtected(); subgp.superClassMethod(); } } class SuperClass { // privateのインスタンス変数はサブクラスから参照できない private String instanceVariable = "SuperClass"; public void superClassMethod() { System.out.println(instanceVariable); } } class SubClass extends SuperClass { private String instanceVariable = "SubClass"; } // ------------------------------------------- class SuperClassProtected { protected String instanceVariable = "SuperClass"; public void superClassMethod() { System.out.println(instanceVariable); } } class SubClassProtected extends SuperClassProtected { protected String instanceVariable = "SubClass"; // public void subClassMethod() { // System.out.println(instanceVariable); と書くと、 // System.out.println(this.instanceVariable); と同じ。"SubClass"が出る。 // superをつけると、"SuperClass"と表示させることもできる // System.out.println(super.instanceVariable); // } } // ------------------------------------------- class SuperClassGetter { private String instanceVariable() { return "SuperClass"; } public void superClassMethod() { System.out.println(instanceVariable()); } } class SubClassGetter extends SuperClassGetter { private String instanceVariable() { return "SubClass"; } } // ------------------------------------------- class SuperClassGetterProtected { protected String instanceVariable() { return "SuperClass"; } public void superClassMethod() { System.out.println(instanceVariable()); } } class SubClassGetterProtected extends SuperClassGetterProtected { protected String instanceVariable() { return "SubClass"; } }C#
Javaの次はやっぱりC#。歴史的経緯からかなりJavaに近い文法を持ちます。その結果もまたJavaや、後述するC++と似ていますが、Javaには無いものもあります。
ちなみにC#にはプロパティがあるので、メソッドの代わりにプロパティで書きました。C#には
virtualとoverrideがあります。virtualは、「このメソッドはオーバーライドされる可能性があるぞ」と教える修飾子で、overrideは、ここでメソッドはオーバーライドしているぞと教える修飾子です。そこまではいいのですが、C#には更に、
newという変わった修飾子があります。このnewは、インスタンス生成のときのnew Human()のnewとは別モノです。overrideの代わりにnewをつけることで、子クラスからの呼び出しであっても、親クラスのメソッドを親クラスの文脈のままで評価させることができます。なぜかというと、親クラスのメソッドをオーバーライド(上書き)しているわけではなく、ただ隠しているだけだからです。ややこしいですね。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected "SuperClass" プロパティがprivate "SuperClass" プロパティがprotected(override) "SubClass" プロパティがprotected(new) "SuperClass"
コードを見る(C#)
using System; public class CSharpSample { public static void Main(string[] args) { Console.WriteLine("---- SubClass ----"); var sub = new SubClass(); sub.SuperClassMethod(); Console.WriteLine("---- SubClassProtected ----"); var subp = new SubClassProtected(); subp.SuperClassMethod(); Console.WriteLine("---- SubClassGetter ----"); var subg = new SubClassGetter(); subg.SuperClassMethod(); Console.WriteLine("---- SubClassGetterProtectedOverride ----"); var subgpo = new SubClassGetterProtectedOverride(); subgpo.SuperClassMethod(); Console.WriteLine("---- SubClassGetterProtectedNew ----"); var subgpn = new SubClassGetterProtectedNew(); subgpn.SuperClassMethod(); } } class SuperClass { private string instanceVariable = "SuperClass"; public void SuperClassMethod() { Console.WriteLine(instanceVariable); } } class SubClass : SuperClass { // warning CS0414: The field 'SubClass.instanceVariable' is assigned but its value is never used private string instanceVariable = "SubClass"; } // ---------------------------- class SuperClassProtected { protected string instanceVariable = "SuperClass"; public void SuperClassMethod() { Console.WriteLine(instanceVariable); } } class SubClassProtected : SuperClassProtected { // newはオーバーライドではなく、継承元のインスタンス変数隠しているよと明示している new protected string instanceVariable = "SubClass"; } // ---------------------------- class SuperClassGetter { private string instanceVariable { get { return "SuperClass"; } } public void SuperClassMethod() { Console.WriteLine(instanceVariable); } } class SubClassGetter : SuperClassGetter { private string instanceVariable { get { return "SubClass"; } } } // ---------------------------- class SuperClassGetterProtected { protected virtual string instanceVariable { get { return "SuperClass"; } } public void SuperClassMethod() { Console.WriteLine(instanceVariable); } } class SubClassGetterProtectedOverride : SuperClassGetterProtected { protected override string instanceVariable { get { return "SubClass"; } } } class SubClassGetterProtectedNew : SuperClassGetterProtected { protected new string instanceVariable { get { return "SubClass"; } } }C++
C++は組み込みの現場などで使われることが多いらしいですが私は何も知りません。
C++ナニモワカラナイ……。Javaとともに、C#の元となった言語なだけあって、C#に挙動がとても似ています。
インスタンス変数の代わりにメソッドを使った場合で、かつoverrideしないというのは、C#でいうところのnewと同じ挙動をします。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected "SuperClass" インスタンス変数の代わりにprivateなメソッド "SuperClass" インスタンス変数の代わりにprotectedなメソッド(override) "SubClass" インスタンス変数の代わりにprotectedなメソッド(overrideしない) "SuperClass"
コードを見る(C++)
#include <iostream> class SuperClass { // デフォルトだとprivate(クラス外からアクセス不可になる) std::string instanceVariable = "SuperClass"; public: void superClassMethod() { std::cout << instanceVariable << std::endl; } }; class SubClass : public SuperClass { std::string instanceVariable = "SubClass"; }; // ------------------------------- class SuperClassProtected { protected: std::string instanceVariable = "SuperClass"; public: void superClassMethod() { std::cout << instanceVariable << std::endl; } }; class SubClassProtected : public SuperClassProtected { protected: std::string instanceVariable = "SubClass"; }; // ------------------------------- class SuperClassGetter { std::string instanceVariable() { return "SuperClass"; } public: void superClassMethod() { std::cout << instanceVariable() << std::endl; } }; class SubClassGetter : public SuperClassGetter { std::string instanceVariable() { return "SubClass"; } }; // ------------------------------- class SuperClassProtectedGetter { protected: std::string instanceVariable() { return "SuperClass"; } public: void superClassMethod() { std::cout << instanceVariable() << std::endl; } }; class SubClassProtectedGetter : public SuperClassProtectedGetter { protected: std::string instanceVariable() { return "SubClass"; } }; // ------------------------------- class SuperClassProtectedGetterOverride { protected: virtual std::string instanceVariable() { return "SuperClass"; } public: void superClassMethod() { std::cout << instanceVariable() << std::endl; } }; class SubClassProtectedGetterOverride : public SuperClassProtectedGetterOverride { protected: std::string instanceVariable() override { return "SubClass"; } }; int main() { std::cout << "---- SubClass ----" << std::endl; SubClass sub; sub.superClassMethod(); std::cout << "---- SubClassProtected ----" << std::endl; SubClassProtected subp; subp.superClassMethod(); std::cout << "---- SubClassGetter ----" << std::endl; SubClassGetter subg; subg.superClassMethod(); std::cout << "---- SubClassProtectedGetter ----" << std::endl; SubClassProtectedGetter subpg; subpg.superClassMethod(); std::cout << "---- SubClassProtectedGetterOverride ----" << std::endl; SubClassProtectedGetterOverride subpgo; subpgo.superClassMethod(); return 0; }Scala
関数型の風を取り込んだAltJavaです。
JavaやC#と大きく違うところは、インスタンス変数のオーバーライドができることです。新機能来ました!
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected(override) "SubClass"
コードを見る(Scala)
object ScalaSample { def main(args: Array[String]): Unit = { println("---- SubClass ----") val sub = new SubClass sub.superClassMethod() println("---- SubClassProtected ----") val subp = new SubClassProtected subp.superClassMethod() } } class SuperClass { private val instanceVariable = "SuperClass"; def superClassMethod(): Unit = { println(instanceVariable); } } class SubClass extends SuperClass { private val instanceVariable = "SubClass"; } // ---------------------------- class SuperClassProtected { protected val instanceVariable = "SuperClass"; def superClassMethod(): Unit = { println(instanceVariable); } } class SubClassProtected extends SuperClassProtected { override protected val instanceVariable = "SubClass"; }Kotlin
Scalaに大きな影響を受けたAltJavaです。Androidアプリの開発で多く用いられています。
オーバーライドする予定のインスタンス変数にはopenをつけなくてはならないこと以外、Scalaと同じです。
つまりopenはC++やC#のvirtualみたいなものですね。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected(override) "SubClass"
コードを見る(Kotlin)
fun main(args: Array<String>) { println("---- SubClass ----"); val sub = SubClass(); sub.superClassMethod(); println("---- SubClassOverride ----"); val subo = SubClassOverride(); subo.superClassMethod(); } open class SuperClass { private val instanceVariable = "SuperClass"; fun superClassMethod() { println(instanceVariable); } } class SubClass : SuperClass() { private val instanceVariable = "SubClass"; } // ----------------------------------- open class SuperClassOverride { open val instanceVariable = "SuperClass"; fun superClassMethod() { println(instanceVariable); } } class SubClassOverride : SuperClassOverride() { override val instanceVariable = "SubClass"; }Swift
アプリと言ったらAndroidだけではありません。iOSを忘れて貰っては困ります。
Swiftのprivateはバージョンによって挙動が変わったりするようですが、今回はSwift 5で検証したので、Javaと同じく、クラス内のみ有効で、継承されません。Swiftの
letは再代入を禁止した変数宣言です。JSでいうとconstです。
そしてSwiftのvarは再代入可能な普通の変数宣言です。JSでいうletです。ややこしや。Swiftでは、インスタンス変数(Swiftではプロパティという)を、継承元と同じものを定義することはできません。
代わりにComputed Property(C#でいうプロパティのようなもの)を使うと、オーバーライドすることができます。
privateのようなアクセス修飾子をつけないと、internalというスコープになります。internalはJavaでいうpublicみたいなものです(雑)。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がinternal 定義不可 Computed Propertyがprivate "SuperClass" Computed Propertyがinternal(override) "SubClass"
コードを見る(Swift)
class SuperClass { private let instanceVariable = "SuperClass"; func SuperClassMethod() { print(instanceVariable); } } class SubClass: SuperClass { private let instanceVariable = "SubClass"; } // -------------------------------- class SuperClassInternal { let instanceVariable = "Error"; func SuperClassMethod() { print(instanceVariable); } } class SubClassInternal: SuperClassInternal { // error: cannot override with a stored property 'instanceVariable' // let instanceVariable = "SubClass"; } // -------------------------------- // Computed Propertyにしてgetterを生やす。こうしたら関数扱いなのでoverrideができるようになる。 class SuperClassGetter { // letだと、error: 'let' declarations cannot be computed properties private var instanceVariable: String { get { return "SuperClass" } } func SuperClassMethod() { print(instanceVariable); } } class SubClassGetter: SuperClassGetter { private var instanceVariable: String { get { return "SubClass" } }; } // -------------------------------- // Computed Propertyにしてgetterを生やす。こうしたら関数扱いなのでoverrideができるようになる。 class SuperClassInternalGetter { // letだと、error: 'let' declarations cannot be computed properties var instanceVariable: String { get { return "SuperClass" } } func SuperClassMethod() { print(instanceVariable); } } class SubClassInternalGetter: SuperClassInternalGetter { override var instanceVariable: String { get { return "SubClass" } }; } print("---- SubClass ----"); let sub = SubClass(); sub.SuperClassMethod(); print("---- SubClassInternal ----"); let subi = SubClassInternal(); subi.SuperClassMethod(); print("---- SubClassGetter ----"); let subg = SubClassGetter(); subg.SuperClassMethod(); print("---- SubClassInternalGetter ----"); let subig = SubClassInternalGetter(); subig.SuperClassMethod();Python
ここからは動的型付け言語!
Qiitaのタグ投稿ランキングで1位を取り続けるなど、その人気を盤石なものにしたPythonです。Pythonのクラスで特徴的なのは、メソッドの第一引数にインスタンス自身がやってくるので宣言時には第1引数に
selfと書くところです。別にselfじゃなくても良いのですが、みんなselfと書きます。あとは動的型付け言語らしく、インスタンス変数を動的に生み出すので、クラス宣言直下ではなくコンストラクタの中でインスタンス変数を定義します。ここはRubyもJavaScriptも同じですね。
Pythonにはメンバのアクセス制限がありません。修飾子
privateはありません。
関数名の先頭にアンダースコアを1つつけて、アクセスしないで欲しいことを示す文化です。しかしPythonでは、子クラスと名前が衝突した場合に備えた機能があります。アンダースコアを2つつけると、内部的なメンバ名が変わり(ネームマングリング)、元の変数名に簡単にアクセスできなくすることができます。この機能を使うことで、子クラスで同名のメンバがあった場合でも、実行時に元の親クラスの文脈で結果を評価することができるようになります。
pep8-ja - 命名規約 - メソッド名とインスタンス変数
結果を見てみましょう。
条件 結果 インスタンス変数 "SubClass" インスタンス変数の代わりにメソッド "SubClass" インスタンス変数(アンスコ2つ付き) "SuperClass" インスタンス変数の代わりにメソッド(アンスコ2つ付き) "SuperClass"
コードを見る(Python)
class SuperClass: def __init__(self): self.instance_variable = "SuperClass" def super_class_method(self): print(self.instance_variable) class SubClass(SuperClass): def __init__(self): super().__init__() self.instance_variable = "SubClass" # ------------------------------ class SuperClassNameMangling: def __init__(self): self.__instance_variable = "SuperClass" def super_class_method(self): print(self.__instance_variable) class SubClassNameMangling(SuperClassNameMangling): def __init__(self): super().__init__() self.__instance_variable = "SubClass" # ------------------------------ class SuperClassGetter: def instance_variable(self): return "SuperClass" def super_class_method(self): print(self.instance_variable()) class SubClassGetter(SuperClassGetter): def instance_variable(self): return "SubClass" # ------------------------------ class SuperClassNameManglingGetter: def __instance_variable(self): return "SuperClass" def super_class_method(self): print(self.__instance_variable()) class SubClassNameManglingGetter(SuperClassNameManglingGetter): def __instance_variable(self): return "SubClass" print('---- SubClass ----') sub = SubClass() sub.super_class_method() print('---- SubClassNameMangling ----') subp = SubClassNameMangling() subp.super_class_method() print('---- SubClassGetter ----') subg = SubClassGetter() subg.super_class_method() print('---- SubClassNameManglingGetter ----') subpg = SubClassNameManglingGetter() subpg.super_class_method()Ruby
「オブジェクト指向の動的型付け言語といったら?」
という連想クイズをされたプログラマは、真っ先にこの言語を思い浮かべる方も多いのではないでしょうか。Rubyにはメソッドに対して使える
privateがありますが、Javaのような言語のprivateとは挙動が違います。詳しくはレシーバとかを説明しなきゃいけなくなるので書きませんが、とりあえず言えることはprivateがあっても継承先にはそのメソッドが見えるということです。なのでJavaでいうとprotectedの方が近いかもしれません。
[Ruby] privateメソッドの本質とそれを理解するメリットちなみにRubyは変数とメソッドの名前空間が別れているので、メソッドをカッコなしで書いても呼び出せます。
それからメソッドはreturnを書かなくても、最後に評価した式の結果を返します。
@変数名がインスタンス変数です。Rubyでは変数名の1文字目で変数の種別(スコープ)がわかります。
条件 結果 インスタンス変数 "SubClass" インスタンス変数の代わりにメソッド "SubClass" インスタンス変数の代わりにprivateなメソッド "SubClass"
コードを見る(Ruby)
class SuperClass def initialize() @instance_variable = "SuperClass" end def super_class_method p @instance_variable end end class SubClass < SuperClass def initialize() super() @instance_variable = "SubClass" end end # -------------------------------- class SuperClassGetter def instance_variable() "SuperClass" end def super_class_method p instance_variable end end class SubClassGetter < SuperClassGetter def instance_variable() "SubClass" end end # -------------------------------- class SuperClassGetterPrivate def super_class_method p instance_variable end private def instance_variable() "SuperClass" end end class SubClassGetterPrivate < SuperClassGetterPrivate private def instance_variable() "SubClass" end end p '---- SubClass ----' subc = SubClass.new subc.super_class_method p '---- SubClassGetter ----' subg = SubClassGetter.new subg.super_class_method p '---- SubClassGetterPrivate ----' subgp = SubClassGetterPrivate.new subgp.super_class_methodPHP
PHPにはJavaのような
privateやprotectedやpublicがあります。
PythonやRubyやJavaScriptには基本的に無いのに! 羨ましい!
でもJavaとは違って、protectedなインスタンス変数はサブクラスのものを読みに行きます。
そこは他の動的型付け言語と足並みを揃えた動きですね。
条件 結果 インスタンス変数がprivate "SuperClass" インスタンス変数がprotected "SubClass" インスタンス変数の代わりにprivateなメソッド "SuperClass" インスタンス変数の代わりにprotectedなメソッド "SubClass"
コードを見る(PHP)
<?php function println($message) { echo $message.PHP_EOL; } // ---------------------------------------- class SuperClass { private $instanceVariable = "SuperClass"; public function superClassMethod() { println($this->instanceVariable); } } class SubClass extends SuperClass { private $instanceVariable = "SubClass"; } // ---------------------------------------- class SuperClassProtected { protected $instanceVariable = "SuperClass"; public function superClassMethod() { println($this->instanceVariable); } } class SubClassProtected extends SuperClassProtected { protected $instanceVariable = "SubClass"; } // ---------------------------------------- class SuperClassGetter { private function instanceVariable() { return "SuperClass"; } public function superClassMethod() { println($this->instanceVariable()); } } class SubClassGetter extends SuperClassGetter { private function instanceVariable() { return "SubClass"; } } // ---------------------------------------- class SuperClassGetterProtected { protected function instanceVariable() { return "SuperClass"; } public function superClassMethod() { println($this->instanceVariable()); } } class SubClassGetterProtected extends SuperClassGetterProtected { protected function instanceVariable() { return "SubClass"; } } // ---------------------------------------- println("---- SubClass ----"); $sub = new SubClass(); $sub->superClassMethod(); println("---- SubClassProtected ----"); $subp = new SubClassProtected(); $subp->superClassMethod(); println("---- SubClassGetter ----"); $subg = new SubClassGetter(); $subg->superClassMethod(); println("---- SubClassGetterProtected ----"); $subgp = new SubClassGetterProtected(); $subgp->superClassMethod();JavaScript
フロントエンドの覇者です。
プロトタイプベースオブジェクト指向言語という、プログラミング言語Selfから影響を受けた言語です。
ES6以降のJavaScriptではクラスベースの書き方も取り入れているため、extendsして継承もできます。
条件 結果 インスタンス変数 "SubClass" 古い書き方でインスタンス変数 "SubClass" インスタンス変数で、呼び出しメソッドがアロー関数 "SubClass" インスタンス変数の代わりにアロー関数 "SubClass"
コードを見る(JavaScript)
class SuperClass { constructor() { this.instanceVariable = "SuperClass" } superClassMethod() { console.log(this.instanceVariable) } } class SubClass extends SuperClass { constructor() { super() this.instanceVariable = "SubClass" } } // ------------------------ function LegacySuperClass() { this.instanceVariable = "SuperClass"; }; LegacySuperClass.prototype.superClassMethod = function() { console.log(this.instanceVariable) }; function LegacySubClass() { this.instanceVariable = "SubClass"; }; LegacySubClass.prototype = new LegacySuperClass(); // ------------------------ class SuperClassArrow { constructor() { this.instanceVariable = "SuperClass" } superClassMethod = () => { console.log(this.instanceVariable) } } class SubClassArrow extends SuperClassArrow { constructor() { super() this.instanceVariable = "SubClass" } } // ------------------------ class SuperClassGetterArrow { instanceVariable = () => { return "SuperClass" } superClassMethod = () => { console.log(this.instanceVariable()) } } class SubClassGetterArrow extends SuperClassGetterArrow { instanceVariable = () => { return "SubClass" } } // ------------------------ console.log('---- SubClass ----') const sub = new SubClass() sub.superClassMethod() console.log('---- LegacySubClass ----') var lsub = new LegacySubClass() lsub.superClassMethod() console.log('---- SubClassArrow ----') const suba = new SubClassArrow() suba.superClassMethod() console.log('---- SubClassGetterArrow ----') const subga = new SubClassGetterArrow() subga.superClassMethod()TypeScript
静的に型をつけられる、イケてるJavaScriptですね。
union型という、高度な型システムを持つHaskellに見られるような型に近いものがあったり、Conditional TypesやらMapped Typesやら表現力のある型があるのが特徴です。TypeScriptでは
privateな同名のメンバを親クラス子クラスで定義するとコンパイルエラーになります。protectedであれば問題ありません。Swiftとは真逆ですね。
条件 結果 インスタンス変数がprivate 定義不可 インスタンス変数がprotected "SubClass" インスタンス変数の代わりにprivateなメソッド 定義不可 インスタンス変数がprotectedで、呼び出しメソッドがアロー関数 "SubClass" インスタンス変数の代わりにprotectedなアロー関数 "SubClass"
コードを見る(TypeScript)
class SuperClass { private readonly instanceVariable: string constructor() { this.instanceVariable = "Error" } public superClassMethod() { console.log(this.instanceVariable) } } class SubClass extends SuperClass { // instanceVariableで定義しようとすると、 // TS2415: Class 'SubClass' incorrectly extends base class 'SuperClass'. // private readonly instanceVariable: string // constructor() { // super() // this.instanceVariable = "SubClass" // } } // ------------------------ class SuperClassGetter { private instanceVariable() { return "Error" } public superClassMethod() { console.log(this.instanceVariable()) } } class SubClassGetter extends SuperClassGetter { // instanceVariableで定義しようとすると、 // TS2415: Class 'SubClassGetter' incorrectly extends base class 'SuperClassGetter'. // Types have separate declarations of a private property 'instanceVariable'. // private instanceVariable() { // return "SubClass" // } } // ------------------------ class SuperClassProtected { protected readonly instanceVariable: string constructor() { this.instanceVariable = "SuperClass" } public superClassMethod() { console.log(this.instanceVariable) } } class SubClassProtected extends SuperClassProtected { protected readonly instanceVariable: string constructor() { super() this.instanceVariable = "SubClass" } } // ------------------------ class SuperClassProtectedGetterArrow { protected instanceVariable = () => { return "SuperClass" } public superClassMethod = () => { console.log(this.instanceVariable()) } } class SubClassProtectedGetterArrow extends SuperClassProtectedGetterArrow { protected instanceVariable = () => { return "SubClass" } } // ------------------------ // アロー関数版 class SuperClassProtectedArrow { protected readonly instanceVariable: string constructor() { this.instanceVariable = "SuperClass" } public superClassMethod = () => { console.log(this.instanceVariable) } } class SubClassProtectedArrow extends SuperClassProtectedArrow { protected readonly instanceVariable: string constructor() { super() this.instanceVariable = "SubClass" } } console.log('---- SubClass ----') const sub = new SubClass() sub.superClassMethod() console.log('---- SubClassGetter ----') const subg = new SubClassGetter() subg.superClassMethod() console.log('---- SubClassProtected ----') const subp = new SubClassProtected() subp.superClassMethod() console.log('---- SubClassProtectedArrow ----') const subpa = new SubClassProtectedArrow() subpa.superClassMethod() console.log('---- SubClassProtectedGetterArrow ----') const subpga = new SubClassProtectedGetterArrow() subpga.superClassMethod()Dart
Flutterが流行りだしてから急に存在感が出てきた印象な言語です。
Dartではprivateやpublicを書くことはありません。メンバの名前にアンダースコアをつけることでprivateにすることはできますが、ライブラリ外から見えなくするというだけなので、Javaなどのprivateとは違います。
条件 結果 インスタンス変数(アンスコ付き) "SubClass" インスタンス変数 "SubClass" インスタンス変数の代わりにメソッド "SubClass"
コードを見る(Dart)
void main() { print("---- SubClassPrivate ----"); final subp = new SubClassPrivate(); subp.superClassMethod(); print("---- SubClass ----"); final sub = new SubClass(); sub.superClassMethod(); print("---- SubClassGetter ----"); final subg = new SubClassGetter(); subg.superClassMethod(); } class SuperClassPrivate { // アンダースコアをつけるとPrivateになるが、 // privateは同一ライブラリに対して可視のため、ここではサブクラスから普通に見えちゃう final String _instanceVariable = "SuperClass"; void superClassMethod() => print(_instanceVariable); } class SubClassPrivate extends SuperClassPrivate { final String _instanceVariable = "SubClass"; } // ------------------------------------ class SuperClass { final String instanceVariable = "SuperClass"; void superClassMethod() => print(instanceVariable); } class SubClass extends SuperClass { final String instanceVariable = "SubClass"; } // ------------------------------------ class SuperClassGetter { String instanceVariable() => "SuperClass"; void superClassMethod() => print(instanceVariable()); } class SubClassGetter extends SuperClassGetter { String instanceVariable() => "SubClass"; }まとめ
ある程度使い慣れた言語でも知らないことがあったりして、意外と楽しかったです。
挙げた例の中には今回初めて書いた言語もあるので、こんな書き方あるよ! とか、間違ってるよ! とかあったらコメントください。全体的にまとめると、動的型付け言語だとスーパークラスのメソッドを呼んだ時でもサブクラスの文脈でまずは評価させようとするような印象でした。一方の静的型付け言語では、「スーパークラスのメソッドなんだから基本はスーパークラスで評価する。ただしオーバーライドしたらその限りじゃない」という感じでした。
個人的に印象深かったのは、下記の5つでした。
- C#には
newというオーバーライドしない用の修飾子がある- ScalaとKotlinは静的型付け言語なのにインスタンス変数のオーバーライドができる
- Pythonのアンダースコア2つのメンバ名はただの名前じゃなく効果もあった
- PHPには動的型付け言語なのにJavaみたいな
privateがある- TypeScriptは重複する
privateな同名メンバをコンパイル時にエラーにして予期せぬ動作になることを防いでくれる更新履歴
- Pythonの命名規約の説明について誤りがあったため、修正を行いました。 @shiracamus さんありがとうございました。
- Pythonでは
privateという言葉は使わないと指摘があったため、修正を行いました。 @Luice さんありがとうございました。
- 投稿日:2020-01-26T15:36:17+09:00
初学者が12.5日でPHP7初級に合格する方法
はじめに
PHP初学者である自分が、12.5日間の勉強で、
PHP7初級に合格することができました。
その方法を本記事にまとめます。何方のご参考になれば幸いです。前提
私
文系卒3年目SEです。
元々はJava+JavaScriptによるシステム開発をやっていました。
最近はWeb API設計が主な業務です。PHPは趣味で少し齧ってみた程度で、業務での使用経験はありません。
受験理由
PHPのフレームワークであるLaravelに興味を持ち、
先ずはPHPの基礎知識を習得したいと思ったことが動機です。
資格取得という形であれば、以下の理由から都合がよいと考え、
受験することにしました。
段階的に学習できる
スキルを他者にも客観的に捉えられる
スケジュール
'20/1/13(祝)- 受験日:1/25(土)
※勉強時間:平均2h/日計:12.5日間
※勉強時間:約25h勉強場所
主に以下で行いました。
通勤電車内(1h)
昼休憩中の自席/談話室(約1h)
自宅では他にやりたいことがあるため、試験勉強は外出中のみ行いました。
使った対策本
以下の問題集のみです。
全14章、問題+解説だけなら220P程度。
資格の問題集としては薄い方だと思います。kindle版にした理由は、以下の二点です。
荷物を軽くしたかったから
電車内で立っていても読みやすいから
上記に差支えがなければ、単行本版でも構わないと思います。
勉強法
1 - 11日目
以下をほぼ毎日行いました。
問題を解く(1-7日目までは1章ずつ、8-11日目までは2章ずつ)
前日解いた問題を再度解く
目印(後述)を付けた問題の解説を暗記。関数は用途だけでなくスペルも覚える
問題集を解く流れ
①3問分の問題番号と解答をメモ
②①で解いた問題の正答と解説を確認
正解以外の選択肢も、何故それが誤答なのかを覚えられるよう意識して解説を読む
これが試験で出たら悩みそうだな、と思った内容が含まれていれば、解答の正否問わず、メモの問題番号に目印を付ける
③①-②を繰り返し、全問誤答が無くなるまで繰り返す
備考
前述の通り、外出中のみ勉強していたため、
休日は勉強しない日もありました。
最終的に、11日目(受験前々日)までに全章完答していれば問題ありません。7日目までのノルマを1章ずつにしたのは、挫折せず勉強の習慣づけを行うためです。
12日目(受験前日)
復習として、以下を行いました。
目印を付けた問題を見直し、覚えていない/自信が無いものがあるか確認する。あれば、簡単にポイントだけ再度ノートに書き取る
スペルが曖昧な関数があれば、5回ずつノートに書き取る
12.5日目(受験当日)
前日同様の復習を行いました。
試験中は、正しい記述を選ぶ問題か、誤った記述を選ぶ問題かを必ず確認しながら
解答を進めました。受験結果
結果は825/1000(合格ライン:700)点で
合格でした。問題集と類似した問題が8割程度出題されました。初見の問題はPHPのバージョン間の違いについての知識問題等です。
感想・次の目標
出題範囲も広くなく、問題集からの出題率が高いです。そのため、気軽にチャレンジできる資格だと思います。
次は春のプロジェクトマネージャ試験を受ける予定です。
上手くいった場合は、また記事にします。読んで頂き、有難うございました。
- 投稿日:2020-01-26T14:28:55+09:00
Laravelでとりあえず画像を表示する
※自分用メモ
Laravelで画像を表示します。
1.resources/viewsにindex.blade.phpを作成し、以下を挿入
<img src= {{asset('/img/piyopiyo.png')}} >asset~部分が環境下でURLに置き換わるそうです。
※bladeにしないと(=index.phpで作ると)波括弧の構文が機能しない
俺はこれで三時間潰しました2.piyopiyo.pngをpublic/imgに入れる
フォルダを作成して突っ込みます。
3.routesのweb.phpに以下のコードを入れる
Route::get('/index/', function () { return view('index'); });終わり!!!
- 投稿日:2020-01-26T03:18:26+09:00
PHPのIterable型を使ってみる
TL;DR
- PHPが用意してる型の1つのIterable型に関する内容
- 特に、array型を受け取る前提、かつ、型に厳格さを求めるfunctionなどの場合、Iterable型での定義は最適な実装方法の1つではないかと感じる
前提
- 実行環境
- PHP 7.1.32
注意
Iterable は PHP 7.1 で導入された疑似型です。array、あるいは Traversable インターフェイスを実装したオブジェクトを 許容します。これらの型は、いずれも foreach で繰り返し可能であり、また、 ジェネレータ 内で yield from できます。
引用元:https://www.php.net/manual/ja/language.types.iterable.php
上記に記載されているように、公式ドキュメントには、Traversable インターフェイスを実装したオブジェクトも許容するとあるが、実際のユースケースとして、arrayの方が頻度は多いと判断したため、arrayを主に利用したサンプルコードと実際の挙動について触れてみる
サンプルコード
<?php function foo(iterable $iterable) { return gettype($iterable); } /* array型を渡したケース */ $array = ['apple', 'orange', 'banana']; echo foo($array); /* 出力結果 */ array /* array型以外を渡した場合 */ echo foo('apple'); /* 出力結果 */ Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be iterable, string given, called in /Users/hoge/sample.php on line 10 and defined in /Users/hoge/sample.php:3 Stack trace: #0 //Users/hoge/sample.php(10): foo('apple') #1 {main} thrown in /Users/hoge/sample.php on line 3
- 公式ドキュメントに記載されているように、許容しない型でiterable型のコールをした場合は、TypeError がスローされている
- 投稿日:2020-01-26T00:45:34+09:00
Laradocを使ってLaravel6の開発環境をインストールする
はじめに
こちらの記事はTechpitに販売されている教材を進めるにあたって、教材を参考(ほぼ丸写し)にさせてもらって作成しています。
参考箇所はプレビュー部分なので、誰でも見ることができます。
URLも貼っておきます → Laravelで飲食店検索LINE Botを作ろう!
開発者はコチラの方です → やんばるさん(@shonansurvivors)前提
Dockerをインストールしていること。
今回は略しますが、開発者のプレビューでもインストールの方法を丁寧に解説されていますので、よければご覧ください。ユーザライブラリフォルダの直下にフォルダを作成
今回はユーザライブラリフォルダの直下にlaravel-projectフォルダを作成します。
下記のコマンドを叩けばユーザライブラリフォルダの直下にlaravel-projectフォルダを作成できます。
さらにlaravel-projectフォルダに移動。$ cd $ pwd /Users/Macでのユーザー名 $ mkdir laravel-project $ cd laravel-projectLaradocのインストール
GitHubからlaradockのファイルをコピーします。
$ git clone https://github.com/Laradock/laradock.git Cloning into 'laradock'... remote: Enumerating objects: 10007, done. remote: Total 10007 (delta 0), reused 0 (delta 0), pack-reused 10007 Receiving objects: 100% (10007/10007), 8.99 MiB | 1.61 MiB/s, done. Resolving deltas: 100% (5432/5432), done.もし下記のようなメッセージ(XcodeとiOSのライセンスの同意)が出たらリンク先を参考にしてみてください。
Agreeing to the Xcode/iOS license requires admin privileges, please run “sudo xcodebuild -license” and then retry this command.Gitコマンド実行時に、XcodeとiOSのライセンス同意を求められた場合の解決法
しばらく時間がかかりますが、完了すると、laravel-projectにlaradockフォルダが作成されます。
$ ls laradocklaradockの.envファイルを作成
laradockフォルダに移動します。cpコマンドでlaradockにあるenv-exampleファイルをコピーし、.envファイルを作成します。
$ cd laradock $ cp env-example .envこれでenv-exampleファイルをもとに.envファイルが作成できました。
laradockの.envファイルを編集する
テキストエディタでlaladockフォルダの.envファイルを開き下記のように変更。
APP_CODE_PATH_HOST=../laravel DATA_PATH_HOST=../dataDockerを使って開発環境を起動する
引用させていただきます。
次にDockerを使って開発環境を起動します。
Docker上で動く各サービスの単位をコンテナと呼びます。
ここでは、以下3つのコンテナを起動します。
・workspace
・php-fpm
・nginxlaradocディレクトリにいることを確認し、下記のコードを実行する。
$ docker-compose up -d workspace php-fpm nginxdocker-composeは、複数のコンテナを同時に取り扱うDocker Composeという機能を使うためのコマンドです
upは、Docker Composeでコンテナを起動するときに使うコマンドです
-dは、コンテナを起動した後に、ターミナルの操作に戻るためのオプションです結構時間かかります。
Creating laradock_docker-in-docker_1 ... done Creating laradock_workspace_1 ... done Creating laradock_php-fpm_1 ... done Creating laradock_nginx_1 ... done上記のメッセージが表示されたらコンテナの起動が成功している。
Dockerコンテナの起動確認
ブラウザを起動し、 localhost とだけ入力。「404 Not Found」のエラーが出れば、Dockerが起動しているのでひとまずはOK。
Laravelのインストール
laradocフォルダにいることを確認した後、Laravelをインストールします。
$ docker-compose exec workspace composer create-project --prefer-dist laravel/laravel . "6.2.*" Installing laravel/laravel (v6.2.0) - Installing laravel/laravel (v6.2.0): Downloading (100%) Created project in . . . // 中略 . Package manifest generated successfully. > @php artisan key:generate --ansi Application key set successfully.てな感じでlaravel-project直下にlaravelのフォルダが作成される。
localhost へブラウザでアクセスしてみるとLaravelのウェルカムページが表示される。






