独り言

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

【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);

解説

基本的な文法の解説は省略します。
Dateオブジェクトの詳細については以下。
Date - JavaScript | MDN
ポイントとしては、JaavScriptでは月は0~11で表します。
また、前月の月末の日付を取得したい場合には、コンストラクタの日付引数で0を指定してあげる必要があります。

【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アプリケーションが動作する。

【PHP】PHP入門(オブジェクト指向編)

PHPオブジェクト指向編です。
オブジェクト指向そのもそについての細かい説明は下記の記事で書いたので、ここでは簡単な説明に留めます。
ソースコードを絡めた説明をメインにします。

case10.hateblo.jp

PHPオブジェクト指向以前の内容については以下の記事を参考にしてください。

case10.hateblo.jp

オブジェクト指向の概要

オブジェクト指向の概要の説明はここでもしておきます。
オブジェクト指向は簡単にいえば、プログラミングを効率化するための手法・考え方になります。
プログラムをクラスという単位でまとめ、クラスを元にオブジェクト(インスタンス)を作成し、インスタンスのやりとりによってプログラムの流れを書いていきます。


クラスの利用

オブジェクト指向ではクラスという概念を使用し、クラスを元にオブジェクトを生成してプログラムを書くという仕組みです。
以下、クラスの宣言のサンプルです。

User.php

<?php

// クラス宣言
class User 
{
    
    // プロパティ
    public $id;
    public $age;
    public $name;

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }
}

クラスを利用するファイル index.php

<?php
require_once('User.php');
// インスタンスの生成
$user = new User();
// プロパティへの値のセット
$user->name = '綾鷹';
$user->age = 20;
// メソッドの呼び出し
$user->selfIntroduction();

実行結果を確認するにはWebサーバーを起動し、ブラウザ上からindex.phpを確認します。
ブラウザでの結果

名前は 綾鷹 です。年齢は 20 です。

解説

クラスを宣言は以下のようになります。

<?php
class クラス名 
{
    // プロパティ
    アクセス修飾子 変数名;

    // コンストラクタ
    public function __construct() 
    {
        // 初期化処理
    }

    // メソッド
    アクセス修飾子 function メソッド名()
    {
        // 処理
    }
}

クラス宣言

クラス名はそのクラスを表すものになります。
今回のサンプルではユーザーを表すクラスとしてUserクラスを作成しました。
クラス名は先頭を大文字で命名するのが一般的になっています。
クラス宣言後の中括弧({})の中には、オブジェクトの状態を表す「プロパティ」、振る舞いを表す「メソッド」、オブジェクト生成時の初期化を行う「コンストラクタ」を書くことができます。
コンストラクタについては後ほどサンプルで詳細を解説します。

プロパティ

プロパティはオブジェクトの状態を表すもので、簡単に言ってしまえば変数です。
変数と違うのは、アクセス修飾子を指定する必要があることです。
アクセス修飾子とは、クラスの外部からでもアクセス(参照、代入)ができるのか、あるいはクラス内からしかアクセスできないのか、といったアクセスのレベルを設定するものです。
アクセス修飾子は「public」「protected」「private」の3つがあります。
ざっくりと説明すると、
publicはどこからでもアクセス可能。
protectedは自クラス内と継承先のクラスからアクセス可能。
privateは自クラス内のみからアクセス可能。
となります。

今回の例ではユーザーが持つプロパティとして、id、名前、年齢という変数を作成しています。

メソッド

メソッドは処理を定義するもので、言わば関数です。
クラス(オブジェクト)が持つ関数というイメージです。
プロパティと同じく、アクセス修飾子を指定できます。
指定できるアクセス修飾子もプロパティと同じで、「public」「protected」「private」の3つが指定できます。
アクセス修飾子を省略した場合はpublicになります。

メソッドでは通常の関数を同じく処理をなんでも定義することができますが、一つ関数と違うポイントとして「$this」というキーワードが出てきます。
サンプルのコードではメソッドは

