PaizaラーニングのPHP入門で学習したら、とてもわかりやすかった

よこのじ(@yokonoji_work)です。

Paizaラーニング「PHP入門編 (全10レッスン)」を利用して、PHPを勉強してみました。勉強ノートとして内容を残しておきます。

Paizaラーニングは、動画でプログラミング学習ができるサービスです。霧島京子ちゃんが聴き取りやすい声・発音でレッスンしてくれますので、楽しく学習が続けられますよ。

Paizaラーニング:https://paiza.jp/works

861-paiza-php-phpとは

PHP入門編1: PHPをはじめよう

PHPとは、Webサービス、Webアプリケーションのような動的なWebページを実現するためのプログラミング言語です。

標準出力は次のように記述します。PHPの処理は<?php と ?>で囲みます。

<?php

echo "hello world";

?>

PythonやRubyなどをやっていると、セミコロン(;)を忘れがちなので注意です。

コメントの付け方

//コメント
/*
複数行のコメント
複数行のコメント
*/

変数を使うときは、先頭にドルマーク($)を付けます。複数の変数を表示させるときはドット(.)でつなげます。スペースを入れたり、文字列を挟むときはダブルクォーテーション(”)で挟んで入力します。

$output1 = "hello";
$output2 = "world";
echo $output1." ".$output2;

ランダムな数値を扱うときは、次のように記述します。この例では1~6の整数がランダムに出力されます。

echo rand(1,6);

ランダムな値があれば、サイコロを作ることができますね。

$rand_num = rand(1,6);
echo "サイコロの値は".$rand_num."です。";

演算子の記述は次のとおりです。

// 代入演算子
$a = "テキスト";
$b = 2;

// 代数演算子 + - * / %
echo (1 + 2) * $b;

// 単項演算子
$b++;

表示出力の改行は、バックスラッシュ(もしくは円マーク)とn(\n)です。

$apple = 350;
$apple_num = 5;

echo "リンゴの値段:".$apple."円\n";
echo "リンゴを買う数:".$apple_num."個\n";

$total = $apple * $apple_num;
echo "合計".$total."円です。";

PHP入門編2: 条件によって処理を変えてみよう

2章は条件分岐についてです。

if文による条件分岐は次のように記述します。

$n = 1;

if($n == 1){
    // 条件式が成り立ったときの処理
    echo "好き";
}elseif($n == 2){
    // 条件式が成り立ったときの処理
    echo "どちらとも言えない";
}else{
    // 条件が成り立たなかったときの処理
    echo "嫌い";
}

elseifのようにelseとifをスペースで区切らずに続けて書くことに注意が必要です。

比較演算子(==, >, <, >=, <=, !=)を使って条件分岐させることもできます。

$hour = 13;

if($hour < 12){
    echo "午前中";
}elseif($hour == 12){
    echo "正午";
}elseif($hour > 12){
    echo "午後";
}

ランダム関数と条件分岐を利用して、RPGの戦闘シーンのような表示を作ることもできます。

// スライムと戦っている。1〜10のサイコロをふって、
// 1〜5まではサイコロの目がダメージとなり
// 6以上でクリティカルヒット、100のダメージを与える事が出来る
$hit = rand(1,10);
echo $hit;
if($hit < 6){
    echo "スライムに".$hit."のダメージを与えた!";
}else{
    echo "クリティカルヒット!スライムに100のダメージを与えた!!";
}

ランダム関数より得られた数値によって、与えるダメージが変わったり、クリティカルヒットが出たりします。

条件分岐ではありませんが、この章の最後では西暦から平成何年かを計算するプログラムを作成します。

$year = date("Y");
echo "西暦".$year."年は";

// 1989年が平成1年なので、平成年は西暦-1988
$heisei = $year -1988;
echo "平成".$heisei."年";

date関数は、年月日や時刻の情報を取得できる関数です。

$date = date("Y/m/d H時i分s秒");
echo $date;  // 2018/11/23 10時56分12秒
_____________________________________________
$date = date("Y");  // 年4桁 2018
$date = date("y");  // 年2桁 18

$date = date("m");  // 月 01~12 先頭に0が付く
$date = date("n");  // 月 1~12 先頭に0が付かない

