phpのコマンドラインオプションの、これなあに？

## このメモについて

久しぶりのQiitaメモになります。
最近は遅まきながらphpも仕事で触れています。それまではちょこちょこと先人が用意してくれたシステムや、WordPressをメンテナンスするくらいで、phpをちゃんと使ったことがなかったので、phpのコマンドラインから色々できるということを知りませんでした...。

改めて、「こんなことできるのね」ということを知りましたので、メモとして上げてみます。

- 動作環境は　macOSです
- 2024年1月: ``php -a`` について追記をしています

## オプション一覧

こちらの情報は、php公式サイトにもありますので、詳細はそちらを参考に。

- http://php.net/manual/ja/features.commandline.options.php

あとは、いくつか使ってみて「知らなかった、便利だ！」と思ったものについて取り上げてみます。

| オプション | 内容  |
|:-----------|:-----------|
| -v      |        バージョンの表示 |
| -a    |  対話モードでphpを実行します。rubyのirb相当のことができます。 | 
| -i    | PHPに関する情報を表示します。（たくさん出てきます！）
| -S adress:port  | ビルトインの簡易webサーバを起動します。  |
| -f ファイル | ファイルを実行します。|
| -l | 構文のチェックをします。|
| -r 'phpのコード'       | コマンドラインで phpの<?..?>タグなしで指定された処理を実行します。 | 
| -B <begin_code>| 入力された行自体が処理される前に、-B の後に指定したコードが実行されます。(よくわからないので、後述！) |
| -F ファイル |引数に指定したファイルから1行ずつ読み込んで処理をします。 (よくわからないので、後述！)|
| -R 'phpのコード'       | それぞれの入力行に対して実行する PHP コードを指定します。(よくわからないので、後述！)|
| -E <end_code>| 入力された全ての行の処理が終わったあとに、-E の後に指定したコードが実行されます。(よくわからないので、後述！) |
| -w | コメントと空白文字を削除してソースを表示します。|

-B, E, R, F オプションについては、helpの簡単なメッセージだけでは何ができるのか良くわかりませんでしたので、この後に man phpを読んで試したことをまとめてみます。

以下、個別のオプションの説明になります。

### 対話モードの例 (-a option)

このオプション知らなかった時は、わざわざファイルにphpのコードを書いたりして配列や関数の動作を確認していました。
知らないって恐ろしい...。

把握している限りでは、他の言語で同様のものはこのような感じ。

- ruby: irb
- perl: perl -de 0
- python: そのまま python

実行結果はこちらです。

```
$ php -a
Interactive shell

php > $array = [
php >     "foo" => "bar",
php >     "bar" => "foo",
php > ];
php > echo $array["foo"];
bar
php > quit // quitもしくはexitで抜けます

```
対話モードでちょっとした関数の動作を確かめたり、自作の関数の動きを確かめるには良いですね！
がんばって勉強します！

#### 追記: AWS SDK for PHP を利用してみます！ (2024年1月)

対話モードでも、PHP のライブラリを読み込んで操作したいことがあるかと思います。
とくにフレームワークを利用したディレクトリ構造ではなくても、対話モードでもパスを指定してライブラリを読み込むことができます。

以下、簡単ですがご紹介します。

**利用した　PHP　の環境**

この記事では　macBook にインストールした php で解説していますが、追記分は Docker Hub に登録されている php:latest のイメージを使いました。

以下、起動したコンテナにシェルをアタッチしての操作になります。
ここでは AWS SDK for PHP を利用してみます。

- 参考: [AWS SDK for PHPバージョン 3 のインストール](https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/getting-started_installation.html)

まずはコンテナ内の php のバージョンと、phar 形式のファイルの取得から。

