【PHP日記】

自分の知識不足を悟った末、なるべく1日一回、Webに関する知識を書いていくだけのブログ。

Dockerの概要を把握する 【19日目】

ポートフォリオを作成する上で、Dockerを使った開発に慣れておいた方がいいと思ったのです、ざっくりまとめてみました。

Dockerとは

手元のPCとウェブサーバー上で環境が異なっている場合、作成したプログラムをそのままウェブサーバーに持っていっても動きません。

また、複数人での開発を行う際にソースコードの共有はできても、そもそものOSや実行環境が異なっているせいで、作成したプログラムが正常に動かないということが多々あります。


そこで、これらの環境をうまく合わせる(実行環境自体を共有する)ために用いられるのが「Docker」です。 Dockerはコンテナ型の仮想マシンなので、PC上にもう一つの仮想マシンを作成し、この中に好きな構成で環境を構築することができます。

また、コンテナ型になっているということは、WebサーバーにもDockerをインストールすることで、手元のPC上で作成したコンテナをそのままウェブサーバーへ移動させる(持ってくる)ことができます。

このようにDockerを使うことで、環境の違いを意識することなく、開発を進めていくことができます。


作業の概要

DockerをインストールしたOSが用意できたら、そこにコンテナの設定ファイルやアプリケーションがまとめられた「Dockerイメージ」を用意します。

イメージを用意するには、Dockerを運営しているDocker hubから引っ張ってくるのが一般的ですが、「Docker file」という.txtファイルをを用いることで、設定した項目内容でDockerイメージをビルドすることもできます。


イメージが用意できたらそれを実行していきます。実行すると、環境を構築するための箱である「Dockerコンテナー」ができるので、その中でさまざまな設定を行ったり、アプリケーションのテストを行ったり、自分好みの中身を作っていきます。

また基本的に、コンテナはアプリケーションの実行環境を提供するものであり、開発作業自体はコンテナの外、つまり"開発者のローカル環境"で行われるため、コンテナを使って開発環境を構築し、アプリケーションの実行やテストをコンテナ内で行うのが一般的です。


コンテナーが用意できたら、その状態からまたイメージを作ることもできます。作成したイメージは「Docker hub」などで共有され、その他のOS上でそのイメージを取得し、同じ環境を構築していくことになります。



開発の流れ

Dockerを使った開発の流れは以下のようになります。


 ① アプリケーションの開発
開発者はローカル環境において、エディタやIDE統合開発環境)を使用してアプリケーションの開発を行います。


 ② イメージの作成
「Docker hub」や「Dockerfile」を利用してイメージを用意します。これによりアプリケーションを実行するための環境がセットアップされます。


 ③ コンテナの起動
用意したイメージを元にコンテナを起動します。ローカル環境で作成したソースコードマウントする(コンテナの外にあるデータをコンテナの中で利用できる状態にする)ことで、ローカル環境の変更がコンテナ内にも反映されます。


 ④ 開発とテスト
ローカル環境でコードを編集して保存すると、コンテナ内でアプリケーションが更新されます。開発者はコンテナ内でアプリケーションを実行し、変更をテストします。


 ⑤ イメージの更新と共有
必要に応じて新しいイメージをビルドします、これを共有することで他の開発者や環境でも同じイメージを使用できます。



以上です。



参考文献

https://dotinstall.com/lessons/basic_docker/27701

https://qiita.com/etaroid/items/b1024c7d200a75b992fc

https://dotinstall.com/lessons/basic_dockerdesktop_mac_v2/57501

https://www.kagoya.jp/howto/cloud/container/dockerformac/

名前空間とオートロード 【18日目】

今日はクラス(Class)を使う上で必須の知識である『名前空間』と『オートロード』についてまとめてみます。


名前空間

名前空間(ネームスペース)とは、複数人で作業する際にクラス、関数、定数などの名前の衝突を防ぐための機能です。
1つのコードで同じ名前を持つクラスや関数を定義しても、それらが互いに干渉することなく共存できます。


使い方

