hysryt / wiki

https://hysryt.github.io/wiki/
0 stars 0 forks source link

PHP #169

Open hysryt opened 3 years ago

hysryt commented 3 years ago

CLI/CGI

PHPにはCLI用とCGI用が存在する。 CGI用PHPは出力にHTTPヘッダーが付けられる。 https://www.php.net/manual/ja/features.commandline.differences.php

CGI用PHPはFastCGIに対応している。 ただしFastCGIを使う場合はphp-fpmを使った方が設定が楽? (php-fpmは内部的にCGI用PHPを使用している)

HomebrewでPHPをインストールすると php コマンドがCLI用PHP、php-cgi コマンドがCGI用PHPとなる。

$ php -v
PHP 8.0.2 (cli) (built: Feb  4 2021 17:58:53) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.2, Copyright (c), by Zend Technologies
$ php-cgi -v
PHP 8.0.2 (cgi-fcgi) (built: Feb  4 2021 17:59:26)
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.2, Copyright (c), by Zend Technologies

参考 https://wiki.bit-hive.com/tomizoo/pg/PHP%20CLI%2FCGI%20SAPI https://www.php.net/manual/ja/features.commandline.php https://server-setting.info/centos/php-fpm-php-cgi.html

hysryt commented 3 years ago

Apache + PHP(CGI)

$ apt update
$ apt install apache2 php-cgi
$ a2dismod php7.4
$ a2enmod cgi
$ cp /usr/bin/php-cgi /var/www/html
$ service apache2 restart

/var/www/html/test.php

<?php
  echo 'Hello';
?>

/etc/apache/apache.conf

AddHandler php-script .php
ScriptAlias /php-cgi "/var/www/html/php-cgi"
Action php-script /php-cgi
<Directory /var/www/>
  Options +ExecCGI
</Directory>

AddHandler で .php ファイルに php-script ハンドラを設定。 ScriptAlias/var/www/html/php-cgicgi-script ハンドラを設定。 Actionphp-script ハンドラへのリクエスト時は /php-cgi をCGIとして実行するように設定。 Options +ExecCGI/var/www/ 内のファイルをCGIとして実行できるように設定。

php-cgi を使う場合は Action ディレクティブなどを使って REDIRECT_STATUS 変数を渡す必要があるとのこと https://msakamoto-sf-2007-to-2011.hatenadiary.jp/entry/20080802/1217662203 (header関数などを使用する場合は php ではなく php-cgi を使う必要がある)

セキュリティ上の理由から php-cgi のラッパーを作成する場合もある。 https://blog.tokumaru.org/2012/05/php-cgi-remote-scripting-cve-2012-1823.html (PHP7.4で試してみた感じ現在はphp-cgi側で対応されている?) いずれにせよ現在ではCGIを使うことはまずなく、FastCGIかモジュールのどちらかで動作させることが多い。

hysryt commented 3 years ago

Apache + PHP(FastCGI)

https://www.php.net/manual/ja/install.fpm.php

$ apt update
$ apt install apach2 php php-fpm
$ a2dismod php7.4
$ a2enmod proxy_fcgi
$ a2enconf php7.4-fpm
$ service apache2 start
$ service php7.4-fpm start

apache と php-fpm の通信方法にはUNIXソケットとTCPの2種類がある。 デフォルトはUNIXソケット(/run/php/php7.4-fpm.sockになっていた)

/etc/php/7.4/fpm/pool.d/www.conf(デフォルトの設定)

[www]
user = www-data
group = www-data
listen = /run/php/php7.4-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

php-fpmの設定値について https://www.php.net/manual/ja/install.fpm.configuration.php https://qiita.com/r_sui/items/29ec160c4e830c138132 https://hackers-high.com/linux/php-fpm-config/

$ apt install php-fpm
$ which php-fpm7.4
/usr/sbin/php-fpm7.4
$ service php7.4-fpm start

php-fpm はインストール時にサービスとして登録されるため service コマンドで起動できる。


