がるの健忘録 このページをアンテナに追加 RSSフィード

2018-01-11

[]foreachとか使わないのかしらん?

定期的に見かけるんだけど、今日もふと見かけたので、割と本気で疑問なので一度書いておこうかなぁ、と。

おいちゃん的には「foreachでくるんだらほんの少しだけ楽ぢゃない?」って思うようなコードが割とコピペで書かれているのを散見するので、「なんか理由があるのか」「とくに理由がないのか」興味もありまして、的な。


ん……典型的にわかりやすいのは、例えば、以下。

$data['hoge'] = $awk['hoge'];
$data['foo'] = $awk['foo'];
$data['bar'] = $awk['bar'];

面倒だから3行しか書いてないけど、これが10行以上とか、わりとざらに拝見。

おいちゃんなら

$params = ['hoge', 'foo', 'bar'];
foreach($params as $p) {
    $data[$p] = $awk[$p];
}

って書くかなぁ。

各項目ごとにちゃんとコメントが入ってるんなら

$params = [
    'hoge', // コメント
    'foo', // コメント
    'bar', // コメント
];

こげな感じで。


例えば「入れる側にprefixとかsuffixとか入れたい」なんてケースもあると思うのよ。

foreach($params as $p) {
    $data[$p . $suffix] = $awk[$p];
}

で終了


「いやいやそうじゃなくて配列じゃなくて変数名として使いたい」ってケースも、時々。

コード的にはこげな感じ。

$hoge = $awk['hoge'];
$foo = $awk['foo'];
$bar = $awk['bar'];

これは

foreach($params as $p) {
    $$p = $awk[$p];
}

で片付く。$paramsの中身が「ちゃんと変数名としてvalidである必要」はあるんだけどね。


これらの利点って。

単純に「項目が増えたり減ったり」、あと「個々の要素になにかひと手間加えたりしたくなった」時に、地味に便利なんじゃないかなぁ、って思うの。

typoの可能性が「原理的に0になる」し、わずかとはいえ「手間が省ける」のは、その手法を選択する「デメリット」がないのであれば、そっちのほうが楽なんじゃないかなぁ? と。

あと、個人的には「先頭に"処理対象を宣言"しておく」のが、可読性の観点から、読みやすいんじゃないかなぁ、と。まぁ単純に「おいちゃん好み」って言い方にしてもよいのだけど。


なので、この辺やらずにえっちらおっちらコピペしてるコードをみると、「……なんでだろ?」って疑問に思うのですだよ。

ってなわけで、興味があったのでメモり。

「いや実はこーゆー理由があってね」的な話があったら、きっと、コメントに書いてくれる人がいると、おいちゃんは信じてるw

2017-11-08

[][]Model、どうすっかねぇ? 的な

直近思案しているのはLaravel5.5案件なのですが。

まぁ割と「あちこちのPHP MVCフレームワークで言える(ような気がする)」ので、あちこちに疑問を投げかける的な想定で。

端的には「データの入力やvalidateの処理、Modelに書きますか? Controllerに書きますか?」的な内容です。


さて。いわゆるMVCの「Model」ですが。

本来的には

アプリケーションデータ、ビジネスルール、ロジック:システムの本体

をつかさどる、とされています。

言い方を変えると「どのModelを使うかの制御(Controller)、表示や出力(View)」以外全部、をつかさどる、と言い伝えられています。


ただまぁ、どこのRoRが元凶とかActive Record以下検閲削除とかは言いませんが、半歩間違えると、Modelって

・ORマッパーのことでしょ?

的なお話になることも、少なからずあるように思われます。


いや別に「Model === ORM」だ、ってんならそれはそれでよいのですが。

その場合はまぁ「Controllerがfatになるのは避けられないよねぇ」とか思うわけです。

いやもちろん「一旦処理を外に書き出して"Controllerというファイル名のファイルの中身は細くする"」ってことも可能ですが、それって本当に「not Fat Controllerなの?」とか思うわけです。

「書き出したクラス」って、所属はController? Model?

「ControllerでもModelでもないそれ以外」だとしたら、それは本当にMVC? とかとか。


あぁ念のため「おいちゃん的には、トップハム・ハット卿 *1 を否定している」わけではありません。

いやまぁ肯定もとくにせんのですが。

その辺はぶっちゃけると「どっちでもいいんじゃないかなぁ一貫性があれば」。


正直、ある程度の規模のシステムを組めば

・太った子が出てくるか

・やたら大量の子だくさんになるか

のどちらかはどちらにしても不可避なので。ゆえに「ある程度納得できる理由と芯の通った哲学」があればよいのではないかなぁ、と思うのですます。


んで。「どっちでもよい」ので、一旦、仮定として「例えば、トップハム・ハット卿を避ける方向」で考えた場合。

Modelですが。「ビジネスロジック全部まるっと」はちぃと難しそうな気がせんでもないのですが、「あるデータの塊に対する責務全般」くらいは、お願いしてもよいのではないかなぁ? とか、おいちゃん個人としては思うのでございます。

