もくじ


PHPの初学者用書籍で基礎を学んでまとめてみる

今まで何度となくいろんなプログラミング言語を学ぼうとして挫折してきました。なので、いろんな言語に共通するようなことは何となくわかる気がします。自分用として大雑把ではありますが、まとめてみました。ノートとってるみたいな感じです。

参考書籍はこちら。
誰もがあきらめずにすむPHP超入門 Kindle版(紙の本もあります)
ちなみに正誤表はこちら。
【正誤情報】誰もがあきらめずにすむPHP超入門

2021年1月に購入していて、少しやってみた記憶はあるんだけど……。何を目的にPHPを学ぼうとしたのか覚えていないし、この本を購入したことすら忘れていて、先日キンドルアプリでPHPで検索したら自分のライブラリからこれが出てきてちょいびっくりしました。w

所々で疑問点をChatGPTにきいています。書籍の後半はデータベースの操作ですが、これについてのノートは別記事で。

あと、書籍ではPHPとデータベースを動かすために「MAMP」を導入していますが、私はPicoを使い始めるときに「XAMPP」を導入たので、そのまま使っています(Windows10環境)。
以前にこの本で勉強しようとしたときはMAMPを入れたのですが、のちに「これ、何で入れたんだったっけ?」になっててアンインストールしました。^^;

XAMPPは簡単だったので、よければお試しください。こちらの記事でインストール手順が丁寧に説明されています。
XAMPP のダウンロード・インストール方法と基本的な使い方

HTMLにPHPプログラムを入力

最低限のHTMLを書き込んだファイルを、拡張子.phpで保存。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>タイトル</title>
</head>
<body>

</body>
</html>

phpプログラムは<?php?>で囲む。
最初に学ぶのはprint

<body>
<?php print "Hello World!"; ?>
<?php 
print "<p>こんにちは!</p>";
print "<img src='neko.jpg'>";
?>
</body>

bodyと/bodyの間に、
<?php print "Hello World!"; ?>と書き込むと、ブラウザにHello World!と表示される。HTMLタグを書き込めばそのように表示される。" "' 'を使い分ける。

でもたしかprintのほかにechoというのがあったはず。PHPのバージョン違いで変わったのかな?と思い、ChatGPTにきいてみました。

ChatGPT
PHP における printecho は、どちらも出力のための言語構造ですが、若干の違いがあります。以下にその違いを簡潔に述べます。

  1. 引数の違い:

    • echo: 複数の引数をカンマ , で区切って受け取ることができます。
      例: echo 'Hello', ' ', 'World!';
    • print: 一つの引数のみを受け取ります。
  2. 戻り値:

    • echo: 戻り値を返しません。
    • print: 成功すれば 1 を返します。
  3. 速度:

    • 実際の運用上、echoprint の速度差はほとんど認識できないレベルですが、一般的には echo の方がわずかに高速とされています。
  4. 書き方:

    • echo は関数ではないため、括弧を使わないことが多いですが、print も関数ではないので、両方とも括弧を使う必要はありません。

初心者向けの学習であれば、print が使われていても大きな問題にはなりません。しかし、実際の開発では echo がよく使われる傾向にあります。

もちろん、2018年12月の本は多少古いと感じられるかもしれませんが、PHP の基本的な構文や関数に関しては変わっていない部分も多いので、学習の参考としては十分有用です。ただ、最新のバージョンや変更に関しては公式ドキュメントや新しいリソースを参照することをおすすめします。

なるほど、わかりました。
あと、printもechoも、数字を出力するときも文字列型に変換して出力するそうです。

関数を使ってみる

次に学ぶのは、rand()という関数。ランダムな数字を出してくれる(乱数を発生)。
print rand();と書けば、1064273446みたいなテキトーな数字が出力される。

そして、引数(ひきすう、と読む)を指定すればその範囲でランダムな数字を出力してくれる。
print rand(1, 5);と書けば、1から5のどれかがランダムに出てくる。
これを使って、くじ引きみたいなことができる。

変数を使ってみる

