独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【PHP】PHP7 技術者認定試験問題集で学習したこと

PHPを使った開発は個人的に色々とやっていましたが、仕事でPHPを人に教える機会ができたので、体系的にPHPを理解する&問題を作成する参考にするという目的でPHP7 技術者認定[初級]試験問題集を購入しました。
資格を取得する気は今のところありませんが、色々と学べることあって内容には満足です。
他のプログラミング言語でも共通で使える知識(ロジックやオブジェクト指向など)は、初見でもほぼ解くことができました。
また、開発する際によく利用するDB周りの処理も割と解けた。
ただ、ファイルの操作など、開発であまり触れてこなかった部分についてはぼろぼろ。
満遍なくいろんな処理を盛り込んだシステムを開発しないと、体系的に知識を得るのは難しいと実感。
普段から開発している人で、自分があまり触れてこなかった分野の知識を保管する意味でも勉強になります。
ファイル操作やスクレイピングなども、勉強のために今後色々作ってみたいと思う。


以下、本を読んで新しく知った知識、理解が深まった箇所の箇条書き。

  • PHPでは言語のキーワードと関数名では大文字と小文字を区別しない(変数名はする)
  • ホワイトスペース:半角スペース、タブ、改行のような目に見えない文字
  • ucwords()関数:文字列中の単語の最初の文字を大文字に変換する
  • strcasecmp()関数:大文字と小文字を区別せず、文字列が一致する場合は0を返す
  • 文字列と数値を比較する場合、文字列の先頭を数値に変換して比較する。「5member」と「44」を比較した場合、5と44が比較されることになり、結果として44が大きいと判断されることになる。
  • strcmp()関数は、文字列をアルファベット順で比較する。数値の場合、文字列に変換し一番目の文字をアルファベット順で比較する。一番目の引数が二番目の引数より大きい場合は正の数、小さい場合は負の数、等しい場合は0を返す。
  • 宇宙演算子(<=>)は、strcmp()関数と同様の比較ができる。左が右より小さい場合は-1、大きい場合は1、等しい場合は0を返す。
  • 配列の値を別の配列にコピーした場合、最初は値をさすが、どちらかの値が変更されたタイミングで、メモリを確保してコピーを作成している。これによりメモリを節約している。
  • foreach文では、ループされる要素の順番は値が格納された順番になる。添字の順番にならないので注意が必要。
  • asort()関数:配列の値を基準に並び替える。キーと値の組み合わせは保持される。
  • implode()関数:配列から文字列を作成する。
  • explode()関数:文字列から配列を作成する。
  • strip_tags()関数:文字列からHTMLタグを取り除く。
  • htmlentities()関数:特殊なHTML文字をエンコードする。
  • $_SERVERグローバル配列:Webサーバーや現在のリクエストに関する情報を保持している。
  • $_SERVER['PHP_SELF']:formタグのアクション属性で指定されたWebサーバーに要求するURLのパス名を表す。
  • $_SERVER['REMOTE_ADDR']:WebページにアクセスしているユーザーのIPアドレスを示す。
  • ??(null合体演算子):サブミットされたデータに値が入力されていなかった際にPHPからの警告が表示されないようにする。$_POST['id'] ?? '' とすると、値が入力されていればその値を表示し、それ以外はから文字となる。PHP7から導入され、それ以前ではisset関数を利用する。
  • filter_input()関数:サブミットされた値が整数か浮動小数点かを判定することができる。
  • PDOのエラーモードは、エラー、警告、サイレントの3種類があり、デフォルトはサイレントモード
  • setAttributeメソッドでPDOのモードを色々と変更できる。
  • PDOのフェッチスタイル:BOTH, NUM, ASSOC, OBJの4種類がある。
  • file_get_contents(ファイル名):ファイルの内容を全て読み込む。
  • file_put_contents(ファイル名, 文字列):文字列をファイルに書き込む。
  • スクリーンスクレイピング:他のWebサイトの画面情報を切り出し、画面の内容から情報を抽出して利用すること。file_get_contents関数で、外部サイトのURLを取得することで、Webページを読み込み、そのWebサイトの情報を利用することができる。
  • fopen(ファイル名, ファイルのモード):ファイルへの接続をオープンしてファイルポインタを取得する。ファイルのモードを指定する必要がある。rb:読み込み、rb+:読み込み書き出し、などなど。先頭に追加するか、末尾に追加するか、ファイルが存在しない場合にどうするかなどで8種類のモードがある。
  • fgets()関数:ファイルから1行を読み込む。
  • feof()関数:ファイルポインタがファイルの終端にあるかどうかを判定する。
  • fgetcsv()関数:CSVファイルの行を読み込む関数。
  • クライアントに送信するファイルの種類を知らせるにはheader()関数を利用する。
  • is_readable()関数:ファイルが読み込み可能かどうか判定する。
  • is_writable()関数:ファイルが書き込み可能かどうか判定する。
  • realpath()関数:ファイル名の相対パス絶対パスに変換する。ディレクトリトラバーサル脆弱性を防ぐ際に使用される。
  • アップロードされたファイルの情報は$_FILE配列で確認できる。
  • unmask()関数;新しく作成するファイルのパーミッションを設定することができる。
  • setcookie()関数:クッキーの設定ができる。引数がかなり多い。。タイムアウトの時間を初め、セキュアの設定など、6つくらい設定できる。
  • セッションが利用するクッキーの値はPHPSESSID
  • cURLPHP拡張機能。HTTPリクエストとレスポンスを柔軟に利用できる。
  • curl_exec()関数、curl_init()関数、http_build_query()関数、curl_getinfo関数、curl_setopt関数など。
  • ブラウザにエラーを表示するかはdispllay_errors構成ディレクティブ、エラーログに出力するかはlog_errorsl構成ディレクティブ、どのエラーを取り扱うかはerror_reporting構成ディレクティブで設定する。
  • phpdbgデバッガ:PHP5.6以降に付随しているPHP付属のデバッガ。
  • Webサーバーの稼働:php -S Webサーバー名:ポート番号
  • PHP REPL:対話的にPHPを実行できる。php -a で実行可能。

