PHPの多重起動防止の書き方

実行時にロックファイルを作成して、実行が終わったら削除する

<?php

$lock_file = "./test.lock";

if (!file_exists($lock_file)){
	$fp = fopen($lock_file, "w+");

	for($i = 0; $i < 50; $i++){
		sleep(1);
	}

	fclose($fp);
	unlink($lock_file);
} else {
	echo "Already running!!";
}

ただし、実行中にエラーがあることがあるので、排他ロックをするという方法もある。

$file = fopen("test.lock", "w+");

if (flock($file, LOCK_EX + LOCK_NB)){
	
	for($i = 0; $i < 50; $i++){
		sleep(1);
	}

	flock($file, LOCK_UN);
} else {
	echo "Already running!!";
}

LOCK_EX: 排他的なロック。このフラグ単体だけの場合、他プロセスがアクセスした時に解除されるまでブロックするという挙動になる

PostgreSQLに画像を保存する

テーブルを作成
$ CREATE TABLE test(image bytea);

bytea型はバイナリデータを保存。1Gまで可能
https://www.postgresql.jp/document/9.6/html/datatype-binary.html

画像データの登録

$dsn = "pgsql:dbname=testdb host=localhost port=5432";
$user = "user1";
$password = "password";

try {
	$dbh = new PDO($dsn, $user, $password);
	print('connection success<br>');

	$stmt = $dbh->prepare("insert into test (image) values (:image)");

	$fp = fopen("images.jpeg", "rb");
	$stmt->bindParam(':image', $fp, PDO::PARAM_LOB);
	$stmt->execute();

} catch(PDOException $e){
	print("Error:".$e->getMessage());
	die();
}

画像の表示

$dsn = "pgsql:dbname=testdb host=localhost port=5432";
$user = "user1";
$password = "password";

try {
	$dbh = new PDO($dsn, $user, $password);

	$stmt = $dbh->prepare("select image from test");
	$stmt->execute();
	$stmt->bindColumn("image", $image, PDO::PARAM_LOB);
	$stmt->fetch();
	header("Content-Type: image/jpeg");
	fpassthru($image);

	unset($db);
} catch(PDOException $e){
	print("Error:".$e->getMessage());
	die();
}

なるほど、DBに画像保存も可能なのね。
データサイズが気になるが、軽量な画像であれば良さそう

PostgreSQLにPDOでphpから接続する

### 接続

$dsn = "pgsql:dbname=testdb host=localhost port=5432";
$user = "user1";
$password = "password";

try {
	$dbh = new PDO($dsn, $user, $password);
	print('接続に成功しました');
} catch(PDOException $e){
	print("Error:".$e->getMessage());
	die();
}

### sql実行

$dsn = "pgsql:dbname=testdb host=localhost port=5432";
$user = "user1";
$password = "password";

try {
	$dbh = new PDO($dsn, $user, $password);
	print('接続に成功しました<br>');

	$sql = 'select * from department';
	foreach($dbh->query($sql) as $row){
		print($row['department_code'].":");
		print($row['department_name'].",");

	}
} catch(PDOException $e){
	print("Error:".$e->getMessage());
	die();
}

$ php index.php
接続に成功しました
a:営業部,b:総務部,d:経理部,e:人事部,f:物流部,

PDOはmysqlと然程変わらないですな

PostgreSQLのVACUUMとは?

PostgreSQLは削除フラグがついて見えなくなっているだけのため、削除データを定期的にきれいにする必要があり、この処理をVACUUMという。

$ psql –version
psql (PostgreSQL) 14.2 (Ubuntu 14.2-1.pgdg20.04+1)
$ sudo -u postgres psql
psql (14.2 (Ubuntu 14.2-1.pgdg20.04+1))
Type “help” for help.

postgres=# \l
postgres=# \c testdb;
testdb=# \d department;
testdb=# select * from department;

