- 投稿日:2020-07-24T23:41:07+09:00
aibo Events API を使って aibo に音声コマンドを実行してもらう
先週、先々週と仮面ライダーゼロワンに
天津垓の相棒である AI 犬型ロボットさうざー役として出演していた aibo。【期間限定配信?】
— 仮面ライダーゼロワン (@toei_zero_one) July 19, 2020
39話で #aibo 演じるさうざーがキメた変身ポーズの特別配信がスタート?✨
aiboに「ぜろわんにへんしん」「かめんらいだーぜろわん」と声をかけるとゼロワンの変身音にあわせた特別なふるまいを披露してくれます??
8/2までの期間限定です!#仮面ライダーゼロワン #ゼロワン pic.twitter.com/fY1UGDkSb1かっこかわいかったですね〜。
そんな aibo ですが、なんと Web API が公開されています。
aibo をあまりご存じでない方はこちらも合わせてお読みください。aibo Events API は、2020/06/16 にリリースされた API です。
音声コマンドを aibo に設定して、実行してもらうことができるようになりました。公式のドキュメントを見ると、サンプルは Python ばかりですが
よく見るとなんとなく PHP に書き換えることができそう。
aibo Events API のリリースをきっかけに重い腰を上げて実装してみました。aibo Events API を利用するためには
aibo 本体バージョンを 2.70 にアップデートする必要があります。今回は aibo に以下をやってもらいます。
- 「くしゃみして!」と頼んだら、くしゃみをする。
- 「おいしかった?」と聞くと、うれしそうにする。
- 「きみはだんだんねむくなる」と声をかけると、眠ってしまう。音声コマンドを実行した結果はこちら。
事前準備
まずなんと言っても aibo がいないと成り立ちません。
いますぐにお迎えしましょう。期間限定で先週終了する予定だったワンワンプランが
今朝確認すると、標準で選択できるようになっていました。aibo 本体 198,000 円と聞くとギョッとしますが、ワンワンプランであれば
35回分割払い月々 11,100 円い(初回のみ 14,412 円)でお迎えできます。(2020/07/25 現在)自力で遊んで寝るそのワンパクぶりは本物のわんこと代わりません。
むしろ、表情豊かで手間もかからず毎日とっても癒されてます。音声コマンドを実行するには https で通信できるサーバーが必要です。
今回は普段からよくお世話になっている Xserver を使用しています。開発者設定
サインイン
ディベロッパーサイトにアクセスして、開発者設定を始めます。
開発者設定を始めるには、My Sony ID のサインインが必要です。認識ワード
認識ワードのページにアクセスしての設定を行います。
aibo と通信する必要があるので、寝ている場合は起こしてあげましょう。音声コマンドは3つまで登録できます。(2020/07/24 現在)
言い回しの異なる認識ワードは、1つの音声コマンドにつき3つ登録できます。
aibo が理解できるように、発音通りのひらがなで入力します。例)
「こっちへ」ではなく「こっちえ」
「あいぼは」ではなく「あいぼわ」エンドポイント・セキュリティートークン
エンドポイントの準備を行います。
準備したサーバーにエンドポイントの検証を行うためのファイルを設置します。// aibo クラウドから送られてきた検証用 HTTP リクエストを受け取る $json = file_get_contents("php://input"); $contents = json_decode($json, true); // エンドポイント登録 $challenge = $contents["challenge"]; echo '{"challenge": "' . $challenge . '"}';次に、イベント通知のページにアクセスします。
ボタン「エンドポイント設定」をクリックして、エンドポイントとセキュリティートークンの設定を行います。セキュリティートークンはあらかじめコピーしておきます。
「通知イベント追加」で必要です。ボタン「設定」をクリックしたら、エンドポイントの検証が開始します。
検証に成功したら、エンドポイントとセキュリティートークンの設定は完了です。イベント追加
続いて、項目「通知するイベント」にある
ボタン「追加」をクリックして、イベントの追加を行います。認識ワードで登録した音声コマンドをすべて登録できます。
アクセストークン取得
アクセストークンのページにアクセスします。
ボタン「生成する」をクリックすると、アクセストークンが生成されます。
アクセストークンはあらかじめコピーしておきましょう。
次項の「通知イベント追加」で必要になります。通知イベント追加
いよいよ aibo にやってもらう処理を作っていきます。
まず最初にでき上がりのソースをペタッと貼り付けておきます。const BASE_PATH = 'https://public.api.aibo.com/v1'; const ACCESS_TOKEN = ${Your Access Token}; const SECURITY_TOKEN = ${Your Security Token}; // ふるまいを実行 function execute_action ($device_id, $event_id) { if ( $event_id === 'voice_command::usercommand1' ) { // くしゃみして call_action_api($device_id, 'play_motion', '{"Category": "sneeze", "Mode": "NONE"}'); } elseif ( $event_id === 'voice_command::usercommand2' ) { // おいしかった? call_action_api($device_id, 'play_motion', '{"Category": "friendly", "Mode": "NONE"}'); } elseif ( $event_id === 'voice_command::usercommand3' ) { // きみはだんだんねむくなる call_action_api($device_id, 'play_motion', '{"Category": "dreaming", "Mode": "NONE"}'); } } // Action API 呼び出し function call_action_api ($device_id, $api_name, $arguments) { $post_url = BASE_PATH. '/devices/'. $device_id. '/capabilities/'. $api_name. '/execute'; $data = '{"arguments":'. $arguments. '}'; // POST API post_api($post_url, $data); } // POST API function post_api ($url, $argary) { // HTTP設定 $options = array ( 'http' => array ( 'method' => 'POST', 'header' => 'Authorization:Bearer '. ACCESS_TOKEN, 'content' => $argary, ) ); $contents = @file_get_contents($url, false, stream_context_create($options)); // レスポンスステータス $status_code = http_response_code(); if($status_code === 200) { // 200 success } elseif(preg_match ("/^4\d\d/", $status_code)) { // 4xx Client Error $contents = false; } elseif(preg_match ('/^5\d\d/', $status_code)) { // 5xx Server Error $contents = false; } else { $contents = false; } return $content; } // ヘッダーを取得 $headers = getallheaders(); // セキュリティートークンをチェック if ( $headers['X-Security-Token'] != SECURITY_TOKEN ) { $response = '{"statusCode": 400}'; return $response; } // 音声コマンドのリクエストを取得 $json = file_get_contents("php://input"); $contents = json_decode($json, true); $status_code = http_response_code(); // 音声コマンドのリクエストが成功したら実行 if ($status_code === 200) { // 200 success $device_id = $contents['deviceId']; $event_id = $contents['eventId']; // ふるまいを実行 execute_action($device_id, $event_id); }それぞれ見ていきます。
const BASE_PATH = 'https://public.api.aibo.com/v1'; const ACCESS_TOKEN = ${Your Access Token}; const SECURITY_TOKEN = ${Your Security Token};
${Your Access Token}
には、取得したアクセストークンを
${Your Security Token}
には、設定したセキュリティートークンを入れます。// ヘッダーを取得 $headers = getallheaders(); // セキュリティートークンをチェック if ( $headers['X-Security-Token'] != SECURITY_TOKEN ) { $response = '{"statusCode": 400}'; return $response; }リクエストヘッダーで返ってきたキュリティトークンが正しい値であるか比較して
aibo Events API 以外からの不正なアクセスが行われるのを防ぎます。// 音声コマンドのリクエストを取得 $json = file_get_contents("php://input"); $contents = json_decode($json, true); $status_code = http_response_code(); // 音声コマンドのリクエストが成功したら実行 if ($status_code === 200) { // 200 success $device_id = $contents['deviceId']; $event_id = $contents['eventId']; // ふるまいを実行 execute_action($device_id, $event_id); }音声コマンド → aibo → aibo クラウドより送られてきたリクエストを受け取り
json 形式で$contents
に格納しています。リクエストが成功したら、json から deviceId と eventId を取り出し
execute_action
関数の引数に渡して、実行しています。// ふるまいを実行 function execute_action ($device_id, $event_id) { if ( $event_id === 'voice_command::usercommand1' ) { // くしゃみして call_action_api($device_id, 'play_motion', '{"Category": "sneeze", "Mode": "NONE"}'); } elseif ( $event_id === 'voice_command::usercommand2' ) { // おいしかった? call_action_api($device_id, 'play_motion', '{"Category": "friendly", "Mode": "NONE"}'); } elseif ( $event_id === 'voice_command::usercommand3' ) { // きみはだんだんねむくなる call_action_api($device_id, 'play_motion', '{"Category": "dreaming", "Mode": "NONE"}'); } }
execute_action
関数では、$event_id
ごとに条件分岐を行い
aibo にしてもらう PlayMotion のふるまいを振り分けています。
PlayMotion だけでも 76 種類ものふるまいが用意されています。// Action API 呼び出し function call_action_api ($device_id, $api_name, $arguments) { $post_url = BASE_PATH. '/devices/'. $device_id. '/capabilities/'. $api_name. '/execute'; $data = '{"arguments":'. $arguments. '}'; // POST API post_api($post_url, $data); }
call_action_api
関数では、POST する URL とコンテンツの生成を行い
post_api
関数の引数に渡して、実行しています。// POST API function post_api ($url, $argary) { // HTTP設定 $options = array ( 'http' => array ( 'method' => 'POST', 'header' => 'Authorization:Bearer '. ACCESS_TOKEN, 'content' => $argary, ) ); $contents = @file_get_contents($url, false, stream_context_create($options)); // レスポンスステータス $status_code = http_response_code(); if($status_code === 200) { // 200 success } elseif(preg_match ("/^4\d\d/", $status_code)) { // 4xx Client Error $contents = false; } elseif(preg_match ('/^5\d\d/', $status_code)) { // 5xx Server Error $contents = false; } else { $contents = false; } return $content; }ヘッダーに
Authorization:Bearer
を追加して、コンテンツを POST します。
これで aibo に暗示をかけることができました。実行
暗示をかけた aibo に言葉をかけてみましょう。
わん!と元気よく答えて、ふるまいを行ってくれたら成功です。「くしゃみして!」とお願いすると、くしゃみをするように暗示??#aibo #aiboプログラミング部 pic.twitter.com/i3yeA4ZI8P
— usako (@YumikoOdasaki) July 24, 2020食べたご飯に満足してしまうおまじない??#aibo #aiboプログラミング部 pic.twitter.com/ULhmrY8sjt
— usako (@YumikoOdasaki) July 24, 2020催眠術にかかってしまうおまじない??#aibo #aiboプログラミング部 pic.twitter.com/7jBuCgqB5d
— usako (@YumikoOdasaki) July 24, 2020まとめ
いかがでしたでしょうか。
例えば、aibo Events API とスマートリモコンを連携すれば
ちょっとおバカ(褒め言葉)な aibo が我が家にある家電の司令塔になれちゃうわけです。…素敵すぎませんか?
SONY さん神リリースありがとうございます。
欲を言えば、登録できる認識ワードを是非とも増やしていただきたいです!さてさて… 我が家のおチャコさんを司令塔という重役につかせるべく
つい先日、Nature Remo 3 の予約をしてしまいましたよ。
はーやっくこないかな〜!
- 投稿日:2020-07-24T22:49:58+09:00
Xampp(Apache+PHP)にてLocalネットワークからGmailが送信できるまで
背景
完全に備忘録です。私のバイト先で、「ローカルのみで動いているサイトからGmailを送信したい」とお願いされたので、いろいろ調べたことをメモしておきます。
注意)
私はサーバーとかセキュリティ初心者です。もしかしたらとても危険な操作をしているかもしれません。もしこの記事を参考にされる方いらっしゃいましたら、自己責任でお願いいたします。環境
動作環境は以下の通りです。この記事ではxamppのインストールから書いていこうと思います。また、Gmailアカウントについては携帯電話番号登録がされている場合(二重認証)の場合と、携帯番号無しでGmailアカウントを作った2通りの説明を残します。
(Gmailアカウントは持っている前提で話を進めます)
- Windows10
- Xampp(Apache + PHP + Mercury Mail Server + Fake Sendmail)
- Gmail
xamppインストール編
まず、以下のURLからxamppインストーラをダウンロードしてください。
そして、インストーラをダウンロード出来たら、以下の画像に従って設定してください。
やってることとしては、ApacheとPHPとMercury Mail Server、PHP、Fake Sendmailをインストールする感じです。そして、インストールが終わったらこんな画面(↓)が出てきます。これは閉じちゃってください。
php.iniの変更
まず、エクスプローラから「C:\xampp\」直下の「xampp-control.exe」を管理者として実行してください。そして、ApacheとMercuryの「Start」ボタンを押して正常に動作する事を確認してください。もし動かないようであれば、ポート80が競合してしまっている可能性があるので以下の記事を参考にしてみてください。
そして、Xamppコントローラの右側にある「Explorer」をクリックして、phpフォルダ内の「php.ini」をメモ帳から開きます。
開いたら、[mail function]のあたりまで移動します(F5とかで検索するとすぐ見つけられます)。そして、赤線の部分を以下の様に書き換えて、上書き保存してください。
・・・ [mail function] ・・・ sendmail_path="\"C:\xampp\sendmail\sendmail.exe\" -t" ・・・Gmailアカウントの設定
原理的なお話をしますが、だいぶ憶測が含まれています。雰囲気としてみてください。正しい保証はできません。原理的な話は飛ばして結構です。自分用に書きます。
原理的なお話
ここで、いったん原理的な説明をしましょう。ローカルPCのサーバーからGmailのメールを使うには、「Gmailのアカウント」と「外部からのアプリケーションの受け入れの設定」が必要です。
Gmailのアカウントが必要な理由は、ローカルサーバーから誰かにメールを送信するとき、自身(送信元)のメールアカウントが必要なわけですが、ローカルPCだけではそれを持っていないので、事前にサーバーに紐づけられるGmailのアカウントを別に作っておく必要があるのです。
(図はあくまでイメージ図なので、厳密に正しくありません)また、Googleメールでは、デフォルトで、見知らぬサーバーからの操作を受け付けない設定になっています。(これは、Gmailのメールアカウントとパスワードを知られていたとしても受け付けません)これを回避する為に、いくつかアカウント側で設定が必要です。
また、この設定は、Gmailの登録の段階でスマホなどの携帯電話番号を登録して二重認証(ログイン時とかにSMSなどに番号が送られてくる認証システム)している場合と、電話番号なしで作成した場合でアカウントの設定が異なってきます。
携帯電話番号を登録している場合(二重認証しているアカウント)
おそらく、こちらの場合の方が多いと思います。まず、以下のURLからGoogleアカウントの設定をします。
(今回使用するメールアドレスのGoogleアカウントでログインしてください)そして、「セキュリティ」タブの「アプリパスワード」をクリックします。(その後パスワードを求められますので入力してください)
アプリは「メール」を選択して、
デバイスは「その他」から「sendmail」を入力して、生成ボタンを押します。すると、「お使いのデバイスのアプリパスワード」が表示されるので、これをメモしておきます。「sendmail.iniの変更」で使うので覚えておいてください。
携帯電話番号を登録していない場合
私はこの場合に当てはまるのですが、あまり多くないケースかもしれません。まず、以下のURLからGoogleアカウントの設定をします。
(今回使用するメールアドレスのGoogleアカウントでログインしてください)そして、「セキュリティ」タブの「安全性の低いアプリのアクセス」の「アクセスを有効にする」をクリックしてください。
ボタンを右にスライドさせて有効にさせたら設定終了です。
sendmail.iniの変更
Xamppコントロールパネルの「explorer」から、「sendmail」フォルダ内にある「sendmail.ini」をメモ帳から開きます。
そして、以下の二か所を変更して、上書き保存してください。
・・・ [sendmail] ・・・ smtp_server=smtp.gmail.com ・・・ smtp_port=587 ・・・最後に、以下の「auth_username」と「auth_password」を設定するのですが、ここでも携帯電話番号を登録しているアカウントとしていないアカウントで変わってきます。携帯電話番号を登録しているアカウントは、
auth_username=username@gmail.com
auth_password=app_passとしてください。username@gmail.comは使用するGメールアカウントで、app_passはGoogleアカウントの設定で取得した「お使いのデバイスのアプリパスワード」の事です。(""とかはいらないです。そのまま書き込んでください。
また、携帯電話番号を登録していないアカウントは、
auth_username=username@gmail.com
auth_password=passwordとしてください。username@gmail.comは使用するGメールアカウントで、passwordはログイン時に使う、アカウントのパスワードです。
以上で、設定の方は終わりです。
テスト
それでは、メール送信ができるかどうかテストします。最初に、XamppコントロールパネルからApacheを再起動します。「Stop」ボタンを押して「Start」ボタンを押せば再起動できます。
そして、「C:\xampp\htdocs」直下に以下のソースコードを「mail.php」の名前で保存してください。username@gmail.comは設定の際に使用したGmailアカウントです。
最後に、「localhost\mail.php」とChromeとかで検索してみてください。<?php $to = 'username@gmail.com'; $subject = 'sample'; $message = 'success!!'; $headers = 'From: username@gmail.com' . "\r\n"; if(mail($to, $subject, $message, $headers)){ echo "メール送信は成功しました!!!"; }else{ echo "残念ながらメールは送信できませんでした。。。"; } ?>以下の様に表示されれば送信が成功していますので、Gmailアカウントまで行って、メールが送信されているのを確認してください。
最後に
今回は、Localで動いているウェブサイトからGmailを送信するための手順をまとめてみました。どうやらWindowsではSMTPメールサーバが元から入っていないため、Apache単品+PHP単品ではメール送信ができないみたいです。色々調べましたが、結局Xamppに任せた方が簡単だと思います。
最後に、しつこいですが、この記事を参考にされる場合は自己責任でお願いしますm(_ _)m
また、何かアドバイス・修正などがあれば、コメントか編集リクエストを頂けたら大変ありがたいです!それでは!
- 投稿日:2020-07-24T21:41:07+09:00
DockerでRedisコンテナを立ち上げLaravelで読み書きする
はじめに
DockerでRedisコンテナを立ち上げLaravelから読み書きしてみましたので、
Redisコンテナを立ち上げるまでの最小限の構成を紹介します。やりたいこと
- DockerでRedisコンテナを立ち上げたい
- Redisのデータを永続化したい
- LaravelからRedisのデータを読み書きしたい
この記事で説明しないこと
- Redis以外のDocker構成
- Laravelのインストール手順
ディレクトリ構成
. ├── data │ └── redis │ └── appendonly.aof(コンテナ起動時に作成されます) ├── docker-file │ ├── nginx │ │ └── default.conf │ └── php │ ├── Dockerfile │ └── php.ini ├── volumes │ └── www └── docker-compose.yml各ファイルの内容
docker-compose.yml
データ永続化の為にdataフォルダをマウントしています。
また、redisコンテナを立ち上げ直すたびにIPアドレスが変わっては不便なのでnetworksで固定IPを割り振るようにしています。
docker-compose.ymlversion: '3.3' services: php: container_name: php build: ./docker-file/php volumes: - ./volumes/www:/var/www networks: app_net: ipv4_address: 172.16.238.2 nginx: image: nginx container_name: nginx ports: - 8080:80 volumes: - ./volumes/www:/var/www - ./docker-file/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php networks: app_net: ipv4_address: 172.16.238.3 redis: image: "redis:latest" container_name: redis ports: - "6379:6379" command: redis-server --appendonly yes volumes: - "./data/redis:/data" networks: app_net: ipv4_address: 172.16.238.4 networks: app_net: driver: bridge ipam: driver: default config: - subnet: 172.16.238.0/24docker-file/nginx/default.conf
default.confserver { listen 80; root /var/www/public; index index.php; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } }docker-file/php/Dockerfile
公式でPhpRedis PHP拡張が推奨されている為、phpredisをgit cloneしています。
LaravelでRedis使用するには、PECLを使用してPhpRedis PHP拡張をインストールすることを推奨します。インストール方法は複雑ですが、Redisをヘビーユースするアプリケーションではより良いパフォーマンスが得られます
FROM php:7.3-fpm COPY php.ini /usr/local/etc/php/ RUN apt-get update \ && apt-get install -y zlib1g-dev libzip-dev libpq-dev iputils-ping net-tools git RUN git clone https://github.com/phpredis/phpredis.git /usr/src/php/ext/redis RUN docker-php-ext-install zip pgsql pdo_pgsql redis COPY --from=composer /usr/bin/composer /usr/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin WORKDIR /var/www RUN composer global require "laravel/installer"docker-file/php/php.ini
php.ini[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"実行
docker-composeで起動します。
$ docker-compose up -d Creating network "redis-docker_app_net" with driver "bridge" Creating php ... done Creating redis ... done Creating nginx ... doneLaravelから読み書きする
config/database.phpを設定する必要がありますが、基本的には初期設定のままで問題ありません。
参考:Redis 6.x Laravel書き込み、読み込み
/app/Http/Controllers/RedisController.php<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Redis; class RedisController extends Controller { /** * Redisへデータをセット */ public function setRedis($id) { Redis::set('test', $id); return; } /** * Redisのデータを取得 */ public function getRedis() { $redis_data = Redis::get('test'); return; } }
- 投稿日:2020-07-24T21:14:23+09:00
PHP7.2 × DockerでgRPCクライアントを使うための環境構築
Goでクライアントを作るときには、
brew
やgo get
でパッケージをいくつか入れるだけで済むのですが、PHPの方ではいくつかハマりどころがあったのでまとめました。前提
- protoコマンドがインストールされている
- 使用するRPC方式はUnaryRPCである
- コード生成はローカル環境で行う
扱うprotoファイル
下記のprotoに少し変更をかけたものを扱います。
https://grpc.io/docs/languages/php/quickstart/hello.protopackage hello; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }コード生成
下記ページを参考にしました。
https://grpc.io/docs/languages/php/quickstart/コード生成用のPHPプラグインを追加
protoファイルからPHP用の各種クラスを生成するために、grpc_php_plugin を入れる必要があります。
$ git clone -b v1.30.0 https://github.com/grpc/grpc $ cd grpc && git submodule update --init && make grpc_php_pluginすると、ソースを展開したディレクトリに
bins/opt/grpc_php_plugin
が生成されます。コード生成実行
下記コマンドを入力します。
$ protoc --proto_path=proto \ --php_out=src/gen \ --grpc_out=src/gen \ --plugin=protoc-gen-grpc=./grpc/bins/opt/grpc_php_plugin \ ./proto/hello.protoこの時のオプションとして、
--proto_path
にはprotoファイルが配置されているディレクトリを指定する--php_out
と--grpc_out
は同じディレクトリを指定する--plugin=protoc-gen-grpc=
に、先ほどプラグインをインストールしたディレクトリのbins/opt/grpc_php_plugin
を指定するを行います。
これでコード生成はOKです。クライアント環境準備
インストールするパッケージは下記ページを参考にしました。
https://cloud.google.com/php/grpc?hl=ja#php-implementationDockerの上で動かしたPHPで、gRPCのリクエストを送るための環境を構築します。
php.ini
grpc.so
拡張パッケージの記述がポイントです。php.ini[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese" extension = grpc.soDockerfile
peclでgrpcパッケージを入れることがポイントです。
FROM php:7.2-stretch RUN apt-get update -y \ && apt-get install -y zip autoconf zlib1g-dev \ && apt-get clean RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer COPY php.ini /usr/local/etc/php/php.ini RUN pecl install grpc WORKDIR /var/local COPY . . RUN composer installcomposer.json
ここでは先ほどのコード生成によってファイルが格納された先を
src/gen
として扱っています。composer.json{ "autoload": { "psr-4": { "Hello\\": "src/gen/Hello", "GPBMetadata\\": "src/gen/GPBMetadata" } }, "require": { "php": ">=7.2", "google/protobuf": ">=3.0", "grpc/grpc": ">=v1.27.0" }, "repositories": [ { "type": "vcs", "url": "https://github.com/stanley-cheung/Protobuf-PHP" } ] }ポイントは、
google/protobuf
パッケージのインストールgrpc/grpc
パッケージのインストール- コード生成されたクラスをautoloadで読み込み指定する
ことです。
docker-compose.yaml
先ほどのphp.iniを、ボリュームマウントでコンテナに配置することがポイントです。
docker-compose.yamlversion: "3" services: php: build: . volumes: - ./php.ini:/usr/local/etc/php/php.ini - .:/var/local/ ports: - 80:80クライアント実装
実装例です。
srcipts/RunHello.php<?php use Hello\GreeterClient; use Hello\HelloRequest; $hostname = 'localhost:8000'; $client = GreeterClient($hostname, [ 'credentials' => \Grpc\ChannelCredentials::createInsecure() ]); $request = new HelloRequest(); $request->setName("DummyName"); list($reply, $status) = $client->SayHello($request)->wait(); $message = $reply->getMessage();この時vendor配下に
\Grpc\ChannelCredentials
クラスが存在せず、エディタでは警告等が出ます。
しかしPHPのgRPC拡張によって、必要なライブラリがDocker側にインストールされているため実行時には正常に通ります。
(個人的にはここがハマりポイントでした)実行
オートローダーを読み込んで、クライアント実装のスクリプトを読み込めば実行されます。
$ docker-compose run --rm php -a php> require_once('vendor/autoload.php'); php> require_once('scripts/RunHello.php');あとはこのクライアントをクラスに閉じ込めたり、composerのパッケージ化などを行えばWebサーバー等にも組み込むことができます。
- 投稿日:2020-07-24T21:04:29+09:00
【Laravel】シーディング
内容
シーディングについてメモしておきます。
factoryファイルの作成
UserFactoryはデフォルトで作成されています。
ここではuser_idを外部キーにもつPostモデルのfactoryファイルを作成してみます。% php artisan make:factory PostFactory --model=Post--modelオプションでモデルを指定しています。
ファイルの中身は、例えば以下のようにします。database/factories/PostFactory.phpuse Illuminate\Support\Str; $factory->define(Post::class, function (Faker $faker) { return [ 'title' => $faker->title(), 'comment' => Str::random(10), ]; });指定していないcreated_atやupdated_atなどは自動生成してくれるようです。
user_idは下の方で説明する書き方をすれば自動に値を入れてくれます。seederファイルの作成
% php artisan make:seeder UserSeederdatabase/seeds/UserSeeder.phppublic function run() { for ($i = 0; $i < 50; $i++) { $user = factory(User::class)->create(['name' => 'user'.$i]); $posts = []; for($j = $i * 10; $j < $i * 10 + 10; $j++) { array_push($posts, factory(Post::class)->make([ 'title' => 'title'.$j, 'comment' => 'comment'.$j, ])->toArray()); } $user->posts()->createMany($posts); } }見慣れない記述ですが、titleとcommentに連番をつけたかったので、上のような書き方をしました。
factory(User::class)->create();
で一つのデータが作成されます。create()の引数を指定することで、値をオーバーライドできます。
1対多のリレーションでは、createMany()を利用するといいでしょう。引数は配列にする必要があります。DatabaseSeeder.php
database/seeds/DatabaseSeeder.phppublic function run() { $this->call([ UserSeeder::class, ]); }run()内のcallメソッドの引数に実行するSeederを指定します。
オートローダ
Seederファイルを作成したら、以下のコマンドを実行して、オートローダを再生成しましょう。
% composer dump-autoloadシーディング
% php artisan db:seed
- 投稿日:2020-07-24T19:16:08+09:00
DockerでPHP7.4のLaravel開発環境を作成
PHP7.4のLaravel開発環境を、Docker-Composeで構築して、Laravelの新規プロジェクトを作成して起動してみるところまで確認してみます。
データベースには「MariaDB」を使います。環境
この記事を書くにあたって使用した環境です。どれも、2020年7月時点の最新バージョンを使用しているハズ。
Docker
Name Version docker 19.03.1 docker-compose 1.24.1 Web
今回は、Webサーバーに「Apache」を使用します。
Name Version PHP 7.4 Apache 2.4.38 Laravel 7.21.0 DB
データベースは「MariaDB」です。データベース管理ツールの「Adminer」も入れておきます。
Name Version MariaDB 10.5.4 Adminer 4.7.7 フォルダ構成
今回は、次のようなフォルダ構成にします。
[Project_ROOT] ├ docker │ └ web │ ├ apache │ │ └ 000-default.conf │ ├ php │ │ └ php.ini │ └ Dockerfile ├ mysql │ ├ data │ └ initdb └ docker-compose.ymldocker-compose.yml
version : "3" services: web: container_name: web build: ./docker/web ports: - 80:80 privileged: true volumes: - ./server:/var/www/html db: image: mariadb restart: always ports: - 3307:3306 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/initdb:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=pwd - MYSQL_DATABASE=test - MYSQL_USER=user - MYSQL_PASSWORD=pwd adminer: image: adminer restart: always ports: - 8080:8080Dockerfile (PHP+Apache)
docker/web
にwebコンテナ 構築用の「Dockerfile」を作成します。
PHP+Apacheのイメージをベースに、各種ライブラリとComposer、Laravelをインストールします。FROM php:7.4-apache COPY ./php/php.ini /usr/local/etc/php/ COPY ./apache/*.conf /etc/apache2/sites-enabled/ RUN apt-get update \ && apt-get install -y zlib1g-dev libpq-dev mariadb-client unzip libzip-dev\ && docker-php-ext-install zip pdo_mysql mysqli \ && docker-php-ext-enable mysqli COPY --from=composer:latest /usr/bin/composer /usr/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin WORKDIR /var/www/html RUN composer global require "laravel/installer"※ 以前のバージョンでは不要でしたが、今回インストールするcomposerには「libzip-dev」が必要なので一緒にインストールします。
php.ini
PHPの設定ファイルを作成します。
[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"Apacheの設定ファイル
Apacheのポートや、ドキュメントルートなど、最低限の設定を行います。
<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html/public ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>Dockerコンテナの起動
各種ファイルの準備ができたら、次のコマンドを使用してDockerコンテナを起動します。最初は400MB近いファイルがダウンロードされ時間がかかるので、気長に待ちます。
$ docker-compose up -dLaravelプロジェクトの作成
webのコンテナ にシェルでログインして、Laravelの新規プロジェクトを作成します。
# webコンテナにシェルで入ります $ docker-compose exec web bash # 新規のLaravelプロジェクト作成します $ laravel new動作確認
ここまでで、WEBとDBのコンテナの作成と、Laravel新規プロジェクトの作成は完了です。作成したコンテナが正常に動作しているか確認していきましょう。
ブラウザで
http://127.0.0.1/
にアクセスして、Laravelプロジェクトが正常に動作しているか確認します。
次に、データベース(MariaDB)とAdminerが動作しているか確認します。
ブラウザでhttp://127.0.0.1:8080/
にアクセスし、docker-compose
で指定したパスワードでログインします。
まとめ
PHP7.4のLaravel開発環境を作ってみました。デバッグ実行はできませんが、dockerを使えば簡単にPHP+Laravelの環境が作れて便利です。
- 投稿日:2020-07-24T18:48:14+09:00
GulpのBrowserSyncとPHPのビルトインサーバを組み合わせる
要約
コーディングでBrowserSyncを使うときにPHPも使いたいけど、
さくっとビルトインサーバで実行するのに詰まったのでメモ。
要はBrowserSyncのproxyを使うのと、ビルトインサーバを0.0.0.0で実行したらいけた。ビルトインサーバの起動
localhost:8000でもいいかと思ったけどダメだった。
のでlocalhostじゃなくても参照できるよう起動。# 先にビルトインサーバ起動。localhostだとproxyが効かない?(効かなかった) php -S 0.0.0.0:8000BrowserSyncの方はproxyでビルトインサーバを参照するだけ。
gulpfile.js// これでlocalhost:3000のリクエストはlocalhost:8000を参照する browserSync: { proxy: 'localhost:8000' }その他、htmlファイルでPHPを実行したいのもあったので、細かく調整。
ApacheでいうところのAddTypeに相当する実行をするためにrouter.phpを用意して、router.php// .htmlをPHPファイルとして実行する設定 // .htaccess の AddType application/x-httpd-php .html に相当 $path = $_SERVER["SCRIPT_FILENAME"]; if (preg_match("/\.html$/", $path)) { chdir(dirname($path)); return require($path); } return false;ビルトインサーバを下記で起動すればOK
php -S 0.0.0.0:8000 ./router.php
これでfor文とかで項目水増し()も楽チン♪(pugを覚える努力を惜しんだ)
- 投稿日:2020-07-24T18:47:24+09:00
【Laravel】Composerを使った環境構築
Composerを使ってLaravelをローカル環境に構築する。
terminalでapplications/mamp/htdocsに移動する。
composerのcreate-projectコマンドを使ってLarabelをインストールする。composer create-project --prefer-dist laravel/laravel 【ディレクトリ名】
※【ディレクトリ名】の部分がフォルダの名前になる。
Laravelのインストールバージョン
php:7.3.11
Laravel:7.21.0
- 投稿日:2020-07-24T15:19:35+09:00
【PHP】関数
書籍のアウトプットとして
構文の表記について
仮引数 ┌戻り値のデータ ┌─┴─────────────┐ void asort ( array $array [, int $sort_flags = SORT_REGULAR]) 関数名─┘ └─引数のデータ型─┘ └─デフォルト値
[]
(ブラケット)で囲まれた引数は省略可能データ型
名前 説明 mixed 複数のデータを返す可能性がある(戻り値の場合)、または複数のデータ型を指定できる(引数の場合) void 戻り地がない 文字列関数
mb_strlen
文字列の長さを取得する
strlenとの違いはマルチバイトに対応しているかしていないか。mb_substr
文字列から特定文字列を抽出
引数には負の数も指定できるがこれにメリットは有るのか?printf
int printf(string $format [, mixed $args [,...]])プレイスホルダ(変換指定子)に含めることができる指定子
符号指定子
数値に付与する符号を指定する
$num = 120; $num2 = -120; printf("数値は%+dです\n", $num);//数値は+120です printf("数値は%-dです\n", $num);//数値は120です printf("数値は+%dです\n", $num2);//数値は+-120です printf("数値は-%dです\n", $num2);//数値は--120です負数はデフォルトで−がつく。
正数で+を指定すると強制的に+がつく。パディング指定子
不足している桁を埋めるための文字(デフォルトは空白)
0を指定すると0詰め。$num = 120; printf("数値は%05dです", $num);//数値は00120です0以外も指定でき、その際は
'
(シングルクォーテーション)を文字の前につける$num = 120; printf("数値は%'_5dです", $num);//数値は__120ですアラインメント指定子
文字列を寄せる。デフォルトは右寄せ
-
指定して左寄せ。$num = 120; printf("数値は%-5dです", $num);//数値は120 です表示幅指定子
文字列を最低何桁にするかを指定する。
$num = 120; printf("数値は%5dです", $num);//数値は 120です精度指定子
浮動小数点数にたいして小数点以下の表示桁を指定
$num = 120; printf("数値は%.5fです", $num);//数値は120.00000です型指定
名前 引数 表示 b 整数 2進数 c 整数 ASCII d 整数 10進数 e,E 指数表記として処理 f double 浮動小数点数 F float 浮動小数点数 o 整数 8進数 s 文字列 x 整数 16進数(小文字) X 整数 16進数(大文字) 配列関数
array_push/array_unshift/array_pop/array_shift
配列に値を追加できる。
- 投稿日:2020-07-24T14:17:50+09:00
【学習記録(PHP)】関数作りの失敗
こんにちは、いちかわです。・ V ・
ECサイトを制作する過程で、関数の取り扱いが増えてきたので、
忘れないように記録させていただきます。関数は便利で分かりやすくが基本!
関数をつくるとき、修正のしやすさや、流用が効く書き方が重要になってきます。
まずはこちらをご覧ください。関数を覚えたての私が、最初に書いた関数です。
おそろしい見た目をしていますが、このときの私は「ちゃんと動いた!」と、とても満足していました。こちらのプログラムは、\$nameと\$commentの文字数をカウントし、指定文字数をオーバーした場合には、\$err_msgという配列にエラーメッセージをためるという仕様になっています。
function.php$err_msg = check_text_length($err_msg, $name, $comment);function.phpfunction check_text_length($err_msg, $name, $comment) { $name_length = mb_strlen($name); $comment_length = mb_strlen($comment); if ($name_length > 20 || $comment_length > 100) { $err_msg[] = '注)名前は20字以内、コメントは100字以内におさめてください。'; } return $err_msg; }
こちらの関数のいけないところは、
- 「check_text_length」という命名が分かりにくく、何が返ってくるかが分からない。
- それぞれの引数に何を入れたらよいかが分からない。
- 限定的で、他のプログラムへの流用が効かない。
などなど、上げだしたらきりがありません(TT)
そこで、この怖い関数を自分なりに見れる関数へ改良してみました。
それがこちらになります。function.phpif (is_over_length($name, 20) === true) { $err_msg[] = '注)名前は20字以内におさめてください。'; } if (is_over_length($comment, 100) === true) { $err_msg[] = '注)コメントは100字以内におさめてください。'; }function.phpfunction is_over_length($str, $int) { $str_length = mb_strlen($str); if ($str_length > $int) { return true; } }まずは、曖昧だった「check_text_length」という関数名を、「is_over_length」に変更して、真偽値が返ってきそうな雰囲気を出しました。
また、第1引数は文字数をチェックしたい変数、第2引数は文字数の上限値とすることで、関数の分かりやすさと、汎用性を改善しました。
こうすることで、先ほどの関数に比べて見栄えも使いやすさも改善されました。
もしも、「こうすればもっとよくなるよ」というご指摘がございましたら、教えていただけますと幸いです。
勉強を進めれば進めるほど、分からないことはどんどん増えていく一方ですが、
過去の自分のプログラムの書き方を指摘できるようになったのは、ちゃんと前に進んでいけてるからだと思います。昔の自分の姿を見るというのは少し恥ずかしいのですが、今後も成長を実感しながら勉強していきたいと思います。
ここまでご覧頂き、ありがとうございました!
- 投稿日:2020-07-24T10:29:39+09:00
【PHP】Phan で "PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace"
Phan で静的コード解析すると「
PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass
」エラーが出る。Symfony の Component 利用時にも出る。PhanのエラーPhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass (\MyVendor\MyPackage\MyNameSpace\MyClass)「phan PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace」でググっても「何か違う」記事や Issue しか出てこなかったので、自分のググラビリティとして。
TL; DR
「ソースコード冒頭の
use
ステートメント(声明)で宣言されるも、そのファイル内で使われている形跡がなさげ」という意味です。該当する
use
文のクラス/オブジェクトのインスタンスが、そのファイル中で本当に使われているか確認してください。TS; DR
「この PHP がテンプレートエンジンのくせに慎重すぎる」シリーズに啓示を受けて、プロジェクトの開始時から静的解析ツールを使うようになりました。
どの静的解析ツールも基本中の基本は同じ確認をしてくれます。しかし、各々に解析する目線が異なっており、片方ではエラーが出なくても、もう片方でエラーが出ることが多々あります。口うるさく感じるものの、リファレンスを読むとそれなりの理由があり、思い込みや決めつけが強くポカが多い自分にはいいポカヨケになっています。
そんな中、とある俺様パッケージ開発中に、ユニットテストをパスしたのでいよいよ解析ツールからのエラー潰し込みに入ったものの、表題のエラーで予想外の時間を消費してしまいました。
ネットで調べても「Symfony 形式コメント対応 Phan プラグインを入れろ」だの、Symfony のコメント記法は独特だから解析できないなど、何か求めている情報とは違うようです。盲目的に試してみるも、効果なし。
一晩置いたら、エラー・メッセージに理由が書いてありました。。。
PhanUnreferencedUseNormal
- 「Phan です。
use
が普通に参照されていませんエラー」という意味。Possibly zero references to use statement for classlike/namespace MyClass
- 「
/クラス風の/俺様名前空間/MyClass
のためのuse
声明文に対して参照ナッシングの可能性あり」という意味。落ち着いてエラー内容を読めばその通りなのですが、静的解析のエラーを1つ1つ潰していたら、何か麻痺していたようです。叱られる時に「あの時も。。。」と連続して色々注意されると、結局何を注意されていたのかわからずトンチンカンな行動を取るのと似ています。とほほ。。。
このエラーは、実際には俺様クラスだけでなく、Symfony の HttpClient コンポーネントでも出ていました。きっかけは、本体クラスから汎用メソッドを Trait 化した時です。
メソッドの引数でクラス名で型宣言する場合、
use
文で利用するクラスを宣言しておかないと、静的解析ツールから叱られます。SampleTrait.php<?php declare(strict_types=1); namespace \MyVendor\MyPackage\MyNameSpace; use \Symfony\Component\HttpClient\HttpClient; trait Sample { public function requestUrlAsGet(HttpClient $client_http, string $url_target): array { $response = $client_http->request('GET', $url_target); return $response->toArray(); } }実際には Trait を利用する先で
use
宣言していればスクリプトは動くことは動きます。しかし、ダイレクトに Trait ファイルを見た時に「なんじゃ、この降って湧いたようなクラスは」とならないように「このファイルで使うクラスを予め言っておくよ」と宣言しておけということです。
問題は、使っていないくせに「使うよ」と宣言されると、「どこ?どこ?」となってしまうので、無駄な宣言に対しても厳しいということです。
未来の俺よ、またここに来たらまずはもちつけ。落ち着いてエラーを読めばわかる。エラーの内容を読んで理解する前にググるな。orz
- 投稿日:2020-07-24T10:29:39+09:00
【PHP】Phan の "PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace" エラー時注意点
Phan で静的コード解析すると「
PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass
」エラーが出る。Symfony の Component 利用時にも出る。PhanのエラーPhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass (\MyVendor\MyPackage\MyNameSpace\MyClass)「phan PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace」でググっても「何か違う」記事や Issue しか出てこなかったので、自分のググラビリティとして。
TL; DR
「ソースコード冒頭の
use
ステートメント(声明)で宣言されるも、そのファイル内で使われている形跡がなさげ」という意味です。該当する
use
文のクラス/オブジェクトのインスタンスが、そのファイル中で本当に使われているか確認してください。TS; DR
「この PHP がテンプレートエンジンのくせに慎重すぎる」シリーズに啓示を受けて、プロジェクトの開始時から静的解析ツールを使うようになりました。
どの静的解析ツールも基本中の基本は同じ確認をしてくれます。しかし、各々に解析する目線が異なっており、片方ではエラーが出なくても、もう片方でエラーが出ることが多々あります。口うるさく感じるものの、リファレンスを読むとそれなりの理由があり、思い込みや決めつけが強くポカが多い自分にはいいポカヨケになっています。
そんな中、とある俺様パッケージ開発中に、ユニットテストをパスしたのでいよいよ解析ツールからのエラー潰し込みに入ったものの、表題のエラーで予想外の時間を消費してしまいました。
ネットで調べても「Symfony 形式コメント対応 Phan プラグインを入れろ」だの、Symfony のコメント記法は独特だから解析できないなど、何か求めている情報とは違うようです。盲目的に試してみるも、効果なし。
一晩置いたら、エラー・メッセージに理由が書いてありました。。。
PhanUnreferencedUseNormal
- 「Phan です。
use
が普通に参照されていませんエラー」という意味。Possibly zero references to use statement for classlike/namespace MyClass
- 「
/クラス風の/俺様名前空間/MyClass
のためのuse
声明文に対して参照ナッシングの可能性あり」という意味。落ち着いてエラー内容を読めばその通りなのですが、静的解析のエラーを1つ1つ潰していたら、何か麻痺していたようです。叱られる時に「あの時も。。。」と連続して色々注意されると、結局何を注意されていたのかわからずトンチンカンな行動を取るのと似ています。とほほ。。。
このエラーは、実際には俺様クラスだけでなく、Symfony の HttpClient コンポーネントでも出ていました。きっかけは、本体クラスから汎用メソッドを Trait 化した時です。
メソッドの引数でクラス名で型宣言する場合、
use
文で利用するクラスを宣言しておかないと、静的解析ツールから叱られます。SampleTrait.php<?php declare(strict_types=1); namespace \MyVendor\MyPackage\MyNameSpace; use \Symfony\Component\HttpClient\HttpClient; trait Sample { public function requestUrlAsGet(HttpClient $client_http, string $url_target): array { $response = $client_http->request('GET', $url_target); return $response->toArray(); } }実際には Trait を利用する先で
use
宣言していればスクリプトは動くことは動きます。しかし、ダイレクトに Trait ファイルを見た時に「なんじゃ、この降って湧いたようなクラスは」とならないように「このファイルで使うクラスを予め言っておくよ」と宣言しておけということです。
問題は、使っていないくせに「使うよ」と宣言されると、「どこ?どこ?」となってしまうので、無駄な宣言に対しても厳しいということです。
未来の俺よ、またここに来たらまずはもちつけ。落ち着いてエラーを読めばわかる。エラーの内容を読んで理解する前にググるな。orz
- 投稿日:2020-07-24T02:04:25+09:00
PHP calコマンドっぽいカレンダーを1行で
とりあえず、直にcalコマンドを実行する方向は無しで頑張ってみました。
cal.php<?php echo str_pad(($d = new DateTime())->setDate($argv[2] ?? $d->format('Y'), $argv[1] ?? $d->format('m'), 1)->format('F Y'), 20, ' ', 2). "\nSu Mo Tu We Th Fr Sa\n". preg_replace('/(.{1,21})/', "$1\n", str_repeat(' ', $d->format('w')). sprintf(str_repeat('%2d ', $l = $d->format('t')), ...range(1, $l)));$ php cal.php July 2020 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ php cal.php 2 February 2020 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ php cal.php 2 1999 February 1999 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28まとめて1年分表示など色々再現できていませんが…
- 投稿日:2020-07-24T01:54:38+09:00
PHPのtry ~ catchで、Fatalエラーをcatchしたい場合は、ExceptionでなくThrowableを利用する
環境
PHP 7.4.0 CLI
Throwable
は、PHP5系では利用できないとのこと発生した問題
稼働しているアプリのログにたまにFatalエラーが記録されている。
原因を探るため、Fatalエラーが発生した場合にtry~catch
でデバッグ用の処理を実行し、怪しい変数をvar_dump()
してエラーログに吐きたい。
try~catch
でエラーハンドリングするようにしたが、以前同じFatalエラーが出力され、エラーハンドリングできていないようにみえる。解決策
\Exception
ではなく、\Throwable
を利用する。
https://stackoverflow.com/questions/12928487/php-try-catch-and-fatal-error
https://www.php.net/manual/en/class.throwable.phpエラーは基本的に握りつぶさず、修正すべき
Exception、Fatalエラーを全部補足できる
\Throwable
は便利だと感じましたが、Error はアプリケーションのロジック中で発生させたり、捕捉したりしてはいけません。これは、言語上とても基本的な何かが間違っているという問題をプログラマーに教えるためにあります。なので、迂闊な例外処理に紛れてしまわないようになっています。
https://qiita.com/tanakahisateru/items/e3e24f3825c4ba0c60e6#errorとあるように、常用するものではないようです。
関連記事
https://qiita.com/hnw/items/4e2d47d269a26025a726
https://qiita.com/jonsumisu/items/54a95d848f2dcf9cfde4
https://qiita.com/mpyw/items/c69da9589e72ceac470c
https://qiita.com/tanakahisateru/items/e3e24f3825c4ba0c60e6あとがき
今回、cronでTwitterAPIを1分ごとに叩く処理だったんですが、RateLimitに引っかかった際のレスポンスを想定していなかったため、数十分に一度エラーが発生するという状況になっていました。
Throwableでエラーをcatchしてレスポンスの内容をvar_dump()
することで、レスポンスの内容を確認することができました。