PHPの文と式

このスライドは突発で開催された**PHPerKaigi Online(仮)**でしゃべるために用意されたものです。

------

## 前回のあらすじ

------

![PHPカンファレンス関西2020 【公募セッション募集について】 今年も例年どおりPHPカンファレンス関西では公募セッション枠として、有志PHPerの皆様からセッションのスピーカーを募集します。 ※LTのスピーカー募集については後日発表予定です。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/ea7677da-7699-49db-746c-98fa1c60d0e5.png)

------

## コードを自在に操るためのPHP文法入門

> PHPのソースコードを正確に検査したり、ソースコードの一部を書き換えたいと思ったことはありませんか？ PHPにはPHP-Parserという構文解析ライブラリがあり、静的解析ツールのPHPStanやリファクタリングツールのRectorはPHP-Parserをベースにしたプラグインでソースコードを検査したり、ソースコードを書き換えたりすることができます。
>
> しかしながら、構文木を操作するには普段何気なくPHPコードを書く以上のプログラミング言語についての知識が求められます。この発表では構文木を取り扱う前提となるプログラミング言語についての知識、PHP-Parserの構造、PHPStanとRectorそれぞれの拡張方法と実例についても紹介します。

----------

![誠に残念ではございますが、新型コロナウイルスの感染拡大を鑑み、PHPカンファレンス関西2020の開催を断念することになりました。楽しみにして頂いた皆様に心よりお詫び申し上げます。公式サイトの告知は追って更新します。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/ddad16e3-a13e-be6f-e53b-96f74803da72.png)

----------

# :cry:

----------

## 気をとりなおして

----------

## こういう記事を先週書きました

