- 投稿日:2020-06-21T23:55:49+09:00
本番運用のための理想的なDockerfileについての考察
はじめに
以前、私の携わっていた案件ではアプリケーションを動作させるDockerイメージが重いことが常に問題になっていました。
Dockerイメージが重いと、良いことは一つもありません。
私の経験上で主要な具体例を上げると…
- 稼働アプリケーションのパフォーマンス低下 (無駄なリソース消費)
- ビルド(CI)にかかる時間の増大
- ローカル開発が重くてストレス (開発にかかる運用負荷の増大)
ということが実際に発生してきます。
どういうコンテナを作るべきかは、Googleがベストプラクティスを提唱しているものが有名で、以下のatmarkITの記事などでも紹介されています。
https://www.atmarkit.co.jp/ait/articles/1807/13/news054.html
どんな環境を想定しているか
具体的には、本項ではステートレスなWEBサービスのフロントエンドについて考察します。
先に進むほど具体的な内容について記しますが、最終的には
Linux + Apache + PHP
のような一般的なWEBサーバのフロントとなるコンテナを実現します。DBやキャッシュ周りは、SaaSや他のコンテナに任せるものと想定します。
本番運用するにあたりレイヤーをどうするか
先ほどのGoogleのプラクティスにもある通り、公開されているイメージをそのまま自分の (自社の) サービスに適用できるケースは少ないでしょう。
何がしかの公開イメージをもとに、足りないライブラリ・パッケージを導入するDockerfileを書き、ビルドしてサービス投入することになるはずです。
ここでまず考えなくてはならないポイントは、ビルドするレイヤーは通常1つでは収まらないということです。
具体的には、CIについて考慮する必要があります。
- OSやミドルウェアなど、基本的な環境を定義してビルドする (1層目)
- CIを回して git clone を行い、最新のアプリ状態をビルドする (2層目)
という、最低2層の構成とすべきでしょう。
レイヤーは少なければ少ないほど、完成するイメージは軽くなります。
ただし、CIを回してビルドするイメージを毎回最初から作っていたのでは、どんなに軽くできるテクニックを駆使していても、毎回相応のビルド時間が発生するので現実的ではありません。「1層目」について、同様の環境で動作するいくつかのアプリケーションは、同じイメージを共用する方が良いでしょう。
これは、セキュリティにとっても利点があります。
例えばOSに脆弱性が出たときに、アプリケーション側のDockerfileのFROM
を書き換えることで、各アプリケーション一律にセキュリティ対応が行われた状態で、すぐデプロイし直せることがその理由です。実際、私がかつて携わっていた案件でも「1層目」を共通化すること自体はネガティブな要素や意見はありませんでした。
場合によっては、アプリケーションごとの差を吸収するために3層としなければならないケースもあるかも知れません。詳しくは個別的な検討が必要でしょう。
また、これより先は、
- 1層目のビルドイメージ … ベースイメージ
- 2層目のビルドイメージ … CIイメージ
という呼び方で話を進めたいと思います。
アンチパターン
全くの私見ではありますが、私の過去の経験をもとに先に良くないベースイメージの構成の仕方を挙げていきます。
- VMで稼働してるインフラの作り方をDockerfileに適用する
- 言語のバージョンをもってイメージを場合分けする
- 公開されているイメージを生かさない
- OSのディストリビューションにこだわる
- Dockerfileとして軽量にできる記法/手法を使わない
主にこの5点だと考えます。
カテゴリごとというよりも、ハマりそうな順に書いてます。。。一文ではよく分からないと思いますので、下記で個別的に詳細説明します。
1. VMで稼働してるインフラの作り方をDockerfileに適用する
これから本番でDockerを採用しようとする企業 (チーム) にとって、最もあると思われるパターン。例えば AWS EC2 のオートスケール環境で稼働しているアプリケーションをコンテナ化する…などの要件ではないかと思います。
一見すると、Dockerfileはシェルスクリプトとほぼ同様に読めるので、VMで『安定稼働』しているサーバインフラの作りをそのまま、あるいは似たものを移植したくなるかも知れません。
これはアンチパターンです。
本項では主にPHP環境を扱うのですが、PHPのインストールのやり方としては、基本的には以下のような3つの方法が考えられます。
- ソースインストール (公式推奨)
- yum や apt などOSのリポジトリ管理ツールからインストール
- .rpm や .deb などのパッケージファイルを使用してインストール
VMで構築する場合は、おそらくこのいずれかを採用するはずですが、Dockerで同じことをするべきではありません。
いずれの方法を取っても、処理的には高コストなのでイメージを重くします。
詳しくは後ほどまた触れていきます。
2. 言語のバージョンをもってイメージを場合分けする
先ほど、「同様の環境で動作するいくつかのアプリケーションは、同じイメージを共用する方が良い」と書きました。
結論から書くと、これは、たとえばPHP5.6で開発されたアプリケーション群と7.0で開発されたアプリケーション群の差異…のことを指しているのではありません。
ここでのアンチパターンは、以下の内容を指しています。
同じPHP7.0のアプリケーションでも、フレームワークが異なると必要なライブラリは異なってきます。
そのため、複数のフレームワークに対応させたイメージは、多数のライブラリを盛り込む必要があり、より重量級になりがちです。また、同言語の同バージョンを後から別のフレームワークに対応させようとした場合、追加で別のソフトウェアを導入する必要も生じます。
これは結果的に、他のアプリケーションに影響を与え安定性を損なう原因になったり、Dockerfileの複雑化・さらなる重量化をもたらします。言語のバージョンよりも、
- フレームワークごと
- フレームワークのバージョンごと
という、2つの視点で分けながら共通化を図る方が自然と考えます。
ただし、フレームワークを利用しない言語やフレームワーク自体の開発が遅い (言語のバージョンのみ上げたい要件が頻発する) 場合などはこの限りではないでしょう。別のスキームの検討が必要と考えます。
3. 公開されているイメージを生かさない
- で書いた通り、言語環境を一から構成する処理は重いです。
Docker Hubには多数のイメージが用意されています。
軽いDockerイメージを作るコツは、実現したい環境に近い公式イメージを基にしてDockerfileを書くことです。具体的には、上記のようにアプリケーションを動作させるための言語環境を基準にした公式イメージを使用するのがより良いと考えます。
「Dockerイメージをなぜ作るのですか?」
という問いに、「アプリケーションを動かすため」と私たちは明確に答えるはずです。目的に適合する最適なイメージを選定することが、私たちに必要な第一歩だと思います。
基となる軽量で最適なイメージをよく考慮しないこと、これがアンチパターンです。
Node.js, Python, Go, Ruby… いずれも言語に応じた公式がありますので、先にそちらを覗いてみるべきでしょう。
https://hub.docker.com/_/node/
https://hub.docker.com/_/python/
https://hub.docker.com/_/golang/
https://hub.docker.com/_/ruby/話をPHPの環境構築に戻すと、PHPのエクステンションは歴史的に非常に複雑化しており、大まかには以下の3パターンに分かれます。
- PHPのソースに同梱されるもの
- 外部提供されるC言語で開発されたもの (
pecl
コマンドでインストール )- 外部提供されるPHPで開発されたもの (
composer
コマンドでインストール )PHPの本番運用を考えるにあたって、
composer
に任せられない 1. 2. を扱うプラクティスは持っておく必要があります。なお、https://hub.docker.com/_/php には以下のように書かれており、
We provide the helper scripts docker-php-ext-configure, docker-php-ext-install, and docker-php-ext-enable to more easily install PHP extensions.
また、例として以下のようなDockerfileが紹介されています。
FROM php:7.4-fpm RUN apt-get update && apt-get install -y \ libfreetype6-dev \ libjpeg62-turbo-dev \ libpng-dev \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) gdここではエクステンションとして、 php-gd のインストールが例として触れられているのですが、これだけ取ってもOSのディストリビューションやPHP自体のインストール時に行うなど、一般的には複数の方法があります。
https://www.php.net/manual/ja/image.installation.phpこれを docker-php-ext-install などのコマンドだけで扱えるなら、よりメリットがありそう…と考えたのですが、結論としてはこの方法は上手くいきませんでした……詳しくは後述します。
4. OSのディストリビューションにこだわる
現サービスを安定運用しているエンジニアにとって (ことさらに運用系に深く関わるエンジニアにとって) 、今まで使用してきた愛着あるLinuxディストリビューションは無視できないポイントでしょう。
しかしながら、3. で書いたようにコンテナ環境を作る唯一の意味は「アプリケーションを動かす」ことのみにあります。OSの種類は問題ではありません。
コンテナのような定義的 (宣言的) なインフラにおいては、ロジックとして正常に動作すると確認できたものは安定運用できると考えて差しつかえありません。
VM環境からコンテナへサービスを移行するときは、機能テストを網羅的に行うことにこそ特に注力すべきです。
(もちろん、適切なスケーリングあるいはオートスケールは当然必要ですが…)本項では、軽量Linuxディストリビューションの一つ Alpine を利用します。
Alpineは、ここ数年でDocker界隈ではかなり注目されているとともに、Docker HubのPHP公式内を俯瞰してみたときに、 (標準であるDebianと比較すると) 非常に軽量なためです。
5. Dockerfileとして軽量にできる記法/手法を使わない
ここでは、WEB検索すると多く出てくる一連の情報をメインに紹介します。
現運用しているなどDockerfileの構成をすぐに大きく変更できない場合でも、もし以下のようなことをまだ行っていない場合は、相応の軽量化が期待できます。
- インストールキャッシュを削除する (apt, yum, apk など利用する場合)
例: (※ 実際にはDockerfileの形式に合わせた最適化が必要) # apt clean または # rm -rf /var/cache/apt/*
- ソースインストールを行う場合は、tarボールやビルドしたソースを削除する
→ 一般のサーバ運用であれば保持しておくべきですが、コンテナでは継続運用しないため。例: (※ 実際にはDockerfileの形式に合わせた最適化が必要) # cd /usr/local/src/httpd-2.4.9 # ./configure --prefix=/opt/httpd/httpd-2.4.9 --with-apr=/opt/apr/apr-1.5.1 --with-apr-util=/opt/apr-util/apr-util-1.5.3 # make # make install // この部分が該当 # make clean # rm -rf /usr/local/src/httpd-2.4.9/
場合によりマルチステージビルドを活用する
→ 一般に公式イメージは軽いのと、コンパイルのステージを分離して複数イメージを利用することが軽量化に効果を発揮する場合があります。ADD より COPY を利用する
→ 詳細は公式に任せて割愛しますが、機能が違うのでCOPY
を使いましょう。
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copyRUN, COPY などの処理はなるべくまとめる
→ 具体的には、\
や&&
で結合しRUNは1回にします。
RUNを次々に並べて書くようなやり方はスマートでないだけでなく、複雑な処理になるとイメージサイズに影響する場合があるようです。ビルドディレクトリ配下に余計なものを置かない
→ COPY の使い方しだいで運ばれてしまいます…
何かの事情で避けられない場合は .dockerignore を書きましょう。Laravel 7.x が稼働するベースイメージを作ってみる
手元に Laravel 7.x を利用して開発したテストアプリケーションがあるため、これを本番相当で動作可能とするユースケースを考えていきます。
まず、Docker HubのPHP公式から適当と思われるイメージを見繕います。
前提として
PHP >= 7.2.5
という要件があるので、最低限に近いところでPHPのバージョンは7.3
としたいと思います。
https://readouble.com/laravel/7.x/ja/installation.htmlここで、考えるべきポイントは4つ。
- AlpineがベースOSとなっていること
- 2.4系のApacheが最初から入っていると、なおベスト
- ZTSは必要ない
- worker系のMPMは使用しないので、FPMも必要ない
ざっくり合いそうなものは以下の感じでした。
- php:7.3-alpine3.12
- php:7.3-apache
ポイントに合わせた観点で行くと、AlpineかつApacheインストール済みのものはありませんでした。php:7.3-apache については、OSは Debian 10 (buster) となります。
一旦、これらを手元のPCでpullして素のイメージのサイズ比較を行います。
$ docker pull php:7.3-alpine3.12 $ docker pull php:7.3-apache$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE php 7.3-alpine3.12 898191dd6734 8 days ago 74.3MB php 7.3-apache 4c443398a62c 8 days ago 410MB正直なところ、全く比較になりません…
いくらApacheが入っていないとはいえ、Alpineが劇的に軽すぎるためかなりのパッケージを積み込んでもbusterのイメージに届くことは、まず絶対にないでしょう。
ここは、 php:7.3-alpine3.12 のイメージを採用することで進めます。
php:7.3-alpine3.12 の素のイメージに足りないものを確認
Laravel 7.x のインストール要件に戻ります。
https://readouble.com/laravel/7.x/ja/installation.html以下のように記載されています。
- PHP >= 7.2.5
- BCMath PHP拡張
- Ctype PHP拡張
- Fileinfo PHP拡張
- JSON PHP拡張
- Mbstring PHP拡張
- OpenSSL PHP拡張
- PDO PHP拡張
- Tokenizer PHP拡張
- XML PHP拡張
素のイメージからコンテナを起動し、足りないものを確認していきます。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE php 7.3-alpine3.12 898191dd6734 8 days ago 74.3MB php 7.3-apache 4c443398a62c 8 days ago 410MB $ docker run -it -d -p 8000:80 898191dd6734 // http://127.0.0.1:8000 でPC目視確認できるよう設定 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b7a35b6625d7 d6096778d65d "docker-php-entrypoi…" 7 seconds ago Up 5 seconds 0.0.0.0:8000->80/tcp wizardly_meitnerここまでは手元のPCで実行しました。
次にコンテナにログインし、ライブラリの状況を調査します。$ docker exec -it b7a35b6625d7 /bin/sh / # php -m [PHP Modules] Core ctype curl date dom fileinfo filter ftp hash iconv json libxml mbstring mysqlnd openssl pcre PDO pdo_sqlite Phar posix readline Reflection session SimpleXML sodium SPL sqlite3 standard tokenizer xml xmlreader xmlwriter zlib [Zend Modules]bcmath 以外は入っているようです… が、結論としてはこれだけでは情報が不足です。
yum や apt を利用する時と異なり、公式イメージはPHPがソースからビルドされているため、結局各ライブラリのインストールが必要となります。
このことを確認するためには、
php -i
を利用するか phpinfo を設置しWEBブラウザで確認しながら考慮すると良いでしょう。/var/www/localhost/htdocs/phpinfo.php<?php phpinfo(); ?>以下のように、ビルドされたエクステンションが enabled となっていることが必要です。
また、以下についても確認しました。
/ # date Sat Jun 20 12:11:15 UTC 2020 / # which composer // 出力なし / # which npm // 出力なしタイムゾーンがUTCになっているのと、Laravel 7で必要なコマンドがないのでこの辺りを調整することにします。
またMySQL接続は必要なので、それらのライブラリも追加でインストールが必要です。実際に書いたDockerfile
FROM php:7.3-alpine3.12 # Document Root RUN mkdir /application # Package Install RUN apk update && apk add --no-cache git tzdata apache2 npm \ php7-common php7-apache2 php7-bcmath php7-ctype php7-fileinfo php7-json \ php7-mbstring php7-openssl php7-pdo php7-mbstring php7-tokenizer php7-xml \ php7-session php7-pdo_mysql php7-mysqli \ && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ && apk del tzdata # Copy php.ini COPY ./etc/php7/php.ini /etc/php7/ # Copy httpd.conf COPY ./etc/apache2/httpd.conf /etc/apache2/ # Composer Install RUN curl -sS https://getcomposer.org/installer | php -- \ --install-dir=/usr/local/bin --filename=composer --version=1.10.6
- --no-cache オプションについて :
/var/cache/apk/ 配下のインストールキャッシュを自動で消してくれます。ある程度は可読性に配慮しつつ、上記のようにまとめました。
git clone するアプリケーションは/application
をドキュメントルートとして配置する想定です。php.ini および httpd.conf は内容を検討し、Dockerfileからの相対パスに配置しておく必要があります。
また、composer
コマンドは常に最新のものが入るので、挙動を一致させ可読性を高める意味でバージョン指定を行うこととしました。手元のPCでビルドして、内容を確認します。
$ docker build -t docker-image-base-laravel7x ./ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-image-base-laravel7x latest b239a1771f04 7 seconds ago 177MB177MBに収まりました!
私のかつての担当案件では、当初のアプリイメージ (CIイメージ) が 3GB 近くあったため、かなり重くなっていました。その後、基本的なつくりは変えずに軽量化を試みて 約半分1.5GB 弱までダイエット。
それでもローカル開発が重い…というあたりの感覚値です。
見込みとしては、CIイメージが 1GB を切ると色々と改善されてくる可能性が高いです。その前提からすると、177MBはかなり良好な数値と言えます。
コンテナに入って状況を確認します。
$ docker run -it -d b239a1771f04 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d87924fc9320 b239a1771f04 "docker-php-entrypoi…" 8 seconds ago Up 6 seconds epic_mccarthy $ docker exec -it d87924fc9320 /bin/shコンテナ内で変更点をチェックしていきます。
/ # date Sun Jun 21 19:02:24 JST 2020 / # apk list | grep "php" | grep "installed" php7-openssl-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-tokenizer-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-pdo_mysql-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-common-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-mysqlnd-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-fileinfo-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-mbstring-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-session-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-json-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-xml-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-apache2-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-ctype-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-bcmath-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-pdo-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] php7-mysqli-7.3.19-r0 x86_64 {php7} (PHP-3.01 BSD LGPL-2.0-or-later MIT Zend-2.0) [installed] / # which composer /usr/local/bin/composer / # composer -V Composer version 1.10.6 2020-05-06 10:28:10 / # which npm /usr/bin/npm / # npm -v 6.14.4問題なく適用されています。
なお、 php7-session というのを含めていますが、これを入れないと以下のようなエラーログが出てLaravel環境のサービスの正常性が確認できませんでした。
/var/log/apache2/access.log[Fri Jun 26 19:30:49.089145 2020] [php7:error] [pid 240] [client 172.17.0.1:42184] PHP Fatal error: Interface 'SessionHandlerInterface' not found in /application/testapp/vendor/laravel/framework/src/Illuminate/Session/FileSessionHandler.php on line 10 [Fri Jun 26 19:30:49.394184 2020] [php7:error] [pid 236] [client 172.17.0.1:42204] PHP Fatal error: Interface 'SessionHandlerInterface' not found in /application/testapp/vendor/laravel/framework/src/Illuminate/Session/FileSessionHandler.php on line 10PECL でインストールするものを考える
まず memcached について下記のような方法を検討しました。
# Install Memcached RUN apk add --no-cache autoconf \ libmemcached-dev zlib-dev \ gcc g++ make \ && pecl install memcached \ && docker-php-ext-enable memcached先ほど docker-php-ext-install は結論としては使えないと書きましたが、その理由としてソースインストールされたPHPへのエクステンションの組込みの問題があります。
apk
を使うことでこの部分をよきに計らってくれ、以下のようなiniファイルが生成されるのです。/ # ls -la /etc/php7/conf.d/ total 72 drwxr-xr-x 1 root root 4096 Jul 4 09:48 . drwxr-xr-x 1 root root 4096 Jul 3 10:49 .. -rw-r--r-- 1 root root 20 Jun 12 21:27 00_bcmath.ini -rw-r--r-- 1 root root 19 Jun 12 21:27 00_ctype.ini -rw-r--r-- 1 root root 22 Jun 12 21:27 00_fileinfo.ini -rw-r--r-- 1 root root 18 Jun 12 21:27 00_json.ini -rw-r--r-- 1 root root 22 Jun 12 21:27 00_mbstring.ini -rw-r--r-- 1 root root 21 Jun 12 21:27 00_openssl.ini -rw-r--r-- 1 root root 17 Jun 12 21:27 00_pdo.ini -rw-r--r-- 1 root root 21 Jun 12 21:27 00_session.ini -rw-r--r-- 1 root root 23 Jun 12 21:27 00_tokenizer.ini -rw-r--r-- 1 root root 17 Jun 12 21:27 00_xml.ini -rw-r--r-- 1 root root 21 Jun 12 21:27 01_mysqlnd.ini -rw-r--r-- 1 root root 20 Jun 12 21:27 02_mysqli.ini -rw-r--r-- 1 root root 23 Jun 12 21:27 02_pdo_mysql.ini -rw-r--r-- 1 root root 22 Jan 23 02:29 10_igbinary.ini実際には
pecl
コマンドを使用してインストールするプラクティスを持つものもapk
を経由しておよそインストール可能と見えます。/ # apk search pecl php7-pecl-imagick-dev-3.4.4-r3 php7-pecl-mailparse-3.1.0-r0 php7-pecl-imagick-3.4.4-r3 php7-pecl-uploadprogress-doc-1.1.3-r1 php7-pecl-amqp-1.10.2-r0 php7-pecl-yaml-2.1.0-r1 php7-pecl-memcache-4.0.5.2-r0 php7-pecl-psr-1.0.0-r0 php7-pecl-redis-5.2.2-r1 php7-pecl-ast-1.0.6-r0 php7-pecl-xdebug-2.9.6-r0 php7-pecl-couchbase-2.6.2-r0 php7-pecl-uploadprogress-1.1.3-r1 php7-pecl-apcu-5.1.18-r0 php7-pecl-timezonedb-2020.1-r0 php7-pecl-igbinary-3.1.2-r0 php7-pecl-uuid-1.1.0-r1 php7-pecl-gmagick-2.0.5_rc1-r5 php7-pecl-lzf-1.6.8-r0 php7-pecl-msgpack-2.1.0-r0 php7-pecl-vips-1.0.10-r1 php7-pecl-zmq-1.1.3-r6 php7-pear-7.3.19-r0 php7-pecl-mcrypt-1.0.3-r0 php7-pecl-xhprof-2.2.0-r0 php7-pecl-oauth-2.0.5-r0 php7-pecl-ssh2-1.2-r0 php7-pecl-memcached-3.1.5-r0 tesseract-ocr-4.1.1-r3 php7-pecl-protobuf-3.12.2-r0 php7-pecl-event-2.5.6-r0 php7-pecl-xhprof-assets-2.2.0-r0総合的に考えると
apk
に寄せてしまい、 docker-php-ext-install/enable 系のコマンドは使わなくても良いと判断しました。redis (phpredis) は特によく使われるので、memcached とあわせてこれらを含んだDockerfileにしてみたものが以下です。
FROM php:7.3-alpine3.12 # Document Root RUN mkdir /application # Package Install RUN apk update && apk add --no-cache git tzdata apache2 npm \ php7-common php7-apache2 php7-bcmath php7-ctype php7-fileinfo php7-json \ php7-mbstring php7-openssl php7-pdo php7-mbstring php7-tokenizer php7-xml \ php7-session php7-pdo_mysql php7-mysqli \ php7-pecl-memcached php7-pecl-memcache php7-pecl-redis \ && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ && apk del tzdata # Copy php.ini COPY ./etc/php7/php.ini /etc/php7/ # Copy httpd.conf COPY ./etc/apache2/httpd.conf /etc/apache2/ # Composer Install RUN curl -sS https://getcomposer.org/installer | php -- \ --install-dir=/usr/local/bin --filename=composer --version=1.10.6各コマンドはざっくり省略しますが、イメージサイズは以下の通りです。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-image-base-laravel7x-cache latest a928f55b49a6 40 seconds ago 180MB180MB…
全くのところ、劇的に軽いです。
Dockerfileはこの作り方が正解なのではないかと考察します。より高速なWEBのために
以前の担当案件では、イメージにPHPを高速化するためのアクセラレータ APCu, OPcache などが入っていました。
このあたりも案件は物理サーバから移植した構成であったため、(当初に) ほとんどそのまま入れたのだと思います。こうしたツールがコンテナにどの程度なじむかは、ちょっと注意・検証が必要かな…と思います。
本項ではこうした所に突っ込むつもりはないので、環境や言語によりけりで確認する必要はあるでしょう。
そもそもWEBを高速化したいなら…
- より一般的なキャッシュ (CDN, フレームワーク, DB, redisに寄せる… etc)
- ボトルネックを見つけて対応 (モデル/SQLまわり, 処理ロジック… etc)
- ネットワークやサーバキャパシティプランニングの問題 (インフラ系)
などのセオリーがあるので、まず内部のキャッシュツールを導入するより、個人的には先にそういった原因調査から行う方がいいのではないかな…と。。
調査の過程で有用性が認められればあるいは、というのが本来の順序と考えます。PHPに関しては言語自体以前よりはるかに高速化しているので、言語のバージョンとフレームワーク如何の組み合わせにも着目する必要があるでしょう。
おわりに
Dockerは検証や開発の環境作りにとても便利です。
また、Kubernetesなどでこれを運用することで、安定した本番環境を得ることもできます。ただし、本番環境はとにかく軽くしておかないとアプリケーションの運用年数が長くなってきた時に、冒頭に書いたようなさまざまな不利益を被ることになります。さらに悪いことに、Dockerfileはコードであるためにサービスイン後にこれを変更することは (特に調整面で…) 手数も多く、当然危険も伴います。
コンテナアプリケーションのサービスイン時には、
- より軽くシンプルなDockerfileの検討
- 機能テストを網羅的に行うこと
- キャパシティプランニングとスケーラビリティの調整
- 現実的なシナリオを想定した負荷試験
などをバランス良く実施しながら、一定の時間をかけ慎重に行った方が賢明でしょう。
Fin ❤︎
- 投稿日:2020-06-21T23:23:44+09:00
【FuelPHP】Html::anchorを使わずにページ遷移させる
問題点 : Html::anchorを使った時
//返り値 <a href="http://www.domain.com/example">Example</a> echo Html::anchor('example', 'Example'); //返り値 <a href="http://www.otherdomain.com/example">Example</a> echo Html::anchor('http://www.otherdomain.com/example', 'Example');公式のドキュメントには上記のように書いていますが、
次のようにaタグの中にspanタグなどを入れたい場合の書き方が解説されていませんでした。<a href="http://www.domain.com/example> <i class=""></i><span>Qiita</span> </a>解決策 : Uri::base()を使う
半日頭を悩ませて、Uri::base()を使う結論に至りました。
<a href="<?php echo Uri::base(); ?> example> <i class=""></i><span>Qiita</span> </a>事前にconfigファイルのbase_urlにベースのurlは書いておかないと使えません。
fuel/app/config/config.php'base_url' => 'http://www.domain.com/',もっといい方法あれば教えていただけると嬉しいです。
ありがとうございました。
- 投稿日:2020-06-21T23:06:04+09:00
一歩進んだMVCフレームワークの使い方
まえ書き
私は現役バリバリでコードを書いてるエンジニアではないので、モダンな内容の記事は書けません。エンジニア1, 2年生がなんとなく理解しているけど、はっきりとは理解できていない、そんなことが、よりはっきりとした理解に変わる。そんな記事が書けたらなと思っています。
MVCパターンの限界
前回の投稿からだいぶ時間がたってしまっていますが、前回投稿した記事では、MVCパターンにおいて、ビジネスロジックとはどういうことなのか、それはどこに実装するのが望ましいのかについて書かせて頂きました。しかしながら、ビジネス要件が目まぐるしく変わる今となっては、これに対応できず限界に達しています。
MVCパターンが限界になるまでの変遷
MVCパターン(≒MVCフレームワーク)の登場
↓
ビジネスロジックをControllerに書くため、Controllerが肥大化
※ そもそも間違ってる、でも当時の参考書がみんなそうなっていた
↓
Skinny Controller, Fat Model
※ だんだんビジネスロジックをModelに書くようになってきた
↓
ビジネスロジックが複雑化
※ 複雑に絡み合ったビジネスロジックがひとつのModelの中に混在。ビジネスロジックの分離
Modelが肥大化することで、本来疎結合であるべきロジックが同一モデル内で複雑に絡み合い密結合となってゆき、可読性が悪く保守性が低くなってきました。前の記事でModelはDAOではないと書きました。しかし、それでも実装がテーブルの設計に引きずられることもありますし、本来そのテーブルに関係のない処理を書かざるを得ないというケースも発生してきます。ビジネスロジックをコントローラーに実装したくはない。どうしよう。。。そこで登場したのがServiceという概念です。
Serviceの役割と見つけ方
ここまでの話で、ビジネスロジックをServiceに実装するのはおわかりかと思います。でも、どのような単位でServiceを分ければ良いのか。ひとつの例として、”業務”単位でわけるというのがあります。例えば、小売店を思い浮かべてみてください。どんな”作業”があるでしょうか?
小売店の作業
- 商品を仕入れる
- 仕入れの代金を支払う
- 仕入れた商品を入庫する
- 注文を受ける
- 商品の代金を受け取る
- 商品を出荷する
実装するクラスはこんなイメージです。ビジネスロジックはModelにある状態ですね。
これを、"業務"という単位で分けてみます。
販売
- 注文を受ける
- 商品の代金を受け取る
- 商品を出荷する
購買
- 商品を仕入れる
- 仕入れの代金を払う
- 仕入れた商品を入庫する
実装するクラスはこんなイメージになります。ビジネスロジックはServiceに移しました。
なんとなく、イメージがつかめたでしょうか?
実際には、”在庫が少なくなってきたら購買にしらせる”というような作業もあったりするでしょう。こういった作業も、Serviceの責務を業務として明確にしたことによって、どこに実装すべきかわかりやすくなりました。大切なことは、責務を明確にしてその責務を壊さない、責務の範囲を広げすぎないことです。これをしてしまうと、Modelと同様に今度はServiceが肥大化してしまいます。ServiceはModelのようにテーブルという制約がありませんので、新しいServiceを設けるなど、柔軟に対応しましょう。
Serviceがもたらす恩恵
さて。Serviceを導入することで保守性が高くなり、コードの見通しもだいぶ良くなりました。しかし、Serviceがもたらしてくれる恩恵は、これだけではありません。
Model, View, Controllerの構成だった時は
- Model : フレームワーク、DBへの依存度が高い
- View : フレームワークへの依存度が高い
- Controller : Model, View, フレームワークへの依存度が高い
Viewは置いといて、これを見てわかるとおり、ModelやControllerはフレームワークや他クラス、DBへの依存度が高いです。依存度が高いということは、テストしづらいということになります。これだけを読むと、初心者の方には何のことか理解できないかもしれませんが、今は”他クラスへの依存が多いとテストしづらい”ということだけ覚えておいてください。(テストのことは、また別の機会に)
ControllerとModelの間にServiceを入れると、Serviceの依存は基本的にModelだけですし、フレームワークが用意しているクラスを継承していませんから、他クラスへの依存度が低いため、テストがしやすくなります。さらに、ビジネスロジックはほとんどServiceに実装されているので、ビジネスロジックのテストをほとんどを自動化できます。
最後に
MVCフレームワークの変遷第2弾、いかがだったでしょうか?MVCにServiceのみを加えるのも、今となっては少し古いアーキテクチャになってしまいましたが、実際の開発現場ではまだまだMVCしか使っていないプロジェクトも多いです。もちろん、開発するWEBサービスの規模によってはそれで良いかもしれませんが、Serviceを加えるアーキテクチャにはメリットが沢山あります。是非一度、あなたのプロジェクトにも導入してみてください。
それではまた!
- 投稿日:2020-06-21T21:53:01+09:00
【Laravel】テンプレートの書き方
概要
LaravelのMVCのうちのビュー周辺について説明するよ
つまり、テンプレートの書き方だね
初心者向け程度の内容だよ
【Laravel】コントローラの書き方と一緒に読むといいよビューの呼び出し
コントローラ側でview()が呼ばれると、引数に与えられたテンプレートがレンダリングされてブラウザに表示されるよ
例えば、view('welcome')の場合、resourcesフォルダ内のviewフォルダの中にあるwelcome.blade.phpまたはwelcome.phpというファイルが呼び出される
2つともファイルがあると拡張子がblade.phpの方が呼び出されるviewフォルダの中にさらにフォルダを作りその中にテンプレートを作ると、
view('フォルダ名.テンプレート名')
のように呼び出せばいいテンプレートの例
- htmlファイルと同じように書く
resources/view/hello.blade.php<html> <head> <title>Hello</title> </head> <body> <h1>Hello</h1> <p>Hello World</p> <p>Hello Laravel</p> </body> </html>これでもいいんだけど、フレームワークらしくないよね
そこで
- Bladeを使う
resources/view/hello.blade.php@extends('template') @section('title', 'Hello') @section('greeting') <p>Hello World</p> <p>Hello Laravel</p> @endsectionresources/view/template.blade.php<html> <head> <title>@yield('title')</title> </head> <body> <h1>Hello</h1> @yield('greeting') </body> </html>@で書かれたものはディレクティブと呼ばれているよ
extendsディレクティブで「template.blade.phpを使うよ」と宣言して、
yieldディレクティブのところに、sectionディレクティブで設定したものが入る感じだよディレクティブには、
- section
- yield
- component
- include
のような組み込むものがあったり
- if
- for
- foreach
- while
- switch
のような制御構文のものがあったり
- csrf
- method
- error
のようなフォーム関連のものがあるよ
使い方や説明はReadoubleに書いてあるから見ておくといいよ(ここでは詳しくは説明しない)値の埋め込み
resources/view/hello.blade.php@extends('template') @section('title', 'Hello') @section('greeting') <p>Hello World</p> @if ($name != null) <p>Hello {{$name}}</p> @endif @endsection上のように{{ }}の中に変数や関数を書くことで値を埋め込めることができる
例えば、コントローラ側で
view('hello',['name'=>'Laravel'])
とあったら、{{$name}}の部分にLaravelが表示されることになるよおわり
今回はここまで
ディレクティブの部分は全部説明すると長くなりそうだったのでReadoubleに丸投げ(手抜き)
要は
- view()でテンプレートを呼び出してレンダリング
- ディレクティブを使いこなそう
- {{ }}内に変数や関数を書いて値を埋め込む
ということ
以上!
- 投稿日:2020-06-21T20:50:10+09:00
【Laravel】コントローラの書き方
概要
Laravelのコントローラ周辺について説明するよ
初心者向けの内容だよ
【Laravel】ルーティングの書き方と一緒に読むといいよコントローラ
コントローラとは、アプリケーションの処理を実装する部分だよ
クライアントからアクセスがあると、ルーティングによって、「○○というアドレスがきたら××という処理を行う」ということを決めるんだけど、この「××という処理」を書いているものがコントローラだよ
コントローラはクラスで表されていて、アクションというメソッドで書かれたものの中に具体的な処理を書いていく基本
コントローラを作成するにはartisanコマンドを使う
% php artisan make:controller コントローラ名コントローラ名は大文字で始まって、○○Controllerという名前にするのが一般的だね
% php artisan make:controller HelloController例えば、上のようなコマンドを打つと以下のようなコントローラができる
app/Http/Controllers/HelloController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class HelloController extends Controller { // } ?>※以下namespaceやuseは省略
例えばindexというアクションを書いてみる
app/Http/Controllers/HelloController.phpclass HelloController extends Controller { public function index() { $param = ['msg' => 'Hello']; return view('welcome', $param); } }アクション内でreturnするのは、主にview()やredirect()だね
view()は引数にテンプレート名やパラメータをとって、ブラウザにビューを表示させる
リダイレクトは例えばredirect('/hello/index')って感じ上の例でいうと、indexの引数は何もないけれど、ルートにパラメータがある場合は引数を設定する
ただし、'http://.../index?id=1'のように?より後ろのパラメータは引数設定しても取得できない
この場合は以下のように引数にRequestオブジェクトの\$requestを設定して、$request->idのように取得すればいいapp/Http/Controllers/HelloController.phpclass HelloController extends Controller { public function index(Request $request) { $param = ['msg' => 'Hello', 'id' => $request->id]; return view('welcome', $param); } }おわり
今回はここまで
あんまり説明することがなかったね
それだけ単純だってこと
次はビューやテンプレートについて話していこうかな
- 投稿日:2020-06-21T19:47:12+09:00
LINEのトークをメールで転送するボットを作った
はじめに
プログラミング初心者の投稿者は完成に1ヶ月を要しました()
が、この記事を読んでいる皆さんは、もっと短期間で作れると思います(たぶん)。
ほかの記事、サイトを調べれば分かるところ、書くのが面倒くさいところは簡潔に説明します。
投稿者はQiitaも初心者ですのでお手柔らかに…
ネット上にはいろいろ記事がありますが、できるだけ新しいものを見つけるようにしてください。仕組み
LINE botを作り、トークを転送したいグループに招待します。
誰かがグループに(テキストメッセージ、写真、動画、スタンプ、ファイルなど)を送ると、LINEは、それらを予め設定しておいたところ(Webhook URL)に送ってきます。
受け取り先(heroku)に、送られてきた内容をメールで送るプログラムを書いておけば、メッセージが送られてくるたびに自動でメール転送できるようになります。LINE Developers 登録
LINE botを作るには、LINE Developersに登録する必要があります。
無料プランで大丈夫です。登録方法は下で説明します
Messaging API
Messaging APIは、あなたのサービスとLINEユーザーの双方向コミュニケーションを可能にする機能です。
https://developers.line.biz/ja/services/messaging-api/LINE Developers & Messaging API の設定
まず、LINE公式サイト Messaging APIを利用するにはを読んで、その通りにやってください
1.チャンネルアクセストークンの発行
長期をおすすめします。2.チャンネルシークレットキー
LINE Developersコンソールの[チャネル基本設定]ページで確認3.WebHook URLの設定
「https://{HEROKU_APP_NAME}.herokuapp.com/callback」というURL形式注:{HEROKU_APP_NAME}はこれからHerokuで作るアプリ名です。1,2,3のアプリ名は控えておいてください。
Heroku
Herokuは今回「受取先」になり、プログラムを実行するところです。
Herokuとは何か?、詳しくは割愛させていただきますが、簡単に言えばサーバです。
イメージ→herokuとPHPでオウム返しlinebotを作る(Windows10)まずは、Herokuの登録をお願いします
(参考サイト)※今回はクレジットカードの登録は必要ありません。
Herokuを使いこなすのに必要な術
https://webbibouroku.com/Blog/Article/heroku-getting-startedつぎに、Heroku CLIをインストールしてください
Heroku CLIは、お使いのパソコンのコマンドラインで、Herokuのアプリのログを見るのに使います。
heroku login //ログイン heroku logs -a アプリ名 -t //ログ常時表示あとは、Herokuを使いこなすのに必要な術をご覧ください
つぎに、HerokuでWeb Appを作ってください
Herokuを使いこなすのに必要な術 アプリケーションの作成
アプリの名前は、先ほどWebHook URLの設定のところで使ったものにしてください。さいごに、HerokuでConfig_Varを設定してください
要するに環境変数です。コードにこれらの秘密情報を記載しないことで、安全性を高めます。
Heroku CLIから設定する↓
Herokuで環境変数を設定する方法Dashboardから入力する↓ (こちらのほうが直感的)
HHeroku公式 Configuration and Config Vars<入力する環境変数>
CHANNEL_ACCESS_TOKEN → さっき調べたもの
CHANNEL_SECRET → さっき調べたもの
FROM_ADRESS → 送信元アドレス(別途用意してください)
TO_ADRESS → 送信先アドレス
BCC_ADRESS → 送信先BCCアドレス
APP_PASSWORD → Gmail ※※は「Gmailから送る」で書きます。申し訳ないですが、いまその設定をするか、あとでもう一度環境変数を追加してください。
Git Hub
投稿者はHerokuにGithubのリポジトリを参照させるようにしました。
こうすることで、コードの変更が簡単になるからです。
Git Hub desktop もあると便利。Visual Studio Code
投稿者はGit hub DesktopからコードをVisual Studio codeで開いていましたが、コードエディタは何でもいいと思います。
LINE SDK
line-bot-sdkというのを利用します(受取先のプログラムに丸ごと組み込みます。後述します)。これはLINEが、開発者がよく使う機能を実装しておいてくれたものです。例えば、開発者(私や読者の皆さん)は、難しいプログラムを一から書く必要はなく、そこに含まれている関数を自分のプログラムの中で呼び出すだけで、メッセージを受け取ったり送ったりできるのです。
Composerで入れます。PHPmailer
PHPでメールを送信するときに使います。システム上これを使わなくてもメールを送れますが、Gmailは暗号化されているメールしか送信?受信?しないので、これが必要です。
Composerで入れます。PHPとComposer
PHPをインストールするのは、調べてもらえればできると思います。
コードエディタで編集していて、エラーが出たときは、構成設定「php.ini」で使いたいものがコメントアウトされていないか調べてください。
あと、そもそもPHPのコードが使えないという場合は「パスが通っていない」可能性があります。PATHを設定する
ComposerはPHPを使う上では必需品らしいです。
これも調べていただければと思います。
簡単に言うと、SDKの更新を簡単にすることができます。投稿者はPHP初心者なので、ここで躓きました。もしどうしてもうまくいかない場合は、Composerを使うことをあきらめ、「line-bot-sdk」と「PHPmailer」を直接Githubからダウンロードして入れてしまってもいいと思います()Composerを使える人は、パソコン上で↓のプログラム(Composer.json)があるところをコマンドプロンプトで開き、
composer installしてください
Heroku(参照しているGithub)に記載するプログラム
Composerする前は、callback.php と composer.json のみ必要です。
vendercomposerしたあとで自動生成されるcallback.php<?php use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; use PHPMailer\PHPMailer\Exception; // Composerでインストールしたライブラリを一括読み込み require_once __DIR__ . '/vendor/autoload.php'; //エラーメッセージ用日本語言語ファイルを読み込む require 'vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php'; //環境変数の読み込み $CHANNEL_ACCESS_TOKEN=getenv('CHANNEL_ACCESS_TOKEN'); $CHANNEL_SECRET=getenv('CHANNEL_SECRET'); $FROM_ADRESS=getenv('FROM_ADRESS'); $TO_ADRESS=getenv('TO_ADRESS'); $BCC_ADRESS=getenv('BCC_ADRESS');//BCC要らない場合は消す $APP_PASSWORD=getenv('APP_PASSWORD'); $contents = file_get_contents('php://input'); //POSTの生データを読み込む $json = json_decode($contents); //生のJSONデータをデコードして変数jsonに格納 $event = $json->events[0]; //変数jsonの中のevent配列の0番目を取り出し、変数eventに格納 $httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient($CHANNEL_ACCESS_TOKEN); // アクセストークンを使いCurlHTTPClientをインスタンス化 $bot = new \LINE\LINEBot($httpClient, ['channelSecret' => $CHANNEL_SECRET]); //CurlHTTPClientとシークレットを使いLINEBotをインスタンス化 $responseProfile = $bot->getProfile($event->source->userId); if ($responseProfile->isSucceeded()) { $profile = $responseProfile->getJSONDecodedBody(); $user_display_name = $profile['displayName']; }else{ error_log("fail to get profile"); } //タイムゾーン設定 date_default_timezone_set('Asia/Tokyo'); //mailを送っていく //mbstring の日本語設定 mb_language("Japanese"); mb_internal_encoding("UTF-8"); // インスタンスを生成(引数に true を指定して例外 Exception を有効に) $mail = new PHPMailer(true); $mail->CharSet = "iso-2022-jp"; $mail->Encoding = "7bit"; //エラーメッセージ用言語ファイルを使用する $mail->setLanguage('ja', 'vendor/phpmailer/phpmailer/language/'); try{ //サーバの設定 $mail->SMTPDebug = SMTP::DEBUG_SERVER; // デバグの出力を有効に(テスト環境での検証用) $mail->isSMTP(); // SMTP を使用 $mail->Host = 'smtp.gmail.com'; // ★★★ Gmail SMTP サーバーを指定 $mail->SMTPAuth = true; // SMTP authentication を有効に $mail->Username = $FROM_ADRESS; // ★★★ Gmail ユーザ名 $mail->Password = $APP_PASSWORD; // ★★★ Gmail パスワード ←アプリパスワード $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // ★★★ 暗号化(TLS)を有効に $mail->Port = 587; //★★★ ポートは 587 //差出人アドレス, 差出人名 $FROM_NAME = "【自動転送】"; $mail->setFrom($FROM_ADRESS, mb_encode_mimeheader($FROM_NAME)); // 受信者アドレス, 受信者名(受信者名はオプション) $TO_NAME="名前"; $mail->addAddress($TO_ADRESS, mb_encode_mimeheader($TO_NAME)); $mail->addBCC($BCC_ADRESS);//BCC要らない場合は消してください //コンテンツ設定 $mail->isHTML(true); // HTML形式を指定 //タイムスタンプのミリ秒を時間に変換。 $SendTime = date("y/m/d H:i:s",($event->timestamp)/1000); //現在の日付 $nowdate = date("m/d"); switch($event->message->type) { case 'text': //メール表題(タイトル) $mail->Subject = mb_encode_mimeheader($nowdate); //本文(HTML用) $bodytext = $SendTime.'//'.$user_display_name.'//'.$event->message->text; $mail->Body = mb_convert_encoding($bodytext,"JIS","UTF-8"); $mail->send(); //送信 break; case 'image': //メール表題(タイトル) $mail->Subject = mb_encode_mimeheader($nowdate); //本文(HTML用) $bodytext = $SendTime.'//'.$user_display_name.'//'."Image"; $mail->Body = mb_convert_encoding($bodytext,"JIS","UTF-8"); $response = $bot->getMessageContent($event->message->id); if ($response->isSucceeded()) { //バイナリデータをjpgに変換 $im=imagecreatefromstring($response->getRawBody()); $filename = date("Ymd-His") . '-' . mt_rand() . '.jpg'; imagejpeg($im, $filename,10);//10はクオリティ低 }else { error_log($response->getHTTPStatus() . ' ' . $response->getRawBody()); } //画像添付 $mail->addAttachment($filename); $mail->send(); //送信 break; case 'video': //処理をここに書く break; case 'audio': //処理をここに書く break; case 'file': //処理をここに書く break; case 'sticker': //処理をここに書く break; default: } } catch (Exception $e) { error_log("Message could not be sent. Mailer Error: {$mail->ErrorInfo}"); } ?>callback.json{ "require": { "linecorp/line-bot-sdk": "^4.3", "ext-gd": "*", "ext-mbstring": "*", "phpmailer/phpmailer": "^6.1" } }composer.lockcomposerすると自動生成されるGmailから送る
メールを送信するには、送信元アドレスが必要です。
Herokuを送信元アドレスにすることはできない(多分)ので、別のメールアドレスを使います。投稿者はGmailを使いました。↑のプログラムは、Gmail用です。
Gmail(SMTP)経由でメール送信するを見て、
ボットにメッセージを送って、一度↑のプログラムを実行させたあと、
アプリのパスワードを生成してください。
「Heroku」の「環境変数設定」のところへ戻って、アプリのパスワードを設定してください。ちなみにメールは
差出人→【自動転送】
タイトル→ 日付
本文→送信時刻//送信者名//メッセージとなります。送信者名は、送信者がボットと友だちになってないと取得できませんが、エラーはでません。
写真は転送できなかった
Herokuの無料プランを使った場合、写真の重い処理に耐えられずに、Heroku側がプログラムを途中中断してしまうみたいです。
ものすごくサイズが小さい写真(数KB)は転送できますw動画やファイルはさらに重い処理が要求されると思われますので、今回プログラムは書きませんでしたが、必要でしたらプログラムに組み込めるようにコメントを入れておきました。
ここまでやればOKなはず…
エラーログは、↓で見れます。コマンドプロンプト上で実行してください。
heroku login //ログイン heroku logs -a アプリ名 -t //ログ常時表示さいごに
記述不足で本当に申し訳ありません。
どういう風にやったか思い出しながら書いたので、抜けている作業があるかもしれませんが、これで精いっぱいです。すみません!
皆さんのお役に立てば幸いです。
- 投稿日:2020-06-21T19:47:12+09:00
LINEのトークをメールで自動転送するボットを作った
はじめに
プログラミング初心者の投稿者は完成に1ヶ月を要しました()
が、この記事を読んでいる皆さんは、もっと短期間で作れると思います(たぶん)。
ほかの記事、サイトを調べれば分かるところ、書くのが面倒くさいところは簡潔に説明します。
投稿者はQiitaも初心者ですのでお手柔らかに…
ネット上にはいろいろ記事がありますが、できるだけ新しいものを見つけるようにしてください。仕組み
LINE botを作り、トークを転送したいグループに招待します。
誰かがグループに(テキストメッセージ、写真、動画、スタンプ、ファイルなど)を送ると、LINEは、それらを予め設定しておいたところ(Webhook URL)に送ってきます。
受け取り先(heroku)に、送られてきた内容をメールで送るプログラムを書いておけば、メッセージが送られてくるたびに自動でメール転送できるようになります。LINE Developers 登録
LINE botを作るには、LINE Developersに登録する必要があります。
無料プランで大丈夫です。登録方法は下で説明します
Messaging API
Messaging APIは、あなたのサービスとLINEユーザーの双方向コミュニケーションを可能にする機能です。
https://developers.line.biz/ja/services/messaging-api/LINE Developers & Messaging API の設定
まず、LINE公式サイト Messaging APIを利用するにはを読んで、その通りにやってください
1.チャンネルアクセストークンの発行
長期をおすすめします。2.チャンネルシークレットキー
LINE Developersコンソールの[チャネル基本設定]ページで確認3.WebHook URLの設定
「https://{HEROKU_APP_NAME}.herokuapp.com/callback」というURL形式注:{HEROKU_APP_NAME}はこれからHerokuで作るアプリ名です。1,2,3のアプリ名は控えておいてください。
Heroku
Herokuは今回「受取先」になり、プログラムを実行するところです。
Herokuとは何か?、詳しくは割愛させていただきますが、簡単に言えばサーバです。
イメージ→herokuとPHPでオウム返しlinebotを作る(Windows10)まずは、Herokuの登録をお願いします
(参考サイト)※今回はクレジットカードの登録は必要ありません。
Herokuを使いこなすのに必要な術
https://webbibouroku.com/Blog/Article/heroku-getting-startedつぎに、Heroku CLIをインストールしてください
Heroku CLIは、お使いのパソコンのコマンドラインで、Herokuのアプリのログを見るのに使います。
heroku login //ログイン heroku logs -a アプリ名 -t //ログ常時表示あとは、Herokuを使いこなすのに必要な術をご覧ください
つぎに、HerokuでWeb Appを作ってください
Herokuを使いこなすのに必要な術 アプリケーションの作成
アプリの名前は、先ほどWebHook URLの設定のところで使ったものにしてください。さいごに、HerokuでConfig_Varを設定してください
要するに環境変数です。コードにこれらの秘密情報を記載しないことで、安全性を高めます。
Heroku CLIから設定する↓
Herokuで環境変数を設定する方法Dashboardから入力する↓ (こちらのほうが直感的)
HHeroku公式 Configuration and Config Vars<入力する環境変数>
CHANNEL_ACCESS_TOKEN → さっき調べたもの
CHANNEL_SECRET → さっき調べたもの
FROM_ADRESS → 送信元アドレス(別途用意してください)
TO_ADRESS → 送信先アドレス
BCC_ADRESS → 送信先BCCアドレス
APP_PASSWORD → Gmail ※※は「Gmailから送る」で書きます。申し訳ないですが、いまその設定をするか、あとでもう一度環境変数を追加してください。
Git Hub
投稿者はHerokuにGithubのリポジトリを参照させるようにしました。
こうすることで、コードの変更が簡単になるからです。
Git Hub desktop もあると便利。Visual Studio Code
投稿者はGit hub DesktopからコードをVisual Studio codeで開いていましたが、コードエディタは何でもいいと思います。
LINE SDK
line-bot-sdkというのを利用します(受取先のプログラムに丸ごと組み込みます。後述します)。これはLINEが、開発者がよく使う機能を実装しておいてくれたものです。例えば、開発者(私や読者の皆さん)は、難しいプログラムを一から書く必要はなく、そこに含まれている関数を自分のプログラムの中で呼び出すだけで、メッセージを受け取ったり送ったりできるのです。
Composerで入れます。PHPmailer
PHPでメールを送信するときに使います。システム上これを使わなくてもメールを送れますが、Gmailは暗号化されているメールしか送信?受信?しないので、これが必要です。
Composerで入れます。PHPとComposer
PHPをインストールするのは、調べてもらえればできると思います。
コードエディタで編集していて、エラーが出たときは、構成設定「php.ini」で使いたいものがコメントアウトされていないか調べてください。
あと、そもそもPHPのコードが使えないという場合は「パスが通っていない」可能性があります。PATHを設定する
ComposerはPHPを使う上では必需品らしいです。
これも調べていただければと思います。
簡単に言うと、SDKの更新を簡単にすることができます。投稿者はPHP初心者なので、ここで躓きました。もしどうしてもうまくいかない場合は、Composerを使うことをあきらめ、「line-bot-sdk」と「PHPmailer」を直接Githubからダウンロードして入れてしまってもいいと思います()Composerを使える人は、パソコン上で↓のプログラム(Composer.json)があるところをコマンドプロンプトで開き、
composer installしてください
Heroku(参照しているGithub)に記載するプログラム
Composerする前は、callback.php と composer.json のみ必要です。
vendercomposerしたあとで自動生成されるcallback.php<?php use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; use PHPMailer\PHPMailer\Exception; // Composerでインストールしたライブラリを一括読み込み require_once __DIR__ . '/vendor/autoload.php'; //エラーメッセージ用日本語言語ファイルを読み込む require 'vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php'; //環境変数の読み込み $CHANNEL_ACCESS_TOKEN=getenv('CHANNEL_ACCESS_TOKEN'); $CHANNEL_SECRET=getenv('CHANNEL_SECRET'); $FROM_ADRESS=getenv('FROM_ADRESS'); $TO_ADRESS=getenv('TO_ADRESS'); $BCC_ADRESS=getenv('BCC_ADRESS');//BCC要らない場合は消す $APP_PASSWORD=getenv('APP_PASSWORD'); $contents = file_get_contents('php://input'); //POSTの生データを読み込む $json = json_decode($contents); //生のJSONデータをデコードして変数jsonに格納 $event = $json->events[0]; //変数jsonの中のevent配列の0番目を取り出し、変数eventに格納 $httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient($CHANNEL_ACCESS_TOKEN); // アクセストークンを使いCurlHTTPClientをインスタンス化 $bot = new \LINE\LINEBot($httpClient, ['channelSecret' => $CHANNEL_SECRET]); //CurlHTTPClientとシークレットを使いLINEBotをインスタンス化 $responseProfile = $bot->getProfile($event->source->userId); if ($responseProfile->isSucceeded()) { $profile = $responseProfile->getJSONDecodedBody(); $user_display_name = $profile['displayName']; }else{ error_log("fail to get profile"); } //タイムゾーン設定 date_default_timezone_set('Asia/Tokyo'); //mailを送っていく //mbstring の日本語設定 mb_language("Japanese"); mb_internal_encoding("UTF-8"); // インスタンスを生成(引数に true を指定して例外 Exception を有効に) $mail = new PHPMailer(true); $mail->CharSet = "iso-2022-jp"; $mail->Encoding = "7bit"; //エラーメッセージ用言語ファイルを使用する $mail->setLanguage('ja', 'vendor/phpmailer/phpmailer/language/'); try{ //サーバの設定 $mail->SMTPDebug = SMTP::DEBUG_SERVER; // デバグの出力を有効に(テスト環境での検証用) $mail->isSMTP(); // SMTP を使用 $mail->Host = 'smtp.gmail.com'; // ★★★ Gmail SMTP サーバーを指定 $mail->SMTPAuth = true; // SMTP authentication を有効に $mail->Username = $FROM_ADRESS; // ★★★ Gmail ユーザ名 $mail->Password = $APP_PASSWORD; // ★★★ Gmail パスワード ←アプリパスワード $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // ★★★ 暗号化(TLS)を有効に $mail->Port = 587; //★★★ ポートは 587 //差出人アドレス, 差出人名 $FROM_NAME = "【自動転送】"; $mail->setFrom($FROM_ADRESS, mb_encode_mimeheader($FROM_NAME)); // 受信者アドレス, 受信者名(受信者名はオプション) $TO_NAME="名前"; $mail->addAddress($TO_ADRESS, mb_encode_mimeheader($TO_NAME)); $mail->addBCC($BCC_ADRESS);//BCC要らない場合は消してください //コンテンツ設定 $mail->isHTML(true); // HTML形式を指定 //タイムスタンプのミリ秒を時間に変換。 $SendTime = date("y/m/d H:i:s",($event->timestamp)/1000); //現在の日付 $nowdate = date("m/d"); switch($event->message->type) { case 'text': //メール表題(タイトル) $mail->Subject = mb_encode_mimeheader($nowdate); //本文(HTML用) $bodytext = $SendTime.'//'.$user_display_name.'//'.$event->message->text; $mail->Body = mb_convert_encoding($bodytext,"JIS","UTF-8"); $mail->send(); //送信 break; case 'image': //メール表題(タイトル) $mail->Subject = mb_encode_mimeheader($nowdate); //本文(HTML用) $bodytext = $SendTime.'//'.$user_display_name.'//'."Image"; $mail->Body = mb_convert_encoding($bodytext,"JIS","UTF-8"); $response = $bot->getMessageContent($event->message->id); if ($response->isSucceeded()) { //バイナリデータをjpgに変換 $im=imagecreatefromstring($response->getRawBody()); $filename = date("Ymd-His") . '-' . mt_rand() . '.jpg'; imagejpeg($im, $filename,10);//10はクオリティ低 }else { error_log($response->getHTTPStatus() . ' ' . $response->getRawBody()); } //画像添付 $mail->addAttachment($filename); $mail->send(); //送信 break; case 'video': //処理をここに書く break; case 'audio': //処理をここに書く break; case 'file': //処理をここに書く break; case 'sticker': //処理をここに書く break; default: } } catch (Exception $e) { error_log("Message could not be sent. Mailer Error: {$mail->ErrorInfo}"); } ?>callback.json{ "require": { "linecorp/line-bot-sdk": "^4.3", "ext-gd": "*", "ext-mbstring": "*", "phpmailer/phpmailer": "^6.1" } }composer.lockcomposerすると自動生成されるGmailから送る
メールを送信するには、送信元アドレスが必要です。
Herokuを送信元アドレスにすることはできない(多分)ので、別のメールアドレスを使います。投稿者はGmailを使いました。↑のプログラムは、Gmail用です。
Gmail(SMTP)経由でメール送信するを見て、
ボットにメッセージを送って、一度↑のプログラムを実行させたあと、
アプリのパスワードを生成してください。
「Heroku」の「環境変数設定」のところへ戻って、アプリのパスワードを設定してください。ちなみにメールは
差出人→【自動転送】
タイトル→ 日付
本文→送信時刻//送信者名//メッセージとなります。送信者名は、送信者がボットと友だちになってないと取得できませんが、エラーはでません。
写真は転送できなかった
Herokuの無料プランを使った場合、写真の重い処理に耐えられずに、Heroku側がプログラムを途中中断してしまうみたいです。
ものすごくサイズが小さい写真(数KB)は転送できますw動画やファイルはさらに重い処理が要求されると思われますので、今回プログラムは書きませんでしたが、必要でしたらプログラムに組み込めるようにコメントを入れておきました。
ここまでやればOKなはず…
エラーログは、↓で見れます。コマンドプロンプト上で実行してください。
heroku login //ログイン heroku logs -a アプリ名 -t //ログ常時表示さいごに
記述不足で本当に申し訳ありません。
どういう風にやったか思い出しながら書いたので、抜けている作業があるかもしれませんが、これで精いっぱいです。すみません!
皆さんのお役に立てば幸いです。
- 投稿日:2020-06-21T19:15:15+09:00
【VS code】キーボードショートカット・keybindings.jsonの開き方
【VS code】キーボードショートカット・keybindings.jsonの開き方
PHPでカーソルを単語の外に出すショートカットキーを設定した際にkeybidings.jsonを利用したため、そのファイルの開き方をまとめます。
*動画の後半がjsonファイルの開き方です。ちなみにtabキーで単語の外に出す設定は以下のリンクで紹介されています.
https://qiita.com/noanoa07/items/0e22d7115a35498e0e08
- 投稿日:2020-06-21T17:39:03+09:00
【PHP】POSTメソッドとHTMLエスケープをしよした割り勘の金額を求めるプログラムの作成
本日は、割り勘する時の一人当たりの金額を調べるプログラムを作成しようと思う。
始めに、HTMLエスケープを実行するためのes()を作成していく
util.php<?php function es($data){ if(is_array($data)){ return array_map(__METHOD__,$data); }else{ return htmlspecialchars($data,ENT_QUOTES,'utf-8'); } }これにより、引数のデータが配列になっている時は、array_mapによって要素一つ一つをエスケープさせ、
esメソッドの配列を作成する。そして、それ以外は、通常通りエスケープを実行する。
次に、文字のエンコードのチェックを行うckenメソッドを作成する。
util.phpfunction cken(array $data){ $result=true; foreach($data as $key=>$value){ //foreach文で配列から値を順に取り出す if(is_array($value)){ //引数が配列のとき $value=implode("",value); //配列を連結させる } if(!mb_check_encoding($value)){ //文字エンコードをチェックしたときに違う値だったという条件 $result=false; break; } } return $result; }次にデータの入力の画面を作成する
name_Check.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>フォーム入力</title> </head> <body> <div> <form action="warikan.php"method="post"> //ここではpostメソッドを指定 <ul> <li><label>合計金額<input type="number" name="goukei"></label></li> <li><label>人数<input type="number"name="ninzu"></label></li> <li><input type="submit" value="割り勘する"></li> </ul> </form> </div> </body>これらのデータを処理するwarikan.php というphpファイルを作成する
warikan.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>フォーム入力</title> </head> <body> <div> <?php require_once('util.php');//util.php を読み込み if(!cken($_POST)){ $encoding=mb_internal_encoding(); $err="ENcoding error the expected encoding is".$encoding; exit($err); } $_POST=es($_POST); ?> <?php $errors=[]; ?> <?php if(isset($_POST['goukei'])){ $goukei=$_POST['goukei']; if(!ctype_digit($goukei)){ $errors[]="合計金額を整数で入力してください"; }} else{ $errors[]="合計金額が未設定"; } ?> <?php if(isset($_POST['ninzu'])){ $ninzu=$_POST['ninzu']; if(!ctype_digit($ninzu)){ $errors[]="人数を整数値で入力してください"; } elseif($ninzu==0){ $errors[]="0では割り切れません"; } } else{ $errors[]="人数が未設定"; } ?> <?php if(count($errors)>0){ echo '<ol class="errror">'; foreach($errors as $value){ echo "<li>","$value","</li>"; } echo "</ol>"; ?> <form action="nameCheck.php" method="post"> <ul> <li><input type="submit"value="戻る"></li> </ul> </form> <?php }else{ $amari=$goukei%$ninzu; $price=($goukei-$amari)/$ninzu; $goukei_fmt=number_format($goukei); $price_fmt=number_format($price); echo "{$goukei_fmt}を{$ninzu}で割ります","<br>"; echo "1にん当たり{$price_fmt}円を払えば、{$amari}円です"; } ?> </div> </body><?php require_once('util.php');//util.php を読み込み if(!cken($_POST)){ $encoding=mb_internal_encoding(); $err="ENcoding error the expected encoding is".$encoding; exit($err); } $_POST=es($_POST); ?>文字のエンコードに失敗した場合に、エラーメッセージを出力しており、
正常に出力しているときには、esメソッドを作成している。<?php if(isset($_POST['goukei'])){ $goukei=$_POST['goukei']; if(!ctype_digit($goukei)){ $errors[]="合計金額を整数で入力してください"; }} else{ $errors[]="合計金額が未設定"; } ?> <?php if(isset($_POST['ninzu'])){ $ninzu=$_POST['ninzu']; if(!ctype_digit($ninzu)){ $errors[]="人数を整数値で入力してください"; } elseif($ninzu==0){ $errors[]="0では割り切れません"; } } else{ $errors[]="人数が未設定"; } ?>こちらでは、$_POSTメソッドがセットされている時のエラーのメッセージを出力するプログラムを作成。
warikan.php<?php if(count($errors)>0){ echo '<ol class="errror">'; foreach($errors as $value){ echo "<li>","$value","</li>"; } echo "</ol>"; ?> <form action="nameCheck.php" method="post"> <ul> <li><input type="submit"value="戻る"></li> </ul> </form> <?php }else{ $amari=$goukei%$ninzu; $price=($goukei-$amari)/$ninzu; $goukei_fmt=number_format($goukei); $price_fmt=number_format($price); echo "{$goukei_fmt}を{$ninzu}で割ります","<br>"; echo "1にん当たり{$price_fmt}円を払えば、{$amari}円です"; } ?> </div> </body>割り勘を処理するプログラムは以下の通りである。
$amari=$goukei%$ninzu; $price=($goukei-$amari)/$ninzu; $goukei_fmt=number_format($goukei); $price_fmt=number_format($price);これで一人当たりの割り勘の金額を求めるプログラムが完成した。
- 投稿日:2020-06-21T17:16:12+09:00
PHP 切り上げ(ceil)、切り捨て(floor)に小数点の桁数を指定できるようにする
PHPには似通った組み込み関数として以下のようなものがあるのだが、
round()
: 四捨五入(modeで多少いじれる)
ceil()
: 切り上げ
floor()
: 切り捨て桁数を指定でいるのは
round()
だけで、
ceil()
,floor()
の二つの関数に関しては、桁指定できる宣言部がない。そこで、切り上げ、切り捨て処理ができる関数を作ってみた。
切り上げ、切り捨て処理
切り上げ処理(ceil_round_up)
ceil_round_up ( float $value [, int $precision = 1 ] )
第一引数 : 切り上げしたい数字
第二引数 : 切り上げしたい小数点桁数(初期値1)function ceil_round_up($value, $precision = 1) { return round($value + 0.5 * pow(0.1, $precision), $precision, PHP_ROUND_HALF_DOWN); }切り捨て処理(ceil_round_down)
floor_round_down ( float $value [, int $precision = 1 ] )
第一引数 : 切り捨てしたい数字
第二引数 : 切り捨てしたい小数点桁数(初期値1)function floor_round_down($value, $precision = 1) { return round($value - 0.5 * pow(0.1, $precision), $precision, PHP_ROUND_HALF_UP); }おそらく正常に回るはず。。
- 投稿日:2020-06-21T16:41:07+09:00
PHP、MySQLで、Wordpressのような記事サイト制作。絞り込み、ページャー機能について
初めまして。
phpを独学で勉強中です。phpの投稿フォームからMySQLへ保存し、一覧で書き出したり絞り込み検索ができるシステムを構築しています。
(Wordpressのブログのイメージです。ページャー機能も付けています)絞り込み検索とページャーの機能はとりあえず動くようになったのですが、下記問題があります。
・複数項目の絞り込みに対応できない。
→現状、キーワード検索のみ
・ページャーが、絞り込みをかけてもテーブル内すべてのデータを含んでしまう。
2ページ目に移動すると絞り込みが解除されてしまう。こちらどの様に改善するかお分かりになる方いらっしゃれば、ご教授いただけますと幸いです。
お手数おかけしますが、よろしくお願いいたします。▼MySQL
テーブル名: shop
カラム1: ID(連番)
カラム2: s_name(テキスト)▼HTML(検索フォーム)
<form action="search.php" method="post"> <dl><dt>キーワード検索</dt> <dd><input type="text" name="sea_name"></dd></dl> <dl><dd><input id="send" type="submit" value="送信する"></dd></dl> </form>▼php(一覧表示)
<?php $sea_name = $_POST['sea_name']; $user = 'user'; $pass = 'pass'; $dsn = 'mysql:host=localhost;dbname=db'; $shop = new PDO($dsn, $user, $pass); if (isset($_GET['page'])) { $page = (int)$_GET['page']; } else { $page = 1; } if ($page > 1) { // 例:2ページ目の場合は、『(2 × 10) - 10 = 10』 $start = ($page * 3) - 3; } else { $start = 0; } if(!empty($sea_name)) { $sea_name = addslashes($sea_name); $where = "s_name REGEXP '$sea_name' &"; } if(!empty($where)) { $where = substr($where, 0, -1); $where = "WHERE " . $where; } $posts = $shop->prepare(" SELECT * FROM shop {$where} ORDER BY id DESC LIMIT {$start}, 3 "); $posts->execute(); $posts = $posts->fetchAll(PDO::FETCH_ASSOC); ?> <p>検索条件</p> <p>フリーワード:<?php echo $sea_name; ?></p> <p>検索結果</p> <?php foreach ($posts as $post) { echo " <li> <h3>{$post['s_name']}</h3> </li> " ;} ?> </ul> <?php $page_num = $shop->prepare(" SELECT COUNT(*) id FROM shop "); $page_num->execute(); $page_num = $page_num->fetchColumn(); $pagination = ceil($page_num / 3); for ($x=1; $x <= $pagination ; $x++) { ?> <a href="?page=<?php echo $x ?>"><?php echo $x; ?></a> <?php } // End of for ?>
- 投稿日:2020-06-21T16:31:17+09:00
Laravelログイン機能の実装について
はじめに
草野と申します。
今回の投稿は、プログラミングスクールを卒業後、同期生に声をかけチームを作り、行っている開発についてアウトプットを行います。自分用のメモのため、文章は拙いですが、少しでも初学者の助けになればと考えています
内容は、表題にもあるとおり、チーム開発によるアプリのログイン機能実装についてです。未熟な点も多いと思います。不備等ありましたらご指摘ください。随時改善して行こうと思います。ちなみに私は、Railsを学びLaravelを次に学んでいるという状況です。Laravelの導入方法について
Laravelの導入方法については以下の記事を参考にさせて頂きました。
完成品
ログイン機能実装手順
※今回は% php artisan migrateまで行ったあとからの手順になります。DBはmysqlを使用しています。
1.環境設定
- laravel/uiライブラリのインストール
- ログイン、新規登録周りファイルのインストール(ここでvueを取り入れています)
- Node.jsのインストール
- npmのインストール
2.日本語化
- config.phpファイル内の設定変更
- 日本語フォルダインストール
- ビューの日本語化を行うため、ja.jsonファイル作成
3.ユーザー新規登録の編集
- usersテーブルへカラムの追加
- ビューへinputを追加
- コントローラー編集
- モデル編集
環境設定
ターミナルで以下のコマンドを実行します。
まずlaravel/uiライブラリをインストールします。ターミナル% composer require laravel/ui
- 変更されるファイル
- composer.json
- composer.lock
ログイン、新規登録周りファイルのインストールします。(ここでvueを取り入れています)
ターミナル% php artisan ui vue --auth
- 追加されるファイル
- app/Http/Controllers/HomeController.php
- resources/js/components/ExampleComponent.vue
- resources/sass/_variables.scss
- resources/views/auth/login.blade.php
- resources/views/auth/passwords/email.blade.php
- resources/views/auth/passwords/reset.blade.php
- resources/views/auth/register.blade.php
- resources/views/auth/verify.blade.php
- resources/views/home.blade.php
- resources/views/layouts/app.blade.php
- 変更されるファイル
- package.json
- resources/js/app.js
- resources/js/bootstrap.js
- resources/sass/app.scss
- routes/web.php
- webpack.mix.js
Node.jsを導入するに当たり、サイトからダウンロードするやり方をしている人(これはYouTubeで見ました)もいましたが、バージョン管理などを行うことを考えるとnodebrewをインストールした方が良いです。
ターミナル% brew install nodebrewNodebrewのインストールを確認します。
ターミナル% nodebrew -vターミナルnodebrew 1.0.1 Usage: nodebrew help Show this message nodebrew install <version> Download and install <version> (from binary) nodebrew compile <version> Download and install <version> (from source) nodebrew install-binary <version> Alias of `install` (For backword compatibility) nodebrew uninstall <version> Uninstall <version> nodebrew use <version> Use <version> nodebrew list List installed versions nodebrew ls Alias for `list` nodebrew ls-remote List remote versions nodebrew ls-all List remote and installed versions nodebrew alias <key> <value> Set alias nodebrew unalias <key> Remove alias nodebrew clean <version> | all Remove source file nodebrew selfupdate Update nodebrew nodebrew migrate-package <version> Install global NPM packages contained in <version> to current version nodebrew exec <version> -- <command> Execute <command> using specified <version> Example: # install nodebrew install v8.9.4 # use a specific version number nodebrew use v8.9.4次にNode.jsのインストールします。
ターミナル% nodebrew install-binary stableすると下記のようなエラーが出ました。
ターミナルFetching: https://nodejs.org/dist/v14.4.0/node-v14.4.0-darwin-x64.tar.gz Warning: Failed to create the file Warning: /Users/kyousuke_kusano/.nodebrew/src/v14.4.0/node-v14.4.0-darwin-x64.t Warning: ar.gz: No such file or directoryインストール用のディレクトリが内容でしたので下記コマンドで作成し、再度% nodebrew install-binary stableコマンドを実行するとインストールに成功します。
ターミナル% mkdir -p ~/.nodebrew/srcNode.jsのインストールを確認します。
ターミナル% nodebrew --versionターミナル#上部記述は省略 Example: # install nodebrew install v8.9.4 # use a specific version number nodebrew use v8.9.4 ←% nodebrew --versionコマンドで出力された『# use a specific version number』の下部に記載されているコマンドを実行します。するとnot installedと出るのでnodebrew install v8.9.4コマンドを実行し、再度下記コマンドを実行し、not installedと出なければOKです。
ターミナル% nodebrew use v8.9.4 >use v8.9.4Node.jsのバージョンが有効化されている事を確認します。
currentに上記と同じバージョンが記載されていれば完了です。ターミナル% nodebrew ls #中略 >current: v8.9.4下記コマンドでパスを通します。
ターミナル% echo 'export PATH=$PATH:~/.nodebrew/current/bin/' >> ~/.bashrc「.bashrc」ファイルがログイン時に読み込まれるように設定していない場合は下記の手順を行います。
「.bash_profile」ファイルを開くターミナル% vi ~/.bash_profile下記内容を追記し保存します。
ターミナルsource ~/.bashrc保存後に下記コマンドで「.bashrc」ファイルを読み込みます。
ターミナル% source ~/.bashrcパスが通っているか確認します。
command not foundにならなければOKです。
これでNode.jsのインストール完了です。ターミナル% npm次にnpmをインストールします。
ターミナル% npm install % npm run dev下記のようにDONEと表示されれば完了です。
ターミナルDONE Compiled successfully in 5385ms Asset Size Chunks Chunk Names /css/app.css 178 KiB /js/app [emitted] /js/app /js/app.js 1.4 MiB /js/app [emitted] /js/appこれでまだ日本語化されていませんが、ログイン機能を実装できています。
日本語化
config.phpファイル内の設定を日本に変更します。
config/app.php'timezone' => 'Asia/Tokyo', #中略 'locale' => 'ja', #中略 'fallback_locale' => 'ja', #中略 'faker_locale' => 'ja_JP',次にインストーラーをダウンロードします。
Laravel 公式が日本語フォルダのインストールを記載してくれているので、利用します。ターミナル% php -r "copy('https://readouble.com/laravel/6.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"インストーラーを起動します。
ターミナル% php -f install-ja-lang.phpこれでバリデーションエラーなどが日本語化されました。
次にビューの日本語化を行います。
下記のファイルは、% php artisan ui vue --authコマンドを実行した時に生成されたデフォルトのログイン画面のビューファイルです。resources/views/auth/login.blade.php@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Login') }}</div> <div class="card-body"> <form method="POST" action="{{ route('login') }}"> @csrf <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> #省略上記ファイル内に__('Login')という記述があります。
__( )というい記述は多言語化というもので中の英語を変換し表示することができます。
何も触らない場合はそのままLoginと表示されます。
ちなみにhome.blade.phpやwelcome.blade.phpファイル内にも__( )を追加して日本語化しています。次に__( )の英語を日本語化するため、resources/lang内にja.jsonファイルを作成し、下記の通り記述します。
resources/lang/ja.json} "Login": "ログイン", "Register": "新規登録", "Forgot Your Password?": "パスワードを忘れた場合", "Reset Password": "パスワード再設定", "Send Password Reset Link": "パスワード再設定URLを送信", "Name": "お名前", "E-Mail Address": "メールアドレス", "Password": "パスワード", "Confirm Password": "パスワード(確認用)", "Remember Me": "ログイン状態を保存", "Hello!": "ご利用ありがとうございます。", "Reset Password Notification": "パスワード再設定のお知らせ", "You are receiving this email because we received a password reset request for your account.": "あなたのアカウントでパスワード再発行のリクエストがありました。", "This password reset link will expire in :count minutes.": "再設定URLの有効期限は :count 分です。", "If you did not request a password reset, no further action is required.": "もしパスワード再発行をリクエストしていない場合、操作は不要です。", "If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser: [:actionURL](:actionURL)": "\":actionText\"ボタンを押しても何も起きない場合、以下URLをコピーしてWebブラウザに貼り付けてください。\n[:actionURL](:actionURL)", "Regards": "よろしくお願いいたします", "Home": "マイページ", "Logout": "ログアウト", "Dashboard": "お知らせ", "You are logged in!":"ログインしました!" }これでビューに日本語が表示されます。
ユーザー新規登録の編集
usersテーブルへカラムの追加を行います。
今回開発するアプリではデフォルトに所属(belongs)というカラムを追加します。
制約等少々デフォルトから編集しております。
※今回% php artisan migrateを行ったあとに環境設定や日本語化を行っていましたので一度% php artisan migrate:rollbackを行った後に下記ファイルにカラム追加の記述を行います。resources/lang/ja.jsonpublic function up() { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name')->unique(); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->string('belongs'); #これを追加 $table->timestamps(); }); }そしてmigrateを実行し、カラムを追加します。
ターミナル% php artisan migrateRailsのgem deviseではなかった\$table->timestamp('email_verified_at')->nullable();と$table->rememberToken();が気になり、少々調べました。
email_verified_atは、新規登録時にメールが送られ、二段階認証を行うためのもののようです。
ローカル環境でも実験できるのかなどは確認できておらず、動作には問題なかったのでこのまま進めます。
rememberTokenは、ログインを保持するためのもので保持期間がデフォルトでは5年に設定されているそうです。今回はその設定は触らず進めます。このカラムは、このチェックを入れてログインすると保存されるようです。
次にビューファイルに所属情報を入力する欄を追加します。
今回は、デフォルトのパスワード(確認用)の部分をコピペしてlabelタグやinputタグ等のオプションを少々触った程度です。resources/views/auth/register.php#省略 <div class="form-group row"> <label for="belongs" class="col-md-4 col-form-label text-md-right">所属</label> <div class="col-md-6"> <input id="berongs" type="text" class="form-control" name="belongs" required autocomplete="organization" value="{{ old('belongs') }}"> @error('belongs') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> #省略次にコントローラー編集します。
バリデーションを追加します。(必須、文字列、最大文字数255文字となります。)app/Http/Controllers/Auth/RegisterController.php#省略 protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], 'belongs' => ['required', 'string', 'max:255'], #ここを追加 ]); } #省略DBに保存するための記述をします。
app/Http/Controllers/Auth/RegisterController.php#省略 protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), 'belongs' => $data['belongs'], #ここを追加 ]); } #省略次にモデル編集します。
$fillableは定義した値しかモデルへ入らないようにするものです。app/User.php#省略 protected $fillable = [ 'name', 'email', 'password', 'belongs', #belongsを追加 ]; #省略これで新規登録に追加したカラムの入力情報がDBへ保存されるようになります。
ここまで読んで頂きありがとうございます。参考にさせて頂いた記事
環境設定ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
Laravel6 ログイン機能を実装する @ucan-lab様
Mac HomebrewでNode.jsをインストールする @miriwo様Node.jsとはーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
初心者向け!3分で理解するNode.jsとは何か?日本語化ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
Laravelの言語を日本語に変更qiita @mitashun様
Laravel の言語を日本語に変更
ログイン認証機能の日本語化(laravel6)vue.jsの使い方ーーーーーーーーーーーーーーーーーーーーーーーーーーーー
合わせてvue.jsについて調べた際に今後参考になりそうだと思い、閲覧させて頂いた記事をご紹介させて頂きます。
Vue.jsでjQueryと共存させる方法 @g-taguchi様
jQuery から Vue.js へのステップアップ @mio3io様
1週間でVue.jsをマスターしようと思った時に参考にしたサイト @mimoe様
- 投稿日:2020-06-21T12:02:15+09:00
YoutubeAPI $ composer require google/apiclient:~2.0で詰まった件について
初投稿になります。
見辛い記事かと思いますがご容赦ください。。今回、オリジナルポートフォリオを作るにあたって、LaravelからYoutubeAPIを使い動画のアップロードをしたいと考えました。
その際そもそものインストールや認証などでかなり詰まったので備忘録として記事にしておきたいと思った次第です。環境
AWS EC2 Micro無料枠
Docker Laradockにて構築
PHP7.3
Laravel 6.10.0まずは公式からサンプルコードをコピーしてきました。
https://developers.google.com/youtube/v3/code_samples/php?hl=ja#resumable_uploadsレジューム可能なアップロード
次のコード サンプルは API の videos.insert メソッドを呼び出して、ユーザーのチャンネルに動画を追加します。また、このコードは Google_MediaFileUpload クラスに true に設定した resumable upload パラメータを付けて使用して、動画を分割してアップロードできるようにしています。<コードは長いので割愛>
詰まりポイント① composerがUpdateできない。
原因:AWS無料枠のメモリ不足まずは$ composer require google/apiclient:~2.0をインストールしろと言われるのですが、そのためにはComposerをUpdateする必要があるらしいので、$ composer update を実行すると、下記エラーが出ました。
mmap() failed: [12] Cannot allocate memory
↓
https://tsukada.sumito.jp/2019/11/26/mmap-failed-12-cannot-allocate-memory/
こちらの方法でswapすることで無事解決。
今回は2048GB回しました。
(ちなみにどうやら保存領域から削ってメモリに充てているっぽい?ので、やりすぎると次はInstall容量を確保できなくなる疑惑。。)これでComposerがUpdateできました。
詰まりポイント② $ composer require google/apiclient:~2.0が実行できない。
原因:composer.json上でリクエストしているバージョンが誤っていた。満を辞して
$ composer require google/apiclient:~2.0を実行すると、./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.Problem 1
- The requested package laravel/framework (locked at v6.10.1, required as ^6.18) is satisfiable by laravel/framework[v6.10.1] but these conflict with your requirements or minimum-stability.
Problem 2
- Conclusion: don't install laravel/framework v6.18.19
- Conclusion: don't install laravel/framework v6.18.18
- Conclusion: don't install laravel/framework v6.18.17
- Conclusion: don't install laravel/framework v6.18.16
- Conclusion: don't install laravel/framework v6.18.15
- Conclusion: don't install laravel/framework v6.18.14
- Conclusion: don't install laravel/framework v6.18.13
- Conclusion: don't install laravel/framework v6.18.12〜〜〜〜〜〜
〜〜〜〜〜〜
〜〜〜〜〜〜
- Installation request for laravel/framework ^6.18 -> satisfiable by laravel/framework[6.x-dev, v6.18.0, v6.18.1, v6.18.10, v6.18.11, v6.18.12, v6.18.13, v6.18.14, v6.18.15, v6.18.16, v6.18.17, v6.18.18, v6.18.19, v6.18.2, v6.18.3, v6.18.4, v6.18.5, v6.18.6, v6.18.7, v6.18.8, v6.18.9].
- Installation request for laravelcollective/html ~5.0 -> satisfiable by laravelcollective/html[5.0.x-dev, 5.1.x-dev, 5.2.x-dev, 5.3.x-dev, 5.4.x-dev, 5.5.x-dev, 5.6.x-dev, 5.7.x-dev, 5.8.x-dev, v5.0.0, v5.0.1, v5.0.2, v5.0.3, v5.0.4, v5.0.5, v5.1.0, v5.1.1, v5.1.10, v5.1.11, v5.1.2, v5.1.3, v5.1.4, v5.1.5, v5.1.6, v5.1.7, v5.1.8, v5.1.9, v5.2, v5.2.1, v5.2.2, v5.2.3, v5.2.4, v5.2.5, v5.2.6, v5.3.0, v5.3.1, v5.3.2, v5.4, v5.4.1, v5.4.2, v5.4.3, v5.4.4, v5.4.5, v5.4.6, v5.4.7, v5.4.8, v5.4.9, v5.5, v5.5.1, v5.5.2, v5.5.3, v5.5.4, v5.6, v5.6.1, v5.6.10, v5.6.2, v5.6.3, v5.6.4, v5.6.5, v5.6.6, v5.6.7, v5.6.8, v5.6.9, v5.7, v5.7.1, v5.8.0, v5.8.1].
ズラーっとエラーが吐き出されます。。
問題は2つあって、composer.json上で、
・laravel/frameworkが現在6.10.1なのに、6.18というバージョンをリクエストしていた。
.laravelcollective/htmlを5.0以下でリクエストしているが、安定稼働?なのはこのバージョンじゃなかった。となり、以下のように修正をすることでインストールが完了しました。
(composer.json)
"require": {
"php": "^7.2",
"aws/aws-sdk-php": "^3.133",
"fideloper/proxy": "^4.0",
"google/apiclient": "~2.0",
"guzzlehttp/guzzle": "^6.5",
"laravel/framework": "^6.10.1",
"laravel/helpers": "^1.1",
"laravel/tinker": "^2.0",
"laravel/ui": "^1.1",
"laravelcollective/html": "~6.0"
},この記事を書いている段階ではまだYoutubeAPIが使えていないのですが、
一応備忘録として。。またできたら更新します。
- 投稿日:2020-06-21T06:15:53+09:00
PHPの環境構築(パスを通すとは)
パスを通すって何?
環境構築って何をすればいいの?この記事は、PHPの環境構築に関するメモです。
MAMP内のPHPのバージョンを変更する手順を解説します。PHPのバージョン確認
ターミナルから、
php -v
コマンドで現在のバージョン情報がわかります。
私の場合、user
ディレクトリから確認してみると以下の情報が出力されました。$ php -v PHP 7.4.7 (cli) (built: Jun 12 2020 00:01:12) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.7, Copyright (c), by Zend Technologies続いて、MAMPのディレクトリから確認してみると...
$ php -v php -v PHP 7.1.33 (cli) (built: Jan 18 2020 13:49:07) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies少し前のバージョンが入っていました。
プログラミング学習にはMAMPを使っていたので、普段使用しているphpのバージョンは7.1.33 (cli)
ということがわかりました。phpのパスの確認
パスとは「ショートカット」とか、「エイリアス」といったの意味です。
ターミナルから、which php
コマンドで現在のパス情報がわかります。
MAMPで実行してみると、以下の情報が出力されました。$ which php /usr/bin/php「パスを通す」とは、phpを格納する場所を指定して、使用するphpを選択することになります。
Finderからたどってみても、phpが格納されているディレクトリを確認することができます。
.bash_profileの変更
home
ディレクトリ(finderで家のマークがある場所)にある.bash_profile
の設定を変更します。
.
から始まるファイルは隠しファイルなので、ls -a
コマンドで確認できます。iMac:~ tetsu$ cd iMac:~ tetsu$ ls -a .bash_profile .sh_history Movies .bash_sessions .ssh Music
vi
コマンドでviエディタを立ち上げます。$ vi .bash_profileviエディタは、最初に「コマンドモード」で立ち上がります。
文字キーをタイプすると、「インサートモード」になり、キー入力が可能になります。
(※一番下に-- INSERT --
と表示される)export PATH=$HOME/.nodebrew/current/bin:$PATH eval "$(rbenv init -)" export PATH=~/.npm-global/bin:$PATH export PATH=/Applications/MAMP/bin/php/php7.4.2/bin:$PATH (※この行を追記してバージョンを指定)編集が終わったら
esc
ボタンで「コマンドモード」に変更して、:wq
コマンド(保存 & 終了)で編集を終えます。パス変更の確認
きちんと
.bash_profile
の変更が反映されているかを確認したい場合はcat
コマンドを使用します。$ cat .bash_profile export PATH=$HOME/.nodebrew/current/bin:$PATH eval "$(rbenv init -)" export PATH=~/.npm-global/bin:$PATH export PATH=/Applications/MAMP/bin/php/php7.4.2/bin:$PATH最後に、もう一度MAMPのディレクトリから
php -v
で確認するとバージョンが変更されていることが分かります。$ php -v PHP 7.4.7 (cli) (built: Jun 12 2020 00:01:12) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.7, Copyright (c), by Zend Technologies無事、PHPのバージョンを変更することができました。
参考
- 投稿日:2020-06-21T01:21:19+09:00
PhpStormのInspectCode
概要
PhpStormにあるコードチェック機能の「コード・インスペクション」を触ったので備忘録。
※PhpStorm : JetBrainsが提供しているPHP用のIDE(統合開発環境)
利用方法
- 上部メニューバーの「Code」 > 「Inspect Code...」
- ファイルやディレクトリを選択して右クリック > 「Inspect Code...」
設定内容
- Inspection scope
- チェック範囲の指定
- 下記の3項目から選択
- Whole project (プロジェクト全体)
- Directory '...XXX' (指定されているディレクトリ配下)
- Custom scope (自身で登録したカスタム設定を使用)
- Inspection profile
- チェック項目を指定
- 特定言語やサービスのチェックを選択やカスタマイズ可能
Custom scope
- ダイアログ左上部の「+」「-」でスコープ(チェック範囲)の設定を作成/削除できる
- Name: スコープ名 (先述の Custom scope でプルダウン表示される)
- Patten: スコープのパターンを記述 (下記のファイル表示でも指定可)
- ファイル表示: ディレクトリ構成が表示されており、「Include」などのボタンで指定(上記のPattenにも連動される)
Patten 設定
簡単な指定方法を紹介
- 拡張子を指定
- file[プロジェクト名]:xxx/*.php
- プロジェクトのxxxディレクトリ配下にある.phpファイルを指定 (ディレクトリ直下以外も再帰的に指定)
- 再帰的にディレクトリ配下を全て指定
- file[プロジェクト名]:xxx//*
- / (ディレクトリ直下のみ指定)
- // (ディレクトリ内を再帰的に指定)
- 拡張子指定した場合は "/" で再帰的に指定するようです
- 除外指定
- !file[プロジェクト名]:xxx//*.html
- プロジェクトのxxxディレクトリ配下にあるHTMLファイルを除外
- 組み合わせ
- AND指定 : &&
- OR指定 : ||
おまけ
プロジェクト全体のチェックではPCのメモリ使用や負荷が大きかっため機能毎に分割してチェックしました。
問題ないコーディングでも構文的なチェックでWarning出る可能性があるため、「Inspection profile」でチェック項目を変更するか、「インスペクションの抑止」のコメント追加で対処が可能です。参照
- 投稿日:2020-06-21T01:21:19+09:00
PhpStormでのコード解析 ~InspectCode設定~
概要
PhpStormにあるコードチェック機能の「コード・インスペクション」を触ったので備忘録。
※PhpStorm : JetBrainsが提供しているPHP用のIDE(統合開発環境)
利用方法
- 上部メニューバーの「Code」 > 「Inspect Code...」
- ファイルやディレクトリを選択して右クリック > 「Inspect Code...」
設定内容
- Inspection scope
- チェック範囲の指定
- 下記の3項目から選択
- Whole project (プロジェクト全体)
- Directory '...XXX' (指定されているディレクトリ配下)
- Custom scope (自身で登録したカスタム設定を使用)
- Inspection profile
- チェック項目を指定
- 特定言語やサービスのチェックを選択やカスタマイズ可能
Custom scope
- ダイアログ左上部の「+」「-」でスコープ(チェック範囲)の設定を作成/削除できる
- Name: スコープ名 (先述の Custom scope でプルダウン表示される)
- Patten: スコープのパターンを記述 (下記のファイル表示でも指定可)
- ファイル表示: ディレクトリ構成が表示されており、「Include」などのボタンで指定(上記のPattenにも連動される)
Patten 設定
簡単な指定方法を紹介
- 拡張子を指定
- file[プロジェクト名]:xxx/*.php
- プロジェクトのxxxディレクトリ配下にある.phpファイルを指定 (ディレクトリ直下以外も再帰的に指定)
- 再帰的にディレクトリ配下を全て指定
- file[プロジェクト名]:xxx//*
- / (ディレクトリ直下のみ指定)
- // (ディレクトリ内を再帰的に指定)
- 拡張子指定した場合は "/" で再帰的に指定するようです
- 除外指定
- !file[プロジェクト名]:xxx//*.html
- プロジェクトのxxxディレクトリ配下にあるHTMLファイルを除外
- 組み合わせ
- AND指定 : &&
- OR指定 : ||
おまけ
プロジェクト全体のチェックではPCのメモリ使用や負荷が大きかっため機能毎に分割してチェックしました。
問題ないコーディングでも構文的なチェックでWarning出る可能性があるため、「Inspection profile」でチェック項目を変更するか、「インスペクションの抑止」のコメント追加で対処が可能です。参照
- 投稿日:2020-06-21T00:15:47+09:00
PHP for文を用いて配列に配列を確認する
目的
- for文を用いて全く同じ配列データをさらに配列に格納する方法を下記に記載する
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
ソフトウェア環境
処理の確認下記のサイトでブラウザ上で確認した。
コードの例
["test01", "test02", "test03"]
という配列さらに配列$main_array
に10回格納するコードを下記に記載する。$i = 0; for ($i = 1; $i <= 10; $i++){ $main_array[] = [ "test01", "test02", "test03", ]; } var_dump($main_array);上記コードの実行結果を下記に記載する。
array(10) { [0]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [1]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [2]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [3]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [4]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [5]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [6]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [7]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [8]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } [9]=> array(3) { [0]=> string(6) "test01" [1]=> string(6) "test02" [2]=> string(6) "test03" } }