- 投稿日:2020-09-27T23:48:56+09:00
オブジェクト指向の真髄がわからなくてもそれっぽいコードが書ける実装ルール
オブジェクト指向よくわからんと思っている方達も「ThoughtWorksアンソロジー」の第5章「オブジェクト指向エクササイズ」にて紹介されている9つのルールを取り入れることで、こいつオブジェクト指向が分かっている!!と思われるようなコードが書ける。はず。
今まで手続き型で実装しオブジェクト指向に慣れていない方にとってはとても異様で非現実的なルールと思われるでしょう。
しかしオブジェクト指向で実装する場合はどれも当たり前に使われるルールとなります。
すぐにでも守ることのできる簡単なルールもあるので是非、今すぐ取り入れてオブジェクト指向完全理解したったって周りにそれっぽい顔していきましょう。※ サンプルコードはPHPで書いてます。
- 1つのメソッドにつきインデントは1段階までにすること
- else 句を使用しないこと
- すべてのプリミティブ型と文字列型をラップすること
- 1行につきドットは1つまでにすること
- 名前を省略しないこと
- すべてのエンティティを小さくすること
- 1つのクラスにつきインスタンス変数は2つまでにすること
- ファーストクラスコレクションを使用すること
- Getter, Setter, プロパティを使用しないこと
1つのメソッドにつきインデントは1段階までにすること
インデントは一つの処理単位となる。
それ以上深くなる場合は、メソッドやクラスに抽出しよう。else 句を使用しないこと
ガード節や早期リターンを使いelse句を無くす。
と、あるが else if は使用しないとして else は処理によっては使用しても良いかなと思う。
逆に else(ではない時) が分離されることで可読性が落ちてしまうこともあるなーと個人的には思う。すべてのプリミティブ型と文字列型をラップすること
その値専用のクラスを作ろう。DDD的に言うと「値オブジェクト」
その値に関わる判断/加工/計算するロジックをそのクラスに置いてあげることでコードの重複を防ぐことや変更の影響範囲を閉じ込めることができる。NG
class User { /** @var String */ private $email; /** @var String */ private $password; // EmailやPasswordに関わる判断/加工/計算メソッドをここに持つ }OK
class User { /** @var Email */ private $email; /** @var Password */ private $password; } class Email { /** @var String */ private $email; // Emailに関わる判断/加工/計算メソッドをここに持つ } class Password { /** @var String */ private $password; // passwordに関わる判断/加工/計算メソッドをここに持つ }小さな小さなクラスが沢山できていく。
1行につきドットは1つまでにすること
ドットは、phpで言うとアロー演算子(->)になる。
オブジェクトを返すメソッドを呼び出し、その返却されたオブジェクトのメソッドをそのまま呼び出すコードなどドットが繋がっている場合、オブジェクトが他のオブジェクトを深く掘り進んでいる事になる。つまりこれはカプセル化に違反している。
ドット一つごとに変数に格納し一行になっているのを別の行に分けていく。名前を省略しないこと
プログラミングの習慣として、数量(Quantity)をqやqtyなどと省略することがある。
人によって解釈は異なる為、ソフトウェアをわかりやすく保つには省略をせず明確な単語を使う。ただ、まぁ習慣として一般的なものであればそれは使っても良いだろうと個人的に思う。
すべてのエンティティを小さくすること
オブジェクト指向は短いメソッドと小さなクラスでプログラムを組み立てる技術。
大きくなったメソッドやクラスなどは小さく分解する。
適切な名前で小さいクラスを作ることでどこに何の処理が書いているかが分かり変更もしやすくなる。エンティティを小さく保つガイドライン
ここら辺を守ると良さそう
要素が30を超えるサブ要素で構成されている場合、重大な問題がある可能性が高くなります。
a)メソッドのコード行は平均30行を超えてはなりません(行スペースとコメントはカウントされません)。 b)クラスには平均30未満のメソッドが含まれている必要があり、結果として最大900行のコードになります。 c)パッケージには30を超えるクラスを含めることはできません。したがって、最大27,000のコード行で構成されます。 d)パッケージが30を超えるサブシステムは避けてください。このようなサブシステムは、最大810,000行のコードで最大900クラスをカウントします。 e)したがって、30のサブシステムを持つシステムは、27,000のクラスと2,430万のコード行を所有します。1つのクラスにつきインスタンス変数は2つまでにすること
インスタンス変数が増えると、クラスの意図がだんだんぼやけてくる。
その結果、複数の目的に使われはじめ、巨大なクラスとなってしまう。ファーストクラスコレクションを使用すること
配列やコレクション(ListやMap等)の操作は、コードが複雑になりがち。
その配列やコレクションをインスタンス変数に持つクラスとして抜き出す。NG
class Profile { /** @var String */ private $fullName; /** @var array */ private $phoneNumbers; }OK
class Profile { /** @var String */ private $fullName; /** @var PhoneNumbers */ private $phoneNumbers; } class PhoneNumbers { /** @var array */ private $phoneNumbers; }Getter, Setter, プロパティを使用しないこと
データクラスは、ただの入れ物ではなく何らかの判断/加工/計算を行うメソッドを持つ為、インスタンス変数をそのまま返すだけの getter は不要。
setter を用意すると自分以外の実装者または3ヶ月後の自分が別の処理で値を書き換えてしまう可能性があり、バグを生み出す原因となる。生成の時にインスタンス変数に詰める(完全コンストラクタパターン)などの方法を取り不変にすると良い。まとめ
あくまでエクササイズなのでこれを絶対のルールにするという訳では無い。
一度エクササイズをしたら、緩めてガイドラインとして使うと良い。参考
「ThoughtWorksアンソロジー ―アジャイルとオブジェクト指向によるソフトウェアイノベーション」「第5章 オブジェクト指向エクササイズ」
「現場で役立つシステム設計の原則: 変更を楽で安全にするオブジェクト指向の実践技法」「第10章 オブジェクト指向設計の学び方と教え方」
- 投稿日:2020-09-27T23:48:56+09:00
【備忘録】オブジェクト指向らしい開発を行う為のルール
「ThoughtWorksアンソロジー」の第5章「オブジェクト指向エクササイズ」にて紹介されている9つのルールを纏めておきます。
- 1つのメソッドにつきインデントは1段階までにすること
- else 句を使用しないこと
- すべてのプリミティブ型と文字列型をラップすること
- 1行につきドットは1つまでにすること
- 名前を省略しないこと
- すべてのエンティティを小さくすること
- 1つのクラスにつきインスタンス変数は2つまでにすること
- ファーストクラスコレクションを使用すること
- Getter, Setter, プロパティを使用しないこと
1つのメソッドにつきインデントは1段階までにすること
インデントは一つの処理単位となる。
それ以上深くなる場合は、メソッドやクラスに抽出しよう。else 句を使用しないこと
ガード節や早期リターンを使いelse句を無くす。
と、あるが else if は使用しないとして else は処理によっては使用しても良いかなと思う。
逆に else を分離することで可読性が落ちてしまうこともあるなーと個人的には思う。すべてのプリミティブ型と文字列型をラップすること
その値専用のクラスを作ろう。DDD的に言うと「値オブジェクト」
その値に関わる判断/加工/計算するロジックをそのクラスに置いてあげることでコードの重複を防ぐことや変更の影響範囲を閉じ込めることができる。1行につきドットは1つまでにすること
ドットは、phpで言うとアロー演算子(->)になる。
オブジェクトを返すメソッドを呼び出し、その返却されたオブジェクトのメソッドをそのまま呼び出すコードなどドットが繋がっている場合、オブジェクトが他のオブジェクトを深く掘り進んでいる事になる。つまりこれはカプセル化に違反している。
ドット一つごとに変数に格納し一行になっているのを別の行に分けていく。名前を省略しないこと
プログラミングの習慣として、数量(Quantity)をqやqtyなどと省略することがある。
人によって解釈は異なる為、ソフトウェアをわかりやすく保つには省略をせず明確な単語を使う。ただ、まぁ習慣として一般的なものであればそれは使っても良いだろうと個人的に思う。
すべてのエンティティを小さくすること
オブジェクト指向は短いメソッドと小さなクラスでプログラムを組み立てる技術。
大きくなったメソッドやクラスなどは小さく分解する。
適切な名前で小さいクラスを作ることでどこに何の処理が書いているかが分かり変更もしやすくなる。エンティティを小さく保つガイドライン
ここら辺を守ると良さそう
要素が30を超えるサブ要素で構成されている場合、重大な問題がある可能性が高くなります。
a)メソッドのコード行は平均30行を超えてはなりません(行スペースとコメントはカウントされません)。
b)クラスには平均30未満のメソッドが含まれている必要があり、結果として最大900行のコードになります。
c)パッケージには30を超えるクラスを含めることはできません。したがって、最大27,000のコード行で構成されます。
d)パッケージが30を超えるサブシステムは避けてください。このようなサブシステムは、最大810,000行のコードで最大900クラスをカウントします。
e)したがって、30のサブシステムを持つシステムは、27,000のクラスと2,430万のコード行を所有します。1つのクラスにつきインスタンス変数は2つまでにすること
インスタンス変数が増えると、クラスの意図がだんだんぼやけてくる。
その結果、複数の目的に使われはじめ、巨大なクラスとなってしまう。ファーストクラスコレクションを使用すること
配列やコレクション(ListやMap等)の操作は、コードが複雑になりがち。
その配列やコレクションをインスタンス変数に持つクラスとして抜き出す。NG
class Profile { /** @var String */ private $fullName; /** @var array */ private $phoneNumbers; }OK
class Profile { /** @var String */ private $fullName; /** @var PhoneNumbers */ private $phoneNumbers; } class PhoneNumbers { /** @var array */ private $phoneNumbers; }Getter, Setter, プロパティを使用しないこと
データクラスは、ただの入れ物ではなく何らかの判断/加工/計算を行うメソッドを持つ為、インスタンス変数をそのまま返すだけの getter は不要。
setter を用意すると自分以外の実装者または3ヶ月後の自分が別の処理で値を書き換えてしまう可能性があり、バグを生み出す原因となる。生成の時にインスタンス変数に詰める(完全コンストラクタパターン)などの方法を取り不変にすると良い。まとめ
あくまでエクササイズなのでこれを絶対のルールにするという訳では無い。
一度エクササイズをしたら、緩めてガイドラインとして使うと良い。参考
「ThoughtWorksアンソロジー ―アジャイルとオブジェクト指向によるソフトウェアイノベーション」「第5章 オブジェクト指向エクササイズ」
「現場で役立つシステム設計の原則: 変更を楽で安全にするオブジェクト指向の実践技法」「第10章 オブジェクト指向設計の学び方と教え方」
- 投稿日:2020-09-27T20:35:32+09:00
FuelPHP Input::post 第2引数
Input::post には第2引数を設定でき、目的の要素が見つからなかった場合に返す値を設定できる。
これによって空かどうかなどの条件式を書く必要がなくなる。example.php$number = \Input::post('number', 0); //numberが見つからなかったら0を返す
- 投稿日:2020-09-27T20:20:12+09:00
CentOS8 + nginx1.18 + php-fpm7.4 + MariaDB10.5 + CodeIgniter4の開発環境準備メモ
概要
2020年9月27日時点で、環境情報(バージョンなど)に記載の各環境を採用した開発環境を構築したので、その環境準備を行った際のメモとして残します。
環境情報(バージョンなど)
- CentOS 8.2.2004 (Core)
- Nginx 1.18.0
- php-fpm 7.4
- MariaDB Ver 15.1 Distrib 10.5.5-MariaDB
- Composer version 1.10.13 2020-09-09 11:46:34
- CodeIgniter 4.0.4
各種設定ファイルパスのメモ
環境 ファイルパス nginx /etc/nginx/conf.d/default.conf nginx /etc/nginx/nginx.conf php /etc/php.ini php-fpm /etc/php-fpm.d/www.conf MariaDB /etc/my.cnf.d/server.cnf MariaDB /etc/my.cnf.d/client.conf CentOSのその他諸々の設定
# timedatectl set-timezone Asia/Tokyo # localectl set-locale LANG=ja_JP.UTF-8 # source /etc/locale.conf # dnf install -y langpacks-ja # dnf install -y vim # dnf install -y elfutils-libelf-devel # dnf -y update # vim /etc/selinux/config SELINUX=disable ← disableに設定Nginxのセットアップ
Nginxをdnfでインストール
# vim /etc/yum.repos.d/nginx.repo/etc/yum.repos.d/nginx.repo[nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true [nginx-mainline] name=nginx mainline repo baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true以下のサイトでは、次のような定義でインストール手順が載っていたが、上記の設定と異なり、若干古いバージョンがセットアップされるようでした。(上記:1.18 下記1.14 2020.09.26時点)
https://www.nginx.com/resources/wiki/start/topics/tutorials/install/nginx.repo[nginx] name=nginx repo baseurl=https://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1# dnf -y install nginxNginxセットアップ完了の確認
# nginx -v nginx version: nginx/1.18.0 # systemctl enable nginx # systemctl start nginx以下にアクセスしてnginxの画面(Welcome to nginx!)が表示されればOK
http://192.168.33.10php-fpm7.4のセットアップ
リポジトリの追加
CentOS8の標準的なリポジトリには、2020.09.26時点で php-fpm 7.2.24 のバージョンとなっています。
7.4が最新なのでせっかくなら7.4を使いたい。
なので、追加でリポジトリを設定してそっちからセットアップ。# dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm # dnf module enable php:remi-7.4php-fpm7.4のインストール
開発要件に合わせて適宜修正が必要なので、下記サイトを参考に、PHP拡張モジュールで必要なものがあれば合わせてインストールする。
https://www.php.net/manual/ja/extensions.php# dnf module install php:remi-7.4 メタデータの期限切れの最終確認: 0:32:40 時間前の 2020年09月26日 19時39分36秒 に実施しました。 依存関係が解決しました。 ======================================================================================================================================== パッケージ アーキテクチャー バージョン リポジトリー サイズ ======================================================================================================================================== group/module パッケージをインストール中: php-cli x86_64 7.4.10-1.el8.remi remi-modular 4.6 M php-common x86_64 7.4.10-1.el8.remi remi-modular 1.2 M php-fpm x86_64 7.4.10-1.el8.remi remi-modular 1.6 M php-mbstring x86_64 7.4.10-1.el8.remi remi-modular 527 k php-xml x86_64 7.4.10-1.el8.remi remi-modular 214 k 依存関係のインストール中: httpd-filesystem noarch 2.4.37-21.module_el8.2.0+494+1df74eae AppStream 36 k oniguruma5php x86_64 6.9.5+rev1-2.el8.remi remi-safe 206 k php-json x86_64 7.4.10-1.el8.remi remi-modular 75 k 弱い依存関係のインストール中: nginx-filesystem noarch 1:1.14.1-9.module_el8.0.0+184+e34fea82 AppStream 24 k モジュールプロファイルのインストール中: php/common モジュールストリームの有効化中: httpd 2.4 nginx 1.14 php remi-7.4 トランザクションの概要 ======================================================================================================================================== インストール 9 パッケージphp-fpmのセットアップ完了の確認
# php -v PHP 7.4.10 (cli) (built: Sep 1 2020 13:58:08) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies # php-fpm -v PHP 7.4.10 (fpm-fcgi) (built: Sep 1 2020 13:58:08) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologiesphp.iniファイルの場所を念のため確認
# php -i | grep php.ini Configuration File (php.ini) Path => /etc Loaded Configuration File => /etc/php.iniphp.iniの設定を変更
php.iniの中身を変更
/etc/php.ini; デフォルト文字コード default_charset = UTF-8 ;HTTPのレスポンスにPHPのバージョン情報を出力するX-Powered-Byヘッダを含めない expose_php = Off ;メモリ使用量の上限 memory_limit = 256M ;POSTリクエストのデータの最大サイズ post_max_size = 128M ;ファイルをアップロードする際のアップロードファイルの最大サイズ upload_max_filesize = 100M ; エラーログ error_log = /var/log/php_error.log ; エラーを出力 error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT ; エラー表示(開発中は便利なので) display_errors = On display_startup_errors = On [Date] date.timezone = Asia/Tokyo [mbstring] ; mbstringのデフォルトの言語 mbstring.language = Japanese ; HTTP入力文字のエンコーディングを内部文字のエンコーディングに自動変換しない mbstring.encoding_translation = Off ; 文字コードを自動検出する際の優先順位 mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCIIphp-fpmとnginxを連携する設定
# cp -p /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.org # vim /etc/nginx/conf.d/default.conf/etc/nginx/conf.d/default.confserver { listen 80; server_name localhost; # ドキュメントルートの設定(apacheなどの標準的なドキュメントルートと同じ場所を設定) root /var/www/html; index index.php index.html index.htm; location / { # 指定された順序でfileやdirの存在を確認し、最初に見つかったものを返却する。 # いずれも無かった場合は、最後に指定されたパスに遷移する。 try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; # 1台のサーバーでnginx+php-fpmを動作させる場合、Unixソケットの方が高速に動作する。 # www.sockの場所は、/etc/nginx/conf.d/php-fpm.confの設定と合わせる。 fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; }
vagrant
ユーザーでnginxもphp-fpm関連も実行されたりするように設定を変更します。/etc/nginx/nginx.conf# 以下をvagrantに変更 user vagrant;/etc/php-fpm.d/www.conf# 以下をvagrantに変更 user = vagrant group = vagrant # コメントを解除してvagrantに変更 listen.owner = vagrant listen.group = vagrant listen.mode = 0660 # listen.ownerとlisten.groupを指定するため以下をコメントアウト ;listen.acl_users = apache,nginx設定を反映するためにサービスを再起動
# systemctl restart php-fpm # systemctl restart nginxphpinfo()で動作確認
phpinfoを実行するサンプルプログラムを用意してnginx+php-fpmの連携確認をする。
/var/www/html/index.php<?php phpinfo();以下のURLにアクセスしてphpinfo()の内容が表示されればnginxとphp-fpmの連携設定まで完了です。
MariaDBのセットアップ
リポジトリの追加
CentOS8のリポジトリでインストールされるMariaDBは10.3で、現時点で最新バージョンが10.5なので、これも最新バージョンをインストールしたいと思います。
# vim /etc/yum.repos.d/mariadb.repo [mariadb] name = MariaDB baseurl = http://yum.mariadb.org/10.5/centos8-amd64 module_hotfixes=1 gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB gpgcheck=1 # dnf info MariaDB-server MariaDB-devel --disablerepo=* --enablerepo=mariadb上記でリポジトリの追加が正常にできていることが確認できたので、実際にインストールします。
MariaDBをdnfでインストール
# dnf instal MariaDB-server MariaDB-devel --disablerepo=* --enablerepo=mariadb エラー: 問題: cannot install the best candidate for the job - nothing provides libaio.so.1()(64bit) needed by MariaDB-server-10.5.5-1.el8.x86_64 - nothing provides libaio.so.1(LIBAIO_0.1)(64bit) needed by MariaDB-server-10.5.5-1.el8.x86_64 - nothing provides libaio.so.1(LIBAIO_0.4)(64bit) needed by MariaDB-server-10.5.5-1.el8.x86_64 - nothing provides lsof needed by MariaDB-server-10.5.5-1.el8.x86_64 - nothing provides perl(DBI) needed by MariaDB-server-10.5.5-1.el8.x86_64 (インストール不可のパッケージをスキップするには、'--skip-broken' を追加してみてください または、'--nobest' を追加して、最適候補のパッケージのみを使用しないでください)なんかエラーになりますね。
エラーの原因としてはlibaio
,lsof
,perl-DBI
がMariaDBインストールに必要だよということらしい。
そのため、該当の3つをインストールする。# dnf install libaio # dnf install lsof # dnf install perl-DBI指摘された3つはインストールしたので再チャレンジします。
# dnf install MariaDB-server MariaDB-devel --disablerepo=* --enablerepo=mariadb メタデータの期限切れの最終確認: 0:10:24 時間前の 2020年09月27日 07時09分32秒 に実施しました。 エラー: 問題: package MariaDB-server-10.5.5-1.el8.x86_64 requires galera-4, but none of the providers can be installed - cannot install the best candidate for the job - nothing provides libboost_program_options.so.1.66.0()(64bit) needed by galera-4-26.4.3-1.rhel8.0.el8.x86_64 - nothing provides libboost_program_options.so.1.66.0()(64bit) needed by galera-4-26.4.4-1.rhel8.0.el8.x86_64 - nothing provides libboost_program_options.so.1.66.0()(64bit) needed by galera-4-26.4.5-1.el8.x86_64 - nothing provides socat needed by galera-4-26.4.5-1.el8.x86_64 (インストール不可のパッケージをスキップするには、'--skip-broken' を追加してみてください または、'--nobest' を追加して、最適候補のパッケージのみを使用しないでください)まだダメなんですね。
libboost_program_options
というのが何か無いと怒られるので、boost-program-options
をインストール。# dnf install boost-program-options3度目の正直にチャレンジ
うまくインストールが開始できました。# dnf install MariaDB-server MariaDB-devel --disablerepo=* --enablerepo=mariadbMariaDBインストールの確認
# mysql -V mariadb Ver 15.1 Distrib 10.5.5-MariaDB, for Linux (x86_64) using readline 5.1MariaDBの自動起動の設定
# systemctl start mariadb # systemctl enable mariadb # systemctl status mariadbmysql_secure_installationで初期設定
mysql_secure_installation# mysql_secure_installation # MariaDBのrootアカウントの現在のパスワードを入力して? Enter current password for root (enter for none): Enter # unix_socketを使った認証に変更する? # unix_socketとは、CentOSのログインユーザー名と、MariaDB側のユーザー名が同名の場合、 # ID/PASSを用いずに認証しましょうという仕組み。(MariaDB 10.4から追加となった?) Switch to unix_socket authentication [Y/n] : n # MariaDBのrootアカウントのパスワードを変更する? Change the root password? [Y/n] : Y New password: Hoge1234 Re-enter new password: Hoge1234 # 匿名ユーザーを削除する? Remove anonymous users? [Y/n] Y # リモートからのMariaDBのrootアカウントでログインを禁止する? Disallow root login remotely? [Y/n] Y # だれでもアクセス可能な「test」データベースがあるけど消しとく? Remove test database and access to it? [Y/n] Y # 権限テーブルを再読み込みすると、ここまでのすべての変更をすぐに反映させることができるけどやる? Reload privilege tables now? [Y/n] YMariaDB初期設定の確認
上記設定でMariaDBのrootアカウントのパスワードを設定しているので、そのパスワードでちゃんとアクセスできるか確認しておきます。
# mysql -u root -p Enter password: Hoge1234 Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 12 Server version: 10.5.5-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | +--------------------+ 3 rows in set (0.000 sec) MariaDB [(none)]> show variables like "chara%"; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.001 sec)OK.
※vagrantユーザーからsudo su -
でrootになった状態だと、mysql -u root -p
で求められるパスワードは何を入れてもログインできるんですね。(知らなかった。。。)MariaDBの構築が完了したので、今度はMariaDBで利用するユーザーやデータベースを作成します。
(別に開発環境なので全部rootユーザー使ってもいいんですが、念のため。)MaraiDBで日本語を問題なく扱うための設定
日本語を扱うため、文字コードを
utf8mb4
に変更します。/etc/my.cnf.d/server.cnf[mariadb] character-set-server = utf8mb4/etc/my.cnf.d/client.cnf[client-mariadb] default-character-set = utf8mb4
utf8mb4
に設定されていることを確認。# systemctl restart mariadb # mysql -u root -p MariaDB [(none)]> show variables like "chara%"; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.002 sec)データベースとユーザーの作成
データベースの作成とユーザーの作成
# mysql -u root -p MariaDB [(none)]> create database yudb; 全てのホストからアクセス可能なユーザー「yu」の作成 MariaDB [(none)]> create user yu; MariaDB [(none)]> set password for yu@'%'=password('yupass'); MariaDB [(none)]> select user, host from mysql.user; +-------------+-----------+ | User | Host | +-------------+-----------+ | yu | % | | mariadb.sys | localhost | | mysql | localhost | | root | localhost | +-------------+-----------+ 4 rows in set (0.004 sec) 全てのホストから「yu」ユーザーでアクセスした際、「yudb」データベースに対してcrudができるように権限を付与 MariaDB [(none)]> grant select,insert,update,delete ON yudb.* TO yu@'%'; 権限を反映 MariaDB [(none)]> flush privileges;PHPプログラムからMariaDBへの接続テスト
index.phpのプログラムをMySQLiを利用したDBアクセスの動作確認用に変更してphp -> MariaDBへの接続確認を行います。
/var/www/html/index.php<?php $server = 'localhost'; $user = 'yu'; $pass = 'yupass'; $dbname = 'yudb'; $my = new MySQLi($server, $user, $pass, $dbname); $my->set_charset('utf8mb4'); $sql = 'select now() as n from dual'; $result = $my->query($sql); // データベースの中身を取得 while($row = $result->fetch_assoc() ){ var_dump($row); } // DB接続を閉じる $my->close();
Fatal error: Uncaught Error: Class 'MySQLi' not found in /var/www/html/index.php:8 Stack trace: #0 {main} thrown in /var/www/html/index.php on line 8
とエラーが発生してしまう。これは、PHPの拡張モジュールで
mysqli extension
がインストールされていないため、見つからないよというエラーです。
そのため、以下のコマンドでインストールしておきます。# dnf -y install php-mysqlndhttp://192.168.33.10/
もう一度アクセスすると以下のように正常にMariaDBへの接続結果を確認できます。
array(1) { ["n"]=> string(19) "2020-09-27 15:11:27" }
CodeIgniterの環境準備
composerの準備から
CodeIgniterの開発環境を準備していきます。
composerを使っていきますので、まずはcomposerの準備から。# cd /usr/local/src # php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" # 以下のハッシュは Latest: v1.10.13 のものです。 # ターゲットとなるバージョンが異なる場合には https://getcomposer.org/download/ を参考にして下さい。 # php -r "if (hash_file('sha384', 'composer-setup.php') === '795f976fe0ebd8b75f26a6dd68f78fd3453ce79f32ecb33e7fd087d39bfeb978342fb73ac986cd4f54edd0dc902601dc') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" # php composer-setup.php # php -r "unlink('composer-setup.php');" # mv composer.phar /usr/local/bin/composer # composer -V Composer version 1.10.13 2020-09-09 11:46:34composerでCodeIgniter4のプロジェクト新規作成
composerを使ってCodeIgniter4のプロジェクトを作成します。
# cd /var/www/html # composer create-project codeigniter4/appstarter codeigniter4以下の問題が発生したようなので、一度作成した
codeigniter4
プロジェクトを削除して、必要な拡張モジュールを入れ直して、プロジェクトの再作成をします。Your requirements could not be resolved to an installable set of packages. Problem 1 - codeigniter4/framework v4.0.4 requires ext-intl * -> the requested PHP extension intl is missing from your system. - codeigniter4/framework v4.0.3 requires ext-intl * -> the requested PHP extension intl is missing from your system. - codeigniter4/framework v4.0.2 requires ext-intl * -> the requested PHP extension intl is missing from your system. - codeigniter4/framework v4.0.1 requires ext-intl * -> the requested PHP extension intl is missing from your system. - codeigniter4/framework 4.0.0 requires ext-intl * -> the requested PHP extension intl is missing from your system. - Installation request for codeigniter4/framework ^4 -> satisfiable by codeigniter4/framework[4.0.0, v4.0.1, v4.0.2, v4.0.3, v4.0.4].# dnf install php-intl再度
composer create-project
を実行すると、警告は出ているが、Problemは無くなったのでとりあえず完了する。http://192.168.33.10/codeigniter4/public/index.php
codeigniter4のプロジェクトに合わせてnginxのドキュメントルートの設定を変更
{project-name}/public/ 配下が、composerでインストールしたcodeigniter4のドキュメントルートとなるようです。
なので、nginxのドキュメントルートを変更します。/etc/nginx/conf.d/default.confroot /var/www/html/codeigniter4/public;上記変更後にnginxを再起動すれば、以下のURLでcodeigniter4のプロジェクトにアクセスできます。
http://192.168.33.10CodeIgniterの環境設定
env
という環境変数設定ファイルがあるのですが、このままでは利用できません。
.env
という名前のファイルにコピーして使い始めます。# cd /var/www/html/codeigniter4 # cp -p env .env/var/www/html/codeigniter4/.env# 開発中のエラー情報などを確認するための設定 CI_ENVIRONMENT = development # config.phpのbase_urlで設定していたやつ app.baseURL = 'http://192.168.33.10/' # MySQLデータベースへの接続情報 database.default.hostname = localhost database.default.database = yudb database.default.username = yu database.default.password = yupass database.default.DBDriver = MySQLi database.default.charset = utf8mb4
- 投稿日:2020-09-27T19:12:41+09:00
PHPによる(複数)画像アップロード処理
PHPの画像処理は苦手とする方が多いみたいで、リクエストをいただいたので、解説していきます!
他にもリクエストがあったら是非お願いします!
期待にお応えできるかはわからないですが笑フロントエンド側の処理は簡単なので省略します笑
一応補足しておくと
post
送信でenctype='multipart/form-data'
これを指定するだけです!user_edit.php<?php session_start(); ?> <!DOCTYPE html> <html> <head> <meta charset='UTF-8'> <title>ユーザー情報編集</title> </head> <body> <h1>ユーザー情報編集</h1> <form method='post' enctype='multipart/form-data' action='upload.php'> <input type='hidden' name='member_id' value='<?= $_GET['member_id']; ?>'> <p>アップロード画像</p> <input type='file' name='photo'> <input type='submit' name='upload' value='アップロード'> </form> <form method='post' action='user_detail.php'> <p>一言メッセージ<p> <input type='hidden' name='member_id' value='<?= $_GET['member_id']; ?>'> <input type='text' name='message' value='<?php if ($_GET['message'] != '未入力') echo $_GET['message']; ?>'> <input type='submit' name='submit' value='送信'> </form> <?php endif; ?> </body> </html>upload.php<?php session_start(); if (isset($_POST['upload']) && isset($_FILES['photo']) && isset($_SESSION['id']) && isset($_POST['member_id']) && $_SESSION['id'] == $_POST['member_id']) { if (empty($_FILES['photo']['name'])) { $msg = 'ファイルを入れてください'; } else { $photo = Date('Ymdhis'); $photo .= uniqid(mt_rand(), true); $tempfile = $_FILES['photo']['tmp_name']; switch (@exif_imagetype($tempfile)) { case 1: $photo .= '.gif'; break; case 2: $photo .= '.jpg'; break; case 3: $photo .= '.png'; break; default: header('Location: upload.php'); exit(); } $filemove = './photo/' . $photo; try { $dbh = new PDO('mysql:host=localhost; dbname=dbname;', 'username', 'password'); } catch (PDOException $e) { echo '接続失敗:' . $e->getMesssage(); exit(); } $upload = 'UPDATE members SET photo = :photo WHERE id = :id'; $stmt = $dbh->prepare($upload); $params = array( ':photo' => $photo, ':id' => $_SESSION['id'], ); if (move_uploaded_file($tempfile, $filemove)) { $msg = '画像アップロード完了'; $stmt->execute($params); } else { $msg = '画像ファイルをアップロードできませんでした'; } } } else { $msg = '画像ファイルを入れてください'; } ?> <!DOCTYPE html> <html> <head> <meta charset='UTF-8'> <title>画像アップロード</title> </head> <body> <?php echo $msg; ?> <p><a href='board.php'>ホームに戻る</a></p> </body> </html>処理の流れはざっとこんな感じ
- アップロード写真が存在するかどうか
- 存在した場合は拡張子のチェックをする
- ユニークな名前に変更して、
- ファイル名をDBに保存し、
- ファイルを指定のディレクトリに保存する
こんな感じ!
まず最初から作るときはバリデーションはガン無視でいいです。
とりあえず最低限動くように作ってデバッグしながら追加していくのがおすすめー
自分でテストするときは悪意のある操作はしないのでね笑では一つずつ解説していきましょう
まずアップロードされたファイルは$_FILES
というグローバル変数に格納されて、サーバーに仮アップロードされます上で一時的に保存される
連想配列の中身 解説 name inputのname属性 type ファイル形式が保存されるが、ユーザー任意のMINEタイプになるので、多分 $_SERVER
と同じ感じで信用できないtmp_name サーバーへ仮アップロードされたディレクトリとファイル名 error エラー情報 size ファイルサイズ(単位はバイト) まず拡張子を確認するために、
$_FILES['input属性']['tmp_name']
を変数に格納して
exif_imagetype
を使って拡張子を判断していきます
exif_imagetype
が返す値は
値 定数 1 IMAGETYPE_GIF 2 IMAGETYPE_JPEG 3 IMAGETYPE_PNG 4 IMAGETYPE_SWF 5 IMAGETYPE_PSD 6 IMAGETYPE_BMP 7 IMAGETYPE_TIFF_II (intel byte order) 8 IMAGETYPE_TIFF_MM (motorola byte order) 9 IMAGETYPE_JPC 10 IMAGETYPE_JP2 11 IMAGETYPE_JPX 12 IMAGETYPE_JB2 13 IMAGETYPE_SWC 14 IMAGETYPE_IFF 15 IMAGETYPE_WBMP 16 IMAGETYPE_XBM 17 IMAGETYPE_ICO 18 IMAGETYPE_WEBP 以上のような値を返すので、それを
switch
文で判別しているだけですswitch (@exif_imagetype($tempfile)) { case 1: $photo .= '.gif'; break; case 2: $photo .= '.jpg'; break; case 3: $photo .= '.png'; break; default: header('Location: upload.php'); exit(); }これは以下のようにも書き換えられます
switch (@exif_imagetype($tempfile)) { case IMAGETYPE_GIF: $photo .= '.gif'; break; case IMAGETYPE_JPEG: $photo .= '.jpg'; break; case IMAGETYPE_PNG: $photo .= '.png'; break; default: header('Location: upload.php'); exit(); }このように
.=
をつけると文字列の末尾に文字列を追加することができるので結果として拡張子をアップデートすることができますねー
拡張子がこの3タイプ以外の場合はリダイレクトされるようになってますここで
@
を付けているのはテキストファイルの拡張子を.jpg
にするとWarning級のエラー
が発生するのでそれを表示させないためです
エラー制御演算子@
で画像の形式を判別するために必要なだけのバイト数を読み込めない場合にエラーを発生するのを抑制してます
この前にファイルサイズでバリデーションをかけるのもいいと思います
やろうと思えばどこまででもできてしまうのでもっと詳しく知りたい方はこちらも参考にしてみてください
https://qiita.com/mpyw/items/939964377766a54d4682拡張子の判断が済んだら、DBにファイル名を保存していきましょう
DBに保存する際には
ファイル名を保存する方法
と、
ファイルをそのままDBに保存する方法
があります。
ファイルをそのままDBに保存する方法をとってしまうと重たくなってしまうので、ファイル名をDBに保存する方法をとります
詳しくは以下の記事を参考にしてください
https://qiita.com/7coco/items/1706a561131363d2c418DBの接続は負荷がかかるため最低限にしたいのでここで接続しています
PDO
には例外が発生するので、try, catch
を使ってエラーハンドリングしています
ここら辺は以下の記事を参考にするといいと思います
https://qiita.com/mpyw/items/b00b72c5c95aac573b71
https://qiita.com/7968/items/6f089fec8dde676abb5bユーザーが入力した情報はエスケープする必要があるので、
プリペアドステートメント
を使ってください
注意して欲しいのはexecute()
しているのはファイルを移動したのが確認できてからですかね。
move_uploaded_file()
を使って、一時的に保存されたファイルを自分が保存したいディレクトリに移しますユーザー任意のファイル名を使用するとバグを生む可能性があるので
uniqid(mt_rand(), true)
を使ってランダムな文字列でファイル名を作成します
date()
関数を使っているのはファイル名の重複を防止するためとデバッグ時の判別をしやすくするためです。
これには以前解説したことのあるmd5(uniqid(mt_rand(), true))
を使ってもいいし、user_id
をファイル名にすることもあります
そちらの解説はこちらから
https://qiita.com/satorunooshie/items/de494a25e7da4da16dcf書き忘れていましたが移動先のディレクトリの権限を変更しておいてください!
chmod 755 ./photo詳しくは以下のサイトを参考にしてください
https://qiita.com/shisama/items/5f4c4fa768642aad9e06
https://www.php.net/manual/ja/function.chmod.phpでは続いて複数ファイルをアップロードする方法についてみていきましょう
今回は少し違うアプローチで崩していきましょうexample.php<?php $dir = __DIR__ . './photo'; const MAX_SIZE = 102400; //php.iniのpost_max_sizeを超えたデータが送信されていないか確認 if (!checkPostMaxSize()) echo 'ファイルサイズは100MB以下にしてください'; if (isset($_FILES['uploadfile']) { for ($i = 0; $i < count($_FILES['uploadfile']['name']); $i++) { list($result, $ext, $error_msg) = checkFile($i); if ($result) { $name = $_FILES['uploadfile']['name'][$i]; $tmp_name = $_FILES['uploadfile']['tmp_name'][$i]; //"upfile_" + 現在のタイムスタンプ + 連番 + "_" + マイクロ秒 + 元のファイル名 + アクセス元IPアドレスに基づくMD5 + 拡張子 $move_to = $dir . '/upfile_' . time() . $i . '_' . md5(microtime() . $name . $_SERVER['REMOTE_ADDR']) . '.' . $ext; if (!move_upload_file($tmp_name, $move_to)) { $error_msg[] = '画像のアップロードに失敗しました'; } else { echo '<img src="' . $move_to . '>'; } } if (count($error_msg) > 0) { foreach ($error_msg as $msg) { echo $msg; } } } } function checkFile($i) { $error_msg = []; $ext = ''; $size = $_FILES['uploadfile']['size'][$i]; $error = $_FILES['uploadfile']['error'][$i]; $img_type = $_FILES['uploadfile']['type'][$i]; $tmp_name = $_FILES['uploadfile']['tmp_name'][$i]; if ($error != UPLOAD_ERR_OK) { switch ($error) { case UPLOAD_ERR_NO_FILE: //アップロードされなかった場合はエラー処理はなし break; case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: $error_msg = 'ファイルサイズは100KB以下にしてください'; break; default: $error_msg = 'アップロードエラーです'; } return [false, $ext, $error_msg]; } else { switch ($img_type) { case 'image/gif': $ext = 'gif'; break; case 'image/jpeg': case 'image/pjpeg': $ext = 'jpg'; break; case 'image/png': case 'image/x-png': $ext = 'png'; break; } $finfo = new finfo(FILEINFO_MIME_TYPE); $finfo_type = $finfo->file($tmp_name); if ($size == 0) { $error_msg[] = 'ファイルが存在しないか空のファイルです'; } elseif ($size > MAX_SIZE) { $error_msg[] = 'ファイルサイズは100KB以下にしてください'; } elseif ($img_type != $finfo_type) { $error_msg[] = 'MIMEタイプが一致しません'; } elseif ($ext != 'gif' && $ext != 'jpg' && $ext != 'png') { $error_msg[] = 'アップロード可能なファイルはgif, jpg, pngのみです'; } else { return [true, $ext, $error_msg]; } } return [false, $ext, $error_msg]; } //php.iniのpost_max_sizeを超えたデータが送信されていないかチェックする関数 function checkPostMaxSize() { $max_size = ini_get('post_max_size'); //post_max_sizeにMが入っていた場合に整数にする $unit = substr($max_size, -1); switch ($unit) { case 'M': $multiple = 1024 ** 2; //pow(1024, 2); break; case 'K': $multiple = 1024; break; case 'G': $multiple = 1024 ** 3; //pow(1024, 3); break; default: $multiple = 1; } $max_size = substr($max_size, 0, strlen($max_size) - 1) * $multiple; //post_max_sizeを超えたデータがPOSTされたかをチェック if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_SERVER['CONTENT_LENGTH'] > $max_size) { return false; } return true; }こんな感じですかねー
拡張子を判断するにはFileInfo
拡張モジュールを使うこともできます
ファイル内の特定の位置からマジックバイトシーケンスを見つけることでファイルのMIMEタイプを推測しているみたいなので、確実な方法ではないようです。ただしWindows環境のPHPではデフォルトで拡張モジュールが有効になっていないので、
php.iniextension=php_fileinfo.dll #;extension=php_fileinfo.dllのように
php.ini
を変更しApache
を再起動してください補足ですが累乗の計算は
pow()
関数を使うことも**
を使うこともできるみたいですー
- 投稿日:2020-09-27T18:07:18+09:00
[Laravel] プロジェクト開始までの忘備録
概要
Laravel プロジェクトを開始するまでのコマンドをまとめた個人メモ。
前提条件
- MacOS
- Composer, Laravel インストールされている
- Docker がインストールされている
プロジェクトの開始
Version6系を使用し、
tasklist
というプロジェクト名で作成する$ composer create-project --prefer-dist laravel/laravel tasklist ^6.0DB作成
Dockerを使用し、
mysql
コンテナを立ち上げる。$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -d -p 3306:3306 mysql $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4ded8e15debd mysql "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:3306->3306/tcp, 33060/tcp mysqlプロセスで表示された
CONTAINER ID
を使用し、コンテナに接続後DBの作成を行う
mysqlのパスワードはMYSQL_ROOT_PASSWORD
引数で指定した値。$ docker exec -it 4ded8e15debd86c73e637b0c612bf30f0cd9de0aec04b4c09a0f53a6fa16f35d bash root@4ded8e15debd:/# mysql -u root -p -h 127.0.0.1 Enter password: mysql mysql> DATABASE tasklist; mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | tasklist | +--------------------+ 5 rows in set (0.03 sec)DB 接続情報の定義
プロジェクト配下の
.env
ファイルで接続情報を定義する。
接続情報は個々の環境に合わせて変更すること。$ cd tasklist $ vim .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=tasklist DB_USERNAME=root DB_PASSWORD=mysqlDB接続の確認
Webアプリが正常にDB接続できているか確認を行う。
以下のように表示されている場合接続できている。$ php artisan tinker Psy Shell v0.10.4 (PHP 7.4.10 — cli) by Justin Hileman >>> DB::reconnect(); => Illuminate\Database\MySqlConnection {#3233}タイムゾーンの変更
デフォルトではUTCのため、
Asia/Tokyo
に変更する。$ vim config/app.php 'timezone' => 'Asia/Tokyo',アプリの起動
内部サーバを起動させる。
デフォルトで用意されているwelcome.blade.php
のviewが表示される$ php artisan serve --host=127.0.0.1 --port=8080備考
コンテナを停止する際は以下コマンドを実行すること。
$ docker stop { CONTAINER ID }
- 投稿日:2020-09-27T18:07:18+09:00
[Laravel] プロジェクト開始までのコマンド忘備録
概要
Laravel プロジェクトを開始するまでのコマンドをまとめた個人メモ。
前提条件
- MacOS
- Composer, Laravel インストールされている
- Docker がインストールされている
プロジェクトの開始
Version6系を使用し、
tasklist
というプロジェクト名で作成する$ composer create-project --prefer-dist laravel/laravel tasklist ^6.0DB作成
Dockerを使用し、
mysql
コンテナを立ち上げる。$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -d -p 3306:3306 mysql $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4ded8e15debd mysql "docker-entrypoint.s…" 6 days ago Up 6 days 0.0.0.0:3306->3306/tcp, 33060/tcp mysqlプロセスで表示された
CONTAINER ID
を使用し、コンテナに接続後DBの作成を行う
mysqlのパスワードはMYSQL_ROOT_PASSWORD
引数で指定した値。$ docker exec -it 4ded8e15debd86c73e637b0c612bf30f0cd9de0aec04b4c09a0f53a6fa16f35d bash root@4ded8e15debd:/# mysql -u root -p -h 127.0.0.1 Enter password: mysql mysql> CREATE DATABASE tasklist; mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | tasklist | +--------------------+ 5 rows in set (0.03 sec)DB 接続情報の定義
プロジェクト配下の
.env
ファイルで接続情報を定義する。
接続情報は個々の環境に合わせて変更すること。$ cd tasklist $ vim .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=tasklist DB_USERNAME=root DB_PASSWORD=mysqlDB接続の確認
Webアプリが正常にDB接続できているか確認を行う。
以下のように表示されている場合接続できている。$ php artisan tinker Psy Shell v0.10.4 (PHP 7.4.10 — cli) by Justin Hileman >>> DB::reconnect(); => Illuminate\Database\MySqlConnection {#3233}タイムゾーンの変更
デフォルトではUTCのため、
Asia/Tokyo
に変更する。$ vim config/app.php 'timezone' => 'Asia/Tokyo',アプリの起動
内部サーバを起動させる。
デフォルトで用意されているwelcome.blade.php
のviewが表示される$ php artisan serve --host=127.0.0.1 --port=8080備考
コンテナを停止する際は以下コマンドを実行すること。
$ docker stop { CONTAINER ID }
- 投稿日:2020-09-27T17:12:39+09:00
[PHP] selfとstaticの違いメモ
要点
self: 定義時のクラスを指す
static: 実行時のクラスを指す例
class Foo { public function helloGateway() { self::hello(); } public static function hello() { echo __CLASS__ . 'hello' . PHP_EOL; } } class Bar extends Foo { public static function hello() { echo __CLASS__ . 'hello' . PHP_EOL; } } $bar = new Bar(); echo $bar->helloGateway(); //この場合はFoohelloが出力される
- 投稿日:2020-09-27T16:30:17+09:00
勉強メモ5_どこかの記事を元にgatling構築
gatlingを使うため、ApacheとPHP導入
以下↓↓の記事を参考
Apache導入
https://qiita.com/sango/items/b045a1da17606eda6f82
PHP導入
https://qiita.com/sango/items/a08c5b04df7125aaaad3
https://techacademy.jp/magazine/23054---------------Apache導入↓↓------------------------- #ルートに変更 su - root #apacheインストール yum -y install httpd #apache起動 systemctl start httpd #apacheが動いてるかブラウザで確認 http://127.0.0.1/---------------PHP導入↓↓------------------------- #PHPインストール yum -y install php #phpファイル作成するため、移動 cd /var/www/html//var/www/html/にPHPファイルを3つ作成
api1.php<?php // 連想配列用意 $array = [ 'tokyo' => [ '品川', '五反田', '三軒茶屋', '四谷' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>api2.php<?php // 連想配列用意 $array = [ 'kanagawa' => [ '横浜', '相模原', '湘南', '鎌倉' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>api3.php?php // 連想配列用意 $array = [ 'saitama' => [ '所沢', '狭山', '川口', '浦和', '小手指', '飯能' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>PHP実行
#まずはApache再起動 systemctl restart httpd #ブラウザでアクセス(JSON形式で市の名称がでればOK) http://127.0.0.1/api1.php http://127.0.0.1/api2.php http://127.0.0.1/api3.phpgatling 構築
以下の記事を参考に構築
https://medium.com/eureka-engineering/gatring-stress-test-14ac5efdfcbc#rootフォルダ移動 su - root cd /root #gatlingのライブラリーをダウンロード curl -O https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/3.3.1/gatling-charts-highcharts-bundle-3.3.1-bundle.zip #gatlingのライブラリーを解凍 unzip gatling-charts-highcharts-bundle-3.3.1-bundle.zip #javaがインストールされてるか確認(java8や11がないと動かないらしい) java -version #gatlingの実行ソース作成(BasicSimulation.scalaのベースの実行ファイルをコピー) cd gatling-charts-highcharts-bundle-3.3.1/user-files/simulations/ mkdir api cp -p computerdatabase/BasicSimulation.scala api/ApiSimulation.scala上記でコピーした(ApiSimulation.scala)を作成
package api //パッケージ名指定 import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration. _ class ApiSimulation extends Simulation { val httpProtocol = http .baseUrl("http://127.0.0.1")//ベースURL .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") .doNotTrackHeader("1") .acceptLanguageHeader("en-US,en;q=0.5") .acceptEncodingHeader("gzip, deflate") .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0") //7秒おきに /api1.php /api2.php /api3.phpにGETするだけの簡単なテスト val scn = scenario("api") .exec( http("request_1") .get("/api1.php")//作成したPHPファイル指定 ) .pause(7) .exec( http("request_2") .get("/api2.php")//作成したPHPファイル指定 ) .pause(7) .exec( http("request_3") .get("/api3.php")//作成したPHPファイル指定 ) setUp(scn.inject(atOnceUsers(1)).protocols(httpProtocol)) //1Userで並列実行 }gatling実行
#gatling実行シェルファイルに移動 cd /root/gatling-charts-highcharts-bundle-3.3.1/bin/ #gatling実行 ./gatling.sh GATLING_HOME is set to /root/gatling-charts-highcharts-bundle-3.3.1 OpenJDK 64-Bit Server VM warning: If the number of processors is expected to increase from one, then you should configure the number of parallel GC threads appropriately using -XX:ParallelGCThreads=N Choose a simulation number: [0] api.ApiSimulation [1] computerdatabase.BasicSimulation [2] computerdatabase.advanced.AdvancedSimulationStep01 [3] computerdatabase.advanced.AdvancedSimulationStep02 [4] computerdatabase.advanced.AdvancedSimulationStep03 [5] computerdatabase.advanced.AdvancedSimulationStep04 [6] computerdatabase.advanced.AdvancedSimulationStep05 0 ←0を選択する Select run description (optional) api: ←api:って入力する Simulation api.ApiSimulation started... ================================================================================ 2020-09-27 16:20:23 5s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=1 KO=0 ) > request_1 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [--------------------------------------------------------------------------] 0% waiting: 0 / active: 1 / done: 0 ================================================================================ ================================================================================ 2020-09-27 16:20:28 10s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=2 KO=0 ) > request_1 (OK=1 KO=0 ) > request_2 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [--------------------------------------------------------------------------] 0% waiting: 0 / active: 1 / done: 0 ================================================================================ ================================================================================ 2020-09-27 16:20:32 14s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=3 KO=0 ) > request_1 (OK=1 KO=0 ) > request_2 (OK=1 KO=0 ) > request_3 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [##########################################################################]100% waiting: 0 / active: 0 / done: 1 ================================================================================ Simulation api.ApiSimulation completed in 14 seconds Parsing log file(s)... Parsing log file(s) done Generating reports... ================================================================================ ---- Global Information -------------------------------------------------------- > request count 3 (OK=3 KO=0 ) > min response time 4 (OK=4 KO=- ) > max response time 60 (OK=60 KO=- ) > mean response time 26 (OK=26 KO=- ) > std deviation 24 (OK=24 KO=- ) > response time 50th percentile 14 (OK=14 KO=- ) > response time 75th percentile 37 (OK=37 KO=- ) > response time 95th percentile 55 (OK=55 KO=- ) > response time 99th percentile 59 (OK=59 KO=- ) > mean requests/sec 0.2 (OK=0.2 KO=- ) ---- Response Time Distribution ------------------------------------------------ > t < 800 ms 3 (100%) > 800 ms < t < 1200 ms 0 ( 0%) > t > 1200 ms 0 ( 0%) > failed 0 ( 0%) ================================================================================ Reports generated in 0s. Please open the following file: /root/gatling-charts-highcharts-bundle-3.3.1/results/apisimulation-20200927072016312/index.html上記の最後に「file: /root/gatling-charts-highcharts-bundle-3.3.1/results/apisimulation-20200927072016312/index.html」
と記載されているので、ブラウザでアクセスしてみる。
アクセスすると、下記みたいなのが表示されたよ。
一旦、ここまでで終了
- 投稿日:2020-09-27T16:30:17+09:00
勉強メモ5_アップされてる記事を元にgatling構築
gatlingを使うため、ApacheとPHP導入
以下↓↓の記事を参考
Apache導入
https://qiita.com/sango/items/b045a1da17606eda6f82
PHP導入
https://qiita.com/sango/items/a08c5b04df7125aaaad3
https://techacademy.jp/magazine/23054---------------Apache導入↓↓------------------------- #ルートに変更 su - root #apacheインストール yum -y install httpd #apache起動 systemctl start httpd #apacheが動いてるかブラウザで確認 http://127.0.0.1/---------------PHP導入↓↓------------------------- #PHPインストール yum -y install php #phpファイル作成するため、移動 cd /var/www/html//var/www/html/にPHPファイルを3つ作成
api1.php<?php // 連想配列用意 $array = [ 'tokyo' => [ '品川', '五反田', '三軒茶屋', '四谷' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>api2.php<?php // 連想配列用意 $array = [ 'kanagawa' => [ '横浜', '相模原', '湘南', '鎌倉' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>api3.php?php // 連想配列用意 $array = [ 'saitama' => [ '所沢', '狭山', '川口', '浦和', '小手指', '飯能' ] ]; // Origin null is not allowed by Access-Control-Allow-Origin.とかのエラー回避の 為、ヘッダー付与 header("Access-Control-Allow-Origin: *"); #echo json_encode($array); echo json_encode($array,JSON_UNESCAPED_UNICODE); ?>PHP実行
#まずはApache再起動 systemctl restart httpd #ブラウザでアクセス(JSON形式で市の名称がでればOK) http://127.0.0.1/api1.php http://127.0.0.1/api2.php http://127.0.0.1/api3.phpgatling 構築
以下の記事を参考に構築
https://medium.com/eureka-engineering/gatring-stress-test-14ac5efdfcbc#rootフォルダ移動 su - root cd /root #gatlingのライブラリーをダウンロード curl -O https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/3.3.1/gatling-charts-highcharts-bundle-3.3.1-bundle.zip #gatlingのライブラリーを解凍 unzip gatling-charts-highcharts-bundle-3.3.1-bundle.zip #javaがインストールされてるか確認(java8や11がないと動かないらしい) java -version #gatlingの実行ソース作成(BasicSimulation.scalaのベースの実行ファイルをコピー) cd gatling-charts-highcharts-bundle-3.3.1/user-files/simulations/ mkdir api cp -p computerdatabase/BasicSimulation.scala api/ApiSimulation.scala上記でコピーした(ApiSimulation.scala)を作成
package api //パッケージ名指定 import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration. _ class ApiSimulation extends Simulation { val httpProtocol = http .baseUrl("http://127.0.0.1")//ベースURL .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") .doNotTrackHeader("1") .acceptLanguageHeader("en-US,en;q=0.5") .acceptEncodingHeader("gzip, deflate") .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0") //7秒おきに /api1.php /api2.php /api3.phpにGETするだけの簡単なテスト val scn = scenario("api") .exec( http("request_1") .get("/api1.php")//作成したPHPファイル指定 ) .pause(7) .exec( http("request_2") .get("/api2.php")//作成したPHPファイル指定 ) .pause(7) .exec( http("request_3") .get("/api3.php")//作成したPHPファイル指定 ) setUp(scn.inject(atOnceUsers(1)).protocols(httpProtocol)) //1Userで並列実行 }gatling実行
#gatling実行シェルファイルに移動 cd /root/gatling-charts-highcharts-bundle-3.3.1/bin/ #gatling実行 ./gatling.sh GATLING_HOME is set to /root/gatling-charts-highcharts-bundle-3.3.1 OpenJDK 64-Bit Server VM warning: If the number of processors is expected to increase from one, then you should configure the number of parallel GC threads appropriately using -XX:ParallelGCThreads=N Choose a simulation number: [0] api.ApiSimulation [1] computerdatabase.BasicSimulation [2] computerdatabase.advanced.AdvancedSimulationStep01 [3] computerdatabase.advanced.AdvancedSimulationStep02 [4] computerdatabase.advanced.AdvancedSimulationStep03 [5] computerdatabase.advanced.AdvancedSimulationStep04 [6] computerdatabase.advanced.AdvancedSimulationStep05 0 ←0を選択する Select run description (optional) api: ←api:って入力する Simulation api.ApiSimulation started... ================================================================================ 2020-09-27 16:20:23 5s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=1 KO=0 ) > request_1 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [--------------------------------------------------------------------------] 0% waiting: 0 / active: 1 / done: 0 ================================================================================ ================================================================================ 2020-09-27 16:20:28 10s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=2 KO=0 ) > request_1 (OK=1 KO=0 ) > request_2 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [--------------------------------------------------------------------------] 0% waiting: 0 / active: 1 / done: 0 ================================================================================ ================================================================================ 2020-09-27 16:20:32 14s elapsed ---- Requests ------------------------------------------------------------------ > Global (OK=3 KO=0 ) > request_1 (OK=1 KO=0 ) > request_2 (OK=1 KO=0 ) > request_3 (OK=1 KO=0 ) ---- api ----------------------------------------------------------------------- [##########################################################################]100% waiting: 0 / active: 0 / done: 1 ================================================================================ Simulation api.ApiSimulation completed in 14 seconds Parsing log file(s)... Parsing log file(s) done Generating reports... ================================================================================ ---- Global Information -------------------------------------------------------- > request count 3 (OK=3 KO=0 ) > min response time 4 (OK=4 KO=- ) > max response time 60 (OK=60 KO=- ) > mean response time 26 (OK=26 KO=- ) > std deviation 24 (OK=24 KO=- ) > response time 50th percentile 14 (OK=14 KO=- ) > response time 75th percentile 37 (OK=37 KO=- ) > response time 95th percentile 55 (OK=55 KO=- ) > response time 99th percentile 59 (OK=59 KO=- ) > mean requests/sec 0.2 (OK=0.2 KO=- ) ---- Response Time Distribution ------------------------------------------------ > t < 800 ms 3 (100%) > 800 ms < t < 1200 ms 0 ( 0%) > t > 1200 ms 0 ( 0%) > failed 0 ( 0%) ================================================================================ Reports generated in 0s. Please open the following file: /root/gatling-charts-highcharts-bundle-3.3.1/results/apisimulation-20200927072016312/index.html上記の最後に「file: /root/gatling-charts-highcharts-bundle-3.3.1/results/apisimulation-20200927072016312/index.html」
と記載されているので、ブラウザでアクセスしてみる。
アクセスすると、下記みたいなのが表示されたよ。
一旦、ここまでで終了
- 投稿日:2020-09-27T16:25:21+09:00
【PHP】endroid/qr-codeを使ってQRコード画像を出力してみた話
はじめに
タイトルの通りですが、
endroid/qr-codeを使ってQRコード画像を出力してみました。以下で作成した環境を引き続き利用していますので、
今回もコマンドラインから実行となります。本編
環境構築
【PHP】TCPDF + FPDIでPDFを出力してみた話 > Dockerを元に
今回新たに必要となった、gd拡張モジュールの導入設定を追加します。# GD RUN apt-get install -y libjpeg-dev libpng-dev libfreetype6-dev RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \ docker-php-ext-install gd追加後、再度ビルドします。
# docker-compose build --no-cacheComposer
Composerを使って、"endroid/qr-code"をインストールしていきましょう。
# cd /home/work/src/ # composer require endroid/qr-code Using version ^3.9 for endroid/qr-code ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 15 installs, 0 updates, 0 removals - Installing myclabs/php-enum (1.7.6): Downloading (100%) - Installing symfony/polyfill-ctype (v1.18.1): Downloading (100%) - Installing symfony/polyfill-php80 (v1.18.1): Downloading (100%) - Installing symfony/polyfill-mbstring (v1.18.1): Downloading (100%) - Installing symfony/polyfill-intl-normalizer (v1.18.1): Downloading (100%) - Installing symfony/polyfill-intl-grapheme (v1.18.1): Downloading (100%) - Installing symfony/string (v5.1.5): Downloading (100%) - Installing symfony/property-info (v5.1.5): Downloading (100%) - Installing symfony/property-access (v5.1.5): Downloading (100%) - Installing symfony/deprecation-contracts (v2.2.0): Downloading (100%) - Installing symfony/options-resolver (v5.1.5): Downloading (100%) - Installing khanamiryan/qrcode-detector-decoder (1.0.3): Downloading (100%) - Installing dasprid/enum (1.0.2): Downloading (100%) - Installing bacon/bacon-qr-code (2.0.2): Downloading (100%) - Installing endroid/qr-code (3.9.1): Downloading (100%) symfony/polyfill-intl-normalizer suggests installing ext-intl (For best performance) symfony/polyfill-intl-grapheme suggests installing ext-intl (For best performance) symfony/property-info suggests installing psr/cache-implementation (To cache results) symfony/property-info suggests installing symfony/doctrine-bridge (To use Doctrine metadata) symfony/property-info suggests installing phpdocumentor/reflection-docblock (To use the PHPDoc) symfony/property-info suggests installing symfony/serializer (To use Serializer metadata) symfony/property-access suggests installing psr/cache-implementation (To cache access methods.) bacon/bacon-qr-code suggests installing ext-imagick (to generate QR code images) endroid/qr-code suggests installing roave/security-advisories (Avoids installation of package versions with vulnerabilities) endroid/qr-code suggests installing symfony/security-checker (Checks your composer.lock for vulnerabilities) endroid/qr-code suggests installing setasign/fpdf (Required to use the FPDF writer.) Writing lock file Generating autoload files 12 packages you are using are looking for funding. Use the `composer fund` command to find out more!実装
”Google.com”へのリンクをQRコードにしてみます。
ファイル構成
# cd /home/work/ # ls /home/work/output/img/qr/qrYYYYmmdd-HHiiss #PDF出力ファイル # ls /home/work/src/resources/img/png/qr-logo.png #QRコード中心に表示されるロゴマーク # ls /home/work/bin/make-qr.php #コマンドラインで実行するソース # ls /home/work/src/classes/MyQrCode.php # TCPDF利用クラス # ls /home/work/src/classes/QrCode/FontColorWithAlpha.php.php # フォントカラークラス(割愛)ソース
/home/work/bin/make-qr.php<?php require_once __DIR__ . '/../src/bootstrap.php'; use Classes\MyQrCode; use Classes\QrCode\FontColorWithAlpha; // QRコードの画像ファイルを出力 $myQrCode = new MyQrCode('https://www.google.com/'); $myQrCode->setForegroundColor(new FontColorWithAlpha(255, 0, 0)); $myQrCode->setBackgroundColor(new FontColorWithAlpha(255, 255, 0)); $myQrCode->outputQrCodeFile();/home/work/src/classes/MyQrCode.php<?php namespace Classes; use Endroid\QrCode\LabelAlignment; use Endroid\QrCode\QrCode; use Classes\QrCode\FontColorWithAlpha; class MyQrCode { //デフォルト設定 private $size = 300;//QRコードサイズ private $margin = 10;//余白 //バーコードの色(デフォルト:黒) private $foregroundColor = [ 'r' => 0, 'g' => 0, 'b' => 0, 'a' => 0 ]; //背景色(デフォルト:白) private $backgroundColor = [ 'r' => 255, 'g' => 255, 'b' => 255, 'a' => 0 ]; // 文字エンコーディング const ENCODING = 'utf-8'; // ラベル const LABEL_TEXT = 'QRコードを読み込む'; const LABEL_FONT_SIZE = 15; // // 出力ファイル const OUTPUT_FILE_DIR = __DIR__.'/../../output/img/qr/'; const OUTPUT_FILE_NAME = 'qr'; // ロゴ const LOGO_IMG = __DIR__.'/../resources/img/png/qr-logo.png'; const LOGO_SIZE_WIDTH = 50; //幅 const LOGO_SIZE_HEIGHT = 50;//高さ // フォントファイル const FONT_FILE_NAME = __DIR__.'/../resources/font/ipam.ttf'; /** * コンストラクタ */ public function __construct(string $_text) { $this->qrCode = new QrCode($_text); } /** * QRCodeの画像出力前の準備処理 */ private function prepareOutput() { // 可変パラメータ $this->qrCode->setSize($this->size); // QRコードのサイズ $this->qrCode->setMargin($this->margin); //QRコードと画像の余白 $this->qrCode->setForegroundColor($this->foregroundColor);//QRコードの色 $this->qrCode->setBackgroundColor($this->backgroundColor);//背景色 // 固定パラメータ $this->qrCode->setEncoding(self::ENCODING);//文字エンコード //QRコードの下に表示されるラベル $this->qrCode->setLabel( self::LABEL_TEXT, self::LABEL_FONT_SIZE, self::FONT_FILE_NAME, LabelAlignment::CENTER() ); //QRコードの中央に表示されるロゴ //ロゴのファイル $this->qrCode->setLogoPath(self::LOGO_IMG); //ロゴのサイズ $this->qrCode->setLogoSize(self::LOGO_SIZE_WIDTH, self::LOGO_SIZE_HEIGHT); } /** * QRCodeの画像出力 */ public function outputQrCodeFile() { // 出力準備 $this->prepareOutput(); // ファイル出力 $this->qrCode->writeFile($this->getOutputFilename()); } /** * 出力するファイル名を取得する * @return string ファイル名 */ private function getOutputFilename() { return self::OUTPUT_FILE_DIR . self::OUTPUT_FILE_NAME . date("Ymd-His"). '.png'; } /** * * QRコードの色を設定する * @param $_fontColor 文字色クラス */ public function setForegroundColor(FontColorWithAlpha $_fontColor) { $this->foregroundColor = $_fontColor->toArray(); } /** * * 背景色の色を設定する * @param $_fontColor 文字色クラス */ public function setBackgroundColor(FontColorWithAlpha $_fontColor) { $this->backgroundColor = $_fontColor->toArray(); } /** * * QRコードのサイズを設定する * @param $_fontColor 文字色クラス */ public function setSize(int $_size) { $this->size = $_size; } /** * * QRコードの余白を設定する * @param $_fontColor 文字色クラス */ public function setMargin(int $_margin) { $this->margin = $_margin; } }実行コマンド
php /home/work/bin/make-qr.php
実行結果
終わりに
QRコードを作成してくれるサービスは既にいくつもあるかと思いますが、
大量に必要な場合や動的に作成したい場合は、簡単に自前で用意できそうですね。参考
- 投稿日:2020-09-27T16:14:57+09:00
php・可変変数に配列を渡す
環境
初書:2020/09/27
PC:macOS 10.15.7
php 7.4.10前置き
PHPで可変変数を扱う時があったのだが、その時にかなり手こずったのでメモ。
前提
可変変数の知識
コード(失敗例)
index.phpclass cls { public string $str1; public string $str2; public function __construct(){ $this->str1 = "aa"; $this->str2 = "bb"; } } $ouc = new cls();とりあえずクラスを用意して、インスタンスした。
そしてこの後、str変数にアクセスしたいのだが、この時に少し変わったことをする。index.php$arr[] = "str1"; $arr[] = "str2"; $ouc->$arr[0] = "cc";何をしたいかというと、
$arr[0]
= "str1"、つまり$ouc->str1 = "cc";
を行おうとした。
一見問題なく実行できそうだが、実はnoticeが発生する。PHP Notice: Array to string conversion in phpこれは配列を文字列として扱おうとした際に起こるエラーである。
だが、パッと見では特に不具合がある場所はない。エラーの原因
原因自体は単純で、構文の解析方法が思っているのと違うということ。
PHP7以降の構文解析は、$ouc->$arr[0] = "cc"; ¯¯¯¯ // 1 ¯¯¯¯¯¯¯¯¯¯¯¯¯ // 2という順番になる。
つまり、$arr (=Array。配列なので)を先に返し、
$ouc->Array[0]
にcc
を代入しようとしたのである。(そして実際に代入した)
そのため、var_dump($ouc)
を行うと、object(cls)#1 (3) { ["str1"]=> string(2) "aa" ["str2"]=> string(2) "bb" ["Array"]=> array(1) { [0]=> string(2) "cc" } }という形になる。
対処法
可変変数の別の記述方法である
$ouc->{$arr[0]} = "cc";という風に、全体を
{}
で括って正しい順番で解析してもらうか、一旦別の変数に置く。終わりに
このバグに遭遇した場合、(構文解析の順番を知らない場合は)エラー文をみてもこれが原因だということに気付けない上に、エラー箇所より前の時点でログを出しても特に見つからない1ので(もちろん、あとでvar_dumpするとすぐにわかるのだが)、頭の片隅に入れておいてもいいかもしれない。
参考サイト
$arr[0]
単体でechoしても、cls
をvar_dumpしても気付かないし、issetしても…falseになるから気付くかもしれない。自分はエラーに気を取られて戻り値が来ている事に気付いてなかった。 ↩
- 投稿日:2020-09-27T16:10:43+09:00
[ WordPress ] プラグイン Custom Field Suite の関連ポストの情報を、投稿ページに表示させる
WordPress のプラグイン Custom Field Suite には「関連ポスト」というフィールドがあります。今回、記事に投稿者を紐づけたいと思いましたが、WordPress にそもそも備わっている author は以下の理由から避けました。
【避けた理由1】セキュリティ
何も対策せず投稿者を作ると、下記のURLが生成されます。
https://example.com/author/tanaka/
https://example.com/author/sato/authorのユーザー名は、そのままWordPressのログインIDとなります。わざわざ晒したくないところです。
【避けた理由2】メールアドレスが必須
ログインIDとなるということは、大抵のサービスと同様、ユーザーを作る際にメールアドレスが求められます。今回、記事に紐付けたい投稿者は50名を超えていました。
Custom Field Suite の関連ポストの情報を、投稿ページに表示させるソースコード
以上の2つの理由から、まずカスタム投稿タイプの機能を使って「投稿者」を作り、投稿者名や所属、略歴などを入力するようにしました。そして、その投稿者の情報を、投稿に紐づけるため、 Custom Field Suite で「関連ポスト」のフィールドを投稿に用意し、投稿者を紐付けました。
その関連ポストの情報を投稿に表示させるソースコードが下記です。特に get_post_meta や wp_get_attachment_image が工夫した点です。
<?php $values = CFS()->get('kanrenpost'); foreach ($values as $post_id): setup_postdata( $post_id ); $rslink = get_permalink($post_id); $rstitle = get_the_title($post_id); $rsoriginalname = get_post_meta($post_id,'kanrenpost__originalname',true); $rsoriginalimage = wp_get_attachment_image(get_post_meta($post_id,'kanrenpost__originalimage',true)); ?> <a href="<?php echo $rslink ?>"> <?php echo $rstitle ?> <?php echo $rsoriginalname ?> <?php echo $rsoriginalimage ?> </a> <?php endforeach; ?>参考URL
WordPress:Custom Field Suiteの使い方からカスタマイズまで | 新宿のホームページ制作会社 ITTI(イッティ)
関数リファレンス/get post meta - WordPress Codex 日本語版
- 投稿日:2020-09-27T11:16:41+09:00
【Laravel】Collectionのrejectメソッド
はじめに
今回もLaravelのCollectionのメソッドについてまとめたいと思います。
rejectメソッドで条件に合わないものを除外してデータ取得する
Collectionの中から条件に合わないものを除外してデータ取得することができます。
$collection = collect([ ['id' => 1, 'name' => '山田', 'age' => 18], ['id' => 2, 'name' => '佐藤', 'age' => 38], ['id' => 3, 'name' => '小林', 'age' => 25], ['id' => 4, 'name' => '鈴木', 'age' => 34], ['id' => 5, 'name' => '田中', 'age' => 29], ]); $filtered = $collection->reject(function($values, $key){ return ($values['age'] < 30); }); print_r($filtered->toArray());実行結果は下記になります。
Array ( [1] => Array ( [id] => 2 [name] => 佐藤 [age] => 38 ) [2] => Array ( [id] => 4 [name] => 鈴木 [age] => 34 ) )※条件式がtrueになるものは除外さます。
おわりに
いかがでしたでしょうか。
rejectメソッドで条件に当てはまるものを除外できるので試してみてください。
- 投稿日:2020-09-27T11:07:27+09:00
PHP Laravel 6 画像のアップロード
前提条件
画像ファイルの保存先 =
storage/app/public
データベースにはファイル名
のみを保存。
ファイル名にはランダムな名前
が自動で割り当てられる。フォームの作成
フォームタグの記述
formタグに
enctype='multipart/form-data'
を記述しないとエラーを吐きます。
これを記述することで複数類(jpeg、png等)のデータ形式を扱う事でできるようになります。(jpeg、png等)create.blade.php<form method="POST" action="{{ route('recommends.store') }}" enctype='multipart/form-data'> @csrf </form>コントローラーの設定
imgがない場合に
$path = $request->file('img')->store('public/img');
でエラーを吐くためif文で分岐させました。recommendMovies/app/Http/Controllers/RecommendMovieController.phppublic function store(Request $request) { if ($request->img === null) { RecommendMovie::create([ 'name' => $request->name, 'description' => $request->description, 'impression' => $request->impression, ]); }else { $path = $request->file('img')->store('public/img'); RecommendMovie::create([ 'name' => $request->name, 'description' => $request->description, 'impression' => $request->impression, 'img' => basename($path) ]); } return redirect('/recommends')->with('message', '作成しました'); }viewの設定
シンボリックリンクを使用します。
php artisan storage:linkstorage\imgが作成され、この中にアップロードした画像が保存されます。
以下の記述でブラウザに表示させることができます。
recommendMovies/app/Http/Controllers/index.php<img src="{{ asset('/storage/img/'.$recommend->img) }}">
- 投稿日:2020-09-27T09:53:47+09:00
php復習、オブジェクト指向編(個人学習備忘録)
はじめに
筆者はずぶずぶの素人です。少しlaravelが書けるレベルです。
書けているけど僕自身がフレームワークに使われています。
laravelを使っていく打ちにもっとlaravelを理解したいと思いました。
フレームワークの本質を理解するにはphpを基礎力を向上させるほかないと考えphpの復習+脱初心者を目指します。class
オブジェクト指向の構成要素の1つ。classはオブジェクトの設計書のようなもの
userの名前をコンストラクトし、user名を出力する簡単なクラスを作成class Animal { p $name; //$user_nameの初期化 function __construct($name) { $this->name = $name; } //getterの役割 public function getName() { return $this->name; } } $nnimal = new Animal('dog'); echo $user->getName(); ------------------------- 結果 dog継承を行う。フレームワークにおいても、Contorollerなどのclassをextendして使用する。
class Dog extends Animal { public function call() { echo 'wan!'; } } $dog = new Dog('wanchan'); echo $dog->getName(); echo '<bar>'; echo $dog->call(); ------------------------------- 結果 wanchan<bar>wan!
アクセス修飾子
public
どこからでもアクセス可能
private
どこからでもアクセス可能です。アクセス修飾子がない場合は、publicを指定したものと同じになります。
protected
そのクラス自身と継承クラスからアクセス可能です。つまり非公開ですが、継承は可能となります。
ポリモーフィズム
ポリモーフィズムとは同名のメソッドで異なる挙動を実現することをいいます。ポリモーフィズムのメソッドは、同じ目的の機能に同名のメソッドを割り当てる ことができるということです。
同名のメソッドで異なる動きを実現することを言います。<?php class Human { protected $name; protected $age; function __construct($name, $age) { $this->name = $name; $this->age = $age; } protected function getInfo() {} } class HumanInfo extends Human { //オーバーライド public function getInfo() { echo $this->name.'は年齢が'.$this->age.'です'; } } class Feature extends Human { protected $job; function __construct($name, $age, $job) { $this->name = $name; $this->age = $age; $this->job = $job; } //オーバーライド public function getInfo() { echo $this->name.'は年齢が'.$this->age.'です'.'職業は'.$this->job.'です'; } } $user_info = new HumanInfo('たく', 29); $user_info->getInfo(); $user_feature = new Feature('まみ', 30, '料理人'); $user_feature->getInfo(); ------------------------------ 結果: たくは年齢が29です まみは年齢が30です職業は料理人ですポリモーフィズムのメリットは、同じ目的の機能に同じ名前のメソッドを指定できる点で、関連するクラスを利用する場合に、異なるメソッド名を覚える必要がなくなります。
但し、この例のように単なる継承とオーバーライドだけでは、それぞれのサブクラスが同名のメソッドを持つことが保証されないので、「抽象メソッド」という仕組みを利用する必要があります。共通のメソッドを持たせる点であるがそもそも強制力はない。なのでabstractを用いて、表現をする。
抽象class
abstruct(抽象)をclassの前につけることで、抽象クラスを表現でき、またメソッドにもabstructを付与すると定義しているメソッドを使用を強制する。
<?php //抽象クラス//設定するメソッドを強制できる。 abstract class Human { protected $name; protected $age; function __construct($name, $age) { $this->name = $name; $this->age = $age; } //抽象メソッドになる。abstractを定義するとメソッド内容はかけない。 abstract protected function getInfo(); } class HumanInfo extends Human { //オーバーライド // public function getInfo() { // echo $this->name.'は年齢が'.$this->age.'です'; // } } -------------------------------------------------------- Class HumanInfo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods コメントするとメソッド入れなさいと怒られちゃいました。そもそも柔軟にメソッドだけ、強制的に共通的に使用したい場合はinterfaceを使用する。
Interface
PHP では1つのサブクラスが同時に複数のスーパークラスを継承すること(多重継承)ができません。
これはポリモーフィズムを実現したい場合、すべての機能(メソッド)をひとつの抽象クラスに含めなければいけないことを意味し、サブクラスがスーパークラスの特定の機能(メソッド)を必要としない場合でも、それらの機能をオーバーライドしなければならなくなるためコードが冗長になってしまいます。
PHP ではこのような場合、インターフェースを利用します。このinterfaceを使用することで共通のメソッドを共有できる様になります。メソッドはそのままに振る舞いは意図して変化できる。
interfaceをするにはinterfaceの宣言とクラスにimplementsを付与すること。
またinterfaceの利点は複数のinterfaceを使用できること。<?php interface HumanInterface { function getInfo(); } interface JobInterface{ function setJob($params); } //抽象クラス//設定するメソッドを強制できる。 abstract class Human { protected $name; protected $age; function __construct($name, $age) { $this->name = $name; $this->age = $age; } } class HumanInfo extends Human implements HumanInterface { //オーバーライド public function getInfo() { echo $this->name.'は年齢が'.$this->age.'です'; } } class Feature extends Human implements HumanInterface, JobInterface { protected $job = []; function __construct($name, $age) { $this->name = $name; $this->age = $age; } public function getInfo() { echo $this->name.'は年齢が'.$this->age.'です'.'職業は'.$this->job.'です'; } public function setJob($params) { return $this->job = $params; } } $user_info = new HumanInfo('たく', 29); $user_info->getInfo(); $user_feature = new Feature('まみ', 30); $user_feature->setJob('料理人'); var_dump($user_feature->job); $user_feature->getInfo(); ?> ________________________________________ 結果 たくは年齢が29です string(9) "料理人" まみは年齢が30です職業は料理人です2つのインターフェイスがあり、implementsを定義することで、インターフェイスを使用できる。
もちろんimplementesで定義した、インターフェイスのメソッドを使用しないとエラーになる。
トレイト
コードの再利用性を高めるための機能
定義方法はtraitを宣言し、メソッドを記述していく。
classなどに組み込む際にはuseで宣言する。
traitは任意のクラスの機能(メソッド)を拡張できる仕組。そのため、traitに定義するメソッドは複数のクラスで利用されることを想定して、できるだけ汎用性が高い処理を定義するべきです。複数のクラスで同様の処理を行うメソッドがある場合は、そのメソッドをtraitに移して各クラスではそのtraitをuseするといったリファクタリングを行うことがベター。
trait NewsTrait { public function getNews() { echo 'ニュース'; } } trait ProductTrait { public function getProduct() { echo 'プロダクト'; } } class Product { //作成したtraitをuseして使用できる。 use ProductTrait; use NewsTrait; public function getInfo() { echo 'クラスです'; } } $product = new Product(); $product->getInfo(); $product->getProduct(); $product->getNews(); --------------------------------- 結果 クラスです プロダクト ニュース
ここからは番外編
型宣言 (タイプヒンティング)
コーディングをより堅牢なものとする!
関数の引数と戻り値に対して定義します → php7.4からは変数宣言にも使用できるようになったそうです。
メリット
本来期待している型と異なる型が引数に格納されることで起こる、思わぬバグを防げる
コードが読みやすくなる。
型の分類
スカラー型
複合型
特殊型
スカラー型
論理値 (boolean)、整数 (integer)、浮動小数点数 (float, double も同じ)
文字列 (string)function addNuber(int $num1, int $num2) { echo $num1 + $num2; } addNuber(1,2); function isArray(array $array) { if(is_array($array)) { echo 'これはarray'; } } $array = [1,2,3]; isArray($array);戻り値の型宣言もできます。
//戻り値の型宣言。 function sum($num1, $num2):int { return $num1 + $num2; } $a = sum(1,2); echo $a;まとめ
laravelを使っていると目にする内容が多いことに気付きました。
これらを理解して、フレームワークをより理解して行きたいです。
- 投稿日:2020-09-27T03:17:49+09:00
【PHP】TCPDF + FPDIでPDFを出力してみた話
はじめに
タイトルの通りですが
TCPDFとFPDIのライブラリを使用して、PDFを出力してみました。
今回はコマンドラインから実行してみます。本編
環境構築
お好みでPHP、Composerが使用できる環境を用意してください。
Docker
今回私が使用したDocker環境は以下です(一部割愛)。
./docker-compose.ymlversion: '3' services: php: build: ./php volumes: - ./work:/home/work./php/dockerfileFROM php:7.3-fpm COPY php.ini /usr/local/etc/php/ # Composer RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ && php -r "if (hash_file('sha384', 'composer-setup.php') === '795f976fe0ebd8b75f26a6dd68f78fd3453ce79f32ecb33e7fd087d39bfeb978342fb73ac986cd4f54edd0dc902601dc') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ && php composer-setup.php \ && php -r "unlink('composer-setup.php');" \ && mv composer.phar /usr/local/bin/composer./php/php.inidate.timezone = "Asia/Tokyo"Composer
立ち上げが完了したらphpのコンテナへ入り、
Composerでライブラリのインストールを行います。また、ライブラリの他にautoloadの記載も追加しています。
/home/work/src/composer.json{ "require": { "setasign/fpdi": "^2.3", "tecnickcom/tcpdf": "^6.3" }, "autoload": { "psr-4": { "Classes\\" : "classes/" } } }インストールが終われば完了です。
# cd /home/work/src # composer install実装
ファイル構成
# cd /home/work/ # ls /home/work/output/pdf/fileYYYYmmdd-HHiiss #PDF出力ファイル # ls /home/work/src/resources/font/ipam.ttf #フォントの格納先 # ls /home/work/src/resources/pdf/templete.pdf #PDFテンプレートの格納先 # ls /home/work/bin/make-pdf.php #コマンドラインで実行するソース # ls /home/work/src/bootstrap.php # 起動用ファイル(割愛) # ls /home/work/src/classes/MyTcpdf.php # TCPDF利用クラス # ls /home/work/src/classes/common/DynamicProperty.php # 動的クラス(割愛) # ls /home/work/src/classes/MyTcpdf/FontColor.php # フォントカラークラス(割愛)テンプレート
至ってシンプルなPDFテンプレートファイルです。
ソース
/home/work/bin/make-pdf.php<?php require_once __DIR__ . '/../src/bootstrap.php'; use Classes\MyTcpdf; use Classes\MyTcpdf\FontColor; // テンプレートを使用せずに出力 $myTcpdf = new MyTcpdf(); $myTcpdf->addPage(); $myTcpdf->setText('テンプレートを使用せずに出力', 10, 20, new FontColor(0, 255, 0)); $myTcpdf->outputPdfFile(); // テンプレートを使用して出力 $myTcpdfTmp = new MyTcpdf(); $myTcpdfTmp->addPageWithTemplete(1); $myTcpdfTmp->setText('テンプレートを使用して出力', 10, 20, new FontColor(255, 0, 0)); $myTcpdfTmp->outputPdfFile();/home/work/src/classes/MyTcpdf.php<?php namespace Classes; use TCPDF_FONTS; use setasign\Fpdi\Tcpdf\Fpdi; use Classes\MyTcpdf\FontColor; class MyTcpdf { // 出力ファイル const OUTPUT_FORMAT_CHAR = 'F'; const OUTPUT_FILE_DIR = __DIR__.'/../../output/pdf/'; const OUTPUT_FILE_NAME = 'file'; // PDF const PDF_ORIENTATION = 'P'; const PDF_UNIT = 'mm'; const PDF_SIZE = 'A4'; const PDF_TEMPLETE = __DIR__.'/../resources/pdf/templete.pdf'; // フォントファイル const FONT_FILE_NAME = __DIR__.'/../resources/font/ipam.ttf'; /** * コンストラクタ */ public function __construct() { $this->fpdi = new Fpdi(self::PDF_ORIENTATION, self::PDF_UNIT, self::PDF_SIZE); $this->fpdi->SetMargins(0, 0, 0); $this->fpdi->SetFont($this->getFont(), '', 32); } /** * 出力文字を設定する * @param _text 文字列 * @param _x X座標 * @param _y Y座標 * @param _h 高さ */ public function setText(string $_text, int $_x, int $_y, FontColor $_fontColor, int $_h = 0) { $this->fpdi->SetTextColor($_fontColor->R, $_fontColor->G, $_fontColor->B); $this->fpdi->SetXY($_x, $_y); $this->fpdi->Write($_h, $_text); } /** * 追加するフォントを取得する */ private function getFont() { return TCPDF_FONTS::addTTFfont(self::FONT_FILE_NAME); } /** * 出力するファイル名を取得する * @return string ファイル名 */ private function getOutputFilename() { return self::OUTPUT_FILE_DIR . self::OUTPUT_FILE_NAME . date("Ymd-His"). '.pdf'; } /** * ページを追加する */ public function addPage() { $this->fpdi->AddPage(); } /** * テンプレートベースのページを追加する */ public function addPageWithTemplete($_pageNum) { $this->fpdi->SetSourceFile(self::PDF_TEMPLETE); $page = $this->fpdi->importPage($_pageNum); $this->fpdi->AddPage(); $this->fpdi->useTemplate($page, null, null, null, null, true); } /** * PDFファイル出力 */ public function outputPdfFile() { $this->fpdi->Output( $this->getOutputFilename(), self::OUTPUT_FORMAT_CHAR ); } }実行コマンド
php /home/work/bin/make-pdf.php
実行結果
以下のようにファイルが出力された。
テンプレートなし テンプレートあり 終わりに
PDFのテンプレートの上に文字を出力できるので、フォーマットを変えずに必要な情報だけを出力できますね。
勤務表や請求書・領収書など、特定の部分だけが変動するファイルに活用できそう。
- 投稿日:2020-09-27T03:17:49+09:00
PHPのTCPDF + FPDIでPDFを出力してみた話
はじめに
タイトルの通りですが
TCPDFとFPDIのライブラリを使用して、PDFを出力してみました。
今回はコマンドラインから実行してみます。本編
環境構築
お好みでPHP、Composerが使用できる環境を用意してください。
Docker
今回私が使用したDocker環境は以下です(一部割愛)。
./docker-compose.ymlversion: '3' services: php: build: ./php volumes: - ./work:/home/work./php/dockerfileFROM php:7.3-fpm COPY php.ini /usr/local/etc/php/ # Composer RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ && php -r "if (hash_file('sha384', 'composer-setup.php') === '795f976fe0ebd8b75f26a6dd68f78fd3453ce79f32ecb33e7fd087d39bfeb978342fb73ac986cd4f54edd0dc902601dc') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ && php composer-setup.php \ && php -r "unlink('composer-setup.php');" \ && mv composer.phar /usr/local/bin/composer./php/php.inidate.timezone = "Asia/Tokyo"Composer
立ち上げが完了したらphpのコンテナへ入り、
Composerでライブラリのインストールを行います。また、ライブラリの他にautoloadの記載も追加しています。
/home/work/src/composer.json{ "require": { "setasign/fpdi": "^2.3", "tecnickcom/tcpdf": "^6.3" }, "autoload": { "psr-4": { "Classes\\" : "classes/" } } }インストールが終われば完了です。
# cd /home/work/src # composer install実装
ファイル構成
# cd /home/work/ # ls /home/work/output/pdf/fileYYYYmmdd-HHiiss #PDF出力ファイル # ls /home/work/src/resources/font/ipam.ttf #フォントの格納先 # ls /home/work/src/resources/pdf/templete.pdf #PDFテンプレートの格納先 # ls /home/work/bin/make-pdf.php #コマンドラインで実行するソース # ls /home/work/src/bootstrap.php # 起動用ファイル(割愛) # ls /home/work/src/classes/MyTcpdf.php # TCPDF利用クラス # ls /home/work/src/classes/common/DynamicProperty.php # 動的クラス(割愛) # ls /home/work/src/classes/MyTcpdf/FontColor.php # フォントカラークラス(割愛)テンプレート
至ってシンプルなPDFテンプレートファイルです。
ソース
/home/work/bin/make-pdf.php<?php require_once __DIR__ . '/../src/bootstrap.php'; use Classes\MyTcpdf; use Classes\MyTcpdf\FontColor; // テンプレートを使用せずに出力 $myTcpdf = new MyTcpdf(); $myTcpdf->addPage(); $myTcpdf->setText('テンプレートを使用せずに出力', 10, 20, new FontColor(0, 255, 0)); $myTcpdf->outputPdfFile(); // テンプレートを使用して出力 $myTcpdfTmp = new MyTcpdf(); $myTcpdfTmp->addPageWithTemplete(1); $myTcpdfTmp->setText('テンプレートを使用して出力', 10, 20, new FontColor(255, 0, 0)); $myTcpdfTmp->outputPdfFile();/home/work/src/classes/MyTcpdf.php<?php namespace Classes; use TCPDF_FONTS; use setasign\Fpdi\Tcpdf\Fpdi; use Classes\MyTcpdf\FontColor; class MyTcpdf { // 出力ファイル const OUTPUT_FORMAT_CHAR = 'F'; const OUTPUT_FILE_DIR = __DIR__.'/../../output/pdf/'; const OUTPUT_FILE_NAME = 'file'; // PDF const PDF_ORIENTATION = 'P'; const PDF_UNIT = 'mm'; const PDF_SIZE = 'A4'; const PDF_TEMPLETE = __DIR__.'/../resources/pdf/templete.pdf'; // フォントファイル const FONT_FILE_NAME = __DIR__.'/../resources/font/ipam.ttf'; /** * コンストラクタ */ public function __construct() { $this->fpdi = new Fpdi(self::PDF_ORIENTATION, self::PDF_UNIT, self::PDF_SIZE); $this->fpdi->SetMargins(0, 0, 0); $this->fpdi->SetFont($this->getFont(), '', 32); } /** * 出力文字を設定する * @param _text 文字列 * @param _x X座標 * @param _y Y座標 * @param _h 高さ */ public function setText(string $_text, int $_x, int $_y, FontColor $_fontColor, int $_h = 0) { $this->fpdi->SetTextColor($_fontColor->R, $_fontColor->G, $_fontColor->B); $this->fpdi->SetXY($_x, $_y); $this->fpdi->Write($_h, $_text); } /** * 追加するフォントを取得する */ private function getFont() { return TCPDF_FONTS::addTTFfont(self::FONT_FILE_NAME); } /** * 出力するファイル名を取得する * @return string ファイル名 */ private function getOutputFilename() { return self::OUTPUT_FILE_DIR . self::OUTPUT_FILE_NAME . date("Ymd-His"). '.pdf'; } /** * ページを追加する */ public function addPage() { $this->fpdi->AddPage(); } /** * テンプレートベースのページを追加する */ public function addPageWithTemplete($_pageNum) { $this->fpdi->SetSourceFile(self::PDF_TEMPLETE); $page = $this->fpdi->importPage($_pageNum); $this->fpdi->AddPage(); $this->fpdi->useTemplate($page, null, null, null, null, true); } /** * PDFファイル出力 */ public function outputPdfFile() { $this->fpdi->Output( $this->getOutputFilename(), self::OUTPUT_FORMAT_CHAR ); } }実行コマンド
php /home/work/bin/make-pdf.php
実行結果
以下のようにファイルが出力された。
テンプレートなし テンプレートあり 終わりに
PDFのテンプレートの上に文字を出力できるので、フォーマットを変えずに必要な情報だけを出力できますね。
勤務表や請求書・領収書など、特定の部分だけが変動するファイルに活用できそう。
- 投稿日:2020-09-27T01:16:55+09:00
PHPUnit assertEquals アサーションについて
目的
- PHPUnitのアサーションの1つであるassertEqualsについてかんたんにまとめる
すみません
- 本記事は筆者の備忘録的要素が強いためメモ的な簡易的な記事となる。
- 内容が薄い場合は参考文献先などを参考に各自て補填していただければ幸いである。
assertEquals
- 検査対象の中に指定された文字列が含まれているかをチェックするアサーションである。
具体例を下記に記載する。
assertEquals('A', 'B');上記のようにテストコードが記載された場合、指定された文字列がAで検査対象がBとなる。
先の説明でAが指定文字列、Bが検査対象としたがAとBが一致しているかを検査するアサーションなので実際には順不同である。
もう少し具体的な例を下記に記載する。
$str
には「hello」の文字列が格納されている。$str = 'hello';
$str
に「hello」という文字列が格納されているかをassertEqualsのアサーションを使ってテストする際は下記のようになる。assertEquals('hello', $str);上記のテストコードを実行すると「テストOK」となる。
まとめ
- assertEqualsアサーションは「指定文字列と検査対象が一致しているか」をチェックするものである。
- ※文字列以外でもチェック可能である。(コメントで教えていただきました!ありがとうございます!)
参考文献