Dockerfile:FROM, RUN COPY, ENV, WORKDIR, ENTRYPOINT

  • DSL:Docker独自の言語

Dockerfileに、インストラクション(命令)を書く。

Dockerfileの上から順に、実行されていく。

   * インストラクション:FROM, RUN, CMD, etc.

  • FROM

Dockerfile の一番上に記載する。
Dockerイメージのベースになるイメージを指定する。
指定するイメージは、Dockerhub に載っているものを使うのが通説。

  • RUN

Dockerイメージをビルドする時に、コンテナ内で実行するコマンドを書く。

  • COPY:COPY <ローカルfile or dir名> <コピー先>

ローカルホスト(自分のpc)で作ったソースコードをdockerイメージに送る。
COPY index.html /var/www/html/ :ローカルの index.html をdocker の /var/www/html/ 直下にコピペする という意味
因みに、「apache」 の仕様上、デフォルトでは/var/www/html/の直下にあるファイルを読み込むようになっている。

FROM ubuntu:22.10

RUN apt update \
  && apt install -y apache2

COPY index.html /var/www/html/

CMD [ "apachectl", "-DFOREGROUND" ]
  • ENV:環境変数:その OS 上の全てで使う変数。機密事項もここに書いたりする。

以下の例のように、APACHE_CONFDIR=/etc/apache2$APACHE_CONFDIRとして、変数として使える。

FROM ubuntu:22.10

ENV APACHE_CONFDIR=/etc/apache2
ENV APACHE_ENVVARS=$APACHE_CONFDIR/envvars

RUN apt update \...

コンテナ内で、確認もできる。

***コンテナ内

root@XXXXXXX:/# echo $APACHE_CONFDIR
/etc/apache2
root@dc1902b02536:/# echo $APACHE_ENVVARS
/etc/apache2/envvars

目的としては、$ docker container exec -it apache2 /bin/bashみたいに、コンテナの中で、作業をするときに、cd XXXコマンドで、ディレクトリの移動を減らすため。

FROM ubuntu:22.10

ENV APACHE_CONFDIR=/etc/apache2
ENV APACHE_ENVVARS=$APACHE_CONFDIR/envvars

WORKDIR /var/www/html <- ここ!

RUN apt update \...

因みに、WORKDIR /var/www/html を指定すると、以下みたいな状態になる。

% docker container exec -it apache2 /bin/bash
root@XXXXX:/var/www/html# pwd
/var/www/html

と、最初から今いるディレクトリを表してくれているかなと。root@XXXXX:/var/www/html#

  • ENTRYPOINT:必ず実行されるコマンド。CMDと近いが、違うところがある。

ほぼ同じ。
違いは、CMD は、上書きできるが、ENTRYPOINT は、上書きされない。
また、CMDとENTRYPOINT は、一緒に使えて、CMD が ENTRYPOINTの引数になる。

$ docker run 引数とできる。その際に
$ docker run bbb とすると、「bbb」が引数として追記される。
$ docker run bbb とすると、「bbb」に全て上書きされる。

例1:
Dockerfile の中身が、CMD [ "echo", "hoge" ] の場合

$ docker docker container run -d practice/apache2:latest echo test
test

上記のコマンドを実行すると、「echo test」に CMD が上書きされ、「test」が出力される。

しかし、

例2:
Dockerfile の中身が、ENTRYPOINT [ "echo", "hoge" ] の場合

$ docker container run -d practice/apache2:latest test
hoge test

上記のコマンドを実行すると、「test」が追加の引数となり、「hoge test」が出力される。

さらに、
例3:
Dockerfile の中身が、以下の場合

ENTRYPOINT [ "echo", "hoge" ] 
CMD ["addCMD"]

$ docker container run -d practice/apache2:latest
hoge addCMD

となる。

Dockerfile 作成手順

  1. ベースイメージを決める

  2. コンテナの中に入って、シェルを実行してみる

  3. 必要な作業をする

  4. Dockerfile に記載する

  5. イメージとコンテナを作り直す

2~5 を繰り返し行い、Dockerfile を作成していく