使うには、『クラスが定義されているファイルの先頭』で名前空間を指定した後、そのクラスを呼び出すときに、設定した名前空間を記述することで機能します。

// Hello.php
namespace PHPnikki\MyProject;

class Hello {
    // クラスの内容
}
// index.php
require('Hello.php');

$hello = new \PHPnikki\MyProject\Hello('hello');

慣習的に名前空間は「ベンダー名\プロジェクト名」で書かれることが多いです。


useキーワード

名前空間は長くなる傾向があるため、呼び出す側で短く定義し直すこともできます。そのためにはuseキーワードを使います。

// Hello.Class.php
namespace PHPnikki\MyProject;

class Hello {
    public function __construct($text) {
        echo $text;
    }
}
use PHPnikki\MyProject\Hello;
require('Hello.class.php');

$hello = new Hello('hello!');

useで指定された名前空間は、常に「絶対名前空間パス」*1として解釈されるため、その先頭にバックスラッシュ(\)をつける必要はありません

また、下記のようにして別名をつけることもできます。

use PHPnikki\MyProject as mp;
require('Hello.Class.php');

$hello = new mp\Hello('hello');


名前空間が同じ

ちなみに同一の名前空間を持つファイルであればクラス名をダイレクトに使うことができます。

// Hello.php
namespace PHP\Hello;

class Hello {
  public function __construct()
  {
    echo "こんにちは!<br>";
  }
}
// World.php
namespace PHP\Hello;
require_once 'Hello.php';

$hello = new Hello();

こうすることで名前空間を記述する手間を省くこともできます。



オートロード

PHPのオートロードは、クラスファイルを手動でrequireやincludeする代わりに、自動的に必要なクラスファイルを読み込む仕組みです。


spl_autoload_register関数

この関数は、newキーワードでクラスをインスタンス化したときに、そのクラスがまだ呼ばれていなかったら自動で実行される関数です。

spl_autoload_register(function ($class_name) {
    require($class_name . '.php');
});
$hello = new Hello('太郎');

呼ばれていなかったクラス名が$class_nameに渡ってくるので、この場合はHelloという文字列が渡され、require('Hello.php');を実行したのと同じになります。


追記

どうやらここで渡される$class_nameには「名前空間」も含まれたクラス名となるようです。

そのため「namespace project」と名付けられたHelloクラスをオートロードする際は、project\Helloという文字列が渡ってきます。


また引数に関数ではなく、配列を渡すやり方もあります。こちらは配列の第一引数に名前空間を含めたクラスの宣言、第二引数にオートロード(起動)させるメソッドを指定します

spl_autoload_register([
  'namespace\ClassName',
  'methodName'
]);



以上です。


*1:絶対名前空間パスとは、グローバル名前空間からの完全なパスを意味し、常に名前空間の最上位からの参照となる。

PHP雑学⑥ 【17日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その⑥です。


フォルダのパーミッションを777にするとは

『フォルダのパーミッションを777に設定する』というのは、そのフォルダに対して全てのユーザーが読み取り、書き込み、実行の全ての権限を持つように設定することを意味します。


アクセス権限

ここでの「777」という数字は、UNIXLinuxシステムで使用されるパーミッション設定*1の表記方法です。

UNIXLinuxファイルシステムでは、ファイルやディレクトリに対して3種類の基本的な「アクセス権限」があります。

  • 読み取り(Read):ファイルの内容を読み取ることができる。ディレクトリの場合、その中にあるファイルやサブディレクトリの一覧を見ることができる。

  • 書き込み(Write):ファイルの内容を変更することができる。ディレクトリの場合、その中に新しいファイルやディレクトリを作成したり、既存のものを削除したりすることができる。

  • 実行(Execute):ファイルを実行することができる。ディレクトリの場合、そのディレクトリ内に移動することができる。


ユーザグループ

これらのアクセス権限は、以下の3つのユーザーグループに対して設定されます。

  • ファイル所有者(Owner)

  • ファイル所有者のグループ(Group)

  • その他のユーザー(Others)