Herokuで構築したシステムをCentOS8環境に移行した時の作業ログ

無料で使えるPaasのサービスであるHerokuにPHPを使ったシステムを構築していましたが、さくらのVPSを契約してCentOS8環境に移行したので、その時の手順をまとめます。

移行前の環境

  • サーバー環境:Heroku
  • DB:PostgreSQL(Heroku Postgres)
  • 言語:PHP

概要
システムの概要は本筋と関係ないので省略。
PHPで作成したWebアプリケーションをHerokuのフリープランの中でデプロイして稼働。
DBはHerokuのサービスの中で無料で使用することができるHeroku Postgresを使用。

移行後の環境

移行しようと思った背景

  • 稼働時間の解決
    Herokuのフリープランだと、初回アクセスに時間がかかる。
    初回アクセスから30分は通常通りに動作するが、また30分たつと初回アクセスが遅くなる。
    dynoというHerokuのコンテナが、起動するときに多少時間がかかるのだが、フリープランだと、基本的にはdynoは起動しておらず、アクセスされて初めて起動。
    その後アクセスし続ければ遅くなることはないが、30分アクセスがなければ自動でまた停止してしまう。
    解決するためには定期的にcurlコマンドなどでリクエストを投げれば解決できる。
    cronとかタスクスケジュールでローカルのPC定期的から定期的にリクエスト投げれば良いが、ローカルのPCの電源が付いている必要がある。
    また、Herokuのフリープランではトータル稼働時間に制限がある。(確か600時間くらい)
    クレジットカードを登録することで無料のままでもトータル稼働時間は伸ばせる。 ただ、1つのシステムだけ稼働していれば十分対応できるが、システムを2つ以上稼働させようと思うとフリープランでは厳しい。

  • ドメインの取得
    自分でサーバーを構築、ドメインの取得をすれば、サブドメインとバーチャルホストの機能を使うことで、1つのサーバーで複数のシステムを構築することができる。

  • DBの統一
    HerokuだとアプリケーションごとにDBを作成する必要があるし、レコード件数に縛りがある。
    自分でサーバー構築したら、複数のシステムがあってもDBを1つにできるしレコード数の制限もない。

  • httpsにしたかった
    個人情報の入力やログイン処理などがあるが、Herokuではスキームはhttpになっている。
    Herokuでもhttpsにすることはできるみたいだが、聞いた話少々面倒らしいので、自分でサーバーを構築した方が手っ取り早そうだった。

  • Linux上で色々遊ぶことができる
    CentOS8上で構築できるので、システムの動作環境以外でも色々Linux絡みの勉強やお試しができる。

移行時に注意するべきこと

  • DB環境
    Herokuの環境はPostgreSQLで、CentOSMySQLなので、DBの違いに注意。
    CentOSでもPostgreSQLをインストールすることもできるが、さくらのVPSだと起動時のスクリプトLAMPのインストールがある。
    PHPのシステムなのでLAMPで楽したかったのでMySQLにした。
    ただ、ローカルの開発環境はXAMPPとかMAMPを使ってたので、MySQLでもPostgreSQLでも適応できるSQLで開発してたので問題なし。

いざ実行

前置きはこれくらいにして実際の作業手順に付いて。

1. Herokuをメンテナンスモードに

まずは移行元となっているアプリケーションを使用できない状態にする必要がある。
Herokuにはメンテナンスモードというのが用意されており、コマンドでサクッと切り替えられたので、それを使用。

heroku maintenance:on --app アプリ名

これでメンテナンスモードになり、システムが使用不可になる。

2. DBのバックアップの取得

続いてはheroku postgresからDBのバックアップの取得をする。
まずはconfigの情報からデータベースの情報を取得。

heroku config:get DATABASE_URL  --app アプリ名

結果、こんな感じのが返ってくる。

postgres://pxxsmjadcejqnv:8a68cb4f3e311442d688439ea25d780f7e580d0263da70afe65181f20c1a705b@ec2-174-129-255-15.compute-1.amazonaws.com:5432/dd2fk9n2pggjn7

これは

postgres://ユーザー名:パスワード@ホスト名:ポート/DB名

となっている。

それを踏まえてpg_dumpコマンドを使ってバックアップを取得する。
※ローカル環境にpostgresqlがインストールされている前提です。じゃないとpg_dumpコマンドが使えない。
Heroku上でもDBのバックアップは取得可能。
ただし、バイナリ形式になっている。
MySQLに移行するためにはinsert文でデータが欲しいので、pg_dumpを使って取得する。

pg_dump -d DB名 -h ホスト名 -U ユーザー名 --column-inserts -W > database.sql

--column-insertのオプションをつけないと、インサート文の形式ではなくなる。
insert文の形式にすると処理に時間がかかるらしいので、postgresqlに対しての移行ならオプションはつけない方が良いでしょう。
今回は移行先のDBが異なるのでinsert文にした。

3. SQL文を実行する

