著者アーカイブ 杉浦

著者:杉浦

【Vue.js】vue-cliでElectronとVueによるデスクトップアプリを作る

 Electronはweb開発に同様のコードでデスクトップアプリを作成するためのプラットフォームです。もともとweb系の開発者ならば新たな言語を増やさずにデスクトップアプリを作ることができます。Electronは便利なのですがただ使うだけでもそれなりに設定が必要になります。vue-cliとつなげるとVue用のボイラープレートがそろった段階で開発を始められます。
 Electron | Build cross platform desktop apps with JavaScript, HTML, and CSS.
 vue-cliはVue.js回りの開発を行うための様々なものが詰め込まれたツールです。GUIが備わっているのでなんとなく色々触るだけでも結構使い方がわかります。
Vue CLI
用意されている中で特に便利なのがプラグインです。Vueと何か他の抽象的な大がかりなパッケージ(TypeScript、ESLint、Jestなどなど)を組み合わせる時に面倒になる周辺設定をパパっと済ませてくれます。Electronもこれに含まれます。
 まずはVue.jsが含まれているプロジェクトを開きます(npm i -g @vue/cliでインストール。vue uiでGUIに移動。新しいプロジェクトの作成でもインポート[既存のvueコード読み込み]でもOK)。後はプロジェクトプラグインからvue-cli-plugin-electron-builderを選んでインストールするだけです。

 するとElectronを扱うのに必要な環境設定コードと実行スクリプトがダンプされます。


vue uiの画面を使えばビルドの設定もUIでできます。これでビルドスクリプトを実行すればデスクトップアプリの完成です。

 

著者:杉浦

ショートカット練習サイトShortcutFooの紹介

 ShortcutFooは様々なショートカットを練習するためのサイトです。ありがちなアプリのショートカットキーのみならず、正規表現、Ruby等の言語、ポート番号に割り当てられたプロトコルなど様々なモノの練習ができます。
Learn shortcuts and commands | ShortcutFoo

 基本的な使い方はDojosに行き好きな対象を選んでPractice、Testを繰り返すのみです。短時間で相当な回数反復できる様に作られているのでショートカットが分かってくると練習開始時より知識が増えたのを実感しやすいです。

 注意点としてテストに出なくともショートカットの説明文も併せて覚えるべき、という点があります。ShortcutFooでスコアを稼ぐには問題文とショートカットの対応を覚えるだけでどういう動作をするショートカットなのか覚える必要がありません。スコアを伸ばすことに集中しだすとショートカットの練習としてまるで役に立たなくなります。存在を知ったショートカットを適宜使用して動作も自身に定着させるべきでしょう。

著者:杉浦

【Go】DLLを読み書きする

 DLL(Dynamic Link Library)はWindowsのOSに近い階層で動作するバイナリアプリです。Libraryとある様に他のプログラムに使われることが意識されています。この他のプログラムの範囲にDLL外のプログラムも含まれている点で独特です。
ダイナミックリンクライブラリ – Wikipedia
DLL とは
 DLLを読むことは多くの言語でできるようです。少なくともJavaScript、PHP、Python、Go言語で可能です。作る方はどうにも少ないようです。この記事ではGo言語でDLLを作成する方法も紹介します。
 読み込みはsyscallパッケージを用います。言語開発者がさじを投げて仕切り直そうとしている部分ですが、とりあえず今動かしてHello Worldするぐらいならできます。
 syscall – The Go Programming Language
 src/syscall/dll_windows.go – The Go Programming Language
 読み込みは次の様にできます。ただ実行するだけでちょっと面倒なのでラッピングした方が便利そうです。


package main

import (
	"fmt"
	"syscall"
)

func main() {
	// 好きなdllを指定
	dll := syscall.MustLoadDLL("Hoge.dll")
	// この関数が終わった時にDLLをメモリから解放
	defer dll.Release()
	// dll中の関数を呼び出し
	proc := dll.MustFindProc("FugaFuga")
	// 引数はuintptr型に変換
	buffer := []byte("Hello World\x00")
	arg := uintptr(unsafe.Pointer(&buffer[0]))
	// 結果を取得. 返り値は(r1, r2 uintptr, lastErr error)
	r, _, _ := proc.Call(arg)
	// 結果を表示
	fmt.Println(r)
}

 書き込みは色々と方法があるようです。一つのやりかたはC言語になりきることです。C言語のコンパイラを利用してGo言語をDLLを作ることができます。