testdb=# delete from department where department_code = ‘c’;
testdb=# vacuum verbose department;
INFO: vacuuming “public.department”
INFO: scanned index “pk_department” to remove 1 row versions
DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO: table “department”: removed 1 dead item identifiers in 1 pages
DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO: index “pk_department” now contains 5 row versions in 2 pages
DETAIL: 1 index row versions were removed.
0 index pages were newly deleted.
0 index pages are currently deleted, of which 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO: table “department”: found 1 removable, 5 nonremovable row versions in 1 out of 1 pages
DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 747
Skipped 0 pages due to buffer pins, 0 frozen pages.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM

vacuum ${table_name} でテーブルをvacuumできる。
なるほど。

autovacuumの設定はpostgres.confで設定されている。

php_valueの使い方

Apacheのモジュールとして使用しているPHPの設定を.htaccessで変更する場合、php_valueとphp_flagで設定する。
php_flagは論理値(true, false)を設定し、php_valueはそれ以外の値を設定する
php_value, php_flag共にPHP_INI_ALLまたはPHP_INI_PERDIRの設定オプションに対して利用できる
セットされている値をクリアしたい場合、php_valueにnoneを値として設定することでクリアできる

よく設定する項目

php_value memory_limit 128M
php_value memory_limit -1
php_value post_max_size 64M
php_value upload_max_filesize 10M
php_value max_execution_time 60
php_value mbstring.internal_encoding UTF-8
php_value mbstring.detect_order UTF-8, SJIS-win, SJIS, eucJP-win, EUC-JP, JIS, ASCII
php_value mbstring.language Japanese
php_value output_buffering off
php_value max_input_vars 2000
php_flag session.cookie_secure On
php_flag session.cookie_httponly On
ini_set("session.cookie_path", "/path/");
php_value date.timezone "Asia/Tokyo"
php_flag short_open_tag On
php_flag display_errors On
php_value error_reporting 6135 // all error
php_flag log_errors On
php_value error_log "./logs/error.log"

### テスト
/etc/apache2/apache2.conf

<Directory /var/www/>
	Options Indexes FollowSymLinks
	AllowOverride None
	Require all granted
</Directory>

.htaccess

php_flag log_errors On
php_value error_log /var/www/html/error.log

$ sudo systemctl restart apache2

error.log
[31-Dec-2022 03:49:59 UTC] PHP Parse error: syntax error, unexpected end of file, expecting ‘;’ or ‘,’ in /var/www/html/app.php on line 5
[31-Dec-2022 03:50:00 UTC] PHP Parse error: syntax error, unexpected end of file, expecting ‘;’ or ‘,’ in /var/www/html/app.php on line 5

OK!

MFAとGoogle Authenticatorの仕組み

AWSのMFAで使用されるGoogle Authenticatorのワンタイムパスワード(TOTP)は、サーバとクライアントで共有する秘密鍵と現在時刻から確認コードを計算するアルゴリズム。

80ビットの秘密鍵をQRコード(16文字のBase32※(A-Z, 2-7))としてブラウザ上に表示し、サーバとクライアントに同じ秘密鍵が保存される。現在時刻は30秒ごとに値が変わるカウンターに変更されてから確認コードの6桁の数字が計算されてアプリに表示される。

※32種類の英数字のみを用いてバイナリデータを示す

TOTPに対応するサービスはGoogle, Amazon, Microsoft, Twitter, Instagram, Facebook, Dropbox, Github, WordPress, Slackなど。

K: 秘密鍵
TC: 現在時刻(UNIX TIME)
X: 時間ステップ(30)
T0: カウント開始時刻(0)
N: トークンの長さ(6)
ハッシュアルゴリズム: SHA-1

T = floor((TC – T0) / X)
H = HMAC-SHA-1(K, T)
TOTP = Truncate(H)
※RFC 4226 に定められている20バイト文字列から10進数N桁のトークンを得る関数

