Quantcast
Channel: PHP7.4タグが付けられた新着記事 - Qiita
Viewing all 113 articles
Browse latest View live

俺のLaravelがこんなに遅いわけがない

$
0
0

Laravel

環境(ベース)

  • PHP 7.4.5
  • Laravel 7.5.1

Docker for Macでnginxとphp-fpmコンテナをunixソケットで繋いだ環境で試してます。

環境の差異

  1. OPcache なし
  2. OPcache あり、プリロードなし
  3. OPcache なし、プリロードあり

比較方法

  • Laravelのwelcome画面をabコマンドの結果で比較します。

Requests per second(1秒間に捌けるリクエスト数)、Time per request(1リクエストあたりの処理時間)に着目します。

以前こんな記事も書いてます。
Apache Bench を初めて使ってみた

OPcacheなし

php.ini(設定例)
[opcache]opcache.enable=0
$ ab -n 1000 -c 100 http://127.0.0.1/
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 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.17.8
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        2426 bytes

Concurrency Level:      100
Time taken for tests:   18.432 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3446000 bytes
HTML transferred:       2426000 bytes
Requests per second:    54.25 [#/sec] (mean)
Time per request:       1843.175 [ms] (mean)
Time per request:       18.432 [ms] (mean, across all concurrent requests)
Transfer rate:          182.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   2.5      0      22
Processing:    79 1744 326.5   1836    2038
Waiting:       69 1743 326.6   1834    2038
Total:         83 1745 324.6   1836    2039

Percentage of the requests served within a certain time (ms)
  50%   1836
  66%   1865
  75%   1883
  80%   1894
  90%   1926
  95%   1948
  98%   1988
  99%   2009
 100%   2039 (longest request)
  • 1秒間に捌けるリクエスト数: 54.25
  • 1リクエストあたりの処理時間: 18.432 (ms)

俺のLaravelがこんなに遅いわけがない...

OPcacheあり、プリロードなし

php.ini(設定例)
[opcache]opcache.enable=1opcache.memory_consumption=128opcache.interned_strings_buffer=8opcache.max_accelerated_files=4000opcache.validate_timestamps=0opcache.huge_code_pages=0
$ ab -n 1000 -c 100 http://127.0.0.1/
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 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.17.8
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        2426 bytes

Concurrency Level:      100
Time taken for tests:   3.318 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3446000 bytes
HTML transferred:       2426000 bytes
Requests per second:    301.41 [#/sec] (mean)
Time per request:       331.772 [ms] (mean)
Time per request:       3.318 [ms] (mean, across all concurrent requests)
Transfer rate:          1014.32 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   4.4      1      24
Processing:    31  316  60.1    319     481
Waiting:        9  311  59.3    316     461
Total:         34  318  57.9    320     483

Percentage of the requests served within a certain time (ms)
  50%    320
  66%    335
  75%    346
  80%    354
  90%    381
  95%    404
  98%    413
  99%    421
 100%    483 (longest request)
  • 1秒間に捌けるリクエスト数: 301.41
  • 1リクエストあたりの処理時間: 3.318 (ms)

54.25から 301.41へおよそ5.55倍の高速化されました!!
圧倒的すぎる速さ!!キャッシュ効果恐るべし!!

OPcacheあり、プリロードあり

php.ini(設定例)
[opcache]opcache.enable=1opcache.memory_consumption=128opcache.interned_strings_buffer=8opcache.max_accelerated_files=4000opcache.validate_timestamps=0opcache.huge_code_pages=0opcache.preload=/var/www/preload.phpopcache.preload_user=www-data

https://github.com/brendt/laravel-preload/blob/master/preload.php

このpreload.phpを参考にしてます。ignoreにいくつか追加してます。
ただお試し的に使ってるので、また内容まとまったら別記事にしたいと思います。

$ ab -n 1000 -c 100 http://127.0.0.1/
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 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.17.8
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        2426 bytes

Concurrency Level:      100
Time taken for tests:   2.878 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3446000 bytes
HTML transferred:       2426000 bytes
Requests per second:    347.40 [#/sec] (mean)
Time per request:       287.850 [ms] (mean)
Time per request:       2.878 [ms] (mean, across all concurrent requests)
Transfer rate:          1169.09 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.3      0       7
Processing:    49  268  46.5    275     344
Waiting:       35  267  46.5    274     342
Total:         54  269  45.5    276     344

Percentage of the requests served within a certain time (ms)
  50%    276
  66%    287
  75%    294
  80%    298
  90%    312
  95%    322
  98%    331
  99%    335
 100%    344 (longest request)
  • 1秒間に捌けるリクエスト数: 347.40
  • 1リクエストあたりの処理時間: 2.878 (ms)

301.41から 347.40へおよそ1.15倍の高速化されました!!

もう俺のLaravelが遅いなんて言わせない...!!

まとめ

# OPcacheなし
Requests per second:    54.25 [#/sec] (mean)
Time per request:       18.432 [ms] (mean, across all concurrent requests)

# OPcacheあり、プリロードなし
Requests per second:    301.41 [#/sec] (mean)
Time per request:       3.318 [ms] (mean, across all concurrent requests)

# OPcacheあり、プリロードあり
Requests per second:    347.40 [#/sec] (mean)
Time per request:       2.878 [ms] (mean, across all concurrent requests)

何も設定してない 54.25の状態から 347.40へおよそ6.4倍と劇的な高速化を遂げました!!!
PHPのポテンシャル半端ないですね!!!

プリロード自体は初めてなので諸々問題出てくるかもしれませんが、何か問題あったらまた記事にしていきたいと思います😃


PHP7.4でもマルチスレッド!!

$
0
0

以前「PHP7.4からのマルチスレッド」という記事を書きましたが、内容が薄すぎたのでもう少し紹介していきたいと思います。コードは全く載せていないのでサンプルはParallelのテストコードを見てください。

なお、PHPにおけるマルチスレッドはCLI限定となります。マルチプロセスを実現するpcntlではwebサーバで使用することを非推奨としていますが、pthreadsやparallelではコンパイルから拒否されます。

拡張モジュール

krakjoe氏が開発しているParallelを使用します。
https://github.com/krakjoe/parallel

マルチスレッドモデル

ParallelはGo言語のgoroutineに大きく影響を受けています。
https://www.php.net/manual/ja/philosophy.parallel.php

parallel\Runtime

RuntimeはpthreadsのWorkerにかなり近いです。
インスタンスが生成されたときにスレッドが起動し、破棄されるときまたはclose()やkill()が呼ばれたときにスレッドをシャットダウンします。run()を呼ぶと引数で指定したクロージャがスケジュールされ、即座にFutureを返します。スケジュールされたクロージャは順次実行されます。

parallel\Future

何の変哲もないFutureパターンの実装です。
cancel()によりスケジュールされたタスクを中断させることができます。

parallel\Channel

仕様はgoroutineのチャンネルとほぼ同じです。Parallelではチャンネルに文字列で名前を設定することができます。匿名でも勝手に名前が付きます。open()によって好きな場所からチャンネル名を指定して特定のチャンネルを得ることができます。また、バッファのサイズを無制限にできます。PHPっぽさが感じられますね。

parallel\Events

複数のFutureやChannelを束ねてデータの受信をイベントとするイベントストリームを提供します。また、poll()でイベントを受け取れますが、データの送信元となったFutureやChannelはイベントストリームから削除されます。継続して同じChannelからのイベントを受け取りたい場合、Event\Type::ReadであろうとEvent\Type::WriteであろうとaddChannel()によって再登録が必要です。

parallel\Events\Input

複数のチャンネルにそれぞれ指定したデータを送信する操作を定義することができます。1つのチャンネルに複数のデータを送信することはできません。Eventsにセットすることで、ストリームが動き始めたタイミングで各チャンネルにデータを送信され、Event\Type::Writeのイベントが発生します。また、Inputに設定した項目は送信されたものから削除されていきます。

parallel\Sync

昔ながらの低レベルの実装をしたければこれを使いましょう。ほとんどのケースではChannelの方が優れているとのこと。

PHPのCallableについて完全に理解する

$
0
0

はじめに

Callableという擬似型は PHP 5.4系で導入されていたようなのですが、ある値が Callableかどうかがどのように決まるのかさっぱり理解できず、業務で使う機会もそんなになかったことから放置していました。

業務で使う機会が出てきたというわけでも全くもって全くないのですが、ふと考えてみたら完全に理解できた気がしてしまったので、ツッコミ覚悟で記事にまとめることにしました。

この記事ではどんな値を is_callable()に渡したら trueを返されるのかを探っていきます。 is_callable()trueを返す値はそのまま call_user_func() , call_user_func_array()などに渡して呼び出すことが可能です。

var_dump()を含んだコードの動作確認は paiza.IOで行っています。執筆時のPHPバージョンは PHP 7.4.1でした。

この記事のスコープ外

「関数やクラスがどの時点で定義されるのか」というのは、これはこれで複雑な話題です。

この記事では、「関数やクラスが定義されているかどうか」を明確にした上で Callableかどうかの判定方法を理解したいため、関数やクラスの定義方法は最もシンプルなもののみを用います。

<?php// シンプルな関数定義functionsomeFunction(){}// シンプルなクラス定義classSomeClass(){publicstaticfuncionSomeClassMethod(){}publicfunctionSomeObjectMethod(){}}// シンプルでない関数定義たちif(true){functionanotherFunction(){}}functiondefineYetAnotherFunction(){functionyetAnotherFunction(){}}// シンプルでないクラス定義たちif(true){classAnotherClass(){publicstaticfuncionAnotherClassMethod(){}publicfunctionAnotherObjectMethod(){}}}functiondefineYetAnotherClass(){classYetAnotherClass(){publicstaticfuncionYetAnotherClassMethod(){}publicfunctionYetAnotherObjectMethod(){}}}

::を含まない文字列

文字列と同じ名前の関数が定義されている場合は Callableであり、そうでない場合は Callableでない、と判定されます。

<?phpfunctionis_odd(int$int):bool{return$int%2===1;}var_dump(is_callable('is_odd'));// => bool(true)var_dump(is_callable('is_even'));// => bool(false)

::の両側になんかある文字列

::の左側の文字列と同じ名前のクラスが定義されていて、かつ ::の右側の文字列と同じ名前のメソッドが定義されていて publicな場合は Callableであり、そうでない場合は Callableでないと判定されます。

<?phpclassSomeClass{publicstaticfunctionsomePublicClassMethod(){}protectedstaticfunctionsomeProtectedClassMethod(){}privatestaticfunctionsomePrivateClassMethod(){}publicfunctionsomePublicObjectMethod(){}protectedfunctionsomeProtectedObjectMethod(){}privatefunctionsomePrivateObjectMethod(){}}var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(false)var_dump(is_callable('AnotherClass::somePublicClassMethod'));// => bool(false)var_dump(is_callable('AnotherClass::somePublicObjectMethod'));// => bool(false)var_dump(is_callable('SomeClass::anotherPublicClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::anotherPublicObjectMethod'));// => bool(false)

クラスのメソッド内で Callableかどうかを判定する場合、 ::の左側の文字列には追加で self , parentが許可され、 ::の右側の文字列には追加で protected , privateなメソッドの名前が許可されます。

<?phpclassParentClass{publicstaticfunctionsomePublicClassMethod(){}publicfunctionsomePublicObjectMethod(){}}classSomeClassextendsParentClass{publicstaticfunctionclassMethod(){var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('self::somePublicClassMethod'));// => bool(true)var_dump(is_callable('parent::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('self::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('parent::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(true)}publicstaticfunctionsomePublicClassMethod(){}protectedstaticfunctionsomeProtectedClassMethod(){}privatestaticfunctionsomePrivateClassMethod(){}publicfunctionobjectMethod(){var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('self::somePublicClassMethod'));// => bool(true)var_dump(is_callable('parent::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('self::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('parent::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(true)}publicfunctionsomePublicObjectMethod(){}protectedfunctionsomeProtectedObjectMethod(){}privatefunctionsomePrivateObjectMethod(){}}SomeClass::classMethod();(newSomeClass())->objectMethod();

Closureオブジェクト

無名関数式を使って作成した Closureオブジェクトは Callableと判定されます。

<?phpvar_dump(is_callable(function():bool{returntrue;}));// => bool(true)

__invoke()を実装したオブジェクト

public function __invoke()を実装したオブジェクトは Callableと判定されます。

<?phpvar_dump(is_callable(newclass(){publicfunction__invoke():bool{returntrue;}}));// => bool(true)

[クラス名, ::を含まない文字列]

※力尽きたので一旦記事は公開する

[クラス名, ::の両側になんかある文字列]

※力尽きたので一旦記事は公開する

[オブジェクト, ::を含まない文字列]

※力尽きたので一旦記事は公開する

[オブジェクト, ::の両側になんかある文字列]

※力尽きたので一旦記事は公開する

MacOSをCatalinaにしてphpが動作しなくなったら

$
0
0

MacOSのバージョンアップ時の定期事故である、phpenvの再インストール

phpが動かなくなった時の対応

よくmacosのバージョンをあげたらいきなりphpが動かなくなることが多いです。
今まで毎回そうだったのでもう定期事故として考えています。面倒だけど、仕方なく新しいの使いたいから今回もバージョンアップ!

$ php -v
dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib
  Referenced from: /Users/syokatsu/.phpenv/versions/7.3.13/bin/php
  Reason: image not found
zsh: abort      php -v

やはりこれか〜!
icu4cの問題は以前もあったので、気軽に次のステップへ

まずはphp-buildのバージョンアップから!

php-buildのバージョンアップ

$ cd ~/.phpenv/plugins/php-build
$ git pull

今まではphp7.3系を利用していたが、php7.4が落ちてきたのでそろそろphp7.4系をinstallすることに!

php7.4のinstall

関連libraryインストール。必要なのは色々あるはずなので、必要な物をinstall

$ brew install bzip2 icu4c krb5 libedit libzip oniguruma openssl@1.1 pkg-config tidy-html5
$ vi ~/.phpenv/plugins/php-build/share/php-build/default_configure_options
~~
この内容を追加
--with-zlib-dir=/usr/local/opt/zlib
--with-bz2=/usr/local/opt/bzip2
--with-libedit=/usr/local/opt/libedit
~~

$ vi ~/.zshrc
export PKG_CONFIG_PATH="/usr/local/opt/krb5/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libedit/lib/pkgconfig:/usr/local/opt/libjpeg/lib/pkgconfig:/usr/local/opt/libpng/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig:/usr/local/opt/libzip/lib/pkgconfig:/usr/local/opt/oniguruma/lib/pkgconfig:/usr/local/opt/openssl@1.1/lib/pkgconfig:/usr/local/opt/tidy-html5/lib/pkgconfig" 

phpのinstall

$ phpenv install 7.4.6
...
[Success]: Built 7.4.6 successfully.

$ phpenv global 7.4.6
$ php -v
PHP 7.4.6 (cli) (built: May 23 2020 01:07:57) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies
    with Xdebug v2.9.5, Copyright (c) 2002-2020, by Derick Rethans

Laravel CSVの出力処理を実装する

$
0
0

概要

Laravelを使って、CSVファイルを出力するサンプルを作成します。

背景

データベースやファイルアクセスをしないテストの方法、インターフェースやジェネレータを使ったコードの書き方等、個別に詳しく書かれた記事はあれど実際使うにはどう書き始めたらいいのかベストプラクティスがわかりませんでした。

日々の業務や副業、勉強会を通じてようやく自分の中で少しずつイメージができてきたので現時点で最高のアウトプットをしていこうと思いました。

目的

この記事ではテストを意識したコードかつ、シンプルに書くことを目標にしてます。
より良いコードにしたいのでアドバイスもらえたらうれしいです。

説明不足なところがあったら補足を追記するので気軽に質問等もいただけたら嬉しいです。

環境

  • PHP 7.4.1
  • Laravel 6.14.0
  • MySQL 8.0.19

サンプルコード

https://github.com/ucan-lab/learn-laravel-export-csv

$git clone git@github.com:ucan-lab/learn-laravel-export-csv.git
$cd learn-laravel-export-csv
$make install$make app
$php artisan migrate:fresh --seed#csv出力コマンド
$php artisan export:user
#テスト実行
$./vendor/bin/phpunit

今回のゴール

スクリーンショット 2020-02-20 12.18.33.png

usersテーブルに入ってるデータをcsv出力する処理を作るところまでゴールとします。

名前,メールアドレス,作成日,更新日
PROF. RACHELLE KUHIC I,leola.rath@example.com,2020-02-01 23:59:59,2020-02-01 23:59:59
JAYLON WOLF,osinski.fernando@example.net,2020-02-01 23:59:59,2020-02-01 23:59:59
LELAND DECKOW,bokon@example.org,2020-02-01 23:59:59,2020-02-01 23:59:59

名前の列は大文字に変換して出力する仕様です。

ベースのコード

環境はこちらのコードを丸コピしてます。

追加したファイル一覧

https://github.com/ucan-lab/learn-laravel-export-csv/pull/1

src/app/Console/Commands/ExportUserCommand.php
src/app/Domain/UserRow.php
src/app/Domain/UserRowHeader.php
src/app/Http/Controllers/Auth/RegisterController.php
src/app/Infrastructure/Adapter/DbUserRepository.php
src/app/Infrastructure/Adapter/FileUserCsvExport.php
src/app/Infrastructure/Adapter/InMemoryUserCsvExport.php
src/app/Infrastructure/Adapter/InMemoryUserRepository.php
src/app/Infrastructure/Eloquent/User.php
src/app/Infrastructure/Port/Export.php
src/app/Infrastructure/Port/UserRepository.php
src/app/Providers/AppServiceProvider.php
src/app/UseCase/UserCsvExportUseCase.php
src/database/factories/UserFactory.php
src/database/seeds/DatabaseSeeder.php
src/database/seeds/UsersTableSeeder.php
src/tests/Unit/UserCsvExportUseCaseTest.php

マイグレーション(テーブル)の確認

今回はLaravelが元々用意してくれている users テーブルをそのまま使います。

src/database/migrations/2014_10_12_000000_create_users_table.php
<?phpuseIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;useIlluminate\Support\Facades\Schema;classCreateUsersTableextendsMigration{/**
     * Run the migrations.
     *
     * @return void
     */publicfunctionup(){Schema::create('users',function(Blueprint$table){$table->bigIncrements('id');$table->string('name');$table->string('email')->unique();$table->timestamp('email_verified_at')->nullable();$table->string('password');$table->rememberToken();$table->timestamps();});}/**
     * Reverse the migrations.
     *
     * @return void
     */publicfunctiondown(){Schema::dropIfExists('users');}}

MySQLのテーブル定義も確認しておきます。

$make mysql
mysql>desc users;+-------------------+-----------------+------+-----+---------+----------------+
| Field             | Type            | Null | Key | Default | Extra          |
+-------------------+-----------------+------+-----+---------+----------------+
| id                | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| name              | varchar(255)    | NO   |     | NULL    |                |
| email             | varchar(255)    | NO   | UNI | NULL    |                |
| email_verified_at | timestamp       | YES  |     | NULL    |                |
| password          | varchar(255)    | NO   |     | NULL    |                |
| remember_token    | varchar(100)    | YES  |     | NULL    |                |
| created_at        | timestamp       | YES  |     | NULL    |                |
| updated_at        | timestamp       | YES  |     | NULL    |                |
+-------------------+-----------------+------+-----+---------+----------------+

app/User.php => app/Infrastructure/Eloquent/User.php

LaravelのEloquentモデルはデフォルトだとapp直下に配置されます。
app/Infrastructure/Eloquent/User.phpへ移動します。
依存するファイルも合わせて修正します。詳細はコミットログ参照

シーダーの作成

Laravelには、シーディングモデルファクトリFakerが用意されており、ダミーデータを簡単に作成できます。

$php artisan make:seeder UsersTableSeeder

src/database/seeds/UsersTableSeeder.phpシーダーのひな形クラスを作ってくれるので下記のように追記します。

src/database/seeds/UsersTableSeeder.php
<?phpdeclare(strict_types=1);useApp\Infrastructure\Eloquent\User;useIlluminate\Database\Seeder;classUsersTableSeederextendsSeeder{/**
     * Run the database seeds.
     *
     * @return void
     */publicfunctionrun(){factory(User::class,3)->create();}}

上記の用にシーダーを追加するだけで、テストデータを3件作成してくれます。
Userモデルクラスの各プロパティにどんなデータが入るかの定義はモデルファクトリで定義されてます。

src/database/factories/UserFactory.php
<?phpdeclare(strict_types=1);/** @var \Illuminate\Database\Eloquent\Factory $factory */useApp\Infrastructure\Eloquent\User;useFaker\GeneratorasFaker;useIlluminate\Support\Str;/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/$factory->define(User::class,function(Faker$faker){return['name'=>$faker->name,'email'=>$faker->unique()->safeEmail,'email_verified_at'=>now(),'password'=>'$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',// password'remember_token'=>Str::random(10),];});

元々用意されている src/database/seeds/DatabaseSeeder.phpUsersTableSeederを呼び出す記述を追記します。

src/database/seeds/DatabaseSeeder.php
<?phpdeclare(strict_types=1);useIlluminate\Database\Seeder;classDatabaseSeederextendsSeeder{/**
     * Seed the application's database.
     *
     * @return void
     */publicfunctionrun(){$this->call(UsersTableSeeder::class);}}

UserRow, UserRowHeader ドメインを定義

この辺りから本題です。

App\Domain\UserRowHeader

app/Domain/UserRowHeader.php
<?phpdeclare(strict_types=1);namespaceApp\Domain;finalclassUserRowHeader{privateconstEOF="\n";privateconstHEADER=['名前','メールアドレス','作成日','更新日',];publicstaticfunctiontoCsv():string{returnimplode(',',self::HEADER).self::EOF;}}

UserRowHeader ドメインクラスではCSVのヘッダー行となる1行目の定義をしてます。

App\Domain\UserRow

app/Domain/UserRow.php
<?phpdeclare(strict_types=1);namespaceApp\Domain;useCarbon\Carbon;finalclassUserRow{privateconstEOF="\n";privateconstDATE_FORMAT='Y-m-d H:i:s';privatestring$name;privatestring$email;privateCarbon$createdAt;privateCarbon$updatedAt;publicfunction__construct(string$name,string$email,Carbon$createdAt,Carbon$updatedAt){$this->name=$name;$this->email=$email;$this->createdAt=$createdAt;$this->updatedAt=$updatedAt;}/**
     * @return string
     */publicfunctiontoCsv():string{returnimplode(',',$this->toArray()).self::EOF;}/**
     * @return array
     */privatefunctiontoArray():array{return[$this->getName(),$this->email,$this->createdAt->format(self::DATE_FORMAT),$this->updatedAt->format(self::DATE_FORMAT),];}/**
     * @return string
     */privatefunctiongetName():string{returnstrtoupper($this->name);}}

UserRow ドメインクラスではCSVの1行の定義をしてます。
ユーザー名は大文字や日付のフォーマット等の業務ロジックはここにまとめます。

UserCsvExportUseCase を定義

app/Infrastructure/Export.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Port;interfaceExport{publicfunctionprepare(string$header):void;publicfunctionwrite(string$row):void;publicfunctiondisorganize():void;}

Export インターフェースを継承するクラスは prepare(前処理)、write(書き込み)、disorganize(後処理)のメソッドを契約します。

app/Infrastructure/UserRepository.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Port;useGenerator;interfaceUserRepository{publicfunctionfindAll():Generator;}

UserRepository インターフェースを継承するクラスはfindAll(全件取得)のメソッドを契約します。

app/UseCase/UserCsvExportUseCase.php
<?phpdeclare(strict_types=1);namespaceApp\UseCase;useApp\Domain\UserRow;useApp\Domain\UserRowHeader;useApp\Infrastructure\Port\Export;useApp\Infrastructure\Port\UserRepository;finalclassUserCsvExportUseCase{/**
     * @var UserRepository
     */privateUserRepository$repository;/**
     * @var Export
     */privateExport$export;/**
     * @param UserRepository $repository
     * @param Export $export
     */publicfunction__construct(UserRepository$repository,Export$export){$this->repository=$repository;$this->export=$export;}/**
     * @return void
     */publicfunctionhandle():void{$this->export->prepare(UserRowHeader::toCsv());/** @var UserRow $row */foreach($this->repository->findAll()as$row){$this->export->write($row->toCsv());}$this->export->disorganize();}}

UserCsvExportUseCase ユースケースクラスはUserRepositoryとExportインターフェースに依存します。
前処理して、全件取得して、書き込みして、後処理して終わるシンプルな作りにできました。
各インターフェースを契約するクラスの中身はあとで書きます。

ユースケースのテストを書く

テストを書く際、データベースやファイルに直接読み書きするようなテストを書いてしまうと
最初は問題ないですが、テストが増えるにつれてテストの実行速度がどんどん落ちてしまいます。

そのため、テストを書く際はデータベースやファイルアクセスが発生しないようメモリ内で良い感じのテストを書きます。

InMemoryUserCsvExport

app/Infrastructure/Adapter/InMemoryUserCsvExport.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Adapter;useApp\Infrastructure\Port\Export;finalclassInMemoryUserCsvExportimplementsExport{/**
     * @var string
     */publicstring$file;/**
     * @param string $header
     */publicfunctionprepare(string$header):void{$this->file=$header;}/**
     * @param string $row
     */publicfunctionwrite(string$row):void{$this->file.=$row;}/**
     * @return void
     */publicfunctiondisorganize():void{}}

Exportインターフェースを契約したInMemoryUserCsvExportクラスを実装します。
やってることは簡単で、prepareメソッドで$fileプロパティに文字列を入れてwriteメソッドが呼ばれたらどんどん追記する形です。
実際にファイルアクセスする場合はdisorganizefclose等の処理を入れますが、ファイルアクセスしないので関数だけ定義してます。

InMemoryUserRepository

app/Infrastructure/Adapter/InMemoryUserRepository.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Adapter;useApp\Domain\UserRow;useApp\Infrastructure\Eloquent\User;useApp\Infrastructure\Port\UserRepository;useGenerator;finalclassInMemoryUserRepositoryimplementsUserRepository{privatearray$usersAttributes;/**
     * @param array $users
     */publicfunction__construct(array$users){$this->usersAttributes=$users;}/**
     * @return Generator
     */publicfunctionfindAll():Generator{foreach($this->usersAttributesas$userAttributes){yield$this->makeUserRow(factory(User::class)->make($userAttributes));}}/**
     * @param User $user
     * @return UserRow
     */privatefunctionmakeUserRow(User$user):UserRow{returnnewUserRow($user->name,$user->email,$user->created_at,$user->updated_at);}}

補足: ジェネレータ

findAllの戻り値の型としてGeneratorオブジェクトを返すと呼び出した側はforeachを使って順に呼び出すことができます。
returnではなくyieldを指定します。
yieldUserRowのインスタンスを返してます。

// UserCsvExportUseCase で findAll を foreach でループ処理できます。foreach($this->repository->findAll()as$row){$this->export->write($row->toCsv());}

ジェネレータのメリットはforeachでループ処理するために巨大な配列を持つ必要がなく1件処理が終わったらメモリを解放して次の処理を実行してくれるので、バッチ処理等のメモリをたくさん使いそうな場合に効果を発揮します。

UserCsvExportUseCaseTest

先ほど作成したInMemoryUserRepositoryInMemoryUserCsvExportを使ってテストコードを書きます。

tests/Unit/UserCsvExportUseCaseTest.php
<?phpdeclare(strict_types=1);namespaceTests\Unit;useApp\Infrastructure\Adapter\InMemoryUserCsvExport;useApp\Infrastructure\Adapter\InMemoryUserRepository;useApp\UseCase\UserCsvExportUseCase;useTests\TestCase;finalclassUserCsvExportUseCaseTestextendsTestCase{/**
     * @param array $users
     * @param string $expectedCsv
     * @dataProvider dataResolve
     */publicfunctiontestResolve(array$users,string$expectedCsv):void{$repository=newInMemoryUserRepository($users);$export=newInMemoryUserCsvExport();$useCase=newUserCsvExportUseCase($repository,$export);$useCase->handle();$this->assertEquals($expectedCsv,$export->file);}/**
     * @return array
     */publicfunctiondataResolve():array{return['正常3件'=>$this->case正常3件(),'正常0件'=>$this->case正常0件(),];}/**
     * @return array
     */publicfunctioncase正常3件():array{$usersAttributes=[['name'=>'yamada','email'=>'yamada@example.com','created_at'=>'2020-01-01 00:00:00','updated_at'=>'2020-01-01 00:00:00'],['name'=>'suzuki','email'=>'suzuki@example.com','created_at'=>'2020-01-01 00:00:00','updated_at'=>'2020-01-01 00:00:00'],['name'=>'tanaka','email'=>'tanaka@example.com','created_at'=>'2020-01-01 00:00:00','updated_at'=>'2020-01-01 00:00:00'],];$expectedCsv=<<<EOT名前,メールアドレス,作成日,更新日YAMADA,yamada@example.com,2020-01-0100:00:00,2020-01-0100:00:00SUZUKI,suzuki@example.com,2020-01-0100:00:00,2020-01-0100:00:00TANAKA,tanaka@example.com,2020-01-0100:00:00,2020-01-0100:00:00EOT;return[$usersAttributes,$expectedCsv,];}/**
     * @return array
     */publicfunctioncase正常0件():array{$usersAttributes=[];$expectedCsv=<<<EOT名前,メールアドレス,作成日,更新日EOT;return[$usersAttributes,$expectedCsv,];}}

想定している $expectedCsvの値とユースケースを実行して作成された値 $export->fileが一致すればokです。

補足: dataProvider

PHPUnitのdataProviderについて補足です。
PHPUnitを実行する際に --debugオプションを付けると詳細ログが見れます。
dataProviderで作った引数もログに出てくるので分かりやすくなります。

データプロバイダ | phpunit.readthedocs.io

$./vendor/bin/phpunit --debugPHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Test 'Tests\Unit\ExampleTest::testBasicTest' started
Test 'Tests\Unit\ExampleTest::testBasicTest' ended
Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常3件" (array(array('yamada', 'yamada@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('suzuki', 'suzuki@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('tanaka', 'tanaka@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00')), '名前,メールアドレス,作成日,更新日\nYAMADA,ya...0:00\n')' started
Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常3件" (array(array('yamada', 'yamada@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('suzuki', 'suzuki@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00'), array('tanaka', 'tanaka@example.com', '2020-01-01 00:00:00', '2020-01-01 00:00:00')), '名前,メールアドレス,作成日,更新日\nYAMADA,ya...0:00\n')' ended
Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常0件" (array(), '名前,メールアドレス,作成日,更新日\n')' started
Test 'Tests\Unit\UserCsvExportUseCaseTest::testResolve with data set "正常0件" (array(), '名前,メールアドレス,作成日,更新日\n')' ended
Test 'Tests\Feature\ExampleTest::testBasicTest' started
Test 'Tests\Feature\ExampleTest::testBasicTest' ended


Time: 3.04 seconds, Memory: 20.00 MB

OK (4 tests, 4 assertions)

CSVの出力処理を実装する

テストコードが書けたところで、実際にデータベースから取得してCSVファイルを出力する処理を実装します。

DbUserRepository

app/Infrastructure/Adapter/DbUserRepository.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Adapter;useApp\Domain\UserRow;useApp\Infrastructure\Eloquent\User;useApp\Infrastructure\Port\UserRepository;useGenerator;finalclassDbUserRepositoryimplementsUserRepository{/**
     * @return Generator
     */publicfunctionfindAll():Generator{/** @var User $user */foreach(User::query()->cursor()as$user){yieldnewUserRow($user->name,$user->email,$user->created_at,$user->updated_at);}}}

UserRepositoryを契約したDbUserRepositoryクラスです。

補足: User::query()->cursor()

cursor()を使うとPDOStatement::fetch
結果セットから1行ずつ取得できます。 cursor()の返り値もジェネレータオブジェクトになります。

User::query()->cursor()ではなく User::all()も動作するかと思いますが、一度に大量のデータを取得するのでデータ件数によってはメモリオーバーになってしまう懸念があります。

FileUserCsvExport

app/Infrastructure/Adapter/FileUserCsvExport.php
<?phpdeclare(strict_types=1);namespaceApp\Infrastructure\Adapter;useApp\Infrastructure\Port\Export;finalclassFileUserCsvExportimplementsExport{/**
     * @var string
     */privatestring$streamFilePath;/**
     * @var resource
     */private$handle;/**
     * @param string $header
     * @return void
     */publicfunctionprepare(string$header):void{$this->streamFilePath=$this->makeStreamFile();$this->handle=fopen($this->streamFilePath,'wb+');$this->write($header);}/**
     * @param string $row
     * @return void
     */publicfunctionwrite(string$row):void{fwrite($this->handle,$row);}/**
     * @return void
     */publicfunctiondisorganize():void{fclose($this->handle);// 後処理 配置したい場所へコピーする等dump(file_get_contents($this->streamFilePath));unlink($this->streamFilePath);}/**
     * @return string
     */privatefunctionmakeStreamFile():string{returntempnam(sys_get_temp_dir(),config('app.name'));}}

Exportを契約したFileUserCsvExportクラスです。

ExportUserCommand

app/Console/Commands/ExportUserCommand.php
<?phpdeclare(strict_types=1);namespaceApp\Console\Commands;useApp\UseCase\UserCsvExportUseCase;useIlluminate\Console\Command;classExportUserCommandextendsCommand{/**
     * The name and signature of the console command.
     *
     * @var string
     */protected$signature='export:user';/**
     * The console command description.
     *
     * @var string
     */protected$description='export user data.';/**
     * @var UserCsvExportUseCase
     */privateUserCsvExportUseCase$useCase;/**
     * ExportUserCommand constructor.
     * @param UserCsvExportUseCase $useCase
     */publicfunction__construct(UserCsvExportUseCase$useCase){parent::__construct();$this->useCase=$useCase;}/**
     * @return void
     */publicfunctionhandle():void{$this->useCase->handle();}}

LaravelにはArtisanコンソールというコマンドラインインターフェイスが用意されてます。
コマンドクラスを作るだけで簡単に自作コマンドを追加できます。

ExportUserCommandでやってることは、UserCsvExportUseCaseのインスタンスを受け取って、handleメソッドを呼び出すだけです。

$php artisan export:user

上記のコマンドが追加されます。

依存性の注入(DI)

app/Providers/AppServiceProvider.php
<?phpdeclare(strict_types=1);namespaceApp\Providers;useApp\Infrastructure\Adapter\DbUserRepository;useApp\Infrastructure\Adapter\FileUserCsvExport;useApp\UseCase\UserCsvExportUseCase;useIlluminate\Support\ServiceProvider;classAppServiceProviderextendsServiceProvider{/**
     * Register any application services.
     *
     * @return void
     */publicfunctionregister(){$this->app->bind(UserCsvExportUseCase::class,function($app){returnnewUserCsvExportUseCase(newDbUserRepository(),newFileUserCsvExport());});}/**
     * Bootstrap any application services.
     *
     * @return void
     */publicfunctionboot(){//}}

UserCsvExportUseCaseクラスをサービスコンテナに登録します。
ここで登録しているので、ExportUserCommandはコンストラクタインジェクションでUserCsvExportUseCaseクラスのインスタンスを受け取れます。

Vultr VPSのCentOS8でNginx+PHP7.4+MySQL8でWordPressをインストール

$
0
0

はじめに

WordPressのインストール記事は多く見つかるが、LEMP Stack (Linux, Nginx, MySQL, PHP) を使わずに個別インストールした手順をメモしておきます。Vultrには最初からWordPressが入ったVPSも選べますが、CentOS7ベースなのでCentOS8を使いたい場合は自分でインストールする必要があります。

私の知る限り、Vultrより安いWordPressが動くVPSは無かったです。Vultrはキャンペーンで紹介者のリンク経由で新規申込した場合に$100分のクレジットがもらえますので、是非使ってください。$100あれば複数VPSを立てたり、クラスタ構成を試したり、いろいろできます。紹介者にもちょっとだけクレジットが入ります。

このリンクで$100もらえます

スクリーンショット 2020-03-09 午後7.03.25.png

私もリファラー経由でアカウントを作ったので$100もらえました。これで最初にあれやこれや試すのは無料でできます。
スクリーンショット 2020-03-09 午後7.17.41.png

1. CentOS8をVultr VPSで構成

とりあえずのお試しなので、一番安いサーバーを選択した。GB単価の安いBlock StorageにWordPressの画像を入れるかも知れないので、Block StorageのチェックをON。別記事で使ったSSH鍵も使えるようにしておくとSSH接続が楽です。なお、画像ファイルが250GBを超える事が想定される場合は最初からVultr Object Storageを使った方がストレージ費用が安くなる(250GB/$5から)と思いますが、その規模ならこの記事の対象読者にならないでしょうからVultr Object Storageは割愛します。

(1) VPS作成

my.vultr.com_deploy_(iPad Pro).png

MacからSSH接続
$ssh-i~/.ssh/vultr2root@VultrIPアドレスTheauthenticityofhost'IPアドレス (IPアドレス)'can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'IPアドレス' (ECDSA) to the list of known hosts.
root@IPアドレス'spassword: 
Activatethewebconsolewith: systemctlenable--nowcockpit.socket

(2) OSの構成

私の過去記事の5〜6をやってSwap領域やFirewallを構成しておく。

過去記事
Vultr VPSにCentOS7, Ruby on Rails 6, Puma, Capistrano3でのProduction環境デプロイ

2. Nginxインストール

(1) yumでインストール

# yum update
# yum info nginx
Last metadata expiration check: 0:00:58 ago on Mon 01 Jun 2020 02:22:09 AM UTC.
Available Packages
Name         : nginx
Epoch        : 1
Version      : 1.14.1
Release      : 9.module_el8.0.0+184+e34fea82
Architecture : x86_64
Size         : 570 k
Source       : nginx-1.14.1-9.module_el8.0.0+184+e34fea82.src.rpm
Repository   : AppStream
Summary      : A high performance web server and reverse proxy server
URL          : http://nginx.org/
License      : BSD
Description  : Nginx is a web server and a reverse proxy server for HTTP, SMTP, POP3 and
             : IMAP protocols, with a strong focus on high concurrency, performance and low
             : memory usage.

# yum install nginx

(2) Start Nginx on Centos 8

起動
# systemctl start nginx
有効化
# systemctl enable nginxCreatedsymlink/etc/systemd/system/multi-user.target.wants/nginx.service/usr/lib/systemd/system/nginx.service.
ステータス確認
# systemctl status nginxnginx.service-ThenginxHTTPandreverseproxyserverLoaded:loaded(/usr/lib/systemd/system/nginx.service;enabled;vendorpreset: disabled)Active:active(running)sinceMon2020-06-0102:31:37UTC;1min23sagoMainPID:2820(nginx)Tasks:2(limit: 2864)Memory:7.3MCGroup:/system.slice/nginx.service├─2820nginx: masterprocess/usr/sbin/nginx└─2821nginx: workerprocessJun0102:31:36vultrguestsystemd[1]:StartingThenginxHTTPandreverseproxyserver...Jun0102:31:36vultrguestnginx[2816]:nginx: theconfigurationfile/etc/nginx/nginx.confsyntaxisokJun0102:31:36vultrguestnginx[2816]:nginx: configurationfile/etc/nginx/nginx.conftestissuccessfulJun0102:31:37vultrguestsystemd[1]:nginx.service:FailedtoparsePIDfromfile/run/nginx.pid:InvalidargumentJun0102:31:37vultrguestsystemd[1]:StartedThenginxHTTPandreverseproxyserver.

一応、基本的なコマンドを書いておきます。OSのバージョンとかで少し違うので。

停止
# systemctl stop nginx
再起動
# systemctl restart nginx
Config再読込
# systemctl reload nginx

NginxはDefault構成でも動作確認できるので、ブラウザにVultr VPSのIPアドレスを入れて確認します。

スクリーンショット 2020-05-31 午後10.37.21.png

(3) Nginxの構成

構成ファイルの場所

  • Nginx configuration directory: /etc/nginx
  • Nginx root directory: /usr/share/nginx/html
  • Master/Global configuration file: /etc/nginx/nginx.conf

3. PHP 7.4のインストール

(1) EPEL と Remi リポジトリの追加

dnfコマンドでリポジトリを追加します。

EPEL
# dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm# rpm -qa | grep epelepel-release-8-8.el8.noarch
REMI
# dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm# rpm -qa | grep remiremi-release-8.1-2.el8.remi.noarch

(2) PHPのインストール

利用可能なPHPモジュールを確認します。

# dnf module list php
Remi's Modular repository for Enterprise Linux 8 - x86_64                                                                                              474 kB/s | 569 kB     00:01    
Safe Remi's RPM repository for Enterprise Linux 8 - x86_64                                                                                             982 kB/s | 1.5 MB     00:01    
CentOS-8 - AppStream
Name                               Stream                                 Profiles                                                 Summary                                             
php                                7.2 [d]                                common [d], devel, minimal                               PHP scripting language                              
php                                7.3                                    common, devel, minimal                                   PHP scripting language                              

Remi's Modular repository for Enterprise Linux 8 - x86_64
Name                               Stream                                 Profiles                                                 Summary                                             
php                                remi-7.2                               common [d], devel, minimal                               PHP scripting language                              
php                                remi-7.3                               common [d], devel, minimal                               PHP scripting language                              
php                                remi-7.4                               common [d], devel, minimal                               PHP scripting language                              

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

remi-7.4が最新なので、有効化します。

# dnf module enable php:remi-7.4
Last metadata expiration check: 0:01:40 ago on Mon 01 Jun 2020 02:48:32 AM UTC.
Dependencies resolved.
=======================================================================================================================================================================================
 Package                                     Architecture                               Version                                      Repository                                   Size
=======================================================================================================================================================================================
Enabling module streams:
 php                                                                                    remi-7.4                                                                                      

Transaction Summary
=======================================================================================================================================================================================

Is this ok [y/N]: y
Complete!

PHP remi-7.4モジュールを有効化したら、PHPと関連パッケージをインストールします。

# dnf install php php-cli php-common
Last metadata expiration check: 0:03:08 ago on Mon 01 Jun 2020 02:48:32 AM UTC.
Dependencies resolved.
=======================================================================================================================================================================================
 Package                                    Architecture                   Version                                                          Repository                            Size
=======================================================================================================================================================================================
Installing:
 php                                        x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         3.0 M
 php-cli                                    x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         4.6 M
 php-common                                 x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         1.2 M
Installing dependencies:
 apr                                        x86_64                         1.6.3-9.el8                                                      AppStream                            125 k
 apr-util                                   x86_64                         1.6.1-6.el8                                                      AppStream                            105 k
 centos-logos-httpd                         noarch                         80.5-2.el8                                                       AppStream                             24 k
 httpd                                      x86_64                         2.4.37-16.module_el8.1.0+256+ae790463                            AppStream                            1.7 M
 httpd-filesystem                           noarch                         2.4.37-16.module_el8.1.0+256+ae790463                            AppStream                             35 k
 httpd-tools                                x86_64                         2.4.37-16.module_el8.1.0+256+ae790463                            AppStream                            103 k
 mod_http2                                  x86_64                         1.11.3-3.module_el8.1.0+213+acce2796                             AppStream                            158 k
 oniguruma                                  x86_64                         6.8.2-1.el8                                                      AppStream                            188 k
 libsodium                                  x86_64                         1.0.18-2.el8                                                     epel                                 162 k
 php-json                                   x86_64                         7.4.6-1.el8.remi                                                 remi-modular                          74 k
Installing weak dependencies:
 apr-util-bdb                               x86_64                         1.6.1-6.el8                                                      AppStream                             25 k
 apr-util-openssl                           x86_64                         1.6.1-6.el8                                                      AppStream                             27 k
 php-fpm                                    x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         1.6 M
 php-mbstring                               x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         527 k
 php-opcache                                x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         334 k
 php-pdo                                    x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         143 k
 php-sodium                                 x86_64                         7.4.6-1.el8.remi                                                 remi-modular                          87 k
 php-xml                                    x86_64                         7.4.6-1.el8.remi                                                 remi-modular                         215 k
Enabling module streams:
 httpd                                                                     2.4                                                                                                        

Transaction Summary
=======================================================================================================================================================================================
Install  21 Packages

Total download size: 14 M
Installed size: 65 M
Is this ok [y/N]: y

インストールしたPHPとPHP-FPMのバージョンを確認します。2020年6月時点で最新の7.4が入りました。

# php -v
PHP 7.4.6 (cli) (built: May 12 2020 08:09:15) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies

# php-fpm -v
PHP 7.4.6 (fpm-fcgi) (built: May 12 2020 08:09:15)
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies

(3) 最大ファイルサイズ設定の変更

DefaultではPHPは2MBまでのファイルしかアップロードできないので、より大きなファイルをアップロードできりょうにphp.iniupload_max_filesizeおよびnginx.confを変更します。

/etc/php.ini
;Maximumallowedsizeforuploadedfiles.;http://php.net/upload-max-filesizeupload_max_filesize=256M;MaximumsizeofPOSTdatathatPHPwillaccept.;Itsvaluemaybe0todisablethelimit.ItisignoredifPOSTdatareading;isdisabledthroughenable_post_data_reading.;http://php.net/post-max-sizepost_max_size=256M
/etc/nginx/nginx.conf
http{....client_max_body_size256M;....}
php-fpmサービスとnginxを再起動
# systemctl restart php-fpm# systemctl restart nginx

(4) NginxとPHPの連携の確認

phpinfo();がブラウザでアクセスして見えるように構成します。

index.phpファイル準備
# echo "<?php phpinfo(); ?>" > /var/www/html/index.php# chown nginx.nginx /var/www/html/index.php

何も構成しない状態でブラウザアクセスすると、エラーになりました。

スクリーンショット 2020-05-31 午後11.15.25.png

nginx-PHP連携のソケットを確認します。

# cat /etc/nginx/conf.d/php-fpm.conf
# PHP-FPM FastCGI server
# network or unix domain socket configuration

upstream php-fpm {
        server unix:/run/php-fpm/www.sock; これをメモしておく。
}
/etc/nginx/conf.d/wordpress.conf
server{server_nameVPSIPアドレス;root/var/www/html;location/{indexindex.htmlindex.htmindex.php;}location~\.php${include/etc/nginx/fastcgi_params;fastcgi_passunix:/run/php-fpm/www.sock; ここはさっき確認したソケットのフルパスfastcgi_indexindex.php;fastcgi_paramSCRIPT_FILENAME$document_root$fastcgi_script_name;}}

/etc/nginx/conf.d/wordpress.confの変更をnginxに読み込みます。

# systemctl reload nginx

php-fpmの/etc/php-fpm.d/www.conf設定ファイルでユーザーとグループをnginxに変更します。

/etc/php-fpm.d/www.conf
-user=apache-group=apache+user=nginx+group=nginx

/etc/php-fpm.d/www.confの変更を反映させるため、php-fpmを再起動します。

# systemctl restart php-fpm

設定したら、index.phpが読めるかどうか、ブラウザで確認します。以下のような画面が出れば正しく設定できています。

スクリーンショット 2020-05-31 午後11.29.29.png

4. MySQL 8のインストール

(1) MariaDBをアンインストール

MariaDBをインストールしてしまっていたら、事前にアンインストールします。

MariaDBを確認
# dnf list installed | grep mariadbmariadb.x86_643:10.3.11-2.module_el8.0.0+35+6f2527edmariadb-common.x86_643:10.3.11-2.module_el8.0.0+35+6f2527ed
アンインストール
# dnf remove mariadb

(2) MySQL8.0のインストール

インストールできるバージョンを確認します。

インストールできるバージョンを確認
# dnf info mysqlLastmetadataexpirationcheck: 0:09:51agoonMon01Jun202002:48:32AMUTC.AvailablePackagesName:mysqlVersion:8.0.17Release:3.module_el8.0.0+181+899d6349Architecture:x86_64Size:11MSource:mysql-8.0.17-3.module_el8.0.0+181+899d6349.src.rpmRepository:AppStreamSummary:MySQLclientprogramsandsharedlibrariesURL:http://www.mysql.comLicense:GPLv2withexceptionsandLGPLv2andBSDDescription:MySQLisamulti-user,multi-threadedSQLdatabaseserver.MySQLisa:client/serverimplementationconsistingofaserverdaemon(mysqld):andmanydifferentclientprogramsandlibraries.Thebasepackage:containsthestandardMySQLclientprogramsandgenericMySQLfiles.

8.0.17をインストールします。

MySQL8.0.17をインストール
# dnf install @mysql:8.0
PHP用のライブラリ群もインストール
# dnf install php-mysqlnd

以上でMySQL8.0のインストールが完了ですが、自動起動の設定をして、MySQLを起動させます。

# systemctl enable mysqld
Created symlink /etc/systemd/system/multi-user.target.wants/mysqld.service → /usr/lib/systemd/system/mysqld.service.
# systemctl start mysqld

(3) MySQLの初期設定

MySQLの初期設定は簡単です。私は全てyで設定しました。

# mysql_secure_installation

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: y

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary                  file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Please set the password for root here.

New password: 

Re-enter new password: 

Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

(4) WordPress用のMySQLユーザーの作成

後でWordPressの初期設定画面に入れるユーザーを作成しておきます。wpuserというユーザー名にしましたが、任意で。

# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.17 Source distribution

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE USER 'wpuser'@'localhost' IDENTIFIED BY '任意のパスワード';
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE DATABASE wordpress;
Query OK, 1 row affected (0.01 sec)

mysql> GRANT ALL ON wordpress.* TO `wpuser `@`localhost`;
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)

mysql> exit
Bye

5. WordPressのインストール

(1) 前提ソフトのインストール

# dnf install tar curl php-json
Last metadata expiration check: 0:46:02 ago on Mon 01 Jun 2020 02:48:32 AM UTC.
Package tar-2:1.30-4.el8.x86_64 is already installed.
Package curl-7.61.1-11.el8.x86_64 is already installed.
Package php-json-7.4.6-1.el8.remi.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!

(2) WordPressのインストール

WordPressをダウンロードして、NginxのRootディレクトリに展開します。/tmpにダウンロードしましたが、どこでも構いません。tarで展開したら、ディレクトリ丸ごとnginxのRootディレクトリにコピーします。

# cd /tmp
# curl https://wordpress.org/latest.tar.gz --output wordpress.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 11.6M  100 11.6M    0     0  32.9M      0 --:--:-- --:--:-- --:--:-- 32.9M
# tar xf wordpress.tar.gz
# cp -r wordpress /var/www/html

最後にパーミッションとSELinux security contextを変更します。

# chown -R nginx.nginx /var/www/html/wordpress
# chcon -t httpd_sys_rw_content_t /var/www/html/wordpress -R

これでWordPressのインストールは完了です。WordPressのトップページ (http://IPアドレス/wordpress) をブラウザで確認して、以下の画面になっていれば成功です。

スクリーンショット 2020-05-31 午後11.41.02.png

(3) WordPressの構成

WordPressの構成はWordPressの初期画面のLet's go!ボタンをクリックして開始します。

スクリーンショット 2020-05-31 午後11.45.38.png

必要項目を入力したらSubmitすると以下の画面になるのでRun the installationをクリックして実行します。

スクリーンショット 2020-05-31 午後11.46.50.png

WordPressのUsernameやパスワードなどを設定して、Install WordPressをクリックします。

スクリーンショット 2020-05-31 午後11.47.31.png

以下の画面になれば、WordPressのインストールは完了ですので、ログインしてWordPress管理画面に入ります。

スクリーンショット 2020-05-31 午後11.50.26.png

ログイン画面は http://IPアドレス/wordpress/wp-login.php です。

スクリーンショット 2020-05-31 午後11.53.07.png

ここから先はWordPressの世界ですので、好きなスキンでコンテンツを作ってください。

スクリーンショット 2020-05-31 午後11.55.33.png

参考記事

AWS(AmazonLinux2)でPHP7.4インストール

$
0
0

背景

AmazonLinux2リポジトリのPHPのバージョンが古く、以前まではRemi7をリポジトリに登録してAwsにPHP環境を作成していたが、一部でインストールする際など諸々考慮が必要となり、管理が煩雑となることから「amazon-linux-extras」を利用するよう変更したら、諸々楽になったので、メモ

Extraインストール

sudo amazon-linux-extras install php7.4
sudo yum update

phpインストール

sudo yum install php php-mbstring

PHP Docker で composer require linecorp/line-bot-sdk がコケる�

$
0
0

状況

Docker Image : php:7.4.6-fpm-alpine3.11

ログ

# composer require linecorp/line-bot-sdk
Using version ^4.4 for linecorp/line-bot-sdk
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - linecorp/line-bot-sdk 4.4.1 requires ext-sockets * -> the requested PHP extension sockets is missing from your system.
    - linecorp/line-bot-sdk 4.4.0 requires ext-sockets * -> the requested PHP extension sockets is missing from your system.
    - Installation request for linecorp/line-bot-sdk ^4.4 -> satisfiable by linecorp/line-bot-sdk[4.4.0, 4.4.1].

  To enable extensions, verify that they are enabled in your .ini files:
    - 
    - /usr/local/etc/php/conf.d/docker-php-ext-pdo_mysql.ini
    - /usr/local/etc/php/conf.d/docker-php-ext-sodium.ini
  You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

the requested PHP extension sockets is missing from your system.

socketsが無いよというお話でした。

対処

Dockerfile内でsocketsをインストールするだけ。

~略~
RUN docker-php-ext-install sockets
~略~

結果

無事、インストール完了

 # composer require linecorp/line-bot-sdk
Using version ^4.4 for linecorp/line-bot-sdk
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing linecorp/line-bot-sdk (4.4.1): Downloading (100%)         
linecorp/line-bot-sdk suggests installing apigen/apigen (Install with roave/better-reflection:dev-master to generate docs)
linecorp/line-bot-sdk suggests installing roave/better-reflection (Required by apigen/apigen:dev-master)
Package jakub-onderka/php-console-color is abandoned, you should avoid using it. Use php-parallel-lint/php-console-color instead.
Package jakub-onderka/php-console-highlighter is abandoned, you should avoid using it. Use php-parallel-lint/php-console-highlighter instead.
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
34 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

LinuxでApache2.4(httpd 2.4.43)+PHP7.4をソースコンパイルしてWebサーバー構築 - 2.PHP導入編

$
0
0

前提と準備

Linuxサーバー構築の記事

前回はApache httpd 2.4.43をソースコンパイルでWebサーバーで構築しましたが、今回は、前回に引き続き、ApacheにWebアプリサーバーの基幹であるPHP 7.4をソースコンパイルで導入します(⑅•ᴗ•⑅)

環境

  • Webサーバープログラム:Apache 2.4.43 + PHP 7.4.6(ソースコンパイル)
  • クライアント:Windows10 Pro
  • サーバーのアーキテクチャ:x64(動作はHyper-Vの第2世代で確認) Linuxのディストリビューション:CentOS 8.1 / openSUSE 15.1 Leap / Ubuntu 20.04(すべて64bit)

前提

  • ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
  • どのディストリビューションでも、ファイアウォールはfirewalldを使う(ディストリビューション独自のファイアウォールコマンドは使用しない)
  • 前回の記事のApache導入を完了していること(動作が確認できて、Webサーバーとして構築されていること)

サーバー条件

IPアドレス

  • クライアント:192.168.1.11
  • Webサーバー:192.168.1.18(どのディストリビューションでも同じIPアドレスで検証)
  • 所属ネットワークセグメント:192.168.1.0/24 Webサーバー.png

パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年6月時点)

  • zlib-1.2.11.tar.gz
  • apr-1.7.0.tar.gz
  • apr-util-1.6.1.tar.gz
  • mysql80-community-release-el8-1.noarch.rpm (CentOS 8.1)
  • mysql80-community-release-sl15-3.noarch.rpm (openSUSE 15.1)
  • mysql-apt-config_0.8.15-1_all.deb (Ubuntu 20.04)
  • oniguruma-devel-6.8.2-1.el8.x86_64.rpm (CentOS 8.1)
  • httpd-2.4.43.tar.gz
  • php-7.4.6.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

作業手順

PHPのインストール

PHPのソースコンパイルに必要なライブラリを導入

CentOS8.1
# dnf -y install libxml2 libxml2-devel sqlite-devel
# dnf -y install oniguruma-devel-6.8.2-1.el8.x86_64.rpm
openSUSE15.1
# zypper -n install libxml2-tools libxml2-devel sqlite3-devel oniguruma-devel
Ubuntu20.04
# apt-get -y install libxml2 libxml2-dev libsqlite3-dev libonig-dev

PHP7.3までは、SQLiteと鬼車のライブラリを使わなくてもマルチバイト文字列を扱うようインストールができたのですが、PHP7.4ではソースコンパイルで必須となりました。

CentOS 8.1のみ、鬼車ライブラリ(oniguruma-devel-6.8.2-1.el8.x86_64.rpm)はデフォルトパッケージには存在しないため、CentOSパッケージのダウンロードページから持ってきました。

2020年6月現在:
http://mirror.centos.org/centos/8/PowerTools/x86_64/os/Packages/oniguruma-devel-6.8.2-1.el8.x86_64.rpm

PHPのソースコンパイル

コンパイルには20~30分前後かかる見込みです。Apacheより規模が大きいので、コンパイル中はコーヒー1杯で休憩をはさんだほうがいいかもしれません(*˘︶˘*).。.:*♡

# cd [php-7.4.6.tar.gzが置いてあるディレクトリ]
# tar xvzf php-7.4.6.tar.gz
# cd php-7.4.6/
# ./configure --with-apxs2=/usr/local/apache2/bin/apxs --with-mysqli --with-pdo-mysql --enable-mbregex --enable-mbstring
# make
# make test
  • MySQLはPDOを有効にします
  • マルチバイト文字列を有効にします

「make」「make test」が時間がかかるコマンドです。
途中でエラーがなければコンパイル完了です。
なお、CentOS 8.1とUbuntu 20.04でmake testで私がテストしたところ、なんとバグは一つもなかったのです( ๑・∞・๑ ) …OSと相性がいいのかもしれません
php_test_fail.png

PHPのインストール

コンパイルが完了したら、インストールします。

CentOS8.1・Ubuntu20.04
# make install
Installing PHP SAPI module:       apache2handler
/usr/local/apache2/build/instdso.sh SH_LIBTOOL='/opt/apr-1.7.0/build-1/libtool' libphp7.la /usr/local/apache2/modules
/opt/apr-1.7.0/build-1/libtool --mode=install install libphp7.la /usr/local/apache2/modules/
libtool: install: install .libs/libphp7.so /usr/local/apache2/modules/libphp7.so
libtool: install: install .libs/libphp7.lai /usr/local/apache2/modules/libphp7.la
libtool: warning: remember to run 'libtool --finish /home/admin/php-7.4.6/libs'
chmod 755 /usr/local/apache2/modules/libphp7.so
[activating module `php7' in /usr/local/apache2/conf/httpd.conf]
Installing shared extensions:     /usr/local/lib/php/extensions/no-debug-zts-20190902/
Installing PHP CLI binary:        /usr/local/bin/
Installing PHP CLI man page:      /usr/local/php/man/man1/
Installing phpdbg binary:         /usr/local/bin/
Installing phpdbg man page:       /usr/local/php/man/man1/
Installing PHP CGI binary:        /usr/local/bin/
Installing PHP CGI man page:      /usr/local/php/man/man1/
Installing build environment:     /usr/local/lib/php/build/
Installing header files:          /usr/local/include/php/
Installing helper programs:       /usr/local/bin/
  program: phpize
  program: php-config
Installing man pages:             /usr/local/php/man/man1/
  page: phpize.1
  page: php-config.1
/home/admin/php-7.4.6/build/shtool install -c ext/phar/phar.phar /usr/local/bin
ln -s -f phar.phar /usr/local/bin/phar
Installing PDO headers:           /usr/local/include/php/ext/pdo/
openSUSE15.1
# make install
Installing PHP SAPI module:       apache2handler
/usr/local/apache2/build/instdso.sh SH_LIBTOOL='/opt/apr-1.7.0/build-1/libtool' libphp7.la /usr/local/apache2/modules
/opt/apr-1.7.0/build-1/libtool --mode=install install libphp7.la /usr/local/apache2/modules/
libtool: install: install .libs/libphp7.so /usr/local/apache2/modules/libphp7.so
libtool: install: install .libs/libphp7.lai /usr/local/apache2/modules/libphp7.la
libtool: warning: remember to run 'libtool --finish /home/admin/php-7.4.6/libs'
chmod 755 /usr/local/apache2/modules/libphp7.so
[activating module `php7' in /usr/local/apache2/conf/httpd.conf]
Installing shared extensions:     /usr/local/lib64/extensions/no-debug-zts-20190902/
Installing PHP CLI binary:        /usr/local/bin/
Installing PHP CLI man page:      /usr/local/php/man/man1/
Installing phpdbg binary:         /usr/local/bin/
Installing phpdbg man page:       /usr/local/php/man/man1/
Installing PHP CGI binary:        /usr/local/bin/
Installing PHP CGI man page:      /usr/local/php/man/man1/
Installing build environment:     /usr/local/lib64/build/
Installing header files:          /usr/local/include/php/
Installing helper programs:       /usr/local/bin/
  program: phpize
  program: php-config
Installing man pages:             /usr/local/php/man/man1/
  page: phpize.1
  page: php-config.1
/home/admin/php-7.4.6/build/shtool install -c ext/phar/phar.phar /usr/local/bin
ln -s -f phar.phar /usr/local/bin/phar
Installing PDO headers:           /usr/local/include/php/ext/pdo/

PHPのライブラリのいくつかは「/usr/local/include/php/」「/usr/local/lib/php/」のように、用途に応じて複数の配置場所へ配置されます。openSUSE 15.1の場合は「/usr/local/lib64/php/」に配置されるものもあります。
また、この時点で、PHPのDLL(ダイナミックリンクライブラリ、外部拡張アプリケーションに該当する)は自動的にApacheのフォルダに配備されます

次に、PHPの環境設定ファイルのコピーを行います。
まずはphp.iniをPHPのライブラリフォルダ/usr/local/lib(openSUSEの場合はlib64)/にコピーします。

CentOS8.1・Ubuntu20.04
# cp php.ini-development /usr/local/lib/php.ini
# ls -l /usr/local/lib
合計 328
-rw-r--r-- 1 root root 144402  6月 24 12:34 libz.a
lrwxrwxrwx 1 root root     14  6月 24 12:34 libz.so -> libz.so.1.2.11
lrwxrwxrwx 1 root root     14  6月 24 12:34 libz.so.1 -> libz.so.1.2.11
-rwxr-xr-x 1 root root 113656  6月 24 12:34 libz.so.1.2.11
drwxr-xr-x 4 root root     37  6月 24 16:19 php
-rw-r--r-- 1 root root  72278  6月 24 16:21 php.ini
drwxr-xr-x 2 root root     21  6月 24 12:34 pkgconfig
openSUSE15.1
# cp php.ini-development /usr/local/lib64/php.ini
# ls -l /usr/local/lib64
合計 72
drwxr-xr-x 1 root root   338  6月 25 16:59 build
drwxr-xr-x 1 root root    42  6月 25 16:59 extensions
-rw-r--r-- 1 root root 72278  6月 25 17:01 php.ini

Apache側の設定

Apacheで、PHPファイルが認識できるようにする必要があるので編集します。PHPのDLLは、PHPインストール時にhttpd.confで自動的に書き込まれるので追加不要です。httpd.conf側でやるべきことは、PHPのMIMEタイプがApacheで認識されればよいのです。

# vi /usr/local/apache2/conf/httpd.conf
/usr/local/apache2/conf/httpd.conf
<IfModuledir_module>
    DirectoryIndex index.html ← 「index.php」を追加
</IfModule><IfModulemime_module>
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    …
    AddType application/x-httpd-php .php  ←   この行を追加
</IfModule>

PHPの環境設定

PHPの文字コードや参照ライブラリの設定を変えます。

PHPをコンパイルインストールした際に、ディストリビューションによって、php.iniの格納ディレクトリが違います。openSUSEのみ、/usr/local/lib64/にPHPが含まれていますので、ライブラリフォルダ「lib」の違いが紛らわしいです

CentOS8.1・Ubuntu20.04
# vi /usr/local/lib/php.ini
openSUSE15.1
# vi /usr/local/lib64/php.ini
php.ini
# 内容が長いので、一部修正するもののみ、修正後のもののみを記載します。セミコロン「;」は外します。「;」外すだけの行も含めて記載されています
output_buffering=Ondefault_charset="UTF-8"[CentOS 8.1・Ubuntu 20.04]include_path=".:/usr/local/include/php:/usr/local/lib/php"[openSUSE 15.1]include_path=".:/usr/local/include/php:/usr/local/lib64"extension_dir="/usr/local/include/php/ext"date.timezone=Asia/Tokyombstring.language=Japanesembstring.encoding_translation=Offmbstring.detect_order=UTF-8, SJIS, EUC-JP, JIS, ASCIImbstring.substitute_character=none

PHPの動作確認

PHP確認用のページ

まずは確認用のPHPページを作ります。

# vi /usr/local/apache2/htdocs/phpi.php
phpi.php
<?phpphpinfo();?>

なぜPHPファイルを/usr/local/apache2/htdocs/に配置する??って聞かれますが、Apacheをソースコンパイルでインストールすると、Webページデータの格納フォルダは「/usr/local/apache2/htdocs/」がデフォルトだからです。まぁ、httpd.confで格納先を変更できますが、今回はそれは割愛します。

Apacheの再起動

# systemctl stop httpd
# systemctl start httpd
# systemctl status httpd

再起動しないとPHPのモジュールをロードした状態でApacheが動作しないので、一度止めてから、再スタートします。その後、statusで起動していることを確認します。
phpinfo.png
PHPが正しく認識できました!!(*´꒳`*)

pdo.png
PDOもちゃんと認識できています。そうしないとMySQLとSQLiteが使えないからね…( ´ •̥ ̫ •̥ ` )

susephpi.png

openSUSE 15.1の場合だと、php.iniの場所が「/usr/local/lib64」となって認識されていることがわかります。

次回

MySQLを導入して、PHPのWebアプリサーバーがデータベースを利用できるようにします

LinuxでApache2.4(httpd 2.4.43)+PHP7.4をソースコンパイルしてWebサーバー構築 - 3.MySQL8.0導入編

$
0
0

前提と準備

Linuxサーバー構築の記事

前回はApache2.4にPHP7.4をソースコンパイルでWebアプリ環境を構築しましたが、今回は、前回に引き続き、データベースサーバーを構築します(⑅•ᴗ•⑅)

データベースサーバーはWebサーバー本体と切り離し可能ですが、今回は簡単なため、Webサーバーと一体でデータベースのMySQLを構築します

環境

  • Webサーバープログラム:Apache 2.4.43 + PHP 7.4.6 + MySQL 8.0
  • クライアント:Windows10 Pro
  • サーバーのアーキテクチャ:x64(動作はHyper-Vの第2世代で確認) Linuxのディストリビューション:CentOS 8.1 / openSUSE 15.1 Leap / Ubuntu 20.04(すべて64bit)

前提

  • ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
  • どのディストリビューションでも、ファイアウォールはfirewalldを使う(ディストリビューション独自のファイアウォールコマンドは使用しない)
  • 前回の記事のApache+PHP導入を完了していること

サーバー条件

IPアドレス

  • クライアント:192.168.1.11
  • Webサーバー:192.168.1.18(どのディストリビューションでも同じIPアドレスで検証)
  • データベースサーバー:(Webサーバーと一体)
  • 所属ネットワークセグメント:192.168.1.0/24 Webサーバー.png

パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年6月時点)

  • zlib-1.2.11.tar.gz
  • apr-1.7.0.tar.gz
  • apr-util-1.6.1.tar.gz
  • mysql80-community-release-el8-1.noarch.rpm (CentOS 8.1)
  • mysql80-community-release-sl15-3.noarch.rpm (openSUSE 15.1)
  • mysql-apt-config_0.8.15-1_all.deb (Ubuntu 20.04)
  • oniguruma-devel-6.8.2-1.el8.x86_64.rpm (CentOS 8.1)
  • httpd-2.4.43.tar.gz
  • php-7.4.6.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

作業手順

MySQL 8.0の導入

MySQLのインストール

ディストリビューションの標準パッケージコマンドにあるMySQLは、バージョンが古いものや互換であるMariaDBであることが多いため、明示的なMySQLのバージョンを導入する場合は、MySQL公式からレポジトリをダウンロードする必要があります。

ダウンロードしたレポジトリを適用し、MySQLをインストールします。

CentOS8.1
# cd [mysql80-community-release-el8-1.noarch.rpmの配置されているディレクトリ]
# dnf -y install mysql80-community-release-el8-1.noarch.rpm
# dnf -y install mysql-server
openSUSE15.1
# cd [mysql80-community-release-sl15-3.noarch.rpmの配置されているディレクトリ]
# zypper install mysql80-community-release-sl15-3.noarch.rpm
…(中略)…
mysql80-community-release-sl15-3.noarch (RPM ファイルキャッシュ): 署名の検証に失敗しました [4-署名の公開鍵がありません]
中止(A)、再試行(R)、無視(I)? [a/r/i] (a):  ← 「i」で続行する

# zypper install mysql-community-server
鍵を拒否しますか(R)? 今後ずっと信頼しますか(A)? [r/a/?] (r):  ← 「a」で続行する
(あとはインストールするか問われるので「y」を選択)
Ubuntu20.04
# cd [mysql-apt-config_0.8.15-1_all.deb.debの配置されているディレクトリ]
# dpkg -i mysql-apt-config_0.8.15-1_all.deb

Which MySQL product do you wish to configure?
→「MySQL Server & Cluster」を選択

Which server version do you wish to receive?
→「mysql-8.0」を選択

Which MySQL product do you wish to configure? に戻る
→「Ok」を選択

# apt-get -y update  ← アップデート後は再起動することが望ましい

# apt-get -y install mysql-client mysql-server
→ 途中MySQLのroot(Linuxのrootではない)のパスワードを設定する必要がある

なお、MySQL 8.0の場合認証基盤がハッシュ値対応で、しかもデフォルトでハッシュ値ベースの認証基盤を有効化している場合があります。しかし、これはPHP7でも対応していないらしく(7.4になった今でも対応している話は聞いていない)、PHPで平文パスワードで記載してアクセスすると認証がうまくできない。そのため、/etc/my.cnfを修正する必要がある(CentOS8.1の場合は/etc/my.cnfでインクルードしている/etc/my.cnf.d/mysql-default-authentication-plugin.cnfを編集する)。

またUbuntu 20.04では、MySQLのapt-getでのインストール中に、MySQLのroot(Linuxのrootではない)パスワードの設定と「Select default authentication plugin」というように認証基盤の選択ができるのでありがたい(✿´ ꒳ ` )

openSUSE15.1
# vi /etc/my.cnf
CentOS8.1
# vi /etc/my.cnf.d/mysql-default-authentication-plugin.cnf
my.cnfまたはmysql-default-authentication-plugin.cnf
default-authentication-plugin=mysql_native_password ← 先頭の「#」ははずす
Ubuntu20.04の場合
[認証基盤を/etc/my.cnfの修正で行う必要はない。インストール時に選択する]
インストール中に「Select default authentication plugin」と問われる
→「Use Legacy Authentication Method」を選択する

MySQLの起動

CentOS8.1
# systemctl start mysqld
# systemctl enable mysqld
# systemctl status mysqld
openSUSE15.1・Ubuntu20.04
# systemctl start mysql
# systemctl enable mysql
# systemctl status mysql

次に、MySQLの起動を行った後に、MySQLの初期パスワードを確認するため、MySQLのログを確認。なおUbuntu 20.04ではパスワード設定をあらかじめインストール時に実施したので、初期パスワードをログから確認する作業は不要。

CentOS8.1
# less -r /var/log/mysqld.log
openSUSE15.1
# less -r /var/log/mysql/mysqld.log

その際、「[Note] A temporary password is generated for root@localhost:~」が存在すれば、初期パスワードが設定されている。なければ設定されていない(CentOS 8.1のMySQL 8.0は設定されていない)。このパスワードを用いて、MySQLコマンドラインを実行する

Ubuntu20.04はこの手順は飛ばす
[MySQL初期パスワードなし]
# mysql -u root
[MySQL初期パスワードあり]
# mysql -u root -p  ← 初期パスワードを入力してログイン

mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '<MySQLのrootパスワード(Linuxのrootパスワードではない)>';
Query OK, 0 rows affected (0.00 sec)

mysql> exit

ここで、MySQLの実際に運用想定のパスワードを設定する。そこで、rootというのはLinuxのrootではなく、MySQLの中のrootであることに注意したい。

「Query OK, 0 rows affected (0.00 sec)」と出力されれば、MySQLのrootパスワードの設定が完了するので、exitで終了する。

では、設定後のMySQLのrootパスワードでログインを試す

# mysql -u root -p
Enter password:  ← MySQLのrootパスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.17 Source distribution

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

こうなればMySQLのrootログインが成功となる。または、以下でも確認できた

# mysqladmin ping -u root -p
Enter Password:  ← MySQLのrootパスワードを入力
mysqld is alive

「mysqld is alive」となればOK

MySQLを試す

ユーザーとデータベースを作ってみる

MySQLが動いたということなので、実際ユーザーとデータベースを作ってみます。

  • データベース名:manutest
  • テストユーザー名:test
  • テストユーザーパスワード:test0
  • 文字コード:UTF-8

最初にMySQLのrootでログインします。

# mysql -u root -p
Enter password:  ← MySQLのrootパスワードを入力

mysql> CREATE DATABASE manutest CHARACTER SET utf8;
↑ データベース「manutest」を文字コードUTF-8で作成する

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| manutest           |  ← 作成したデータベースが表示される
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> CREATE USER 'test'@'localhost' IDENTIFIED BY 'test0';
↑ ユーザー名「test」をパスワード「test0」にて作成する

mysql> GRANT ALL ON manutest.* TO 'test'@'localhost';
↑ データベース「manutest」に対して、ユーザー「test」が全機能を使えるようにする

エラーがなく「Query OK, 0 rows affected」が表示されれば、データベースとユーザーは作成完了。

パスワードポリシーエラーが発生する場合

もしパスワードポリシーに反しているとエラーが発生してしまう場合は、ポリシーの状態を確認して、変更できるみたいです。ちなみにCentOS 8.1では存在せず、openSUSE 15.1では存在していました。

mysql> SHOW VARIABLES LIKE 'validate_password%';
↑ Empty setならポリシーは存在しないが、もし存在する場合は、初期では
 validate_password_lengthが8、validate_password.policyが「MEDIUM」

mysql> SET GLOBAL validate_password.length=4;
mysql> SET GLOBAL validate_password.policy=LOW;
↑ これで4文字以内のパスワード、試験目的で簡単なパスワードを設定できる

mysql> SET GLOBAL validate_password.length=8;
mysql> SET GLOBAL validate_password.policy=MEDIUM;
↑ 戻す場合

では、作成したテスト用のユーザーとデータベースで、MySQLを使えるか試します。

mysql> exit
Bye

# mysql -u test -p
Enter password:  ← パスワード「test0」を入れる
(中略)

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| manutest           |  ← 作成したデータベースが表示されれば成功
+--------------------+
2 rows in set (0.00 sec)

テーブルを作成してデータを入れてみる

では、作成したデータベースに、テーブルを作ってそこにデータを入れてみます!

今回は、このテーブルをテスト用に作成します。
テーブル名:testtb

idnamememo
INTVARCHAR(64)VARCHAR(256)
必須属性PRIMARY KEYNOT NULL初期値NULL
その他属性AUTO_INCREMENT--
mysql> USE manutest;
mysql> CREATE TABLE testtb (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, memo VARCHAR(256) DEFAULT NULL);
Query OK, 0 rows affected (0.11 sec)

mysql> SHOW TABLES;
+--------------------+
| Tables_in_manutest |
+--------------------+
| testtb             |
+--------------------+
1 row in set (0.00 sec)

テーブルの作成に成功したら、実際にデータを入れてみます。
テストテーブルtesttbは、idが自動採番で、nameが必須入力となるため、例として以下の値を挿入します。

  • id:(自動採番のため省略)
  • name:テスト
  • memo:Only for test.
mysql> INSERT INTO testtb (name, memo) VALUES ('テスト', 'Only for test.');
mysql> SELECT * FROM testtb;
+----+-----------+----------------+
| id | name      | memo           |
+----+-----------+----------------+
|  1 | テスト    | Only for test. |
+----+-----------+----------------+
1 row in set (0.00 sec)

このように登録したデータが文字化けすることなくデータベースに登録されました♪
成功です!

PHP上でMySQLを扱ってみる

MySQLのインストールも、動作も問題なく確認できたので、いよいよPHPからMySQLを扱ってみたいと思います。

MySQLは終了して、PHPページを作成します。

PHPからMySQLの接続を確認

まずはPHPでPOSTにて受け取ったデータをMySQLデータベースへ登録する受信PHPを作成します!っとその前に、PHPでMySQLに接続できるかちゃんと確かめないといけません(*˘ᗜ˘*;)

接続確認ページを作ります

# cd ~
# vi connect.php
connect.php
<?php$dsn='mysql:dbname=manutest; host=127.0.0.1';$usr='test';$pass='test0';try{$db=newPDO($dsn,$usr,$pass);print'Connection successful.';$db=NULL;}catch(PDOException$e){die('Connect error : '.$e->getMessage());}?>

これを、Apache導入時にインストールした先のWebページの格納場所 /usr/local/apache2/htdocs/に移動しましょう!

# mv connect.php /usr/local/apache2/htdocs/

PHPページの配置なので、特にApacheを再起動することは不要なので、https://[LinuxサーバーのIPアドレス]/connect.php と入力して確認。今回はLinuxサーバーのIPアドレスは192.168.1.18なので、ブラウザでhttps:~の後に、192.168.1.18/connect.phpをURLを入力してアクセス。

conn.png
connect.phpでは接続成功すると上記のメッセージを出力するので、↑の画像は接続が成功したんです!!

PHPからMySQLにデータを登録できるかを確認

続いて、いよいよデータが登録できるかを確認します。
再びWebページを作ります。

まずは登録フォームから。

# vi test_form.html
test_form.html
<!DOCTYPE html><html><head><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"><title>テストページ - Test page</title></head><body><formmethod="POST"action="test_form.php"><inputtype="text"name="name"/><inputtype="text"name="memo"/><inputtype="submit"value="Submit"/></form></body></html>

続いて、フォームから受け取ったデータをMySQLデータベースへ追加するPHPを作成

# vi test_form.php
test_form.php
<!DOCTYPE html><html><head><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"><title>テスト入力 - Test insert</title></head><body><?phpfunctiongetDb(){$dsn='mysql:dbname=manutest; host=127.0.0.1';$usr='test';$pass='test0';try{$db=newPDO($dsn,$usr,$pass);$db->exec('SET NAMES utf8');}catch(PDOException$e){die('Connect error : '.$e->getMessage());}return$db;}try{$db=getDb();$stt=$db->prepare('INSERT INTO testtb (name, memo) VALUES (:one, :two)');$stt->bindValue(':one',$_POST['name']);$stt->bindValue(':two',$_POST['memo']);$stt->execute();print$_POST['name'].' - '.$_POST['memo'].' : Insert OK.';$stt=$db->query("SELECT * FROM testtb");?><tableborder="1"><?phpwhile($row=$stt->fetch(PDO::FETCH_ASSOC)){?><tr><td><?phpprint$row['name'];?></td><td><?phpprint$row['memo'];?></td></tr><?php}?></table><?php$db=NULL;}catch(PDOException$e){die('Process error : '.$e->getMesssage());}?></body></html>

上記2ファイルをApacheのWebページデータの場所へ。

# mv test_form.* /usr/local/apache2/htdocs/

では、ブラウザでhttps://[IPアドレス]/test_form.htmlにアクセスしてみます。

reg1.png
適当に左右のフォームに文字列を入れて…(なるべく日本語で)、Submitを押します

reg2.png
成功しました!入力されたデータがPHPにPOSTされて、MySQLのデータベースに登録されました。ではコマンドラインからも確認してみましょう。

PHPでは、manutestというデータベースに、ユーザーtestで接続しているので、コマンドからもtestでMySQLにログインします。

# mysql -u test -p
Enter password:  ← パスワード「test0」を入力

mysql> USE manutest;
mysql> SELECT * FROM testtb;
+----+-----------------+----------------+
| id | name            | memo           |
+----+-----------------+----------------+
|  1 | テスト          | Only for test. |
|  2 | パンケーキ      | 食べたい       |
+----+-----------------+----------------+
2 rows in set (0.00 sec)

ちゃんと追加されていました!!(*˘ᗜ˘*).。.:*♡

あとがき

ApacheとPHP+MySQLは、私の過去の実務経験からも、大企業までも頻繁にこのパターンで使われていました。逆にJavaは少なかったかな…と感じています。

PHPのほうがJavaの商用ライセンスよりも安価でオープンなので、大企業でも、もちろん中小個人でも導入しやすいのかな…って感じがします。

LinuxへのApache+PHP+MySQLに導入する費用としては、ライセンスがいらないので、あとは給料に対する労力の費用と、中古PCやラズパイ関係なく、装置の費用なのかな…と感じがします。

導入費用が1時間2500円であれば、Webサーバーとファイルサーバーを新規構築するのに2時間かければ、合計5000円。それに、中古PC or ラズパイとHDDやSSDの新品の組み合わせが3~40000円となれば、新規導入費用は5万円弱なので、高価なWindows Serverマシンを30万円かけて買うよりは、6分の1以下と、中小企業や個人経営程度でもITソリューションの潤いはやってくるのかな…と感じる。

特に、ラズパイなどIoTの場合は、センサーなどの物性分野とハイブリッドで組み合わせて、データ化できるのも期待されるので、今後はいろいろと視野を広げて、Qiitaに載せてみたいと思う( ˙꒳​˙ᐢ )

今後と次回

PHPによるWebアプリサーバー構築のほかにも、Java(OpenJDK)によるWebアプリサーバーについても、構築に触れてみたい

AWS+CentOS+LAMP+WordPress構築

$
0
0

はじめに

私自身、AWSとかLAMP環境とか全く分からない状況から始めた。
EC2にはAmazon Linux2があるが、中身を理解するなら、個人的な意見になるがCentOSで始めたほうが良いと思う。

このページでは、WordPressが使える環境を構築する。OSはCentOS7を使用(Amazonマーケットプレイスにあるやつ)。SSH接続はMacのターミナルから実施。

無料で実施できるので金が無い人でも安心。
(クレカ登録必要なのと、AWSの仕様上使いすぎると金取られるので、サーバーを24時間5台ぐらいつけっぱなしだけは辞めておいたほうが良い。)

セットアップは全体的に以下のサイトを参考。

ネコでもわかる!さくらのVPS講座

AWSのアカウントを作成する

以下のページからAWSのアカウントを作成しコンソールにログインする。

https://portal.aws.amazon.com/billing/signup#/start

仮想マシンを起動する

  1. AMI(Amazonマシンイメージ)を選択

  2. インスタンスタイプを選択

  3. キーペアを作成

    名前は自由。作成するとpemファイルがダウンロードされるので保管する。

鍵の権限設定

これを実行しないと、SSH接続のタイミングでエラーが発生する。pemファイルはSSHKeyフォルダを作成し配置。

chmodコマンド

chmod 600 /SSHkey/CentOS7Key.pem

SSH接続

ブラウザからも接続できるが、ターミナルから実行した方が楽。

MacのターミナルでEC2にSSHでログインする

  ssh -i /SSHkey/CentOS7Key.pem centos@ec2-18-218-155-69.us-east-2.compute.amazonaws.com

ユーザーはrootではなくcentosでログインする必要がある。

CentOS環境整備

  • OSバージョン確認
cat /etc/redhat-release

CentOS Linux release 7.7.1908 (Core)

  • OSアップデート
sudo yum update
  • パスワード設定
sudo passwd centos

これでパスワードを設定していなくても、パスワードを設定できる。

Apache httpdセットアップ

Apache2もあるが、今回はhttpdを使用する。

  • httpdインストール
sudo yum install httpd
  • httpd起動

起動時、centosユーザのパスワードが求められる。

  systemctl start httpd
  • ファイアウォールの設定

http通信を許可するためにポート解放する必要がある。
また、設定前に「firewalld」をインストールする必要がある。
設定後は「firewalld」を再起動する。

sudo yum install firewalld
  sudo firewall-cmd --add-service=http --zone=public --permanent
  systemctl restart firewalld
  • AmazonEC2のセキュリティグループの設定

本来ならこれだけで接続できるが、AmazonEC2の場合はセキュリティグループの設定もしなければならない。以下のリンクの「セキュリティーグループの設定」を参照。

AWS EC2でWebサーバーを構築してみる

  • ブラウザから接続

AmazonEC2のインスタンスの説明にある、パブリックDNSかIPv4パブリックIPをコピペしてhttp接続をする。

以下、パブリックDNSの例。

http://ec2-18-218-155-69.us-east-2.compute.amazonaws.com/

  • サーバー起動時設定

サーバー起動タイミングでhttpdが起動する設定。

  systemctl enable httpd

設定されたかの確認は以下のコマンド。以下のコマンドを実行するとviエディタで開かれる。閉じるときは「:q」。

  systemctl list-unit-files -t service

PHPセットアップ

インストールするときのバージョンに注意する。
以下のやり方を参考。

CentOS7でphp7.4をインストールしようとして躓いた話

  • Remiリポジトリの情報

以下が公式リンク。

http://rpms.remirepo.net/

CentOS7を使用する場合は「remi-release-7.rpm」でいいと思われる。

  • epel-release インストール
sudo yum install epel-release
  • Remiリポジトリインストール
sudo rpm -ivh http://ftp.riken.jp/Linux/remi/enterprise/remi-release-7.rpm
  • php確認
  yum list php*
  • httpd+php7.4+mariadb インストール
sudo yum install httpd mariadb-server php74
  • phpファイル作成

/var/www/html/の下にindex.phpを作成。

<html>
    <body>
      <?php echo "Hello World! php" ?>
    </body>
  </html>

ファイル作成コマンドは以下の通り。

sudo touch index.php

ファイル編集コマンドは以下の通り。

sudo vi index.php

ファイルアップ後再起動は以下の通り。

  systemctl restart httpd

現在の状態だとphpが見えないので、以下を実施する。

  • /etc/yum.repos.d/remi-safe.repo修正(無効にする)
  enabled=1

  enable=0
  • /etc/yum.repos.d/remi-php74.repo修正(有効にする)
  enabled=0

  enabled=1
  • /etc/yum.repos.d/CentOS-Base.repo修正(php除外)

baseとupdatesに以下を追加。(おそらく16行目付近と24行目付近)

  exclude=php*
  • 以前のphpをアンインストール

suでrootユーザに変更してから実行する。

for i in`rpm -qa |grep php`;do yum -y remove $i;done
  • phpを再度インストール
  yum install php
  • サーバー再起動
  systemctl restart httpd.service
  • ページ確認

以下のURLのように/index.phpを後ろにつける。

http://ec2-18-218-155-69.us-east-2.compute.amazonaws.com/index.php

MariaDBセットアップ

既に上のPHP手順でインストールはしている。
一応確認。

  • MariaDBのインストール
  yum install mariadb-server
  • MariaDBの有効化とスタート
  systemctl enable mariadb
  systemctl start mariadb
  • 初期設定
  mysql_secure_installation

具体的な手順は以下参照。

ネコでもわかる!さくらのVPS講座 ~第四回「phpとMariaDBをインストールしよう」

  • ログイン
  mysql -u root -p

PHPMyAdminのセットアップ

DBを操作しやすくするツールである。

  • phpMyAdminのインストール
  yum-config-manager --enable remi
  yum install phpMyAdmin
  • /etc/httpd/conf.d/phpMyAdmin.conf修正




のRequire local
をRequire all grantedに修正

  • phpMyAdminの確認

以下のURLのように後ろに/phpmyadmin/をつける

http://ec2-18-218-155-69.us-east-2.compute.amazonaws.com/phpmyadmin/

WordPressのセットアップ

  • wordpress用のDB作成

以下のリンクを実施。

ネコでもわかる!さくらのVPS講座 ~第八回「WordPressサイトを公開しよう」

  • wordPress取得
  yum install wget
  cd /var/www/html
  wget https://ja.wordpress.org/wordpress-5.4.2-ja.tar.gz
  rm index.php5
  tar xvzf wordpress-5.4.2-ja.tar.gz
  • wordPressセットアップ
rm wordpress-5.4.2-ja.tar.gz
  mv wordpress/*.rmdir wordpress
  chown-R apache:apache *
  systemctl restart httpd
  • 動作確認

いつも通りURLにアクセス。

おわりに

これで画面にWordPressの設定画面が見えます。よって構築は終わりです。お疲れ様でした。
結構細かく書いて、GUI操作だけリンクに書くようにしてるので、この手順を完全に真似すれば構築できると思います。もし分からない、もっといいやり方ある等意見ありましたらコメントお願いします。

読解メモ: PHP RFC: Make constructors and destructors return void

$
0
0

この記事は @carrotRakkoPHP RFC: Make constructors and destructors return voidを読み解いて自分なりにまとめなおしたものです。

英語の解釈や PHP の仕様/実装などなどについて間違っている部分を見つけたらご指摘くださると幸いです。

この記事を書いている時点で読んでいるリビジョンは 2020/07/02 23:13 のものです。

__construct()の返り値の型指定

型指定の仕方を3パターン考えてみます↓

  1. 型指定なし: __construct()
  2. 型指定あり & void: __construct(): void
  3. 型指定あり & void以外: __construct(): bool

PHP 7.4.x では(事実)

型指定の仕方どうなる
型指定なし: __construct()問題なし
型指定あり & void: __construct(): voidFatal error
型指定あり & void以外: __construct(): boolFatal error

PHP 8.0 では(提案)

型指定の仕方どうなる
型指定なし: __construct()問題なし
型指定あり & void: __construct(): void問題なし
型指定あり & void以外: __construct(): boolFatal error

PHP 8.1/9.0 では(提案)

PHP 8.0 では(提案)と同じです。

型指定の仕方どうなる
型指定なし: __construct()問題なし
型指定あり & void: __construct(): void問題なし
型指定あり & void以外: __construct(): boolFatal error

__construct()に返り値の型指定をしなかった場合

暗黙的に返り値の型指定とみなされるパターンを2つ考えてみます↓

  1. なにも指定していないとみなされる(そのまま): 文法違反ですが強いて言うなら __construct(): void|mixed
  2. voidを指定したとみなされる: __construct(): voidとみなされる

PHP 7.4.x では(事実)

なにも指定していないとみなされる(そのまま): 文法違反ですが強いて言うなら __construct(): void|mixed

PHP 8.0 では(提案)

なにも指定していないとみなされる(そのまま): 文法違反ですが強いて言うなら __construct(): void|mixed

PHP 8.1/9.0 では(提案)

voidを指定したとみなされる: __construct(): voidとみなされる

__construct()から値を返した場合

PHP の怒り方を3パターン考えてみます↓

  1. 怒られない
  2. Deprecated
  3. Fatal error

PHP 7.4.x では(事実)

怒られない

PHP 8.0 では(提案)

Deprecated

PHP 8.1/9.0 では(提案)

Fatal error

LinuxでApache2.4(httpd 2.4.43)+PHP7.4でWebサーバー構築 - 4.セキュリティ(chownとfirewalld)編

$
0
0

前提と準備

Linuxサーバー構築の記事

前回まではApache2.4にPHP7.4+MySQL8.0でWebアプリ環境を構築しましたが、セキュリティについても触れないといけませんね;;

Linux標準のfirewalldであれば、iptablesのような難しいコマンドルールだったり、SELinuxといったファイルごとの複雑な制御を行うことなく、誰がどのポートを使うかのみを指定するだけで、ファイアウォールで基本的なセキュリティを設定することができます(もちろん制御が複雑なセキュリティを使ったほうが内部侵入されたときも比較的安心ですが…)

他にもファイルの暗号化も併用したいところだが、話が難しくなるので、今回はfirewalldによるファイアウォール、chownでアクセス権を設定するにとどめておきます

また今回の検証では、別のネットワークセグメントからもアクセスの可否を調べたいので、Webサーバーはまたネットワークアダプタを増設して、もう一つのネットワークセグメントを増設します(192.168.1.0/24を使っていますが、Webサーバーにもう一つネットワークアダプタ(仮想ブリッジですが)増設して、192.168.5.0/24を構築しています)

環境

  • Webサーバープログラム:Apache 2.4.43 + PHP 7.4.6 + MySQL 8.0
  • クライアント(メイン):Windows10 Pro
  • クライアント(増築側):GUIのOSであれば、フリー(ここではInsider Preview使用中のWindows10イタリア語をHyper-V(第1世代)で使用しました)
  • サーバーのアーキテクチャ:x64(動作はHyper-Vの第2世代で確認)
  • Linuxディストリビューション:CentOS 8.1 / openSUSE 15.1 Leap / Ubuntu 20.04(すべて64bit)

前提

  • ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
  • どのディストリビューションでも、ファイアウォールはfirewalldを使う(ディストリビューション独自のファイアウォールコマンドは使用しない)
  • 前回の記事のApache+PHP+MySQLの一式のWebアプリサーバー構築そのものを完了していること

サーバー条件

IPアドレス

  • クライアント(メイン):192.168.1.11
  • クライアント(増築側):192.168.5.2
  • Webサーバー(2ポート):(メイン側IP)192.168.1.18、(増築側IP)192.168.5.1 (どのディストリビューションでも同じIPアドレスで検証)
  • データベースサーバー:(Webサーバーと一体)
  • 所属ネットワークセグメント:(メイン)192.168.1.0/24、(増築)192.168.5.0/24 web-from-2-port.png

パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年6月時点)

  • zlib-1.2.11.tar.gz
  • apr-1.7.0.tar.gz
  • apr-util-1.6.1.tar.gz
  • mysql80-community-release-el8-1.noarch.rpm (CentOS 8.1)
  • mysql80-community-release-sl15-3.noarch.rpm (openSUSE 15.1)
  • mysql-apt-config_0.8.15-1_all.deb (Ubuntu 20.04)
  • oniguruma-devel-6.8.2-1.el8.x86_64.rpm (CentOS 8.1)
  • httpd-2.4.43.tar.gz
  • php-7.4.6.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

firewalldによるネットワークごとのアクセス制御

現時点でのfirewalldの状態

前回の構築そのものを行った時点では、firewalldには以下のルールが存在しているかと思います。

  • 192.168.1.0/24からのポート80(HTTP):許可
  • 192.168.1.0/24からのポート443(HTTPS):許可
  • それ以外に、SSHやSambaなど別途ポートを許可しているものもあるが、ここではWebサーバーと関係ないので省略

この時点では「サーバー条件」の「IPアドレス」の図のように192.168.5.0/24のネットワークを新しく増設しても、ファイアウォールを開けていないので、アクセスできないはずです。

ここではクライアント(192.168.5.2)からWebサーバーへアクセスするには、Webサーバーの増設側のポートのIPアドレス、https(ポートは443)://192.168.5.1/ を入力すればわかります。もちろん192.168.5.0/24側のクライアントからはメイン側(192.168.1.18)へはアクセスできないことは、ネットワークの構造から明白です。

192-168-5-0_443-refuse.png

ほらね(* ॑꒳ ॑* )⋆*

firewalldでネットワークセグメントごとのアクセスを許可・拒否

別のネットワークセグメントのアクセスを許可

では192.168.5.0/24でも許可できるよう、Webサーバーのfirewalldに「192.168.5.0/24からのポート443(HTTPS):許可」というルールを追加してみます

ネットワークセグメントを特定して、特定のポートを受信ルールを入力するには、firewalldのrich ruleを使います。--add-rich-ruleを用いて、'~'で囲ったルールを入れます。

# firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="443" protocol="tcp" accept'

これは、IPファミリーがIPv4、送信元が192.168.5.0/24で、ポート443、プロトコルがTCPで、受信を許可する、という意味です。

both.png

  • 192.168.1.0/24からのポート80(HTTP):許可
  • 192.168.1.0/24からのポート443(HTTPS):許可
  • 192.168.5.0/24からのポート443(HTTPS):許可

上の画面は、このfirewalldの状態でアクセスしてみた画面です。確かにアクセスできるようになりました!!ちなみに永続的にアクセスできるようにするには「firewall-cmd」の後ろに「--permanent」というオプションを付ければ、再起動してもルールが適用されたままになります

ちなみにネットワーク192.168.5.0/24からのポート443(HTTPS)の許可をやめたい場合は、--remove-rich-ruleを使って、'~'で囲った中に、削除したいルールを入れます。例えば、IPv4で、192.168.5.0/24からの、ポート443(TCP)の許可しているルールを削除するには、

# firewall-cmd --remove-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="443" protocol="tcp" accept'

を入力すると、192.168.5.0/24からポート443(HTTPS)にアクセスできなくなります。

192-168-5-0_443-refuse.png

ネットワークアクセスを拒否するには

次にこんな実験をしてみた。メインのネットワークセグメント192.168.1.0/24で、ポート80(HTTP)を拒否し、ポート443(HTTPS)のみを許可した状態にしたままにする場合には、IPv4、192.168.1.0/24からのポート80(TCP)の許可というルールを、削除します

# firewall-cmd --remove-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="80" protocol="tcp" accept'

基本的にfirewalldはルールに存在しないものはデフォルトで遮断しているので、ルールにリストアップされていない場合は受信拒否する(ホワイトリスト方式)ので、上記みたいに、192.168.1.0/24のポート80がルールにない場合は、アクセス不可能になります。

port80refuse.png

Apacheの所有者とパーミッションの管理

Apacheの起動ユーザーを変更する

Apacheではデフォルトでは(ソースをコンパイルしてインストールした場合)、ユーザーは「daemon」で起動しています。ps -auxで、全部のプロセスとユーザー一覧を表示し、うち「httpd」を起動しているものに絞るので、grep httpdで絞り込みます

# ps -aux | grep httpd
root     14249  0.0  0.4 121504 17124 ?        Ss   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14250  0.0  0.5 1332460 22448 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14251  0.0  0.4 1330220 19460 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
daemon   14252  0.0  0.6 1332460 25428 ?       Sl   17:15   0:00 /usr/local/apache2/bin/httpd -k start
admin    17782  0.0  0.0   8176   924 pts/0    S+   18:45   0:00 grep --color=auto httpd

上記のように「/usr/local/apache2/bin/httpd」というhttpd本体を動かしているユーザーは「daemon」というデフォルトのユーザーになります。

では、明示的にApacheを動かすユーザーが決まっている場合、どのようにして編集すればいいのかどうかは、httpd.confを編集して対応しました。例えば、Apacheをユーザー名「apache」、グループ名「users」で実行したい場合(openSUSEで実験したので、グループ名はusersになっています)は、

# vi /usr/local/apache2/conf/httpd.conf
httpd.conf
Userdaemon   ← apacheに置き換える
Groupdaemon   ← usersに置き換える

それで、Apacheを再起動して、再度ps -auxで、httpdを動かしているユーザーを確認すると、

# ps -aux | grep httpd
root     17985  0.0  0.4 121504 16944 ?        Ss   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17986  0.0  0.3 1327980 16060 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17987  0.0  0.2 1327980 11980 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
apache   17988  0.0  0.2 1327980 11980 ?       Sl   18:48   0:00 /usr/local/apache2/bin/httpd -k start
admin    18071  0.0  0.0   8176   836 pts/0    S+   18:48   0:00 grep --color=auto httpd

ちゃんとapacheがhttpdを実行していることがわかります。

Apache上のファイルを他人に見せたい場合と見せたくない場合

Apacheの実行ユーザーを変更できたので、今度は他人に見せるものと見せたくないものを管理したいと思います。

他人のユーザーで、読み込みを許可しない場合

例えば、ApacheのhtdocsフォルダがWebで公開されているものとして、そこには以下のファイルが存在しているものとしましょう。

# cd /usr/local/apache2/htdocs
# ls -l
合計 24
-rw-r--r-- 1 admin users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root  root    45  6月 12  2007 index.html
-rw-r--r-- 1 root  root    20  6月 25 17:14 phpi.php
-rw-r--r-- 1 root  root   172  6月 25 18:52 some.html
-rw-r--r-- 1 admin users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin users 1271  6月 25 17:45 test_form.php

そこで「some.html」にアクセスするとしましょう。所有者はroot、パーミッションは644なので、この設定は、誰でも「some.html」はアクセスできるという意味で、ちなみにApacheでのアクセスは読み込みだけを行い、書き込みも実行も行わないので、読み込むだけにします。

200ok.png

Apacheの実行ユーザーはapacheにしました。その際はちゃんと読み込めました

そこで所有者はrootのままで「some.html」のパーミッションを600(他のユーザーは読み込み不可能にし、ユーザー自身だけが使用できる状態にする)に変更してみたんです

# chmod 600 some.html
# ls -l
合計 24
-rw-r--r-- 1 admin users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root  root    45  6月 12  2007 index.html
-rw-r--r-- 1 root  root    20  6月 25 17:14 phpi.php
-rw------- 1 root  root   172  6月 25 18:52 some.html
-rw-r--r-- 1 admin users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin users 1271  6月 25 17:45 test_form.php

403.png

今度はForbiddenになって読み込めなくなった。Apacheの実行ユーザーapacheがrootのものを読み込むことを禁止されているから明らかだね。

Apache実行ユーザーと同じ所有者で、他人のユーザーを許可しないファイル

今度は、さきほどの「some.html」を、Apache実行ユーザーの所有にし、他のユーザーが読み込めない状態のままに変更して、再度アクセスできるか調べます。

# chown apache:users some.html
# ls -l
合計 24
-rw-r--r-- 1 admin  users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root   root    45  6月 12  2007 index.html
-rw-r--r-- 1 root   root    20  6月 25 17:14 phpi.php
-rw------- 1 apache users  172  6月 25 18:52 some.html
-rw-r--r-- 1 admin  users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin  users 1271  6月 25 17:45 test_form.php

200ok.png

今度はアクセスできた( ´ •̥ ̫ •̥ ` )
自分自身のファイルだから読み込めるんだね

Apache実行ユーザーと同じ所有者で、どのユーザーを許可しないファイル

最後に、誰も読み込みを許可しないhtmlで検証してみます
「some.html」パーミッションは000か200に設定し、誰も読み込みを許可しない設定にします

# chmod 200 some.html
# ls -l
合計 24
-rw-r--r-- 1 admin  users  304  6月 25 17:43 connect.php
-rw-r--r-- 1 root   root    45  6月 12  2007 index.html
-rw-r--r-- 1 root   root    20  6月 25 17:14 phpi.php
--w------- 1 apache users  172  6月 25 18:52 some.html
-rw-r--r-- 1 admin  users  346  6月 25 17:45 test_form.html
-rw-r--r-- 1 admin  users 1271  6月 25 17:45 test_form.php

403.png

そうだよね( ˙꒳​˙ᐢ )
自分自身も読み込みを許可していないと、確かにこうなるしね…

まとめ

ミドルウェアを構築しました、その次に来るのは、誰が何を許可・拒否するかの取り決めを行うことが重要視されるので、Qiitaにはぜひとも基本としてfirewalldと所有者でアクセス権を設定する方法もここに載せておきたかったです

高度なセキュリティ分野だと暗号化も入るらしいが、ファイルシステムの暗号化に踏み切ると、パスワードの入力や鍵のタイミングについても複雑な課題が入ってしまうので、初心者にはあまりお勧めできないかな…と思ったので、必要最低限の暗号化についてはHTTPSの構築方法にとどめておいた( ´ •̥ ̫ •̥ ` )

まぁ大事なのは、漏れては問題となるようなものを置かない、不要なポートを開放しない、それが絶対的な前提だしね…

参照サイト

DockerでPHP7.4の環境を整えるときの問題点

$
0
0

DockerでPHP7.4の環境構築をした話

結論

PHPのDockerfileに以下を加える。

  • DockerでPHP7.4系以上を使うなら、ライブラリの「oniguruma」を入れる。
    • apk add --update --no-cache oniguruma-dev \を追記。
  • DockerでMySQLを使うなら、PDOドライバーを入れよう。
    • RUN docker-php-ext-install -j$(nproc) pdo_mysqlを追記。

対象

  • DockerでPHP7.4以上を使いたい方
  • DockerでMySQLの環境構築。

環境

  • OS Catalina 10.15.4
  • Docker 2.3.0.3
    • php:7.4.7-fpm-alpine

はじめに

phpenvを用いてPHPのバージョン管理すればDockerなんて使わなくていいやと思ってたんですけど、MySQLを使うことになって、5.7と8.0系で認証方式が違うとか、8.0系の方が早いとかありますけど、手元のpcにbrewで8.0から5.7系にダウングレードしたらお手上げ状態になったので、勉強も兼ねてサクッとDockerを使いました。
なお、Dockerfileのことしか主にコードを開示していないので、その他は頑張ってください。

問題点

  • php:7.4.0*の環境のDockerfileをビルド時No package 'oniguruma' foundとエラー。
  • PHPでMySQLを使おうとするとcould not find driverというエラー

上記にもあげている通り、2点問題に遭遇しました。

php:7.4.*の環境のDockerfileをビルド時No package 'oniguruma' foundとエラーがでよる。

はい、1つ目です。

Dockerfile
RUN apk upgrade --update\
&& apk add --update--no-cache oniguruma-dev \ # ここ&& apk --no-cache --virtual .build-deps add make g++ gcc re2c autoconf \
  && apk --no-cache add gettext-dev libzip-dev curl-dev \
  && docker-php-ext-install -j$(nproc) gettext mbstring zip opcache ctype json bcmath sockets curl \
  && pecl channel-update pecl.php.net

php:7.4以降はlibonig-devというパッケージをする必要があるそうです。

https://github.com/kkos/oniguruma

上記2行目にも書いていますが、apk add --update --no-cache oniguruma-devを追加して下さい。

PHPでMySQLを使おうとするとcould not find driverというエラー

はい、2つ目です。

このエラーが出ている人はphpinfo();をphpファイルに書いてもらって、
PDO項目を見ていただくとわかると思うのですが、sqliteしかないと思われます。

つまりデフォルトでドライバーはsqliteしか入ってないらしいですね。

これには少しびっくりしたと同時に、コンテナは本当に最小限のものしか入ってないのかと思いましたね。。。

というわけでDockerfileに書きましょう。

Dockerfile
# PDO driver(MySQL)RUN docker-php-ext-install -j$(nproc) pdo_mysql

先ほどと同じDockerfileに追記してください。

buildした後、phpinfo();のPDO driver項目を見るとmysqlが追加されていると思います。

他のデータベースを使いたい時も同じようにDockerfileにかけば使用できるでしょう。

PHPのDockerfile全体

今回はUbuntuでなく、Alpineで軽量化しています。
なので皆さんご存知のapt-getは使えず、apkというパッケージマネージャを使う必要があります。

Dockerfile
FROM php:7.4.7-fpm-alpineARG environmentRUN apk upgrade --update\
&& apk add --update--no-cache oniguruma-dev \
&& apk --no-cache--virtual .build-deps add make g++ gcc re2c autoconf \
&& apk --no-cache add gettext-dev libzip-dev curl-dev \
&& docker-php-ext-install -j$(nproc) gettext mbstring zip opcache ctype json bcmath sockets curl \
&& pecl channel-update pecl.php.net

# PDO(MySQL)RUN docker-php-ext-install -j$(nproc) pdo_mysql

まとめ

  • PHP7.4以上を使う場合はパッケージonigurumaをDockerfileに追記する。
  • PDOドライバーはデフォルトでsqliteしか入ってない。

参考

EC2上のnginxとPHP7.4を連携

$
0
0

EC2にインストールされたnginx上で、PHP7.4を動かす方法のメモです。
Appacheと違って、少し設定が必要だったので、手順をまとめておこうと思います。
(Amazon Linux 2での設定方法です。)

  • php7.4のインストールは、こちらを参考にしてください。

nginxの設定

/etc/nginx/nginx.confにphpの設定を行います。

$sudovi/etc/nginx/nginx.confserver{listen80;listen[::]:80;server_name_;root/usr/share/nginx/html;#Loadconfigurationfilesforthedefaultserverblock.include/etc/nginx/default.d/*.conf;error_page404/404.html;location=/40x.html{}error_page500502503504/50x.html;location=/50x.html{}location~\.php${fastcgi_passunix:/var/run/php-fpm/php-fpm.sock;fastcgi_indexindex.php;fastcgi_paramSCRIPT_FILENAME$document_root$fastcgi_script_name;includefastcgi_params;}}

php-fpmの設定

/etc/opt/remi/php74/php-fpm.d/www.confにnginxと連携するための設定を行います(5箇所、設定を行います)。

$sudovi/etc/opt/remi/php74/php-fpm.d/www.confuser=nginxgroup=nginxlisten=/var/run/php-fpm/php-fpm.socklisten.owner=nginxlisten.group=nginx

sockファイル用の設定

php-fpmでは、上記の/var/run/php-fpm/php-fpm.sockファイルを起動時に作成します。
Amazon Linux 2では、再起動すると/var/runディレクトリがリセットされてしますので、起動時に/var/run/php-fpmを自動生成するスクリプトを設定します。

$sudovi/etc/tmpfiles.d/php-fpm-run.confd/var/run/php-fpm0755rootroot

php-fpmの起動

以下のコマンドで、php-fpmを起動します。

$sudosystemctlstartphp74-php-fpm.service

php-fpmの確認

以下のコマンドで、php-fpmの状態を確認します。
active(running)となっていれば、正常に起動しています。

$sudosystemctlstatusphp74-php-fpm.servicephp74-php-fpm.service-ThePHPFastCGIProcessManagerLoaded:loaded(/usr/lib/systemd/system/php74-php-fpm.service; disabled; vendor preset: disabled)Active:active(running)sinceMo2020-07-2021:13:23UTC; 36min agoMainPID:24651(php-fpm)Status:"Processes active: 0, idle: 5, Requests: 1, slow: 0, Traffic: 0req/sec"CGroup:/system.slice/php74-php-fpm.service├─24651php-fpm:masterprocess(/etc/opt/remi/php74/php-fpm.conf)├─24652php-fpm:poolwww├─24653php-fpm:poolwww├─24654php-fpm:poolwww├─24655php-fpm:poolwww└─24656php-fpm:poolwww

phpinfo()で、phpが動いているかを確認

phpinfoを呼び出すファイルを作成し、nginxのホームディレクトリ(/usr/share/nginx/html)に配置します。

$sudoviinfo.php<?phpphpinfo(); ?>

ブラウザからアクセスし、以下の画面が表示されれば、nginx上で、phpが正常に稼働しています。

以上で、nginxとPHP7.4の連携設定は完了です。





PHP で型付のローカル変数を定義するライブラリを作った

$
0
0

概要

PHP で型のあるローカル変数を定義するライブラリを作った。

https://github.com/sj-i/typist

誤った型の値を代入しようとすると \TypeErrorを投げる。

型を書ける奴だから TypeWriter か、とも思ったけど、長かったので Typist という名前にした。
PHP 7.4 以降で利用可能。

インストール

composer require sj-i/typist

基本的な使い方

usefunctionTypist\int;usefunctionTypist\string;// int 型と string 型のローカル変数を定義$_=[int($hoge_id,1),string($hoge_name,'name'),];// 渡した値で初期化されるassert(1===$hoge_id);assert('name'===$hoge_name);// int 型の変数へ文字列を突っ込もうとすると TypeError$hoge_id='a';// string 型の変数へ int の値を突っ込もうとすると TypeError$hoge_name=1;

どうなってんの?

PHP 7.4 で型付プロパティが導入された。
こいつには面白い性質があって、ある変数が型付プロパティと同じものを参照している間、その変数は同じものを参照する全ての型付プロパティで宣言された型の制約を引き継いで受けることになる。
よって指定した型の型付プロパティを持つオブジェクトを生成し、参照渡し経由で変数を型付プロパティに参照させ、オブジェクトは GC されないよう使わない変数 $_でつかまえておくことで、この型付ローカル変数も実現できる。

なるほど分からん

型付プロパティの RFCに詳しく書いてある。
ピンとこないとしたら、たぶん PHP における参照がどういう意味のものか、がピンとこないのだと思う。俺もこの RFC を見るまでよく分かっていなかった。ので、PHP の参照について簡単に説明しよう。

PHP の参照

$a=1;$b=&$a;$c=&$b;

上記のようなコードについて考えよう。
最初の 1 行目 $a = 1;の状態を雑に図示するとこう。

Untitled Diagram.png

超シンプル。

次に 2 行目の $b =& $a;の状態を、同じく雑に図示するとこう。

Untitled Diagram (3).png

$b$aを指すというより、$aが指すものと同じものを指す仲間に加わる感じ。

そしてお察しのとおり、更にその次の $c =& $b;の状態はこうなる。

Untitled Diagram (2).png

$c$bを指すように、と数珠つなぎになっていくわけではなく、やはり同じものを指す仲間に加わる感じ。

同じものを指す仲間達をひとまとめにし、(型付プロパティの RFC の言葉を借りて)参照セットとでも呼ぶことにしよう。参照セットにあるどの変数への代入も、同じセット内の他の変数の値へ影響する。同時に一括代入しているようなもの、というとらえ方もできる(実際の実装は異なるが、概念的にはそうもとらえられる)。

参照と型付プロパティ

さて、この参照セットのメンバへ、型付プロパティが含まれている場合は何が起きるだろうか?

Untitled Diagram (4).png

プロパティだと $aとか $bのままじゃ不自然なので、オブジェクト $oのプロパティだということにしよう。

$o=newclass(){publicint$a=1;publicfloat$b;};$o->b=&$o->a;$c=&$o->b;

Untitled Diagram (5).png

型付プロパティは、プロパティが宣言された型の値を持つこと、読み込み時に指定した型の値だけが得られることを保証する仕組み。これを実現するには代入時に値の型の検査が必要。

$o->aへ何かの値を代入するとする。

$o->a=1;// 合法$o->a='abc';// $o->a の型宣言は int なので違法、TypeError

分かりやすい。

しかし、次の例ではどうだろう。

$o->b=1.5;// 合法……?var_dump($o->a);// $o->a の型宣言は int なのに 1.5 とか出ちゃうの……?

あるいはまた、次の例ではどうだろう。

$c=newDateTime();// 合法……?var_dump($o->a);// $o->a の型宣言は int なのに現在日時が出ちゃうの……?

同じ参照セットに含まれる全ての型付プロパティの型で型検査を行い、すべての型検査に通る値だけが代入に成功するのでなければまずい。型宣言によって得られる筈の保証が台無しになってしまう。そして実際に、PHP はそのように順繰りの型検査を行う。

1intの型検査にも floatの型検査にも通るので代入可能。
1.5intの型検査を通らないので代入不可能。
日時型は intの型検査にも floatの型検査にも通らないので代入不可能。

このようにして、型付プロパティと同じ参照セットのメンバであるただのローカル変数 $cは、めでたく代入時に型検査の行われる変数になった、というわけだ。

この参照と型付プロパティについての挙動を把握すると、うまくやれば型付ローカル変数を定義するためのライブラリを作れそうな気がしてくる。

既存の実装

先に今回とってない実装方針について、他実装を紹介しつつざっくり説明。

型付プロパティの参照を取り出して参照代入でローカル変数へ突っ込めば、その変数は元の型付プロパティと同じ型の検査を受けるローカル変数になる。

比類なき nikic の人が Poor Man's Typed Variables として紹介してるのはこちらの方針。

function&int(int$i){$obj=newclass{publicint$prop;};$obj->prop=$i;$GLOBALS['huge_memory_leak'][]=$obj;return$obj->prop;}$i=&int(0);$i="foobar";// Uncaught TypeError: Cannot assign string to// reference held by property class@anonymous::$prop// of type int

このスライドの実装を見ると分かる通り、型付プロパティを持つオブジェクト(以下型付プロパティオブジェクト)を GC させないよう、refcount1を保つためにグローバル変数へ生成したオブジェクトを突っ込んでる。もちろんめっちゃメモリリークする。

azjezz/typedという、型付プロパティの参照で型付ローカル変数を、というのをやってる別ライブラリがある。こちらもおおむね同じやり方。虚空から欲しい型の値を生成してる感を出すため、各変数について生成する型付プロパティオブジェクトはグローバルステートへ突っ込み、refcount を維持する。もちろんめっちゃメモリリークするので、手動のメソッド呼び出しで型付プロパティオブジェクトを開放する方法を提供している。

らなくあさんが以前に紹介していたこの手法の例示も、先にオブジェクトを生成し、プロパティの参照代入でローカル変数へ持ってくるというやり方↓。

これらの実装を見ての感想は↓のような感じ。

  • なんとなく参照代入の &かわいくない気がする
  • メモリ管理をそんなに頑張りたくない
  • 利用側で謎オブジェクトを生成するのは嫌、なんかそれっぽい関数経由で変数定義したい

今回の実装

  • &が露出しないよう参照渡し利用した
    • 型付きプロパティが型を付けたい変数と同じ参照セットに入ればよいのだから、プロパティからの参照代入ではなくプロパティへの参照代入でも同じことができる、というわけだ
    • 型指定で参照渡しした変数は psalm が別の型による再代入へ警告をくれるので、静的解析へも対応
  • 参照渡しの利用によって返り値の枠が空いたので、メモリ管理を頑張らないで済むよう型付プロパティオブジェクト(Enforcer)をそのまま return し、ローカル変数で受け取って、呼び出し側でライフタイムを管理する仕組みにした
    • refcount 維持用に持つだけでアクセスの必要はないオブジェクトなので、見た目の存在感が薄めのローカル変数として $_という配列を用意し、そこへ突っ込んでいく
    • $_は psalm のような静的解析ツールで未使用変数警告を出さず無視してくれる名前でもある
  • 静的メソッドと通常関数の二通りのインターフェースを用意した
  • 今のところスカラ型とクラス / interface、nullable の型指定に対応
    • iterable とか array もやれそう、ただ要素型指定できない奴とか要らない気もする
    • callable はとりあえず諦め

お前この裏技に否定的じゃなかったっけ?使い道は?

  • PHP 7.4 リリース前の去年 1 月時点でわりと否定的なことを言ってた
  • つい最近また同様に否定的なことを言う機会があった
  • その流れで、PHP 8 の機能を使って何か面白いことやれないかな、とガチャガチャ試す時、そういえばこんなのあったなと少し書いてみるのにつながる
  • ガチャガチャやってる間にそれっぽいライブラリになってしまったので、一応公開、という形
  • ローカル変数の型を実行時に強制するのがそんなに有用か、には引き続き懐疑的姿勢
  • PHP 7.4 までの言語の型機能と静的解析ツールの型推論に型アノーテーション、それに加えて webmozart/assertあたりで用足りるのではないか
    • クラスだのメソッドだの関数だの適度に小分けにコード書いてると、ローカル変数はごく短命となる筈で、引数や返り値のような断続的な型チェックでもわりと用が足りてしまい、こういうハックの有用性は薄れる筈
  • ローカル変数の寿命が長く型の動きが不安なコード、であれば、こういうのも多少使う意味があるかもしれない(PHP 7.4 だけど魂はレガシーコード、みたいな奴があれば)
  • 静的解析向けの型アノーテーションは油断すると嘘をつく場合もあるので、不安な箇所でランタイムのチェックも足す用、という意味はあるかもしれない

おまけ


  1. (「参照カウント」と呼ぶと紛らわしい話の流れなので refcount 呼ばわり) 

PHPでClassをrequireせずに使う

$
0
0

Laravelのrequest()とかがどうやって呼ばれてるのかを調べていくうちに辿り着いたのでメモがてら。

Classは簡単に読み込めるけど、Functionは一筋縄ではいかなさそう。

ついでにClassに好き勝手プロパティ増やせることも発見した。

autoloaderを実装

spl_autoload_registerを使う。Laravelはcomposerが上手いことやってくれてるっぽかった

bootstrap.php
<?functionregist(){spl_autoload_register(function(){require'./Hoge.php';require'./Piyo.php';});}

使いたいクラス

適当に用意

Hoge.php
<?classHoge{public$hoge;}
Piyo.php
<?classPiyo{public$piyo;}

autoloaderを呼んで使う

ついでに好き勝手にプロパティも生やす

test.php
<?require'./bootstrap.php';// ここでクラスをautoloadするregist();// どこからも呼んでないけど$h=newHoge;// 生やせる$h->fuga="aaa";$h->hogepiyo="bbb";// 使える$p=newPiyo;$p->fuga="aaa";$p->hogepiyo="bbb";var_dump($h,$p);/*
結果

class Hoge#2 (3) {
  public $hoge =>
  NULL
  public $fuga =>
  string(3) "aaa"
  public $hogepiyo =>
  string(3) "bbb"
}

class Piyo#3 (3) {
  public $piyo =>
  NULL
  public $fuga =>
  string(3) "aaa"
  public $hogepiyo =>
  string(3) "bbb"
}

*/

連想配列のデフォルト値を省略すると実行速度は速くなる

$
0
0

気になったこと

以下のように引数の連想配列に不足しているキーを自動で補ってくれる関数がある。

f.php
functionf($option=[]){$option+=['text1'=>'1','text2'=>'2','separator'=>'/',];returnimplode($option['separator'],[$option['text1'],$option['text2']]);}

関数の呼び出し時に(a)オプション配列にすべてのキーを指定する場合、(b)デフォルト値を省略した場合、どちらの実行速度が速いか?

時間計測スクリプト

benchmark.php
$t1=microtime(true);for($i=0;$i<500000;$i++){f(['text1'=>'abc','text2'=>'def','separator'=>'/',]);}$t2=microtime(true);$t3=microtime(true);for($i=0;$i<500000;$i++){f(['text1'=>'abc','text2'=>'def',]);}$t4=microtime(true);echo($t2-$t1)."\n";echo($t4-$t3)."\n";exit;

結果

条件実行時間avg (sec)
(a)オプション配列にすべてのキーを指定する場合0.12881302833557
(b)デフォルト値を省略した場合0.12693285942078
  • デフォルト値を省略したほうが実行速度は(若干)速い。
  • フレームワーク(CakePHP, Laravelなど)の関数のように中身が複雑だと、さらに差が広がる。
  • デフォルト値は省略したほうが実行速度は速いが、フレームワークのバージョンアップでデフォルト値が変わり、デグレが起きる可能性に留意する。

AWS EC2 Linuxにオプションモジュールをインストール

$
0
0

症状

WordPressの管理者画面のサイトヘルスに「1件の致命的な問題」が出てる><
内容は、以下の通り。

1つ以上の必須モジュールが存在しません
PHP モジュールはサイトの稼働に必要なほとんどのタスクをサーバー上で実行します。変更はサーバー管理者が実施する必要があります。
- オプションのモジュール dom がインストールされていないか、無効化されています。
- オプションのモジュール mbstring がインストールされていないか、無効化されています。
- オプションのモジュール imagick がインストールされていないか、無効化されています。
- 必須モジュール gd がインストールされていないか、無効化されています。

環境

  • AWS Amazon EC2 Linux 2
  • php 7.4.7
  • WordPress

対処法

上で「インストールされていないか、無効化されています」と言われているものをインストールして、httpd.serviceを再起動する。

$ sudo yum install -y php php-dom
$ sudo yum install -y php php-mbstring
$ sudo yum install -y php php-imagick
$ sudo yum install -y php php-gd
$ sudo systemctl restart httpd.service

wordpressのダッシュボードに行くと、致命的な問題が消えた。よかったよかった。

PHP try catch finally文で例外処理を記載する

$
0
0

目的

  • 例外処理の確認方法を下記にまとめる

実施環境

  • 下記に例外処理のtry catch finally文の書き方の例を記載する。

    try{//例外が発生する可能性がある処理}catch(例外クラス名例外クラスの戻り値を受け取る変数名){//「例外が発生する可能性のある処理」が例外となった時に実行される処理}finally{//例外に関係なく実行される処理}

参考文献

Viewing all 113 articles
Browse latest View live