PHPで変数を使うときは、$(ドル記号)を頭につける。
$mike = rand();と書いたときは、変数mikeに、rand()関数が発生させた数字が格納される。関数そのものじゃなく、結果が格納される。

変数は、ひとつの値を格納できる。一箱にひとつ。

変数は好きな名前をつけられるが、使う文字に決まりがあるので注意。
アルファベット、数字、アンダーバー以外は使えない。大文字と小文字は区別される。数字を先頭にしてはいけない。

if文を使ってみる

さっきの、rand(1, 5)を使ってくじ引きを作ってみる。

<?php
$mike = rand(1, 5);
if($mike==1) {
  print "大吉";
} elseif($mike==2) {
  print "中吉";
} elseif($mike==3) {
  print "小吉";
} elseif($mike==4) {
  print "吉";
} else {
  print "半吉";
}
?>

変数$mikeに、rand(1, 5)の結果を格納。1から5の数字のうちどれかひとつ。
ifの次のカッコ内の意味は、(変数mikeの中身は1か?)。$mikeと1が同じかどうかチェックしてる。同じなら、Trueという値が返ってきます。違えば、Falseという値が返ってきます。

もしTrueなら、そのすぐあとの{ }の中が実行されます。Falseなら、{ }を飛ばして次のチェック、elseif($mike==2)に行きます。そして、1でも2でも3でも4でもなかったら、最後のelseの{ }の中が実行されます。

比較演算子の表を置いておきます。

使用例 意味
A==B AとBが等しいときにTrue
A===B AとBが等しく型も同じときにTrue
A>B AはBより大きいときにTrue
A>=B AはB以上のときにTrue
A<B AはB未満のときにTrue
A<=B AはB以下のときにTrue
A<>B AとBは等しくないときTrue
A!=B AとBは等しくないときTrue
A!==B AとBは等しくないか、型が異なるときTrue

データを受け取る

print $_GET["mike"];と書くと、変数mikeの中身を受け取ってブラウザに表示する。
このためには、変数mikeの中身をどこかからもらわなければいけない。

これが書かれているファイルをブラウザで表示して、そのアドレスバーのURLの最後に?mike=三毛猫だよ!と付け加えて、エンターキーを押してみる。するとブラウザに、三毛猫だよ!と表示される。
そして、アドレスバーにはhttp://localhost/php_lesson/test.php?mike=三毛猫だよ!みたいに表示されている。

URLに?変数名=文字列をつけると、$_GET["変数名"]で文字が受け取れるのだそう。

$_GETは変数だけど、PHPに最初から用意されている変数(スーパーグローバル変数)なのだそう。すごい名前!アンダーバーと大文字だから、自分で作った変数と見分けられる。

そして変数名の後ろに[ ]がついている。こういうのを配列変数という。ふつうの変数は値をひとつ、一箱にひとつしか格納できなかったけど、配列変数は複数の値を格納できる。

フォームでデータを送信し、受け取ってもらう

さっきの$_GETが書かれているファイルに、別のファイルからデータを送信する。このためには、送信する側のファイルにHTMLでフォームを作る。以下は例。

<form action="受け取る側のファイルURL">
<input type="text" name="mike">
<input type="submit" value="送信">
</form>

こうすると、テキストエリアと送信ボタンが表示される。文字を入力して送信ボタンを押すと、受け取る側のファイルが表示されて、入力したテキストが表示されている。
そしてURLにはテキストがくっついている。

URLにテキストがくっついて見えてしまわないように、POST送信を使ってみる。

送信する側のファイルのタグを変更する。
<form action="受け取る側のファイルURL" method="post">と、method="post"を追加する。

受け取る側のファイルも、もちろん変更する。
$_POST["mike"]と、GETをPOSTに書きかえる。

そうすると、今度はURLには何もくっついてこない。

ひとつのファイルで送受信

フォームとphpコードを同じファイルに書くこともできる。formタグにactionで送信先を指定しない場合、データは自分自身に送られる。

送信された文字列に何かをくっつけて表示する