% ab -n 10000 -c 100 http://localhost:8080/test.php
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software:        Apache/2.4.46
Server Hostname:        localhost
Server Port:            8080
Document Path:          /test.php
Document Length:        5 bytes
Concurrency Level:      100
Time taken for tests:   9.438 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1750000 bytes
HTML transferred:       50000 bytes
Requests per second:    1059.58 [#/sec] (mean)
Time per request:       94.377 [ms] (mean)
Time per request:       0.944 [ms] (mean, across all concurrent requests)
Transfer rate:          181.08 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   11  87.5      2    1818
Processing:     7   83 157.2     66    1853
Waiting:        4   75 129.8     63    1820
Total:          8   94 180.2     72    1894
Percentage of the requests served within a certain time (ms)
  50%     72
  66%     77
  75%     81
  80%     83
  90%     91
  95%    106
  98%    344
  99%   1830
 100%   1894 (longest request)

途中から遅くなるのはプロセスが少ないせい?

hysryt commented 3 years ago

Apache + mod_php

% ab -n 10000 -c 100 http://localhost:8080/test.php
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software:        Apache/2.4.46
Server Hostname:        localhost
Server Port:            8080
Document Path:          /test.php
Document Length:        5 bytes
Concurrency Level:      100
Time taken for tests:   6.861 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1710000 bytes
HTML transferred:       50000 bytes
Requests per second:    1457.54 [#/sec] (mean)
Time per request:       68.609 [ms] (mean)
Time per request:       0.686 [ms] (mean, across all concurrent requests)
Transfer rate:          243.40 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9  11.9      2     162
Processing:     5   59  23.5     56     240
Waiting:        3   56  24.2     53     240
Total:          7   68  23.3     64     240
Percentage of the requests served within a certain time (ms)
  50%     64
  66%     70
  75%     74
  80%     77
  90%     84
  95%     91
  98%    172
  99%    216
 100%    240 (longest request)
hysryt commented 3 years ago

エラー

https://qiita.com/mpyw/items/c69da9589e72ceac470c https://qiita.com/tanakahisateru/items/e3e24f3825c4ba0c60e6 https://www.moxio.com/blog/34/best-practices-for-php-exception-handling

用語説明

用語 説明
致命的なエラー E_ERRORに該当するエラー。Fatal Errorともいう。PHP7以降ではErrorを投げる。
警告 E_WARNINGに該当するエラー。Warning。
Error Errorクラス。内部エラー。catch句でキャッチできる。
https://www.php.net/manual/ja/function.set-error-handler.php
コンパイルエラーのようなものなので発生した場合はcatchして処理するのではなくコードを修正するべき。
本番環境では発生してはならない。
Exception Exceptionクラス。ユーザー例外。catch句でキャッチできる。
https://www.php.net/manual/ja/class.exception.php
LogicExceptionとRuntimeExceptionの分類が難しいもの?
ErrorException E_ERROR,E_WORNINGなどを表すための例外。
LogicException Exceptionクラスのサブクラス。
Errorに近い。
コードの不備による例外。Errorと同じように、発生した場合はcatchして処理するのではなくコードを修正するべき。
RuntimeException Exceptionクラスのサブクラス。
実行時の例外。(ユーザーからの入力やネットワーク接続エラーなど)

set_error_handler()

https://www.php.net/manual/ja/function.set-error-handler.php

E_NOTICE、E_WARNING、E_USER_NOTICE、E_RECOVERABLE_ERROR をハンドリングする関数を設定できる。 ハンドラーの中で ErrorException を投げることでキャッチ可能なExceptionに変換できる。

E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNINGはハンドリングできない。

set_error_handler(function($errno, $errstr, $errfile, $errline){
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
});

try {
    file_get_contents('nonexistent file'); // ファイルが存在しないため E_WARNING 発生
} catch (ErrorException $e) {
    var_dump($e);
}

Exception

Exception は LogicException と RuntimeException に大別できる。 ロジック上に問題のあるもの(修正が必要なもの)はLogicException、それ以外はRuntimeException。 発生時点で判別が難しいものはException?

hysryt commented 3 years ago

シリアライズ

オブジェクトのシリアライズには serialize()、シリアライズかされたデータからオブジェクトを復元するには unserialize() を使用する。

class Test() {}

$test = new Test();

$serialized = serialize($test); // シリアライズ
$test = unserliazed($serialized);  // アンシリアライズ

シリアライズ化されたデータはnullバイトを含むバリナリデータとなる。 そのためデータベースに保存する際はTEXT型ではなくBLOB型で保存する必要がある。

hysryt commented 3 years ago

オートロード

ファイルの自動読み込み。 spl_autoload_register() に読み込み用の関数を登録することで、必要になったタイミングで自動でファイルを読み込むことができる。

名前空間が Test から始まるクラスを、autoload.php と同じ階層からオートロードする例。

spl_autoload_register(function(string $class) {
    $targetNamespace = 'Test\\';

    // 管轄外の名前空間のものは無視
    if (!(strpos($class, $targetNamespace) === 0)) {
        return;
    }

    $includePath = __DIR__;
    $classPath = substr($class, strlen($targetNamespace));
    $filePath = str_replace('\\', DIRECTORY_SEPARATOR, $classPath) . '.php';

    $fileFullPath = $includePath . DIRECTORY_SEPARATOR . $filePath;
    if (is_file($fileFullPath) && is_readable($fileFullPath)) {
        require_once($fileFullPath);
    }
});