テーブルを作成するSQL文に関しては、開発時点で作成済みなので、移行先の環境にも既にスキーマやテーブル、インデックスなどは作成済み。
なのでインサート文のみ実行。
注意点としては、各テーブルの主キーはidで自動採番にしていたので、その点の考慮が必要。
PostgreSQLだとserial、MySQLだとauto_increment
ただ、最近はMySQLでもserialが使える(内部的にはauto_incrementとして処理しているだけらしいが)
ので、テーブル作成のcreate文はserialにしてればそのまま活用可能。
ただインサート後にauto_incrementの数値を更新するSQLを作成する必要がある。
postgreSQLのバックアップにserialのデータの更新があったので、その数値を参考にauto_incrementの更新を作成して実行。

ALTER TABLE xxxx AUTO_INCREMENT=100;

インサート文は特にエラーも起きず、問題点はなかった。

4. プログラムの移行

プログラムのソースコードGitHubで管理していたので、移行で特に困ることはなかった。
さくらのサーバーにあらかじめGitがインストールされていたので、それを使ってClone及びプルする感じ。

DBの接続先をMySQLにして、サーバーのMySQLのパスワードに変更。
デフォルトだとrootユーザーしかなく、パスワードが設定されていないので、必要に応じてユーザーを作成してパスワードを設定して、接続URLを合わせてソースコードを修正。

また、作成したデータベースに対して接続するユーザーからのアクセス権限をつけないと、プログラムからアクセスできないので注意。

grant all on opencourt.* to root@'localhost';

プログラムでエラーが出た時には
/avr/log/php-fpm/www-error.log
の中にあるので、動作確認でエラーが出る場合はこちらを参照。

なんか、json_encodeの関数でエラーが出たりする現象があった。
jsonのライブラリがインストールされてなかったみたい。
https://zafiel.wingall.com/archives/10521
ここを参考にインストールした。

5.サブドメインとバーチャルホストの設定

サーバーを移行した一番の目的がこれ。
ドメインを取得して、サブドメインとバーチャルホストを使うことで、一つのドメインで複数のシステムを同時に公開することができる。

サブドメインの設定方法は下記のサイトを参考にした。
http://monopocket.jp/blog/it_others/2457/
送信ボタンを押すまで反映されないことに注意。

Apacheのバーチャルホスト用の設定ファイルが必要。
/etc/httpd/conf.d/
の中に、なんでもいいので拡張子が.confのファイルを作成する。
vhost.confとかvirtualhost.confとかにするのが一般的っぽい。
書き方は適当にネットで検索してそれっぽいのを探す。

書き方が間違っていなければ、apacheの再起動でうまくいく。

6. SSLの設定

Herokuの時にはhttpsにしてなかったので、移行ついでにSSLの設定も実施した。
最初は、一番安い有料のSSL証明書を購入して
https://qiita.com/yoshizaki_kkgk/items/e6f39a5bfb99900b44b2
ここを参考にやってみた。
※vhost.confの中に「Listen 443」の行があるが、これがあるとサーバー起動でエラーになるので注意。
また、認証ファイルの配置方法についてはメールの案内に従った方が確実。
手順通りでSSLにすることはできたが、どうやらサブドメインに対応してなかった。。。
よくよく調べてみると、サブドメインに対応するにはワイルドカード型という証明書である必要があるらしい。。。
ただ、ワイルドカード型は値段が高価。
ということで断念。

色々調べていると、無料で使えるSSL証明書で「Lets Encrypt」というのがあるらしい。
https://knowledge.sakura.ad.jp/10534/
それを使うことにした。
上記の情報は色々バージョンが古かったので、CentOS8で新しいバージョンに対応しているもの
https://www.server-world.info/query?os=CentOS_8&p=ssl&f=2
ここを参考にした。

最終的にはこんな感じにコマンドで、サブドメインに対してSSLを対応させることができた。

certbot-auto -w /var/www/html -w /var/www/html/aaaa -w /var/www/html/bbbb -d eventmanc.com -d aaaa.eventmanc.com -d bbbb.eventmanc.com

ただ、SSLの有効期限はデフォルトでは3ヶ月らしい。
有効期限が1ヶ月を切ったタイミングで

certbot-auto renew  

コマンドを入力すると期限をリセットできるらしい。
しかし手動で打つのは面倒なので、cron使って毎日夜中に自動でコマンドが入力されるように更新する。

crontab -e
#  中身
0 3 *  *  * root /usr/bin/certbot-auto renew

7. Heroku環境のプログラムの修正

システム移行後は移行前のシステムでデータを登録されても困るので、使えないようにする。
.htaccessを設定して、index.php(index.html)にしかアクセスされないようにする。
そのページでは、新しいシステムのURLの案内を書いておく。
内容に問題なければ、メンテナンスモードを解除する。

heroku maintenance:off --app アプリ名

まとめ

手順として1〜7まで書きましたが、実際には4, 5, 6のプログラムの移行、サブドメインとバーチャルホストの設定、SSLの設定は移行する前に事前に実施しておく。
そしてサーバー移行のタイミングで

  1. Herokuをメンテナンスモードにする
  2. データベースのバックアップをとってデータ移行
  3. 移行先環境で軽く動作確認
  4. 移行元のプログラムの修正と反映
  5. メンテナンスモードの解除

という感じです。

【JavaScript】シンプルなカレンダーの作成

JavaScriptでシンプルなカレンダーの作成。
いくつかネットに転がっているコードを参考にもしましたが、基本的にDateの使い方さえ知っていればあとはifとforだけで意外と簡単に作れると知りました。

