- 投稿日:2020-05-17T23:15:37+09:00
【Laravel・Eloquent】modelの関数名になぜscopeをつけるのか
scopeの定義方法
class Hoge extends Model { public function scopeFuge($query, $code) { //処理 } }関数名にscopeを追加し、引数
$query
と$code(引数名は自由)
を定義します。引数
$queryとは
この引数は、whereで取得されるのと同じBuilderインスタンスが渡されます。
$code(引数名は自由)
コントローラから渡された引数がこの値に代入されます。
例
Person.php(Model)
class Person extends Model { public function scopeNameEqual($query, $str) { return $query->where('name', $str); } }PersonController.php(Controller)
use App\Person; class PersonController extends Controller { public function search(Request $request) { $item = Person::nameEqual($request->input)->first(); //〜その他処理は割愛〜 return view('person.find, $param); } }scopeを呼び出す時は、関数名のscopeを省略します。
なぜscopeを使うのか
scopeを使うことにより、上記の2つの引数が利用できるようになります。
- 投稿日:2020-05-17T23:02:47+09:00
is_xxx() 関数を使わずに型判定できますか?
問題
\CarrotRakko
という名前空間の直下に以下の8つの関数を定義せよ。定義する関数のシグネチャと外から見た挙動は、グローバル名前空間に存在する同名の関数と同じにすること。関数/メソッド呼び出しは行なってはならない。ここで、関数/メソッドの呼び出しとはPHP処理系の実装レベルで関数/メソッドがコールされることを指すのであって、PHPコードでの呼び出し方は問わない。なお、以下のソースコードでは関数/メソッド呼び出しを行わずに実装できる部分を、関数/メソッド呼び出しによって簡潔に表現することがある。
実装に用いる言語およびバージョンは PHP 7.4.x とする。公式でない処理系や標準でバンドルされていないエクステンション、自作のエクステンションの使用などは認めない。
問1:
is_bool(mixed $var): bool
問2:is_int(mixed $var): bool
問3:is_float(mixed $var): bool
問4:is_string(mixed $var): bool
問5:is_array(mixed $var): bool
問6:is_object(mixed $var): bool
問7:is_resource(mixed $var): bool
問8:is_null(mixed $var): bool
問1:
is_bool(mixed $var): bool
最初に思いついたのは次のような実装です。
<?php namespace CarrotRakko; /** * boolean型かどうか * * @param mixed $var * @return bool */ function is_bool($var): bool { return ($var === true) || ($var === false); }boolean型 には
true
とfalse
の2値しかないのでこういうことができます。しかしながら、型の中には取りうる値が有限個ではないもの( string型, array型, object型, resource型 )や、有限個ではあるがリテラルをベタがきするには多すぎるもの( int型, float型 )があるため、boolean型 の値の種類の少なさに依存しない実装をしてみたいところです。function is_bool($var): bool { return !!$var === $var; }ここで 論理否定演算子(Logical Not Operator) について確認しておきたいと思います。
!$var
が評価されるときはまず、$var
が boolean型に変換 されます。変換された結果がtrue
であれば!$var
はfalse
に、false
であれば!$var
はtrue
に評価されます。論理否定演算子の確認が済んだところで、 booleanへの変換(Converting to boolean)を理解しましょう。リンク先を読むと、PHPの8つの型はいずれもboolean型に変換できることがわかります。
ここで、
!
を使って実装したis_bool(mixed $var): bool
関数が正しい挙動をすることを証明できるようになりました。!!$var
は常にboolean型なので、$var
がboolean型ではないときには!!$var === $var
はfalse
に評価されます。$var
がtrue
のときもfalse
のときも!!$var === $var
はtrue
に評価されるので、この関数の挙動が正しいことを証明できました。
!!
してから元々の値と比較するというような便利なことは他の型ではできそうにないので、もうちょっと一般化しておきましょう。function is_bool($var): bool { return (bool)$var === $var; }問2:
is_int(mixed $var): bool
整数への変換(Converting to integer)を読むと、boolean型, int型, float型, string型, resource型, null型の6つの型はint型に変換できるようです。array型, object型の2つの型をint型に変換しようとしたときの挙動は未定義と書いてあります。
循環呼び出しにならないように
is_array()
,is_object()
を定義できると期待すると、次のような実装ができます。<?php namespace CarrotRakko; /** * int型かどうか * * @param mixed $var * @return bool */ function is_int($var): bool { return !is_array($var) && !is_object($var) && (int)$var === $var; }問3:
is_float(mixed $var): bool
float への変換(Converting to float)を読むと、float型への変換が定義されている型とint型への変換が定義されている型は同じことがわかります。したがって、
is_int()
を実装したときと同じ期待のもとで次のような実装が可能です。float型にはNAN
というNAN !== NAN
な値があることに注意が必要です。<?php namespace CarrotRakko; /** * float型かどうか * * @param mixed $var * @return bool */ function is_float($var): bool { return !is_array($var) && !is_object($var) && ( (float)$var === $var || $var !== $var ); }問4:
is_string(mixed $var): bool
文字列への変換(Converting to string)を読みましょう。array型とobject型があやしいので実験してみます。
(string)[]; // => PHP Notice: Array to string conversion (string)(new class() {}); // => PHP Fatal error: Uncaught Error: Object of class class@anonymous could not be converted to string諦めて
is_int()
,is_float()
と同様の実装をします。<?php namespace CarrotRakko; /** * string型かどうか * * @param mixed $var * @return bool */ function is_string($var): bool { return !is_array($var) && !is_object($var) && (string)$var === $var; }問5:
is_array(mixed $var): bool
配列への変換(Converting to array)を読むと、8つの型はいずれもarray型に変換できるので、次の実装ができます。
<?php namespace CarrotRakko; /** * array型かどうか * * @param mixed $var * @return bool */ function is_array($var): bool { return (array)$var === $var; }問6:
is_object(mixed $var): bool
オブジェクトへの変換(Converting to object)を読むと、8つの型はいずれもobject型に変換できるので、次の実装ができます。
<?php namespace CarrotRakko; /** * object型かどうか * * @param mixed $var * @return bool */ function is_object($var): bool { return (object)$var === $var; }問7:
is_resource(mixed $var): bool
リソースへの変換(Converting to resource)を読むと、resource型に関しては今までと同じ戦略が通用しそうにありません。循環呼び出しにならないように
is_null()
を定義できる自信があるので、次のような実装でゴリ押します。<?php namespace CarrotRakko; /** * resource型かどうか * * @param mixed $var * @return bool */ function is_resource($var): bool { return !is_bool($var) && !is_int($var) && !is_float($var) && !is_string($var) && !is_array($var) && !is_object($var) && !is_null($var); }問8:
is_null(mixed $var): bool
遊ぶ順番を間違えた感...。
<?php namespace CarrotRakko; /** * null型かどうか * * @param mixed $var * @return bool */ function is_null($var): bool { return null === $var; }展望
関数を実装してみたはいいものの、エラーを吐かないかどうかや、挙動がビルトインの関数と同じかのテストをする必要があるなと思っています。
また、PHPには8つの型を判定する関数以外にも
is_xxx()
な名前の関数があるので、挙動を理解して再発明してみたいですね。問9:
is_a
問10:is_callable
問11:is_countable
問12:is_finite
問13:is_infinite
問14:is_iterable
問15:is_nan
問16:is_numeric
問17:is_scalar
問18:is_subclass_of
- 投稿日:2020-05-17T22:27:01+09:00
PHPで日本語のメールを送信する時のおさらい
(第〇弾なのかわからなくなってしまったが……)久々のおさらいシリーズ。
最近、仕事で単発のメールフォームを作ったのだが、メール送信周りの処理をスクラッチしたところ、受信するメーラーによって文字化けしたり、迷惑メール化しちゃったりと結構色々大変だったので、そのノウハウをまとめておこうかと思った次第。
まぁ、基本的に送信時のメールヘッダを「これでもか!」ってぐらい念入りに設定してあげるのがトラブル起こさない秘訣である。横着して簡素なメールヘッダにすると、たいていどこかでトラブルが起きるんだよね(苦笑)PHPで日本語メールを送るなら
mb_send_mail()
関数を使う日本語などのマルチバイト文字列が件名や本文に含まれるメールを送信する場合は、
mb_send_mail()
関数を使うのが安心である。この関数はmail()
のラッパーで、実体であるmail()
関数がメール送信処理を行う前に件名や本文を自動でエンコードしてくれる。いわゆるマルチバイト文字列フィルター付きのメール送信関数と云える。
この関数を実行する前に、mb_language()
関数で使用言語を指定し、mb_internal_encoding()
で内部エンコードを指定しておけば、その指定に沿った言語セットとエンコード方式でメールのマルチバイト文字を自動変換してくれるので、件名や本文などを個別にエンコード処理するといった手間や処理ミスから解放されるので、心穏やかにメール送信処理を組めるようになるのだ。
あと、多言語対応システムでメールを送信する場合に、言語や文字コードを変数化して切り替えることが容易なので、スッキリした汎用的なコードが書けるというメリットもある。mb_language( 'Japanese' ); mb_internal_encoding( 'UTF-8' ); mb_send_mail( $send_to, $subject, $body, $headers );こんな風に使うのが一般的だ。
メールヘッダの一覧
必要最小限のメールヘッダ定義だけでメール送信を行うと、受信するメーラーによって文字化けやら迷惑メールフィルタに引っかかったりと様々なケースが発生する。開発していると、動作検証するメーラーはPCのGmailとスマホのメーラーぐらいまでと、そこまで数多くのメーラーでの受信確認までは手が回らない。そのため、それら限定されたメーラー群では不具合が出なかったのでリリースしてみたものの、実際運用したら、やれこのメーラーだと文字化けした、このメーラーだと迷惑メールに分類されてしまっただのというケースがしばしば発生するのだ。
そういう出戻りを極力避けるためにも、メールヘッダはきちんと念入りに設定しておくのが最善だ。たいていメール送信における問題は、メールヘッダの設定や調整でほぼトラブルシュートができるので、先んじてきちんと設定しておくのが重要になってくる。まずは、メールヘッダの種類と設定値をここで抑えておくことにする。コーディングの基本は「理解して」使うことが何よりも重要なのだ。
下記一覧の「必要度」が「〇」なものがメールを送る際には設定しておくべきヘッダだ。「△」はなるべく設定しておいた方が良い必要度で、「-」は宛先のメール種別や転送を伴うか等必要に応じて使うヘッダとなる。まぁ、最低限「〇」と「△」のメールヘッダはすべて設定するようにしておけば、そうそうトラブルは起きないはずだ。
ヘッダ名 設定値の内容 補足 必要度 MIME-Version
英数字以外の文字や画像を取り扱える電子メールであることを示すMIMEのバージョン番号 現在は1.0のみ規定されているので固定値で 1.0
を指定する△ Content-Transfer-Encoding
メール転送中の符号化方式を指定する。初期値の 7bit
の他、8bit
、binary
、quoted-printable
、base64
が指定可能日本語を取り扱う場合は初期値の 7bit
で良い。base64
等にすると送信メールのデータ量が肥大化してしまうデメリットがあるので注意△ Content-Type
メールのメッセージ中のデータ形式。設定値の仕様は {type}/{subtype}; {parameter},...
であるテキストメールなら text/plain;
、HTMLメールならtext/html;
とし、パラメータとして文字コード指定(charset=UTF-8
等)を付与しておくことを推奨する(※後述する)〇 Return-Path
メールが届かなかった場合に、そのメールが送り返される返信先のメールアドレス。Reply-Toと異なり、メールサーバが付加するヘッダ値となる。別名「エンベローブ・フロム・アドレス」 指定しても、通常メールサーバによって上書きされてしまうが、強制的に指定値を押し通すことも可能(※後述する) 〇 From
(差出人の名前もしくは組織名と)送信元メールアドレス 複数指定可能。複数指定する場合はカンマ ,
で区切る。"{差出人名} <{送信元メールアドレス}>
"もしくは{送信元メールアドレス}
のみのいずれかで指定する〇 Sender
実際の(差出人の名前もしくは組織名と)送信元メールアドレス 複数指定不可。" {差出人名} <{送信元メールアドレス}>
"もしくは{送信元メールアドレス}
のみのいずれかで指定する〇 To
(受信者の名前と)宛先のメールアドレス 複数指定可能。複数指定する場合はカンマ ,
で区切る。FromやSenderの書式が利用可能〇 Cc
カーボンコピー先の(受信者の名前と)宛先メールアドレス 複数指定可能。複数指定する場合はカンマ ,
で区切る。FromやSenderの書式が利用可能- Bcc
ブラインドコピー先の(受信者の名前と)宛先メールアドレス 複数指定可能。複数指定する場合はカンマ ,
で区切る。FromやSenderの書式が利用可能- Resent-From
メール転送時の転送元メールアドレス 複数指定可能。仕様はFromやToと同等 - Resent-Sender
メール転送時の実際の転送元メールアドレス 複数指定不可 - Resent-To
メール転送時の転送先のメールアドレス 複数指定可能。仕様はFromやToと同等 - Resent-cc
メール転送時のカーボンコピー先のメールアドレス 複数指定可能。仕様はFromやToと同等 - Resent-bcc
メール転送時のブラインドコピー先のメールアドレス 複数指定可能。仕様はFromやToと同等 - Subject
メールの件名 mail()
では別途引数化されているのでヘッダに指定する必要はない× Comments
任意のコメント - Keywords
メール本文において注目してほしいキーワード等 メーラーによっては指定されたキーワードをハイライトしてくれたりするらしいが、ほとんど実装されていない × Reply-To
返信先のメールアドレス 一般的に、未指定時はFromの値が返信先として使用される 〇 In-Reply-To
返信時にどのメールへの返信かを示す値。Message-IDの値を指定する 複数指定不可 - References
返信などで関係している他のメッセージの一覧。Message-IDの値を指定する 複数指定可能。多くのメーラーでこの値を元にスレッド表示を行っている - Message-ID
個別のメールを特定するためのユニークID。 {ユニークID}@{ドメイン名(FQDN)}
という形式で指定する。このIDは全世界で唯一なものにならなければならないと規定されている - Date
メールの作成(=送信)日時 メールサーバ側で付与するので設定不要 × Recieved
メールを転送したメールサーバ情報を示す文字列 メールサーバが追加する文字列のため設定不要 × Encrypted
暗号化用の設定 実際には、必要に応じてメールサーバ側でメールの暗号化を行うので設定は不要 × Mail-Followup-To
主にメーリングリストで利用されるメーリングリストの宛先 複数指定可能 - Mail-Reply-To
主にメーリングリストで利用される送信元のメールアドレス 複数指定不可 - Organization
送信者が所属する組織名 △ Precedence
メールサーバの配信優先度。 list
>junk
>bulk
の順に優先されるメールサーバによってこの値を使うかどうかは異なるので、必ずしも設定した優先度通りに配信されるわけではない - Return-Receipt-To
配送確認の返信先メールアドレス。メールサーバのメールボックスにメールが届いた際に、メールサーバ側がこの返信先へ返信を行う 開封通知とは異なるので注意 - Errors-To
エラー時の返信先メールアドレス 主にメーリングリストなどで利用される - Disposition-Notification-To
開封通知の返信先メールアドレス - X-*****
X-
で始まるメールヘッダは送信元が任意に設定可能な拡張ヘッダとなるこのヘッダ値が解釈されるかは受信するメーラー側に依存するので、必須なヘッダではない × X-Sender
送信元のメールアドレスを示す メーラーごとの拡張仕様のため、使用されるかは受信したメーラー次第となる △ X-Mailer
一般的には、送信元のメールソフト(メーラー・アプリケーション)の種類を示す メーラーごとの拡張仕様のため、使用されるかは受信したメーラー次第となる △ X-UIDL
POP利用時用のユニークID POPサーバが付加する値なので設定不要 × X-Priority
メールの重要度。通常は 3 で、1(高)~5(低)で指定できる メーラーごとの拡張仕様のため、使用されるかは受信したメーラー次第となる △ X-Msmail-Priority
マイクロソフト製メーラー(Outlook等)用の重要度。仕様はX-Priorityと同じ メーラーごとの拡張仕様のため、使用されるかは受信したメーラー次第となる - いやぁ、今回メールヘッダの設定値を調べてみて、色々と勉強になった。今までそこまで意識もせずに漠然と設定してた自分に喝を入れたいものだ。
メールヘッダは省略しないで設定しよう
ここからは実践編だ。前項のリストを元に、メールヘッダを省略せずに設定していこう。基本的にメールヘッダは改行コード区切りのプレーンな文字列として取り扱われるので、最終的に文字列として
mb_send_mail()
に渡してあげる必要があるのだが、設定値が多いのでそのまま文字列として定義していくとコードの見通しが悪くなる。そこで、連想配列として定義して、最後に文字列展開するのがベストだと思う。$headers = [ 'MIME-Version' => '1.0', 'Content-Transfer-Encoding' => '7bit', 'Content-Type' => 'text/plain; charset=UTF-8', 'Return-Path' => 'from@example.com', 'From' => 'SenderName <from@example.com>', 'Sender' => 'SenderName <from@example.com>', 'Reply-To' => 'from@example.com', 'Organization' => 'OrganizationName', 'X-Sender' => 'from@example.com', 'X-Mailer' => 'Postfix/2.10.1', 'X-Priority' => '3', ]; array_walk( $headers, function( $_val, $_key ) use ( &$header_str ) { $header_str .= sprintf( "%s: %s \r\n", trim( $_key ), trim( $_val ) ); } ); mb_send_mail( $send_to, $subject, $body, $header_str );
mb_send_mail()
関数に渡すメールヘッダ文字列の変数$header_str
はarray_walk()
関数のクロージャに参照渡しされ、初回のクロージャ起動時に初期化されるのでグローバルスコープ下での初期化は不要だ。
蛇足だが、連想配列をキー・バリュー形式の文字列に展開する時は、このクロージャを使うやり方がお手軽なので、覚えておくと重宝する。
Content-Type
ヘッダには文字コードパラメータを含めること
Content-Type
ヘッダがContent-Type: text/plain;
のようなタイプ指定だけだと、Thunderbird等のメーラーで本文が文字化けする場合がある。これは、メールヘッダの指定時に、mb_language( 'Japanese' )
とmb_internal_encoding( 'UTF-8' )
で言語や文字コードを指定していても回避できない。おそらく、メーラー側でContent-Type
ヘッダ値に文字コードがない場合は、ユーザが指定しているメーラーのエンコーディング指定を優先してしまうからと推測される。
なので、対策としてはContent-Type
ヘッダには優先的にエンコードする文字コードを制御するために文字コードパラメータを含めておくことである。Content-Type: text/plain; charset=UTF-8日本語などのマルチバイト文字列を含むテキスト形式のメールを送信する場合であれば、上記のようにメールヘッダを指定しておくのが一番安全だ。
メールヘッダにマルチバイト文字列を含む場合は
mb_encode_mimeheader()
を使う
mb_send_mail()
関数がマルチバイト文字列を自動エンコードするのは「件名」と「本文」だけで、メールヘッダに含まれるマルチバイト文字列はエンコードされない。なので、メールヘッダに日本語を含むような場合は、それぞれ個別にエンコードしてあげないと文字化けが起こる場合がある(Outlookやスマートフォンのメーラー等で起きやすい)。そのため、mb_encode_mimeheader()
関数を利用して、メールヘッダを個別にエンコードしていこう。$headers = [ 'From' => mb_encode_mimeheader( '送信者の名前' ) .' <'. $from_email_address .'>', 'Sender' => mb_encode_mimeheader( '送信者の名前' ) .' <'. $from_email_address .'>', 'Organization' => mb_encode_mimeheader( '送信者の組織名' ), ];通常、メールヘッダ内でマルチバイト文字列を含む可能性があるのは上記のように「From」「Sender」「Organization」ぐらいなので、それらのメールヘッダを設定する場合には注意しておこう。一応、「To」や「Cc」「Bcc」等にも宛先の受信者名としてマルチバイト文字列を含むことができるが、システムから送信するメールについて、そこまでフランクな仕様を持ち込まなくても良いのではないかと思うので、今回はあえて割愛している。
Return-Path
ヘッダに指定した値を強制的に適用するReturn-Pathは別名「エンベローブ・フロム・アドレス」と云われ、メールサーバ(SMTPサーバ)側で指定されている値がメール送信時に割り振られる。そのため、PHP側でメールヘッダの値を設定しても通常は無視されてしまうのだ。さらに、メールサーバ側ではメール・アプリケーション(Postfixやqmail等)で最適なメールアドレスをReturn-Pathアドレスとして設定されていないと、初期値が使用されてしまう。例えばPostfixでは、postfix/canonicalで初期値が、
root root@localhost.localdomain──となっているので、受信したメールのReturn-Pathヘッダのアドレスが
root@localhost.localdomain
等の存在しない値になってしまう。このアドレスのドメイン部分が、メールが送信されたホストのDNSに登録されているSPFレコード(TXTレコードの一種)のドメインやIPアドレスと異なると、受信したメーラー側でSPF認証に失敗して「このメールは送信元をなりすましている可能性が高い」と判断して、迷惑メールに振り分けてしまうのだ。SPFレコードの詳細は、下記の記事が詳しくてわかりやすいので参照してみて欲しい。さて、これを解決するには、まずメールサーバとしてのメール・アプリケーション側でReturn-PathアドレスをSPFレコードに登録されている最適なFQDNに変更するのが最善手である(なお、そもそもDNSにSPFレコードが設定されていない場合は、どうしようもないので、それは必ずやること)。しかし、そこまでするとなるとPHP以外の知見が必要になって来て何気に面倒なので、ここではお手軽にSPFレコードに登録されているFQDNのメールアドレスをReturn-Pathとして強制的に設定してあげる方法を紹介しておく。
$from_address = 'from@example.com'; $add_params = '-f'. $from_address; mb_send_mail( $send_to, $subject, $body, $header_str, $add_params );
mb_send_mail()
関数では第5引数additional_parameterを使い、MTA(メール・アプリケーション≒sendmail
もしくは
上記の例では、-f
オプション(エンベローブ・フロム・アドレス設定オプション)のパラメータとして送信者のメールアドレスfrom@example.com
をしている。これによって、MTAが送信するメールのReturn-Pathがfrom@example.com
に固定化される。最後に、受信したメールのReturn-PathヘッダやSPFレコード認証を正常通過しているかを確認してみよう。Gmailで受信した場合での確認例になるが、受信したメールの右上の「その他」のアイコンから「メッセージのソースを表示」をクリックすると、メールヘッダが含まれる全ソースが表示される。
Return-Path: <from@example.com> Received-SPF: pass (google.com: domain of from@example.com designates ***.***.***.*** as permitted sender) client-ip=***.***.***.***;上記のように、Return-Pathヘッダが指定したメールアドレスになっていることと、Received-SPFが正常に認証されたことを示す「pass」となっていればOkだ。もしSPF認証に失敗していた場合は、SPFレコードに登録されているFQDNとReturn-Pathアドレスのドメインが一致していない可能性がある。
おまけとして、任意のホストがSPFレコードを持っているかどうかをチェックするためのPHPスクリプトを紹介しておく。
<?php $host_name = $_SERVER['HTTP_HOST'];// チェックしたいホスト名を指定する $dns_records = dns_get_record( $host_name, DNS_ALL ); $result = ''; if ( ! empty( $dns_records ) ) { foreach ( $dns_records as $_record ) { if ( 'TXT' === $_record['type'] && strpos( $_record['txt'], 'v=spf1' ) !== false ) { $result .= "The \"{$host_name}\" has a SPF record(\"{$_record['txt']}\")." . PHP_EOL; } } } if ( empty( $result ) ) { $result .= "The \"{$host_name}\" does not have a SPF record." . PHP_EOL; } echo $result; exit;変数
$host_name
にReturn-Pathとして設定したいホスト名(FQDN)を指定して実行すると、そのホストのDNSがSPFレコードを持っているかを判定して、そのSPFレコードの設定値を表示してくれる。ただし、該当のホストがSPFレコードを持っていたとしても、その設定値でメーラー側のSPF認証を通過できるかまでは検証しないので、その点は留意してほしい。MTAのTLS通信設定を確認しておく
Gmail等では、メール受信時にTLS通信がされていないメールについては警告が出る。今のところこれによって迷惑メールに分別されることはないようだが、将来的にはわからない。なので、利用しているMTAで外部へのSMTP通信時にTLS通信を使うように設定しておくと安心である。
例えば、Postfixでは外部へのSMTP通信時にTLSが利用可能ならTLS通信を使う設定が、デフォルトでOFFになっているので、有効化しておこう。Postfixでのやり方は下記の記事が参考になるかと。メール本文をテンプレート化して外部ファイルで管理する
PHP側の処理に応じてメールの本文を変更したい場合などは、メール本文をテンプレート化して外部ファイルとして管理すると、ソース内にメールメッセージをハードコーディングすることもなく見通しの良いコードが書ける。例えば多言語対応したアプリケーションで、日本語環境ユーザと英語圏ユーザとで送信メールの記述言語を切り分けたい時など、このテンプレート方式にしておくとメール送信周りは汎用コード化でき、メールメッセージへの保守はテンプレート側だけに集約できるので、運用管理が抜群にしやすくなる。
では、下記のようなテンプレートをメール本文として読み込んでみよう。templates/mail_body.php<?= $display_name ?>さん、こんにちは。 あなたのアカウントが新たに作成されました。 下記のURLから、ユーザー名:<?= $user_id ?>とご登録時のパスワードにてサインインしてください。 サインインURL: <?= $signin_url ?> 〇〇〇〇事務局より次に、読み込み元での処理だ。
// 変数定義 $display_name = 'テスト・ユーザー'; $user_id = 'test_user1'; $signin_url = 'https://example.com/signin/?uid=' . $user_id; // テンプレート読み込み ob_start(); include __DIR__ . '/templates/mail_body.php'; $mail_body = ob_get_contents(); ob_end_clean(); $mail_body = strtr( $mail_body, [ "\r\n" => "\n", "\r" => "\n" ] );// to all LFポイントはテキスト形式であるテンプレートをバッファリングによって読み込むことで、変数の埋め込み処理が完了した状態のテキストを、出力を伴うことなくメール送信用の$mail_bodyとして格納できる点だ。コードの見通しも良いし、読み込むテンプレートファイルを条件分岐させることも容易だ。
あと、最後に改行コードをすべてLFに変換しているが、これは必須ではない。稀に、テンプレートファイルで読み込んだ本文中の改行コードが、メール送信時に重複解釈されて空行が増えたり、逆に改行されないというケースが発生した場合の対処用のコードである。詳しくは、PHPのmail()のドキュメントで下記のように注意されている。メッセージが受信されなかった場合には、LF(\n)のみを使ってみてください。 Unix の MTA の中には、自動的に LF を CRLF に変換してしまう もの (有名なところでは、qmailなど) があります(もし CRLF を利用していた場合、CR が重複してしまいます)。 ただし、これは最後の手段です。というのも、これは RFC 2822に違反しているからです。
本当はテンプレート読み込み型のメール送信時に改行コードに異常が見られた場合にのみ使った方が良いのだろうが、私は面倒なので一律で変換している。
今回のおさらいはここまで。
最後に、このおさらいで紹介した一連の手順をふまえて、
mb_send_mail()
のラッパーとして拡張したメール送信処理を私の個人ブログに掲載してあるので、興味がある人は覗いてみてくださいな。
- 投稿日:2020-05-17T22:18:30+09:00
phpのarrayキャスト
(array)の挙動を確認します。
- 文字列から配列に展開されるか?
- 配列は二重配列になるか?
phpのマニュアル
// 配列と文字列は異なる値です var_dump(["a"] === "a"); // 文字列が配列に変換され一致します var_dump(["a"] === (array)"a"); // 元から配列なら変換されません // 配列で囲まれるわけではありません // 型の変換なので、配列型は配列型のままでした var_dump(["a"] === (array)["a"]); // 配列と文字列なので当然異なる値です var_dump(["a", "b", "c"] === "abc"); // もしかして複数文字は配列に展開される? ["a", "b", "c"] var_dump(["a", "b", "c"] === (array)"abc"); // そんなわけありません。 // c言語のchar型の配列と間違っていました。 // 文字列を配列にキャストすると項目が1つの配列になりました。 var_dump((array)"abc" === ["abc"]); // 元から配列なら変換されません var_dump(["a", "b", "c"] === (array)["a", "b", "c"]);
- 投稿日:2020-05-17T22:18:30+09:00
phpのarrayキャストで元々arrayが二重arrayになるわけではない
すいません。マニュアルをよく読めば書いてあることです。
(array)の挙動を確認します。
- 文字列から配列に展開されるか?
- 配列は二重配列になるか?
phpのマニュアル
// 配列と文字は異なる値です var_dump(["a"] === "a"); // false // 文字が配列に変換され一致します var_dump(["a"] === (array)"a"); // true // 元から配列なら変換されません // 配列で囲まれるわけではありません // 型の変換なので、配列型は配列型のままでした var_dump(["a"] === (array)["a"]); // true // 配列と文字列なので当然異なる値です var_dump(["a", "b", "c"] === "abc"); // false // もしかして複数文字は配列に展開される? ["a", "b", "c"] var_dump(["a", "b", "c"] === (array)"abc"); // false // そんなわけありません。 // c言語のchar型の配列と間違っていました。 // 文字列を配列にキャストすると項目が1つの配列になりました。 var_dump(["abc"] === (array)"abc"); // true // 元から配列なら変換されません var_dump(["a", "b", "c"] === (array)["a", "b", "c"]); // true
- 投稿日:2020-05-17T21:08:35+09:00
phpの「=>」(ダブルアロー演算子)と「->」(アロー演算子)
「=>」:ダブルアロー演算子
連想配列のこの「キー」にこの「値」を入れますよー
というのを表すための演算子。phpの
ダブルアローがわからなければこれを読むんだ!!http://raichel.hatenablog.com/entry/2015/01/15/012116
「->」:アロー演算子
インスタンスの持つ、プロパティにアクセスするための演算子。
この記事で私は理解した。
https://techacademy.jp/magazine/19296
インスタンスは「クラスを実体化したもの」。
インスタンスがわからないときには、オブジェクト指向を学ぼう。
- 投稿日:2020-05-17T20:49:14+09:00
LaravelでJSONリクエストを受け取る
前提条件
eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っていますControllerにメソッド追加
(1) /sample/app/Http/Controllers/SampleController.phpにrequestJson1メソッド、requestJson2メソッドを追記
public function requestJson1(Request $request) { return view('sample.requestJson'); } public function requestJson2(Request $request) { $data = [ 'a' => $request->input('a'), 'b' => $request->input('b.bb'), 'c' => $request->input('c')['cc'] ]; return $data; }(2) /sample/routes/web.phpに下記を追記
Route::get('sample/request-json1', 'SampleController@requestJson1');
Route::post('sample/request-json2', 'SampleController@requestJson2');viewの作成
/sample/resources/views/sample/requestJson.blade.phpファイル作成
requestJson.blade.php<html> <head> <title>sample</title> <script type="text/javascript"> function ajax(){ var form = document.getElementById("sampleForm"); var reqData = {"_token": null, "a": null, "b": {"bb": null}, "c": {"cc": null}}; reqData._token = form._token.value; reqData.a = form.a.value; reqData.b.bb = form.b.value; reqData.c.cc = form.c.value; var req = new XMLHttpRequest(); req.open(form.method, form.action); req.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); req.responseType = 'json'; req.send(JSON.stringify(reqData)); req.onload = function () { var json = req.response; alert("a:" + json['a'] + "\n" + "b.bb:" + json['b']+ "\n" + "c.cc:" + json['c']); } } </script> </head> <body> <form id="sampleForm" action="{{ url('sample/request-json2') }}" method="post" onsubmit="ajax(); return false;"> @csrf <input type="text" name="a" > <input type="text" name="b" > <input type="text" name="c" > <input type="submit" > </form> </body> </html>Laravelでjsonリクエストを受け取るようにするために
req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
を書きました
LaravelのControllerクラスはjsonを受け取るからと言って特別な記載は要りません
Content-Typeヘッダプロパティにapplication/jsonが指定されていれば、inputメソッドで値を取得できます動作確認
http://localhost/laravelSample/sample/request-json1
インプットフォームに値を適当に入力して送信ボタンをクリック
インプットフォームに入力した値がalertで表示された
- 投稿日:2020-05-17T20:49:14+09:00
laradock環境下のmysqlで文字化けしちゃった
はじめに
laradockで環境構築を行ってからseederでデータ投入。
その後にmysqlコンテナに入り、データが入っているか確認すると英語や数字は問題なく表示された。が、
日本語のデータが文字化けしていた。
それを解決するのにハマってしまったので、備忘録としてこの記事を書きたいと思う。原因
mysqlにて
status
コマンドで文字コードを確認。
Server characterset: utf8
Db characterset: utf8
Client characterset: latin1
Conn. characterset: latin1上記にあるように
Client characterset: latin1
Conn. characterset: latin1ClientとConnの文字コードがlatin1になってたのが原因だとわかった。
解決策
latin1をutf8にするためにlaradock/mysql/my.cnfに下記を追加した。
character-set-server=utf8
[client]
default-character-set=utf8
それから
docker-compose stop mysql
docker-compose down build mysql
docker-compose up -d mysql
でmysqlコンテナを再起動すると解決した。参考サイト
- 投稿日:2020-05-17T20:33:23+09:00
PHPの環境構築
MAMPのインストール
各種設定
- MAMPのPreferanceから使用しているPHPのバージョン確認
- Set Web & MySQL ports to 80 & 3306クリックでポート変更
- 「アプリケーション」→「MAMP」→「bin」→「php」→「使用中のバージョン」→「conf」→「php.ini」
php.ini;date.timezone = "Europe/Berlin"
以下に変更
php.inidate.timezone = "Asia/Tokyo"動作確認
適当なファイルを作ってMAMP起動する
index.php<?php echo "Hellow World";以上。
- 投稿日:2020-05-17T19:58:11+09:00
【Laravel】.env.testingの使用方法と注意点
LaravelでPHPUnitを使うときにテスト用の`.env`を設定するのにハマったので記事にしました。
環境
Docker 19.03.8
PHP 7.3.16
Laravel 6.18.3.env.testingの設定方法
本番DBを汚さないようにPHPUnitを実行する時のみテスト用のDBを使うようなことはよくあると思います。
その方法の一つとして
.env.testing
を作成し、phpunitにその設定値を読み込ませるやり方があります。
.env.example
をコピーして.env.testing
を作成し、以下の箇所を変更します。env.testingAPP_ENV=testing // testingに変更 APP_KEY= // 空にしておく // 接続したいDBの情報を設定する DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=test_database DB_USERNAME=root DB_PASSWORD=そうしたら
phpunit.xml
を開き、APP_ENV
の値をtestingに変更します。phpunit.xml<php> <env name="APP_ENV" value="testing" force="true"/> <env name="DB_CONNECTION" value="mysql"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="MAIL_DRIVER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> </php>最後に
.env.testing
に新規にアプリケーションキーを設定したら完了です。
これをしないと.env
のAPP_KEY
を参照しに行ってしまい、本番用のDBのデータが全部飛ぶので注意しましょう。php artisan key:generate --env=testingハマったポイント
docker-compose.ymlなどで環境変数に
APP_ENV
やDB_DATABASE
などの設定をしている場合、注意しなくてはいけない点があります。
phpunit.xml
で環境変数を上書きする際に、デフォルトだと<server>
タグになっていますが、これを<env>
タグに変更する必要があります。phpunit.xml<php> <server name="APP_ENV" value="testing" force="true"/> <server name="DB_CONNECTION" value="mysql"/> ... </php>phpunit.xml<php> <env name="APP_ENV" value="testing" force="true"/> <env name="DB_CONNECTION" value="mysql"/> ... </php>
<server>
タグと<env>
タグは以下のような違いになっています。$_SERVER['APP_ENV'] = 'testing'; // <server> $_ENV['APP_ENV'] = 'testing'; // <env>今回は
docker-compose.yml
で環境変数としてAPP_ENV
などを定義していたので、さらにそれを上書きする必要がありました。参考文献
Laravel 7.x テスト: テストの準備
Laravel × Docker でテスト用のデータベースコンテナを使う
- 投稿日:2020-05-17T18:49:33+09:00
ControllerからViewへの変数受け渡し
概要
Laravelの諸機能について忘れないようにアウトプットする自分用メモ。
ControllerからViewへの変数受け渡し
参考:https://qiita.com/ryo2132/items/63ced19601b3fa30e6de
参考:https://qiita.com/_Mizuki/items/df3a62fbe18bbbbc9ced
参考:http://blog.tojiru.net/article/230164798.htmlControllerからviewに値を渡す場合、ベターな方法が複数あるらしいので調べた。
phpのcompact関数
viewに値を渡す際、compact関数を使用する方法がわりとベターらしい。
compact関数の理解が怪しかったので確認した。
公式の解説では変数名とその値から配列を作成する。とのこと。公式の例
<?php $city = "San Francisco"; $state = "CA"; $event = "SIGGRAPH"; $location_vars = array("city", "state"); $result = compact("event", $location_vars); print_r($result); ?> // result Array ( [event] => SIGGRAPH [city] => San Francisco [state] => CA )良くわからん...。
自分なりに分かりやすかった例。$arr = array( 'apple' => $apple, 'orange' => $orange, 'lemon' => $lemon, ); // ↑ // 同じ意味 // ↓ $arr = compact('apple', 'orange', 'lemon');連想配列のkeyと変数名を勝手に紐付けてくれるらしい。
Controllerからviewへの変数の受け渡し
viewに値を渡すときにcompact関数を使用する場合は以下のようにするといい。
public function fruit() { $apple = 'apple'; $orange = 'orange'; $lemon = 'lemon'; return view('fruit', compact('apple', 'orange', 'lemon')); }Laravel独自のコレクションなのかと思ってたけど、そういうワケじゃないんですね...。
そもそもviewヘルパ関数がどんなだったっけと思って見直した。第二引数に連想配列渡すだけなんだ。それを作りやすいからcompact使っているだけ。
つまりこの書き方でも一緒です。public function fruit() { $apple = 'apple'; $orange = 'orange'; $lemon = 'lemon'; $fruit = ['apple'=>$apple, 'orange'=>$orange, 'lemon'=>$lemon]; return view('fruit', $fruit); }withメソッド
Controllerからviewへの受け渡しにwithメソッドを使用する場合。
public function fruit() { $apple = 'apple'; $orange = 'orange'; $lemon = 'lemon'; return view('fruit')->with('apple', $apple)->with('orange', $orange)->with('lemon', $lemon); }もしくはこう
public function fruit() { $apple = 'apple'; $orange = 'orange'; $lemon = 'lemon'; return view('fruit')->with([ "apple" => $apple, "orange" => $orange, 'lemon' => $lemon, ]); }ちょっと可読性悪いですね。
個人的ベスト
- 配列を渡す
状況にもよりますが、配列作って渡してあげる方法が一番好きです。
viewヘルパ関数の第二引数が大きくなるのはあまり好きではない。// 画面にフルーツを渡す public function fruit() { $fruit = $this->returnfruit(); return view('fruit', $fruit); } // フルーツの配列を返す private function returnfruit() { $apple = 'apple'; $orange = 'orange'; $lemon = 'lemon'; return compact('apple', 'orange', 'lemon'); }
- 投稿日:2020-05-17T17:52:00+09:00
PHPのstaticの理解〜初心者用〜
はじめに
PHPを勉強し始めると、すぐに出てくるstaticというキーワードですが
自分が理解をするのに時間がかかってしまったので
アウトプットも兼ねてまとめていきます。staticとは
インスタンスを作らずにクラス変数もしくは、
クラスメソッドを宣言すればアクセスすることができます。staticは、インスタンス(個別の実体)ではなく
クラス側にプロパティやメソッドを持たせることができます。
クラス側に持たせた変数は「クラス変数」
関数を「クラスメソッド」といいます。注意点
1.staticの変数はオブジェクトからのアロー演算子->でアクセス不可
2.sutaticではないメソッドをstaticをつけたものと同じ方法で呼び出すとエラーになる詳しくは、PHPドキュメントのこちらをご確認ください。
クラス変数やクラスメソッドの呼び出し方
クラス変数やクラスメソッドでは、アロー演算子->が使えないため
->の代わりにスコープ定義演算子(::)を
$thisの代わりにselfを使います。Nekoクラスにクラス変数とクラスメソッドを使ってみます。
test.php<?php <?php class Neko { public static $type = "飼いネコ"; public $suffix = ""; public $hp = 20; public $power = 10; function __construct($suffix) { $this->suffix = $suffix; } function name() { return $this::$type . "の" . $this->suffix; } function attack($owner) { print $this->name() . "が" . $owner . "を攻撃し" . $this->power . "ポイントのダメージを与えた!" . PHP_EOL; } //クラスメソッド static function description() { print self::$type . "は、凶暴だが最愛の家族だ。" . PHP_EOL; } } Neko::description(); $neko_A = new Neko("モカ"); $neko_B = new Neko("カフェ"); $neko_A->attack("飼い主"); $neko_B->attack("他のネコ");コンソール入力
$php tex.php実行結果
飼いネコは、凶暴だが最愛の家族だ。 飼いネコのモカが飼い主を攻撃し10ポイントのダメージを与えた! 飼いネコのカフェが他のネコを攻撃し10ポイントのダメージクラスの説明をする関数
description()という名前のクラスメソッドを用意しました。Neko::description();と書いて呼び起こしていますが
アロー関数->を使うとどうでしょう。// Neko::description(); だったものを Neko->description();実行結果
PHP Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR)となり、変なところに->があるねん!という内容になります。
なので、staticをつけた変数や関数はクラス名::○○と書く必要があるということですね。(変数を参照したい場合はスコープ定義演算子の後に$を入れてください)まとめ
スコープ定義演算子が出てきた時は、
アロー演算子を覚えたばかりだと戸惑ってしまいますが
staticを使った関数の場合はアロー演算子ではなく
スコープ定義演算子を使うと覚えれば良さそうです。
・クラス名::○○ static宣言されたメソッドやプロパティへアクセスする
・self::○○ 自クラスのクラス変数、クラスメソッドへアクセスする
・parent:: 指定した親クラスを示す
・インスタンス変数::○○ (self::○○) インスタンスが持つクラス変数やクラスメソッドへアクセスする
- 投稿日:2020-05-17T17:26:25+09:00
PHPのstatic とは、静的とは
static(静的)概要
PHP Manualより
クラスプロパティもしくはメソッドを static として宣言することで、 クラスのインスタンス化の必要なしにアクセスすることができます。 static なプロパティは、インスタンス化されたクラスオブジェクトから アクセスすることはできません
つまりクラス外から staticメソッド(静的メソッド)にアクセスする場合、
Foo::staticではこのメソッドにアクセスできるが、foo->static()の形ではアクセスできない。staticメソッド(静的メソッド)についての備忘
- 静的(静的メソッド、静的プロパティ)というのは、インスタンス化しないで使うということ
- なのでインスタンスに影響を受けない(保守しやすい)
- 基本宣言されたクラス内で使用するケースが多い(self::xxxの形)
- 平たく言うと、インスタンス化が必要な時は、staticはつけない、インスタンス化が不要な時はstaticをつける
- アクセスする際は、
::
を使用する。::
はスコープ定義演算子と言う- 無闇にインスタンスを実装して保守範囲を広げないために静的を利用する
staticメソッド(静的メソッド)にアクセスする例
sample.php<?php class Foo { public static function aStaticMethod() { echo "Hello Static!!!"; } } Foo::aStaticMethod(); //or $classname = 'Foo'; $classname::aStaticMethod(); //出力結果 "Hello Static!!!"; //staticなメソッドには->ではアクセスできない $foo = new Foo(); $foo->aStaticMethod; //出力結果 PHP Notice: Undefined property:xxx ?>参考
PHP: 静的メソッドは何のためにあるか?
self::と$this->の使い分け
PHP - What is OOP?
- 投稿日:2020-05-17T15:08:25+09:00
PHPで画像をアップロード
学習をしていく中で、理解が難しかったので、要点をまとめてみました。
まず前提として、HTMLでフォーム画面に以下の記述が必要になります。
<input type="file" enctype="multipart/form-data" method="post" name="img"> //nameは任意 //enctypeは画像の情報を送信するのに必要なおまじないと思ってください。画像をアップロードする流れ
①移動先のディレクトリを変数に格納する
②一時的に保存するファイルを変数に格納する
③アップロードしたい画像のファイル名を変数に格納する
④move_file_uploads関数でアップロードする①移動先のディレクトリを変数に格納する
$uploads_dir = './uploads';のちの記述をすっきりさせるために格納させています。
②一時的に保存するファイルを変数に格納する
$tmp = $_FILES['img']['tmp_name']; //imgはinputで記述したnameの中身です var_dump($tmp); //string(14) "/tmp/phpimzszk"tmp_nameとはアップロードした画像ファイルをサーバー上で一時的に保存する場所のファイル名です
③アップロードしたい画像のファイル名を変数に格納する
$name = basename($_FILES['img']['name']); //imgはinputで記述したnameの中身です var_dump($name); //string(9) "melon.jpg" 画像ファイルbasename関数でファイル名を取得します。この記事がかなり分かり易かったので参考にしました。
https://www.flatflag.nir87.com/basename-844
$FILES['img']['name']がファイル名です④move_file_uploads関数でアップロードする
move_file_uploads($tmp, "$uploads_dir/$name"); //move_file_uploads(もとにあるファイル名, 移動先のファイル名)詳しくはこちらをご参照ください
https://www.php.net/manual/ja/function.move-uploaded-file.php
- 投稿日:2020-05-17T14:04:17+09:00
PHP 初心者向け実行環境(Local by Flywheel)と練習問題
対象者
初めてプログラミングを学習したい方
(用語の解説等はあえてしません)
※わからないことはすぐに調べる癖をつけよう!Windows 10 64bit
事前準備
- Visual Studio Codeの導入(IDE)
- Local by Flywheelの導入
Visual Studio Code(IDE)
https://azure.microsoft.com/ja-jp/products/visual-studio-code/
ここからVisual Studio Codeをダウンロードインストールが終わったらプラグインを導入しよう
最低限欲しいプラグイン
- Japanese Language Pack for VS Code 日本語化のプラグイン
- Prettier コードのフォーマット自動でおこなってくれる
Local by Flywheel
環境構築がめちゃくちゃ手軽なので選びましたが、XAMPPでもおっけーです。
こちらを参考に
PHPを実行する
まで進めて動作確認をしよう
練習問題
環境構築が既に終わってる方はこちらから
ちなみにですが、模範解答はまた後日
その1
加算演算子を使用して以下の変数を計算して下さい
study01.php$a = 2 $b = 3 $c = "答え"その2
以下の変数とif, else if, else とループ文(for, foreach等)を使用し
50点未満なら「不可」
50点以上65点未満なら「可」
65点以上80点未満なら「良」
80点以上なら「優」というコードを作成してください。
70点の場合の結果を表示させてください。study02.php<?php $result = array("不可", "可", "良", "優"); ?>その3
study03.phpとstudy04.phpを流用して、
if文とループ処理分(for, foreach等)を使い「"言語名"は"難易度"です」と表示してください。
例: C言語を選択したとき
C言語は難しいです
※プログラムの難易度は実際と違いますC言語 = 難しい
Java = 普通
JavaScript = 優しいstudy03.php<html> <head> <title>プログラムの難易度</title> </head> <body> <form action="study04.php" method="post"> 習得したいプログラム言語を選択してください<br> <input type="radio" name="choice" value="1">C言語 <input type="radio" name="choice" value="2">Java <input type="radio" name="choice" value="3">JavaScript <input type="submit" value="送信"> </form> </body> </html>study04.php<html> <head> <title>プログラム言語難易度</title> <body> あなたが選んだプログラム言語は <?php $program = array( "1" => "C言語", "2" => "Java", "3" => "JavaScript" ); $difficult = "難しい"; // C言語 = 難しい $normal = "普通"; // Java = 普通 $easy = "易しい"; // JavaScript = 優しい // ここにコーディング ?> です。 </body> </html>
- 投稿日:2020-05-17T07:00:57+09:00
PHPにてPDOでMySQLに接続して、複数のSQLを実行する
1つのPHPファイル内に、INSERTとSELECTのSQLを記載する方法です。
DB接続1回で複数のSQLが実行できます。
2個目のSQLでは配列$dataの値をクリアすることがポイントです。以下のソースコードは、ユーザーID登録実行画面の想定です。
前画面のユーザーID登録確認画面からユーザー名を受け取り、DB登録しています。<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="description" content="XXXX"> <title>XXXX</title> <link rel="stylesheet" href="/main.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> </head> <body> <?php //データベースの接続エラー対策 try { // POSTメソッドで前の画面の入力値を取得する $user_name=$_POST['user_name']; //データベースに接続する $dsn = 'mysql:dbname=XXXX;host=mysqlXXXX'; $user = 'XXXX'; //''内にはユーザ名を入力 $password = 'XXXX'; //''内にはパスワードを入力 $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //7桁数値をランダムで生成する $user_id= rand(100000,9999999); //ランダムで生成した7桁数値の存在チェック。$user_idがDBに存在する場合、再度ランダム数値を生成する $sql ='SELECT COUNT(*) as cnt FROM XXXX_table WHERE user_id=?'; $stmt = $dbh->prepare($sql); // prepareメソッドでSQLをセット $data[] = $user_id; // 1つ目の?にセットしたいデータが入っている変数を書く $stmt->execute($data); //executeでDBへの処理要求を実行 $rec=$stmt->fetch(PDO::FETCH_ASSOC); // SQL分を使ってレコードを追加 $sql ='INSERT INTO place_data_table(place_id) VALUES (?)'; //'INSERT INTO~'を変数$sqlに格納する $stmt = $dbh->prepare($sql); // prepareメソッドでSQLをセット $data = []; // 配列$dataの値をクリア $data[] = $user_id; // 1つ目の?にセットしたいデータが入っている変数を書く $stmt->execute($data); //executeでDBへの処理要求を実行 // データベースから切断する $dbh = null; //結果を表示 print $user_name; print 'を追加しました。<br />'; } catch (Exception $e) { /* ?><pre><?= print_r($e, true); ?></pre><?php デバッグの時にはコメントアウトを外す。エラー内容、問題が発生した行番がわかる*/ print 'ただいま障害により接続できません。'; exit(); //強制終了の命令 } ?> <a href="user_list.php">戻る</a> </body> </html>
- 投稿日:2020-05-17T06:52:30+09:00
有料のWebサービスをリリースするまでに取り組んだこと・知見をまとめました【個人開発】
1. 作ったサービス
ツイ消し職人
https://twikeshi.net/ツイ消し職人は大量のツイートを消したい方向けのツールです。
3,200件を超えるツイートを一括削除できます。
無料のツールなどでうまく削除できなかった方は是非ご利用ください。
既存のフォロワーをそのままに、Twitterをやり直すことができます。2. 自己紹介
こんにちは、ひろと申します。
今年の3月に会社を辞め、現在はフリーランスエンジニアとして活動しています。メガバンクのシステムエンジニア →
広告代理店(東証一部上場企業)のWebエンジニア →
フリーランスエンジニアという経歴です。仕事でコードを書き始めたのは前職からで、プログラミングの経験年数は2年と9ヶ月くらいです。
3. なぜ作ったのか
私は今年の3月にフリーランスとして独立しました。
それに伴い、学生時代から使っていたTwitterアカウントの運用を変えようと思い、今までのツイートを削除してやり直すことにしました。
アカウントを作り直す選択肢もあったのですが、フォロワーを減らしたくなかったため、ツイ消しの道を選びました。
調べてみるとツイートの一括削除ツールがいくつか見つかったため、それを使ってツイ消しをすることにしました。しかし、既存のツールではツイートの削除ができませんでした。
私の今までのツイート数は19万件で、Twitterアーカイブをダウンロードしたところzipファイルのサイズはなんと31GB。スマホの7GBプランなら4.5ヶ月分の通信量が必要になってしまう、とんでもない容量です。
そう、私がツイ廃だったのが全ての原因です。普通のツイ消しサービスは、API制限の関係で3,200ツイートが削除の上限となってしまいます。私の19万ツイートに対してはあまりに無力すぎました。
もちろん、API制限を回避するためにTwitterアーカイブをアップロードして削除を行うサービスもあります。
しかし、31GBのzipファイルを送りつけると必ず500エラーが返ってきてしまい、私の試した範囲では、まともに動くものはありませんでした。海外の有料サービスでさえダメでした。(具体的なサービス名は出しませんが、日本円で1,600円払いました。手痛い出費です)そこで私は、「ツイ廃でもツイートを削除できるサービス」が必要だと思い、ツイ消し職人の開発を始めました。
4. リリースするまでに取り組んだこと
取り組んだ全てのことを記載しています。
一. サービスの命名
最初は「ツイートクリーナー」という名前にしていました。
開発中盤に「ツイ消し職人」という名前を思いつき、変更しました。
ランサーズなどで募集するのも良いと思います。二. ドメインの取得
ムームードメインでtwikeshi.netを取得しました。
欲しいドメインが埋まっている場合は、twikeshi-app.netのように工夫するのも良いと思います。三. 商標権の取得
今回はお金がなかったので保留しています(いつでも取れるように商標調査は終えています)。
今はToreruなどの便利なサービスがあり、ものすごく簡単に出願できます。
48,000円で5年間有効になります。
商標権の取る取らないを選択するのは自由ですが、後から商標を第三者に取得されて商標権の侵害警告を受けた場合、サービス名やドメインを変える必要があるリスクは認識しておく必要があります。四. プライシング
海外の同じようなサービスを参考に値付けを行いました。
現在は700円(税込)で提供しています。その後、プライシングに関する本を3冊読んで(この本とこの本とこの本)考え方が変わったので、そのうち値上げするかもしれません。
どんなに高くても、その価格で買いたい人がいます。私自身も、このサービスを他の人が作っていたとしたら、喜んで利用していました。
間違っても、本来ターゲットでない人を取り込むために値下げするのはやめてください。
例えば、私は友人達に「ツイ消し職人の適正価格はいくらだと思う?」と質問をすると、2人が「100円」と答えました。
しかし、断言しますが彼らは100円でも絶対に利用しません。何故ならば、彼らはこのツールの価値を理解していないからです。ツイ消しをしようと思ったことがない人に相場感を聞いても意味がありません。
逆に、本っっ当にツイ消しをしたくて困っている人からすれば、このツールが例え1万円でも喜んでお金を払うはずです。五. 技術選定
1. バックエンド
バックエンドはLaravelで開発しました。
私は前職でSpring Bootを使っていましたが、このフレームワークではTwitterログインを実装するのに苦労しそうだったため、一からLaravelを学ぶことにしました。
Railsと悩みましたが、後述する理由によりレンタルサーバーで運用したかったので、Railsは諦めました。
Laravelはコードもドキュメントも読みやすいため、使っていて楽しいですね。
ドットインストールのLaravel入門がとても分かりやすかったのでオススメです。
役割 技術 PHPフレームワーク Laravel データベース MySQL Twitterログイン Laravel Socialite Twitter APIライブラリ TwitterOAuth メール送信 SendGrid 2. フロントエンド
CSSフレームワークにはMaterializeを採用しました。これも初めて使ったのですが、ドキュメントが分かりやすく情報量も多いのでオススメです。
今回はフロントで処理をする必要が無かったので、基本的にJavaScriptは使っていません。ファイルアップロードの画面は、アニメーションを付けるためにVue.jsを使いました。Vue.jsは以前から使っていたので、特に困ることはありませんでした。
次はNuxt.jsに挑戦するために勉強中です。
役割 技術 CSSフレームワーク Materialize JavaScriptフレームワーク Vue.js 決済 Stripe Checkout 3. インフラ
バックエンドの項目で触れましたが、サーバーにはレンタルサーバーを採用しています。
役割 技術 レンタルサーバー エックスサーバー Heroku / VPS / AWS EC2 / GCP App Engineなどの選択肢もありましたが、主にコストと運用の観点から除外しました。個人開発は自分でインフラを選べるのが良いですね。
今後もどんどんサービスを作っていく予定なので、サーバー費がかさむのはイヤだし、サービスを作る度に環境を構築するのも避けたかったのです。もちろん要件によってはレンタルサーバーが使えない場合もあります(ミドルウェアの設定変更や追加インストールが必要な場合など)。
rootユーザーが使えないと困る場合は、状況に応じて各サービスを比較検討しましょう。
サーバーはHerokuだけどストレージにはAWS S3を使って、DBにはGCP Cloud SQLを使うといったトリッキーなこともできます。柔軟な発想で最適な構成を作りましょう。参考に、私の考える主なインフラサービスのメリット・デメリットをまとめておきます。
※App Engineは詳しくないので簡易的な記載になってます
インフラサービス メリット デメリット VPS(IaaS) ・安い
・root使える・借りる度にお金がかさむ
・環境構築や設定が必要EC2(IaaS) ・root使える
・マイクロサービス沢山ある・高い
・借りる度にお金がかさむ
・環境構築や設定が必要App Engine(PaaS) ・環境構築不要 ・高い
・借りる度にお金がかさむHeroku(PaaS) ・安い
・環境構築不要・借りる度にお金がかさむ
・30秒タイムアウト辛いレンタルサーバー(ほぼPaaS) ・安い
・1台でアプリ沢山動かせる
・環境構築ほぼ不要・root使えない 六. 設計
小規模なサービスなので、ここにはほぼ時間をかけていません。
ワイヤーフレームなどは作らず、実際に画面をコーディングしてレイアウトを決めました。
DB設計もパパッと考えて終わり。
開発の中で必要になったときに都度、テーブルやカラム・画面を増やしていきました。
サービスによっては色々な機能を思いつくと思いますが、まずはスモールスタートでリリースすることをゴールにしましょう。YAGNIは正義。七. 開発
一番時間をかけたのはこの工程です。
他の仕事が並行していたため正確ではありませんが、全体で2〜3週間はかかったと思います。
伝えたい情報がある場合はコメントを書いています。
有料サービスのみ必要になる項目には「☆」を付けています。1. サービスの機能開発
一. Twitterログイン
ツイ消し職人では、決済完了時とツイート削除完了時に確認メールを送信しています。
そのため、Twitter AppのAdditional permissionsとして、Request email addressにチェックを入れています。二. ☆決済
Stripe Checkoutは神。
JavaScriptをちょろっと書くだけで決済を提供できます。返金もボタンポチるだけです。
ツイ消し職人はクレジットカード、Google Pay、Apple Payに対応しています。三. アーカイブアップロード
Twitterが生成したデータを読み取らないといけないので、アーカイブのどのファイルに何の情報があるのかを全て自分で調べました。
そして、ツイートの削除に本当に必要なファイルだけをアップロードさせることで、ファイルサイズを31GB→200MBまで減らすことができました。アーカイブからはツイートの削除に必要な情報を正規表現で抽出する必要があります。
最初は、JavaScriptを使いフロント側で情報を抽出し、サーバーには最低限のデータだけ送るようにする予定だったのですが、少し時間がかかりそうだったので諦めました。FileReader.readAsText()に100MBのファイルを食わせるとクラッシュしてしまうことが判明し、ファイルをチャンクして処理する必要が出てきたためです。
コンソールにエラーは出力されず、サイレントでクラッシュするので問題の特定に時間がかかりました。マジでやめてほしい。
サーバ側で抽出処理をやっても特に問題はないので、サーバ側で処理するようにしました。四. ツイート削除
ノーコメント
五. 非同期処理
アップロードされてそのままツイートの削除を行うと、画面がタイムアウトしてしまいます。
そのため削除処理はLaravelのキューを使って非同期にしています。
失敗時の再実行もできるようになるので便利ですね。六. メール送信
必ずユーザーに到達するようにSendGridを使っています。
返信や問い合わせを受けるためにはメールサーバーの設定が必要なので注意してください。
サーバーが用意できない場合は、G Suiteなどのホスティングサービスを利用しましょう。七. ログ出力 + Slack通知
本番での例外発生時にはSlackにスタックトレースを飛ばすようにしています。
他にも、ツイート削除処理成功時など、正常系でも重要なものはSlackに通知を飛ばしてます。ログは、出せる項目をなるべく出すようにしています。
Laravelのログ出力について記事書いてるので興味あったら読んでください↓
【Laravel】ログのフォーマットを変更してIPアドレスやユーザー名などを出力する2. リリース準備
一. LP(トップページ)作成
ユーザーに効果的に訴求できる文言を考える必要があります。
デザイナーの人は腕の見せどころだと思います。
文字や画像・アニメーションを使っていい感じのレイアウトにしましょう。ペライチなどのツールを使っても良いと思います。
コンバージョンに直結するので、一番力を入れるべき部分です。外注も考えましょう。二. 利用規約・プライバシーポリシーの制定
この本が大変参考になりました。コピペできるひな形データも付いてくるのでオススメです。
三. ☆特定商取引法に基づく表示の作成
同上。
有料サービスの場合は必須です。
本名や住所、電話番号を晒さないといけないので、ここが一番の難関ではないでしょうか。
私の場合、IP電話アプリ(SMARTalk)を使い050から始まる電話番号を載せています。四. Googleアナリティクス・Search Console設定
ノーコメント
五. meta description設定
Googleの検索結果でタイトルとともに出るやつです。
meta keywordは不要です。六. ファビコン設定
GIMPで作りました。
サービスのロゴがある場合はファビコンにも活かせます。七. OGP設定
GIMPで作りました。
OGP画像を動的に生成するサービスでは、トップページ用の画像を同じ方法で作るのも良いかも。
CTRに直結するので、ここも外注を検討しましょう。八. サイトマップ設定
Search Consoleで送信します。
sitemap.xml Editorを使うと簡単に作成できます。
サイトが新しく、外部からのリンクが少ない場合はあったほうが良いみたいです。九. SNSシェアボタンの設置
↑こういうのです。
ユーザーに拡散してもらえる仕組みを作っておくことは重要です。
ちなみにツイ消し職人は全く拡散されていません。悲しい。十. お問い合わせフォームの設置
自分で作るのがめんどくさい場合は、Googleフォームやformrunなどを活用しましょう。
お問い合わせフォームの代わりに、チャットサポートツールを入れるのもオススメです。3. リリース
一. デプロイ
Laravel + レンタルサーバーの場合はgit pullすればほぼ終わりです。
あとは.env書いてマイグレーションしてキャッシュ系のコマンドを叩くだけです。
もちろん、GitHub ActionsなどのCIを設定するのも良いと思います。私の場合は、以下のようなデプロイスクリプトを用意しています。
deploy.sh#!/bin/sh git pull composer install --optimize-autoloader --no-dev php artisan config:cache php artisan view:cache php artisan route:cache二. テスト
本番環境で全ての機能がうまく動くことを確認しました。
中規模〜大規模サービスの場合は、検証環境の用意とテスト自動化がされていないと運用がしんどくなります。八. リリース後
1. 知り合い・友人への拡散
LINE, Twitter, Facebookなどで拡散して使ってもらいましょう。
2. プレスリリースを出す
お金があればPR TIMESなどの有名サイトに出すのがオススメです。
(もしくは、法人ならスタートアップチャレンジの条件を満たすと無料になります)私はお金がなかったので、valuepressのフリープランで配信しました。
3,200件を超えるツイートを一括削除できるツイ消しサービス「ツイ消し職人」を提供開始3. アプリ紹介サイトに登録
私の場合、AnyMakeやmakepost、Eggineer、Applishowを活用しています。
4. 新聞の広告枠に出稿する
リリース直後にスポーツ新聞の方から電話があり、新聞とサイトに広告を載せないかと打診がありました。
条件が合わなかったためお断りしましたが、人によっては選択肢になり得ると思います。5. アフィリエイト広告に出稿する
現在検討中です。
お金がある場合は、A8.netなどの大手ASPを使うのが良いと思います。
もしもアフィリエイトやマネートラックならば初期費用0円・月額費用0円で始められるようです。6. SNS広告に出稿する
現在検討中です。
私の場合はTwitterユーザーをターゲットにしたサービスなので、Twitter広告と相性が良いです。
とりあえず試してみて、どれくらい成果が出るかチェックしてみようと思います。7. 保守開発
本番環境でのエラーを監視し、新しく発現したバグがあれば修正しましょう。
手元で再現しないエラーは...ユーザー問い合わせを待つしか無い。。もちろん、機能追加などサービス改善のための開発は怠らないようにしましょう。
大幅リニューアルや作り直しなどの選択肢もあります。8. ブログなどでの発信
この記事のことですね。
Qiita, Crieit, Note, ブログなどの選択肢があります。
サービスを知ってもらうだけでなく、転職活動などでも役に立ちます。
- 投稿日:2020-05-17T04:38:49+09:00
phpについて(初心者)その1
はじめに
こちらではphpについて問合せがきた内容について書いていきたいと思います。
(適切な題名が思い浮かばなかったためその1といたします)
はじめはどう書いたらいいのかわからなく、初歩的な内容だとそもそもググっても出てこず、挫折することもあると思います。
こちらではその内容を解決する手助けができればと思います。コードは問合せ者からいただいたものを部分的に抽出し、書き換えたものです。
自分のコードと置き換えつつ解決できればとおもいます。
またこれは1つの案にすぎません。
こんなやり方もあるんだなと参考程度になればと思います。
また、もっとスマートな方法があるとは思いますが、ご容赦いただければと思います。環境
CentOS
Apache
Vagrant
※こちらは筆者の環境であり問合せ者の環境ではありません内容
問合せ:htmlから取得した内容をフィールドに反映したいが一つの
フィールドに複数の要素を反映したい。いただいたコード(実際はもっとながいですがこん今回は一部抜粋)
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>インプット</h1> <header> <form action="test.php" method="post"> <table border="3"> <tbody> <tr> <td align="left" valign="top" class="title">name</td> <td><input type="text" name="comment"></td> </tr> <tr> <td align="left" valign="top" class="title">checkbox</td> <td> <input name="checkbox" type="hidden" value=""> <input type="checkbox" name="checkbox[]" value="checkbox1">checkbox1</input> <br> <input type="checkbox" name="checkbox[]" value="checkbox2">checkbox2</input> </td> </tr> </tbody> </table> <input type="submit" value="送信" class="button"> </form> </body> </html>test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php $Array = $_POST; foreach ($Array as $key => $value) { if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } if ($key == 'checkbox') { echo "<tr>"; echo "<td class='title'>".$key."</td>"; for ($i = 0; $i < count($value);++$i) { echo "<td>".$value[$i]."</td>"; } } } ?> </table> </body> </html>画面1
遷移後画面
フィールドが新しく作られるようになっています。
なのでコードの内容を噛み砕いていきたいお思います。test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php #入力内容の取得 $Array = $_POST; foreach ($Array as $key => $value) { #コメントレコードの作成 if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } #チェックボックスレコードの作成 if ($key == 'checkbox') { echo "<tr>"; #kayフィールドの作成 echo "<td class='title'>".$key."</td>"; #valueフィールドの作成 echo "<td>"/*ここに書く*/"</td>"; } } } ?> </table> </body> </html>test.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="css/style.css"> </head> <body> <header> <h1>確認画面</h1> </header> <table border="2"> <?php $Array = $_POST; foreach ($Array as $key => $value) { if ($key == 'comment') { echo "<tr>"; echo "<td class='title'>" . $key . "</td>"; echo "<td>" . $value . "</td>"; echo "</tr>"; } if ($key == 'checkbox') { echo "<tr>"; echo "<td class='title'>".$key."</td>"; echo "<td>"; #for文の配置変更 for ($i = 0; $i < count($value);++$i) { #ここから加えたコード if($i==0){ echo $value[$i]; }else{ echo ",".$value[$i]; } #ここまで } echo "</td>"; } } ?> </table> </body> </html>まとめ
いかがだったでしょうか、なるべく変更を加えることなく期待結果になるようにしてみました。
解説が下手でわかりにくかもしれませんが少しでも助けになればと思います。
- 投稿日:2020-05-17T02:30:12+09:00
VSCode でPHPをステップ実行するまでの3つの手順 (ローカル版 + VM環境版)
更新
2020/05/17launch.json
に pathMappings の例を追記
- 今日も明日も明後日も VSCode でしょう?
ゴール
- 以下の場合でステップ実行できること
- デバッグ対象がローカル -- ローカルに
PHP, Web サーバ, ソースコード, Vscode
がある場合- デバッグ対象がVM上 -- ゲストOSに
PHP, Web サーバ, ソースコード
ホストOSにVScode
がある場合書かないこと
- PHPと Web サーバのインストール手順
準備
拡張
PHP Debug
をインストールデバッグ構成ファイルを作る
/.vscode/launch.json{ "version": "0.2.0", "configurations": [ { "name": "Listen for XDebug PHP", "type": "php", "request": "launch", "port": 9000, // 以下、デバッグ対象がVM上にある場合のみ書く "pathMappings": { // ゲストOSのソースの場所 : VSCode のあるホストOSから見えるゲストOS上のソースの場所 // e.g. いずれかひとつでOK // "C:/app/src/wwwroot" : "//guest-hostname/wwwroot" // "C:/app/src/wwwroot" : "//192.168.100.101/wwwroot" // "C:/app/src/wwwroot" : "${workspaceRoot}" } } ] }
Xdebug
をインストールⅰ.
php -i
の結果 (全部) を https://xdebug.org/wizard に張り付けるⅱ. DLLをダウンロード
ⅲ.
php.ini
と同じ階層にext
フォルダを作りそこにダウンロードしたDLL
を置く ※php.ini
の場所は上述のコマンドで出力されているⅳ. Xdebug を設定
/php.ini; ローカルのみで環境をつくってる場合 zend_extension = php_xdebug-2.9.5-7.4-vc15-x86_64.dll xdebug.remote_enable = 1 xdebug.remote_autostart = 1 xdebug.remote_host=localhost ; - - - ; デバッグ対象がVM上にある場合 zend_extension = php_xdebug-2.9.5-7.4-vc15-x86_64.dll xdebug.remote_enable = 1 xdebug.remote_autostart = 1 ; ゲストOSから見たホストOSのIP xdebug.remote_host=192.168.100.101ⅴ. Web サーバを再起動
(デバッグ対象がVM上にある場合のみ) 公開ルートをホストと共有
C:/app/src/wwwroot
を共有フォルダに設定するステップ実行
- 「ブレークモード」になることを確認
余談ですがブレークモードで
ctrl + alt + w
してw
だけ離してa
で選択してる式をウォッチ式に追加できますね。この辺り Visual Studio のショートカット体系に寄せてくれていて、ありがたいです?参考
Documentation - all settings: https://xdebug.org/docs/all_settings
- 投稿日:2020-05-17T02:07:47+09:00
AWSのELB(Elastic Load Balancing) 構成時に Auth0 認証の例外 (Invalid state) に対処する
経緯
- ローカル環境 (ロードバランサー無し) ではうまく認証できる。
AWS上 (ロードバランサーあり) だと
Invalid state
でエラーになる。/Auth0.phpthrow new CoreException('Invalid state');https://github.com/auth0/auth0-PHP/blob/660163b31beae4c550db68022c568b41df75d8e9/src/Auth0.php#L518
エラーの原因を調べる。どうやら xxx.auth0.com 側の認証処理後に
callback url
に乗せて Get パラメータで戻されるstate
の値とサーバのstate
の値に相違がある...。結論
EC2 > ロードバランシング > ターゲットグループ > 属性の編集 > 「維持設定 有効化:
on
」 にする。注: 根本解決ではないが、ひとまずこの対処でいくことにした。
一定負荷を超えた場合に備えてスケーラブルなサービスを提供している場合、この方法ではのちのち問題になるのでAmazon ElastiCache
を使うのが定石らしい。余談
んなことで2時間ちかく時間が...けどこれロードバランサー使うときの常識なんだろうな?
ちゃんと理解しておかないと他のトラブル時に対処できない、まずいな。参考
スティッキーセッションというらしい。
https://hack-le.com/sticky-session/
- 投稿日:2020-05-17T01:47:22+09:00
VSCode でPHPをステップ実行するまでの3つの手順 (ローカル版 + VM環境版)
追記: (1)参考に
XdebugDocumentation - all settings
のURLを追記した。(2) XDebug を Xdebug に表記修正。今日も明日も明後日も VSCode でしょう?
ゴール
- 以下の場合でステップ実行できること
- デバッグ対象がローカル -- ローカルに
PHP, Web サーバ, Vscode
がある場合- デバッグ対象がVM上 -- ゲストOSに
PHP, Web サーバ
ホストOSにVScode
がある場合書かないこと
- PHPと Web サーバのインストール手順
準備
拡張
PHP Debug
をインストールデバッグ構成ファイルを作る
/.vscode/launch.json{ "version": "0.2.0", "configurations": [ { "name": "Listen for XDebug PHP", "type": "php", "request": "launch", "port": 9000, // 以下、デバッグ対象がVM上にある場合のみ書く "pathMappings": { // ゲストOSのソースの場所 : VSCodeが認識するソースの場所 "C:/FURUNOSYSTEMS/UNIFAS/WWWROOT" : "//Win-5vkkk6q23vh/wwwroot" } } ] }
Xdebug
をインストールⅰ.
php -i
の結果 (全部) を https://xdebug.org/wizard に張り付けるⅱ. DLLをダウンロード
ⅲ.
php.ini
と同じ階層にext
フォルダを作りそこにダウンロードしたDLL
を置く ※php.ini
の場所は上述のコマンドで出力されているⅳ. Xdebug を設定
/php.ini; ローカルのみで環境をつくってる場合 zend_extension = php_xdebug-2.9.5-7.4-vc15-x86_64.dll xdebug.remote_enable = 1 xdebug.remote_autostart = 1 xdebug.remote_host=localhost ; - - - ; デバッグ対象がVM上にある場合 zend_extension = php_xdebug-2.9.5-7.4-vc15-x86_64.dll xdebug.remote_enable = 1 xdebug.remote_autostart = 1 ; ゲストOSから見たホストOSのIP xdebug.remote_host=192.168.100.xxxⅴ. Web サーバを再起動
ステップ実行
- 「ブレークモード」になることを確認
余談ですがブレークモードで
ctrl + alt + w
してw
だけ離してa
で選択してる式をウォッチ式に追加できますね。この辺り Visual Studio のショートカット体系に寄せてくれていて、ありがたいです?参考
Documentation - all settings: https://xdebug.org/docs/all_settings