PHP:クラスを使ってみたpart3

思ったより時間食った。。。まあ、しゃーなしやな。勉強勉強!そして進化しているのは分かるから良き!!!

今回の成長ポイント:
  • __construct() の新しい使い方。

  • __constrcut()の前に、変数を宣言して使う方法。

    • private array $cards;:class Cards でも、class OpenCards でも $cards は array型と分かっているから、型宣言をしている

    • 一方で、 class OpenCards の private $cardsArr は、new Cards($suit); で、オブジェクトが返ってきているので、int や array などの 型 宣言していない

/*
何枚開けたときに全種類そろったかを出力してください。
ただし、全て開けても全種類そろわないときは、"unlucky" と出力


買ったカードの枚数 $boughtMuch、カードの種類数を示す整数(最大値) $suit

====考察
forループで、値を取得していく。出てくる数字は、バラバラ。
$suitが最大値。
1 ~ $suit以下 の数字の配列$cards を作る。
for ループで数字を取得していって、$cards の中にある数字と一致したら、$cards の中のその数字を消していく。
最終的に、count($cards) === 0 の時、全種類そろうことになる。
*/
    class Cards
    {
        private array $cards;
        public function __construct(private int $suit)
        {
            $this->suit = $suit;
            for ($i = 1; $i <= $this->suit; $i++) {
                $this->cards[] = $i;
            }
        }
        
        public function cards(): array
        {
            return $this->cards;
        }
    }
    

    class OpenCards
    {
        private $cardsArr;
        private array $cards;
        public function __construct(private int $boughtMuch, private int $suit)
        {
            $this->boughtMuch = $boughtMuch;
            $this->suit = $suit;
            $this->cardsArr = new Cards($suit);
            $this->cards = $this->cardsArr->cards();
        }

        public function openCards(): mixed
        {
            for ($i = 1; $i <= $this->boughtMuch; $i++) {

                fscanf(STDIN, "%d", $number);
                $key = array_search($number, $this->cards);

                if ($key !== false) {
                    unset($this->cards[$key]);
                }
                
                if (count($this->cards) === 0) {
                    return $i;
                } elseif ($i === $this->boughtMuch) {
                    return "unlucky";
                }
            }
        }
    }
    fscanf(STDIN, "%d %d", $boughtMuch, $suit);
    $openCards = new openCards($boughtMuch, $suit);
    $result = $openCards->openCards();
    echo $result;
本当は、やりたかったけど、できなかったこと

グローバルスコープでは、以下の3行のコードで終わらしたかった。

$openCards = new openCards();
$result = $openCards->openCards();
echo $result;

以下のようにして、$boughtMuch, $suit を取得して、class Cards$suitを投げて使いたかったけど、そうすると、$suit の扱いにこんがらがって、昨日と同じようにグローバルスコープで取得してから使うことにした。

public function openCards(): mixed
{
   fscanf(STDIN, "%d %d", $boughtMuch, $suit);
   for ($i = 1; $i <= $this->boughtMuch; $i++) { ... }
}

p.s. 次は、ポリモーフィズムとかそういうオブジェクト指向の書き方の原則?を意識して書くようにしていけば、かなり今の段階での完成形に近づくかな〜

docker-compose.yml:volumesについて

volumes とは

dockerコンテナの中にある「保存領域(ストレージみたいな)」。

コンテナが削除されたら、その中身全てが削除される。ただ、データベースのデータを残したい場合に、volumes にデータを保存するようにすることで、それを叶える。

また、ホストマシン(自分のpc)とコンテナでデータを共有する時に使う。
以下の例で考えると、ホストマシンの「/src」ディレクトリの中身と、dockerコンテナの「/var/www/html」の中身が同期される。

同期されると嬉しい理由は、いちいちビルドし直す手間が省けるようになるため!!

docker-compose.yml
volumes:
    - ./src:/var/www/html

./src: の「:」が何を意味するのかというと、
「:」は境界線みたいなもので、左側と右側を区別している。