phpコードのほうを以下のように書く。

<?php
print isset($_GET["mike"])?$_GET["mike"]."なのだにゃん":"なにか入力してください";
?>

これは三項演算子といって、if文を簡略化して書く方法だそうです。
xxx ? yyy : zzzという形になっていて、xxxがTrueならyyyを実行、Falseならzzzを実行。簡単!(ただし分岐が2つの場合で、処理を一行で書けるときだけ)

そしてissetは、引数のなかの変数に中身があればTrueを返す関数。yyyにあたる部分の.は、文字列を結合するという意味。入力した文字に「なのだにゃん」をくっつけてねっていうこと。

そしてこのコード、テキストエリアを空欄で送信しても「なのだにゃん」が出力されてしまうので、これを修正する。空欄で送信するということは、""、つまり空、からっぽを送信していることになる。送信された値があるか?だけでなく、その値は""と同じではないか?もチェックする必要がある。

isset($_GET["mike"])?を、isset($_GET["mike"]) && $_GET["mike"]!=""?に書き直す。

&&は条件を追加するための演算子。日本語で意味をあらわすと「かつ」だそうで、&&の左右がどちらもTrueのときだけTrueを返す。A かつ B である、のとき。

タグを送信されても無効にする

テキストエリアにタグを入力されると、それがファイルに影響を及ぼしてしまうので、タグを無効化するコードを追加する。

$inu=htmlspecialchars($_GET["mike"], ENT_QUOTES);
print isset($_GET["mike"]) && $_GET["mike"]!=""?$inu."なのだにゃん":"なにか入力してください";

でもこれだと、最初のアクセスのとき「なにか入力してください」の前にエラーコードが表示されるんですよね。
Warning: Undefined array key "mike" in C:\xampp\htdocs\php_lesson\ninja.php on line 15

プログラムコードを確認したけど、本の通りだし。ここはChatGPTにきいてみよう。

ChatGPT
指摘されたエラーは、$_GET["mike"] が定義されていない場合に発生します。これは、フォームが初めて表示された際(つまり、まだフォームが送信されていない場合)に発生します。

エラーを回避するために、$_GET["mike"] の存在を確認してから操作を行うように修正します。以下のように修正します。

<?php
if (isset($_GET["mike"])) {
    $inu = htmlspecialchars($_GET["mike"], ENT_QUOTES);
    print $inu !== "" ? $inu . "なのだにゃん" : "なにか入力してください";
} else {
    print "なにか入力してください";
}
?>

この修正により、$_GET["mike"] がセットされている場合のみ $inu を定義・変換し、それ以外の場合はエラー無しで "なにか入力してください" と出力します。

とのこと。やってみたらエラーなしになりました。
三項演算子ではできなくなりましたね。むーん。

ファイルにメッセージを書き込む

おおっ!いよいよフラットファイルCMSにちょっとだけ近づくことができるのか?
書き込んだ内容を、txtファイルに記録していくコードが出てきました。フォームのあとにこいつを書き込みます。

<?php
$pon=fopen("memo.txt","at");
fwrite($pon,$_GET["mike"]."\n");
fclose($pon);
?>

fopenはファイルを開く関数。引数に開くファイルを指定(ファイルがなければ作成)、ファイルを開くときにどうするかも引数で指定。"at"はファイルの末尾に追記するモード

ファイルを操作するときは「ファイルハンドル」というものを作成しなければならない。変数$pon。を作った。変数ponにファイルハンドルが代入されているってことらしい(めんどい)。とりあえず、変数ponをファイルそのもののように扱える。

次に、fwrite関数でファイルに書き込む。引数にファイルハンドルと書き込む内容を指定。

最後にfclose関数でファイルを閉じる。引数にファイルハンドルを指定。

"\n"は改行コード。文末に改行コードをつけて書き込むことで、一回一回の書き込みが別行に分かれる。

これはフラットファイルCMSではなく、私が知っている範囲だとcgiプログラムの掲示板みたいなことですね。ログファイルをいじったことがある。スパム投稿を削除するの大変だったんだよねぃ。