$date = date("d");  // 日 01~31 先頭に0が付く
$date = date("j");  // 日 1~31 先頭に0が付かない

$date = date("h"); // 時 12時間表記 01~12 先頭に0が付く
$date = date("g"); // 時 12時間表記 1~12 先頭に0が付かない
$date = date("H"); // 時 24時間表記 00~23 先頭に0が付く
$date = date("G");  //  時 24時間表記 0~23 先頭に0が付かない

$date = date("i");  // 分 00~59 先頭に0が付く

$date = date("i");  // 秒 00~59 先頭に0が付く

$date = date("a"); // amまたはpm
$date = date("A"); // AMまたはPM

月を英語表記で返したり、うるう年か判定することもできます。詳細はPHPのマニュアルをご確認ください。

PHP マニュアル date

ちなみに、時刻表示の確認を行っていると、の部分がちょうど9時間ずれていることに気付きました。調べてみると、原因はタイムゾーンがJST(日本標準時)ではなくUTC(協定世界時)になっているからでした。

// タイムゾーンを確認
$date = date("e"); // UTC

JSTはUTC+09:00ですので、UTCで時刻取得すると9時間遅い時間が表示されることになります(日本時間で19時に時刻取得したとき、タイムゾーンがUTCだと10時を取得して表示する)。

これを改善するためには、date_default_timezone_setでタイムゾーンを日本に設定します。次のプログラムはUTCで時刻取得した後に、日本時間に設定して、再度時刻取得したものです。

echo "タイムゾーン".date("e")."の場合、".date("H")."時\n";
// 出力結果:タイムゾーンUTCの場合、01時

// タイムゾーンを日本にセットする
date_default_timezone_set('Asia/Tokyo');

echo "タイムゾーン".date("e")."の場合、".date("H")."時\n";
// 出力結果:タイムゾーンAsia/Tokyoの場合、10時

このとおり、タイムゾーンの設定で日本時間で正しく表示できるようになりました。

date_default_timezone_set

サポートされるタイムゾーンのリスト

【PHP】取得した現在日時が9時間ずれているときは

PHP入門編3: ループ処理を学ぶ

標準入力は、fgets(STDIN)で行います。1行の入力を取り込むことができますが、入力の改行も取り込むので、echoで改行も付けていると2重に改行が入ります。

/* 入力
hello world
hello world2
*/
$input = fgets(STDIN);
$input2 = fgets(STDIN);
echo "標準入力値:".$input."\n";
echo "標準入力値:".$input2."\n";
/* 出力
標準入力値:hello world

標準入力値:hello world2
*/

入力データから改行を取り除くには、trim関数を使用します。

$input = trim(fgets(STDIN));

それではループ処理を行います。while文で1~10の数字を出力するには、次のように記述します。

$i = 1;
while($i <= 10){
    echo $i."\n";
    $i++;
}

HTMLタグと合わせて、プルダウンで1~50歳の年齢入力フォームを作ってみます。このようにwhile文でループさせれば簡単に作れますね。

<select name="age">
<?php
$i = 1;
while($i <= 50){
    echo "<option>".$i."歳</option>";
    $i++;
}
?>
</select>

次にfor文です。while文よりスマートな記述ができます。

// 1~10の表示
for($i=1; $i<=10; $i++){
    echo $i."\n";
}

// 10~1の表示
for($i=10; $i>0; $i--){
    echo $i."\n";
}

西暦年と平成年の対応表もループ処理なら簡単です。

for($year=1989; $year<=2018; $year++){
    $heisei = $year - 1988;
    echo "西暦".$year."年は".$heisei."年です。\n";
}
// 出力
西暦1989年は1年です。
・
・
・
西暦2018年は30年です。

PHP入門編4:配列の基礎、explodeを学ぶ

順番があるデータやエクセルなどの複数行データは配列で扱うのが便利です。

861-paiza-php-配列

配列への代入は次のように行います。また、配列の中身をすべて出力する場合は、print_r関数を使います。

$name_1 = "勇者2";
$name_2 = "魔法使い";