const today = new Date(); // 現在の日時  
const year = today.getFullYear(); // 年  
const month = today.getMonth(); // 月 0~11  
// 月末・月初の取得
const start = new Date(year, month, 1);     // 月初
const last = new Date(year, month + 1, 0);  // 月末
const startDate = start.getDate();          // 月初
const lastDate = last.getDate();            // 月末
const startDay = start.getDay();            // 月初の曜日
const lastDay = last.getDay();              // 月末の曜日

// カレンダーの作成
const weeks = ['日', '月', '火', '水', '木', '金', '土'];
// HTML表示用変数
let calendar = '<table>';
calendar +=`<caption>${year}年${month + 1}月</caption>`;
// 曜日の行
calendar += '<tr>';
for (const week of weeks) {
    calendar += '<th>' + week + '</td>';
}
calendar += '</tr>';

let dayCount = 0; // 曜日カウント用
for(let i = startDate; i <= lastDate; i++) {
    if(dayCount === 0) {
        calendar += '<tr>';
    }
    // 1日までの曜日に空白を入れる
    if(i === startDate) {
        for(let j = 0; j < startDay; j++) {
            calendar += '<td></td>';
            dayCount++;
        }
    }
    calendar += '<td>' + i +'</td>';
    dayCount++;
    if(dayCount === 7) {
        calendar += '</tr>';
        dayCount = 0;
    }
}
// 最終週は土曜になるまでから空白を入れる
for(let i = lastDay; i < 7; i++) {
    calendar += '<td></td>';
    if(i === 7) {
        calendar += '</tr>';
    }
}

calendar += '<table>';

// HTMLに書き込み
document.write(calendar);

【PHP】PHP入門(環境構築)

PHPは、数あるプログラミング言語の中でも比較的に動作させやすいプログラミング言語です。
しかし、PHPの動作環境は多くあるため、ここで簡単に比較しつつまとめてみます。

目次

PHP単体でインストール

PHPは公式サイトから最新バージョンをダウンロードできます。
https://www.php.net/downloads.php
ただし、PHPは本来Webアプリケーション開発用の言語であり、PHP単体で動作させることはほとんどありません。
コマンドからの実行もできますが、Webサーバー上で動作させ、Webアプリ用開発言語として使用します。
そのため、PHPの開発環境構築では、単体でのインストールよりも、WebサーバーやDBとセットになっている環境をそのままインストールする方法が主流です。

PHPを単体でインストールする場合は、別途Webサーバーの環境を用意する必要があります。
Webサーバーは、Apache、Nginx、IISなどがあります。
LinuxMacの場合はApacheやNginxを別途インストールして、PHPを動作させます。
Windowsの場合は、ApacheやNginxを別途インストールするか、IISの機能を有効化して、PHPが動作するように設定する必要があります。

XAMPPについて

PHPの開発環境でよく利用されるのはXAMPP(ザンプ)環境です。
https://www.apachefriends.org/jp/index.html
昔、LAMPP(LinuxApacheMySQLPHPPerl)と呼ばれる、LinuxPHPを動作させるための環境が流行しました。
それがLinuxに限らず、WindowsMac環境でも利用できるようになった環境がXAMPPです。
Webサーバー(Apache)が付随しているため、インストールすればすぐにPHPを動作させることができます。
また、DB環境(MySQL, MariaDB)の環境もセットになっているため、DBを利用する本格的なWebアプリの開発をする場合でも環境構築の手間が最小限ですみます。

MAMPについて

XAMPPはMac環境用もありますが、MacではXAMPPとは別でMAMPという開発環境もあります。
https://www.mamp.info/en/windows/
MAMPは有償版(MAMP Pro)と無償版があります。
有償版では使用できる機能が増えますが、無償版でもXAMPPと比べても遜色はないと個人的には思うので、学習用や小規模な開発向けであれば無償版でも十分に使えます。
ちなみにMAMPWindowsでも使用可能なようです。
Windows版は私は使用経験がないので使い勝手の程はわかりませんが。。

MAMPMAMP Proの違い、XAMPPとMAMPの細かい違いなどは私も詳しく把握していません。
気になる方は他のサイトで調べながら自分にあっているもの、使いやすそうなものを選んでください。
※個人的にはMacでのXAMPPはGitとの連携で苦労した経験があるので、Macの時はMAMPを使っています。

私はWindowsではXAMPP環境を、MacではMAMP環境を利用したことがありますが、小規模な開発においては使い勝手として大きな違いはないように感じます。
デフォルトのポート番号や、DB環境でのrootユーザーのパスワードなど、細かい違いはいくつかありますが。

XAMPP・MAMPのインストールと動作確認

XAMPP, MAMP, どちらも、インストーラをダウンロードして指示に従ってインストールすれば簡単にインストール可能。
XAMPPでもMAMPでも、インストールが完了したら、コントロールパネルが起動できるようになる。
(XAMPP, MAMPで検索するとアプリが出てくるはず)
コントロールパネルから、サーバーの起動や停止などが行える。
PHPを動作させるには最低限Webサーバー(Apache)が動いている必要がある。
XAMPPの場合はApacheを選んでスタートさせる。
MAMPの場合はStart ServerでApacheMySQLが起動する。

XAMPPやMAMP環境でのドキュメントルートは、
インストールディレクトリ/htdocs
になります。
このディレクトリの中に「index.php」を作成すれば、
http://localhost:ポート番号/
でアクセス可能。
index.php以外のファイル名であれば、
http://localhost:ポート番号/phpのファイル名
でアクセス可能。
デフォルトのポート番号はXAMPPは80(なので指定不要)、MAMPは8888。設定で変更可能。

