- 投稿日:2020-08-14T23:40:30+09:00
foreachで連想配列の要素を変更したい場合は参照渡しする
参照渡しとは
この内容については、独習PHP 第3版で以下のように説明されています。
変数に値を格納→厳密にはコンピュータ上に用意されたメモリに格納
メモリにはそれぞれの場所を表す番号(アドレス)が振られている。
変数とは、値の格納先(アドレス)に対してつけられた名札みたいなもの
「=」演算子で変数を代入する=メモリ上の値を別のアドレスにコピーする
ref_val.php<?php $x = 1; $y = $x; // $xの値を$yにコピー $x = 5; // $xの値を変更 print $y; // 結果:1($xの変更に影響しない) ?>一方、参照(リファレンス)による代入は、メモリ上のアドレスそのものを引き渡す代入のこと
ref_ref.php<?php $x = 1; $y = &$x; // $xのアドレスを$yにコピー $x = 5; // $xの値を変更 print $y; // 結果:5($xの変更に影響する) ?>foreach命令→デフォルトでは配列要素を値渡し
foreach_val.php<?php $data = ['渋谷区','豊島区','品川区','新宿区']; foreach($data as $value){ $value = '東京都'.$value; } print_r($data) // 結果:Array([0] => 渋谷区、[1] => 豊島区、[2] => 品川区、[3] => 新宿区 ?>したがって、連想配列の要素を変更したい場合は「&」をつけて参照渡しする
foreach_ref.php<?php $data = ['渋谷区','豊島区','品川区','新宿区']; foreach($data as &$value){ $value = '東京都'.$value; } print_r($data) // 結果:Array([0] => 東京都渋谷区、[1] => 東京都豊島区、[2] => 東京都品川区、[3] => 東京都新宿区 ?>参考文献
この記事は以下の情報を参考にして執筆しました。
- 投稿日:2020-08-14T23:06:33+09:00
Carbonを用いて期間検索機能を作る
はじめに
日付検索する時にDBには年月日時分秒まで登録されているけど期間(年月日)で検索したい時ありませんか。
そんな時はCarbonを使って検索機能を作りましょう。Carbonを用いて期間検索機能を作る
ある事柄が始まった日時
$period[start_at]
の検索で、検索期間(年月日)のはじめを$request['start_at_from']
、検索期間(年月日)の終わりを$request['start_at_to']
として$requestの配列で受け取ったとします。
$period['start_at_from']
と$period['start_at_to']
は(2020-08-13のように)年月日で受け取っているとしてCarbonを用いて00:00:00
と日時分を仮置きします。//2020-08-13 $period['start_at_from'] //2020-08-13 00:00:00 Carbon::parse($period['start_at_from'])
startOfDay()
メソッドで1日の中の一番初めの時刻(00:00:00)、endOfDay()
メソッドで1日の中の一番終わりの時刻(23:59:59)を取得できます。//2020-08-13 00:00:00 Carbon::parse($period['start_at_from'])->startOfDay()); //2020-08-13 23:59:59 Carbon::parse($request['start_at_to'])->endOfDay());
where
は第一引数にカラム名、第二引数に比較演算子、またはSQLで使うオペレータ、第三引数に比較する値を指定します。
上記までのものも合わせて下記に実装をまとめます。Controller.phpuse Carbon\Carbon; public function periodSearch(Request $request) { $inputs = $request->validatedValues(); $this->where('start_at', '>=', Carbon::parse($request['start_at_from'])->startOfDay()); $this->where('start_at', '<=', Carbon::parse($request['start_at_to'])->endOfDay()); } return view('periodSearch', ['period'=> $period]);おわりに
Carbonは日時を操作するのに大変便利なので使いこなせるようにCarbonはどんなことができるか調べておくと良いかもしれません。
- 投稿日:2020-08-14T21:37:58+09:00
php.ini ディレクティブ モードと設定ファイル
PHP でファイルアップロード処理を実装する場合
雑に.htaccess
でpost_max_size
やupload_max_filesize
を変更したり
メモリでコケたらini_set()
でmemory_limit
を変更することがある
あるよね?
(環境構築時に要件に応じた設定ができていれば、それがベストなんだろうけど)今回、
upload_tmp_dir
ディレクティブを変更する際、恥ずかしながら初めてモードの存在を知ったので
自分への戒めPHP ディレクティブ モード
PHP ディレクティブには
以下のいずれかのモードが定義されており、モード毎に変更を指定可能な方法が異なる
https://www.php.net/manual/ja/configuration.changes.modes.php例えば
upload_tmp_dir
ディレクティブはPHP_INI_SYSTEM
モードであるため
.htaccess
やini_set()
では変更できない
https://www.php.net/manual/ja/ini.list.php設定ファイル
設定ファイル 区分 影響範囲 備考 php.ini PHP 全体 httpd.conf Apache 全体 .htaccess Apache ディレクトリ単位 web.config IIS ディレクトリ単位 .user.ini PHP ディレクトリ単位 PHP 5.3.0 以降 ini_set() PHP スクリプトレベル ただし、Web サーバ(Apache、IIS)により使用できる設定ファイルが異なる
例えば
PHP_INI_PERDIR
モードのディレクティブを変更する場合
よくある Apache サーバの場合は.htaccess
やhttpd.conf
を使用できるが
Windows 上で稼働している IIS サーバの場合はweb.config
を使用して設定を行う必要がある
または.user.ini
を使用した PHP 側での変更も可能
- 投稿日:2020-08-14T18:26:55+09:00
MySQLで複数のDBをまたいで使用する
概要
サービスが大きくなったりしてDBが増えたときに、複数のDBをまたいで使用する場合の書き方です。
環境
PHP 5.3.29
FuelPHP 1.7.3
MySQL 5.5.61本題
方法1
まず、FuelPHPの
fuel/app/config
以下にあるdb.php
に設定を書きます。書くファイルはそれぞれの環境に応じたものにします。(開発環境であればdevelopment/db.php
)
ここに複数の設定を書けばクエリを発行する際に各DBを指定して使用できます。db.php<?php return array{ 'db01' => array( 'type' => 'pdo', 'connection' => array( 'dsn' => 'mysql:host=localhost;dbname=db01', 'username' => '', 'password' => '', ), ), 'db02' => array( 'type' => 'pdo', 'connection' => array( 'dsn' => 'mysql:host=localhost;dbname=db02', 'username' => '', 'password' => '', ), ), 'db03' => array( 'type' => 'pdo', 'connection' => array( 'dsn' => 'mysql:host=localhost;dbname=db03', 'username' => '', 'password' => '', ), ), ); ?>各パラメータは各々の環境に合わせます。
次に、モデルにクエリを発行する文を書きます。
examplemodel.php<?php namespace Example; class Model_Examplemodel extends \Model { public static function get_example() { $get = \DB::select() ->from('tablename') ->execute('db01') return $get; } } ?>このように
execute()
の引数にdb.phpで定義した文字を入れればそのDBを参照してくれます。方法2
セレクトするテーブルの情報にDB名を加える形でも可能です。
examplemodel.php<?php namespace Example; class Model_Examplemodel extends \Model { public static function get_example() { $get = \DB::select() ->from('db01.tablename') ->execute() return $get; } } ?>joinする場合
joinする場合は、方法1をとるとすべてそのDBにあるテーブルが参照されます。
ですので、db01・db02それぞれからテーブルを参照する必要がある場合は方法2、もしくは両方を合わせて使う必要があります。examplemodel.php<?php namespace Example; class Model_Examplemodel extends \Model { public static function get_example() { $get = \DB::select() ->from('db01.table01') ->join('db02.table02') ->on('table01.column01', '=', 'table02.column01') ->execute() return $get; } } ?>カラム名が被っている場合はその点にも注意します。
終わりに
FuelPHPを書いていて、発行されたクエリが簡単にわかる方法はないかなーと思って探しています。知っている方いましたらコメントいただけるとすごく助かります。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えないという話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employee
テーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
name
カラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
LIKE
であいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employee
テーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$name
に「山田」が入った状態。前方と後方に付加した
'%'
文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,
Illuminate\Database\Eloquent\Collection
でのwhere
利用は,like
が使えないようです。API 仕様書では,第二パラメータ
$operator
で指定できるような思わせぶりなので,すっかり,ハマってしまいました。どう対応したか
結局,
like
利用をあきらめて,完全一致検索に仕様変更しました。そもそも……練習用だからと横着して,「苗字 + 半角スペース1文字 + 名前」をひとまとめにして,
name
カラムにするというテーブルの作り方がマズイですね。きちんと「苗字」と「名前」は別カラムに分けたうえで,完全一致検索という仕様にするのが,いちばんスッキリするかと。
EmployeeController.php(変更前)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }EmployeeController.php(変更後)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', $name); }他のアプローチ
どうしても,あいまい検索が必要ならば,Eloquent モデルで組むのをあきらめて,クエリビルダを使う方式にすると,
like
指定での検索ができそうです(未検証)。公式ドキュメントを見ても,
like
指定ができないという記述は発見できずじまいでした。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えないとハマったときの話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employee
テーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
name
カラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
like
であいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employee
テーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$name
に「山田」が入った状態。前方と後方に付加した
'%'
文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collection
でのwhere
利用は,like
が使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()
を呼ぶとIlluminate\Database\Eloquent\Collection
インスタンスが返って来るのに対し,query()
を呼ぶと,Illuminate\Database\Eloquent\Builder
インスタンスが返るという差異があり,それぞれのインスタンスでは,挙動が異なるということへの気づきを得られました。これからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えない!?とハマったときの話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employee
テーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
name
カラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
like
であいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employee
テーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$name
に「山田」が入った状態。前方と後方に付加した
'%'
文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collection
でのwhere
利用は,like
が使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()
を呼ぶとIlluminate\Database\Eloquent\Collection
インスタンスが返って来るのに対し,query()
を呼ぶと,Illuminate\Database\Eloquent\Builder
インスタンスが返るという差異があり,それぞれのインスタンスでは,挙動が異なるということへの気づきを得られました。これからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T16:47:35+09:00
Laravel の Eloquent モデルでは where に like が使えない!?とハマった話
コロナから Laravel 入門,3日目で盛大にコケる
コロナでフルリモート勤務になり,平日・休日問わず,自宅警備員として絶賛激務中のおじさんです。
通勤時間がなくなったことをきっかけに,「いつか勉強しよう」と思っていた Laravel を触ってみることに……
Laravel 習得の第一関門(?)とも言える Eloquent で,さっそく盛大にコケましたので,記事にしてみました。
マサカリ歓迎案件です。
Eloquent でコケるストーリー
おじさんの作業した条件です。
利用環境
- データベース:SQLite
- Laravel バージョン:7.24.0
テーブルとモデル
以下のような
employee
テーブルを作りました。苗字と名前を,半角スペース1文字で区切ったものを,ひとまとめに連結して
name
カラムに入れています(そんな定義のテーブルを作ること自体,雑すぎるというざわつきが聞こえてきそうです)。
id name 1 山田 太郎 2 山田 花子 3 鈴木 一郎 対応するモデルも作成します。
Employee.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employee'; protected $guarded = array('id'); }実際のコード
HTML 画面に入力された「名前」を取得し,
like
演算子であいまい検索をしようとしたのですが,ここでトラブル発生……EmployeeController.php<?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; class EmployeeController extends Controller { public function index(Request $request) { $employee = Employee::all(); // 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); } return response()->json([ 'responseJSON' => $employee->toArray(), ], 200); } }遭遇したトラブル
前述のとおり
employee
テーブルには,二人の「山田」さんを登録しておいたのですが,HTML 画面で「山田」と入力しても,検索結果がゼロ件になってしまいます。ちょうど,以下のコードでは,変数
$name
に「山田」が入った状態。前方と後方に付加した
'%'
文字によって,ワイルドカード検索が起きるという期待は,見事,裏切られてしまいます orzEmployeeController.php(抜粋)// 「名前」による絞り込み $name = $request->query('name'); if (!is_null($name)) { $employee = $employee->where('name', 'like', '%' . $name . '%'); }調べて分かったこと
結論から言うと,Illuminate\Database\Eloquent\Collection
でのwhere
利用は,like
演算子が使えないようです。結論から言うと,おじさんの試みたモデルの作成方法がマズかったというのが,原因でした(@nunulk さんのアドバイスで解決できました)。
EmployeeController.php(変更前)$employee = Employee::all(); return response()->json([ 'responseJSON' => $employee->toArray(), ], 200);EmployeeController.php(変更後)$employee = Employee::query(); return response()->json([ 'responseJSON' => $employee->get(), ], 200);まとめ
モデルクラスに対して,
all()
を呼ぶとIlluminate\Database\Eloquent\Collection
インスタンスが返って来るのに対し,query()
を呼ぶと,Illuminate\Database\Eloquent\Builder
インスタンスが返るという差異があり,それぞれのインスタンスでは,where
関数の挙動が異なるということへの気づきを得られました。
Illuminate\Database\Eloquent\Collection::where
⇒like
演算子が使えない(動かない。そういう仕様?)Illuminate\Database\Eloquent\Builder::where
⇒like
演算子が使えるこれからもしっかり,API 仕様書を読み込んで勉強していこうと思う出来事でした。
@nunulk さん,貴重なアドバイス,感謝いたします。
- 投稿日:2020-08-14T16:40:54+09:00
phpのあとにapacheをインストールした場合ソースが表示されるのを修正
本来はapacheをインストール後にphpを入れればいいのだが、先にphpを入れてしまった場合の対処方法
php -> apache2 をインストール
sudo apt install php sudo apt install apache2apache用のphpモジュールのインストール
sudo apt-get install libapache2-mod-php一旦apacheのphp関係の動作を止める
sudo a2enmod mpm_prefork or sudo a2dismod mpm_event※動作方法を mpm_prefork か mpm_event にするかはチューニングする際に考慮します。php-cgi を使用するときなど。
phpを再度apacheに認識させる
sudo a2enmod php7.3apacheサービスの再起動
sudo service apache2 restart
- 投稿日:2020-08-14T15:33:20+09:00
ポートフォリオ 「FOOTBALL SHIRTS」 FWを使用せず素のPHPで制作しました。
初めに
フロントエンドエンジニアを目指してプログラミングを学習しています。
長田と申します。
プログラミング学習のアウトプットとして自作のWebサービス「FOOTBALL SHIRTS」のポートフォリオを制作しました。
この記事では「FOOTBALL SHIRTS」の概要や制作過程について説明します。
ソースコード↓
https://github.com/satoruosada/uniform目的
・フルスクラッチ開発を行うことでWebアプリの基本的な構成、動作を知る.
・自作のWebアプリで同じ初学者の方の役に立つサービスを提供したい.スペック
使用言語 / HTML5/ CSS3 / Javascript / PHP
DBMS / MySQL
開発環境 / MacOS Catalina 10.15.6
バージョン管理 / SourceTree(3.0.15)
主な機能
ユーザー管理機能
・ユーザー登録機能
・ユーザーログイン機能
・ユーザー編集機能
・ユーザー削除機能出品する商品登録管理機能
・商品登録
・商品編集
・商品詳細
・商品一覧(ページネーション)
・商品検索機能
商品詳細機能
・商品詳細表示
・商品へのリンク
・お気に入り機能
・掲示板機能(メッセージ投稿)概要
「FOOTBALL SHIRTS」は、サッカーのユニフォームを出品するWebサービスです。
サッカーのユニフォームマニアが様々な世界中のサッカーのユニフォームを集めたり、転売できる専門のwebサービスを制作してみました。
開発手順
実装させたい主な機能から必要な項目を洗い出し、サンプルとしてExcelに必要なDB情報を書き出していきました。
洗い出した情報を元にテーブルを作成します。AdobeXDデザインカンプ作成
コーディング
デザインカンプを元に画面モックを作成
その後裏側の機能を実装していきますセキュリティ対策
バリデーションチェック
サーバーサイド(PHP)側
☑︎未入力チェック
☑︎最大、最小文字数チェック
☑︎半角英数字チェック
☑︎正規表現を使用したemail型式チェック
☑︎正規表現を使用したURL型式チェック
☑︎同値チェック
それぞれ関数を作成し各フォームで判定を行なっています。
以下一部抜粋です
フロントエンド(HTML)側
なりすまし対策について
セッションハイジャックによるなりすまし対策としてsession_regenerate_id関数を使用しています。
session_regenerate_id関数は、現在のセッションのデータを保持したまま、セッションIDを新しく生成してくれます。パスワードのハッシュ化について
パスワードをDBで登録する際は開発環境から見えてしまうのでセキュリティ上よくありません。
「FOOTBALL SHIRTS」ではpassword_hash関数でパスワードをハッシュ化してDB登録しています。
ログイン時には、
password_verifiを使用し、ハッシュ化されたパスワードを確認しています。
このとき$passはフォームからpostされたパスワード
DBから配列形式で取り出した情報を$resultに詰め
array_shiftを使って先頭から要素を一つ取り出し第二引数としています。SQLインジェクション対策
DB接続時は、プレースホルダーを利用しSQL文を作成。
プリペアードステートメントを使うことでSQLインジェクション対策を行なっています。
XSS(クロスサイトスクリプティング)対策
画面へ文字列や数値を出力する際は、htmlspecialchars関数を使いエスケープ処理を行なっています。
エスケープ処理とは特殊な文字を無害な文字に強制的に置き換える方法です
第二引数のエスケープにはいくつか種類がありますが最もエスケープ文字数の多いENT_QUOTESを使用しています。「FOOTBALL SHIRTS」で出来ること
①「FOOTBALL SHIRTS」へのユーザー登録、ログイン、ログアウト
②ログイン後「FOOTBALL SHIRTS」の商品登録(出品)・編集・マイページ機能
「FOOTBALL SHIRTS」の商品登録ページでは、「FOOTBALL SHIRTS」の商品名、カテゴリー、詳細コメント、商品の画像の登録が可能です。
画像登録にはjQueryを利用しドラック&ドロップでInputされるよう設定しています。
登録完了後はマイページに遷移し、きちんと登録できたことが確認できるようメッセージが表示されます。
マイページでは自身が登録した「FOOTBALL SHIRTS」の商品の閲覧、編集、自身のプロフィール編集、パスワード変更、退会、お気に入り登録した「FOOTBALL SHIRTS」の閲覧が可能です。
③「FOOTBALL SHIRTS」詳細画面に、Ajax処理によるお気に入り登録機能、掲示板機能を実装
お気に入り機能について
「FOOTBALL SHIRTS」の商品詳細画面では、商品画像右上のハートアイコンを押すことで
マイページのお気に入り一覧に商品を登録できます。
こちらはAjaxを用いて実装しています。掲示板機能について
「FOOTBALL SHIRTS」のやり取りについて気軽に質問できるように
掲示板機能を各詳細ページに実装しています。まず掲示板自体が存在するかDB情報を確認。
無ければ新規作成します。
もし既に掲示板情報があればメッセージのみ追加できるよう条件分岐させています。④商品一覧・検索機能
- 投稿日:2020-08-14T14:45:01+09:00
Zend Server Basic for IBM i のOSへのバンドル停止と無償利用の終了。有償エディションかCommunity PHPへの移行を
Zend Server Basic for IBM i のOSへのバンドルは2020-06-30で終了しました。無償利用は 2021-06-30 で終了します。
IBM から 2020/04/14 にこんな発表がありました。
ソフトウェアの営業活動終了およびサポート終了: IBM Rational Developer for AIX and Linux V9.1.x
タイトルからは IBM i には関係があると感じられませんが、こんなことが書いてありました。
2020 年 6 月 30 日付で、 IBM は、 IBM i オペレーティング・システム (5770-SS1) を使用した Zend Server Basic エディションの提供を終了します。
2021 年 6 月 30 日付で、 IBM i を使用した Zend Server Basic エディションを入手したすべての IBM i クライアントに向けた Zend Server Basic Support は終了します。 IBM i 用の Zend Server Professional および Zend Server Enterprise エディションは、Zend by Perforce © (「Zend」) が直接、提供およびサポートします。
すでに、プリロードや ESS からダウンロードという形での Zend Server Basic エディションの IBM からの提供は終了しています。
さらに、2021-06-30 で無償使用権が終了します。
「Support は終了します」だから、他のサポート終了ソフトと同じように、ノーサポートで、そのまま使えばいんじゃないの ? と思われるかもしれませんが、ここでのサポートは「使用権」も対象になるようです。期限を超えての利用はライセンス違反・コンプライアンス違反 になる可能性があります。レターにはこうも書いてあります。オリジナルの IBM のレターはリンクになっている部分があるので、オリジナルのレターも確認してください。
Effective June 30, 2020, IBM will no longer deliver Zend Server Basic edition with the IBM i operating system (5770-SS1).
- Clients who wish to continue using Zend Server Basic edition can acquire the product directly from Zend.
- Clients can continue developing and deploying their PHP applications on Zend Server Professional and Zend Server Enterprise editions for IBM i, which will be supported directly by Zend. See Transform Your IBM i Power® Systems into Platforms for Innovation.
- Clients who wish to use the open source Community PHP, see Installing Community PHP on IBM i.
- Clients who require support for Community PHP or other Open Source products may acquire an Open Source Support contract from IBM. See Speed innovation with open source support. Clients may explore other solutions for PHP code development such as PHPStorm or VSCode. This is not a comprehensive list of replacement products. Other companies and open source communities may offer PHP code development solutions.
Effective June 30, 2021, Zend Server Basic Support is withdrawn for all Zend Server delivered with IBM i. For IBM i clients who have acquired Zend Server with IBM i, the last date to register Zend Server for one-year Zend Server Basic Support is June 30, 2020. Any registrations for Zend Server Basic Support after June 30, 2020 will result in end of Zend Server Basic Support effective June 30, 2021. Zend Server Professional and Zend Server Enterprise editions for IBM i will continue be supported directly by Zend.
Zend by Perforce からの BLOGはこちらです。FAQ もあります。
IBM and Zend by Perforce Announcement for IBM i Users
移行先は?
IBM i 用の Zend Server Professional か Zend Server Enterprise エディションの購入
一つ目は上位の有料エディションである Zend Server Professional 、 Zend Server Enterprise エディションを Zend by Perforce 社から直接購入することです。
レターにはこうあります。IBM i 用の Zend Server Professional および Zend Server Enterprise エディションは、Zend by Perforce © (「Zend」) が直接、提供およびサポートします。
この場合、IBM i の各種統合機能はそのまま動きます。多くの追加モジュール・IBM i 統合機能もこれまで同様にコンパイル済みで同梱されます。
無償の Community PHP への移行
無料で、PHP を使いたい場合は、Community 版の PHP に移行することもできます。
ただし、追加モジュールは別途自分で導入したり、コンパイルする必要があります。
また、バグやセキュリティパッチの提供は、有償の Zend Server より短期間となります。比較情報
IBM i、Zend by Perforce 両方から比較の情報が出ています。
これらを検討して適切な移行先を判断してください。PHP on IBM i
PHP and IBM i – A Winning Combination上のリンクに Zend Server の Free to use? が「Yes, until June 30, 2021」とはっきり書いてあります。やはり、無償利用が許可されるのは 2021-06-30 までのようです。
Community PHP の導入
Community PHP を移行先とする場合、メディアからRSTLICPGMで導入というわけにはいきません。
Community PHP は、今どきの IBM i のオープンソースとして yum で導入します。IBM i のオープンソースの yum 導入の機能は、こちらをご覧ください。
https://bitbucket.org/ibmi/opensource/src/154edd688e225f978cd20a428c2d9c879748af4b/docs/yum/
Community PHP の導入は追加レポジトリーから行います。
こちらに情報があります。https://bitbucket.org/ibmi/opensource/src/master/docs/yum/3RD_PARTY_REPOS.md
yum-config-manager コマンドを使えるようにするために、yum-utils パッケージを導入します。
次に下記の様にレポジトリーを追加します。yum-config-manager --add-repo http://repos.zend.com/ibmiphp/下記で php の基本文が導入されます。
yum install php-common php-cliこの後、必要な Extension の入手、Webサーバーとの統合、DB アクセスのセットアップが必要です。
こちらの記事に、php の導入、Webサーバーとの統合、DB アクセスのセットアップがまとまっれていました。
許可の無い転載を禁じます。
この記事は筆者の個人的な責任で無保証で提供しています。
当記事に関してIBMやビジネスパートナーに問い合わせることは、固くお断りします。
- 投稿日:2020-08-14T13:32:30+09:00
PHP: クッキーの使い方
こちらと同じことを、PHP で行いました。
jquery-cookie の使い方
Perl: クッキーの使い方クッキーを送る
cookie_put.php<?php // ------------------------------------------------------------------ // // cookie_put.php // $expire = time()+60*60*24*7; setcookie('message','これはテストです。',$expire); setcookie('aa','りんご',$expire); setcookie('bb','ぶどう',$expire); setcookie('cc','桃',$expire); echo "<!DOCTYPE html>"; echo "<html lang=\"ja\">"; echo "<body>"; echo "Aug/14/2020<p/>"; echo "</body>"; echo "</html>"; // ------------------------------------------------------------------ ?>クッキーを確認
cookie_get.php<?php // ------------------------------------------------------------------ // // cookie_get.php // echo "<!DOCTYPE html>"; echo "<html lang=\"ja\">"; echo "<body>"; echo "message: " . $_COOKIE['message'] . "<br />"; echo "aa: " . $_COOKIE['aa'] . "<br />"; echo "bb: " . $_COOKIE['bb'] . "<br />"; echo "cc: " . $_COOKIE['cc'] . "<br />"; echo "Aug/14/2020<p/>"; echo "</body>"; echo "</html>"; // ------------------------------------------------------------------ ?>
- 投稿日:2020-08-14T12:52:41+09:00
「型」はプログラムのシナリオ設計
要約
- 「型」はプログラムのシナリオ。正しい使い方の道筋と、得られる結果を教えてくれる。
- 「型」が間違っていることは想定されたプログラムのシナリオとは違っていることを示している。
- 「型」が間違っていた場合、言語がエラーとして教えてくれるので起動修正することが出来る。
- この辺意識すると関数型言語でもとっつきやすくなるよ!
はじめに
- PHP歴が長いので、コードはPHPベースです。
- 動的型付け言語だろうと型付ける派です。
「型」が定義されていることの恩恵
「型」が定義されていない場合
まずは簡単な例として数値を計算するメソッドを作ります。
function add($baseNumber, $addNumber);
baseNumber
にもaddNumber
にも型は指定されていない状態です。
この関数を見た時に、何を渡すでしょうか?
名前にNumberとあるので、整数か小数の、とりあえず数字を渡すことを考えるでしょう。
果たしてそれはこの関数の想定された動作なのでしょうか?
また保証された結果が返ってくるのでしょうか?「型」が定義されている場合
次に型を付けてみましょう。
function add(int $baseNumber, int $addNumber): int;こうなっていた場合、
baseNumber
やaddNumber
には必ず整数を渡しますよね。
型を付けたことでadd()
関数は整数を渡すのが正しい使い方だと分かるようになり、整数を返すのが正しい結果だと分かるようになりました。恩恵のまとめ
型があることで
add()
関数の使い方の道筋=シナリオがより明確になったよりシナリオを意識した「型」を作る
先ほどは加算という簡単な例でした。
型を付けなくても変数名で何とかしようと思えば、何とか出来る範囲でしょう。
ではより具体的なシナリオを意識した場合、どちらが分かりやすくなるか比較していきましょう。支払金額累計の処理
支払金額累計の処理を作ることになりました。
ルールは以下の通りです。支払金額累計のルール
支払金額累計 = 今月分までの支払い金額 + 昨年度の支払い累計
「型」が定義されていない場合
// 定義 function calculatePaymentTotal($currentPaymentTotal, $lastYearPaymentTotal); function currentPaymentTotal(); function lastYearPaymentTotal(); // 利用 calculatePaymentTotal(currentPaymentTotal(), lastYearPaymentTotal());変数名からそれとなく
currentPaymentTotal() = 今月分までの支払い金額
と
lastYearPaymentTotal() = 昨年度の支払い累計
を渡せばいいことが想定できると思います。仕様が複雑化した場合
// 定義 function calculatePaymentTotal($currentPaymentTotal, $lastYearPaymentTotal); function currentPaymentTotal(); function lastYearPaymentTotal(); function currentPaymentTotalByPurchaser(); function lastYearPaymentTotalByPurchaser(); function currentPaymentTotalBySale(); function lastYearPaymentTotalBySale(); // 利用 // どれが想定されたシナリオ? calculatePaymentTotal(currentPaymentTotal(), lastYearPaymentTotal()); // 〇? ×? calculatePaymentTotal(currentPaymentTotalByPurchaser(), lastYearPaymentTotalByPurchaser()); // 〇? ×? calculatePaymentTotal(currentPaymentTotalBySale(), lastYearPaymentTotalBySale()); // 〇? ×?仕様が増え、累計に関する処理が増えました。
果たしてcalculatePaymentTotal()
を使おうとした場合、その使い方は正しいのでしょうか?
そもそも該当の関数を見落として、別の処理を使ってしまうかもしれません。
どれが正しいシナリオなのか、想定するのも困難になりました。「型」が定義されている場合
function calculatePaymentTotal( CurrentPaymentTotal $currentPaymentTotal, LastYearPaymentTotal $lastYearPaymentTotal ); class CurrentPaymentTotal(); class LastYearPaymentTotal();型を指定していることで、
CurrentPaymentTotal
とLastYearPaymentTotal
を渡せばいいことがはっきりしていますね。
では仕様が複雑化した場合はどうでしょうか。仕様が複雑化した場合
// 定義 function calculatePaymentTotal( CurrentPaymentTotal $currentPaymentTotal, LastYearPaymentTotal $lastYearPaymentTotal ); class CurrentPaymentTotal(); class LastYearPaymentTotal(); class CurrentPaymentTotalByPurchaser(); class LastYearPaymentTotalByPurchaser(); class CurrentPaymentTotalBySale(); class LastYearPaymentTotalBySale(); // 利用 $currentPaymentTotal = new CurrentPaymentTotal(); $lastYearPaymentTotalnew = new LastYearPaymentTotal(); calculatePaymentTotal($currentPaymentTotal , $lastYearPaymentTotal)型を指定していることで、いくら仕様が増えようと、
CurrentPaymentTotal
とLastYearPaymentTotal
を渡すことが正しいシナリオであると定まっています。
またCurrentPaymentTotal
やLastYearPaymentTotal
を先に処理ことがはっきりしているので、処理の順序も崩れることがありません。まとめ
型を定義して、より堅牢なシナリオを作ろう
型を定義しない場合と定義した場合とで、何をしたいか、何をしてほしいかの読み取りやすさがきっぱりと別れたと思います。
型を考え定義することは少しのコストがかかりますが、堅牢なシナリオを作ることが出来ます。
その恩恵は持続し、より堅牢なプログラムになります。
どんどん定義していきましょう。型に頼らなくても似たようなことできるのでは?
上記に挙げたメリットは頑張ればDocコメントやコードレビューなどのコミュニケーションで回避できるものでもあります。
ただ経験上、開発者、レビュアー、レビュイーの誰もがコストを強いられうまくいった試しがありません。
そこは言語に任せて、誰も間違えない環境を作っていく方がコスト減につながると思っています。
- 投稿日:2020-08-14T07:42:32+09:00
Laravel x GAS API で Spreadsheet 自動作成してみた
はじめに
実務で、GAS(Google Apps Script)を使って Spreadsheet 作成APIを作ったので、そのやり方を書く。
今回は、Laravel の Guzzle を使って APIを叩いたので、そのテンプレも書くことにした。ここでは、clasp などのコマンドラインでの作業の説明は割愛する。
Guzzle テンプレート
$client = new \GuzzleHttp\Client(); url = https://script.google.com/a/XXXX.co.jp/macros/s/XXXXXXXXXXXXXXXXXXXXXXXX/exec $sheets = ['[シート名]' => [['名前', 'Soma', 'Sekimoto'],['住所', 'Tokyo', 'Suginami'], ['年齢と性別', ' 23歳', '男']]] $res = $client->post( $url, [\GuzzleHttp\RequestOptions::JSON => [ 'title' => 'スプレッドシートタイトル', 'sheets' => $sheets, 'spread_sheet_id' => '[スプレッドシート ID]', 'startPositions' => ['startRow' => 3, 'startColumn' => 2], ]]);GAS(Google Apps Script) API
CreateSheet.jsfunction doPost(e) { // 引数で送られてきたパラメータを取得する var c = e.postData.contents var contents = JSON.parse(c) // パラメーターをそれぞれの変数に格納 var title = contents.title var sheets = contents.sheets var startPositions = contents.startPositions var startRow = startPositions.startRow var startColumn = startPositions.startColumn var spreadsheetId = contents.spread_sheet_id var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId).copy(title) : SpreadsheetApp.create(title); // Parameter に spread_sheet_id がない場合は、新しいスプレッドシートを作るようにする。 i = 0 for (var sheet_name in sheets) { if (i >= spreadsheet.getSheets().length) { spreadsheet.insertSheet() } // シートが足りなくなった時は新しいシートを追加させる。 var sheet = spreadsheet.getSheets()[i] var rows = sheets[sheet_name] sheet.getRange(startRow, startColumn, rows.length, rows[0].length).setValues(rows) sheet.getRange(startRow, startColumn, rows.length, rows[0].length).createFilter() sheet.setName(sheet_name) i += 1; } spreadsheet.addEditor("/xx/domain/XXXX.co.jp") // 編集者を付与できる。 return ContentService.createTextOutput(JSON.stringify({url: spreadsheet.getUrl()})).setMimeType(ContentService.MimeType.JSON); // 作成したシートの URL を返す。 }これでスプレッドシート自動作成API完成!!!!
おわりに
既存アプリ x GAS APIの無限大の可能性を利用すれば、最強のAPIレポジトリを完成させることも夢ではありません。みなさんの参考になれば幸いです。
もっとこうした方がいい!!、ここは間違ってる!! 等ございましたら遠慮なくお申し付けください!
もっとスマートなコードの書き方あると思うので、思いつく方いらっしゃいましたらご教授お願いします!
- 投稿日:2020-08-14T05:41:38+09:00
docker-compose を用いて Apache・PHP・MySQL の開発環境を構築してみた
はじめに
docker-compose を用いて Apache・PHP・MySQL の開発環境を構築してみた備忘録になります。
バージョン情報
docker-compose はインストールされていることが前提になります。
今回は以下のバージョンでの動きになります。$ docker-compose -v docker-compose version 1.26.2, build eefe0d31PHP のバージョンは 5.4、MySQL のバージョンは 5.5 の環境を構築します。
Apache のバージョンは特に気にしていません。ディレクトリ構造
ディレクトリ構造は以下のようになります。
. ├── config │ ├── mysql │ │ ├── Dockerfile │ │ ├── initdb.d │ │ │ └── init.sql │ │ └── my.cnf │ └── php │ ├── Dockerfile │ ├── apache2.conf │ ├── php.ini │ └── sites │ ├── 000-default.conf │ └── default-ssl.conf ├── data ├── docker-compose.yml └── html └── test ├── connect.php └── index.phpGitHub にもあげました。ご参考まで。
設定
各サービスに以下のような設定ファイルがそれぞれあると思いますが、それらを変更したものを動かしたいと思います。
Apache の設定
今回は DocumentRoot を変更してみます。
公式ドキュメント をみると以下のように Dockerfile を記述することで DocumentRoot を変更できるようです。
いろいろな記事をみるとコンテナから 000-default.conf などの設定ファイルをホスト側にもってきてそれを修正した後、Dockerfile のCOPY
でコンテナ側にコピーするようでした。
やはり公式ドキュメントをみるのが一番ですね。公式ドキュメントのように行ったらコンテナが再起動ループに陥ってしまいました。
※ 理由をよく調べたら php:5.5-apache 以降であればそれでよかったらしいです。
なので、php:5.4-apache のイメージからコンテナを立ち上げて、元ファイルをホスト側にコピーして、それを編集することにしました。
# とりあえず php:5.4-apache のコンテナを動かしてログインする $ docker image pull php:5.4-apache $ docker container run --name php54apache -d php:5.4-apache $ docker exec -it php54apache /bin/bash # 設定ファイルを確認 ( php:5.4-apache 以前は apache2.conf の修正も必要) $ ls /etc/apache2/apache2.conf httpd.conf $ ls /etc/apache2/sites-available 000-default.conf default-ssl.conf # コンテナを抜ける $ exit # ホスト側にコピー $ docker container cp php54apache:/etc/apache2/apache2.conf ./config/php $ docker container cp php54apache:/etc/apache2/sites-available/000-default.conf ./config/php/sites $ docker container cp php54apache:/etc/apache2/sites-available/default-ssl.conf ./config/php/sitesこれらの
apache2.conf
、000-default.conf
、default-ssl.conf
に記述してある DocumentRoot を変更します。今回は以下のように変更しました。
# DocumentRoot /var/www/html DocumentRoot /var/www/html/test
これで DocumentRoot の変更の準備は完了です。
PHP の設定
PHP は
php.ini
によって設定を行います。公式ドキュメント をみると以下のように記述することで php.ini を指定するらしいです。
しかし、コンテナを立ち上げてログインしてみても、
php.ini-development
やphp.ini-produciton
のようなファイルは存在しませんでした。おそらく Apache の設定でもそうであったように公式のドキュメントは過去のバージョンまで挙動は保証していないようです。
サンプルにあるような
php:7.4-fpm-alpine
ならそれで良さそうですね。今回は
php.ini-development
やphp.ini-produciton
については GitHub で公開してあったのでそちらの php.ini-development を元に php.ini を作成することにしました。php.ini の修正についてはこちらを参照して、ロケーションや言語の設定を修正しました。
MySQL の設定ファイルの変更
MySQL の設定ファイルは
my.cnf
によって行います。MySQL の設定ファイルの my.cnf は 公式ドキュメント によると
/etc/mysql/my.cnf
に配置してあるとのことでした。なのでそちらをローカルホストに持ってきて、適宜修正していきたいと思います。
# とりあえず mysql:5.5 のコンテナを動かしてログインする $ docker image pull mysql:5.5 $ docker container run -e MYSQL_ROOT_PASSWORD=sample_pw --name mysql55 -d mysql:5.5 $ docker exec -it mysql55 /bin/bash # /etc/mysql/my.cnf を確認 $ ls /etc/mysql/my.cnf /etc/mysql/my.cnf # コンテナを抜ける $ exit # my.cnf をホスト側にコピー $ docker container cp mysql55:/etc/mysql/my.cnf ./config/mysql/あとは環境に合わせて修正して、
my.cnf
を作成してください。※ ちなみに今回は
my.cnf
の修正は行っていません。Dockerfile の作成
Dockerfile は
php:5.4-apache
とmysql:5.5
のイメージについて作成しました。php:5.4-apache
config/php/Dockerfile# image FROM php:5.4-apache # Set php.ini COPY ./php.ini /usr/local/etc/php/ # Set apache conf (Before tag:5.4-apache) COPY ./apache2.conf /etc/apache2/ COPY ./sites/*.conf /etc/apache2/sites-available/ # Set apache conf (After tag:5.5-apache) # ENV APACHE_DOCUMENT_ROOT /var/www/html/test # RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf # RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf # Install MySQL connection module RUN apt-get update \ && apt-get install -y libpq-dev \ && docker-php-ext-install pdo_mysql pdo_pgsql mysqli mbstringここでは「DocumentRoot の変更」、「php.ini の配置」、「MySQL と疎通するためのモジュールを追加」しています。
mysql:5.5
config/mysql/Dockerfile# image FROM mysql:5.5 # Set my.cnf COPY ./my.cnf /etc/mysql/conf.d/ # Set Japanese RUN apt-get update && apt-get install -y \ locales \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen \ && locale-gen ENV LANG ja_JP.UTF-8 CMD ["mysqld", "--character-set-server=utf8", "--collation-server=utf8_unicode_ci"]デフォルトのままであると MySQL にログインした後に日本語の入力ができなかったため、ここでは日本語を入力可能にするような設定をおこなています。
MySQL の日本語の設定は こちら を参照しました。
MySQL の初期データの投入
docker-compose 起動時に MySQL に初期データを投入してみたく、以下のような SQL を用意しました。
init.sqlDROP TABLE IF EXISTS sample_table; CREATE TABLE sample_table ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name TEXT NOT NULL ) charset=utf8; INSERT INTO sample_table (name) VALUES ("太郎"),("花子"),("令和");コンテナ起動後にここで記述した SQL が実行されていることを確認します。
docker-compose.yml の作成
以下のように docker-compose.yml を作成しました。
docker-compose.ymlversion: '3.8' services: # PHP Apache php-apache: build: ./config/php ports: - "8080:80" volumes: - ./html:/var/www/html restart: always depends_on: - mysql # MySQL mysql: build: ./config/mysql ports: - 3306:3306 volumes: - ./config/mysql/initdb.d:/docker-entrypoint-initdb.d - ./data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: sample_root_passward MYSQL_DATABASE: sample_db MYSQL_USER: sample_user MYSQL_PASSWORD: sample_passMySQL のデータの永続化について
MySQL のデータディレクトリは /var/lib/mysql であり、
- ./data:/var/lib/mysql
とあるようにホスト側の./data
ディレクトリにマウントすることによってデータを永続化しております。試してみたところ
docker-compose stop
やdocker-compose down
してもデータが永続化されていることが確認できました。docker-compose 使い方
いよいよ docker-compose を動かします。
docker-compose 起動
まずはなにも起動されていないことを確認します。
$ docker-compose ps Name Command State Ports ------------------------------ $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES今回はわかりやすいようにコンテナも空の状態にしておきます。
docker-compose.yml が存在する階層で
docker-compose up -d
コマンドを実行することで、docker-compose.yml の記述をもとにコンテナが作成されます。※ docker-compose のコマンドの
build
とup
の違いやオプションについてはこちらがわかりやすかったです。# image の作成 $ docker-compose build --no-cache Successfully tagged docker-compose-sample_php-apache:latest # コンテナの構築・起動 $ docker-compose up -d Creating network "docker-compose-sample_default" with the default driver Creating docker-compose-sample_mysql_1 ... done Creating docker-compose-sample_php-apache_1 ... done # docker-compose の確認 $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------- docker-compose-sample_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp docker-compose-sample_php-apache_1 apache2-foreground Up 0.0.0.0:8080->80/tcp # コンテナの確認 $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 26b0cec2daad docker-compose-sample_php-apache "apache2-foreground" 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp docker-compose-sample_php-apache_1 eaae044f4bba mysql:5.5 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:3306->3306/tcp docker-compose-sample_mysql_1コンテナが動いていることが確認できました。
PHP-Apache コンテナの確認
DocumentRoot の直下に配置する、php:5.4-apache コンテナの疎通確認用のページとして以下のようなファイルを用意しました。
html/test/index.php<?php echo __DIR__; phpinfo();
http://localhost:8080
にアクセスすると以下のように表示されるはずです。
echo __DIR__;
はその PHP ファイルが置かれている絶対パスを出力するので DocumentRoot の変更が行われていることが確認できます。また、
php.ini
についてオリジナルものから修正を加えていれば、phpinfo();
の出力でそちらも反映されていることも確認できるかと思います。MySQL コンテナの確認
mysql:5.5
のイメージで作成されたコンテナにログインして、初期データが投入されていることを確認します。shell# コンテナにログイン $ docker exec -it eaae044f4bba /bin/bash # MySQL にログイン $ mysql -p Enter password:パスワードを求められるので docker-compose.yml で
MYSQL_ROOT_PASSWORD
の環境変数に登録したsample_root_passward
と入力するとログインできます。docker-compose.yml に登録した
MYSQL_DATABASE
、MYSQL_USER
が登録されていることを確認します。mysql> select user, host from mysql.user; +-------------+-----------+ | user | host | +-------------+-----------+ | root | % | | sample_user | % | | root | localhost | +-------------+-----------+ 3 rows in set (0.00 sec) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sample_db | +--------------------+ 4 rows in set (0.00 sec)
sample_user
とsample_db
があることが確認できました。続いて
init.sql
で作成したテーブルとカラムについても確認してみます。mysql> use sample_db; Database changed mysql> show tables; +---------------------+ | Tables_in_sample_db | +---------------------+ | sample_table | +---------------------+ 1 row in set (0.00 sec) mysql> select * from sample_table; +----+--------+ | id | name | +----+--------+ | 1 | 太郎 | | 2 | 花子 | | 3 | 令和 | +----+--------+ 3 rows in set (0.00 sec)テーブルとカラムについても問題なく確認できました。
PHP-Apache から MySQL コンテナへの疎通確認
PHP-Apache から MySQL コンテナへの疎通確認用のページとして以下のようなファイルを用意しました。
html/test/connect.php<?php try { // host=XXXの部分のXXXにはmysqlのサービス名を指定します $dsn = 'mysql:host=mysql;dbname=sample_db;'; $db = new PDO($dsn, 'sample_user', 'sample_pass'); $sql = 'SELECT * FROM sample_table;'; $stmt = $db->prepare($sql); $stmt->execute(); $result = $stmt->fetchAll(PDO::FETCH_ASSOC); var_dump($result); } catch (PDOException $e) { echo $e->getMessage(); exit; }※ 注意点として、ホスト名は
localhost
や127.0.0.1
ではなく、docker-compose.yml でサービス名として指定してたmysql
を使用します。
http://localhost:8080/connect.php
にアクセスすると以下のように表示されればが PHP-Apache から MySQL コンテナへの疎通がうまく行っています。docker-compose 停止・削除
以下のコマンドで 停止・削除 を行います。
# コンテナを停止 $ docker-compose stop # コンテナを停止し、そのコンテナとネットワークの削除 $ docker-compose down # コンテナを停止し、そのコンテナとネットワークを削除、さらにイメージも削除 $ docker-compose down --rmi all --volumes
./config/mysql/initdb.d/init.sql
の初期データ投入からやり直したい時などはdocker-compose down --rmi all --volumes
してから./data
ディレクトリの中身も削除してdocker-compose up -d
でコンテナの構築・起動からやり直してください。まとめ
以下を自動化する docker-compose の開発環境構築を行いました。
- DocumentRoot の変更
- php.ini の変更
- my.cnf の変更
- MySQL への初期データの投入
- MySQL のデータの永続化
- PHP-Apache コンテナから MySQL コンテナへの疎通
つまり、これらの環境がワンライナーで構築できるようになりました。
学んだこと
今回初めて docker-compose で開発環境を構築してみて学んだことを羅列します。
- 公式ドキュメント(DockerHub)は読んだ方がいい
- Qiita よりも DockerHub を先に見た方がよさそうです。
- docker-compose.yml と Dockerfile はかき分ける
- 一度変更すればコンテナ起動後に変更のない設定ファイルなどは Dockerfile で
COPY
でコンテナ側にファイルを配置して、コンテナ起動後に変更のあるものは docker-compose.yml でマウントさせるという書き分けがよさそうです。- Dockerfile をいきなり書くのは難しい
- 以下の手順で Dockerfile を書くと良いです。
- ベースイメージを決める
- ベースイメージのコンテナ内で作業しつつ、うまくいった処理をメモ
- 全て成功したらDockerfileを作成
以上になります。
- 投稿日:2020-08-14T01:50:21+09:00
PHPで「ここはis_null?empty?isset?」ってなったので比較してまとめた。
is_null?empty?isset?ってなることが多々あったので、比較実験した。
実験内容
それぞれに以下を与えて結果を取得する。
- $string; //変数を宣言するだけ
- $string = 'test'; //文字列
- $string = null; //null
- 未定義の変数
- 空文字
最終結果はページの最後に表にしています。
is_null()
変数がNULLかどうか調べる。つまり、nullならばtrueが、それ以外であればfalseが返ってくる。
$string; is_null($string); // true$string = 'test'; is_null($string); // false$string = null; is_null($string); // true//定義していない $a is_null($a); // true//空文字 is_null(''); // falseempty()
変数が空であるかどうかを検査する。「変数が空」のイメージがしっくり来ないので、以下でテスト。
$string; empty($string); // true$string = 'test'; empty($string); // false$string = null; empty($string); // true//定義していない $b empty($b); // trueここまでは、is_nullと挙動が同じだなーって思ったが、以下の挙動が違った。
//空文字 empty(''); // true空文字は「変数が空である」と捉えられるらしい。
isset()
変数が宣言されていること、そして NULL とは異なることを検査する。
つまり、
- 変数が宣言されている
- その変数がnullではない
という2つの条件が整うとtrueが返ってくる。is_nullとemptyとは異なり2つ条件があるっぽい。
is_nullとemptyとは「あったらtrue」という感じでニュアンスが違う。
$string; isset($string); //false (変数は宣言されているが、nullなのでアウト)$string = 'test'; isset($string); // true (変数は宣言され、文字列が与えられているのでセーフ)$string = null; isset($string); // false (変数は宣言されているが、nullなのでアウト)// 定義していない $c isset($c); // false (変数が宣言されていないのでアウト)なるほど。じゃあ変数としてではなく、直接文字列とか与えたらどうなる??(以下のように)
isset(''); isset('string');結果は、
PHP Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)ダメらしい。
まとめ
上の結果を以下の表にまとめました。
$string $string = 'test' $string = null 定義していない変数 空文字を直接 is_null true false true true false empty true false true true true isset false true false false 空文字にかかわらず直接与えるとエラー