$team = array("勇者", "魔法使い", $name_1);
print_r($team);
// 出力
Array
(
    [0] => 勇者
    [1] => 魔法使い
    [2] => 勇者2
)
  • 配列の特定の要素を取り出す場合は、$team[2]のようにkey番号を指定します。
  • 配列に要素の追加を行う場合は、$team[]に要素を代入すると最後尾に追加されます。
  • $team[3]とkey番号を指定して代入すると、要素を上書きできます。
  • 配列から要素を削除する場合は、unset関数を使用します。削除されたkeyは歯抜けとなります。
$team = array("勇者", "魔法使い", "オオカミ");
$team[] = "ドラゴン";
$team[] = "ドラゴン";
$team[3] = "遊び人";
unset($team[2]);
print_r($team);
echo $team[2];

// 出力
Array
(
    [0] => 勇者
    [1] => 魔法使い
    [3] => 遊び人
    [4] => ドラゴン
)

配列には、arrayを使わずに格納することもできます。

$team = array("勇者", "魔法使い", "オオカミ");
$team = ["勇者", "魔法使い", "オオカミ"];

explode関数は、区切り文字で文字列を分割して配列に代入する関数です。次の例ではカンマ(,)で区切っています。また、配列の要素数を調べるにはcount関数を使用します。

// 入力
勇者,魔法使い,ドラゴン,オオカミ,遊び人

$input = trim(fgets(STDIN));
$array = explode(",", $input);
print_r($array);
echo count($array);

// 出力
Array
(
    [0] => 勇者
    [1] => 魔法使い
    [2] => ドラゴン
    [3] => オオカミ
    [4] => 遊び人
)
5

ループ処理を利用して、複数行の入力データをある分だけ取得して配列に格納することもできます。

次の例では、入力に空の行(改行のみ)がある場合にそこで処理が停止しないように、入力データを取り込むときにはtrimで改行を除去せずに配列に代入するときにtrimで改行を除去しています。これにより、空の行(改行のみ)がある場合でもwhile文で処理を行いますので、入力の末尾までデータを取り込むことができます。

// 入力
勇者
魔法使い

ドラゴン

$input = fgets(STDIN);
while($input){
    $array[] = trim($input);
    $input = fgets(STDIN);
}
print_r($array);

// 出力
Array
(
    [0] => 勇者
    [1] => 魔法使い
    [2] => 
    [3] => ドラゴン
)

入力とループの処理をスマートに書くこともできます。

while($input = fgets(STDIN)){
    $array[] = trim($input);
}

こちらは、配列を使ったランダムくじ引きです。入力データの個数に応じて、0~個数-1の数字をランダムに出力して、その数をkeyとして対応する要素を出力しています。

// 入力
勇者,魔法使い,オオカミ,スライム,ドラゴン

$input = trim(fgets(STDIN));
$member = explode(",", $input);
print_r($member);
$max = count($member) - 1;
$key = rand(0, $max);
echo $key."\n";
echo $member[$key];

// 出力
Array
(
    [0] => 勇者
    [1] => 魔法使い
    [2] => オオカミ
    [3] => スライム
    [4] => ドラゴン
)
2
オオカミ

PHP入門編5:連想配列、foreach、ソートを学ぶ

連想配列はkeyを文字列にしたものです。RPGゲームで、key:薬草/value:3(個)のようにアイテムの所持数を管理したりするのに使えます。keyの重複は不可です(上書きされます)。

連想配列は、keyを指定する以外は配列と同じように操作できます。

$item = array(
    "ロングソード"=>2,
    "鉄の盾"=>1
);
$item["クリスタル"] = 6;
print_r($item);
unset($item["鉄の盾"]);
print_r($item);

// 出力
Array
(
    [ロングソード] => 2
    [鉄の盾] => 1
    [クリスタル] => 6
)
Array
(
    [ロングソード] => 2
    [クリスタル] => 6
)

配列をソートするには、sort関数(小さい順)、rsort関数(大きい順)を使います。日本語のソートは、ひらがな、カタカナ、漢字、全角数字の順で行われます。

$item = array("イージスシールド", "ウィンドスピア", "アースブレイカー");
sort($item);
print_r($item);