ほら「Tell, Don't Ask」とか言うじゃないですかまぁ一般的なModelのコードもそんな感じだとは思うのですが。とはいえ、もうちょっと「いろいろ、Modelに持ち込んでもよいのかなぁ」とか、なんとか。


で、ちょいとちょうど良い感じがあったので、さっそくの実例

ちと書いてもらったコードがあったので、微妙に細部を変えながら、転記。

    public function postTweet(Request $request)
    {
        $userId = auth()->id();
        if (null === $userId) {
            return redirect('/');
        }
        // ここはもうちょっと「$request->validate」つかうのが本当なんだろうと思う
        $tweetPost = $request->input('tweet');
        if (144 < strlen($tweetPost)) {
            return redirect('/');
        }

        $tweetModel = new Tweet();

        $tweetModel->user_id = $userId;
        $tweetModel->tweet = $tweetPost;
        $tweetModel->save();

        Session::flash('message', 'tweet した');
        return redirect('/');
    }

うんたぶん、LaravelでなくほかのPHP MVCフレームワークであっても、大体こんな感じだろうなぁと思うですサンプルコード見ている限り。

別の場所をグぐってみても。

http://libro.tuyano.com/index3?id=7896003&page=3

public function postNew(Request $request)
{
    $name = $request->input('name');
    $mail = $request->input('mail');
    $age = $request->input('age');
    $data = array(
        'name' => $name,
        'mail' => $mail,
        'age' => $age
    );
    MyTable::create($data);
    return redirect()->action('HeloController@getIndex');
}

https://qiita.com/yagi21/items/eea131ef0d3bc20be59a

  public function res(Request $request){
    //フォームから受け取る
    $フォームのname = $request->input('フォームのname');
      .
      .
      .
    //DB保存
  }

などなど。

公式も

https://readouble.com/laravel/5.5/ja/eloquent.html