echo "名前は $this->name です。年齢は $this->age です。";

となっています。
thisは「自分自身」を表すキーワードです。
nameとageはプロパティの名前です。
「$this->name」「$this->age」はそのインスタンス(オブジェクト)のname, ageを表すものとなっています。
クラスの中ではnameとageというプロパティはそれぞれ1つずつしか宣言していません。
しかし、後述するnewというキーワードを使用すると、クラスを元にインスタンス(オブジェクト)を作成することができて、作成したインスタンスの分だけname, ageのプロパティも出来上がります。
$thisをつけると、そのインスタンス自身のname, ageを表す、という意味になります。
name, ageの前には$を付けないことに注意しましょう。

また、「$this」とプロパティの間に「->」(ハイフンと不等号)があります。
これは日本語で表すと「の」を意味する接続詞と考えると良いでしょう。
そのインスタンス「の」nameプロパティ。
のような感じです。
この記号はインスタンスを使用する場合に頻繁に出てきますので、覚えておくようにしましょう。
ちなみにJavaでは同じ表現を「.」(ピリオド)を使って表します。

外部ファイルの読み込み

ここからはindex.php(クラスを利用しているファイル)について解説します。
まず外部ファイルの読み込みからです。
ファイルの先頭に

require_once('User.php');

というコードがあります。
これはオブジェクト指向とは直接関係ないですが、PHPで別ファイルとして定義されたもの(外部ファイル)を読み込むための構文になります。
PHPでは外部ファイルを読み込むための構文は4つあります。
「require」「require_once」「include」「include_once」の4つです。
それぞれ、似てはいるものの微妙に動きが異なります。

require:指定されたファイルを読み込み、読み込みに失敗した場合(エラーが発生した場合)は処理が停止します。
require_once:requireとほぼ同じ意味ですが、違う点は、既に読み込まれているかどうかをチェックし、読み込まれている場合には読み込みません。
include:指定されたファイルを読み込み、読み込みに失敗しても処理は停止せずに、警告を出して処理を続行します。
include_once:includeとほぼ同じ意味ですが、既に読み込まれているかをチェックし、読み込まれている場合には読み込みません。

どれを使うかはケースバイケースですが、多くの場合、PHPのみのファイル(クラスが定義されたファイルなど)を読み込む場合はrequire_once、Webページに表示されるコンテンツの一部を表示する場合にはincludeを使う場合が多いです。
ここでは特に問題がない限りはrequire_onceを使用していきます。
ファイルを読み込むには引数に文字列で読み込むファイルのパスを指定する必要があります。
ファイルのパスは絶対パスによる指定と相対パスによる指定が可能です。
今回のサンプルではそれぞれのファイルが同じ階層にいることを前提としてファイル名のみを指定しています。
異なる階層にある場合には絶対パス相対パスでファイルの場所を指定する必要があります。

インスタンスの生成

クラスを元に作成されたオブジェクトのことをPHPではインスタンスと呼びます。
インスタンスを生成するのはnewというキーワードを使用します。
サンプルでは以下のようになっています。

// インスタンスの生成
$user = new User();

構文としては以下のようになります。

変数 = new クラス名();

この場合、クラス名の後の()は省略可能です。
ただし、コンストラクタを定義している場合はその引数に応じた値を渡してあげる必要があります。
この点については後述します。

プロパティ・メソッドへのアクセス

インスタンスは生成しただけではあまり意味はありません。
それを使うことに意義があります。
プロパティにアクセスするためには、
インスタンス名(変数名)->プロパティ名
インスタンス名(変数名)->メソッド名(); とすることでプロパティやメソッドを使用することができます。
サンプルでは以下のようになっています。

// プロパティへの値のセット
$user->name = '綾鷹';
$user->age = 20;
// メソッドの呼び出し
$user->selfIntroduction();