```bash
root@cf5c6813322c:/work# php -v
PHP 8.3.2 (cli) (built: Jan 19 2024 22:40:23) (NTS)

# phar 形式の AWS SDK for PHP を /work ディレクトリにダウンロードします。
root@cf5c6813322c:/work# curl https://docs.aws.amazon.com/aws-sdk-php/v3/download/aws.phar -O aws.phar

root@cf5c6813322c:/work# ls
aws.phar

# 対話モードで php を起動します。
root@cf5c6813322c:/work# php -a
Interactive shell

php > 
```

さて、ここから　php 内部の操作になります。
取得した aws.phar は require で読み込めば OK です。

```php

# 絶対パスでなくても大丈夫です。
php > require('./aws.phar');

# では SDK のバージョンを確認してみます。
php > var_dump(\Aws\Sdk::VERSION);
string(7) "3.296.6"
```

うまくいきました。

では、次に[公式ドキュメントのサンプル](https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/hello.html)にある、S3 バケット用のクライアントを作成してみます。

```php

# use を使って名前空間のインポートをしてみますが....
php > use Aws\S3\S3Client;  
php > $s3Client = new S3Client([
    'profile' => 'default',
    'region' => 'us-west-2',
    'version' => '2006-03-01'
]);

# 怒られてしまいました！
Warning: Uncaught Error: Class "S3Client" not found in php shell code:1
Stack trace:
#0 {main}
  thrown in php shell code on line 1
```

対話モードの場合は、完全修飾名で利用してみましょう。

```php
# 名前空間をフル指定してクライアントを作成してみます。
php > $s3Client = new Aws\S3\S3Client([
    'profile' => 'default',
    'region' => 'us-west-2',
    'version' => '2006-03-01'
]);

# エンドポイントを取り出してみます。
php > print($s3Client->getEndpoint());
https://s3.us-west-2.amazonaws.com

# リージョンを取り出してみます。
php > print($s3Client->getRegion());
us-west-2

# オブジェクトを返すものは　var_dump してみます。
php > var_dump($s3Client->getConfig());
array(15) {
  ["ignore_configured_endpoint_urls"]=>
  bool(false)
  ["use_fips_endpoint"]=>
  object(Aws\Endpoint\UseFipsEndpoint\Configuration)#44 (1) {
    ["useFipsEndpoint":"Aws\Endpoint\UseFipsEndpoint\Configuration":private]=>
    bool(false)
  }
  ... 以下略 ....
```

このような形で利用ができました。
SDK の ZIP ファイルであれば、zip ファイルを展開し、その直下で php -a で対話モードを起動すれば、以下のような指定で同じく利用できます。

```php
require './autoload.php';
```

クラスの確認、API 呼び出しの確認の参考になれば幸いです。

### ワンライナーでの例 (-r, -R option)

続きまして、ワンライナーで処理を試す系のオプションです。

#### -r オプション

コマンドラインで phpの<?..?>タグなしで指定された処理を実行します。
こちらは操作がイメージしやすいですね。

man php に載っていたサンプルを動かすとこんな感じです。


```source:bash

# 引数には phpの開始タグと終了タグを省略したコードを渡します
$ php -r 'echo "Hello World\n";'
Hello World

$ php -r 'print_r(dba_handlers(1));'
Array
(
    [ndbm] => NDBM
    [cdb] => 0.75, $Id$
    [cdb_make] => 0.75, $Id$
    [inifile] => 1.0, $Id$
    [flatfile] => 1.0, $Id$
)

```

#### -R オプション

このオプションも含め、パッとhelpを見ても分からなかったのが -B, E, R, F オプション。
"Run PHP \<code\> for every input line" とありますが、理解できないのはわたしだけでしょうか...。
php -R と打っただけでは待ち受け状態になってしまいます。

検索しても php -R ....  での情報があまり見つからず、man php してみたところ説明やサンプルが出てきたので、実際の動きから確かめてみます。

##### man php からの抜粋（と意訳）

manの中にこのオプションについて触れている箇所があったので、抜粋＆おおざっぱに訳してみたものを載せてみます。