そして、これも最初にアクセスしたときにエラーメッセージが出るので、さっきChatGPTに教えてもらったのを参考に書き直し。

if (isset($_GET["mike"])) {
$pon=fopen("memo.txt","at");
fwrite($pon,$_GET["mike"]."\n");
fclose($pon);
}

エラーメッセージは消えました。

さきほど、"at"でファイルの末尾に書き込むモードとありました。では、ファイルの冒頭に書き込むモードとかあるのかな?ChatGPTにきいてみました。

ChatGPT:
fopen 関数には多くのモードが存在し、それぞれのモードは異なる目的のために使用されます。以下に主要なモードの一部を挙げます:

  1. r - 読み取りのみのモード。ファイルポインタはファイルの先頭に配置されます。
  2. r+ - 読み取り/書き込みのモード。ファイルポインタはファイルの先頭に配置されます。
  3. w - 書き込みのみのモード。ファイルが存在すれば上書きし、存在しなければ新しいファイルを作成します。ファイルポインタはファイルの先頭に配置されます。
  4. w+ - 読み取り/書き込みのモード。ファイルが存在すれば上書きし、存在しなければ新しいファイルを作成します。ファイルポインタはファイルの先頭に配置されます。
  5. a - 書き込みのみのモード。ファイルが存在する場合はファイルの末尾に追記し、存在しない場合は新しいファイルを作成します。ファイルポインタはファイルの末尾に配置されます。
  6. a+ - 読み取り/書き込みのモード。ファイルが存在する場合はファイルの末尾に追記し、存在しない場合は新しいファイルを作成します。ファイルポインタはファイルの末尾に配置されます。
  7. x - 書き込みのみのモード。専用のファイルを新しく作成します。ファイルが既に存在する場合は fopen は失敗し、false を返します。
  8. x+ - 読み取り/書き込みのモード。専用のファイルを新しく作成します。ファイルが既に存在する場合は fopen は失敗し、false を返します。

ファイルの冒頭に直接書き込むモードは存在しません。ファイルの冒頭にデータを追加するためには、既存のファイルの内容を一度読み取り、新しいデータと組み合わせて再度書き込む必要があります。

aとatは何が違うの?

ChatGPT
aat の違いに関して、PHP の公式ドキュメントには次のように記述されています:

  • a: ファイルを書き込み専用で開きます。もし、ファイルが既に存在する場合、データはファイルの末尾に追加されます(すなわち、ファイルの内容は上書きされません)。もし、ファイルが存在しない場合、新しいファイルが作成されます。このモードでは、ファイルの中のどの位置にも移動することはできません。
  • at: a と同様にファイルを書き込み専用で開きますが、ファイルに対する fseek() の動作が異なります。

実際のところ、多くのシステムでは aat の違いはほとんどありません。t は、Windows の場合、テキストモードでファイルを開くことを示しています。しかし、PHP においては、t が指定されていなくてもテキストモードでの動作がデフォルトです。そのため、実際の動作には変わりがないといえるでしょう。

しかし、常に最も標準的で予想される動作を取るためには、特別な理由がなければ a を使うのが良いでしょう。

なるほどなるほど。

公式にもっと詳しく書いてありました。
https://www.php.net/manual/ja/function.fopen

さらに質問を続けたところ、fseek()はファイルの中でポインタを移動させるための関数、aでファイルを開くとポインタは末尾から動かない(移動に失敗する)、atで開いてもポインタの移動はできるものの書き込みは末尾にされる(移動に失敗する)。
もしfseek()で任意の場所に書き込みたいときは、読み書きの両方をサポートするr+w+を使う方がよいとのこと。

ファイルからメッセージを読み込む

readfile("memo.txt");と書き込むと、ファイルの内容が改行無しで表示されました。

readfileは、ファイルの全内容を標準出力(ディスプレイ)に出力する関数。ディスプレイ以外の出力先ってある?て感じですが、スクリプトの実行方法や環境によってコンソールやターミナルだったりするそうです。

改行も適用させて表示させるには?