プロパティは変数とおぼ同じなので値を代入することができます。
参照して出力することも可能です。
メソッドは呼び出すことで実行可能です。
今回のサンプルではプロパティやメソッドを自由に使うことができますが、これはそれぞれのプロパティやメソッドのアクセス修飾子がpublicだからです。
privateやprotectedだった場合にはアクセスできずにエラーになってしまいますので注意が必要です。


コンストラク

プロパティとメソッドについては軽くサンプルを示しましたがコンストラクタについてはサンプルを示していなかったのでここで追記してみます。
User.phpを以下のようにします。

<?php

// クラス宣言
class User 
{
    
    // プロパティ
    public $id;
    public $age;
    public $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }
}

そしてindex.phpを以下のようにします。

<?php
require_once('User.php');
// インスタンスの生成
$user = new User('綾鷹', 20);
// メソッドの呼び出し
$user->selfIntroduction();

実行結果は最初の例と変更ありません。

コンストラクタの定義

コンストラクタは、インスタンス生成時(つまりnewというキーワードを使った時)に呼ばれる特殊なメソッドです。
メソッド名は「__construct」で固定となります。
サンプルのコードでは以下のようになっています。

// コンストラクタ
public function __construct($name, $age)
{
    $this->name = $name;
    $this->age = $age;
}

ちなみに、PHPでは特殊なメソッド、関数として(アンダースコア)2つから始まるものが定義されていることがあります。
自分でメソッドを定義する時にも
(アンダースコア)2つから始まるメソッドを定義することもできますが、自分で定義した名前なのか、PHPが予め用意しているメソッドの名前なのか区別がつきにくくなるため、メソッドを自分で定義する時に_2つから始まるものは避けるようにしましょう。
(このような関数はマジックメソッドと呼ばれています。他の言語でも似た特徴を持つものもあります。)

サンプルをみてわかるように、コンストラクタでは引数を持つこともできます。
この例ではサンプルで受け取った値をそのままプロパティの値にセットしています。
この例のように、コンストラクタではプロパティの値を初期化するために使用されることが多いです。

ちなみに、コンストラクタは一つしか定義することができません。
引数の数を変更して複数定義しようとしてもエラーになるので注意してください。
Javaなどの言語では、オーバーロードという仕組みがあり、引数が異なれば同じメソッド名を複数定義することも可能ですが、PHPでその機能はサポートされていないので注意してください。)

コンストラクタの使用

コンストラクタを使用するには、newでクラス名を指定した後にコンストラクタの引数に対応した実引数を渡してあげます。

// インスタンスの生成
$user = new User('綾鷹', 20);

クラスプロパティとクラスメソッド

クラス内で定義したプロパティやメソッドは、newを使用してインスタンスを作成するたびに、インスタンス毎に作成されていくようなイメージです。
一方、クラスで1つ(全てのオブジェクトで共有される)プロパティやメソッドを作成することも可能です。
このようなプロパティやメソッドとそれぞれクラスプロパティ、クラスメソッドと呼びます。
staticというキーワードをつけることで実現できます。
User.phpとindex.phpを以下のように修正します。

User.php

<?php

// クラス宣言
class User 
{
    
    // クラスプロパティ
    public static $count = 0;

    // プロパティ
    public $id;
    public $age;
    public $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
        self::countUp();  // 追加
    }

    // クラスメソッド
    public static function countUp() 
    {
        self::$count++;
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }
}

index.php

<?php
require_once('User.php');
// インスタンスの生成
$user = new User('綾鷹', 20);
echo 'カウント:' . User::$count . '<br>';

$user = new User('伊右衛門', 30);
echo 'カウント:' . User::$count . '<br>';

User::countUp();
echo 'カウント:' . User::$count . '<br>';

実行結果

カウント:1
カウント:2
カウント:3

クラスプロパティとクラスメソッドの宣言

クラスプロパティとクラスメソッドの宣言方法は、通常のプロパティとメソッドの宣言方法とほとんど同じです。
違いはstaticがつく点です。

// クラスプロパティ
public static $count;