rsort($item);
print_r($item);

// 出力
Array
(
    [0] => アースブレイカー
    [1] => イージスシールド
    [2] => ウィンドスピア
)
Array
(
    [0] => ウィンドスピア
    [1] => イージスシールド
    [2] => アースブレイカー
)

連想配列をvalueを使ってソートするには、asort関数、arsort関数を使用します。

$item = array(
	"イージスシールド" => 40,
	"ウィンドスピア" =>1,
	"アースブレイカー" =>12
);
asort($item);
print_r($item);
arsort($item);
print_r($item);

// 出力
Array
(
    [ウィンドスピア] => 1
    [アースブレイカー] => 12
    [イージスシールド] => 40
)
Array
(
    [イージスシールド] => 40
    [アースブレイカー] => 12
    [ウィンドスピア] => 1
)

上記の連想配列をkeyでソートするには、ksort関数、krsort関数を使用します。

ksort($item);
print_r($item);
krsort($item);
print_r($item);

// 出力
Array
(
    [アースブレイカー] => 12
    [イージスシールド] => 40
    [ウィンドスピア] => 1
)
Array
(
    [ウィンドスピア] => 1
    [イージスシールド] => 40
    [アースブレイカー] => 12
)

次に、配列の要素をすべて取り出すループ処理foreachをみてみます。

foreach(配列 as バリューを代入する変数){
    処理
}

print_rでは要素を一覧表示させるだけでしたが、foreachを使えば取り出した要素を加工したりすることもできます。

$array = array("勇者", "魔法使い", "遊び人");

foreach($array as $value){
    echo "<strong>".$value."</strong><br>"."\n";
}

要素(value)だけでなく、keyも取り出すことができます。この場合はasの後ろで$key => $valueのように記述します。

$array = array(
    "勇者"=>41,
    "魔法使い"=>15,
    "遊び人"=>23
    );
foreach($array as $key => $value){
	echo $key."のHPは".$value."です。\n";
}

// 出力
勇者のHPは41です。
魔法使いのHPは15です。
遊び人のHPは23です。

PHP入門編6:多次元配列を理解しよう

2つのインデックスで要素を指定する2次元配列を学びます。

861-paiza-php-2次元配列

ゲームのマップ、将棋の盤面、画像データ、エクセルデータのように行と列で位置を表せるデータを格納するのに適しています。3つのインデックスを使えば3Dデータを扱うこともできますよ。

それでは2次元配列を作ってみます。次の例では、teamAとteamBという配列をteamsという配列に格納しています。

$teamA = ["勇者", "戦士", "魔法使い"];
$teamB = ["スライム","ドラゴン","魔王"];

$teams = [$teamA, $teamB];

echo $teams[0][0] . ",";
echo $teams[0][1] . ",";
echo $teams[0][2] . "\n";

echo $teams[1][0] . ",";
echo $teams[1][1] . ",";
echo $teams[1][2] . "\n";

// 出力
勇者,戦士,魔法使い
スライム,ドラゴン,魔王

2次元配列への代入で要素を上書きすることもできます。count関数で要素の数を取り出せるのも1次配列と同様です。

$teams =
    [
        ["勇者", "戦士"],
        ["盗賊", "忍者", "商人"],
        ["スライム", "ドラゴン", "魔王"],
        ["ディアブロ"]
    ];
$teams[0][1] = "魔道士";

echo count($teams)."\n";
echo count($teams[0])."\n";

// 出力
4    // $teams配列は4つの配列から成る
2    // $teams[0]は勇者と魔道士の2つの要素から成る

2次元配列の要素の追加、削除を行います。追加の場合は$teams[0][]のようにインデックスの指定なしに代入して、削除の場合はunset($teams[0][1]);のようにunset関数で削除する要素を指定します。

$teams =
    [
        ["勇者", "戦士"],
        ["盗賊", "忍者", "商人"],
        ["スライム", "ドラゴン", "魔王"],
        ["魔法使い"],
    ];
$teams[] = ["メタルモンスター", "シルバーモンスター", "ブラックモンスター"];
$teams[0][] = "レッドドラゴン";
unset($teams[1]);
unset($teams[0][1]);
print_r($teams);

