PHPの型について

## 自己紹介

はじめまして、日本システム開発株式会社の藤井といいます。
業務でPHPを触ることが多く、勉強してみようと思ってドキュメントを読み、役立ちそうな内容を記事にまとめています。
記事の内容は大体[php.net](https://www.php.net/manual/ja/)に書いてありますが、ドキュメントを読むのが面倒くさいと思った方は、
ぜひこの記事を役立ててください。

## 概要

- 各型の特徴、型の相互変換についてまとめています。
- この記事のコードはPHP8.0で実行しています。
- どのOSでもPHPをインストールすれば実行できる内容になっています。

## 基本の型

4つのスカラー型(一つの値を格納する)

| 名前         | 表記          | 概要                          |
| ------------ | ------------- | ----------------------------- |
| 論理値型     | bool          | 真、偽(true、false)を格納する |
| 整数型       | int           | 整数を格納する                |
| 浮動小数点型 | float(double) | 小数を格納する                |
| 文字列型     | string        | 文字列を格納する              |

4つの複合型(スカラー型、複合型の値を一つ以上格納する)

| 名前         | 表記     | 概要                                           |
| ------------ | -------- | ---------------------------------------------- |
| 配列         | array    | 各値を特定の情報(文字列など)に紐づけて格納する |
| オブジェクト | object   | 各値と動作を格納する                           |
| 呼び出し可能 | callable | 呼び出し可能であることを表す                   |
| 繰り返し可能 | iterable | 繰り返し可能であることを表す                   |

2つの特殊型

| 名前     | 表記       | 概要                      |
| -------- | ---------- | ------------------------- |
| リソース | resource   | php外部のデータを管理する |
| ヌル     | NULL(null) | 値が存在しないことを表す  |

## 各型の説明

各型ごとに説明と、コードサンプルを記載します。  
コードサンプルは`php -a`のインタラクティブモードで実行しています。  
コードサンプルは先頭が`php >`の行がphpの実行コード、  
そうでない行が1行前の実行結果になります。(二重引用符で囲っています)  

変数は先頭に`$`を付けますが、定数は付けません。

### bool

#### 説明

真、偽どちらかを格納する。
真はtrue(TRUE)で表し、偽はfalse(FALSE)で表します。
大文字小文字の区別はしません。

#### コード

```php

php > $trueData = true;
php > echo gettype($trueData);
"boolean"

php > $falseData = false;
php > echo gettype($falseData);
"boolean"
```

### int

#### 説明

10進数、2進数、8進数、16進数の表記で格納することができる。
符号(-または+)を付けることも可能。
2進数は0b、8進数は0(php8.1.0以降は0oも可)、16真数は0xを先頭につけ表記する。

phpは符号なし整数をサポートしておらず、整数のサイズ(バイト数)は定数`PHP_INT_SIZE`で、
整数の最大値は定数`PHP_INI_MAX`で、整数の最小値は定数`PHP_INI_MIN`に格納されている。

最大値、最小値を超えた整数を格納した場合、浮動小数点型になる。

#### コード

整数の格納

```php
php > $decimal = 50;
php > echo "型:".gettype($decimal), ", 値:".$decimal;
"型:integer, 値:50"

php > $binary = 0b110010;
php > echo "型:".gettype($binary), ", 値:".$binary;
"型:integer, 値:50"

php > $octal = 062;
php > echo "型:".gettype($octal), ", 値:".$octal;
"型:integer, 値:50"

php > $hexadecimal = 0x32;
php > echo "型:".gettype($hexadecimal), ", 値:".$hexadecimal;
"型:integer, 値:50"
```

整数のサイズ、最大値、最小値

```php
php > echo "サイズ:". PHP_INT_SIZE ."バイト, ", "最大値:". PHP_INT_MAX .", ", "最小値:". PHP_INT_MIN;
"サイズ:8バイト, 最大値:9223372036854775807, 最小値:-9223372036854775808"
```

整数の最大値、最小値を超えた値の格納

```php
php > $intBitSize = PHP_INT_SIZE * 8;

php > $intMax = 2 ** ($intBitSize - 2) + (2 ** ($intBitSize - 2) - 1);
php > echo "型:".gettype($intMax), ", 値:".$intMax;
"型:integer, 値:9223372036854775807" // PHP_INT_SIZEによって変わります
php > $intMax += 1;
php > echo "型:".gettype($intMax), ", 値:".$intMax;
"型:double, 値:9.2233720368548E+18" // PHP_INT_SIZEによって変わります

php > $intMin = (-2) ** ($intBitSize - 1); //-より**の優先順位が高いため()を使用
php > echo "型:".gettype($intMin), ", 値:".$intMin;
"型:integer, 値:-9223372036854775808" // PHP_INT_SIZEによって変わります
php > $intMin = $intMin - 1;
php > echo "型:".gettype($intMin), ", 値:".$intMin;
"型:double, 値:-9.2233720368548E+18" // PHP_INT_SIZEによって変わります
```

### float(double)

#### 説明

小数を格納します。
指数表記でも格納できます。指数表記の場合、eの後に符号を付けて指数を表現します。
gettypeの結果はdoubleになります。

#### コード

```php
php > $floatData = 1.001;
php > echo "型:".gettype($floatData), ", 値:".$floatData;
"型:double, 値:1.001"

php > $exponentPlus = 1e2;
php > $exponentMinus = 1e-2;
php > echo "型:".gettype($exponentPlus), ", 値:".$exponentPlus;
"型:double, 値:100"
php > echo "型:".gettype($exponentMinus), ", 値:".$exponentMinus;
"型:double, 値:0.01"
```

### string

#### 説明

文字列は4つの方法で格納できます。

- 引用符
- 二重引用符
- ヒアドキュメント構文
- Nowdoc構文

引用符は`'`を意味し、`'`の始まりから終わりまでを文字列として格納します。  
特殊な動作として、文字列に`'`を含めたい場合は`\`でエスケープします。(`\'`)  
同様に、文字列に`\`を含めたい場合は`\`でエスケープします。(`\\`)  
`\'`、`\\`以外の`\`はそのまま格納されます。  
  
二重引用符は`"`を意味し、引用符とほぼ同じ解釈をします。  
`"`、`\`は同様にエスケープを必要とし、エスケープ以外の`\`はそのまま格納されます。  
詳細は[ドキュメント](https://www.php.net/manual/ja/language.types.string.php#language.types.string.syntax.double)を閲覧ください。  
改行コード、水平タブなど特殊な解釈をする文字列も存在します。  
  
引用符、二重引用符ともに複数行の文字列を格納できます。(引用符、二重引用符の始まりから終わりまでが格納されます)
  
ヒアドキュメント構文は、`<<<ID`の形式で記述します。  
```<<<`以後の文字列が終端IDとなり、終端IDまでのすべての文字列を格納します。  
文字列の解釈は二重引用符と同様です。  
  
Nowdoc構文は、`<<<'ID'`の形式で記述します。  
構文は終端IDを引用符で囲むこと以外同じですが、文字列を解釈しません。  
つまり、`\\`はそのまま`\\`として格納されます。  
  
二重引用符、ヒアドキュメント構文では、文字列内の変数を解釈できます。

#### コード

引用符

```php
php > $aiueo = 'aiueo';
php > echo $aiueo;
"aiueo"

php > $escSinglequote = 'I\'m human.';
php > echo $escSinglequote;
"I'm human."
php > $escBackslash = 'C:\\Program Files';
php > echo $escBackslash;
"C:\Program Files"
php > $notEscBackslash = 'C:\Program Files';
php > echo $notEscBackslash;
"C:\Program Files"

php > $multiLine = 'aiueo
php > kakikukeko';
php > echo $multiLine;
"aiueo
kakikukeko"
```

二重引用符

```php
php > $aiueo = "aiueo";
php > echo $aiueo;
"aiueo"

php > $escDoublequote = "\" is double quotation mark.";
php > echo $escDoublequote;
'" is double quotation mark.'
```

ヒアドキュメント構文

```php
php > $heredoc = <<<EOF
php > a  i  u  e  o
php > ka ki ku ke ko
php > EOF;
php > echo $heredoc;
"a  i  u  e  o
ka ki ku ke ko"
```

Newdoc構文

```php
php > $newdoc = <<<'EOF'
php > 1 2 3 4 5
php > a b c d e
php > EOF;
php > echo $newdoc;
"1 2 3 4 5
a b c d e"
```

変数の解釈

```php
php > $dayOfWeek = '水曜日';
php > $parseVariable = "今日は${dayOfWeek}です。";
php > echo $parseVariable;
"今日は水曜日です。"
```

### array

#### 説明

複数の値を格納できる型です。  
各値に対して、数字もしくは文字列に紐づけて格納します。  
  
配列は`[]`で作成できます。  
各データを数字に紐づけて格納する場合は、`['data']`もしくは`[0 => 'data']`、  
文字列に紐づけて格納する場合は、`['sample' => 'data']`で作成します。  
配列を値のみで作成した場合(`['data']`)、値は先頭から0,1,2...の数字で紐づけられます。
  
各データへの取得は、数字もしくは文字列を指定して行います。  
  
※Arrayを出力するためにprint_r関数を利用しています。
詳細は[ドキュメント](https://www.php.net/manual/ja/function.print-r.php)を参照ください。第二引数にtrueを渡すと文字列が返されます。

#### コード

数字に紐づけて格納

```php
php > $fruit = ['apple', 'blueberry', 'cherry'];
php > echo "型:".gettype($fruit), ", 値:". print_r($fruit, true);
"型:array, 値:Array
(
    [0] => apple
    [1] => blueberry
    [2] => cherry
)"

php > $fruitNum = [0 => 'apple', 1 => 'blueberry', 2 => 'cherry'];
php > echo "型:".gettype($fruitNum), ", 値:". print_r($fruitNum, true);
"型:array, 値:Array
(
    [0] => apple
    [1] => blueberry
    [2] => cherry
)"

php > echo $fruit[0];
"apple"
php > echo $fruitNum[0];
"apple"
```

文字列に紐づけて格納

```php
php > $monthJa = ['january' => '1月', 'february' => '2月', 'march' => '3月'];
php > echo "型:".gettype($monthJa), ", 値:". print_r($monthJa, true);
"型:array, 値:Array
(
    [january] => 1月
    [february] => 2月
    [march] => 3月
)"
php > echo $monthJa['january'];
"1月"
```

### object

#### 説明

配列が複数の値を格納できることに対し、オブジェクトは複数の値と動作を格納できます。  
今回はオブジェクトの基本となるstdClassを使用します。  
  
オブジェクトは`new`で作成します。  
値を格納するためには`->`を使用します。取得する際も`->`を使用します。

#### コード

```php
php > $stdobject = new stdClass();
php > $stdobject->cpu = 'intel i7';
php > $stdobject->memory = '8GB';
php > $stdobject->storage = '1TB';

php > echo 'CPU:' .$stdobject->cpu;
"CPU:intel i7"
php > echo 'MEMORY:' .$stdobject->memory;
"MEMORY:8GB"
php > echo 'STORAGE:' .$stdobject->storage;
"STORAGE:1TB"
php > echo "型:".gettype($stdobject), ", 値:". print_r($stdobject, true);
"型:object, 値:stdClass Object
(
    [cpu] => intel i7
    [memory] => 8GB
    [storage] => 1TB
)"
```

### callable

#### 説明

関数であることを表しています。  
callableの変数を作成する方法はなく、[型宣言](https://www.php.net/manual/ja/language.types.declarations.php)で使用されます。

### iterable

#### 説明

繰り返し可能であることを表しています。  
callableと同じく、iterableの変数を作成する方法はなく、[型宣言](https://www.php.net/manual/ja/language.types.declarations.php)で使用されます。

### resource

#### 説明

外部リソースを保持する型です。外部リソースとはファイルなどを指します。  
リソース型は、[特別な関数](https://www.php.net/manual/ja/resource.php)を使用して作成します。  
リソースへの参照を閉じると、gettypeの返り値に`(closed)`が付与されます。

#### コード

```php
php > $file = fopen('sample.txt', 'w+');
php > fwrite($file, "hello world". PHP_EOL);
php > fwrite($file, "Hello World!!". PHP_EOL);

php > rewind($file); //書き込み後、読み込むためにファイルポインタを先頭に戻します
php > echo fgets($file);
php > echo fgets($file);
php > echo "型:".gettype($file), ", 値:". print_r($file, true);
"型:resource, 値:Resource id #2"

php > fclose($file);
php > echo "型:".gettype($file), ", 値:". print_r($file, true);
"型:resource (closed), 値:Resource id #2"
```

### null

#### 説明

変数が値を持たないことを表します。  
null型はnullしか値を持たず、nullは大文字小文字を区別しません。  

変数は以下の3つの場合、nullとみなされます。

- 定数nullが代入されている
- まだ値が何も代入されていない
- unset()されている

変数がnullとみなされるかは、is_null関数を使用して確認できます。  
is_null関数は変数がnullとみなされる場合、trueをそれ以外はfalseを返します。  
  
値が何も代入されていない変数、unset()された変数は存在しなくなり、アクセスできなくなります。  
グローバルスコープで使用可能なすべての変数を保持する、$GLOBALSを使用し、挙動を確認します。

#### コード

```php
php > $displayKeys = ['sample' => 0, 'none' => 1, 'nothing' => 2]; // 今回使用する変数名

php > $sample = 'sample';
php > var_export(is_null($sample));
"false"
php > print_r(array_intersect_key($GLOBALS, $displayKeys));
"Array
(
    [sample] => sample
)"

php > $none;
php > var_export(is_null($none)); // 変数が存在しないため、警告がでます。
"true"
php > print_r(array_intersect_key($GLOBALS, $displayKeys)); // noneは存在しません。
"Array
(
    [sample] => sample
)"

php > $nothing = null;
php > var_export(is_null($nothing));
"true"
php > print_r(array_intersect_key($GLOBALS, $displayKeys)); // nothingは値を代入しているため、存在します。
"Array
(
    [sample] => sample
    [nothing] =>
)"

php > unset($sample); // 変数の
php > var_export(is_null($sample)); // unsetで変数sampleが存在しなくなったため、警告がでます。
"true"
php > print_r(array_intersect_key($GLOBALS, $displayKeys)); // sampleはunsetで削除されています。
"Array
(
    [nothing] =>
)"
```

## 型の変換

型は`(型の名前)`を変数の先頭につけることで、相互に変換することができます。  
`(string) 0`は文字列の0になります。  
echoは渡した値を強制的に文字列に変換するため、今回は値の中身をそのまま出力するvar_dump関数を使用します。  

### スカラー型から複合型への変換

#### 説明

スカラー型(bool, int, float, string)から複合型(array, object)への変換は、
同じ処理をします。  
  
スカラー型をarrayに変換すると、スカラー型の値を先頭に格納した配列に変わります。  
スカラー型をobjectに変換すると、スカラー型の値をscalarプロパティに格納したオブジェクト(stdClass)に変わります。

#### コード

```php
php > $intOne = 1;
php > var_dump((array)$intOne);
'array(1) {
  [0]=>
  int(1)
}'