それぞれのアクセス権限には数値が割り当てられており、読み取りは「4」、書き込みは「2」、実行は「1」のようになっています。

パーミッション設定を行う際はこれらの数値を組み合わせて特定のユーザーグループに対するアクセス権限を設定します。


たとえば『読み取り』と『書き込み』の権限を設定する場合は、『読み取り(4)+ 書き込み(2)= 6』といった具合です。

777」というパーミッション設定では『所有者、グループ、その他のユーザー』に対して、読み取り「4」、書き込み「2」、実行「1」の権限を与えることを示します。


したがって、全てのユーザーがそのフォルダやその中のファイルを自由に操作できますが、セキュリティ上のリスクが伴うため、通常は必要最低限のアクセス権限を設定することが望ましいです。



csvファイルを読み込んだ中身

以下のようなCSVファイルがあったとします。

a, b, c,
d, e, f

そこに以下のスクリプトを実行して、csvファイルをPHPで読み込んだとします。

$csv = new SplFileObject('abc.csv');
$csv->setFlags(SplFileObject::READ_CSV);

このとき$csvに対してforeachを実行すると、$rowにはCSVファイルが1行ずつ配列として格納されます。

foreach ($csv as $row) {    // $rowにはCSVファイルが一行ずつ入る
    if($line[0] === null) continue;
    ....
}

また、if($line[0] === null) continue;という1文で始まっていますね。これが何なのか確かめるために、CSVファイルの中身を見てみます。

[
    [a, b, c],
    [d, e, f],
    [null]
]

最後には"何もない"を意味するnullが差し込まれていますね。つまり先ほどの処理は「最後にnullが入っているけどそれはスキップしてね」という処理を行っています。



以上です。


*1:ファイルやディレクトリに対してどこまでアクセス可能か、ということを示す許可設定。

PHP雑学⑤ 【16日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その⑤です。


require()とinclude()の違い

どちらもPHPにおいて外部ファイルを読み込むための似たような機能を提供しますが、主な違いはエラーの取り扱いにあります。


require()
  • ファイルを読み込んだときに、指定されたファイルが存在しないか読み込めない場合、致命的なエラー(Fatal Error)を返し、スクリプトの実行が中断されます

  • そのため、指定した外部コードが重要であり、プログラムが正しく動くためにどうしても必要不可欠なファイルの場合に使用されます。


include()
  • ファイルを読み込んだときに、指定されたファイルが存在しないか読み込めない場合、警告(Warning)を返し、スクリプトの実行は継続されます

  • 外部のコードがオプションである場合や、ファイルの存在が必要不可欠でない場合に使用されます。


そのため、一般的にはrequireを使用することが推奨されます。外部ファイルが存在しない時点でスクリプトの実行が中断され、プログラマがすぐに対処できるようになるからです。


onceキーワード

ちなみにどちらもonceというキーワードを付けることができ、指定したファイルがすでに読み込まれていたら「スキップ」してくれます。

require_once('partial.php');
include_once('partial.php');



ダブル"とシングル'の違い

ダブルクオーテーション"の場合、文字列中に変数を埋め込んでも、変数内の値を取り出して表示してくれます。

逆にシングルの場合は、文字列中に変数を埋め込むと変数名のまま表示されます。

$name = "太郎くん";
echo "こんにちわ、$name!";    // こんにちわ、太郎くん!
echo 'こんにちわ、$name!';    // こんにちわ、$name!

そのため、中の値を取り出すには.などのキーワードが必要になります。


その他にも、特殊文字の解釈が違います。例えばダブルクオーテーションでは特定のエスケープシーケンス(\n\tなど)が特殊文字として認識されます。

echo "こんにちわ!\tさようなら!";   // 結果『こんにちわ!  さようなら!』

しかしシングルクオーテーションでは、エスケープシーケンスは解釈されず、通常の文字列として表示されます。

echo 'こんにちわ!\tさようなら!';   // 結果『こんにちわ!\tさようなら!』

あとシングルクオーテーションの方が若干処理速度が速いため、一般的にはシングル'を使い、変数の展開などが必要な場合にダブル"を使うらしいですが、この速度の違いはほとんど無視できるレベルなのでどちらでも問題ないようです。