// 出力
Array
(
    [0] => Array
        (
            [0] => 勇者
            [2] => レッドドラゴン
        )

    [2] => Array
        (
            [0] => スライム
            [1] => ドラゴン
            [2] => 魔王
        )

    [3] => Array
        (
            [0] => 魔法使い
        )

    [4] => Array
        (
            [0] => メタルモンスター
            [1] => シルバーモンスター
            [2] => ブラックモンスター
        )

)

2次元配列のループ処理を行います。foreachで配列要素を取り出して、さらにその配列から順に要素を取り出します。また、コメントアウトしているforでも同様の処理が可能です。

$teams =
    [
        ["勇者", "戦士", "魔法使い"],
        ["盗賊", "忍者", "商人"],
    ];
foreach($teams as $team){
    foreach($team as $player){
        echo $player." ";
    }
    echo "\n";
    echo "---\n";
}
// for($i=0; $i<count($teams); $i++){
//     for($j=0; $j<count($teams[$i]); $j++){
//         echo $teams[$i][$j];
//         echo " ";
//     }
//     echo "\n";
//     echo "---\n";
// }

// 出力
勇者 戦士 魔法使い 
---
盗賊 忍者 商人 
---

2次元配列の作り方は、「[]で囲う」「array」の2つの方法があります。

$teams = [
    ["勇者", "戦士", "魔法使い"],
    ["盗賊", "忍者", "商人"]
];
$teams = array(
  array("勇者", "戦士", "魔法使い"),
  array("盗賊", "忍者", "商人")
);

配列を指定した要素で埋めるarray_fillを使ってみます。array_fillには、最初のインデックス、埋める個数、埋める値を指定します。

$mapRow = array_fill(0, 10, "森");
$landMap = array_fill(0, 5, $mapRow);

foreach($landMap as $row){
    foreach($row as $col){
        echo $col;
    }
    echo "\n";
}

// 出力
森森森森森森森森森森
森森森森森森森森森森
森森森森森森森森森森
森森森森森森森森森森
森森森森森森森森森森

PHP入門編7:関数を理解しよう

ここでは関数について学びます。関数は、コードを分割して共通の処理として使用する機能です。

早速、関数を作ってみます。

function Say_hello(){
    echo "hello";
}
say_hello();

関数は、関数を定義した前でも後ろでも呼び出すことができます。

関数名を付けるときは「動詞+名詞」とすると、どのような内容かわかりやすいです。また、関数名に使える文字は次のとおりです。

  • 1文字目:アルファベット/アンダースコア(_)
  • 2文字目以降:アルファベット/アンダースコア(_)/数字

関数は、引数で値を渡して、戻り値で返してもらうこともできます。

function sum($x, $y){
    return $x + $y;
}
$num1 = sum(30, 4);

ここで、スコープについて見ておきます。スコープとは、変数の有効範囲のことです。

この例では$aが関数の中と外にありますが、別のものとして扱われます。hello:3と関数の中の$aが出力されているのとは別に、$a+$bの30が出力されているのが分かります。

$a = 10;
$b = 20;
function sum($x, $y){
    $a = 3;
    echo "hello:".$a."\n";
    return $x + $y;
}
$num = sum($a, $b);
echo $num;

// 出力
hello:3
30

関数の中の変数は、ローカル変数として関数の中だけで有効な変数です。関数の外の変数は、グローバル変数でプログラム全体で使うことができます。しかし、上記の例で関数内の$a = 3を削除したとしても、$a = 10が有効になるわけではないので、hello:としか表示されません(この点はC言語と異なっています)。

関数の引数にはデフォルト値を設定することができます。次の例はデフォルト値に「村人」を設定していて、引数の指定がないときはデフォルト値を使って出力しているのが分かります。

function introduce($name = "村人"){
    echo "私は".$name."です。\n";
}
introduce("勇者");
introduce();

// 出力
私は勇者です。
私は村人です。

PHP入門編8:クラスを理解しよう

PHPはオブジェクト指向のプログラミング言語と言われています。ここでは、クラス、オブジェクトについて学びます。