・左側(./src)は、ホストマシン(自分のpc)のディレクトリ
・右側(/var/www/html)は、dockerのコンテナ内のディレクトリ
をそれぞれ指している。

右側は、コンテナ内で保存(共有)しときたい ディレクトリ を書く。
左側は、「docker-compose.yml」と同じディレクトリ(階層)を書くこと。
参考サイト

docker-composeでvolumesを設定する

・chat-GPT4

p.s. マウント:接続して、使えるようにすること

PHP:クラスを使ってみた part2

メソッドの中で、同じクラスの別のメソッドを呼ぶ時は、$this->が必要!という学びがあった。

3つのクラスを1つのクラスで呼び出せるように、書き直した!

2つの円の中心の座標を (xc, yc)、半径をそれぞれ r_1, r_2 (r_1 < r_2) とします。

暴風域にいる条件
r1^2 <= (x - xc)^2 + (y - yc)^2 <= r2^2

n は与えられる人の数を表す整数

1行目:xc yc r_1 r_2

・1 ≦ n ≦ 100
・1 ≦ r_1 < r_2 ≦ 100
・-100 ≦ xc, yc, x_i, y_i ≦ 100
*/
    class CalTyphoonSize
    {
        public function __construct(private int $radius1, private int $radius2)
        {
            $this->radius1 = $radius1;
            $this->radius2 = $radius2;
        }
        
        // 小さい半径の2乗の計算
        public function calSmallSize(): int
        {
            return pow($this->radius1, 2);
        }
        
        // 大きい半径の2乗の計算
        public function calLargeSize(): int
        {
            return pow($this->radius2, 2);
        }
    }
    
    class RangeOfXAndY
    {
        public function __construct
           (
              private int $x, 
              private int $y, 
              private int $xC, 
              private int $yC
            )
        {
            $this->x = $x;
            $this->y = $y;
            $this->xC = $xC;
            $this->yC = $yC;
        }
        
        public function rangeOfXAndY(): int
        {
            // 他のメソッドを呼び出す時は、$this-> が必要!
            $calX = $this->calX($this->x, $this->xC);
            $calY = $this->calY($this->y, $this->yC);
            return $calX + $calY;
        }
        
        
        private function calX(int $xPeople, int $xTyphoon): int
        {
            //  (x - xc)^2
            $calculatedX = abs($xPeople - $xTyphoon); 
            return pow($calculatedX, 2);
        }
        
        private function calY($yPeople, $yTyphoon): int
        {
            $calculatedY = abs($yPeople - $yTyphoon); 
            return pow($calculatedY, 2);
        }
    }
    
    class IsZone
    {
        public function __construct
           (
              private int $calculatedR1, 
              private int $calculatedR2, 
              private int $sumXY
           )
        {
 
            $this->calculatedR1 = $calculatedR1;
            $this->calculatedR2 = $calculatedR2;
            $this->sumXY = $sumXY;
        }
        
        public function isZone():bool
        {
            return ($this->calculatedR1 <= $this->sumXY && 
                    $this->sumXY <= $this->calculatedR2) ? true : false;
        }
    }

    class TyphoonZone {
        private $calTyphoonSize;
        private $rangeOfXAndY;
        private $isZone;
        
        public function __construct
            (
               int $radius1, 
               int $radius2, 
               int $x, 
               int $y, 
               int $xC, 
               int $yC
            ) {
                 $this->calTyphoonSize = new CalTyphoonSize($radius1, $radius2);
                 $this->rangeOfXAndY = new RangeOfXAndY($x, $y, $xC, $yC);
                 $sizeOfSmallTyphoon = $this->calTyphoonSize->calSmallSize();
                 $sizeOfLargeTyphoon = $this->calTyphoonSize->calLargeSize();
                 $resultRangeOfXAndY = $this->rangeOfXAndY->rangeOfXAndY();
                 $this->isZone = new IsZone($sizeOfSmallTyphoon, $sizeOfLargeTyphoon, $resultRangeOfXAndY);
        }
        
        public function isInZone(): bool {
            return $this->isZone->isInZone();
        }
    }

    fscanf(STDIN, "%d %d %d %d", $xC, $yC, $radius1, $radius2);
    fscanf(STDIN, "%d", $numberOfPeople);
    for ($i = 0; $i < $numberOfPeople; $i++) {
        fscanf(STDIN, "%d %d", $x, $y);
        $typhoonZone = new TyphoonZone($radius1, $radius2, $x, $y ,$xC, $yC);
        if ($typhoonZone->isInZone()) {
            echo 'yes' . PHP_EOL;
        } else {
            echo 'no' . PHP_EOL;
        }
    }