テキストエディタIDE

開発を行う時にはテキストエディタIDE統合開発環境)が必要です。
主流なエディタ・IDEとしては、

などがあります。
私はVS Code派。
VS CodeEclipseは無料で使用可能。
PHPStormはライセンスが必要。1ヶ月は無料で使用可能。

VS CodePHP用のカスタマイズ

インストールした直後は英語なので、日本語にする場合には「Japanese Language Pack」をインストールします。
また、PHPの開発をしやすくするために「PHP IntelliSense」の拡張機能をインストールします。
あとはお好みで調べながら便利そうな拡張機能をインストールしておく。

設定ファイル

PHPを動作させる上で関係してくる設定ファイルは

などがあります。

httpd.conf

Apacheの設定ファイルです。
MAMPの場合、
Application/MAMP/conf/apache/httpd.conf
にあります。

設定でよく使用するのは
DocumentRoot
の設定。

DocumentRootは、URLで
サーバー名:ポート番号
にアクセスした時にアクセスされるディレクトリ。

デフォルトでは
DocumentRoot "/Applications/MAMP/htdocs"
になっている。
この時、サーバー名:ポート番号でアクセスしたときには
/Applications/MAMP/htdocs/index.html
/Applications/MAMP/htdocs/index.php
あたりが実行、表示される。

php.ini

PHPの設定ファイル。
ファイルの場所はMAMPの場合は Applications/MAMP/bin/php/php<バージョン>/conf/php.ini
XAMPPの場合は
C:¥xampp¥php¥php.ini

phpinfoの画面で確認できる。

環境構築の時に変更する可能性の高い設定としてはdisplay_errorsがある
PHPのプログラムでエラーや警告が発生した時に画面に表示するかどうかを設定する。
本番環境の運用の場合は、エラーが表示されてしまうとプログラムやDBの情報がユーザーに知られてしまう可能性があるため、Offにしておくべき。
開発環境では、バグを見つけやすくするためにOnにしておいた方が開発効率が良い。

error_reportingで、エラーの出力レベルを設定できる。
潜在的な不具合を生まないためには警告なども表示された方が良いので、E_ALLにして全部表示される設定にしておくのが良い。

.htaccess

Webサーバーをディレクトリ単位で制御するためのファイル。
リダイレクト、ベーシック認証、エラーページの作成などが行える。
Webサーバー全体の設定はhttpd.confで行うが、ディレクトリ単位で設定を上書きしたい場合は、.htaccessを使う。
必要なければ使わなくても大丈夫。

CentOSPHPをインストール

Linux環境(CentOS)をインストールする手順です。

# Apacheのインストール
$ sudo yum -y install httpd
# サービス起動
$ sudo systemctl start httpd
# 起動確認
$ sudo systemctl status httpd
# 自動起動設定
$ sudo systemctl enable httpd


# PHPのインストール

# 古いバージョンがインストールされている場合はアンインストールする
$ sudo yum remove php*

# EPELパッケージをインストール
$ sudo yum install epel-release
# Remiパッケージを追加
$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
# PHPインストール
$ sudo yum install --enablerepo=remi,remi-php72 php php-devel php-mbstring php-pdo php-gd php-xml php-mcrypt
# バージョン確認
$ php --version
# Apache再起動
$ sudo systemctl restart httpd

【UML】UML入門

UMLの基本的な内容について解説します。

UMLとは

  • Unified Modeling Language の略です。
  • オブジェクト指向におけるプログラムの構造を把握する場合に使用します。
  • システムの要件や業務の流れを整理する際にも使用されます。
  • UMLに含まれる図のことを「ダイアグラム」と呼びます。

UMLを学習することのメリット

  • クラス間の関連性の整理に役立ちます。
  • オブジェクト指向の設計・理解の手助けとなります。
  • 日本語の通じない相手にもクラス設計を伝えることができます。
  • 業務分析の際の整理術として使用できます。

UMLについての知識がなくても、オブジェクト指向プログラミングは可能です。
しかし、デザインパターンオブジェクト指向を活用した設計パターン)を理解しようと思うと、UML(その中でも特にクラス図)の理解は必須だと思ったほうが良いでしょう。
全てのダイアグラムを理解する必要はありませんが、「クラス図 + α」程度の知識があるとオブジェクト指向言語での開発・設計・分析をする際に役に立ちます。

ダイアグラム一覧

UMLで使用されるダイアグラムは以下のようなものがあります。

  • アクテビティ図
  • オブジェクト図
  • クラス図
  • コミュニケーション図
  • コンポーネント
  • コンポジット構造図
  • シーケンス図
  • 状態マシン図
  • 相互作用概念図
  • タイミング図
  • 配置図
  • パッケージ図
  • ユースケース

かなり多くのダイアグラムが存在しますが、大きく2種類に分類できます。

  • 構造の表記
  • 振る舞いの表記

構造の記述

  • オブジェクト図
  • クラス図
  • コンポーネント
  • コンポジット構成図
  • 配置図
  • パッケージ図

振る舞いの記述

  • アクティビティ図
  • コミュニケーション図
  • シーケンス図
  • 状態マシン図
  • 相互作用概要
  • タイミング図
  • ユースケース

かなり多くの図があることがわかるかと思いますが、実際に業務で良く使用されているものはそれほど多くありません。
以下の4つを覚えておけば概ね困ることはないかと思います。

プログラムを把握するためによく使用されるダイアグラム

  • クラス図
  • シーケンス図