オブジェクトとは

  • 変数とメソッドをセットにしたもの
  • クラスからオブジェクト(インスタンス)を作って利用する
  • クラス: オブジェクトの設計図
  • オブジェクト: クラスから作ったもの(インスタンス)

861-paiza-php-クラスを理解しよう

クラスはオブジェクトの枠となるもので、クラスを実体化したものがオブジェクトです。

861-paiza-php-オブジェクトの使い方

それでは、クラスを作ってみます。

次の例では、classでPlayerというクラスを作っています。ここではwalk()は関数ではなくメソッドと呼ぶようになります。そして、newのあとでPlayerクラスを呼び出せばオブジェクトを作ることができます。

class Player{
    public function walk(){
        $message = "勇者は荒野を歩いていた。";
        echo $message;
    }
}
$player1 = new Player();
$player1->walk();

クラスからオブジェクトを作るときに、オブジェクトごとに異なるメンバ変数を指定することもできます。次の例では$myNameがメンバ変数です。メンバ変数はオブジェクトが持つ変数で、オブジェクトがある限りデータが保持されます。

class Player{
    public $myName;
    public function __construct($name){
        $this->myName = $name;
    }
    public function walk(){
        echo $this->myName."は荒野を歩いていた。"."\n";
    }
}
$player1 = new Player("戦士");
$player1->walk();

$player2 = new Player("魔法使い");
$player2->walk();

$player1->walk();

// 出力
戦士は荒野を歩いていた。
魔法使いは荒野を歩いていた。
戦士は荒野を歩いていた。

もちろん、戻り値のあるメソッドを作ることもできます。

class Item{
    public $price;
    public $quantity;

    public function __construct($newPrice, $newQuantity){
        $this->price = $newPrice;
        $this->quantity = $newQuantity;
    }
    public function getTotalPrice(){
        return $this->price * $this->quantity;
    }
}

$apple = new Item(120, 15);
$total = $apple->getTotalPrice();
echo "合計金額は".$total."円です。\n";

// 出力
合計金額は1800円です。

「public」や「private」はアクセス修飾子といいます。アクセス修飾子を付けることで、その変数やメソッドへのアクセスをコントロールできるようになりますので覚えておきましょう。

  • publicが付いていると、他のメソッドやクラスの外から呼ぶことができます
  • privateが付いていると、そのクラス内だけで呼ぶことができます

 

この章の最後に、staticについて確認します。staticは静的な変数やメソッドを定義することができるものです。staticが付いたメンバー変数やメソッドは、全てのインスタンスで共通して利用できるようになります。また、オブジェクトを作らなくてもアクセスできるようになります。

class Item {
    public static $tax = 1.08;

    public static function getTotalAmount($price, $quantity) {
        return round($price * $quantity * self::$tax);
    }
}

$total = Item::getTotalAmount(120, 15);
echo "合計金額は" . $total . "円です。\n";

PHPの静的変数 (static変数) の挙動まとめ

PHP入門編9: さらにクラスを理解しよう

この章は、クラス継承やメソッドのオーバーライドなど、PHPでのオブジェクト指向開発について学びます。

クラス継承とは、あるクラスの機能や特徴を引き継いで新しいクラスを作ることです。次の例では、Playerクラスを元に魔法使いクラスを作っています。

861-paiza-php-継承

早速、クラスの継承で新しいクラスを作ってみます。この例ではBoxクラスを親クラス(スーパークラス)として、JewelryBoxクラスという子クラス(サブクラス)を作成しています。JewelryBoxクラスにはopenメソッドを定義していませんが、Boxクラスから引き継げているのが分かります。

class Box{
    public $myItem;
    public function __construct(){
        $this->myItem = "新しいアイテム";
    }
    public function open(){
        echo "宝箱を開いた。".$this->myItem."を手に入れた。\n";
    }
}

class JewelryBox extends Box{
    public function look(){
        echo "宝箱はキラキラと輝いている。\n";
    }
}

$box = new Box();
$box->open();
echo "\n";
$jewelryBox = new JewelryBox();
$jewelryBox->look();
$jewelryBox->open();

// 出力
宝箱を開いた。新しいアイテムを手に入れた。