print "<pre>"を使って改行コードや半角スペースをそのまま表示させるという方法が、書籍では紹介されていました。けどこれだと、書体が微妙な感じになるよね……

改行コードを<br>に変換して表示させるには?
ChatGPTに聞いてみました。

ChatGPT
ファイルの内容を読み込んで、その中の改行コードを<br>に変換して表示させるには、nl2br関数を使用することができます。この関数は、文字列内の改行コード(\n、\r\nなど)をHTMLの
タグに変換します。

以下のコードは、memo.txtの内容を読み取り、改行を<br>に変換して表示する例です:

$contents = file_get_contents("memo.txt");
print nl2br($contents);

この方法を使用することで、<pre>タグを使用する場合のようにフォントや書体が変わることなく、正しく改行を反映させることができます。

file_get_contentsはPHPの組み込み関数の一つだそうです。

んで最後にhtml無効化コードを追加。できたコードがこちら。

<?php
if (isset($_GET["mike"])) {
$pon=fopen("memo.txt","a");
$inu=htmlspecialchars($_GET["mike"],ENT_QUOTES);
fwrite($pon,$inu."\n");
fclose($pon);
}

$contents = file_get_contents("memo.txt");
print nl2br($contents);
?>

このあと書籍はデータベースの項に行くんですが、その前にもう少しこのコードを変更してみたいんですよね、、

表示順を変えてみたい

まず、ファイルへの書き込みを、文末ではなく、一行目に書き込みたい。
ChatGPTに聞いてみましたら、これちょっと面倒な手順が必要でした。

ChatGPT
ファイルの先頭にデータを追加するためには、以下のステップを踏む必要があります:

  1. ファイルの現在の内容を読み取ります。
  2. 新しいデータを現在の内容の前に追加します。
  3. 変更した内容をファイルに書き戻します。

以下は、そのステップを実現するためのコード例です:

if (isset($_GET["mike"])) {
    // 1. ファイルの現在の内容を読み取る
    $currentContents = file_get_contents("memo.txt");

    // 2. 新しいデータを現在の内容の前に追加する
    $inu = htmlspecialchars($_GET["mike"], ENT_QUOTES);
    $newContents = $inu . "\n" . $currentContents;

    // 3. 変更した内容をファイルに書き戻す
    file_put_contents("memo.txt", $newContents);
}

注意点として、この方法は特にファイルサイズが大きい場合、パフォーマンスに影響する可能性があります。大きなファイルを扱う場合、他の効率的なアプローチを検討する必要があります。

大きなファイルのときは、逐次的な読み書きを行う方法を使うと良いそうです。たとえば、

一時ファイル(temp.txt)を作ってそこに新しいデータを書き込み、元ファイルの内容を少しずつ読み取りながら一時ファイルに書き込む。最後に一時ファイルの名前を元ファイルの名前に変更する。

という手順を踏むのだそうです。書き込む位置を先頭にするだけで一気に面倒になるんですね……。

では、ファイルに書き込むのは文末のままで、表示させるときに新しいものから順にするにはどうすればいい?

ChatGPT
ファイルの内容を新しいものから表示させるには、ファイルを逆順に読み取って出力すれば良いです。以下は、その方法を示すいくつかのアプローチを示しています:

  1. file() 関数を使用する

file() 関数は、ファイルの各行を配列の要素として返します。この配列を逆順にしてから表示することで、新しい内容から表示させることができます。

$lines = file('memo.txt');
$reversedLines = array_reverse($lines);
foreach ($reversedLines as $line) {
    echo $line . "<br>";
}

~中略~

これらのアプローチの中で、最もシンプルで一般的なのはfile()関数を使用する方法です。大きなファイルを扱う場合はメモリの使用量に注意が必要ですが、中程度のサイズのファイルであれば問題なく動作します。

表示を逆順にするほうが簡単ですね。そして、cgiプログラムの掲示板が一定数の投稿を超えると過去ログを作る仕様になっているのは、ファイルサイズが大きいとマズいからだったんですね。