package main

import "C"

// ↓のコメントはDLLのためのデコレータ

//export GetRouteFilename
func GetRouteFilename() int {
	// DLL内で実行される関数の中身
	msg := []byte("Hello World")
	println(msg)
	return 0
}

func main() {
	// main()がないとバグる
}

 これを次のコマンドでコンパイルするとDLLが作られます。

go build -buildmode=c-shared  -o godll.dll godll.go
著者:杉浦

【Laravel】6.xからの新しいデバッグ用関数ddd

 Laravelにはdump(), dd()の様なデバッグ用にリッチな画面出力関数が用意されています。dddはそれに加わる新たなデバッグ用関数です。
ヘルパ 6.0 Laravel#dd
Laravel Ignition Introduces the ddd() Helper – Laravel News
 Laravel6.xではエラーページ用の機能がLaravel/Ignitionになっています。dddはこのIgnitionに付属する機能です。Ignitionのエラーページは画像の様な白を基調とした画面で今までの黒画面とはガラッと変わりました。タブ毎にデータを分けることで肥大化したデバッグ用情報を読みやすくしています。

Ignition Is the New Error Page for Laravel – Laravel Newsから引用
 ddd関数は画面出力をこのエラーページ同様にリッチなものにします。

 ddd関数を使うことでリクエスト、ログイン情報、発行されたSQLなども合わせて見ることができます。

著者:杉浦

【Linux】xxdコマンドでバイナリファイルを16進数で表示、16進数からバイナリファイルを構築

 バイナリファイルを扱う時、特に面倒なのは読み書きにバイナリ専用の道具が必要になることです。16進数表記のテキストにすることで一気に操作しやすくなります。Linuxのコマンドを用いるならば次のようにこれを実現できます。リンクはDebianですが大体同じ感じで他の色々なLinuxデストリにも入っているようです(手元のGit Bashでもできました)。
xxd(1) — xxd — Debian unstable — Debian Manpages

xxd -p ファイル名

 これを使うだけで次の様にバイナリファイルが16進表記で表示されます。これを読みやすい様2^n文字区切りにして保存すればそれでOKです(自分の場合、リダイレクトでファイル化→手元のサクラエディタで弄る、としています)。そうすればバイナリファイルのフォーマットのリファレンスと快適ににらめっこして作業を進められます。

 似たような16進表記用コマンドにhexdump, odというものもあります。
 編集した後の16進表記ファイルをバイナリファイルに再構築する必要もあります。これは次のコマンドでできます。

xxd -r -p 16進表記ファイル名 出力されるバイナリファイル名

 オプションもろくに覚える必要もなく実に簡単です。

著者:杉浦

【PHP】提案中のObject Initializerとオブジェクト初期化の小技

 PHPでObject Initializerという記法が現在提案されています。
 PHP: rfc:object-initializer
 [RFC] Object Initializer – Externals
 [VOTE] Object Initializer – Externals
 その名の通り、オブジェクトの初期化に関する記法です。具体的には次の様な記述をします。

<?php
 
class Customer
{
  public $id;
  public $name;
  private DateTimeImmutable $createdAt;
 
  public function __construct()
  {
    $this->createdAt = new DateTimeImmutable("now");
  }
}
 
class Car
{
  public int $yearOfProduction;
  public string $vin;
}

<?php
 
$customer = new Customer {
  id = 123,
  name = "John Doe",
};
 
$car = new Car {
  yearOfProduction = 2019,
  vin = "1FTFW1CVXAFD54385",
};
// <a href="https://wiki.php.net/rfc/object-initializer">PHP: rfc:object-initializer</a>から引用

 単なる代入によるpublicプロパティの初期化をコンストラクタ外で行おうとする方法です。これによりコンストラクタのコーディング、プロパティの代入忘れ、引数の順番間違いから解放されます。
 これだけだと良いやり方に見えますが、提案の賛成:反対は2019/10/08 17:30(JST)時点で3:17です。投票に理由をコメントする必要はないのでスレを読み取って想像するしかありませんが、コンストラクタと共存すると怪しかったり、__setを介する直接代入でない部分の扱いだったり、今のPHPでも手間をかければ似たようなことができたりで賛成者は少ないです。

 PHPの既にある仕様で多数のプロパティを初期化する方法は多々あります。フレームワークを利用するとさらに増えます。自分がよく使うのは連想配列を用いたパターンです。