php > var_dump((object)$intOne);
'object(stdClass)#1 (1) {
  ["scalar"]=>
  int(1)
}'
```

### bool

#### 説明

bool→int  
trueが1にfalseが0に変換されます。  

bool→float  
trueが1.0にfalseが0.0に変換されます。

bool→string  
trueが文字列の1にfalseが空文字列に変換されます。  

#### コード

```php
php > $boolTrue = true;
php > $boolFalse = false;

php > var_dump((int)$boolTrue);
"int(1)"
php > var_dump((int)$boolFalse);
"int(0)"

php > var_dump((float)$boolTrue);
"float(1)"
php > var_dump((float)$boolFalse);
"float(0)"

php > var_dump((string)$boolTrue);
'string(1) "1"'
php > var_dump((string)$boolFalse);
'string(0) ""'
```

### int

#### 説明

int→bool  
0はfalseに変換されます。  
それ以外はtrueに変換されます。  

int→float  
浮動小数点表現に変換されます。(0 → 0.0)  

int→string  
文字列表現に変換されます。(10 → "10")  

#### コード

```php
php > $intBoolZero = 0;
php > $intBoolNonZero = 15;

php > var_dump((bool)$intBoolZero);
"bool(false)"
php > var_dump((bool)$intBoolNonZero);
"bool(true)"