宝箱はキラキラと輝いている。
宝箱を開いた。新しいアイテムを手に入れた。

子クラス側で親クラスにあるメソッドを再定義することをオーバーライドと言います。オーバーライドしたメソッドのみ親クラスとは別の処理をさせることができます。

class Box {
    public $myItem;
    public function __construct($item) {
        $this->myItem = $item;
    }
    public function open() {
        echo "宝箱を開いた。".$this->myItem."を手に入れた。\n";
    }
}

class MagicBox extends Box{
    public function look(){
        echo "宝箱は妖しく輝いている。\n";
    }
    public function open() {
        echo "宝箱を開いた。".$this->myItem."が襲ってきた。\n";
    }
}

wizardクラスは魔法使いのオブジェクトを作るためのクラスです。なので、わざわざオブジェクトを作るときに”魔法使い”という引数を渡すのはおかしい!そう思う場合はコンストラクターを呼び出して”魔法使い”を設定しておけば、オブジェクトを作るときに引数がなくても大丈夫です。

class Player {
    public $myName;
    public function __construct($name) {
        $this->myName = $name;
    }
    public function attack($enemy) {
        echo $this->myName."は、".$enemy."を攻撃した!\n";
    }
}

class Wizard extends Player {
    public function __construct(){
        parent::__construct("魔法使い");
    }
    public function attack($enemy) {
        echo "シャラララーン!\n";
        echo $this->myName."は、".$enemy."に炎を放った!\n";
    }
}

$wizard = new Wizard();

staticを付けた変数はすべてのインスタンスで共通だと前の章で学びましたが、親クラスと子クラスでも共通のものです。

この例では、$characterCountをコンストラクターが呼ばれるたびにカウントアップしています。そのため、Playerクラスでオブジェクトを作ったとき、子クラスでオブジェクトを作ったときに+1されます。

class Player {
    public $myName;
    private static $characterCount = 0;
    public function __construct($name) {
        $this->myName = $name;
        Player::$characterCount++;
        echo Player::$characterCount."番目のプレイヤー".$this->myName."\n";
    }
    public function attack($enemy) {
        echo $this->myName."は、".$enemy."を攻撃した!\n";
    }
}

この章の最後に、PHPにあらかじめ用意されている組み込みクラスであるDateTimeクラスを使ってみます。

現在の日時とmodifyにより100日後の時刻を表示させています。なお、イギリス・ロンドンの時刻であるグリニッジ標準時が標準タイムゾーンとなっていますので、タイムゾーンをAsia/Tokyoに設定しています。

$now = new DateTime();
$now->setTimezone(new DateTimeZone("Asia/Tokyo"));
echo $now->format("Y-m-d H:i:s")."\n";

$now->modify("+100 days");
echo "変更後の時刻は、".$now->format("Y-m-d H:i:s")."です。";

// 出力
2018-11-26 22:01:31
変更後の時刻は、2019-03-06 22:01:31です。

PHP入門編10: 例外処理を理解しよう

この章では、例外処理を学びます。

エラーには様々な理由がありますが、処理を停止するものや処理を継続するものなどエラーの内容によって動作も異なります。

861-paiza-php-よく目にするエラー

これらのエラーを例外と呼びます。例外が発生したときに実行する処理を例外処理と呼び、次のtry,catch,throwにより処理するための機能を実装できます。

861-paiza-php-例外処理の機能

例外が発生するのは、例えばこのような場合です。

  • ゼロで割り算
  • 数値変換で数字ではない文字を指定
  • 配列の範囲外にアクセス
  • ファイルが存在しない
  • オブジェクトがnull
  • 引数が不正

 

実際に例外が発生するプログラムを書いてみます。この例では、199xという実際にはない年を指定しているので例外が発生しています。なお、この例ではecho “start\n”;まで処理されて表示もされます。

echo "start\n";
$date = new DateTime("199x-01-01");
echo $date->format("Y/m/d")."\n";
echo "end\n";

// エラー
PHP Fatal error:  Uncaught Exception: DateTime::__construct(): Failed to parse time string (199x-01-01) at position 0 (1): Unexpected character in /workspace/Main.php:6
Stack trace:
#0 /workspace/Main.php(6): DateTime->__construct('199x-01-01')
#1 {main}
  thrown in /workspace/Main.php on line 6