```

Using parameter -r you can directly execute PHP code simply as you would do inside a .php file when using the eval() function. (-r オプションについてはなんとなくわかるので省略）

It is also possible to process the standard input line by line using either the parameter -R or -F. 
(-R や -F オプションを使うと、標準入力から一行ずつ実行することができます)

In this mode each separate input line causes the code specified by -R or the file specified by -F to be executed. You can access the input line by $argn. While processing the input lines $argi contains the number of the actual line being processed.
（このモードでは、-R での標準入力や -F で指定したファイルの一行ずつが処理対象になります。各行の内容は、$argn という変数でアクセスできます。また、$argi という変数から、実際に処理されている行の番号を取得することができます）

Further more the parameters -B and -E can be used to execute code (see -r) before and after all input lines have  been processed respectively. 
（さらに、-Bと-Eというパラメータを使うと、標準入力の前後で処理を実行することができます：意訳です）

Notice that the input is read from STDIN and therefore reading from STDIN explicitly changes the next input line or skips input lines.
（このモードでの入力内容はSTDINから読み込まれるので、明示的にSTDINから読み込む処理を実行させると、次の行が変更されたりスキップされてしまうので注意してください：このへん意訳です....）

```

なにやら、入力された各行に対して何かを処理するのに使えそうです。

##### ためしてみよう

動きが不明なので、試してみます。manの解説にある通り、各行の内容は\$argnで、行番号は\$argi で取得できるそうなので、テストデータを作ってトライしてみます。

```source:bash

# seqコマンドを使って10から1までカウントダウンするデータを作成（計11行です）
$ seq 10 1
10
9
8
7
6
5
4
3
2
1

# seqの内容をパイプで php -R に渡して表示します
$ seq 10 1 | php -R 'echo "これは $argi 行目の内容です: $argn\n";'
これは 1 行目の内容です: 10
これは 2 行目の内容です: 9
これは 3 行目の内容です: 8
これは 4 行目の内容です: 7
これは 5 行目の内容です: 6
これは 6 行目の内容です: 5
これは 7 行目の内容です: 4
これは 8 行目の内容です: 3
これは 9 行目の内容です: 2
これは 10 行目の内容です: 1
```

seqで生成された内容11行を、それぞれ1行ずつ処理しています。
各行の内容は確かに $argn に格納されています。

これでやっと分かりました....

$argn で行の内容が取得できるので、では明示的にfgetsで標準入力から1行読み込むことをさせてみます。
そうすると、manに記載のあった通り、2行目ごとにスキップされてしまっています....。

```
# $argi が行を取得してくれるので、fgetsで処理しようとすると、次の行がスキップされてしまいます
$ seq 10 1 | php -R 'echo "これは $argi 行目の内容です: $argn\n"; fgets(STDIN);'
これは 1 行目の内容です: 10
これは 2 行目の内容です: 8
これは 3 行目の内容です: 6
これは 4 行目の内容です: 4
これは 5 行目の内容です: 2

# $argn を除いてfgetsの内容だけにすると、2行目ごとの内容が表示されます
$ seq 10 1 | php -R '$l = fgets(STDIN); echo "標準入力から読み込み: $l";'
標準入力から読み込み: 9
標準入力から読み込み: 7
標準入力から読み込み: 5
標準入力から読み込み: 3
標準入力から読み込み: 1

# $argn と fgetsでの取得内容を一緒に表示
# -R オプションの動作で、$argn にはすでに値が入っています
$ seq 10 1 | php -R '$l = fgets(STDIN); echo "\$argn: $argn 標準入力から読み込み: $l";'
$argn: 10 標準入力から読み込み: 9
$argn: 8 標準入力から読み込み: 7
$argn: 6 標準入力から読み込み: 5
$argn: 4 標準入力から読み込み: 3
$argn: 2 標準入力から読み込み: 1
```