php > $intFloat = 0;
php > var_dump((float)$intFloat);
"float(0)"

php > $intString = 10;
php > var_dump((string)$intString);
'string(2) "10"'
```

### float(double)

#### 説明

float→bool  
0.0および-0.0はfalseに変換されます。  
それ以外はtrueに変換されます。  

float→int  
小数点以下は切り捨てられます。  

float→string  
文字列表現に変換されます。(1.0 → "1.0")

#### コード

```php
php > $floatBoolZero = 0.0;
php > $floatBoolNonZero = 2.0;

php > var_dump((bool)$floatBoolZero);
"bool(false)"
php > var_dump((bool)$floatBoolNonZero);
"bool(true)"

php > $floatInt = 1.5;
php > var_dump((int)$floatInt);
"int(1)"

php > $floatString = 3.5;
php > var_dump((string)$floatString);
'string(3) "3.5"'
```

### string

#### 説明

string→bool  
空文字列および文字列の0がfalseに変換されます。(文字列の0がfalseに変換されるのはphpの特殊な挙動です。)  
それ以外はtrueに変換されます。  

string→int  
値が[数値形式](https://www.php.net/manual/ja/language.types.numeric-strings.php)の場合、対応する数値の整数表現に変換されます。(小数の場合、小数点以下は切り捨てられます)  
それ以外の場合、0に変換されます。  

string→float  
intと同じく、値が[数値形式](https://www.php.net/manual/ja/language.types.numeric-strings.php)の場合、対応する数値の浮動小数点表現に変換されます。(整数の場合、小数になります。)  

#### コード

```php
php > $stringBoolEmpty = "";
php > $stringBoolZero = "0";
php > $stringBoolTrue = "12";