業務分析でよく使用されるダイアグラム

ここからは各ダイアグラムについて簡単に解説します。

クラス図

UMLの中で最も使用頻度の高いダイアグラムです。
クラスの仕様とクラス間の関連性を記述できるものです。
デザインパターンを説明するうえでクラス図を使用するので、オブジェクト指向言語を学んでいる人は最低限クラス図は抑えておきましょう。
クラス図は以下のような図になります。

クラス図
クラス図

一番上はクラス名。
真ん中はフィールド。
下はメソッドになります。
クラスフィールドやクラスメソッドの場合は、下線を入れます。
アクセス修飾子を指定することも可能です。
アクセス修飾子を指定する場合は
- :private

:protected

  • :public
    をフィールド名やメソッド名の右側に記述します。
    メソッド名やフィールド名の右側に型が書かれている場合もあります。
    その場合、フィールドの右側にあるのはフィールドの型。
    メソッドの右側にあるのはメソッドの戻り値の型になります。

継承

クラス図で継承を表すこともできます。
継承を表す図は以下になります。

継承
継承

図の矢印(中身が白で実線の矢印)は継承を指します。
上のクラスがスーパークラスで、下がサブクラスです。
抽象クラスや抽象メソッドは斜体で表記するのが一般的です。
矢印が指している方がスーパークラス
クラス図を初めて見る人は、矢印は逆なのでは?と感じる人が多いです。
直感のイメージとしては、継承先の方に矢印が向いている方が自然に感じると思います。
しかし、実際には継承元の方に矢印が向くので注意してください。
矢印を、「知っている」という風にとらえるとしっくりくると思います。
サブクラスはスーパークラスを知っている必要があります。
一方でスーパークラスはサブクラスの存在を知らなくても単体で存在することが可能です。
矢印を、ある意味で「依存している」と捉えると理解しやすいかもしれません。

また、継承はモデリングの世界では、「特化-汎化」と表現されることがあります。
スーパークラスから見たサブクラスを「特化」
サブクラスから見たスーパークラスを「汎化」と呼びます。

実装

継承ではなく、インターフェースを実装する場合は以下のような図になります。

実装
実装

図が若干見えにくいかもしれませんが、矢印が実線ではなく破線になっています。
実装の場合は破線で表現します。
インターフェースはクラスと区別するために<>という目印をつけることがあります。

集約

以下は集約を表す図です。

集約
集約

集約は、インスタンス(オブジェクト)を配列やリストなどでフィールド(プロパティ)に保持しているイメージです。
図では、 Carクラス(車クラス)がTireクラス(タイヤクラス)をフィールドで複数保持している状態を表しています。

関連

以下は関連を表す図です。

関連
関連

関連、つまり、そのクラスのことを知っている状態を表した図です。
プログラム的には集約と同じ意味と捉えても良いでしょう。
持っている、というよりも、関連している、ということを強調したい時に使うようです。

使用

以下は使用を表す図です。

使用
使用

実線ではなく破線となっている点に注意してください。
図は、Utilクラスを使用することを表しています。

集約、関連、使用などについては、意味や表記方法が似ていてどこで何を使うかがイメージしにくいかもしれません。
正直なところ、UMLはプログラムと厳密に結びついているわけではなく、プログラムを整理したり、人に分かりやすく伝えるためのツールなので、厳密さにこだわるよりも、伝わりやすさを優先してケースバイケースで選べば良いでしょう。

シーケンス図

クラス図は、クラス間の静的な関係を表した図でした。
クラス図ではクラスの振る舞いの相互作用(どのメソッドからどのメソッドが呼ばれるかなど)は分かりません。
この振る舞いの相互作用を表したのがシーケンス図です。

シーケンス図
シーケンス図

オブジェクトとオブジェクト間のメッセージの流れを表現することができます。
オブジェクトに限らず、ユーザーとシステム間のメッセージのやりとりなどでも使用可能です。
いずれにせよ、時系列でのメッセージのやりとりを表現する手法として使用できます。

ユースケース

ユースケース図は、ユーザーとシステム間のやり取り(相互作用)を記述すための図です。

ユースケース図
ユースケース

これはシステムの要件定義などで使用されます。

アクティビティ図

手続きや、メソッド(関数)のロジック、ビジネスプロセスなどのフローを記述するための技法です。
フローチャートに似ています。

アクティビティ図
アクティビティ図

オブジェクト図

オブジェクトのある時点におけるスナップショットを表す図です。
インスタンス図とも呼ばれます。
クラス図だけでは構造を把握するのが困難な場合に有効です。

コミュニケーション図

シーケンス図では、時系列でオブジェクト間のやり取りを記述しますが、コミュニケーション図では、時系列に関係なくオブジェクトを配置できます。
メッセージのやり取りをオブジェクトの関係を中心に表現できる。

コミュニケーション図
コミュニケーション図

コンポーネント

コンポーネントの構造や、コンポーネント間の相互作用と表す図です。
そもそもコンポーネントとは、ソフトウェアの部品という意味ですがクラスとの明確な違いはありません。
一般には複数のクラスから構成されるシステムの一部をコンポーネンと呼びます。
システムをいくつかの部品に分割し、その関係性や構造を示す場合に使用します。

コンポジット構成図

複数のクラスを包括するようなクラス、コンポーネントにおいて、その内部構造を表現するための図です。

ステートマシン図

ステートマシン図は、1つのオブジェクトの存在期間中における振る舞いを示す図。

相互作用概要図