ということで、-R オプションを使う場合は、STDINに明示的にアクセスすると予想外の結果にまってしまいそうなので、$argn を使うのが良さそうでしょうか...。

ほかには、入力行からタグ　（HTMLのタグ）を取り除くような例もあります。

```
# こちらはリダイレクトを使って入力
$ php -R 'echo strip_tags($argn)."\n";' < readme.html 
```


#### -B, -E オプションもつけてみよう

"Further more the parameters -B and -E can be used to execute code (see -r) before and after all input lines have  been processed respectively. " ということなので、こちらも実際の動きから確かめてみます。

##### -E オプション

-R をつけていなくても、phpに標準入力で渡された内容は$argn, $argiに格納されるので、-E (入力後の処理）でその変数にアクセスすることができました。簡単なword count(wcコマンド) と同じような処理をしてみます。

```
# man phpにあったサンプルを使ってみます
# 入力行の行数を出力するサンプル（WordPressのreadme.htmlを指定）
$ cat readme.html | php -E 'echo "Lines: $argi\n";'
Lines: 98

# wcコマンドでも確認してみます
$ wc -l readme.html 
      98 readme.html
      
      
# wordpessの直下のディレクトリで、wp-*.phpにマッチするファイルを渡して行数をカウント

$ ls wp-*.php | php -R '@$l+=count(file($argn)); echo "$argn\n";' -E 'echo "Lines:$l\n";'
wp-activate.php
wp-blog-header.php
wp-comments-post.php
wp-config-sample.php
wp-cron.php
wp-links-opml.php
wp-load.php
wp-login.php
wp-mail.php
wp-settings.php
wp-signup.php
wp-trackback.php
Lines:3334

# wcコマンドの結果と比較してみます
$ wc -l wp-*.php
     162 wp-activate.php
      21 wp-blog-header.php
      55 wp-comments-post.php
      93 wp-config-sample.php
     129 wp-cron.php
      80 wp-links-opml.php
      95 wp-load.php
     991 wp-login.php
     246 wp-mail.php
     432 wp-settings.php
     903 wp-signup.php
     127 wp-trackback.php
    3334 total    
```
 
##### -B オプション

-Rや-Eの前処理になる変数などを指定できたりします。この例は単純な例ですが....
                                            
```      
$ seq 3 1 | php -B '$data = 5;' -R 'echo "これは $argi 行目の内容です: $argn -B で渡します: $data\n";'
これは 1 行目の内容です: 3 -B で渡します: 5
これは 2 行目の内容です: 2 -B で渡します: 5
これは 3 行目の内容です: 1 -B で渡します: 5
```

##### -F オプション

-F は、-Bで処理したいスクリプトの内容を、指定したファイルから取得して実行させます。

```
$ echo '<?php echo "Num: $argn - Lines: $argi\n";' > test.php
$ seq 5 1 | php -F test.php 
Num: 5 - Lines: 1
Num: 4 - Lines: 2
Num: 3 - Lines: 3
Num: 2 - Lines: 4
Num: 1 - Lines: 5
```

長くなりましたが、なんとか -E, -R, -Bあたりが分かりました...。


### 構文チェック

```source:bash
$ php -l sample.php 

# このパターンもOK (パイプ、リダイレクト)
$ cat sample.php | php -l
$ php -l < sample.php 
```

### コメントを削除してソースを表示

こんな感じになります。

```source:sample.php
<?php
  // This is a comment....
/*  行コメント
  これもコメント
  */
  echo "php -wのテストです";
  echo "コメントは削除して表示されます";
```

コメントを含むソースを-wオプションで表示させると、このような感じになります。

```source:bash
$ php --strip sample.php 
<?php
 echo "php -wのテストです"; echo "コメントは削除して表示されます"
```

### ビルトインサーバの起動 (-S)

-aと並んで、「これ知っておけばよかった...」と思うことこの上ないほどのオプションです。
標準出力にエラーメッセージ（DBとの接続エラーなど）も出てくるので、大変助かります。

```
# localhost:9000 で起動の例
$ php -S localhost:9000

PHP 5.6.25 Development Server started at Sun Nov 20 08:44:50 2016
Listening on http://localhost:9000
Document root is /xxxxxxx/wordpress
Press Ctrl-C to quit.
[Sun Nov 20 08:45:08 2016] ::1:55829 [200]: /readme.html
[Sun Nov 20 08:45:08 2016] ::1:55831 [200]: /wp-admin/images/wordpress-logo.png
[Sun Nov 20 08:45:08 2016] ::1:55830 [200]: /wp-admin/css/install.css?ver=20100228
[Sun Nov 20 08:45:08 2016] ::1:55832 [200]: /wp-admin/images/wordpress-logo.svg?ver=20131107
[Sun Nov 20 08:45:08 2016] ::1:55833 [404]: /favicon.ico - No such file or directory
.....


# 1024番以下のポートは一般ユーザだとエラーになるのでsudoとかして下さい
$ php -S localhost:80
[Sun Nov 20 08:42:39 2016] Failed to listen on localhost:80 (reason: Permission denied)
$ php -S localhost:90
[Sun Nov 20 08:42:44 2016] Failed to listen on localhost:90 (reason: Permission denied)
```

pythonやnode.jsでも簡易なWebサーバが起動できます。
各言語を利用したWebサーバを起動する方法は、こちらの記事が非常によくまとまっています。ぜひご一読を！

- ワンライナーWebサーバを集めてみた (@sudahiroshi さま)
 - http://qiita.com/sudahiroshi/items/e74d61d939f18779970d


### ビルトインサーバでのデバッグ (with VisualStudio Code)

ここは補足になりますが、ビルトインサーバを利用した場合でも、VisualStudio CodeやPHPStormで問題なくデバッグできます。
また、処理速度やApacheやNginx本来の機能に頼る部分はさておき、ビルトインサーバでもWordpressの動作確認は可能です。

参考までに、キャプチャを貼っておきます。


![php-command-line.png](https://qiita-image-store.s3.amazonaws.com/0/27976/d050385e-6205-7b55-7069-3f1a4f760cc4.png "php-command-line.png")

MAMPやXAMPPを入れると対象ディレクトリの切り替えが面倒だったり、php以外の設定ファイル (httpd.conf)をいじるのが面倒だったりしますので、作業しているその場所を、「サクッとDocumentRootにして動作確認したい！」という場合には非常に助かります。

知らなくてすみませんでした...。


### 関数の情報表示

そのほかのオプションは、だいたい想像がつきそうなので、--rm オプションの表示例のみ挙げてみます。

```source:php
$ php --rf trim
Function [ <internal:standard> function trim ] {

  - Parameters [2] {
    Parameter #0 [ <required> $str ]
    Parameter #1 [ <optional> $character_mask ]
  }
}

$ php --rf rand
Function [ <internal:standard> function rand ] {

  - Parameters [2] {
    Parameter #0 [ <optional> $min ]
    Parameter #1 [ <optional> $max ]
  }
}

$ php --rf dba_handlers
Function [ <internal:dba> function dba_handlers ] {

  - Parameters [1] {
    Parameter #0 [ <optional> $full_info ]
  }
}
```

## まとめ

こんな感じで、あらためてphp で指定できるコマンドラインオプションについて記載してみました。
恥ずかしながら、まだまだ知らない、理解をきちんとしていないところがあるので、不備や間違いなどはご指摘いただけると幸いです。

各言語との比較なども、気がついたら追記補足していきたいと思います。

### 参考にしたページなど

- man php
- PHP公式サイト：オプション一覧
 -http://php.net/manual/ja/features.commandline.options.php
 
- ワンライナーWebサーバを集めてみた (@sudahiroshi さま)
 - http://qiita.com/sudahiroshi/items/e74d61d939f18779970d