php > var_dump((bool)$stringBoolEmpty);
"bool(false)"
php > var_dump((bool)$stringBoolZero);
"bool(false)"
php > var_dump((bool)$stringBoolTrue);
"bool(true)"

php > $stringIntNum = "10";
php > $stringIntFloat = "10.5";
php > $stringIntNotNum = "aiu";

php > var_dump((int)$stringIntNum);
"int(10)"
php > var_dump((int)$stringIntFloat);
"int(10)"
php > var_dump((int)$stringIntNotNum);
"int(0)"

php > $stringFloatDouble = "1.7";
php > $stringFloatNum = "1";
php > $stringFloatNotNum = "float";

php > var_dump((float)$stringFloatDouble);
"float(1.7)"
php > var_dump((float)$stringFloatNum);
"float(1)"
php > var_dump((float)$stringFloatNotNum);
"float(0)"
```

### 複合型同士の返還

#### 説明

array→object  
配列のキーと値それぞれがオブジェクト(stdClass)のプロパティとその値になります。

object→array  
[ドキュメント](https://www.php.net/manual/ja/language.types.array.php#language.types.array.casting)を参照ください。  
基本、プロパティと値のそれぞれが配列のキーとその値になるようです。  
例外として、プロパティのアクセス権によりキーが変化すること、未初期化の型付きプロパティは削除されることがあるようです。  

#### コード

```php
php > $game = ['darts', 'bowling', 'table-tennis' => '卓球'];
php > var_dump((object)$game);
'object(stdClass)#1 (3) {
  ["0"]=>
  string(5) "darts"
  ["1"]=>
  string(7) "bowling"
  ["table-tennis"]=>
  string(6) "卓球"
}'

php > $sportObj = new stdClass();
php > $sportObj->soccer = 'サッカー';
php > $sportObj->baseball = '野球';
php > $sportObj->basketball = 'バスケットボール';
php > var_dump((array)$sportObj);
'array(3) {
  ["soccer"]=>
  string(12) "サッカー"
  ["baseball"]=>
  string(6) "野球"
  ["basketball"]=>
  string(24) "バスケットボール"
}'
```

## 参考/引用

https://www.php.net/manual/ja/index.php