相互作用概要図は、アクティビティ図とシーケンス図を合体させたものです。
アクティビティ図の中のアクテビティを、シーケンス図に置き換えたもの、もしくはアクティビティ図の記法で制御フローを分割したシーケンス図。

タイミング図

タイミングは、オブジェクトの状態が変化するタイミングを表すことを重視した図です。

配置図

配置図は、システムの物理的なレイアウトを示す図です。
ソフトウェアのどの部分がハードウェアのどの部分で動作するかを示す。

パッケージ図

パッケージとは、クラスをグルーピングするための仕組みです。
言語によっては名前空間とも呼ばれます。
パッケージ図は、パッケージ(名前空間)、およびパッケージ間の依存関係を表す図です。
規模が大きいシステムでは、パッケージ間の依存関係を把握するのに便利です。

まとめ

後半のダイアグラムに関しては、画像を入れてませんが、画像がない図においては使用頻度が低いものなので必ずしも覚える必要はありません。
(私もほぼ使用したことのないものばかりです)
興味がある方は書籍や他のサイトを参考に学習してみてください。
とりあえず、クラス図、シーケンス図、ユースケース図、アクティビティ図においては概要を理解しておき、それ以外は必要に応じて調べながら活用できれば良いでしょう。
クラス図は、業務で作成を求められない場合でも、自分自身のクラスの関連性を把握するために紙などに書くと頭が整理されますので、積極的に活用することをおすすめします。

関連書籍

もっと深く勉強したい方は以下の書籍がおすすめです。

【Redmine】Redmineの構築

サーバーにRedmineの環境を構築する作業をしましたが、いくつかつまずいた箇所があったのでそのメモです。

サーバーの環境