Runtime error(Exit status: 255)

次に、例外処理を書いてみます。tryで例外を検出する範囲を指定して、その中で例外が発生したときにその内容を$eに格納してcatchブロックの処理を実行します。このcatchの部分の処理を例外ハンドラと呼びます。finally内の処理は例外が発生しても、しなくても実行される処理です。

echo "start\n";
try{
    $date = new DateTime("199x-01-01");
    echo $date->format("Y/m/d")."\n";
} catch(Exception $e){
    echo $e->getMessage()."\n";
} finally{
    echo "end\n";
}

// エラー
start
DateTime::__construct(): Failed to parse time string (199x-01-01) at position 0 (1): Unexpected character
end

エラーを一般ユーザーに伝える内容と開発者に伝える内容で出力先を分けることもできます。fputs(STDERR~と記述すると、標準エラー出力として出力します。

echo "start\n";
try{
    $date = new DateTime("199x-01-01");
    echo $date->format("Y/m/d")."\n";
} catch(Exception $e){
    echo "指定された日付が不正です\n";
    fputs(STDERR, $e->getMessage()."\n");
} finally{
    echo "end\n";
}

// 通常出力
start
指定された日付が不正です
end

// 標準エラー出力
DateTime::__construct(): Failed to parse time string (199x-01-01) at position 0 (1): Unexpected character

次はthrowについて確認します。throwは例外が発生していなくても意図的に例外を発生させることができます。

echo "start\n";
try {
    throw new Exception("意図的な例外");
    echo "例外を投げた後\n";
} catch (Exception $e) {
    echo "例外発生:", $e->getMessage(). "\n";
} finally {
    echo "end\n";
}

// 出力
start
例外発生:意図的な例外
end

その他、throwで投げてcatchで捕まえる例外にはRangeExceptionやLengthExceptionなどの具体的な例外を指定することもできます。

こちらは、複数の例外に対応する方法です。

echo "start\n";
try {
    $pattern = 2;
    if($pattern == 0){
        throw new RangeException("意図的な範囲例外");
    } else if($pattern == 1){
        throw new LengthException("意図的な長さ例外");
    } else{
        throw new InvalidArgumentException("意図的なその他例外");
    }
    echo "例外を投げた後\n";
} catch (RangeException $e) {
    echo "例外発生1:" . $e->getMessage() . "\n";
} catch (LengthException $e) {
    echo "例外発生2:" . $e->getMessage() . "\n";
} catch (Exception $e) {
    echo "例外発生3:" . $e->getMessage() . "\n";
} finally {
    echo "end\n";
}

ここで注目する点は、ExceptionでInvalidArgumentExceptionをキャッチできていることです。これは、Exceptionクラスがすべての例外クラスの親クラス(スーパークラス)だからです。そのため、Exceptionは最後に置いてキャッチする必要があります(先頭に配置するとすべての例外を先頭のExceptionでキャッチしてしまいます)。

例外処理を呼び出し元に任せる方法。日付が不正な場合と正常な場合で処理経路が異なります。

function test_exception($date) {
    echo "2\n";
    try {
        echo "3\n";
        return new DateTime($date);
        echo "4\n";
    } catch (Exception $e) {
        echo "5\n";
        throw $e;
    }
    echo "6\n";
}
echo "1\n";
try {
    $datetime = test_exception("199x-01-01");    //1991-01-01
    echo "7\n";
} catch (Exception $e) {
    echo "8\n";
}
echo "9\n";

// 出力(日付199x)
1
2
3
5
8
9

// 出力(日付1991)
1
2
3
7
9

 

以上で、PaizaラーニングのPHP入門は終了です。とてもわかりやすい講座だったと思います。

プログラミングは手を動かさないと身に付きませんので、PHPを学びたいと思ったらぜひPaizaラーニングで動画を見て演習問題を解きながら理解を深めてください。

Paizaラーニング:https://paiza.jp/works

 

 

PHPを覚えるとWordPressのテーマ作成もできるようになります