Swaggerの基本構造を学びたい

sample.yaml

openapi: 3.0.0
info:
  ...
servers:
  ...
paths:
  ...
components:
  ...
security:
  ...
tags:
  ...
exeternalDocs:
  ....

openapi: バージョンを記載(必須)
info: APIのメタデータ(必須) title, description, termsOfService, contact, license, version
servers: APIを提供するサーバを記述(配列で複数記述可能, 任意)
paths: APIで利用可能なエンドポイントやメソッドを記述(必須)
L put(tag, summary, description, operationId, requestBody(description, contents(application/json, xml, x-www-form-urlencoded), required), responses)
L post(tags, summary, description, operationId, requestBody(description, contents(application/json, xml, x-www-form-urlencoded), required)), response)
L get (tags, summary, description, operationId, requestBody(description, contents(application/json, xml, x-www-form-urlencoded), required)), response, security)
L get (tags, summary, description, operationId, parameters, response)
components: APIで使用するオブジェクトスキーマを記述(必須)
L schimas: type, properties(type(integer, string, array), format(int64, int32, date-time), example, required)
security: API全体を通して使用可能なセキュリティ仕様を記述する(OAuth)
tags: name, description, externalDocs, APIで使用されるタグのリスト。各種ツールによってパースされる際は、記述された順序で出力される。タグ名はユニークでなければならない。(任意)
externalDocs: 外部ドキュメントを記述する(API仕様書)

なるほど、swaggerの仕様を理解するだけで大分違うな…

OpenAPI, Swagger入門

OpenAPI: RESTful APIの仕様を記述するフォーマット
Swagger: OpenAPIを使用するツール

## Swagger Editor
$ git clone https://github.com/swagger-api/swagger-editor.git
$ cd swagger-editor
$ npm start

http://192.168.56.10:3001/

なんだこれは…

OpenAPIに慣れたい

swaggerのbasic structure
https://swagger.io/docs/specification/basic-structure/

$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require zircote/swagger-php
$ php composer.phar exec openapi -h

openapiファイルの作成
$ vendor/bin/openapi -o ${出力先} –format ${出力フォーマット} ${スキャン対象ディレクトリ}
$ vendor/bin/openapi -o openapi.json –format json app/Http/Controller/Api

<?php

use OpenApi\Annotations as OA;

class OpenApi {}

class MyController {

	public funtion getResource(){
		
	}
}

https://zircote.github.io/swagger-php/guide/
The idea is to add swagger-php annotations or attributes next to the relevant PHP code in your application. These will contain the details about your API and swagger-php will convert those into machine-readable OpenAPI documentation.

require("vendor/autoload.php");

$openapi = \OpenApi\Generator::scan(["project"]);

header('Content-Type: application/x-yaml');
echo $openapi->toYaml();

annotation:あるデータに対して関連する情報(メタデータ)を注釈として付与すること

/**
 * @OA\Get(
 * tags={"Common"},
 * path="/api/user",
 * @OA\Response(
 *		response="200".
 *		description="success",
 *		@OA\JsonContent(ref="#/components/schemas/user_responder")
 * ),
 * @OA\Response(
 *		response="204",
 *		description="there is no authorized user",
 *		@OA\JsonContent(ref="#/components/schemas/204_no_content")
 *		)
 *	)
 */

schema:

/**
 * @OA\Schema(
 *	schema="user_responder",
 *	required={"id", "name", "email","created_at"},
 *	@OA\Property(
 *		property="id",
 *		type="integer",
 *		description="userId",
 *		example-"1"
 *	),
 *	@OA\Property(
 *		property="name",
 *		type="string",
 *		description="username",
 *		example="sample taro"
 *	),
 *
 *)
 */

annotationはパスとレスポンスで、schemaは入力値を定義しとるんかな。
どちっかというと、swaggerがよくわかってない。

openapi-generatorを利用

$ npm init
$ npm install –include=dev openapi-generator-cli
npm ERR! code E404
npm ERR! 404 Not Found – GET https://registry.npmjs.org/openapi-generator-cli – Not found
npm ERR! 404
npm ERR! 404 ‘openapi-generator-cli@*’ is not in this registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

npm ERR! A complete log of this run can be found in:
あれ? イマイチよくわからない

PostgreSQLでリレーション関係のあるテーブルを作りたい

create table person (
	id int,
	first_name varchar(50),
	last_name varchar(50),
	gender varchar(7),
	birth_day date
);

testdb=# \d person
Table “public.person”
Column | Type | Collation | Nullable | Default
————+———————–+———–+———-+———
id | integer | | |
first_name | character varying(50) | | |
last_name | character varying(50) | | |
gender | character varying(7) | | |
birth_day | date | | |

### テーブルに制約をつける

create table person (
	id BIGSERIAL not null primary key,
	first_name varchar(50) not null,
	last_name varchar(50) not null,
	gender varchar(7) not null,
	birth_day date not null,
	email varchar(150)
);

bigserial 整数を自動決定
primary key キーとして扱う
not null 空白を許可しない

### テーブルにデータを挿入

insert into person (
	first_name,
	last_name,
	gender,
	birth_day
) values ('Anne', 'Smith', 'Femail', DATE '1988-01-09');

insert into person (
	first_name,
	last_name,
	gender,
	birth_day,
	email
) values ('Jake', 'Jone', 'Male', DATE '1990-01-10', 'jake@gmail.com');

testdb=# select * from person;
id | first_name | last_name | gender | birth_day | email
—-+————+———–+——–+————+—————-
1 | Anne | Smith | Femail | 1988-01-09 |
2 | Jake | Jone | Male | 1990-01-10 | jake@gmail.com
(2 rows)

データの自動生成
https://mockaroo.com/

create table person (
	id BIGSERIAL not null primary key,
	first_name varchar(50) not null,
	last_name varchar(50) not null,
	gender varchar(7) not null,
	email varchar(150),
	date_of_birth date not null,
	country varchar(50) not null
);

$ \i /home/vagrant/dev/app/person.sql
$ select * from person;

create table person (
	id BIGSERIAL not null primary key,
	first_name varchar(50) not null,
	last_name varchar(50) not null,
	gender varchar(7) not null,
	email varchar(150),
	date_of_birth date not null,
	country varchar(50) not null,
	car_id BIGINT REFERENCES car (id),
	unique(car_id)
);
create table car (
	id BIGSERIAL not null primary key,
	make varchar(50) not null,
	model varchar(50) not null,
	price varchar(50) not null
);

BIGINT REFERENCESで関連づけるのか。なるほど、勉強になるね。

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!