インストールを実施した環境は以下

  • OS:CentOS6(Sakura VPS
  • DB:MySQL 5.6
  • Ruby:未導入

インストールしたもの
* Ruby 2.5.7 * Redmine 4.0.7

導入手順

導入手順いついては、基本的には公式サイトのインストール手順を参考に実施。

Redmineガイド

ガイドによると、MySQL5.6以降については既知の問題があると書かれているが、サーバー環境は既に他のシステムも色々と導入されてしまっているので、バージョンを変更するわけにもいかないので、とりあえずそのまま手順書通り進めることにした。

※実際には手順書通りにはうまくかなかった。
これについては後ほど記載。

Rubyインストール

RedmineRailsで動いているとのこと。
ということでまずはRubyのインストールが必要。
以下のサイトを参考にインストール

https://qiita.com/NaokiIshimura/items/ff04b6eaa40b33c4bea8

https://qiita.com/h_matsuno1028/items/7d3a13be4f37bff8be36

rbenvというものを使ってrubyをインストールする。 インストールしたら、使用するrubyのバージョンを rbenv global バージョン で指定する。
バージョンの指定までしないと、gemやrubyコマンドが使えないので注意。
今回構築した環境ではバージョン2.5.7しか選べなかったので、そちらを選択した。

Railsのインストール

Rubyのあとはrailsのインストール

railsのインストール

gem list -e -rails

とかで、インストールできるバージョンを確認できるらしいけれど、特にバージョンの表示がされなかった。。 ただ、

gem install rails

としたらインストール自体は成功した。
ただし、

rails -v

でバージョン確認すると、rails 6.0がインストールされてしまっていた。
詳しくはわからないが、Redmineのガイドを見る限り、バージョン5系の方が無難な気がしたので、一度アンインストールして5系をインストールすることに。

以下のサイトを参考に一度アンインストール

https://qiita.com/Hassan/items/eef26c870eb26a0c68e0

サイト通りにやればうまくアンインストールできた。

次はバージョン指定してインストール

gem install rails -v 5.2.4

うまくインストールすることができた。

Redmine

導入したRedmineのバージョンは4.0.7
ダンロードサイト
からredmine-4.0.7.tar.gzをダウンロード。
scpコマンドでサーバーに転送し、tarコマンドで展開。
展開したディレクトリを適当なディレクトリに配置。
今回は/var/www/html/redmine
に配置。
※ネットで色んなサイトを見ていると、/var/lib/redmine
あたりに配置するのが定番っぽかった。

redmineディレクトリに移動して

$ gem install bundler
$ bundle install --without development test

を実行する場面で色々とエラーが出た。
エラーになるのは事前に必要となるものがインストールされていないことが原因らしい。
親切にもメッセージをよく見るとインストールのコマンドが表示されていたりするので、指示に従って足りないものをインストールする。

私の環境では

sudo yum install mysql-devel

gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'

などが必要だった。

その後、データの追加

RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

でエラーが出まくった。
テーブルにカラムがない、だのなんだのと色々エラーが出た。

こんな感じのエラー

Error: Mysql2::Error: Unknown column 'roles.builtin' in 'where clause': SELECT  1 AS one FROM `roles` WHERE `roles`.`builtin` = 0 LIMIT 1

試しにテーブルにカラムを追加してみると、上記のエラーは出なくなったが、別のエラーが出てきた。
その度にカラムを追加するなど、対応をしてみたが、一向にエラーがなくなる気配がなく、埒が明かない。

根本的に環境に何か問題があるのかもしれない。

エラーメッセージを頼りに色々と調べていると、同じエラーでMySQLのデータベースの文字コードutf-8にしたら解決した、的なページが出てきた。
ということで試してみた。

公式サイトだとMySQLのデータベース作成のコマンドは

create database redmine character set utf-8mb4;

となっているが、utf-8に変更してみた。

drop database redmine;
create database redmine character set urf-8;

そうすると、テーブル作成のスクリプトもデフォルトのデータ作成のスクリプトもうまくいった。

Step9でWEBrickによるwebサーバを起動して、インストールができたか確認する手順がある。

bundle exec rails server webrick -e production

この時、ポートが3000になるので、ファイアウィールで3000を解放する必要がある。
CentOS6の場合はiptablesでポートを開放する。
この辺りの記事を参考に
https://loups-garous.hatenablog.com/entry/20120917/1347900472

Apacheで動作させる

動作確認できたら、Apacheで動くように設定する。
どうやらrailsapacheで動かすのに色々必要っぽい。

こちらも公式サイトで手順があるので、基本はこれを参考に実施。
https://redmine.jp/tech_note/apache-passenger/

手順に沿ってやるのだが

passenger-install-apache2-module

のコマンドで色々とエラーが出た。
が、こちらも必要なものをインストールしてください、とインストールのコマンドが出てきてくれた。

 * To install Curl development headers with SSL support:
   Please install it with yum install libcurl-devel

 * To install Apache 2 development headers:
   Please install it with yum install httpd-devel

 * To install Apache Portable Runtime (APR) development headers:
   Please install it with yum install apr-devel

 * To install Apache Portable Runtime Utility (APU) development headers:
   Please install it with yum install apr-util-devel

ということでyumコマンドでそれぞれインストールして、再度実施したらうまくいった。

最後は、httpdの設定ファイルに追加する内容が出てくるので、それをコピーしておく。

LoadModule passenger_module /home/develop/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/passenger-6.0.6/buildout/apache2/mod_passenger.so
   <IfModule mod_passenger.c>
     PassengerRoot /home/develop/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/passenger-6.0.6
     PassengerDefaultRuby /home/develop/.rbenv/versions/2.5.8/bin/ruby
   </IfModule>

公式サイトだと、httpd.confに追加すると書いているのだが、色々サイトを見ていると、
etc/httpd/conf.f/passenger.conf
を作成して、その中に記述する、と書いているサイトもあった。
どっち??
と思ったけど、おそらくどっちでも良いらしい。
2つ書いてhttpdを再起動したら、既に読み込まれています、的な警告メッセージが出た。
なのでどこに書いてもおそらくは読み込まれているのだろう。
ディレクトリで読み込むファイルが決まるようになっているのかな??

設定完了後、
http://<サーバーのIP>/redmine にアクセスしてみたが、

index of /redmine
ディレクトリの中身が表示されるだけで、redmineが起動しない現象が起きた。
これはおそらくVirtualHostの問題。
Redmineインストールディレクトリ/public
ドメインと紐づければおそらくうまくいく。

【AWS】【Docker】【Java】Amazon Linux 2 でJavaのWebアプリケーションをデプロイするまで(Docker編)

以前、AWS EC2のAmazon Linux 2の環境にJavaのWebアプリケーションをデプロイするまでの手順解説の記事を書きました。
[Amazon Linux 2でJavaのWebアプリケーション動作環境を構築してデプロイするまで]https://case10.hateblo.jp/entry/2020/10/06/165543 今回はJava, TomcatをDockerで動かす場合の手順になります。
※DBはDocker使わずにホストに直接インストールする前提。

Dockerのインストール

# Dockerのインストール
$ sudo yum install -y docker
# サービスの起動
$ sudo systemctl start docker
# 確認
$ sudo docker version
# ユーザーの追加(必要ない??)
$ sudo usermod -a -G docker ec2-user
# システムの自動起動設定
$ sudo systemctl enable docker

マウント用のディレクトリの作成

Docker上のTomcatとマウントする用のディレクトリを作成しておく。
マウントしておくと、ここにファイルをおけばコンテナ上にもファイルが配置される。
また、コンテナを削除してもファイルが削除されないようになる。

# Tomcat用のディレクトリ作成
$ sudo mkdir /opt/tomcat

Tomcatのコンテナを起動

# TomcatのDockerイメージ取得
$ sudo docker pull tomcat
# 取得できたか確認
$ sudo docker images

# ホスト側の/opt/tomcat/gropAディレクトリを/usr/local/tomcat/にマウントする
# --nameオプションではコンテナ名を指定。任意。
$ sudo docker run -d -p 8080:8080 -v /opt/tomcat:/usr/local/tomcat --name tomcatA tomcat

# 起動したか確認
$ duso docker ps

# インスタンスを再起動した時に自動でコンテナが起動するように設定
$ sudo docker update --restart=always tomcatA

Tomcatの設定

tomcatではwebapps配下にデプロイするファイルを配置する。
しかしコンテナのTomcatではデフォルトではWebappsの中身は空で、webapps.distに必要なファイルが入っているので、中身をコピーする。
※リネームしても良いかも。
リネームの場合は、どこのディレクトリとマウントしているかによって、リネームができなくなる場合があるので注意。

# Docker(Tomcat)に入り込む
$ sudo docker exec -it tomcatA bash
# Webapps.distの中身をWebappsにコピー
$ cp -r webapps.dist/* webapps
# Dockerから抜ける
$ exit

動作検証

ブラウザから

http://<パブリックDNS>:8080  

にアクセスしてTomcatのホーム画面が出るかを確認する。

マウントができているか確認したい場合は、ホスト側でtomcat/webappsの中に適当なディレクトリを作成し、index.htmlを適当に作成する。
ブラウザから

http://<パブリックDNS>:8080/<作成したディレクトリ>  

にアクセスして、index.htmlの内容が表示されていればOK。

あとは必要に応じてwarファイルを配置してデプロイすればWebアプリケーションが動作する。