PHPの基礎①変数 (独学エンジニア レッスン9より)

# 目標
<strong>今回やること</strong>
* PHPの基礎を学ぶ①

<strong>タスクばらし<strong/>
* 変数
* 定数
* 型
* 配列
* 演算子
* 発展

# 変数
<strong>変数の誕生</strong>
メモリ領域に変数の「名前」というラベルを貼ることで、コードを読み書きしやすくした
よって、変数名には意味のわかりやすい名前をつけることが大事

<strong>変数の挙動</strong>
* リファレンス
リファレンスとは同じ変数の内容を別の名前で呼び出すこと
「変数の中身」と「変数の名前」は別に存在しているよ
変数名の前に&を付けて代入するとリファレンスが代入される
```
$d = &$c
```

* 代入
通常の変数への代入は「値」を渡している
`$a`の中身をコピーしてコピーしたものを`$b`に入れてる
`$a`と`$b`で変数の中身は別物なので、`$a`の値を書き換えても`$b`の値は変わらない
```
$b = $a
 ```

* 通常の変数の代入では「式」が代入されている
 
* 式の定義は「値があるもの全て」
変数の中身 ≠ 式(値。ただの文字列)

<strong>変数のスコープ</strong>
名前の衝突を回避するために作られた概念が「スコープ」
プログラム全体で使う対応表と、関数の呼び出しごとに専用の対応表を分けることで名前の衝突を避けた

* PHPのスコープにはグローバルスコープとローカルスコープがある
変数の定義する時に名前の有効範囲を意識した上で定義場所を考えるのが重要

```
$i = 0;  // グローバルスコープ

function call() {
	$i = 0;  // ローカルスコープ
}
```

<strong>定義済み変数</strong>

* 定義済み変数は実行時に自動的に定義される変数

```
$http_response_header
 HTTPレスポンスヘッダ配列

$argc 
CLIでスクリプトに渡された引数の数

$argv
CLIで渡された引数の配列
```

* スーパーグローバル変数
スーパーグローバル変数はどんなスコープからでも使用可のなグローバル変数

```
$GLOBALS 
全てのグローバル変数への参照を持つ連想配列

$_SERVER 
サーバー情報および実行時の環境情報を持つ連想配列

$_POST 
HTTP POSTで渡された変数の連想配列

$_FILES 
アップロードされた項目の連想配列

$_COOKIE 
HTTPクッキーから渡された変数の連想配列

$_SESSION 
セッション変数を含む連想配列

$_REQUEST 
$_GET、$_POST、$_COOKIEの内容をまとめた連想配列

$_ENV 
環境変数の連想配列
```
# 定数
<strong>定数って何？</strong>
定数とは実行中に値を変更できないもの

* 特徴
一度設定されると再定義または未定義にできない
変数のスコープに関係なく、どんな場所からでもアクセス可能
前に$記号がいらない

* 定義
constキーワードの後に指定することで定数を定義できる
トップレベルのスコープで定義する必要がある

* その他
慣習的に常に大文字で定義される
複数単語の場合は_で繋げる

* 定数を使うシーンは主に3つ
1, 値を変更されたくない時
2, 値に意味を持たせたい時
3, 値を一元管理したい時

<strong>定義済み定数</strong>

* 自動的に定義されている定数
PHPの実行時に、PHPの実行環境を表す定数などが自動的に定義される

```
PHP_VERSION
現在のPHPのバージョン

PHP_OS
実行しているOS

E_ERROR
重大な実行時エラーのエラー番号
```

* マジカル定数
マジカル定数は、使われる場所によって自動的に値が変化する定義済み定数

```
__FILE__
ファイルのフルパスとファイル名

__DIR__
ファイルの存在するディレクトリ

__LINE__
ファイル上の現在の行番号

__FUNCTION__
関数名
```

# 型
<strong>仕様としての型</strong>
* 関数の引数や戻り値に型を指定することで、システムの安全性を高める
* 変数などの値について型を明示せず、実行時にプログラムが自動で型を判定することを動的型付けという

※動的型付けは柔軟性がメリットだが、型チェックによるバグの発見がしにくくなるというデメリットもある

<strong>PHPの型は大きく分けて７つ</strong>
1, 整数
2, 浮動小数点
3, 文字列
4, 論理型
5, 配列
6, null
7, その他