// クラスメソッド
public static function countUp() 
{
    self::$count++;
}

新しく「self::」というキーワードが出てきました。
通常のプロパティやメソッドの場合、自クラス内からアクセスする場合は「$this」というキーワードを付けましたが、クラスプロパティやクラスメソッドにアクセスする場合にはそれが「self::」に変わるので覚えておきましょう。
また、プロパティにアクセスする時には変数名に$マークもつける点にも注意です。

クラスプロパティやクラスメソッドの利用

外部から、クラスプロパティ、クラスメソッドにアクセスする場合には、
クラス名::プロパティ名
クラス名::メソッド名
とします。

User::countUp();
echo 'カウント:' . User::$count . '<br>';

また、サンプルソースではコンストラクタの中でcountUpメソッドを呼び出していることに注意してください。
そうするとnewを使うたびに$countがカウントアップされます。
また、自分で「User::countUp();」を呼び出すことでもカウントアップされます。
結果として、インスタンス生成を2回、カウントアップメソッドの呼び出しを1回しているため、最終的に$countプロパティの値が3になっていることがわかります。
処理の流れを追うことができれば、インスタンス間で値が共有されて、クラスで1つのみであることがイメージできるかと思います。


定数

クラスの中には定数を定義することができます。
定数は再代入することができない変数です。
定数を定義するには、constというキーワードを使用します。
変数名には$マークを付けず、アルファベットの大文字で名前を付けます。

User.php

<?php

// クラス宣言
class User 
{
    // 定数
    public const MAX_SCORE = 100;
    
    // クラスプロパティ
    public static $count = 0;

    // プロパティ
    public $id;
    public $age;
    public $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
        self::countUp();  // 追加
    }

    // クラスメソッド
    public static function countUp() 
    {
        self::$count++;
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }
}

index.php

<?php
require_once('User.php');

echo User::MAX_SCORE;

実行結果

100

定数宣言

定数宣言は以下のようになります。

// 定数
public const MAX_SCORE = 100;

使用する場合は、
クラス名::定数名
となります。

echo User::MAX_SCORE;

定数は再代入することができないので、代入する処理を書くとエラーになります。


継承

継承は元々あるクラスの特徴を引き継いで新しいクラスを作ることができる機能です。
User.phpをそのままに、同じ階層にExUser.phpを作成します。

ExUser.php

<?php

require_once('User.php');

class ExUser extends User
{
    // 新しいプロパティ追加
    public $score;

    public function __construct($name, $age, $score)
    {
        parent::__construct($name, $age);
        $this->score = $score;
    }

    // 新しいメソッドの追加
    public function scoreUp()
    {
        $this->score += 10; 
    }

    // メソッドのオーバーライド
    public function selfIntroduction()
    {
        parent::selfIntroduction();
        echo "scoreは $this->score です。";
    }
}

index.phpを以下に書き換えます。

<?php
require_once('ExUser.php');
// インスタンスの生成
$exUser = new ExUser('いろはす', 10, 70);
$exUser->scoreUp();
$exUser->selfIntroduction();

実行結果

名前は いろはす です。年齢は 10 です。scoreは 80 です。

extends

クラスの継承を行う時にはクラス宣言時にextendsというキーワードを使用します。
(拡張という意味があります。)

class ExUser extends User
{
    ...
}

こうすると、Userクラスを継承(拡張)してExUserクラスを作成したことになります。
この時、Userクラスのことを親クラス、あるいはスーパークラスなどと呼びます。
ExUserのことを、子クラス、あるいはサブクラスなどと呼びます。
継承を行うと、親クラスの機能は定義しなくてもそのまま使用することができます。
つまり、$nameプロパティや$ageプロパティなどはそのまま使えます。
もちろんメソッドも使用することができます。
親クラスの状態を受け継いだまま、新しい機能を追記することができます。
今回の例では$scoreプロパティとscoreUpメソッドを新しく追記しています。

parentキーワード