子クラスはデータ型も継承する

まず、クラス名はデータ型の一種です。PHPにおいて、クラスを元にして作られるオブジェクトは親クラスの型となります。

以下の場合、クラス名はMyClassであり、このクラスから生成されるオブジェクトは MyClass型のデータとなります。

class MyClass {
    public $property;
    
    public function myMethod() {
        echo "私はメソッドです。";
    }
}

つまりクラスは継承すると親クラスのデータ型も一緒に引き継ぎます。

例えばParentClassという親クラスを継承したChildClassがあったとして、ChildClassParentClass型も継承しているため、ParentClass型で型チェックを行われる場合でもエラーは起こらず、そのまま実行されます。

$child = new ChildClass('hello', 'world');

function helloWorld(ParentClass $child)
{
  $child->show();
}

helloShow($child);    // エラーは起こらない

このように親クラスの型も継承されるため、将来的にParentClass型を継承したクラスを多く作ったとしても、毎回クラスの型に合わせてコードを修正することが少なくなります。



以上です。


PHP雑学④ 【15日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その④です。


define()とconstの違い

どちらも定数を定義する機能を持っていますが、お互い違った特徴を持っています。


define()
  • 関数として提供され、実行時に使用される

  • 変数や、関数の戻り値を使える

  • クラス定数を定義する際には使えない

  • 定義はグローバルスコープとして登録される


const
  • 構文であり、if文や関数の中では使えない

  • 変数や、関数の戻り値を使えない

  • クラス内でのみ使用できる定数を定義するために使用される

  • 定義は名前空間上で行われる



アクセス修飾子protectedの使い所

protectedPHPのクラス内におけるアクセス修飾子の一つで、指定したメンバが「クラス内またはサブクラス内からアクセス可能である」ことを明示するために使用します。

では実際にどのような場面で使われることになるのか見てみます。以下は親クラスHelloを継承した子クラスHelloWorldの中で、親クラスが持つshowメソッドをオーバーライド(親メソッドを新たに定義し直すこと)する例です。

class Hello {
  // private $text;    // クラス内でしか利用できない
  protected $text;    // 子クラスでの利用のみ可能にしている

  public function __construct($text) {
    $this->text = $text;
  }

  public function show() {
    printf('%s' , $this->text);
  }
}

class HelloWorld extends Hello {
  private $secondText;

  public function __construct($text, $secondText) {
    parent::__construct($text);
    $this->secondText = $secondText;
  }

  public function show() {    // オーバーライド
    printf('%s %s' , $this->text, $this->secondText);
  }
}

$hello = new HelloWorld('hello', 'world');
$hello->show();    // 結果『hello world』

もし$textが親クラスの中でprivateとして定義されている場合、子クラスHelloWorldでオーバーライドしたshowメソッドの中で$textというメンバ変数を利用することはできません。

それに対し、アクセス修飾子をprotectedにして$textを定義することで子クラスでも利用が可能になり、親クラスのメンバ変数を使用することができます。

因みにHelloWorldのコンストラクタ内の$textは、あくまで引数で親クラスの初期化処理をしているだけなので関係ありません。



finalキーワード

親クラスを継承した子クラスの中で、メソッドを新しく再定義することをオーバーライドと言いますが、親クラスの中で、finalキーワードを使ってメソッドを定義することでオーバーライド不可として設定することができます。

class Hello {
  protected $text;

  public function __construct($text) {
    $this->text = $text;
  }

  final public function show() {    このメソッドは子クラスの中で再定義できない
    printf('%s' , $this->text);
  }
}



以上です。



参考文献

https://dotinstall.com/lessons/basic_php_objects/54014


『self::』と『$this』それぞれの違い 【14日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、self::と$thisの違いについてです。


self::と$thisの違い

PHPにおいてself::$thisは、クラス内で使用される2つの異なる参照方法ですが、それぞれ違う用途があります。