## 1, 整数
記載事項特になし

## 2, 浮動小数点
浮動小数点とは少数や分数のように、整数で表現できない実数
コンピュータは少数の扱いが苦手

* 扱い方
少数は丸めによる誤差が発生する可能性がある
↓
小数点を使った比較、計算をしないようにする
浮動小数点で比較や計算を行う時は任意精度数学関数(BC Math関数)を使う

## 3, 文字列
文字列は文字が連結されたもの

* 初期化はシングルクオートもしくはダブルクオートを使う
シングルクオートは変数を展開しない
ダブルクオートは変数を展開する
シングルクオートの方が動作処理速度は速い

* シングルクオートやダブルクオートを文字として扱うにはエスケープする
```
echo "I\'m "Kiyoto Yamaura"' . PHP_EOL; // I'm "Kiyoto Yamaura"
```

* エスケープシーケンスとはプログラム中で扱いにくい文字を表現するための文字

以下はダブルクオートで囲まれた場合、特殊な文字として解釈される
```
\n
ラインフィールド（改行）

\r
キャリッジリターン（改行）

\t
タブ
```

※改行コードを実行環境で切り替えたい時はPHP_EOLを使う

* 複数行になる長い文字列を記載する方法としてヒアドキュメントとNowdocがある

ヒアドキュメントはダブルクオートで囲んだ文字列として扱う（変数展開される）
```
$name = 'Kiyoto';
$str = <<<EOT
こんにちは。
名前は $name です。
EOT; // 名前は Kiyoto です。
```

Nowdocはシングルクオートで囲んだ文字列として扱う（変数展開されない）
```
$name = 'Kiyoto';
$str = <<<'EOT'
こんにちは。
名前は $name です。
EOT; // 名前は $name です。
```

* 半角文字列と全角文字列の違い

全角文字はマルチバイトとしてカウントされるので注意
```
$str1 = 'abc';
$str2 = 'あいう';
echo strlen($str1); // 3
echo strlen($str2); // 9
echo mb_strlen($str2); // 3
```

半角と全角のゆらぎは統一すべき
```
$name = 'ﾑﾗｶﾐ ﾊﾙｷ' // 半角
# 半角カタカナは全角カタカナに、全角スペースは半角スペースに、
# 全角英数字は半角英数字に変換する
echo mb_convert_kana($name, 'Kas'); // ムラカミ ハルキ
```

## 4, 論理型
論理型はtrue(真)かfalse(偽)のどちらかを表す真偽値

* 初期化には定数trueまたはfalseを指定。両方とも大文字小文字に依存しない
```
var_dump(true) // bool(true)
var_dump(false) // bool(false)
```
* if文など、論理型を必要とする場合には値は自動的に変換される
以下はfalseと判定される
```
・false(論理型)
・0(整数型)
・0.0(浮動小数点)
・""(空の文字列)
・"0"(文字列のゼロ)
・[](要素数がゼロの配列)
・null
・空のタグから作成された SimpleXML オブジェクト
```

※ifの判定は真偽値で行われるため重要

## 5, 配列
複数の値をまとめて保持するためのもの

* PHPの内部では配列と連想配列は区別されない
```
$arr1 = ['a', 'b'];
$arr2 = [
	0 => 'a',
	1 => 'b',
];
var_dump($arr1 === $arr2); // true
```

## 6, null
nullはある変数が値を持たないことを表す特別な型

* 以下の場合にnullになる
定数nullが代入されている場合
まだ値が何も代入されていない場合
unset()されている場合

* nullと未定義は違うので注意
* 変数にnullを代入すると「値を持たない変数を定義」していることになる
```
$a = null;
var_dump($a); // NULL
var_dump($b); // Warning: Undefined variable $b. NULL
```

## 7, その他
* リソース
リソースは外部リソースへのリファレンスを保持しているデータ型
```
$fp = fopen("test.txt", "x"); // 外部ファイルを書き込む
echo gettype($fp); // resource
fclose($fp); // ファイルを閉じる
```

* コールバック
コールバックは「関数を引数で取る関数のための擬似的な型」。
呼び出し可能な関数かをチェックする
引数として取れる関数のことをコールバック関数という
```
$triple = function ($int) {
	return $int * 3;
};

$numbers = array_map($triple, [1, 2, 3]);

var_dump(implode('', $numbers)); // string(11) "3 6 9"
var_dump(is_callble($triple)); // bool(ture)
```

