ログ日記

作業ログと日記とメモ

Emacsの使い方を変えようとして無理だった

Emacsのウィンドウまたはフレームごとにバッファリストを分けたい。
色々調べたけれど無理そう。


stackoverflow.com
ここの3番目の答えにあるように、自分の使い方としては昔から

emacs &

で別々のEmacsを立ち上げていた。



例えばユーザーのマイページを作る場合、

emacs src/Page/MypageController.php &
emacs template/mypate.blade.php &
emacs css/mypage.css &

のように複数立ち上げて横に並べたい。
cssを編集していて、これは共通だなと思ったら 「C-x C-f common.css」としてcssのウィンドウで別cssのバッファを開く。
htmlを書いていて、これは共通メニューだなと思ったら「C-x C-f menu.blade.php」でhtmlのウィンドウで別のhtmlバッファを開く。
共通cssからmypage.cssに戻るときは、「C-x b RET」で戻る。


この何も考えずに機械的に戻る作業が、横分割のウィンドウだと出来ないんだよなあ…。
メニューに対応する menu.css を開いて3ファイル以上になった場合でも、バッファ一覧を見ずに「C-x b my TAB RET」で切り替える。
一覧から選ぶという作業をせずに、それでいて頭を使わずに補完したい。



ido, elscreen, elscreen-separate-buffer-list を入れると近いところまで行ったんだけど、なんか微妙に違う。
分割された一つのウィンドウの中でタブ切り替えしたいけど、タブはフレームで共通っぽい。ウィンドウ分割とタブ分割を両方使うと変な動きになる。
desktop-save-modeとの同時使用も動きが怪しい。


qiita.com
こんな感じでタブを消して、新タブ=新フレームみたいにしてウィンドウは一つでやれば良いのかもしれないが…うーん。


persp-modeも試したけどなんか動きが変。
spacemacsも試したら変わりすぎた。



detail.chiebukuro.yahoo.co.jp
動きがおかしい部分はこんなふうにサクッと修正できればいいんだけど…。

Closure Compiler の出力するJavaScriptを ES5(旧)とES6(新)で比べる

ふと、ES6よりES5で書いた方が軽くなるかと思ったので疑問を解消するために確かめる。
※ ES5、ES6と書きつつ、単に書き方の違いの調査になってきたので新・旧とする。



Closure Compiler と Closure Libraryをダウンロードする。

wget https://dl.google.com/closure-compiler/compiler-latest.zip

unar compiler-latest.zip
ln -s compiler-latest/closure-compiler-v20190325.jar closure-compiler.jar


https://github.com/google/closure-library/archive/v20190325.tar.gz
unar v20190325.tar.gz
ln -s closure-library-20190325 closure-library 

プログラムのディレクトリを作る。

mkdir js
mkdir js/es5
mkdir js/es6

Closure Compiler は何度も実行するのでコマンドを書いておく。
build.sh

#!/bin/bash


set -eux

function compile(){
    java -jar compiler.jar \
         --compilation_level=ADVANCED_OPTIMIZATIONS \
         --define 'goog.LOCALE=ja' \
         --define 'goog.DEBUG=false' \
         --create_source_map $1.js.map \
         --dependency_mode=STRICT \
         --entry_point=goog:entrypoint \
         'closure-library/closure/**.js' \
        '!closure-library/closure/**test.js' \
         "js/$1/**.js" \
         > $1.js

    echo >> $1.js
    echo "//@ sourceMappingURL=$1.js.map" >> $1.js
}

compile $1

まずは簡単なHello Worldから。

goog.provide('entrypoint');

function hello(v){
  console.log('hello ' + v);
}
function startApp(){
  hello('es5');
}

startApp();

goog.module('entrypoint');

const hello = (v) => console.log('hello ' + v);
const startApp = () => {
  hello('es6');
}

startApp();

まあこれぐらいなら基本なので全て削除されるかなと予想しつつコンパイル

chmld +x build.sh