これまで自分自身のインスタンス、クラスを表すキーワードとして「this」や「self」がありましたが、継承を使う場合には新しく親クラス表す「parent」キーワードが登場します。
parent::プロパティ名
parent::メソッド名
で親クラスのプロパティやメソッドにアクセスすることができます。
今回のサンプルでは、コンストラクタの中で親クラスのコンストラクタを呼び出す場合などに利用しています。

多重継承

複数のクラスを同時に継承することを多重継承と呼びます。
PHPでは多重継承はできません。(多くのオブジェクト指向言語では多重継承はできない。一部できるものもある。)
その代わり、何世代にも渡って継承して新しいクラスを作成することは可能です。

オーバーライド

継承を行う上で重要な概念の一つにオーバーライドがあります。
オーバーライドはメソッドの上書きという意味があります。
今回の例ではselfIntroductionメソッド(自己紹介メソッド)がオーバーライドに該当します。
親クラス(Userクラス)でも全く同じ名前のメソッドが存在しますが、子クラス(ExUser)でも同じ名前のメソッドを定義しています。
これがオーバーライドです。
クラスを拡張したことでプロパティが増えた(score)ので、自己紹介メソッドで追加したプロパティも表示するようにしています。
親クラスのメソッドをparentを指定して呼び出しつつ、新しく追加する処理のみを追加しています。
これにより新しく追加したい部分のみの処理を定義すれば良いため、効率が上がります。

抽象クラスとインターフェース

継承を利用すれば、開発を行う際に役割分担をしやすくなるというメリットもあります。
親クラスをAさんが開発し、そのクラスを継承した子クラスをBさんが開発する、のような役割分担ができます。
しかし、役割分担するとミスも生じやすくなります。
例えば、Aさんが作成したクラスは、継承することを前提としており、特定のメソッドを必ずオーバーライドして欲しかったとします。
このとき、子クラスを作成したとしても、メソッドをオーバーライドし忘れたり、あるいはオーバーライドしたつもりでもメソッド名のスペルが間違っていてオーバーライドになっていないといったミスが発生する可能性があります。

このようなミスを発生させないための仕組みとして、抽象クラス、インターフェースという概念があります。

抽象クラス

抽象クラスは、継承することを前提としたクラスです。
抽象クラスは、宣言時にabstractキーワードを使用します。
また、抽象クラスは抽象メソッドと呼ばれる処理のないメソッドを定義することができます。
処理が書かれている通常のメソッドも定義することができます。
抽象クラスではない通常のクラスのことを、抽象クラスの対比として「具象クラス」と呼ぶことがあります。
以下はサンプルです。

抽象クラス
Employee.php

<?php

abstract class Employee 
{
    private $saraly;

    // コンストラクタ
    public function __construct($saraly)
    {
        $this->saraly = $saraly;
    }

    // 通常のメソッド
    public function getSalary()
    {
        return $this->saraly;
    }

    // 抽象メソッド
    public abstract function work();
}

具象クラス
Peogrammer.php

<?php

require_once('Employee.php');

class Programmer extends Employee 
{
    // オーバーライドしないとエラーになる
    public function work()
    {
        echo 'プログラミングする';
    }
}

index.php

<?php
require_once('Programmer.php');
// インスタンスの生成
// $employee = new Employee();  // ←エラーになる

$programer = new Programmer('200000') . '<br>';
echo $programer->getSalary();
$programer->work();

実行結果

200000
プログラミングする

Employeeクラスが抽象クラスです。
クラス宣言にabstractがつきます。
プロパティや通常のメソッドも持っています。(salaryプロパティとgetSalaryメソッド)
workメソッドが抽象メソッドです。
アクセス修飾子の後にabstractキーワードをつけます。
処理は書かず、中かっこ{}も書きません。
その代わりにセミコロン(;)で終了します。