PHP:クラスを使ってみた

そろそろ関数は慣れてきたので、「クラス」を使い慣れたいなと思い、わざわざ遠回りして、クラスを作って、使い、答えを出してみた。

最初の解答で分かったこと、

  • クラスのスコープ外にある、foreach のなかで、class CalSumが元になる インスタンス を作成(インスタンス化)をしている。$calSum = new CalSum($v);

  • それにより、class CalSumにあるメソッドcalSum()が使えるようになった。$calSum->calSum();

  • また、$calSum = new CalSum($v);$calSum2 = new CalSum($v); は違うインスタンスを生成していることがわかった。元になる クラス(class CalSum) は、同じだけど。

/*
・連続する "<" の数が整数の 10 の位を表し、
それに続く連続する "/" (スラッシュ) の数が整数の 1 の位を表す

・3 ≦ (E の長さ) ≦ 100
*/

$result = 0;
$result2 = 0;
$paizaNotation = str_split(trim(fgets(STDIN)));

class CalSum
{
    private int $amount = 0;
    public function __construct(private string $v)
    {
        $this->v = $v;
    }
    
    public function calSum():int
    {
        if ($this->v === '/') {
            return $this->amount += 1;
        } elseif ($this->v === '<') {
            return $this->amount += 10;
        }
    }
}

foreach ($paizaNotation as $v) {
    if ($v !== '+') {
        $calSum = new CalSum($v);
        $calSum2 = new CalSum($v);
        $result += $calSum->calSum();
        $result2 += $calSum2->calSum() - 5;
    }
}
echo $result . ' ' . $result2;  // 入力値:</</   -> 結果:22  2

$result2 = 10 - 5 + 1 - 5 + 10 - 5 + 1 - 5 = 2
これより、$calSum と $calSum2 は、別物の インスタンス であるとわかる。
ちなみに、`$result2 += $calSum->calSum();` としたら、
結果は、$result と同じ 22 が出力される。
よって、$calSum と $calSum2 では、それぞれ互いに影響を与えていないことがわかる。

$calSum = new CalSum($v); インスタンス化をして、インスタンスメソッドcalSum()を扱えるようにしている。
まあ、初期段階の理解と操作力を得る上では、この程度で良いだろう。

インスタンスメソッドは、特定のインスタンスに対する操作を行います。」 とのことなので、この時の「特定のインスタンス」とは、「$calSum = new CalSum($v)」でしょう。

別解1
class CalSum
{
    private int $amount = 0;
    
    public function calSum(string $v):int
    {
        if ($v === '/') {
            return $this->amount += 1;
        } elseif ($v === '<') {
            return $this->amount += 10;
        }
    }
}

foreach ($paizaNotation as $v) {
    if ($v !== '+') {
        $calSum = new CalSum();
        $result += $calSum->calSum($v);
    }
}

別解1 で分かりやすいなと思ったのは、$amountの値が、維持されているということ。
foreach で 1文字ずつ $calSum = new CalSum(); によって生成された インスタンス の メソッド(calSum()) に投げて、計算をしているが、$amountの値が1つ前までの処理(文字) の合計値を維持しているところが、分かりやすいかなと。

理解度が上がってきているのが、分かる!!!

PHP:配列を2つ用意して、ブラウザのページ遷移の仕組みを真似てみた。

今回のネックは、

戻るボタンを2回以上押して、他のページに飛び、
また、戻るボタンを押した時のページ遷移先を表現すること

上記の問題を解決するのに、配列を2つ用意した。

イメージ:末尾が現在地