TOTPの実装方法についてClassmethodさんの解説
https://dev.classmethod.jp/articles/totp-implementation-pure-python/

自分でもやろうと思ったが、HMAC-SHA-1以降のTruncate関数の実装のところがうまくできない。

import os
import base64
import time
import math
import hmac
import hashlib

private_key = os.urandom(5) # 5byte = 80bit
private_key_base32 = (base64.b32encode(private_key))

t1 = time.time()
h = math.floor((t1 - 0) // 30)
sig = hmac.new(private_key_base32, str(h).encode('utf-8'), hashlib.sha1).hexdigest()

MFAが共通の秘密鍵とUnixTime、SHA-1のハッシュ関数からTruncate関数で10進数に変換して30秒ごとに表示しているということがわかりました。UNIXタイムを認証に使う場合、サーバ側とクライアント側でUnixタイムがずれていないことが前提となるため、どうやって担保するのが良いかわかりませんでしたが、アプリケーション側のユーザ認証でもAWSのMFAと同じような秘密鍵を共有した認証の仕組みが作れたら仕組みとして面白いのかなと思いました。

参考サイト
https://sekika.github.io/2016/03/26/GoogleAuthenticator/

[docker] amazonlinux2でPHP8.1を動かしたい

Dockerfile

FROM amazonlinux:2

RUN yum update -y

RUN amazon-linux-extras install -y epel
RUN yum -y install \
http://rpms.remirepo.net/enterprise/remi-release-7.rpm

RUN yum -y install \
    pcre-devel \
    php81 \
    php81-php-cli \
    php81-php-common \
    php81-php-devel \
    php81-php-gd \
    php81-php-intl \
    php81-php-mbstring \
    php81-php-mysqlnd \
    php81-php-pear \
    php81-php-pecl-apcu \
    php81-php-process \
    php81-php-opcache \
    php81-php-redis \
    php81-php-soap \
    php81-php-pecl-xdebug3 \
    php81-php-xml \
    php81-php-zip

 RUN alternatives --install /usr/bin/php php /usr/bin/php81 1

$ sudo docker build . -t amzn2php8.1
extrasではなくepelでremiでインストールする。
なるほどー

docker-compose up -dとdown –rmi all

$ docker-compose up -d
docker-compose.yml

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:80"

http://192.168.56.10:8080/
$ sudo docker images;
dockerhttpd_web latest 71323a09a84b 2 minutes ago 56.9MB
$ sudo docker-compose down
$ sudo docker images;
dockerhttpd_web latest 71323a09a84b 4 minutes ago 56.9MB
-> docker-compose downだけだと、imageは残ったまま

$ sudo docker-compose up -d
$ sudo docker-compose down –rmi all
$ sudo docker images;

imageも削除される
なるほど、概要は理解した

【clocコマンド】ディレクトリ配下のソースコードの行数を調べたい

wcコマンド: ファイルの行数、文字数を数える

$ ls -la
total 16
drwxrwxr-x 2 vagrant vagrant 4096 Dec 8 08:55 .
drwxr-xr-x 24 vagrant vagrant 4096 Dec 8 07:38 ..
-rw-rw-r– 1 vagrant vagrant 928 Dec 8 09:20 api.php
-rw-rw-r– 1 vagrant vagrant 892 Dec 8 09:24 index.php

$ wc -l $(find ./ -type f)
30 ./api.php
39 ./index.php
69 total

### cloc
clocコマンドでも利用できるみたいだ
$ sudo apt install cloc
$ cloc ./
2 text files.
2 unique files.
0 files ignored.

github.com/AlDanial/cloc v 1.82 T=0.01 s (199.1 files/s, 7068.3 lines/s)
——————————————————————————-
Language files blank comment code
——————————————————————————-
PHP 2 16 7 48
——————————————————————————-
SUM: 2 16 7 48
——————————————————————————-

うーん、wcよりclocの方が正確だな…