Programmerクラスが抽象クラスを継承した具象クラスです。
抽象クラスを継承した場合、そのクラスに定義されている抽象メソッドをオーバーライドしないとエラーになります。
コメントアウトして実行するとどうなるか確認すると良いでしょう。)
※ただし、抽象クラスを継承したクラス自体を抽象クラスにしてしまえば(abstractをつける)、オーバーライドしなくてもエラーにはなりません。

index.phpで実行結果を確認しています。
抽象クラスは、継承されることを前提としたクラスであると言いましたが、その理由からnewでインスタンスを作成することがでないことに注意して下さい。

インターフェース

抽象クラスとは別にインターフェースという概念もあります。
インターフェースは抽象クラスよりもさらに抽象度の高い概念を定義する機能です。
インターフェースではプロパティや通常のメソッドを定義することができず、抽象メソッドと定数が定義可能です。
インターフェースを定義するときはclassではなくinterfaceというキーワードを使用します。
また、インターフェースは継承ではなく実装すると呼び、extendsではなくimplementsというキーワードを使用します。

インターフェース
Runnable.php

<?php

interface Runnable
{
    // 抽象クラス(absract不要)
    public function run();
}

実装したクラス
Programmer.php

<?php

require_once('Employee.php');
require_once('Runnable.php');

class Programmer extends Employee implements Runnable
{
    public function work()
    {
        echo 'プログラミングする';
    }

    public function run()
    {
        echo 'ゆっくり走る';
    }
}

index.php

<?php
require_once('Programmer.php');
// インスタンスの生成
$programer = new Programmer('200000');
$programer->run();

実行結果

ゆっくり走る

このサンプルではRunnnableがインターフェースです。
インターフェースでは抽象メソッドしか定義できないため、abstractは省略可能です。
ProgrammerクラスがRunnableを実装しているクラスです。
実装するにはimlementsキーワードを使用しています。
メソッドのオーバーライド方法は今までと変わりありません。

インターフェースは抽象クラスしか定義できないため、抽象クラスよりも使いにくいと感じるかもしれません。
しかし、実はインターフェースの抽象度が高いため、柔軟性も高くなります。
PHPではクラスを継承するときに多重継承はできないと書きましたが、インターフェースは複数実装することができます。
オブジェクト指向をうまく活用するときにはこの辺りが役に立ちます。
(クラスの継承の場合、継承元(親クラス)で同じ名前のメソッドがある場合、呼び出し時にどちらを呼び出すかが判断できなくなります。
しかし、インターフェースの場合はそもそも抽象メソッドしか定義できないため、メソッド名が重複してもどっちみちオーバーライドするので関係ありません。)

カプセル化

先に作成していたUserクラスを少しすっきりさせておきます。

User.php

<?php

// クラス宣言
class User 
{   
    // プロパティ
    public $age;
    public $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }
}

実はこのクラスいくつか問題があります。
それは、プロパティのアクセス修飾子がpublicになっていることです。
publicでなければ外部からアクセスすることができないため、その方が良いと思うかもしれません。

index.phpを以下のようにしてみます。

<?php
require_once('User.php');
// インスタンスの生成
$user = new User('ポカリ', 20);
$user->age = 300;
$user->selfIntroduction();

実行結果

名前は ポカリ です。年齢は 300 です。

このサンプルでは、インスタンス作成後にプロパティの値を変更されています。
ageは年齢を表すプロパティのため、できれば0~100くらいの値でおさめられる方が望ましいです。
しかし、プロパティがpublicで誰でもアクセス可能となっているため、上記のようなことができてしまいます。
Userクラスを以下のようにするとやりたいことができるようになります。

User.php

<?php