ついでにちょっと気づいたので、foreachについても説明してもらいました。

ChatGPT
foreach は PHP で配列やオブジェクトを繰り返し処理するための構文です。foreach は、配列の各要素に対して順番に操作を行いたいときに非常に役立ちます。

基本的な使用法は次のようになります:

$array = array("apple", "banana", "cherry");

foreach ($array as $fruit) {
    echo $fruit . "<br>";
}

このコードは以下の出力を生成します:

apple
banana
cherry

キーと値の両方を取得したい場合は、次のように記述します:

$array = array("a" => "apple", "b" => "banana", "c" => "cherry");

foreach ($array as $key => $value) {
    echo "$key: $value" . "<br>";
}

出力:

a: apple
b: banana
c: cherry

foreach の主な利点は、繰り返し処理を簡単に記述できること、および配列のサイズが変わっても自動的に全ての要素を処理してくれることです。

foreach を使うべき時:

  1. 配列の各要素に操作を適用する場合
    例:配列のすべての要素に対して何らかの操作を行いたい場合。

  2. アソシエイティブ配列のキーと値を同時に処理する場合
    例:キーと値のペアを持つ配列で、両方とも処理する必要がある場合。

  3. オブジェクトのプロパティを繰り返し処理する場合
    例:オブジェクトが Iterator インターフェイスを実装している場合や、Traversable インターフェイスを持つ場合。

要するに、foreach は、配列やオブジェクトの各要素を効率的かつ読みやすく処理するための非常に便利な構文です。

ファイルの内容を配列の中身として扱っているから、foreachを使っているということですね。

基礎学習として重要なものをあげてもらいました

ChatGPT
PHPの基礎を学ぶ際に重要なトピックや概念は多岐にわたりますが、以下は特に重要なものをいくつか挙げてみました:

  1. 変数とデータ型:

    • 文字列、整数、浮動小数点数、真偽値などの基本的なデータ型
    • 変数のスコープ(ローカル、グローバル、スタティック)
  2. 制御構文:

    • if, else, elseif による条件分岐
    • switch
    • while, do-while, for によるループ
  3. 配列:

    • インデックス配列とアソシエイティブ配列
    • 配列関数(例:array_push, array_pop, in_array など)
  4. 関数:

    • ユーザー定義関数の作成と使用
    • 引数と戻り値
    • 変数のスコープと関数
  5. フォーム処理:

    • $_POST$_GET を使用したデータの受け取り
    • フォームのバリデーション
  6. セッションとクッキー:

    • ユーザーセッションの開始、データの保存、終了
    • クッキーの設定と取得
  7. エラー処理:

    • エラー報告レベル
    • カスタムエラーハンドラ
  8. オブジェクト指向プログラミング:

    • クラスとオブジェクト
    • 継承、インターフェース、抽象クラス
    • アクセス修飾子(public, private, protected)
  9. ファイル操作:

    • ファイルの読み書き
    • ファイルシステムの操作(例:ディレクトリの作成、ファイルのコピー/移動/削除)
  10. 外部リソースとのやりとり:

    • データベース接続と操作(特に MySQL との連携)
    • RESTful API との通信
  11. セキュリティ:

    • SQLインジェクション、XSS、CSRFなどの主な攻撃からの防御
    • データのサニタイズとバリデーション

以上は基礎として特に重要と考えるトピックの一部です。これらのトピックをしっかりと理解し、実際にコードを書いて実践することで、PHPの基本を固めることができます。

あうあうあう……
書籍のタイトルが超入門ていうだけあって、ほんとに門を少し入った程度なのね、、

次の章(データベースを扱う)でwhile文とかがでてくるらしいけど、それでも門を少し入った程度に変わりはなさそう。

でもそのおかげで私でも理解がすすんでいるわけですね。別の言語の「変数とデータ型」の解説から難しめな感じで延々続くテキストは途中で投げ出しました。

あとね、ChatGPTに途中で色々きけるのもいいんだと思います。いい時代ですね。

PHPの基礎を学ぶ2へ続く