<?php
class Pos {
    public function __construct($attributes)
    {
        $this->lat = $attributes['lat'];
        $this->lng = $attributes['lng'];
        $this->x = $attributes['x'] ?? null;
        $this->y = $attributes['y'] ?? null;
    }
}

new Pos([
    'lat' => 35.658577,
    'lng' => 139.745451
]);

 連想配列にまとめることで引数を抑え、連想配列であるため引数の意味を理解しやすい、というものです。このやり方にはIDEの自動補完がまるで働かないという点で問題がありますが、程よく手軽に使える点と他クラスのarray変換ですぐ結び付けられる点で使用しています。他にもstdClassを使ったパターンやインタフェースとDTO(DataTransfarObject)を使うやり方もあります。後者は大がかりなものを扱う時、重用できます。

<?php
class Pos {
    public function __construct($attributes)
    {
        $this->lat = $attributes->lat;
        $this->lng = $attributes->lng;
    }
}

$args = (object)[
    'lat' => 35.658577,
    'lng' => 139.745451
];
new Pos($args);
interface PosArgsContract {
    public $lat;
    public $lng;
}
class Pos {
    public function __construct(PosArgsContract $attributes)
    {
        $this->lat = $attributes->lat;
        $this->lng = $attributes->lng;
    }
}

 初期化とは少し違いますがLaravelのFormRequestとEloquentをつなぐやり方として次のワンライナーがあります。

public function create(UserCreateRequest $request) {
    $user = User::create($request->validated());
}

 validatedメソッドの返り値はバリデーションを通過したリクエストのボディ全ての連想配列です。リクエストのキーをテーブルのキーと一致させることによって上記の記述でブラウザのリクエストからデータベースへのSQL発行まで一気に繋げられます。しかもバリデーション通過済みなので安全な値です。

著者:杉浦

【Laravel】EloquentモデルのObserverの活用

 LaravelのEloquentにはObserverという仕組みがあります。ObserverはEloquentのイベント発生によって発火するメソッドの集まりです。
Eloquent:利用の開始 6.0 Laravel#オブザーバ
 Observerは次のようにartisanでボイラープレート的なモノを生成できます。

php artisan make:observer UserObserver --model=User

 以下の様に生成されたクラスをLaravelの起動時処理で登録することによってイベント監視が動作します。

<?php

namespace App\Providers;

use App\Observers\UserObserver;
use App\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    //省略
    /**
     * 全アプリケーションサービスの初期起動
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class); // ここでモデル::observe(オブザーバクラス::class)が基本
        Post::observe([PostObserver::class, BBSObserver::class); // 複数でもOK
        foreach (['Post', 'Comment'] as $className) {// 共通のオブザーバを使うならこんな書き方もあり
            /* @var Eloquent $className */
            $className::observe(UpdateTalkedAtObserver::class);
        }
    }
}

 オブザーバの使い時は色々あります。例えば、ある集約全体における更新日時を記録する場合です。
 LaravelのEloquentにはタイムスタンプを自動記録する機能があり、あるレコードを更新した際にそのレコード内の特定のカラム(デフォはupdated_at)に日時を記録してくれます。これだけでも便利なのですが、これだけではシステムの要求を満たせない場合もあります。例えば、あるユーザの情報が複数テーブルに分割されており、いずれかの情報が更新された時にユーザ情報が更新されたとし、その更新日時を記録する必要がある、という場合です。より具体的に次のER図のテーブル構成があるとします。

 ブログへの投稿かコメントを行った時にユーザの最終発言日時を記録する、とします。このような時、次のObserverを作ると要求を満たせます。

class UpdateTalkedAtObserver
{
    public function created(Eloquent $postOrComment) // 宣言した型に依らず、登録したEloquentクラスのインスタンスが渡される
    {
        $postOrComment->user->talked_at = $postOrComment->created_at;
        $postOrComment->user->save();
    }

    public function updated(Eloquent $postOrComment) // 宣言した型に依らず、登録したEloquentクラスのインスタンスが渡される
    {
        $postOrComment->user->talked_at = $postOrComment->updated_at;
        $postOrComment->user->save();
    }
}

 他にもログ取り、検索テーブル用の同期、削除や復元の連鎖など使うと楽になる場面がいくらかあります。また単にモデルのコードが膨れ上がらないための分割としても使えます。