./build.sh es5
./build.sh es6


結果。
app.js

console.log("hello es5");

console.log("hello es6");

ファイルを分けてみる。

// app.js
goog.provide('entrypoint');
goog.require('app.Foo');

window['startApp'] = app.Foo.startApp;

// foo.js
goog.provide('app.Foo');

app.Foo.hello = function(v){
  console.log('hello ' + v);
}
app.Foo.startApp = function(){
  app.Foo.hello('es5');
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');
window['startApp'] = app.startApp;

// foo.js
goog.module('app');

const hello = (v) => console.log('hello ' + v);
const startApp = () => {
  hello('es6');
}

exports = {
  startApp
};


結果

window.startApp=function(){console.log("hello es5")};

window.startApp=function(){console.log("hello es6")};

どちらも無駄がない。



HTMLを作って呼び出す形にする。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Closure Test</title>

    <script src="es5.js"></script>
<!--    <script src="es6.js"></script> -->
  </head>
  <body>
    <h1>Closure Test</h1>
    <div id="contents">
      <div class="list"></div>
      <div class="list"></div>
      <div class="list"></div>
    </div>

    <script>
      startApp5();
      //startApp6();
    </script>
  </body>
</html>


// app.js
goog.provide('entrypoint');

goog.require('app.Foo');

window['startApp5'] = app.Foo.startApp;

// foo.js
goog.provide('app.Foo');
goog.provide('app.Bar');

goog.require('goog.dom');

app.Foo.hello = function(v){
  console.log('hello ' + v);
  
  var parentEl = goog.dom.getElement('contents');
  var children = goog.dom.getElementsByClass('list', parentEl);
  for (var i = 0; i < children.length; i++){
    goog.dom.setTextContent(children[i], 'list ' + i);
  }
}
app.Foo.startApp = function(){
  app.Foo.hello('es5');
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');
window['startApp6'] = app.startApp;

// foo.js
goog.module('app');

const dom = goog.require('goog.dom');

const hello = (v) => {
  console.log('hello ' + v);

  const parentEl = dom.getElement('contents');
  const children = dom.getElementsByClass('list', parentEl);
  for (let i = 0; i < children.length; i++){
    dom.setTextContent(children[i], 'list ' + i);
  }
};
const startApp = () => hello('es6');


exports = {
  startApp
};


結果

var h = Array.prototype.indexOf ? function(a, b) {
    return Array.prototype.indexOf.call(a, b, void 0)
}
: function(a, b) {
    if ("string" == typeof a)
        return "string" == typeof b && 1 == b.length ? a.indexOf(b, 0) : -1;
    for (var c = 0; c < a.length; c++)
        if (c in a && a[c] === b)
            return c;
    return -1
}
;
window.startApp5 = function() {
    console.log("hello es5");
    var a, b = (a = document.getElementById("contents5")) || document;
    if (b.querySelectorAll && b.querySelector)
        a = b.querySelectorAll(".list");
    else {
        var c;
        b = document;
        a = a || b;
        if (a.querySelectorAll && a.querySelector)
            a = a.querySelectorAll(".list");
        else if (a.getElementsByClassName) {
            var d = a.getElementsByClassName("list");
            a = d
        } else {
            d = a.getElementsByTagName("*");
            var e = {};
            for (b = c = 0; a = d[b]; b++) {
                var g = a.className, f;
                if (f = "function" == typeof g.split)
                    f = 0 <= h(g.split(/\s+/), "list");
                f && (e[c++] = a)
            }
            e.length = c;
            a = e
        }
    }
    for (b = 0; b < a.length; b++)
        a[b].innerHTML = "list " + b
}
;

これもどちらも同じなので片方は省略。

goog.domを使ったのでindexOfやquerySelectorが無い場合の処理が入った。
querySelectorがある場合の処理だけ抜き出すと

window.startApp6 = function() {
    console.log("hello es6");
    var a, b = (a = document.getElementById("contents6")) || document;
    a = b.querySelectorAll(".list");
    }
    for (b = 0; b < a.length; b++)
        a[b].innerHTML = "list " + b
};

こうなる。
変数が再利用されていてvarとかletとかのレベルではなかった。




次にクラス。

// app.js
goog.provide('entrypoint');

goog.require('app.Bar');
goog.require('app.Baz');

var bar = new app.Bar('test');
bar.hello();
var baz = new app.Baz('test 2');
baz.hello();

// foo.js
goog.provide('app.Foo');
goog.provide('app.Bar');
goog.provide('app.Baz');

/**
 * @constructor
 */
app.Foo = function(value){
  this.value_ = value;
};
app.Foo.prototype.hello = function(){
  console.log('Foo > ' + this.value_);
};


/**
 * @constructor
 * @extends {app.Foo}
 */
app.Bar = function(value){
  app.Bar.base(this, 'constructor', value);
};
goog.inherits(app.Bar, app.Foo);

app.Foo.prototype.hello = function(){
  console.log('Bar > ' + this.value_);
};

/**
 * @constructor
 * @extends {app.Bar}
 */
app.Baz = function(value){
  app.Baz.base(this, 'constructor', value);
};
goog.inherits(app.Baz, app.Bar);

app.Foo.prototype.hello = function(){
  console.log('Baz > ' + this.value_);
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');

const bar = new app.Bar('test');
bar.hello();
const baz = new app.Baz('test 2');
baz.hello();

// foo.js
goog.module('app');

class Foo {
  constructor(value){
    this.value_ = value;
  }

  hello(){
    console.log('Foo > ' + this.value_);
  }
}

class Bar extends Foo {
  constructor(value){
    super(value);
  }

  hello(){
    console.log('Bar > ' + this.value_);
  }
}

class Baz extends Bar {
  constructor(value){
    super(value);
  }

  hello(){
    console.log('Baz > ' + this.value_);
  }
}


exports = {
  Foo,
  Bar,
  Baz
}


結果

function c(a, d) {
    function g() {}
    g.prototype = d.prototype;
    a.f = d.prototype;
    a.prototype = new g;
    a.prototype.constructor = a;
    a.c = function(l, m, n) {
        for (var h = Array(arguments.length - 2), b = 2; b < arguments.length; b++)
            h[b - 2] = arguments[b];
        return d.prototype[m].apply(l, h)
    }
}
;function e(a) {
    this.a = a
}
e.prototype.b = function() {
    console.log("Foo > " + this.a)
}
;
function f(a) {
    this.a = a
}
c(f, e);
e.prototype.b = function() {
    console.log("Bar > " + this.a)
}
;
function k(a) {
    this.a = a
}
c(k, f);
e.prototype.b = function() {
    console.log("Baz > " + this.a)
}
;
(new f("test")).b();
(new k("test 2")).b();

var d = "function" == typeof Object.create ? Object.create : function(a) {
    function b() {}
    b.prototype = a;
    return new b
}
, e;
if ("function" == typeof Object.setPrototypeOf)
    e = Object.setPrototypeOf;
else {
    var f;
    a: {
        var g = {
            c: !0
        }
          , h = {};
        try {
            h.__proto__ = g;
            f = h.c;
            break a
        } catch (a) {}
        f = !1
    }
    e = f ? function(a, b) {
        a.__proto__ = b;
        if (a.__proto__ !== b)
            throw new TypeError(a + " is not extensible");
        return a
    }
    : null
}
var k = e;
function l(a, b) {
    a.prototype = d(b.prototype);
    a.prototype.constructor = a;
    if (k)
        k(a, b);
    else
        for (var c in b)
            if ("prototype" != c)
                if (Object.defineProperties) {
                    var m = Object.getOwnPropertyDescriptor(b, c);
                    m && Object.defineProperty(a, c, m)
                } else
                    a[c] = b[c];
    a.f = b.prototype
}
;function n(a) {
    this.a = a
}
n.prototype.b = function() {
    console.log("Foo > " + this.a)
}
;
function p(a) {
    this.a = a
}
l(p, n);
p.prototype.b = function() {
    console.log("Bar > " + this.a)
}
;
function q(a) {
    this.a = a
}
l(q, p);
q.prototype.b = function() {
    console.log("Baz > " + this.a)
}
;
(new p("test")).b();
(new q("test 2")).b();

class構文の方はトランスパイル後の共通コードが若干複雑になっているような。
ただし本体のコードはやはり同じだった。





その他、細かい動作等。
よくあるスコープミスのコード

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  for (var i = 0; i < a.length; i++){
    b.push(function(){
      console.log(i);
    });
  }
  for (var j = 0; j < b.length; j++){
    b[j]();
  }
}
app();
// compile
(function() {
    for (var a = [1, 2, 3, 5, 8], b = [], c = 0; c < a.length; c++)
        b.push(function() {
            console.log(c)
        });
    for (a = 0; a < b.length; a++)
        b[a]()
}
)();

単純なコードのままだけれどバグっている。
letを使う

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  for (let i = 0; i < a.length; i++){
    b.push(function(){
      console.log(i);
    });
  }
  for (let i = 0; i < b.length; i++){
    b[i]();
  }
}
app();
// compile
(function() {
    for (var a = [1, 2, 3, 5, 8], c = [], b = {
        a: 0
    }; b.a < a.length; b = {
        a: b.a
    },
    b.a++)
        c.push(function(d) {
            return function() {
                console.log(d.a)
            }
        }(b));
    for (a = 0; a < c.length; a++)
        c[a]()
}
)();

