PHP初期学習まとめ

PHPを仕事で使うことになりそうなので、自分が後から参照するためにまとめてみた。

なお、PHPは、公式の[マニュアル](http://php.net/manual/ja/index.php)が充実している印象である。

# 序論・変数・演算子

* ステートメントは「;」で区切る。
* 変数名は、大文字小文字の区別がある。
* 関数名は、大文字小文字の区別がない。
* 1行コメントは「#」「//」の2種類。
* ブロックコメントは「/* ... */」の1種類。
* 変数名の前には、`$`を付ける。
* グローバル定数は「const HOGE = 1;」のように定義する。
* ローカル定数は「define("HOGE", 1)」のように定義する。
* デバッグ時には、print_r()かvar_dump()を利用すると良さそう。ブラウザ上では、「pre」で囲むと出力が見易くなる。
* 演算子は、C/C++などに似ている。文字列の結合を「.」で行うことができる。
* 厳密な比較は「===」「!==」で行う。(「"99"==99」はtrueになるが「"99"===99」はfalseになる。)
* PHP7から「<=>」の比較演算子が使えるようになった。
* PHP7から「??」が使えるようになった。「a ?? b ?? c」と記載すると、aがNULLのとき、bが評価され、bがNULLのときはcが評価される。途中でNULLがなかった場合にはその値がこの式の値になる。
* キャストは、`(int)$a`のように記載する。int, boo, float, string, array, object, unsetなどがある。unsetはNULLになる。
* `$a instanceof DateTime`は、`$a`という変数がDateTime型のときにtrueになり、それ以外のときにfalseになる。

## 演算子の優先順位

|演算子|結合性|
|:--|:--|
|clone, new|結合しない|
|[|左|
|**|右|
|++, --, ~, (int)などのキャスト, @|右|
|instanceof|結合しない|
|!|右|
|*, /, %|左|
|+, -, .|左|
|<<, >>|左|
|<, <=, >, >=|結合しない|
|==, !=, ===, !==, <>, <=>|結合しない|
|&|左|
|^|左|
|｜|左|
|&&|左|
|｜｜|左|
|??|右|
|?:|左|
|=, +=, -=, *=, **=, /=, .=, %=, &=, ｜=, ^=, <<=, >>=, =>|右|
|and|左|
|xor|左|
|or|左|
|,|左|

## 可変変数

``` php
$vehicle = "car";
$$vehicle = "車"; // $vehicleに入っている値の変数名にアクセスしている。すなわち、$carという変数に「車」という文字列を設定している。
echo $car; // 「車」が出力される。
```

# 制御構文

## if

``` php:if
if(条件式) {
  処理
} else if(条件式) {
  処理
} else {
  処理
}
```

## switch

``` php:switch
switch(式) {
  case 値1:
    処理
    break;
  case 値2:
    処理
    break;
  default:
    処理
    break;
}
```

## while, do-while, for

ループ内の制御として、break, continueが使用できる。

``` php:while
while(条件式) {
  処理
}
```

``` php:do-while
do {
  処理
} while(条件式);
```

``` php:for
for(カウンタの初期化;条件式;カウンタの更新) {
  処理
}
```

# 関数

## 基本形

``` php:関数定義の書式
function 関数名(型 引数1=初期値1, 型 引数2=初期値2, 型 引数3, ...) : 返り値の型 {
  処理
  return 戻り値;
}
```

引数の型や返り値の型は省略可能。

## 可変引数

``` php:可変引数の関数定義の書式
function 関数名(引数1, ...引数3) {
  処理
  return 戻り値;
}
```

具体的には、以下の感じになる。

``` php:可変引数関数の具体例
function hoge($arg1, ...$args) {
  print_r($arg1 . PHP_EOL);
  print_r($args);
}

hoge('a', 'b', 'c', 'd')
```

以下のように出力される。

```
a
Array
(
  [0] => b
  [1] => c
  [2] => d
)
```

## 参照渡し

引数の前に「&」を付けると参照渡しができる。

``` php:参照渡しの関数定義の書式
function hoge($arg1, &$arg2) {
  $arg2 += 10;
}

$v1 = 10;
$v2 = 11;
hoge($v1, $v2)
```

上の例では、hogeを呼び出すことで`$v2`に10が足されて21になっている。

## 可変関数

動的に関数呼び出しを変更できる。
関数が存在するか否かをfunction_exists()で確認できる。

``` php
function func1($hoge) {
  echo "func1 : {$hoge}";
}

function func2($hoge) {
  echo "func2 : {$hoge}";
}

$run = "func2";
if(function_exists($run)) {
  $run('GO');
}
```

## 無名関数

``` php
$func = function($hoge) {
  echo "無名関数 : {$hoge}";
}; // ←代入文なので「;」が必要

$func('GO');
```

呼び出し元にある変数を使用するには以下のように定義する。
但し、funcに代入した時点のvar1が代入されるためfuncに入れた後に、var1を変更した上で、funcを呼び出しても、var1の値はvar1が変更される前の値になる(遅延評価されない)。

``` php
$var1 = 'あいうえお'
$func = function($hoge) use ($var1) {
  echo "無名関数 : {$hoge}" . $var1 ;
}; // ←代入文なので「;」が必要

$func('GO');
```

## その他

* 関数内でグローバル変数にアクセスする場合には`global $変数名;`と宣言した上で、関数内で使用する。

# 文字列操作

* 文字列中で変数展開可能。`"{$var}あいう"`のような感じ。定数は展開できないので注意すること。
* printf, sprintf有り。

## ヒアドキュメント

ダブルクォートで囲んだ文字列と同等(変数展開される)。

``` php
<<< "識別子" // 識別子はダブルクォートで囲む
任意の文字列
識別子;
```

## NowDoc

シングルクォートで囲んだ文字列と同等(変数展開されない)。

``` php
<<< '識別子' // 識別子はシングルクォートで囲む
任意の文字列
識別子;
```

## 文字列操作関数

|関数|機能|
|:--|:--|
|mb_strlen(文字列)|文字数を取得|
|mb_substr(文字列, 文字位置, 文字数)|文字列から文字を取り出す。文字位置に負の数を指定すると後ろから|
|mb_convert_kana(文字列, オプション, エンコーディング)|オプションに応じて文字列を変換(対応表は後述)|
|strtoupper(文字列)|大文字に変換|
|strtolower(文字列)|小文字に変換|
|ucfirst(文字列)|英文の先頭の文字を大文字に変換|
|ucwords(文字列)|英文の各単語の先頭の文字を大文字に変換|
|trim(文字列)|先頭、末尾の半角空白、タブ、改行、キャリッジリターン、NULL、垂直タブを削除。第2引数で他の文字を削除対象に指定できる。(全角空白も追加したいなら"\x20\t\n\r\0\v　"と指定する)|
|ltrim(文字列)|先頭の半角空白、タブ、改行、キャリッジリターン、NULL、垂直タブを削除。第2引数で他の文字を削除対象に指定できる|
|rtrim(文字列)|末尾の半角空白、タブ、改行、キャリッジリターン、NULL、垂直タブを削除。第2引数で他の文字を削除対象に指定できる|
|htmlspecialchars(文字列, ENT_QUOTES, 'UTF-8')|HTML文字をエスケープ。ENT_QUOTESを指定するとダブルクォートとシングルクォートが対象。デフォルトはダブルクォートのみ|
|strip_tags(文字列)|HTMLタグを取り除く|
|rawurlencode(文字列)|URLエンコード(空白文字を%20に変換)|
|urlencode(文字列)|URLエンコード(空白文字を+に変換)|
|rawurldecode(文字列)|rawurlencodeの結果をデコード|
|urldecode(文字列)|urlencodeの結果をデコード|

mb_convert_kanaのオプションについて

|オプション|変換|
|:-:|:--|
|r|全英→半英|
|R|半英→全英|
|n|全数→半数|
|N|半数→全数|
|a|半角の英数記号→全角の英数記号|
|A|全角の英数記号→半角の英数記号|
|s|全角スペース→半角スペース|
|S|半角スペース→全角スペース|
|h|ひらがな→半角カタカナ|
|H|半角カタカナ→ひらがな|
|c|全角カタカナ→ひらがな|
|C|ひらがな→全角カタカナ|
|k|全角カタカナ→半角カタカナ|
|K|半角カタカナ→全角カタカナ|
|V|濁点付きの文字を1文字に変換|

# 配列操作

配列には以下の2種類がある。

* インデックス配列(`$a = [要素1, 要素2, ...];`又は`$a = array(要素1, 要素2, ...);`で初期化可能)
* 連想配列(`$a = [キー1 => 値1, キー2 => 値2, ...];`又は`$a = array(キー1 => 値1, キー2 => 値2, ...);`で初期化可能)

## 配列操作関数

|関数|機能|
|:--|:--|
|count(配列)|要素数を取得|
|explode(文字列, 文字)|文字列から指定された文字で区切って配列にする。rubyのsplitに相当|
|implode(文字, 配列)|配列から指定した文字で区切って文字列にする。rubyのjoinに相当|
|array_splice(&配列, 開始位置, 長さ)|配列の要素を削除する。削除された要素が返却される。|
|array_merge(配列1, 配列2, ...)|連結された配列が返却される。「+」演算子では連結という動きにはならない。連想配列でキーが重複していた場合には後勝ち|
|array_merge_recursive(配列1, 配列2, ...)|連結された配列が返却される。連想配列でキーが重複していた場合には多重配列にする|
|array_combine(配列1, 配列2)|配列1をキー、配列2を値にした連想配列が返却される|
|array_unique(配列)|重複した値を取り除く|
|array_slice(配列, 開始位置, 長さ)|部分配列を作成する|
|array_filter(配列, 関数)|配列要素を引数とした関数がtrueとなる要素のみの配列が返却される|
|array_reverse(配列)|配列の逆順にする|
|in_array(値, 配列)|値が配列に入っていればtrue、いなければfalse|
|array_search(値, 配列)|値が配列に入っていれば最初に見つかった値のキーを返却、見つからなければfalse|
|array_diff(配列1, 配列2, 配列3, ...)|配列1の中で、配列2, 3, ...に入っていない値を配列で返却する|
|str_replace(配列1, 配列2, 配列3)|配列3が置換対象。配列2の文字列を配列3で置換する。インデックス配列が対象|
|str_ireplace(配列1, 配列2, 配列3)|str_replaceの大文字小文字無視バージョン|
|preg_replace(配列1, 配列2, 配列3)|配列3が置換対象。配列2の文字列を配列3で置換する。連想配列が対象。検索・置換対象は値|
|preg_grep(正規表現文字列, 配列)|正規表現文字列にマッチする要素を配列として返却。第3引数にPREG_GREP_INVERTを指定するとマッチしなかった配列を取得できる|

ソートに関するものは別表に抜き出した。

|関数|機能|キーと値の関係性が維持されるか|
|:--|:--|:--|
|sort(&配列)|インデックス配列の昇順ソート|×|
|rsort(&配列)|インデックス配列の降順ソート|×|
|asort(&配列)|連想配列を値で昇順ソート|○|
|arsort(&配列)|連想配列を値で降順ソート|○|
|ksort(&配列)|連想配列をキーで昇順ソート|○|
|krsort(&配列)|連想配列をキーで降順ソート|○|
|natsort(&配列)|自然順のソート|○|
|natcasesort(&配列)|大文字小文字を無視した自然順のソート|○|
|uasort(&配列)|ユーザー定義順に値でソート|○|
|uksort(&配列)|ユーザー定義順にキーでソート|○|
|usort(&配列)|ユーザー定義順に値でソート|×|

## 構文

### 全要素に対する処理をするときの構文(インデックス配列)

``` php
foreach ($array as $value) {
  処理($value)
}
```

### 全要素に対する処理をするときの構文(連想配列)

``` php
foreach ($array as $key => $value) {
  処理($key, $value)
}
```

### インデックス配列を変数に展開

``` php
list($var1, $var2, ...) = 配列;
```

### rubyのmapっぽいやつ

`$userdata`は無くてもよい。
array_walkが成功するとtrueが返却され、失敗するとfalseが返却される。
`$array`がインデックス配列の場合には、`$key`にはインデックス番号が渡される。
array_walkは引数に渡した配列の内容が変化する。

``` php
array_walk(&$array, $callback, $userdata);

fuction コールバック関数名($value, $key, $userdata) {
  処理
}
```

array_mapは引数に渡した配列の内容は変化しない。
インデックス配列のみが対象。
要素数が同じ配列であれば、1〜複数指定できる

``` php
$mapped_array = array_map($callback, $array1, $array2, ...);

fuction コールバック関数名($value1, $value2, ...) {
  処理
}
```

# クラス、トレイト、インターフェース、抽象クラス

* 多重継承不可。
* trait、interface、abstractが存在している。
* interfaceは実装を持てず、abstractは実装を持てる。
* interfaceは、メソッドと定数のみ設定可能。publicのみ設定可能(サンプルには記載していないが、extendsで親インターフェースクラスを指定可能)。
* 1つのクラスは、trait、interfaceを複数利用可能である。
* アクセス指定子は、C++と同等扱いのpublic, protected, privateがある。
* 自身のプロパティ(`$property`)には、`$this->$property`でアクセスする。`$this`を省略するとローカル変数を探しにいってしまう。

``` PHP:クラス関連の宣言例
class クラス名 extends 親クラス名 implements インターフェース名, インターフェース名, ... {
  use トレイト名, トレイト名, ...;
  // クラスメソッド、クラスプロパティ
  public static const 定数名 = 値;
  public static 変数名;
  public static function メソッド名(引数, 引数, ...) {
  }
  // プロパティ
  public 変数名;
  protected 変数名;
  private 変数名;

  // メソッド
  public function メソッド名(引数, 引数, ...) {
  }
  protected function メソッド名(引数, 引数, ...) {
  }
  private function メソッド名(引数, 引数, ...) {
  }
}

interface インターフェース名 {
  const 定数 = 値;
  function 関数名(引数, 引数, ...);
}

abstract class 抽象クラス名 {
  abstract function 抽象メソッド名(引数, 引数, ...);
}

trait トレイト名 {
  // プロパティ
  // メソッド
}
```

クラス関連のサンプルコード。

``` php:class.php
<?php
abstract class Vehicle {
  private $color;

  function __construct($color) {
    $this->color = $color;
  }

  function getColor() {
    return $this->color;
  }

  abstract function run();

  function stop() {
    echo "乗り物停止<br>";
  }
}

trait Audio {
  function turnon() {
    echo "オーディオ電源オン<br>";
  }
}

trait Navigation {
  function turnon() {
    echo "カーナビ電源オン<br>";
    echo "どこに行きますか?" . $this->whereAreYouGoing() . "<br>" ;
  }

  // トレイトを利用するクラスに実装させることができる。
  abstract function whereAreYouGoing();
}

interface SafetyCheck {
  function check();
}

class Car extends Vehicle implements SafetyCheck {
  // トレイトの利用
  use Audio, Navigation {
    // メソッド名の衝突関連
    Audio::turnon as audioTurnon;
    Navigation::turnon as naviTurnon;
    Audio::turnon insteadof Navigation; // 単にturnonがコールされたらAudioのメソッドを呼ぶ
  }

  // プロパティ(public, protected, private)
  const TYPE = '車';
  private $number;

  // コンストラクタ
  function __construct($color, $number) {
    parent::__construct($color);  // 親クラスのコンストラクタを呼び出す。
    $this->number = $number;
  }

  // メソッド(public, protected, private。省略時は、public)
  function run() {
    // 抽象メソッドをオーバーライドして実装
    echo self::TYPE . "：Fast Speed!<br>";
  }

  function stop() {
    echo self::TYPE . "停止<br>";
  }

  // Navigationトレイトを利用する場合に実装が必要なメソッド
  function whereAreYouGoing() {
    return '東京';
  }

  function check() {
    echo "ブレーキ、ミラー、ランプ確認OK<br>";
  }

  // 文字列型にキャストされたときに返却する値。
  function __toString() {
    return self::TYPE . ": {$this->getColor()}";
  }
}

class Bicycle extends Vehicle implements SafetyCheck {
  // トレイトの利用
  const TYPE = '自転車';
  use Navigation;

  // コンストラクタ
  function __construct($color) {
    parent::__construct($color);  // 親クラスのコンストラクタを呼び出す。
  }

  // メソッド(public, protected, private。省略時は、public)
  function run() {
    // 抽象メソッドをオーバーライドして実装
    echo self::TYPE . "：Slow Speed!<br>";
  }

  // Navigationトレイトを利用する場合に実装が必要なメソッド
  function whereAreYouGoing() {
    return '富士山';
  }

  function check() {
    echo "ブレーキ、ランプ確認OK<br>";
  }
}
```

``` php:index.php
<?php
require_once("class.php");
?>

<pre>
<?php
echo "【車】の確認<br>";
$car = new Car('赤', '12-34');
$car->turnon(); // オーディオが起動
$car->naviTurnon();
$car->audioTurnon();
$car->check();
$car->run();
$car->stop();
echo "{$car}<br>"; // 文字列型にキャスト

echo "<br>";

echo "【自転車】の確認<br>";
$bike = new Bicycle('青');
$bike->turnon();
$bike->check();
$bike->run();
$bike->stop();
?>
</pre>
```

``` :実行結果
【車】の確認
オーディオ電源オン
カーナビ電源オン
どこに行きますか?東京
オーディオ電源オン
ブレーキ、ミラー、ランプ確認OK
車：Fast Speed!
車停止
車: 赤

【自転車】の確認
カーナビ電源オン
どこに行きますか?富士山
ブレーキ、ランプ確認OK
自転車：Slow Speed!
乗り物停止
```