結論からいうと、静的プロパティ / メソッドにアクセスする際に使用されるのがself::で、インスタンス化されたオブジェクトを参照する際に使うのが$thisです。


self::

まずself::に関してですが、こちらはクラス自体を表します。インスタンス化されたオブジェクトではないため、クラスレベルに属している静的メソッドおよびプロパティを呼び出す際に使用されます。


静的メソッドとプロパティについて

簡単にいうと、クラスに属していて、特定のインスタンスには属していないものをいいます。

staticキーワードを使用して宣言され、クラスをインスタンス化しなくてもアクセス(参照)できます。

class MyClass {
    public static function Method() {
        // ...
    }
}

MyClass::Method();


$this

$thisは現在のクラスのインスタンス(オブジェクト)を示すため、非静的メソッド(通常のメソッド)やプロパティにアクセスするために使われるキーワードです。


コードの例

以下にそれぞれの簡単なコード例を挙げておきます。

// self::
class MyClass {
  private static $count = 0;    // 静的プロパティ

  public static function countUp() {
    self::$count++;
  }
}
// $this
class MyClass {
  private $name = '';    // プロパティ

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



まとめ

まとめると、静的プロパティ / メソッドにアクセスする際はクラス自体を表す self:: を使い、非静的(通常の)メソッド / プロパティにアクセスする際はインスタンス化されたオブジェクトをを表す $this を使う形になります。



以上です。



参考文献

https://qiita.com/miriwo/items/d33b63029217a675f440

https://www.php.net/manual/ja/language.oop5.static.php


PHP雑学③ 【13日目】

PHPを学び始めた素人目線での、大事だったり大事じゃなかったりする雑知識メモ、その③です。


子クラスは親クラスのコンストラクタを実行しない(限定的に)

親クラス(Super class)を継承した子クラス(Sub Class)に、独自のコンストラクタ(初期化処理)が設定されている場合、自動では親クラスのコンストラクタは実行されません。

そのため、子クラスが親クラスの初期化処理を引き継ぐには、明示的にparent::__construct()を呼ぶことで親クラスのコンストラクタを実行してあげる必要があります。

class ParentClass {
  public function __construct() {
      echo "親クラスが呼び出されました。";
  }
}

class ChildClass extends ParentClass {
  public function __construct() {
      parent::__construct();    // 親クラスのコンストラクタを呼ぶ
      echo "子クラスが呼び出されました。";
  }
}

$obj = new ChildClass();    // 親クラスが呼び出されました。子クラスが呼び出されました



echoを短縮形で書く

HTMLの文中にPHPechoを出力するというのはよくあるため、短縮形が用意されています。

<?php echo 'こんにちわ、今日もいい天気ですね' ?>
<?= 'こんにちわ今日もいい天気ですね' ?>

上の2つはどちらも全く同じ意味になります。



文字実体参照で悪意ある攻撃を防ぐ

PHPでは、クロスサイトスクリプティング*1(通称:XSS)からユーザーを守るために、特殊文字エスケープ*2する必要があります。

<?php $hello = "<script>alert('Hello Dark World');</script>"; ?>
<?= $hello ?>

例えば、上の行で言うところの <script> 部分をエスケープすることによって、アラートを起動させることなく無害なテキストとして表示させます。
エスケープするには htmlspecialchars() を用います。

<?php $hello = "<script>alert('Hello Dark World');</script>"; ?>
<?= htmlspecialchars($hello, ENT_QUOTES, 'UTF-8') ?>

ENT_QUOTESは、htmlspecialchars関数に設定するフラグで、クオーテーションをエスケープするための指定です。
UTF-8はそのWebページの文字コードを指定しています。


結果として、Webページでは以下の文字列が出力されます。

<script>alert('Hello Dark World');</script>

このように、攻撃者が埋め込んだ悪意のあるスクリプトが実行されないよう、どんな値であれHTMLへの埋め込みを行う際は、文字実体参照エスケープしたほうが良いでしょう。


以上です。


*1:悪意のあるコードをWeb閲覧者に実行する

*2:HTMLタグではなく文字として認識させるための処理。