* Iterable
Iterableはforeachで繰り返しができる変数の型
```
var_dump(is_iterable([1, 2, 3])); // bool(true)
var_dump(is_iterable(1)); // bool(false)
```

# 型変換と型宣言
<strong>型変換</strong>
* PHPは型を自動的に変換するので、バグの温床になりかねない
```
var_dump(10 * "10 cats"); // int(100)
```

* 暗黙的変換
自動的に行われる変換
```
if (10) {
	echo 'true';
} // true
```

* 明示的変換
明示的に指示した変換
```
var_dump((string) 10); // string(2) "10"
var_dump((bool) 10); // bool(true) 
```

* 緩やかな比較
暗黙的な型変換が発生しうる
「==」「!=」「<>」switch文

* 厳密な比較
型の変換はしない
「===」「!==」

※PHPで比較する場合は厳密な比較を行うのが基本
※型ごとに変換のルールがあるため、公式ドキュメントに目を通す


<strong>型宣言<strong>
* 関数の引数や戻り値に型を指定することができる(型宣言)
```
function double($number) {
	return $number * 2;
}
echo double('1a'); // 実行できる

function double_type(int $number): int {
	return $number * 2;
}
echo double_type('1a'); // エラーになる
```

* 型を宣言しないと
どんな値でも引数に取れてしまう
関数内部で型に応じた処理をしないといけなくなる
処理を怠ると関数が想定外の動きをする

* 型を宣言すると
型が保証されているので型が合っている前提で処理をかける
想定外の引数を取るとエラーになるので不具合を発見できる
引数と戻り値の型が明確に分かりやすくなる

* 基本的な型
```
array
値が配列でないといけない

bool
値が真偽値でないといけない

int
値が整数でないといけない

float
値が浮動小数点でないといけない

string
値が文字列でないといけない

object
値がオブジェクトでないといけない

callable
値がコールバックでないといけない

iterable
値がiterableでないといけない
```

* その他の型の指定
```
union型
複数の異なる型を指定することができる。T1|T2|...のように記載する

mixed型
array|bool|callable|int|float|object|resource|string|nullと同じ。何でもあり型

null許容
型が指定されたものかnullであることを指定できる。型の名前の前に?をつける

void型
関数が値を返さないことを示す戻り値の型(戻り値専用)
```

```
function double(int|float $item): ?int {
	if ($item >= 0) {
		return floor($item * 2);
	}
	return null;
}
```

```
funciton printHello(): void {
	echo 'Hello';
}
```

* 厳密な型付け
デフォルトではPHPは暗黙的変換をしようとするので、厳密に判定したい時は厳密な型付けを宣言する
```
declare(strict_types=1);

funciton double_type(int $number): int {
	return $number * 2;
}
echo double_type(1.5); // エラーになる
```

# 配列
<strong>基本</strong>
* 配列と連想配列
配列は複数の値をまとめて管理するためのもの。
配列と連想配列があり、内部では同じ扱い

* 配列
要素に名前を付けず、配列の先頭から0から始まる番号が振られる
```
$members = ['Watanabe', 'Fukushima']; // 初期化
$members[] = 'Taguchi'; // 要素の追加
echo $members[2]; // 要素へのアクセス。Taguchi
```

* 連想配列
要素に名前を付け(キー)、キーでアクセスする
```
$user = ['name' => 'Kume']; // 初期化
$user['age'] = 25; //要素の追加
echo $user['age']; // 要素へのアクセス。25
```

* 多次元配列
配列の値に配列が指定されたものを多次元配列という
```
$members = [
	[
		'name' => 'Fukuchima',
		'age' => '35',
	],
	[
		'name' => 'Kume',
		'age' => '25',
	],
];

echo $members[0]['name']; // Fukushima
echo $members[1]['age']; // 25
```

※複数行の配列の要素の最後にはカンマを付けたほうがよい。(後から要素を追加する可能性が高いため)

<strong>よく行う操作</strong>
* 要素を追加する
配列に要素を追加する方法は大きく2つある

$array[]
空インデックスを使って追加できる
```
$arr = [1];
$arr[] = 2;
var_dump($arr); // { [0]=> int(1) [1]=> int(2) }
```