[![Qiita echoとprintの違い](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/a6d1a1d2-c006-9266-bacf-6ceddf003136.png)](https://qiita.com/tadsan/items/e09475093bc336881b20)

----------

## echoとprintの違いを要約すると

* 関数マニュアルに載っているが**関数ではない**
* `echo`と`print`は出力バッファに書き込む
* `echo`は**文**だが`print`は**式**(式文)
* `echo`は複数の引数、`print`は1つの引数

----------

## `echo`は**文**だが`print`は**式**(式文)

-------

## というわけで、今回は文と式に絞って話をします

-------

## クイズ

-------

syntaxエラーにならないのはどちらでしょう。

```php
echo print 1;
print echo 1;
```

-------

## プログラミング言語を習うと初めにこういうことを習う

-------

## 入門者に叩き込まれる謎の用語

* `if`文で条件分岐をします
* `for`文で繰り返します
* 代入文で変数に値を入れます
* `return`文で関数から値を返します

-------

## たいていの人は、それが何なのか悩む前に使いかたをマスターしてしまうので何も困らない

-------

## PHPにはいろんな文と式がある

-------

## キーワードのリスト

<a href="https://www.php.net/manual/ja/reserved.keywords.php"><img width="824" alt="PHPマニュアルに載っているPHPのキーワード(予約語)のリスト" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/b9f0f9fb-0d87-e6df-60e1-f353d90cb66b.png"></a>

-------

## このリストには文と式がごちゃごちゃに載っている

-------

## プログラムの構成要素を分解してみよう

-------

## PHPの場合

```php
<?php

function sum(array $values)
{
    $sum = 0;
    
    foreach ($values as $v) {
        $sum += $v;
    }

    return $sum;
}
```

-------

## 意味をつけていく

```php
<?php // ← PHP開始タグ

function sum(array $values) // ← 関数定義
{
    $sum = 0; // 変数代入
    
    foreach ($values as $v) { // foreach
        $sum += $v; // 変数代入
    }

    return $sum; // 戻り値
}
```

-------

## 文と式を分けていく

```php
<?php // ← PHP開始タグ(特殊)

function sum(array $values) // ← 関数定義「文」
{
    $sum = 0; // 変数代入「式」
    
    foreach ($values as $v) { // foreach「文」
        $sum += $v; // 変数代入「式」
    }

    return $sum; // 戻り値「文」
}
```

-------

## PHPには書く場所が限られる要素がある

-------

## PHPではこういうコードは書けない

```php
<?php

const A = hoge();

function a($a = foo())
{
    return $a;
}
```

-------

## PHPではこういうコードは書けない

何を当たり前のことをという

```php
foreach (function a(){return [];} a() as $b) {
    return $b;
}
```

-------

## 式(expression, expr)とは何か

-------

## 式はこんなところに書ける

ここに書けるのは式

 * `if`の条件を書くところ `if ($v == 1)`
 * `while`の条件を書くところ `while (count($a) !== 0)`
 * 関数呼び出しの引数 `foo($a + 1)`
 * 代入の右辺 `$v = 30 * MINUTES`
 * `return` に書けるもの `return foo() + bar()`
 * `echo`, `print`, `<?= ?>` に渡せるもの

-------

## よく使われる式 (その1)

 * 関数・メソッド呼び出し `printf()` `$obj->foo()` `$func()`
 * 演算子式
   * 論理否定 `!$foo`
   * 比較式 `$a == $b` `$user instanceof User`
   * 算術演算 `1 + 1`
   * 短絡論理演算子 `$n <= 0 && $n < 24` `foo() or bar()`
   * インクリメント・デクリメント `$i++` `--$i`

--------

## よく使われる式 (その2)

 * 無名関数式 `function ($a) { return $a * 2; }` `fn($a) => $a * 2`
 * 無名クラス `new class('foo'){ function __construct($name) { $this->name = $name; } }`
 * 配列リテラル `[$a, $b, 'foo' => foo()]`
 * 三項演算子 `$foo == 'bar' ? 'A' : 'B'`

--------

## よく使われる式 (その3)

 * `()` 式のグルーピング
 * `isset`, `empty`
 * `eval`
 * `assert`
    * PHP5では関数だったがPHP7で言語構造になった
 * `exit` `die`
 * 変数・スカラーリテラル・定数・マジカル定数
   * `$a` `1` `"a"` `true` `HOGE`

-------

## 式の特徴

 * 式の中に再帰的に式を書ける

-------

## こんなものも式

 * `$a = include __DIR__ . '/config.php'`
 * `$b = eval('return 1 + 1;')`
 * `$c = yield 1;`
 * `$d == 1 or die()`
 * `$e = foo() and print $e`

とてもひどいことに、式の種類によって `()` が省略可能かどうかがバラバラで、個別に覚えないといけない。(または編集中にsyntax checkする)

-------

## 式はこんなところにも書けるぞ

 * `foreach` の `as` の左側
 * `foreach (foo() as $f)`
 * `include`, `require`の引数
 * 式の中には任意の組み合せの式が書ける
    * ただし `[] + 1` のような式はPHP7ではパーサーレベルで怒られるようになった

-------

## 文

-------

## よく使われる文

 * 関数/メソッド定義文 `function` 
 * クラス定義文 `class` `interface` `use`
 * 制御構文 `while` `for` `do` `foreach`
 * 出力文 `echo` `<?=`
 * 定数定義文 `const FOO = 1;`
 * インポート文 `use` `use function` `use const` `use ... as`
 * 式文 `$v = BAR ?: buz();`

-------

## 文にはこんなものもある
 
 * キャスト `(int)` `(string)` `(bool)`
 * トレイトの追加 `use`
 * 実行ディレクティブ `declare(strict_types=1);`
 * HTML出力 `?><?php` の外側
 * 静的変数 `static $var`
 * グローバル変数 `global $var`

-------

## 文の分類

-------

## 文の特徴

 * 式は任意の式を組み合わせられる
 * 文は文ごとに決まったルールがある
    * 式を書けるもの
    * 式と文を書けるもの
    * それ以外の記述ができるもの

-------

## 式文 (expression statement)

```php
<?php
require_once ___DIR__ . '/foo.php'; // require式だけの文
$n = 100; // 代入式だけの文
printf("%f", 1.1); // 関数呼び出しだけの文
assert($n < 100); // assert式だけの文
print 1; // print式だけの文
```

-------

## 式文 (expression statement)

 * 全ての式は `;`または`?>` で区切ることで文になる。
 * 式を式文(expression statement)と呼ぶ。
   * ただし、`;`がつくからと言って式文とは限らない。
 * この記事のコード注釈で「式」と書いた場合は、その式を内包する文である

-------

## 複文

複数の文が書ける。普通は`if`や`foreach`と組み合わせて使う。

```php
{
    foo();
    bar();
}
```

-------

## 条件文 `if`, `else`, `elseif`

この`文`には文を一つだけ書けるが、通常は `if ($cond) echo 1;` のように `{}` を付けずに書くことは好まれず、 `if (true) { echo 1; }` のように複文を使って改行して書くことが好まれる。

```php
if (式) 文
if (式) 文 else 文 
if (式) 文 elseif (式) 文
if (式) 文 elseif (式) 文  else 文 
```

別構文

```php
if (式): 文 endif;
if (式): 文 else: 文 endif;
if (式): 文 elseif: (式) 文 endif;
if (式): 文 elseif: (式) 文  else 文 endif;
```

-------

## 繰り返し `while`

```php
while (式) 文
```

別構文

```php
while (式): 文 endwhile;
```

---------

## 複雑な繰り返し `for`

```php
for (最初に実行される式; 継続判定式; 繰り返しごとに実行される式) 文
```

別構文

```php
for (最初に実行される式; 継続判定式; 繰り返しごとに実行される式): 文 endfor;
```

-------

## 文と式文のややこしいケース

初心者殺し

```php
// これは関数定義文なので最後に ; は要らない
function f() { ... }

// これは変数代入式なので最後に ; が必要
$f = function() { ... };
```

-------

## 代入式と代入文のややこしいケース

```php
// これは代入式
$a = 1;

// これはstatic変数の宣言文
static $b = 1;
```

-------

## 特別な式が書ける文

 * 静的に解決できる式だけ書ける (PHP 7)
   * `const`
   * プロパティ

```php
const SEC = 1;
const MINUTES = 60 * SEC;
const HOUR = 60 * MINUTES;
```

-------

## クイズの解答

-------

```php
echo print 1; // echo は文なので式(print)を引数にとれる
print echo 1; // echo は文なので式(print)の引数に書けない
```