著者:杉浦

【Laravel】6.1.0リリース

 Release v6.1.0 · laravel/framework
 Eager Lazy Collections Added to Laravel 6.1 – Laravel News
 Laravel v6.1.0がリリースされました。Laravelはバージョン6からセマンティックバージョニングを採用しているため、v6.0.xからv6.1.0への移行時にソースコードを修正する必要はありません。composer updateのみでOKです。
 私的に最もありがたいのは

Fixed migration class duplicate check in make:migration command (#30095)

 です。何が起きるかというと、次のようにmake:migrationによるマイグレーションファイル生成時、migrateコマンド実行時にエラーの起きるファイルの生成をしようとしていた場合、それを止めてくれます。

>php artisan make:migration modify_column_in_users_table
Created Migration: 2019_10_03_173420_modify_column_in_users_table

>php artisan make:migration modify_column_in_users_table

   InvalidArgumentException  : A ModifyColumnInUsersTable class already exists.

 今まではファイル名の接頭辞にある時刻の違いによって同名クラスを持つファイルを生成できていましたが、未然に防がれるようになりました。これによっていざマイグレーションしようという際に失敗する場合が減ります。特に開発者が一人の時は滅多に起きなくなるでしょう。
 以下Laravel Newsで取り上げらているEager Lazy Collectionsについての簡単な解説です。

Added Illuminate\Support\LazyCollection::eager() method (#29832)

 LazyCollectionにeager()メソッドが増えました。LazyCollectionはLaravel6からの新機能です。これは必要な分だけデータを持つCollectionとでもいうべきものです。LazyCollectionはCollectionより処理が遅くなりがちですが、とにかくメモリを食いません。DB(データベース)に保存されているPHPに許可されているメモリサイズを優に超えるデータ全体を対象にSQLを意識せずなんやかんやすることもできます。
 eagerメソッドはLazyCollectionのままCollection的なふるまいをする速度優先用メソッドです。LazyCollectionのプロパティに対象全体を保持することによってCollection同様の速度を実現します。対象全体を保持するのでメモリエラーの危険が顕在化しますが、LazyCollectionインスタンスをCollectionインスタンスに移す必要がなくなります。

$users = User::cursor();

$users = $users->eager();// ここで$users内部に保持

$count = $users->count();// $users内部の参照で終わるから高速

$users->each(function ($user) { // $users内部の参照で終わるから高速
    //
});

 特別何もしなくても恩恵にあずかれる変更にSeeder実行時のCLIの詳細化があります。

Added runtime information output for seeders (#30086)

 Laravel6.1.0からはdb:seedを実行した時、次のように各Seederの実行時間が表示されます。

Seeding: UserSeeder
Seeded:  UserSeeder (1.56 seconds)
著者:杉浦

【Go】Go言語ことはじめ

Goは、シンプルで信頼性の高い効率的なソフトウェアを簡単に構築できるオープンソースのプログラミング言語です。
The Go Programming Language

 Go言語はC言語の様なコンパイルの必要な静的言語でとても高速で軽量な動作ができます。Go言語は記述の簡易さとパフォーマンスの両面を上げることを目的の一つとして作成されました。
 とりあえずコーディングを試すなら公式にオンラインエディタとそれ用の実行環境が置いてあります。
 
 ローカルにおけるGoの実行環境の作成は公式サイトに行って実行ファイルをダウンロードするだけです。windowsならインストーラを起動すればPATHも通してくれます。

 手軽に手元で試すエディタにはVSCodeがおすすめです。無料ですしGoでググって上位に出てくるプラグインを入れればそれなりに機能がそろいます。
 ソースコードの置き場にいきなり気にするようなルールはありませんでした。コンパイル後の実行ファイルがソースコードの配置場所でなくgoコマンドを実行したカレントディレクトリ直下にできることには注意です。
 コードは*.goであれば動作します。例えば次です。

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello, World")
}

 とりあえずすぐに使うコマンドはgo run [ファイル名], go buildの二つでOKです。go runは指定したファイルを元にファイルをコンパイル、実行します。go buildは自動で対象を探してコンパイルをします。go buildはpackage名と同名の関数がないと動きません。go runはファイルの中にmain関数がないと動きません。package mainのmain関数を用意するのがよいです。go runするかコンパイルされた実行ファイルを実行するとHello, Worldが表示されます。

 Goの公式は何かと充実しています。Pythonを思い出す豊富な組み込みパッケージについても次のリンクに実例付きで網羅されています。
Packages – The Go Programming Language
 他にもGoコマンドに始まり、エディタ、ソースコードの書き方にまで言及してきます。公式の各ページにはサムネにあるGo言語マスコットであるgopherくんが至る所に変わった形でいて飽きさせません。

著者:杉浦

【Laravel】ログファイルのPermission deniedを恒久的に防ぐ

 Laravelに限った話でないですが、あるプログラムを異なるユーザが実行し同じ宛先のログファイルに追記をしようとする場合、書き込み権限を持っておらずPermission deniedで弾かれることがあります。この記事ではLaravelにおけるこのエラーの防ぎ方を書きます。
 前提としてsudo php artisan hogehoge(root以外が触れられるべきでないローカル領域に触れる処理とか)とwebサーバによるpublic/index.php(webページの表示)の二つが走るものとします。この前提の鬼門はrootユーザ製ログファイルにapache等のwebサーバがログを追記しようとする場合です。デフォルトでは次のようなファイルができあがり、webサーバに書き込み権限がないままwebサーバがログを書こうとしてこけます。

-rw-r--r-- 1 root    root          1368 10月  1 17:13 laravel-2019-10-01.log

 いくつか試した対応策で私的に最も有効なのは次です。

// config/logging.php
<?php

use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;

// whoamiコマンドを実行して現在のPHPプロセスの所有者を得る
// whoamiはwhoamiを実行したユーザの有効なuserid(ユーザ名)を表示するコマンド
$__current_process_user = trim(shell_exec('whoami'));
return [

/// 省略 ///

    'channels' => [
        /// 省略 ///
        'daily' => [
            'driver' => 'daily',
            // whoamiから得た名前別にログファイルを分ける
            'path' => storage_path('logs/laravel-'.$__current_process_user.'.log'),
            'level' => 'debug',
        ],
        /// 省略 ///
    ],
];

 コードのコメントにある通りwhoamiコマンドからプログラムの実行ユーザを得て、名前別にログファイルを分けるやり方です。
 これを用いると次のようにログファイルが分割され、権限による問題が起きなくなります。

-rw-rw-r-- 1 apache   apache     234 10月  1 17:25 laravel-apache-2019-10-01.log
-rw-rw-r-- 1 cplab    cplab     1332 10月  1 17:25 laravel-cplab-2019-10-01.log
-rw-rw-r-- 1 root     root      1368 10月  1 17:13 laravel-root-2019-10-01.log

 ログを時系列順に見たいならcat, sortコマンドがいくらか役に立ちます。

cat laravel-*-2019-10-01.log | sort

とすれば、Laravelログ共通の[2019-10-01 17:19:00] local.DEBUG: を用いて各行が時系列順に結合された結果を見れます。いくらか、というのはスタックトレース等の複数行に渡るメッセージが壊れるからです。残念ながらこれの対策は個別にファイルを見るぐらいしか見つけられていません。

 以下の二つは余談的な他のPermission denied対策です。状況によっては有効かもしれません。
 一つ目はログファイルのデフォルトアクセス権限を変えるやり方です。

// config/logging.php
        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'permission' => 0666,// 権限が設定できる
        ],

 デフォルトのアクセス権限を誰でも読み書き可にすることでroot:rootなログファイルがあってもwebサーバから追記できます。誰でも既存のログを破壊できる点が気になって没にしました。
 二つ目はPHP組み込み関数get_current_userを使うやり方です。

// config/logging.php
        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel-'.get_current_user().'.log'),
            'level' => 'debug',
        ],

 一見shell_exec(‘whoami’)と同様の結果を得られそうな関数名ですが異なる動作をします。get_current_userは現在のPHPスクリプトの所有者の名前を返します。どういうことかというと

-rw-r--r--    1 cplab     cplab       1686  9月 19 12:02 artisan

というファイルがあって

sudo php artisan hoge

とするとget_current_userはcplabを返します。しかしプロセス所有者はrootなので生成されるログファイルの所有者はrootです。artisanとwebページを分ける意味では適していますが実行ユーザ基準でログを分離するwhoamiの方がより安全と考えて没になりました。