1回目:
$result = [1, 2, 3, 4, 5, 6]
$result2 =  [1, 2, 3, 4, 5, 6] <- 末尾を削除する配列

戻るボタンを押した!
$result = [1, 2, 3, 4, 5, 6, 5]
$result2 =  [1, 2, 3, 4, 5]

2回目:
$result = [1, 2, 3, 4, 5, 6, 5, 7]
$result2 =  [1, 2, 3, 4, 5, 7]

戻るボタンを押した!
$result = [1, 2, 3, 4, 5, 6, 5, 7, 5]
$result2 =  [1, 2, 3, 4, 5]

戻るボタンを押した!
$result = [1, 2, 3, 4, 5, 6, 5, 7, 5, 4]
$result2 =  [1, 2, 3, 4]

3回目:
$result = [1, 2, 3, 4, 5, 6, 5, 7, 5, 4, 8]
$result2 =  [1, 2, 3, 4, 8]
配列を2つ用意して、2つともページ遷移後のページ名を格納する。
ただし、1つの配列は、use the back button(戻るボタンのクリック) 時、末尾の値を削除する。
そして、その配列の新しい最後の値を別の配列の末尾に追加する(前のページに戻る)。
*/
    $n = (int) trim(fgets(STDIN));
    // 全てのクエリを持つ配列。$queryArr
    $queryArr = [];
    // 答えになる、ページ名を格納していく、配列。初期値は、'blank page'になる。
    $resultArr = ['blank page'];
    // こちらの配列は、back btn がでてきたら、最後の要素を削除する。
    $resultArr2 = ['blank page'];
    // $n === 1 なら、即終了。
    if ($n === 1) {
        echo 'blank page';
        die();
    }
    
    for ($i = 0; $i < $n; $i++) {
        $queryArr[] = trim(fgets(STDIN));
    }
    
    // $n === 2 の時も、すぐに回答が作れる.
    if ($n === 2) {
        $secondePage = 1;
        $secondePageName = judgePageName($queryArr, $secondePage);
        echo 'blank page' . PHP_EOL;
        echo $secondePageName;
        die();
    }
    
    // 作業回数を数えとく、変数 $count を作る。
    // 初期値が固定値であり、2つめのクエリが、必ず、blank page 以外のページになるため
    // $count = 1から始める。$count === $n - 1 になったら、終了。
    $count = 1;
    
    // 1つ前のページに戻るか判断する
    for($i = $count; $i < $n; $i++) {
        if (reset(explode(' ', $queryArr[$i])) === 'use') {
            array_pop($resultArr2);
            // 1つ前のページ名を$resultArr に格納する
            $resultArr[] = end($resultArr2);
            $count++;
        } else {
            $pageName = judgePageName($queryArr, $i);
            $resultArr[] = $pageName;
            $resultArr2[] = $pageName;
            $count++;
        }
    }
    
    function judgePageName(array $queryArr, int $count): string
    {
        // 3つ目の要素以降を取得する。それが、ページ名になる
        $startIndex = 2;
        $slicedArr = array_slice(explode(' ', $queryArr[$count]), $startIndex);
        $pageName = implode(' ', $slicedArr);
        return $pageName;
    }
    
    foreach ($resultArr as $v) {
        echo $v . PHP_EOL;
    }

reset() :配列の最初の値を取り出す関数。

p.s. クラスを使っていきたい。クラスの基本的な動作を理解したが、操作できるレベルに到達したい。。

PHP:配列の最初のkeyを取り出す、配列の最初と最後の要素を取得。

  • array_key_first:配列の最初のkey を取得する

$keys = array_key_first($rainAll);

  • reset(), end() :配列の最初と最後の要素を取得。

reset($resultDays) . ' ' . end($resultDays);

getSumRain():ここが成長を感じた点

平均降水確率を作成する際、合計の降水確率が欲しい。それを別の関数(getSumRain)で求めることにした。

  • 考え:旅行の日数分、forループを回す

ただし、$i = 0 で回すと、毎回、$i = 0 に初期化されてループが回るので、意図した合計値を求められない。