array_push
1度に複数の要素を追加できる
```
$arr = [1];
array_push($arr, 2, 3);
var_dump($arr); // { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
```

※要素を一つだけ追加する時は$array[]を使うのがおすすめ

* 配列を結合する
2つの配列を結合する方法は大きく2つある

`+`
左側を優先して結合する
```
$arr1 = [1, 2];
$arr2 = [3, 4, 5];
var_dump($arr1 + $arr2); // { [0]=> int(1) [1]=> int(2) [2]=> int(5) }
```

array_merge
新要素を末尾に追加する
```
$arr1 = [1, 2];
$arr2 = [3, 4, 5];
var_dump(array_merge($arr1, $arr2)); // { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) }
```

※基本的にはarray_mergeを使用する

キー名が数字の配列をarray_mergeする時は気をつけよう
キー名が数字の時はキーを整理し直す
```
$arr1 = ['1' => 'a'];
$arr2 = ['10' => 'b'];
var_dump($arr1 + $arr2); 
// { [1]=> string(1) "a" [10]=> string(1) "b" }

var_dump(array_merge($arr1, $arr2));
// { [0]=> string(1) "a" [1]=> string(1) "b" } 
```

* 要素数を数える
要素数を数えたい時はcount関数を使う
```
$numbers = [1, 2];
var_dump(count($numbers)); // int(2)

$book = [
	'name' => 'Harry Potter',
	'author' => 'J.K. Rowling',
];
var_dump(count($book)); // int(2)
```

* 配列の並び替え
配列の並び替えで一番使うのがsort関数
```
$arr1 = [10, 5, 7];
sort($arr1);
var_dump($arr1); // { [0]=> int(5) [1]=> int(7) [2]=> int(10) }

$arr2 = ['a' => 10, 'b' => 5];
sort($arr2);
var_dump($arr2); // { [0]=> int(5) [1]=> int(10) }

$arr3 = ['a' => 10, 'b' => 5];
asort($arr3);
var_dump($arr3); // { ["b"]=> int(5) ["a"]=> int(10) }
``` 

※sort関数はキーと値の対応関係を維持しない

# 演算子
* 加算子と減算子
加算子と減算子を使うと、数値に1を加えるもしくは1を引くことができる

```
++$a 
前置加算子
$aに1を加え、$aを返す

$a++ 
後置加算子
$aを返し、$aに1を加える

--$a 
前置加算子
$aから1を引き、$aを返す

$a-- 
後置加算子
$aを返し、$aから1を引く
```

* 複合演算子
代数演算子と代入演算子(=)を合わせて使用することができる
```
$a += $b
加算
// $a = $a + $b

$a -= $b
減算
// $a = $a - $b

$a *= $b
乗算
// $a = $a * $b

$a /= $b
除算
// $a = $a / $b

$a %= $b
剰余
// $a = $a % $b

$a **= $b
累乗
// $a = $a ** $b

$a .= $b
結合
// $a = $a . $b
```

* 特殊な比較演算子
```
$a <=> $b
宇宙演算子
$aが$bより小さければ-1、$aが$bより大きければ1、そうでなければ0を返す
```

# 発展
<strong>変数のスコープのベストプラクティス</strong>
* 変数のスコープは最小限に抑えるのが原則。
一つの変数が広い範囲で使われていると、、

1, 脆弱性が上がる
新しいコードの追加や変数の変更で、後々エラーを発生する可能性が上がる

2, コードを把握しにくくなる
一つの変数が離れた場所で使われてたら把握しにくくなる


* 変数の参照は近くにまとめよう
変数の宣言は最初に使う場所の近くで行う
変数を最初に参照してから最後に参照するまでの期間も短くする

* 変数は一つの目的にだけ使う
一つの変数を複数の目的のために使うと混乱の元なのでNG
使っていない変数がないようにすることも大事

<strong>変数名の付け方</strong>
* 悪い名前
変数名と実態が異なる
意味がわかりにくい
他人が読んで理解に時間がかかる

* 良い名前
実態を正確に表している
意味がわかりやすい
他人が読んですぐ理解できる

* 名前の付け方
単語が示しているものを表す
```
$lines               // △：1ファイルあたりの行数を示したいが、行数しか表せてない 
$linesPerFile   // ◯：示しているものを正確に表している
```