forEachを使うと安全。

goog.require('goog.array');

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  goog.array.forEach(a, function(a, i){
    b.push(function(){
      console.log(i);
    });
  });
  for (var j = 0; j < b.length; j++){
    b[j]();
  }
}
app();
// compile
var f = Array.prototype.forEach ? function(a, b) {
    Array.prototype.forEach.call(a, b, void 0)
}
: function(a, b) {
    for (var e = a.length, d = "string" == typeof a ? a.split("") : a, c = 0; c < e; c++)
        c in d && b.call(void 0, d[c], c, a)
}
;
(function() {
    var a = [];
    f([1, 2, 3, 5, 8], function(e, d) {
        a.push(function() {
            console.log(d)
        })
    });
    for (var b = 0; b < a.length; b++)
        a[b]()
}
)();

ただし素のJSで使おうとすると危険。

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  a.forEach(function(a, i){
    b.push(function(){
      console.log(i);
    });
  });
  for (let i = 0; i < b.length; i++){
    b[i]();
  }
}
app();
// compile
(function() {
    var a = [];
    [1, 2, 3, 5, 8].forEach(function(d, c) {
        a.push(function() {
            console.log(c)
        })
    });
    for (var b = 0; b < a.length; b++)
        a[b]()
}
)();

[].forEach を直接書くと、それはトランスパイル後もそのままになっている。