getAvgRainsAndDays関数で、$i は、0, 1, 2, ... と足されていくので、それをgetSumRain関数でも連動するように、$count を作った。

  • $i < $tripDays + $count

上記のように設定した理由は、毎回、旅行日数分ループを回したいが、$i の初期値が getSumRain関数が呼び出されるたびに、$count 分増えるので、同じく、上限値を$count分、毎回増やして対応するっていう意図。

  function getSumRain(array $rainAll, int $tripDays, int $dayoffs, int $count): int
    {   
        $sum = 0;
        for($i = $count; $i < $tripDays + $count; $i++) {
            $sum += $rainAll[$i];
        }
        return $sum;
    }
    
    // $tripDays分の平均降水確率とその各日付 の配列
    function getAvgRainsAndDays(array $rainAll, int $tripDays, int $dayoffs, array $daysAndRains): array
    {
        $rainAvg = [];
        $avgOfDays = [];
        // 日付の配列を生成
        $daysKey = array_keys($daysAndRains);
        $count = 0;
        for ($i = 0; $i < $dayoffs; $i++) {
            if (($i + $tripDays) <= $dayoffs) {
                $sum = getSumRain($rainAll, $tripDays, $dayoffs, $count);
                $count++;

^^^^^^^^^^^^

fscanf(STDIN, "%d %d", $dayoffs, $tripDays);
    
    // 休日が、1日の場合
    if ($dayoffs === 1) {
        [$a, $b] = explode(" ", trim(fgets(STDIN)));
        echo $a . ' ' . $a;
        die();
    }

    $daysAndRains = [];
    for($i = 0; $i < $dayoffs; $i++) {
        [$day, $rain] = explode(" ", rim(fgets(STDIN)););
        $daysAndRains[$day] = $rain;
    }
    
    // $rainAllは、降水確率だけを格納した配列。
    $rainAll = [];
    foreach ($daysAndRains as $day => $rain) {
        $rainAll[] = (int) $rain;
    }
    
    // 旅行日数が、1日の場合
    if ($tripDays === 1) {
        asort($rainAll);
        // 降水確率の最小値のkeyをとりだし、それに該当する日付を取得する
        $keys = array_key_first($rainAll);
        $result = reset(array_slice($daysAndRains, $keys, 1));
        echo $result . ' ' . $result;
        die();
    }
    
    function getSumRain(array $rainAll, int $tripDays, int $dayoffs, int $count): int
    {   
        $sum = 0;
        for($i = $count; $i < $tripDays + $count; $i++) {
            $sum += $rainAll[$i];
        }
        return $sum;
    }
    
    // $tripDays分の平均降水確率とその各日付 の配列
    function getAvgRainsAndDays(array $rainAll, int $tripDays, int $dayoffs, array $daysAndRains): array
    {
        $rainAvg = [];
        $avgOfDays = [];
        // 日付の配列を生成
        $daysKey = array_keys($daysAndRains);
        $count = 0;
        for ($i = 0; $i < $dayoffs; $i++) {
            if (($i + $tripDays) <= $dayoffs) {
                $sum = getSumRain($rainAll, $tripDays, $dayoffs, $count);
                $count++;
                $avg = $sum / $tripDays;
                $rainAvg[] = $avg;
                // 各平均降水確率の日付を取得し、配列に格納する。
                $avgOfDays[] = array_slice($daysKey, $i, $tripDays);
            } else {
                return array($rainAvg, $avgOfDays);
            }
        }
    }
    $avgRainsAndDays = getAvgRainsAndDays($rainAll, $tripDays, $dayoffs, $daysAndRains);
    list($avgOfRains, $daysOfAvg) = $avgRainsAndDays;
    
    // 平均降水確率を昇順に並び替える。ただし、keyを維持したまま。
    asort($avgOfRains);
    
    // 最初のkeyを取得する
    $resultOfKey = array_key_first($avgOfRains);
    
    $resultDays = $daysOfAvg[$resultOfKey];
    
    echo reset($resultDays) . ' ' . end($resultDays);