// クラス宣言
class User 
{   
    // プロパティ
    private $age;
    private $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->setName($name);
        $this->setAge($age);
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }

    public function getAge()
    {
        return $this->age;
    }

    public function setAge($age)
    {
        if ($age >= 0 && $age <= 100) {
            $this->age = $age;
        } else {
            $this->age = 20;
        }
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

index.php

<?php
require_once('User.php');
// インスタンスの生成
$user = new User('ポカリ', 20);
$user->setAge(300);
$user->selfIntroduction();

実行結果

名前は ポカリ です。年齢は 20 です。

プロパティのアクセス修飾子をpriavteにし、自クラス以外はアクセスできないようにしました。
そうすると、外部からのアクセスができなくなりますが、その代わりsetAge, getAgeなど、メソッドを用意し、メソッド経由でアクセスできるようにしています。
こうすることで、想定外の値が渡されてきた場合にも適切な値になるように処理したり、あるいはエラーが出るように処理することなどができるようになります。
メソッド経由でのアクセスにすることで、取得はできるがセットはできないようにする、など、柔軟に対応できるようになります。
このように、プロパティを直接アクセスできないようにし、メソッド経由でアクセスできようにすることをカプセル化と呼びます。
不要な情報は隠蔽し、必要な情報のみを公開する、というオブジェクト指向における重要な概念となります。

名前空間

作成するプログラムの規模が大きくなると、使用するクラスやファイルの数も多くなります。
使用するクラスやファイルの数が多くなると、プログラムの役割ごとにカテゴリ分けしておくと便利です。
また、クラスや関数の数が多くなると、既存のライブラリに用意されているクラスや関数の名前と重複してしまう可能性も出てきます。
このような、名前の重複を回避、かつ役割ごとにカテゴリ分けする仕組みとして用意されているのが名前空間です。
名前空間の定義はnamespaceというキーワードを使用します。
名前空間の定義されたファイルを使用する場合は、useキーワードを使用します。

User.php

<?php
// 名前空間の定義
namespace MyProject;

// クラス宣言
class User 
{   
    // プロパティ
    private $age;
    private $name;

    // コンストラクタ
    public function __construct($name, $age)
    {
        $this->setName($name);
        $this->setAge($age);
    }

    // メソッド
    public function selfIntroduction() 
    {
        echo "名前は $this->name です。年齢は $this->age です。";
    }

    public function getAge()
    {
        return $this->age;
    }

    public function setAge($age)
    {
        if ($age >= 0 && $age <= 100) {
            $this->age = $age;
        } else {
            $this->age = 20;
        }
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

index.php

<?php
require_once('User.php');

// 名前空間がついたクラスの利用
use MyProject\User;

// インスタンスの生成
$user = new User('ポカリ', 20);
$user->selfIntroduction();

名前空間の宣言はファイルの先頭(クラス定義や関数定義の前)に行う必要があるので注意してください。
使用する場合は「名前空間\クラス名」とすることで使用することができるのですが、毎回そう書くのは面倒なので、
use 名前空間\クラス名 でとすことでクラス名だけで使用することができるようになります。
名前空間の付け方は任意ですが、ベンダー名\プロジェクト名 のようにつけることが多いです。

型付け

メソッドの引数やプロパティには、型を指定することができます。
TODO

トレイト

コードの断片を共通化したいときに使える仕組み。
TODO

【SQL】ビットマップインデックスの仕組み

ビットマップインデックスの仕組みについてのメモ。
Qiitaでも同じ記事書いてます。

そもそもインデックスとは

ビットマップインデックスの前にそもそもインデックスとは何かについて簡単に説明。
インデックスはざっくりいえばテーブルに対してのSQL(SELECT文)の処理速度を高速化するための仕組み。
日本語でいうと索引。専門書や辞書で後ろの方についている索引と同じイメージ。
辞書の場合、調べたい用語があるときに、1ページから順番に探していくよりも、先に索引で該当の用語が何ページにあるかを確認し、そのページを調べにいくほうが時短確実に時短。
その仕組みをDBMSで実現しているのがインデックス。

インデックスの種類

インデックスにはいくつかの種類がある。

・Bツリーインデックス
・ビットマップインデックス
・ハッシュインデックス

などなど。
DBMSにおいて単にインデックスというと、大抵の場合Bツリーインデックスのことを指す。
本の索引のイメージに近いのがBツリーインデックス。
名前の通り木構造になっていて、検索値を元に該当レコードが格納されているブロックのアドレスを取得する仕組み。
Bツリーインデックスについては詳しくは解説しているサイトや記事がたくさんあるので他を参照ください。

ビットマップインデックスとは

ビットマップインデックスは、検索に用いられるカラムに対して、その値とレコードとのビットマップを使ってレコードを検索するインデックス。
このインデックスが有効なのは検索するカラムのカーディナリティが低い(取りうる値の種類が少ない)場合。

例えば、「名前」「性別」「血液型」という情報を持つユーザーテーブルがあって、10万件のレコードがあったとする。
この場合、名前はほとんどの場合被らないので、レコード毎にほぼ違った値になる。
つまりカーディナリティが高い。
一方、性別は男と女の2種類の値しか取らないし、血液型はA、B、O、ABの4種類しかない。
つまりカーディナリティが低い。

この場合、名前で検索を行った際にはBツリーインデックスによる検索の方が明らかに効率が良いが、 性別や血液型で検索を行う場合、ビットマップインデックスの方が効率が良くなる可能性が高い。

サンプル

言葉だけの説明だと分かりにくいのでサンプルで。

名前、血液型、性別というカラムを持ったユーザーテーブルを考える。
以下の6件のレコードが登録されているとする。

ユーザー

ID 名前 血液型 性別
1 佐藤 A
2 鈴木 B
3 田中 O
4 高橋 A
5 伊藤 O
6 山本 AB

この時、血液型と性別に対するビットマップインデックスは以下のようになる。

血液型のビットマップ

A 1 0 0 1 0 0
B 0 1 0 0 0 0
O 0 0 1 0 1 0
AB 0 0 0 0 0 1
レコード番号 1 2 3 4 5 6

性別のビットマップ

1 0 1 0 1 0
0 1 0 1 0 1
レコード番号 1 2 3 4 5 6

レコード番号というのは、レコードを特定するための値だと思ってください。
Oracleでいうrowid、PostgreSQLでいうTIDみたいなもの。
ここではわかりやすくするためIDと一致するようにしています。
Markdownでのテーブルの性質上一番上の行が太字になっていますが、特に意味はありません。)

ここで例えば

select * from ユーザー where 血液型 = 'A';

というSQLを実行したとする。
ビットマップインデックスが使用される場合、血液型のビットマップのAのビット列を見る。
「100100」 ビット列はこうなっているので、1つ目のレコードと4つ目のレコードが該当する。
結果的に佐藤、高橋のレコードがヒットする。

このように、条件に合致するビット列を見て、値が1になっているレコードを取得するのがビットマップインデクスの仕組み。

ビットマップインデックスのもう一つのメリット

ビットマップインデックスにはもう一つ、OR検索でもインデックスが使用されるという特徴がある。
Bツリーインデックスの場合、性質上OR演算子があるとほとんどの場合インデックスは使用されないので、その点がBツリーインデックスと異なる点。

例えば

select * from ユーザー where 血液型 = 'O' or 性別 = '';

というSQLを実行したとする。

血液型Oに該当するビット列は
「001010」
性別が女に該当するビット列は
「010101」

それぞれのビット列の論理和を取ると
「011111」となる。
この結果、鈴木さんから山本さんまでの5レコードがヒットする。

同じ理屈でAND条件(論理積)も使えるが、AND条件の場合はBツリーインデックス有効なのでメリットというほどでもないかと。

DBMS毎のビットマップインデックスの仕様

Oracle

Oracleの場合、create index するときに「bitmap」のオプションを付けるとビットマップインデックスになる。

PostgreSQL

インデックス作成時には指定することはできないが、実行計画では「Bitmap Scan」が確認できる。
どうやらSQL実行時にワーキングメモリ内にビットマップを作成して検索速度を上げているらしい。

SQL Server

私が調べた限りサポートしてないっぽい。

他のDBMS製品

すみませんが未調査です。知る機会があれば、もしくは情報いただければ更新します。