class FlightController extends Controller
{
    /**
     * 新しいflightインスタンスの生成
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // リクエストのバリデート処理…

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

なので、Laravel公式的にも「処理はControllerに書く」のほうに倒れてるんだろうなぁ、とは思うです。

さて上述に共通するのは「Controllerにデータ取得とvalidateの処理が書いてある」あたり。


んで、おいちゃんが気になっているのが

・入力データの取得

・validate

の処理を「Controllerでやっている」あたり。

その辺「どうなんだろう??」と思うですだす。


で、一案っつか二案なんだが。

上述の処理を「model側に移す」のって、みんなの印象値的にどうなんだろう? という、質問ですます。

1つは「全部まとめて1メソッド」、もう1つは「入力、validate、保存の3メソッド」の提案。

まぁModel内部的にはどちらにしても切り分けるんだろうけど。


1つまとめの場合は身もふたもなくて

    public function postTweet(Request $request)
    {
        $tweetModel = new Tweet();
        $r = $tweetModel->insert用に入力してvalidateして保存();
        if (true === $r) {
            Session::flash('message', 'tweet した');
        }
        return redirect('/');
    }

3つの場合は

    public function postTweet(Request $request)
    {
        $tweetModel = new Tweet();
        $r = $tweetModel->insert用の入力();
        if (false === $r) {
            return redirect('/');
        }
        $r = $tweetModel->insert用のvalidate();
        if (false === $r) {
            return redirect('/');
        }
        $r = $tweetModel->insert保存();
        if (true === $r) {
            Session::flash('message', 'tweet した');
        }
        return redirect('/');
    }

なんかifがうざったいから、例外投げるほうが楽かもしんまい。

    public function postTweet(Request $request)
    {
        try {
            $tweetModel = new Tweet();
            $tweetModel->insert用の入力();
            $tweetModel->insert用のvalidate();
            $tweetModel->insert保存();
        } catch(Throwable $e) {
            return redirect('/');
        }

        Session::flash('message', 'tweet した');
        return redirect('/');
    }

こんな感じ。

細かい話をすると「insertとupdate」って、似てるけど処理が違うので。データ取り込みもvalidateも保存する時も、微妙に入り口を分けていたい感じではある。

ので、全体的に「insert用の」ってつけてるの感じ。


Modelのほうはまぁだいたいイメージがつくと思うので適宜オミット。

ちな、これの実装を愚直にやると「formのnameアトリビュート値が固定」になるんだけど。「いやまぁ固定でもいいじゃない」ってのと「可変にしたきゃ、デフォルト引数とかうまく使ってよ」とか、まぁ解決策はいくつか。

面倒なんで書かないけど、質問がきたら書くかも。


あと、細かいところでエラー処理。

まぁ例外投げる時は「例外のメッセージの中に、それこそjson文字列とかででも細かい情報突き返せばよくね?」とかアバウトにアバウトに。いやまぁModelインスタンスん中にエラー情報入れてgetってもいいだろうし。

戻り値でreturnであれば、別途「error_detail」とかってメソッド使って詳細吐き出せばよいと思うですます。


ってなわけで、これやると「Controllerでやることが減る」変わりに「Modelでやることが増える」んだよねぇ当たり前だよ動かしただけだもん。

ただ、Modelって「実のところ、なんなの?」って考えた時に。

もしModelが「ある程度のビジネス処理をつかさどるところ」なのであれば、一つの提案としては上述のようなコード「も」想定できるかなぁ、とか思うわけですます。


一方で「LaravelのController(の1メソッド)って、必ずしも"外部から呼ばれる"前提とは限らない」と思われるので(web.phpとかにルーティング書かなければ呼ばれないし)。

例えば「このControllerのこのメソッドは"このデータの塊を、入力受け取ってvalidateして保存する"用のメソッドなんだ!!」ってなるのであれば、それはそれで「そーゆー方向性と指針なんだなぁ」とも思うのです。「それって、ビジネスロジックって言わね?」とかって疑問もありますが、別にそれはそれで一貫性があれば。「Controllerにビジネスロジックを書いたら死ぬ」ってわけでもないだろうし。

いやまぁ結果的に「Controllerに書く内容が増えて、太りやすくなるよなぁ」とは思うのですが「それはそれでいいじゃん」であれば、それはそれでよいんじゃないかなぁ、とも思いますです。はい。


ってなわけで。

上述みたいなコードで「データに紐づく責務」をModel側に移すのって、世間的にはどーゆー反応なのかしらん? ってのが、おいちゃんの疑問。いや上述のような感じのコードって、見る見ないでいうと「ほとんど見かけない」ので。

個人的見解と感想と好みと雑感と偏見」ざぶざぶでよいので、コメントなどいただけると、おいちゃんが喜んだりすると思われますので、ぜひ ノ

*1: 「ふとっちょのきょくちょう」(Fat Controller)、って、説明しなきゃわからないギャグを書くな

2017-11-04

[]PHPで動的なSQLでプリペアドステートメントな一例(本題は、IN句でどうやってプリペアるか)

端的には「PHPで、動的にSQL文を組む必要があるときにどうやってプリペアドステートメントで組んでいくか」の一例と、それに合わせて「INをうまいことプリペアドステートメントで使いたい」時の一例を書いてみます。

いやなんか知られてるような知られてないような微妙なナレッジだったので「まぁ書いておけばいいや」的な、雑な発想ですw


毎度のお話な気もするのですが。

「ここクラックできるんぢゃね?」とかあったらコメント等にて突っ込みをいただければ、速やかに修正を入れます ノ


さて「PHPで、動的にSQL文を組む必要があるとき」って例えばどんな? ってのがあるのですが。

割と端的には「(複数の入力項目がある)検索系のフォーム」。

「なにか入力されていたらその項目を検索用に使う」「なにも入力されてなかったらその項目は検索用に使わない」ってのは、多分、業務的には「ちょいちょいあって」かつ「ある程度、動的に対応する必要があるんじゃないかなぁ」と思うです。

噛み砕くと「WHERE句あたりを動的にする必要があるよねぇ」的な。


一方では「動的にSQLを生成せざるを得ない明確な要件」があって。

他方で「動的なSQL生成はSQL=Injection的に危険」というお話があって。

その間を少しパテのように埋めてみましょう、ってのが、この記事の主題でございます。


さて前提として、例えば以下の3項目があると仮定しましょう。項目は超絶適当です*1

・name:名前

・age:年齢

・email:メアド

あと、やっぱり前提として、面倒なんで「検索formの名前も同じ」と仮定します。

検索は、おいちゃん的には「ブックマークできた方が楽だよねぇ」ってんでGETメソッドでやり取りする事が多いので、メソッドはGET前提。

& PHP7ね。null合体演算子が楽だからw。PHP5な方々は、適当に読み替えてちょ。


さて。上述をシンプルに実装するんであれば、まぁ、だいたいこんな感じのコードになります。

// WHERE句用作業領域
$where_wk = [];

// SQLのベース
$sql = 'SELECT * FROM 検索対象テーブル';

// WHERE句の積み込み
foreach($_GET as $k => $v) {
    if ('' !== $v) {
        $where_wk[] = "{$k} = '{$v}'";
    }
}
if ([] !== $where_wk) {
    $sql .= ' WHERE ' . implode(' AND ', $where_wk);
}

// SQL文の〆
$sql .= ';';

………さてこれで「OK」とか本気で思っている場合は以下検閲削除*2

上述のコードは「どの角度からどうみてもどんなレベルからも明らかに絶対に完全にダメ」でございます。


とりあえず最低限

・$_GETに与えられたkeyとか当たり前に使ってる

SQL文組み立てるのに「変数をダイレクトに入れている」

とか、昨今の事情的にはアウト感満載でございます。


んでは。

その辺を踏まえたうえで「たとえばこうやったら?」の一例を、簡単にコードで書いてみましょう。

一端あらかじめ、$dbhに「PDOの接続済インスタンス」が入ってるものとします。

// WHERE句用作業領域
$where_wk = [];
// ターゲット用のホワイトリスト
$target = ['name', 'age', 'email'];
// プレースホルダに充てるデータ用の作業配列
$placeholder_data = [];

// SQLのベース
$sql = 'SELECT * FROM 検索対象テーブル ';

// WHERE句の積み込み
foreach($target as $k) {
    if ('' !== ($_GET[$k] ?? '')) {
        // 「WHERE句の欠片」を配列にため込む
        $where_wk[] = "{$k} = :{$k}";
        // プレースホルダに当て込む値を保持しておく
        $placeholder_data[":{$k}"] = $_GET[$k];
    }
}
// 「WHERE句の欠片」からWHERE句を生成する
if ([] !== $where_wk) {
    $sql .= ' WHERE ' . implode(' AND ', $where_wk);
}

// SQL文の〆
$sql .= ';';

// 文の準備
$pre = $dbh->prepare($sql);
// プレースホルダに値を当て込む
foreach($placeholder_data as $k => $v) {
    $pre->bindValue($k, $v);
}

幾分おおざっぱですが、大体こんなところでしょうか。

個人的にはbindValueの第三引数は明示するほうが割と好みなので。$placeholder_dataにデータを入れるあたりで適宜、型を指定して入れつつ

// プレースホルダに値を当て込む
foreach($placeholder_data as $k => $v) {
    if ( (is_int($v))||(is_float($v)) ) {
        $type = PDO::PARAM_INT;
    } else {
        $type = PDO::PARAM_STR;
    }
    $pre->bindValue($k, $v, $type);
}

とかって書く方が好みですが、まぁこの辺はお好みで。

「わざわざ$placeholder_dataとかはさまなくてもいいじゃん」とか一瞬思いがちなのですが

SQLが組み立て終わらないとprepareが発行できない

ので、少し手間を踏んでます。


こんな風にすると、とりあえず

・「SQLを動的に組み立てる」ところで、外部入力を直接使う箇所はない(自分で用意したホワイトリストだけを使ってる)

ので。「自分に悪意がある or 思いっきり不注意」以外では、あまりSQL-Injectionが入り込む余地はないのではないかなぁ、と。

プリペアドが名前付きなのは「そのほうが解りやすくて好みだから」です。


さて。

ここを少し応用して「IN句がある場合」を書いてみましょう。

ちなみに、斜めにざっくりとググるといくつかで「エスケープしたうえで文字列ダイレクト連結」とか見かけたりしますが、個人的にはあまりお好まない感じかなぁ、と。

重ねますと「値が数値である前提」ではあるものの「エスケープすらせずに文字列ダイレクト連結」とかってのも以前に拝見をしましたが、まぁ正直「お作法的にどうなのよ?」とか思ったりしてみたものでございます。っつかおいちゃん的には「激しくお好まない」実装です。

せっかく「プリペアド前提」で書いてるんだから、それくらいは貫いてみたいものでございます。


「おいちゃんならこう書くかなぁ」的サンプルは、以下の通り。

多分「動的な検索+IN句」は少ないとは思うのですが、その辺はあえて「混ぜられるよ〜」ってニュアンス込みで、混ぜてみます。

「IN句を構成する」ために必要なデータはあらかじめ「どこかでゲトってる」前提。「副問い合わせ」だと、参考にならんでしょ?w

// WHERE句用作業領域
$where_wk = [];
// ターゲット用のホワイトリスト
$target = ['name', 'age', 'email'];
// プレースホルダに充てるデータ用の作業配列
$placeholder_data = [];
// IN句で使いたいデータ
$in_array = [1, 2, 3, 4, 5]; // XXX 本当は上の方の処理でデータが入ってるはず、前提

// SQLのベース
$sql = 'SELECT * FROM 検索対象テーブル ';

// WHERE句の積み込み
foreach($target as $k) {
    if ('' !== ($_GET[$k] ?? '')) {
        // 「WHERE句の欠片」を配列にため込む
        $where_wk[] = "{$k} = :{$k}";
        // プレースホルダに当て込む値を保持しておく
        $placeholder_data[":{$k}"] = $_GET[$k];
    }
}

// IN句の積み込み
$in_wk = [];
// $in_arrayの整形
$in_array = array_values($in_array);
//
foreach($in_array as $num => $in_datum) {
    // 先にプレースホルダ名を作っておく
    $key = ":hoge_{$num}";
    // プレースホルダ名を作業領域に積んでいく
    $in_wk[] = $key;
    // プレースホルダに当て込む値を保持しておく
    $placeholder_data[$key] = $in_datum;
}
// IN句の組み立て、或いは「WHERE句の欠片」への積み込み
$where_wk[] = 'hoge IN (' . implode(', ', $in_wk) . ')';

// 「WHERE句の欠片」からWHERE句を生成する
if ([] !== $where_wk) {
    $sql .= ' WHERE ' . implode(' AND ', $where_wk);
}

// SQL文の〆
$sql .= ';';

// 文の準備と値の当て込み
$pre = $dbh->prepare($sql);
// プレースホルダに値を当て込む
foreach($placeholder_data as $k => $v) {
    $pre->bindValue($k, $v);
}

変数名とか超雑だけど、大体、イメージとしてはこんな感じ。

これだと「外部由来の変数は埋め込んでない」ので安全性が担保しやすいし。?じゃなくて:nameにする事で「順番が〜」とかあんまり考えずにどかどか組み立てられるし、万が一のデバッグも、?よりはしやすいんじゃなかろうか、と思うですだす。

「IN句の積み込み」〜「IN句の組み立て」までは一連の流れなので。「複数のINがある」ケースとかは、この一連をloopさせるくらいで片付くと思う。


ちな。

もちろんこの辺の論調で、カラム名について「いくら"自前で用意したホワイトリスト"でも、念のためにエスケープをするべきだ!!」って話が出る可能性はあって、そこについてはとりあえず肯定も否定もしないかなぁ、位の感触。

もちろん、原理原則的に「より安全」なのは特段に否定をしないので、その辺が否定しない理由。

ただまぁ「自分で用意するカラム名」で「引っかかりかねないような命名」って、普通あんまりしないので「そこまでやらんでもいいんじゃないかなぁ」って、今の所は思ってる……多分「何度か連打とかでやらかされたら」この辺はすぐに手のひらを返すんだけどw

なので。実装時に「いや俺は気になるからカラム名をエスケープするのだ!!」って諸氏を止める気は全くありませんので、その辺はご自由に。

おいちゃんは今までの経験と自分の手癖的に「そーゆーネーミングにはしない」前提で、面倒なんでエスケープは省いてます。多分「ヤバい可能性」が想起されたら、速攻でエスケープ処理入れるんだろうなぁw


以上。

おいちゃん的には大分と以前にたどり着いて「割と普通に書いている」処理なのですが。

なんか見聞きしていると「必ずしも一般的とは限らんのかしらん??」と思ったので、幾分メモ書き的ニュアンス込みで、メモり。


誰かの参考にでもなれば幸いでございます。


追伸

IN句のあたり、おおざっぱに関数化するんなら、例えばこんなん。

おいちゃんにしては極めて珍しく参照渡しがありますが。

//
function make_in($col_name, $in_array, &$placeholder_data) {
    // IN句の積み込み
    $in_wk = [];
    // $in_arrayの整形
    $in_array = array_values($in_array);
    //
    foreach($in_array as $num => $in_datum) {
        // 先にプレースホルダ名を作っておく:カラム名+'_'+番号
        $key = ":{$col_name}_{$num}";
        // プレースホルダ名を作業領域に積んでいく
        $in_wk[] = $key;
        // プレースホルダに当て込む値を保持しておく
        $placeholder_data[$key] = $in_datum;
    }
    // IN句の組み立て、或いは「WHERE句の欠片」をreturn
    return "{$col_name} IN (" . implode(', ', $in_wk) . ')';
}

これを前提に

// WHERE句の積み込み
foreach($target as $k) {
    if ('' !== ($_GET[$k] ?? '')) {
        $where_wk[] = "{$k} = :{$k}";
        $placeholder_data[":{$k}"] = $_GET[$k];
    }
}
// IN句の積み込み
$where_wk[] = make_in('hoge', $in_array, $placeholder_data);
$where_wk[] = make_in('foo', [10,20], $placeholder_data); // 即席でこさえてみたダミーデータ

//
if ([] !== $where_wk) {
    $sql .= ' WHERE ' . implode(' AND ', $where_wk);
}

こんな感じで使えば、複数出てきた時に楽ちんだったりします。


2017-11-04 11:10頃追記

徳丸さんから

$in_array = ['1) ; DELETE FROM foo -- ' => 1, 1 => 2];

だとやばくね?

という意図の(もっと口調としては丁寧な)指摘を受けました。

確かに$in_arrayは「内部でSQLで作る想定なのでkeyについては数値前提」だったのですが、何か間違って「外部由来」にしたら危ないなぁ、というのに気付きました。

色々と手はあるかなぁと思うのですが、基本的に$in_arrayは「いわゆる(C++的な意味で)vector」のみを想定しているので。

使う前に、身もふたもなく

$in_array = array_values($in_array);

を差し込んで「問答無用でおかしなkeyを排除する」方向でいったいよいかなぁ、と思ったので、その方向で修正を入れてみます。


ちなみにコードは修正済。

修正前は、以下のコードでした。

//
function make_in($col_name, $in_array, &$placeholder_data) {
    // IN句の積み込み
    $in_wk = [];
    foreach($in_array as $num => $in_datum) {
        // 先にプレースホルダ名を作っておく:カラム名+'_'+番号
        $key = ":{$col_name}_{$num}";
        // プレースホルダ名を作業領域に積んでいく
        $in_wk[] = $key;
        // プレースホルダに当て込む値を保持しておく
        $placeholder_data[$key] = $in_datum;
    }
    // IN句の組み立て、或いは「WHERE句の欠片」をreturn
    return "{$col_name} IN (" . implode(', ', $in_wk) . ')';
}

*1:英字のスペルが短いからとかいう真実に対しては黙秘します

*2:ヤバい台詞が山盛りで記述されたのであわてて消してみました まる

2017-10-19

[][]基礎を学びたい時の書籍草案

発端としては。

うちの子(というか元生徒さん)が「内部的な仕組みとかハードとかやってなかったなぁ」というお話があったので。

おいちゃんなりに、多少「お勧め可能な書籍」とかを勧められればなぁ、ってのが発端でございます。


……なんか記憶にあると思ったら、以前にも http://d.hatena.ne.jp/gallu/20170119/p1 でつぶやいてございましたw

書籍にかぶりがあるのはまぁ「そんなもん」だとおもっていただければ。


注意事項

比較的ノンブレーキ(アクセルフルスロットルではない。ブレーキをかけてないだけだ)なので、書籍には偏りもあれば、濃度もあります。

適宜自分で選びなおしつつ、適量を少量づつ、用法容量を守ってお使いください*1


さて。

何はさておきまずはここからでしょう。


UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

鉄板中の鉄板でございます。

まずはここから。これは必読。


この後ですが


は割とお勧め。ただ、ちと「訳が悪い」というお話もあり、確かに微妙なところもあるので。

もう一つとしては

コンピュータシステムの理論と実装

あたりもよろしいのではないかなぁ、と。

併読ももちろんありで。


プログラム自体、の話をすると、まずはなんといってもこちら。

これはやはりほぼ「必読」。


ここから先ですが……幾分深くに潜り込むのであれば

熱血!アセンブラ入門

熱血!アセンブラ入門

大熱血! アセンブラ入門

大熱血! アセンブラ入門

あたり。

ちなみに「大熱血! アセンブラ入門」は「購入したけどまだ読んでません」ので、厳密な是非は不明。

ただ、以前の内容とざっと斜め読みの雰囲気からして「面白い」のは確定であろうか、と(笑


ここから一瞬寄り道をするのであれば

新装版 達人プログラマー 職人から名匠への道

新装版 達人プログラマー 職人から名匠への道

も面白い。「プログラミングを深く学ぶ」上では非常によいげなので。

この系の「少し古いけど興味深い書籍」はまだストックがあるので、興味が向いたら適宜、突っついてくださいませw


さらに深く掘り下げていくと

30日でできる! OS自作入門

30日でできる! OS自作入門

経由で

CPUの創りかた

CPUの創りかた

って方向も、存在は、する(笑


さて深堀繋がりで。

インフラの場合、深い根っこのあたりとしては

なんてのがあって、起動プロセスが理解できる、という、割合と稀有な書籍。

そこまでいかんにしても

ルーター自作でわかるパケットの流れ

ルーター自作でわかるパケットの流れ

くらいを押させておくと「パケットがどのように流れるのか」が理解できるので、なにかよい基礎が築ける可能性、が、ある。


インフラ繋がりとしては

インターネットのカタチ―もろさが織り成す粘り強い世界―

インターネットのカタチ―もろさが織り成す粘り強い世界―

を抑えておくと、今度は「インターネット」ってもん(の濃いところ)が見えてくるかもしれない。


余談だけど、あきみちさんの「IPv6本」、楽しみだよねぇ(笑


さて。

プログラムインフラときたら、次はDB

データベースシステム概論

データベースシステム概論

は面白いんだけど、絶版だし、そもそもお値が張る。古いし。

An Introduction to Database Systems

An Introduction to Database Systems

のほうにしてもお値段が以下略。


なので、とりあえず

あたりが穏当にお勧め。

あと、実務ベースだけど

SQLアンチパターン

SQLアンチパターン

は合わせて読んでおいたほうが安全な感じ。


あとは、プロジェクトぶん回し的な意味で

人月の神話【新装版】

人月の神話【新装版】

はほぼ必読。

トム・デマルコさん、って方も色々書籍だしてるんで、検索してみるとおもしろげ。

https://www.amazon.co.jp/%E3%83%88%E3%83%A0-%E3%83%87%E3%83%9E%E3%83%AB%E3%82%B3/e/B000AP7OPO/ref=pd_sim_14_bl_19?_encoding=UTF8&refRID=HG973H4YBVAFDDH1BSF1


余談だけど

暗号技術入門 第3版

暗号技術入門 第3版

もわりと「知らないと痛い」内容が多いので、一読をお勧め。


以上駆け足でなんか色々抜けてそうな気がするんだけど、今「ふと思いつく」程度の、手元にある書籍の群れからのチョイス、でした。

何かの役にでも、立つとよいなぁ。

*1PL法対策

2017-08-29

[][]夢歩き おいちゃん変

久々のTRPGネタ。

先日、他人さまのマスターで深淵をやる機会がちょいちょいとありまして。

何となく思ってないわけでもないのですが「おいちゃんの"深淵の夢歩き"」って、いくつか特徴的なところがある可能性、が想起されたので(ってか差分があったので)。

これが「駄目マスター」だと「駄目なんだなぁ」で片付くのですが、そうではないちゃんと面白いマスターさんで、かつ「おいちゃんとは違う夢歩き(の見解)」だったので、興味深いこの感覚が薄くなる前に一筆。


あぁ当然ですが、以下ぜんぶ「おいちゃんの個人的見解」ですので念のため。


さて。

おいちゃんの夢歩きスキルのベースはぶっちゃけると「Tarot占い」です。

知ってる人しか知らないような気がしますが*1、おいちゃんは、プロのTarot占い師でもあります。

それ一本で食ってた時期もそれなりにありますし、そのあとも色々と占いは受け付けてますし*2、素人さんとかセミプロさん相手に教えてた時期もある、くらいにはプロです。


閑話休題


なので、おいちゃんの夢歩きを「十全に習得する」場合「じゃぁまずはTarotを一通り理解しようか」とかいう気の狂ったお話になるので、まぁそこまで踏み込まない程度に(笑


まずは夢歩きの回数から。

まぁ深淵で「オープニング」と「エンディング」に夢を歩かない、というケースは相応にレアであると思われるのですが。

それ以外に「何回夢を歩くか」は、割と、マスターさんによる色が出るような気がいたします。


おいちゃんは、「オープニング」「エンディング」以外で、キャラにもよりますが大体3〜5回くらいは夢を歩かせるようにしています。場合によってはもうちょいましまし。

多分、めっさ多いです(笑


この辺はおいちゃんの「深淵、というゲームに対するスタンス」などあるか、と思うのですが。

おいちゃんの深淵は、どちらかというと「事実をリアルに正確に」というよりは「幻想的に美しく狂おしく感情の渦に振り回され翻弄されるように」的なセッションを、割合に好みます。

その辺で「夢歩き」ってのは、とても「日常に入り込む非日常」を演出できるんで、割と多用するんですね。

「夢と現(うつつ)の間(あわい)」ってな感じ、でしょうか。


あと、基本キャラクターは「悲惨で陰惨で致命的で暴力的*3運命」を、2つとか3つとか付与されているので。

せめても、プレイヤーが「こんな方向に話を進めたい」っていう、マスターへの意見陳述ルートだ、とも思ってます*4


なので。

基本的には、夢を使って「少しづつ事実(または別のナニカ)を明らかにしていく」っていうような感じで、運命などを紐解く情報源として使います。


んで。その「明らかになっていく情報」ですが。

キャラクターにもよるのですが、「キャラクター本人の謎」と「シナリオの謎」の、できるだけ双方を、織り交ぜたりまぜっかえしたり融合させたりしながら出す事が多いように思います。


この辺は「キャラクターの運命の使い方」にもよるのですが。

個人的には、出来るだけ「各キャラクターの運命が、今回のシナリオにも絡んでいく」ように作る事が多いので*5

キャラクターの運命のひも解きとシナリオのひも解きには「ある程度の関連性」があるので、そんなに苦労する事もないですね。


あと、そのシナリオに「フレーバー」を入れる、なんて事も割とよくあるので。

そういう時は、夢の中にそのフレーバーを、割と執拗に様々な角度からぶち込む、なんてのもやります。

つい先日やったシナリオで吐露させていただくと、多分おいちゃんだと「花畑、蜂、蜂蜜、ミード、蜘蛛、8本脚、糸」あたりのキーワードを使うと思います。


さて、ほんちゃんの夢歩きですが。

上述に書いた

・本人の運命

・シナリオ

・シナリオフレーバー

のそれぞれから、キーワードを抜き出します。

で、プラスして「出された運命カードの"語り部"の文言」を使います。


これで「語るためのベース」が揃いました。

後は「ショートショートな物語を紡ぐ」だけです。

ちなみに「最後の、真相に迫るところ」以外は、あちこちモヤっとさせたままでよいと思います。「適度に隠された」ものは、プレイヤーたちが沢山の妄想をするためのよいエサになりますから(笑


そうですねぇ今、即興で一つ、作成をしてみましょう。

http://d.hatena.ne.jp/gallu/20091223/p5 とかちょうどよいので、使ってみますw


スタート場所

 1.寒村、冬直前(もしくは春)の宿屋。旅路での宿泊

スタート付近夢歩きのきっかけ

 6.村特産の飲み物(お茶、あるいはアルコール類)

オプション

 3.満開の花(場合によっては一箇所だけ奇妙に)

中盤以降の嫌な場所/もの

 3.いきなり出現する青龍:大暴れ

シナリオKey人物の背景

 4.すでに滅びた村。何らかの理由で「すでに滅びた人々」が「夢の中で」生きている。事件の解決は夢の開放、すなわち「村人の死」を招く。が、このままではPC達も夢から出られない。

シナリオKey人物の立場

 3.騎士や貴族などの高い身分の人

シナリオKeyな魔族

 1.八弦琴の魔族


シナリオフレーバーには、前述の「花畑、蜂、蜂蜜、ミード、蜘蛛、8本脚、糸」をそのまま使ってみます。

多分……全体としては「村にしがみついていた貴族」「すでに村は滅びた」「でも"滅びた事"が受け入れられなくて(+魔族の力?)、村が"夢の中で"まだ生きている」的な背景が浮かび上がります。

スタート付近夢歩きのきっかけで「村特産の飲み物(お茶、あるいはアルコール類)」とあるので、村の特産品をミード(蜂蜜酒)としておきましょう。……金持ってそうだなぁ貴族がしがみつく理由の一端が見えそうな気がするよ(笑

恐らくですが、村が滅びた理由が「青龍」で、きっと夢の中でも定期的に「襲ってくる」のでしょう。

蜘蛛がフレーバーにあるので。貴族は「蜘蛛の糸のように現実に糸を伸ばして、"夢の村"という蜘蛛の巣に人々をとらえて、その生命を使って生きながらえている」のかもしれません。

ここで「あぁ蜘蛛の大公スフィンとかいたなぁ」からお話を紡いでもよいでしょうし、その辺はマスターの深淵知識次第なので「特に蜘蛛の魔族については気にしない(出さない)」でもよいと思います。大体、セッションやってるとちょいちょい「PCが魔族化」してたりするので、設定以外にも、大量に魔族っぽい存在は転がってるだろうしなぁ(笑


そうするとシナリオの流れは見えてきます。

http://d.hatena.ne.jp/gallu/20091223/p4 あたりでも少し語っているのですが。

ものっそおおざっぱに。コンベンション前提で、セッション前の説明やらテンプレ選びやら設定決めやらが、深淵は丁寧にやっておいたほうがよいと思うので、1〜1.5時間、と仮定して。

割と多くのコンベンションって「11時くらいに開始、18〜19時には終了」が多いように思うので、大体7時間。準備にのりしろみて2時間、昼飯1時間と考えると、残は5時間。

どこぞのお話ではないのですが*6

タイムテーブルを作ったところで「30分のシーン想定で、実際には1時間30分が必要になる」なんてのはザラ、なので。

まぁとりあえず「30分想定くらいのボリュームで実際には1時間かかるであろう」とみると、残5時間なので、大体「1シナリオ、5シーン」という大まかな流れの骨格が見えてきます。


このうち「第一シーン:オープニング」と「第五シーン:エンディング」は確定しているので、あとは2〜4までの3つ。

ごく大まかには

2.シナリオに流されている:あるいは違和感を感じて調査する(必要なら、軽いバトル一ついれて"戦闘ルールを体に叩き込む")

3.謎を解き明かし、最終ターゲットをマーキングする

4.ラストバトル


くらいの感じでしょうか。

これが謎中心だったりプレイヤーがさまよったりすると

2.シナリオに流されている:あるいは違和感を感じて調査する

3.謎が解らないまま、迷走を繰り返す

4.敵が謎を明かしつつ(大体は不利な状態で)ラストバトル


くらいまでのワンパンは許されるのではないか、と思います(笑


んで、夢歩きの話に戻りますが。

大体、おいちゃんがよく使うのは


・シナリオ導入時の「謎に満ちた」夢歩き

・「だんだん、真相の一部が明らかになっていく」夢歩き*複数回

・ラストバトル直前くらいの「全ての真実が、今、明らかになる!!」な夢歩き


くらい、ですかねぇ。

あとは、夢歩きのちょっと「ひねった」バージョンとして「最後で視点がどんでん返し」なんてのをやる事があります。

わりと強く印象に残っているのが。

「魔族に作られたもの」の運命を持っているキャラクターがいたのですが。


ず〜っと、夢歩きで「君は、何か目の前にある水槽のようなもので、"ナニカ"を作っている」という、作成者側の視点で夢をあるかせ続けて。

「自分の失った記憶……もしかして、自分は魔族ないしそれに類するもので、何かよからぬものを作ってしまったのか?」って方向にず〜〜〜っとミスリードさせておいて。

最後の「全ての真実が!!」のタイミングで、「君は水槽の中の"ナニカ"に声をかけるよ。"さぁ目覚めるんだ 'PCの名前' よ!!"」って爆弾落としたことがありました(笑

プレイヤーさんが、本気で驚いてましたねぇ(笑


まぁ、おいちゃんの夢歩きは、割とそんな感じで作っていくことが多いように思います。

実際に「どんな風に夢を歩くのか」については、そのうちBlogのほうに書いていければなぁ、と思いますので、よろしかったらご期待くださいませ。

*1:当たり前だ

*2:今は「知り合い経由」くらいしか受け付けてませんが

*3:はずの

*4:陳述が「通るのか」はまた別問題w

*5:作成場所:セッション場所。作成時間:セッション直前(若しくはセッション中)

*6:Ashenroses Report、と書いてわかる御仁でここを見る可能性があるのは、多分、地球上でわずか数名であろうw