具体的な名前を付ける
```
$times               // △：「視聴回数」を表現したいが、何の回数かわかりやすい
$viewingTimes  // ◯：視聴回数ということが明確
```

意味が分かる長さにする
```
$m          // ☓：「分数」を表現したいが、短すぎて意味不明
$min       // ◯：よくある略式
$minute  // ◯：意味が明確
```

* ループ変数
i, j, kは単純なループの場合だけにする
ループ変数の名前に意味のある名前を付けると分かりやすくなる

* 避けるべき名前
省略のしすぎや誤解を招くような名前
```
$mm   // memberをmmに省略。ミリメートルに見える
```

汎用的な名前
```
$tmp  // 何を表しているのかがわからない
```

・意味が似ている名前を複数使う
```
$input, $inputValue  // どう違うのかが分からない
```

名前に数字を使わない
```
$file1, $file2  // 数値以外で変数を区別できないか検討する
```

<strong>型を使いこなす<strong>
* マジックナンバーを使わない

* 「0による除算」エラーが起きないようにする
```
100 / $num;  // $numだとエラーになるので注意
```

* 浮動小数点の数値が等しいかを比較しない
```
if ((0.1 + 0.7) === 0.8) {
	echo 'equal';
} else {
	echo 'not equal';
} // not equal
```

* 丸め誤差に注意する
```
const PRICE = 1800;
const TAX = 1.08;
echo ceil(PRICE * TAX); // 1945(1944になってほしい)
var_dump(PRICE * TAX); // 1944.0000000000002
var_dump(ceil(PRICE * 108 / 100)) // 1944
```

* ブール変数を使うと分かりやすくなる
ブール変数を使えばif文の評価の目的が明確になる
```
$cnt = count($inputs);
if ($cnt >= MIN_INPUTS && $cnt <= MAX_INPUTS && validated($inputs) < 0) {
	...
}

// bool変数を使うと
$completed = $cnt >= MIN_INPUTS && $cnt <= MAX_INPUTS;
$noError = validated($inputs) < 0;
if ($completed && $noError) {
	...
}
```

* 配列の範囲外にアクセスしない
```
$numbers = [1, 2, 3];
for ($i = 0; $i <= count($numbers); $i++) {
	echo $numbers[$i];
} // PHP Warning: Undefined array key 3
```

* 多次元の配列の場合、インデックスが正しい順番かチェック
```
$array[j][i]   // 本当はこのつもりが
$array[i][j]   // こちらでアクセスしてしまう
```

<strong>配列を高階関数で操作する</strong>
* 高階関数とは引数や返り値に関数を利用した関数のこと
関数を引数に取っている

下の例だと、array_mapは高階関数
```
function double($n) {
	return $n * 2;
}

array_map('double', [1, 2]);  // Array( [0] => 2 [1] => 4 )
```

配列の要素すべてに関数の処理を行いたい時はarray_mapを使おう
```
array_map($callback, $array) : array
```

配列の全要素を2乗するなら
```
function square($n) {
	return $n * $n;
}

$arr = [2, 4, 6];
$newArr = array_map('square', $arr);
print_r($newArr);  // Array([0] => 4 [1] => 16 [2] => 36)
```

* 配列の要素をフィルタリングする時に使うのがarray_filter
```
array_filter($array, $callback, $mode = 0) : array
```

配列の要素から偶数の要素だけを抽出するなら
```
function even($n) {
	return ($n % 2) === 0;
}

$arr = [1, 2, 3, 4, 5];
$newArr = array_filter($arr, 'even');
print_r($newArr);  // Array([1] => 2 [3] => 4)
```

<strong>演算子を詳しく</strong>
* 条件演算子
条件に応じて採用する値を決めたい場合、三項演算子やnull合体演算子を使うとコンパクトに書ける

三項演算子
条件tureであれば値1、falseであれば値2を返す
(条件) ? (trueのときの値1) : (falseの時の値2)
```
$age = (empty($_POST['age'])) ? 0 : $_POST['age'];
```

null合体演算子
値1がnullだったら値2を、そうでなければ値1を返す
(値1) ?? (値2)
```
$age = $_POST['age'] ?? 0;
```

# 参考サイト

[独学エンジニア](https://dokugaku-engineer.com/course/serverside "独学エンジニア")