a.forEach(function(a, i){

の箇所を

a.myforEach(function(a, i){

にするとコンパイルエラーになるから判別は出来ているんだけど。
直接arrayの関数を呼び出す場合はブラウザ対応を開発者に任せるということかな。




クラスやthisが入ると長くなるのかなと思いきや、そうでもない。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
console.log(foo.getValue());
// compile
console.log("prefix_val_suffix");

あれ?クラスは?
驚愕の圧縮率。



何度も使うと結果が変わる。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
console.log(foo.getValue());

const foo2 = new Foo('val 2');
console.log(foo2.getValue());

const foo3 = new Foo('val 3');
console.log(foo3.getValue());
// compile
function a(b) {
    this.a = b
}
console.log("prefix_" + (new a("val")).a + "_suffix");
console.log("prefix_" + (new a("val 2")).a + "_suffix");
console.log("prefix_" + (new a("val 3")).a + "_suffix");

それでも動的な部分以外は定数になっている。


setValueを呼び出しても結果が変わる。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
foo.setValue('val2');
console.log(foo.getValue());
// compile
var a = new function() {
    this.a = "val"
}
;
a.a = "val2";
console.log("prefix_" + a.a + "_suffix");

なんかこのコンパイル結果を再びコンパイルすると綺麗に消せそうだけれども。
一度しか利用されていない変数やメソッドはインライン展開されるということかな。



クラス定数はどうか。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    const v2 = plusSuffix(v1);
    const v3 = this.plusPrefix(v2);
    return Foo.plusSuffix(v3);
  }

  plusPrefix(v) { return this.CLASS_PREFIX + v; }
  get CLASS_PREFIX() { return 'classprefix_'; }

  static plusSuffix(v) { return v + Foo.CLASS_SUFFIX; };
  static get CLASS_SUFFIX() { return '_classsuffix'; }

}

const foo = new Foo('val');
console.log(foo.getValue());
// compile
var a = "undefined" != typeof window && window === this ? this : "undefined" != typeof global && null != global ? global : this;
function b(d) {
    this.b = d
}
a.Object.defineProperties(b.prototype, {
    a: {
        configurable: !0,
        enumerable: !0,
        get: function() {
            return "classprefix_"
        }
    }
});
a.Object.defineProperties(b, {
    a: {
        configurable: !0,
        enumerable: !0,
        get: function() {
            return "_classsuffix"
        }
    }
});
var c = new b("val");
console.log(c.a + ("prefix_" + c.b + "_suffix") + b.a);

なんか増えた。
素直にクラス外で定数にした方が良さそう。



効率良いトランスパイル結果を得ようと思ったら、 goog.module でモジュールにしつつthisを使わないシンプルな関数や定数の組み合わせでコードを書くのが良さそうだ。
まあそもそもthisを使うためにnewするのだから、thisを使わないのにクラスメソッドにするのは無駄な設計とも言えるね。

Kensington Expert Mouse を分解して断線を修理した

ExpertMouseが切れたりついたりして、仮想マシンの「どちらに接続しますなか?」が何度も出るようになったので直すことにする。
断線?というか接触不良的なやつ。
どうもコードがマウスに収納されていく部分が怪しくて、そこを持ってぐねぐね動かすとランプが激しく点滅する。

裏側4隅のネジを外す。
f:id:n314:20190331001438j:plain

ボールの下のネジを二箇所外すと取れる。
f:id:n314:20190331001542j:plain
かなり埃が溜まってたので左右の6箇所のネジを外して全部取って掃除した。


コードをうねうね動かした感じでは最初の想定通りの場所だったので、付近を切断。
f:id:n314:20190331001618j:plain
中に5本のコードがあるので全て接続。
なんか銅線?の他に繊維っぽい束もある。シールド的なこと?
また切れたら嫌なのではんだでがっつり接着した。
集中してたら写真撮るの忘れた。この写真は一通り終わってテープ巻いた後の状態。


カバー?的な入り口にハマってるやつは抜けなかったので諦めて別の場所に切れ込みを入れた。
適当に切ったけど丁度良いサイズで固定できた。

コードを引っ張っても点滅しなくなったので取り敢えず一安心。

フラッシュセッションにハマる

前にも同じ問題でハマったのに忘れていて再びハマったメモ。


リダイレクト前にセッションに値を保存して、リダイレクト後のページで取り出して、削除するような一度だけ使うセッション変数にはフラッシュセッションを使う。
セッション系ライブラリには大抵 flash メソッドがあると思う。
リロードにや更に次のページにも対応するために keep メソッドなんかも用意されている。

で、何故か値が消えていることがある。


詳しく処理を追うと、フロントコントローラーでファイルが存在しない場合は全てPHPが起動するようになっていて、not foundでもPHPが実行される。
このときログインメニューやメッセージ用にnot foundページにも関わらずセッションを使っていたりすると、ここでフラッシュセッションがリセットされる。


画面遷移のどこかで画像などがnot foundになった場合に、一見正常フローのようでも「画像呼び出し=>ファイルがない=>phpでnot found用処理」となって見えない画面遷移とフラッシュセッションのリセットが動いていることになる。


よく考えれば当然のことなんだけれど、スマートな解決方法が思い付かない。
とりあえず404や500のときはkeepする、あるいはフラッシュセッションを消す処理を実行しない、というようなちょっとややこしいプログラムを書いた。

macOS Mojave にアップグレードしたらVMware Fusionがプチフリするようになった

High Sierra の時も問題があったので、今回も何か挙動が変になることがあるだろうなーと思っていたらやっぱり問題が起きた。


ブラウザの新規ウィンドウを立ち上げたり画面遷移しただけで5〜10秒ほどフリーズするようになった。



VMware Fusion 設定の詳細の「ハードウェアバッファリング」を自動から有効に変更したら直った。今のところは。
「自動」の挙動が変わったのか、以前は無効でも問題なかったのかは不明。


あとごくまれにマウスカーソルが一瞬巨大化する問題は残ってる。

スタートアップがPHPを使っていると聞いた話

この前、外国のスタートアップをやっている人にサービスを見せてもらった。
Qiitaによく書かれているような新しめのフロントエンドの技術盛り沢山で、クラウドや聞いたことのないようなサービスからTrelloのような知っているサービスなど色々使いつつ、なんかセンサー集計っぽい感じのWebが今風の画面でデザインされていた。他にもいくつかプロジェクト?があった。
VPNで海外の拠点に繋いで、遅いなーとか言いつつ。日本語は片言だったのであまり詳しくは聞けてないんだけど。
そういやブロックチェーンもやっていると言っていた。
よくある日本の零細企業ベンチャーじゃなくてガチのスタートアップや…とか思いつつ。

それで位置情報を使うからPostgreSQLを使っているだとか聞いて、あ、ネットで見た話だ、とかも思いつつ。


サーバーサイドは何?って話になって、PHPSymfonyだって聞いてびっくりした。
あれ?人気ないの日本だけ?
PHPは日本で?あまり人気ない感じなんですよねーとか言って、そうなの?なんでだろうね?なんて。
いや、そりゃーWordPressがあるから使われてる数で言ったら多いんだろうけど、新しいことやってる人にPHPは選択肢に上がってこないイメージだった。


まあ実際は驚きつつも、WebならPHPが一番手っ取り早いし、LaravelとかじゃなくてもSymfonyで十分というか、DBに近い部分で変に新しいものを選んだらメリットよりデメリットの方が大きいとか、PostGIS使うなら主流のORMの恩恵あまり無いよねとか、ロジックはどんどんフロントエンドに行くからPHPで複雑なプログラミングはしないよねとか、色々思いつつ。

そんな話を聞いたあとに
https://qiita.com/dossari-book-archive/items/ad4f7bcaaebca6281154 こことか
https://qiita.com/charmston/items/df31a419a4e57ebe86ba こことか読んで、ちょっともやもやした。


ただPHPは言語自体を勉強するという感じのものではないのは確かだし、たぶんそのスタートアップの人もPHPでこんな面白いプログラムを作った!とは言わないと思う。知らんけど。


自分も毎日PHP書いてるけど、特に記事に書くようなこともなく。
PHPで関数の末尾呼び出しを最適化する - Qiita 書いてもネタみたいになっちゃう。
本当はネタじゃなくて真面目にHTML解析タイプのテンプレートエンジンを作りたいんだけども。今は使いどころがないんよね。10ページ程度のサイトをいくつも作るような会社に居るなら完成まで作ったのかもしれない。

あ、DIコンテナ https://github.com/nishimura/simple-container とか https://github.com/nishimura/db-migration DBバージョン管理ツールは最近作ったんだった。作ったというか、作っていたものを小さく整理した。
最近はとにかくシンプルにしようと思っていて、たかだかコンテナやDBツールで何ファイルも何千行もあるライブラリを読み込むのは無駄じゃん?と思って必要な機能だけ切り出した感じ。なので特に書くことが無い。


そういうわけで、PHPは新しい環境でも普通に使われているけど使われ方が普通すぎて特にアピールすることもないので不人気に見えるのでは?とか思った。
ぐだぐだ書いておいてn=1だけどね。


最近のPHPは、スカラー型からnullableな型、そしてついにTyped Properties とか、かなり大きな変更の流れですごいことだと思ってるんだけど、便利な新機能!ってことじゃなくてじわじわくる感じだからなあ。
LaravelでURLルーティングからの引数に直接DB Entityを差し込むやつ、あれのプロパティ版がはかどるんじゃない?と思ってるんだけど。
そのDB Entityも型があったらDBのnullableをそのままマッピングできて、JavaっぽいのにJavaよりすごくない?とか。


オレオレフレームワークばかりだった頃からLaravelとかCakePHPとかSymfonyとかに統一されてきて(それでも数が多いけど)、何かPHPの記事を見かけてもライブラリの使い方の説明だけなのが寂しい感じはある。

うーん、なんだかまとまらないままポスト。

さくらクラウドでsystemd-nspawnを使う場合のネットワーク設定

IPが一つの場合

選ぶ余地もなく --network-veth のプライベートネットワーク。
/etc/systemd/nspawn/guest1.nspawn などのファイルに

[Network]
Port=80:80

と書くと元々あったIPマスカレードに加えてポートフォワードが設定されるので、ポート競合が起きない状況だと便利。
ゲストの自動的に設定されたIPにフォワードしてくれる。

ルータ+スイッチ配下で複数IPを使う場合

https://n314.hatenablog.com/entry/2018/09/29/134220
ここの続き。


一つのMACアドレスに対して複数のIPを割り当てる必要があるのが何とも悩みどころ。
ホストOSにIPを複数割り当てつつ上で書いたポート転送で

[Network]
Port=172.17.0.3:80:80

docker-composeのように書ければそれが一番楽だったんだけど、これは無理だった。


取り敢えず以下の2パターンにするしかなさそう。

ネットワーク共有

ホストOSのネットワークを共有する。
IPをホストOSで複数割り当てつつ、ゲストはその中の一つを使う。

ネットワーク絡みの問題が何も起きないが、サーバー絡みの問題が起きる。
大抵の設定ファイルには

Listen *
ListenAddress 0.0.0.0

のようになっているので、これをゲストOSのIP割り当てを考慮して変更していかないといけない。
ip a でIP一覧を出すとゲストOSの分全て表示されるので数が増えるとややこしい。

あと 127.0.0.1 も競合する。localhost を 127.0.0.2 とかに変えていけばいいんだろうけど、アプリによってはIP固定で書かれていたりしそう。

ipvlan

ipvlanでは --network-ipvlan=eth0 のように設定してMACアドレスを共有しつつ、新しいNICがゲストに作られる。
ホストにIPが割り当てられないし、ゲストにログインして ip a すると自分のIPしか表示されずブリッジっぽくなっている。
ただし普通のブリッジではないので ゲストとホストは通信できない模様。
systemd-nspawnで外部からssh可能でnvidia GPUを使えるコンテナを作る - Qiita
ゲスト同士は通信できる(なんで?)

ログサーバーをゲストに立ててるんだけど、そこにホストのログを送れない。
まあその一点以外はブリッジ接続と同じ感じで使えるので、ホストはただの箱として以外使わないようにして取り敢えずこれで試してる。

プライベートネットワーク+ルーティング

NATとかマスカレードとかフォワードを自分で設定。
いちいちゲストの設定変更時にホストのルーティング変更をするのは大変そう。なので試していない。
最後の手段。