コマンドの合わせ技でsymfonyを起動する

こんにちは。10月に入社しました三浦です。

10月22日に弊社の引っ越しがありました。
いくつもの机や椅子を組み立てて、新オフィスでの業務を迎えています。
組み立て中は、別の方が持参した電動ドリルが大活躍しました。
バッテリー切れでヘトヘトになっていたので、持ち主の方から電気をもらっていることを祈ります。

さて本題ですが、今回はsymfonyの起動コマンドをコマンドラインで生成させてみます。
symfonyを自習していた当初、手動でifconfigを打ってIPアドレスを確認してからコマンドに入力していましたが、
何回も打っていたのでIPアドレスを自動で取得できる方法を考えてみました。

今回のテーマ

linux搭載のコマンドだけで、symfony起動コマンドを生成する。

前提条件

以下条件の下で、ホストOSのWebブラウザからsymfonyサイトにアクセスすることを考えます。
1. ホストOS:Windows10、VM:ubuntu18.04
2. 両方ともIPアドレスはDHCPによる自動付与とする。
3. VM上でsymfonyを稼働させる。

ポイント

3の前提より、単にserver:startを実行するだけではアクセスできません。

php bin/console server:start

代わりに、アクセスするIPアドレスとポート番号を明示して起動します。

php bin/console server:start {VMのIPアドレス}:{port番号}

IPアドレスの取得

ifconfigの出力結果から、IPアドレス(XXX.XXX.XXX.XXXの部分)を抜き出してみます。
ここでは、ホストOSと通信できるVM側のNICをeth0とします。

$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet XXX.XXX.XXX.XXX  netmask YYYY.YYY.YYY.YYY  broadcast ZZZ.ZZZ.ZZZ.ZZZ
        inet6 aaaa::bbbb:cccc:dddd:eeee  prefixlen 64  scopeid 0x0<global>
        ether ff:gg:hh:ii:jj:kk  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ifconfigの結果から抜き出すコマンドは以下の通りです。

echo `ifconfig eth0 | grep inet | grep -v inet6 | awk -F' ' '{ print $2 }'` … (a)

symfony起動コマンドの生成

(a)のコマンドを実行して出力されるIPアドレスをシェル変数に格納し、symfonyの起動コマンドを作成します。
なお、ポート番号は8000番を指定しています。

cd {起動するプロジェクト名}
SERVER_IP=`ifconfig eth0 | grep inet | grep -v inet6 | awk -F' ' '{ print $2 }'`
php bin/console server:start ${SERVER_IP}:8000

もう一工夫する

symfony起動中の時に server:start を実行するとエラーになってしまいますので、symfonyが停止中の時だけコマンドを発行するようにします。
今回は、プロジェクトフォルダ直下に「.web-server-pid」がなければ、symfony停止中と判断します。
if文を使って、symfony停止中の判断を追加します。

cd {起動するプロジェクト名}
if [ ! -f .web-server-pid ]; then
  echo `ifconfig eth0 | grep inet | grep -v inet6 | awk -F' ' '{ print $2 }'`
  php bin/console server:start ${SERVER_IP}:8000
fi

これで、IPアドレスを都度取得してsymfonyを起動するコマンドが出来上がります。
別ファイルに保存して適切なパーミッションを設定すれば、ワンタッチでsymfonyを起動するシェルとになります。

最後に

小さいコマンドながら、自分で作れたときは達成感を感じました。
これからも学ぶことは多いですが、今回のような達成感を糧に進んでいきます。

命名について:The Art of Readable Codeを読んで。

はじめに

今日も元気に働きバチ!こんにちは。廣田です。
弊社ではコミュニケーションツールとしてslackを使用していますが、たまに見るヘルプセンターの文面がユニークで仕事中にフフっとしてしまいます。

さて先日、先達の方々にコードを見ていただける機会がありまして
いろんなダメ出しがあったのですが、変数やメソッドの命名について多く指摘をうけました。

そこで結構前に紹介してもらっておきながら未だ読んでいなかった名著The Art of Readable Codeを読んだので、
命名について書いている2章から、学んだことを抜粋して紹介していこうと思います。

Packing Information into Names

名前はちょっとしたコメントのようなものです。
十分なスペースが無くとも、良い名前を選ぶことにより多くの情報を伝えることができるのです。

明確な単語を選ぶ

例えば ”get” という単語はとても不明確です。例えば以下のようなメソッドがあるとします。

def GetPage(url):
……

この場合、このメソッドはどこからpageを取得するのでしょうか。

ローカルキャッシュでしょうか? DB、インターネットからかもしれません。
もしインターネットからであれば、FetchPage()やDownloadPageといったほうがより具体的です。

同様によく使われる単語としてsizeがありますが、返り値が予測できるような、より具体的な命名を考えるべきです。
例えばツリー構造の高さを示すならTreeHeight、ノードの数ならNumNodes()、 メモリのサイズならMemoryBytes()など。

よく使われる単語について以下のように置き替えることで、より具体的な表現ができるでしょう。

不明確な単語 より明確な代替語
send deliver, dispatch, announce, distribute, route
find search, extract, locate, recover
start launch, create, begin, open
make create, set up, build, generate, compose, add, new

 

tmpやretvalのような総称的な命名を避ける

“Tmp”や”retval”, “foo”などはよく使われる命名ですが、そのものの持つ値や目的を描写するような名前を選ぶべきです。
以下のようなコードを例に見ていきましょう。

function (v) {
  var retval = 0;
  for (var i = 0; i < v.length; i ++ ) {
	retval += v[i] * v[i];
	}
  return Math.sqrt(retval);
}

変数retvalは“I am a returned value”という以上の情報を含んでいません。

より良い命名とは変数の目的やそのものの含む値を詳細に表すものです。

先述のコードでは変数の中身は”v”の2乗の累積でした。
なのでより良い命名は、”sum_squares”などでしょう。このような命名にすることで、バグも見付けやすくなります。

抽象的な名前よりも具体的な名前をつけるようにする

たとえばServerCanStart()という名前のメソッドがあるとします。
そして、このメソッドはサーバが規定の TCP/IPポートをListenできているかを検証するものだとします。

この場合、より具体的な名前はCanListenOnPort()などでしょう。
こちらのほうが、メソッドがなにをするものなのかを直接的に示しているからです。

抽象的な命名をした場合、チームに新しく加わったメンバーにとって、それが何であるのか、なぜ必要なのかということが分かりにくくなってしまいます

名前に追加の情報を付け加える

読み手が変数について知っておかなければならない重要な情報がある場合、
名前に追加の単語を付け加えることは価値のあることです。

たとえば16進数の文字列を含んだ変数があるとします。

  string id; // Example: "af84ef845cd8"

この場合、hex_id という名前のほうがより良いでしょう。
読み手がidのフォーマットは16進数だとわかるからです。

 変数名  より良い命名
暗号化されていないパスワード password ⇒ plaintext_password
UTF-8にエンコードされたhtmlバイト html ⇒ html_utf8

名前の長さはどの程度にするべきか?

変数名は長すぎてはよくありません。
名前が長くなればなるほど、覚えづらくなり、スペースも消費してしまいます。

一方、1単語、あるいは1文字の名前というのも考え物です。

短い名前は短いスコープで

複数ブロックにまたがるような変数に対し1,2文字の暗号のような名前を使ってはいけません。
そのような短い名前は2,3行の短い範囲で使用される変数には良いでしょう。

頭文字や省略形の使用

例えば”BackEndManager”のかわりに”BEManager”のような省略形を使うことがあります。
これは混乱を生じさせるでしょうか?

もしその省略形が、プロジェクトに特有のものであるときは使用を避けるべきです。
そのプロジェクトに新しく参加した人たちにとって暗号のようにしか見えないからです。
さらに言うと、そのコードを書いた人にさえ、ずっと後に見たときに暗号のように見えるはずです。

省略形を使えるかどうかの判断基準は
新しいチームメイトが、その名前が何を意味しているか理解できるか?
この1点です。

さいごに

と、ざっくりとまとめてしまいましたが、
初めてコードを見た人が「何を保持している変数なのか」、「具体的に何をする関数なのか」ということがわかる命名をするのが重要ということで、今後の業務で気を付けていこうと思います。

また、この内容はかなり私の意訳が入っているのと、実際の本にはもっとたくさんの例やコードが書いてあるので、そちらを読むことをお勧めします。
あと挿絵が面白いです(笑)

日本語の本もamazonで見かけました。

3章以降も学ぶ内容が多いので、自分のような初学者はまず読んだほうが良い内容だと思いました。
では今回はこの辺で。

僕らの96bit戦争

はじめに

こんにちは、イメージ・マジックの安藤です。

先日の台風24号で私の住んでいるアパートは何度も大いに揺らされ、今にも倒壊するのではないかという勢いでした。
本当に大変だったのは翌日の電車でしたね。私の使用している沿線は早朝の段階では止まっていました。

そんなわけで、今回の話題は文字表現とビット演算の話です。

情報を管理する媒体について

当社で日々製造されている製品は、実際にお客様のお手元に届けられるまでに実に様々な工程を経ています。
しかも、それらは大量生産されているようなものばかりではなくお客様ごとのオンデマンド製品も多分に扱っています。
これらの製品を間違いなくお客様にお届けするためには、注文や製品ごとに情報をきっちりと管理していく必要があります。
そしてこれらの製造を支える在庫の管理・調整も効率的に向上を回していくうえで欠かせないものです。

そこで重要になってくるのが情報を管理する媒体です。最も簡単に実現できるものとしては、バーコードを印刷したラベルシールがあります。

ところで、皆様はユニクロのセルフレジを利用したことがありますでしょうか?
商品を置くだけで何をどのくらい買うのかを瞬時に判断してくれるので初めて使われた方は驚かれるかと思います。

実は、このレジにはRFID(非接触型ICタグ)という仕組みが使われており、一度に複数の商品を読み取ることができます。

当社においても、RFIDを実用していくために現在様々な検討をしています。

RFIDに書き込む、RFIDを読み込む

RFIDにはEPC Class1 Generation2という規格があり、4つのメモリバンクで構成されています。

  • RESERVED
  • EPC
  • TID
  • USER

データを読み書きする部分は主にEPCメモリで、通常は96bitを使います。USERメモリも自由に読み書きできますが、使用するチップによっては実装されていないこともあり容量も特に決まっていません。
TIDメモリはRead Onlyで32bit以上のチップの識別情報が書き込まれています。

一言でまとめますと、96bitに書きたい情報を詰めろということです。

96bitの使い道を考える

96bitでできることは何か考えてみましょう。

まず、これをビットボートとして扱った場合は96の状態を表現できますが、RFIDの使い道としては微妙すぎます。

次に、考えることとしては数字として使うことでしょう。32bitの整数であれば3つ分表現できますが、やはり不便です。

そこで行き着くものとしては、やはりASCII文字になるかと思います。この場合、1文字が8bitで最大12文字まで使用できます。

ところで、ここで一つ問題があります。
日本で使われているバーコードの規格であるJANは13桁で規定されているのです。もっとも、13桁目はパリティビットとなっているため導出が不可能というわけではないですが様々な場面で余計な手間がかかってしまいます。

様々な検討の結果、最終的にASCIIの余計な文字を削って16字まで表現できるようにするという方式を取ることにしました。

96bitで16文字を表現する

96bitで表現できる文字数を増やすには、1文字に使用できる選択肢を減らせばいいです。
1文字を6bit(=64種類)とすると96/6=16となり16文字分使えることとなります。
この際、64文字は好きなように選ぶことができます。

今回は0-9A-Zの半角英数大文字と括弧など一部の半角記号を除いた55文字を採用することとしました。

エンコード/デコードを実装する

諸般の事情により、上記の実装はnode.jsで行いRFIDリーダライタとのやり取りはネイティブモジュールを挟むことで実現しています。

読み取られたデータは16Bのデータとなっており、最初の4BはTIDです。
デコード時は12Bを6bitごとに分解する必要があり、エンコード時は最大16文字を6bitで表現し12Bに詰めなおす必要があります。

const codeMap = []; // 省略 最大64種類の文字に置き換えられる

const bitMap = [1, 2, 4, 8, 16, 32, 64, 128];

const calc = arr => arr.map((n , i) => n ? bitMap[i] : 0)
    .reduce((sum,  n) => sum + n,  0);

const toBit = (b, arr = [], index = 0) => index === 6 ? arr :
    toBit(parseInt(b / 2, 10), arr.concat(b % 2), index + 1);

const encode = src => {
    const chunk = src.split('') // 1文字ごとに分ける
        .map(s => toBit(codeMap.indexOf(s)))
        .reduce((a , b) => a.concat(b), []);
    return Array(12).fill(0)
        .map((_, i) => calc(chunk.slice(i * 8, (i + 1) * 8)));
};

const decode = src => {
    const chunk = src.slice(4) // TIDをスキップ
        .map(b => Array(8).fill(0).map((_, i) => b >> i & 1))
        .reduce((a , b) => a.concat(b), []);
    return Array(16).fill(0)
        .map((_, i) => codeMap[calc(chunk.slice(i * 6, (i + 1) * 6))])
        .join('');
};

おわりに

エンコード/デコードの実装が意外と面倒で2時間ほどかけてしまいましたが、これで16文字の範囲で自由にRFIDを書き換えることが可能になりました。
ちなみにネイティブライブラリは当社別メンバーの力作です。

参考

20160129-UHF帯RFID標準コード体系ガイドライン初版

印刷しているのは、商品だけじゃありません

はじめに

こんにちは、イメージ・マジックの矢野です。入社から2か月が経過し、順調に机の上がゴチャゴチャしてきました。カメラが2台、バーコードプリンターが2台、小さなスピーカーが1台、さらにカードリーダーが1台、デスクの上に転がっています。

印刷しているもの

当社が印刷しているのは、Tシャツやパーカー、マグカップ、カーペットなどの製品そのものももちろんですが、それだけではお客様のお手元に製品を届けることはできません。生産工程でそれらを管理するためのラベルシールや、納品書、配送業者さんにお願いする送り状なども自社のシステムで印刷しています。当然といえば当然なのですが。

ラベルシールの印刷には、専用のプリンターを使用しています。私は転職してきて初めて触りました。ずんぐりむっくりでかわいい形ですが、言うことを聞かせるのはなかなか難しいです。プリンター専用のツールやライブラリを使ってバーコードや文字列や画像を出力するように指示することもできますが、プリンターの制御用コマンドを直接(「直接」とは言っても、通り抜けなければならないレイヤーは多数あります)送信することもできます。

ところで、「プリンターで何かを印刷する」というのはどういうことなのでしょう? それは文字列や画像、バーコードを印刷しなさいというコマンドの積み重ねです。ラベルシールにバーコードを印刷する場合には、最終的にたくさんのしましまが紙面に出力されるようなコマンドをアプリケーションで生成して、それをプリンターに送信する必要があるわけです。

たくさんのしましまを、どうやってプリンターに送信しましょうか?

  1. しましまを1本1本引くコマンドを生成して、順番に実行する
  2. バーコード全体の画像をPNGなどのラスター画像で生成して、画像を印刷するコマンドを実行する
  3. 「このデータ(ID)に対応するバーコードを、このバーコード規格を使ってイイ感じに生成してよ」とプリンターに任せるコマンドを実行する
  4. その他

1も2も3も正解ですが、1は特に厳しい。バーコードの仕様に従って、1本1本のしましまの太さと間隔をどうにかこうにかしなければなりません。2は割とよくあると思います。バーコード画像を生成するライブラリを使用すれば、割とサクっとできるでしょう。でも、せっかく専用のプリンターを使っているのですから、彼(彼女?)に任せてしまってみましょう。

イイ感じにやってもらう

Windowsで、Win32 APIを使って実装してみます。プリンターによろしくやってもらう代わりに、プリンター固有のお作法に従わなくてはなりません。ここでは、Zebra Technologies社のプリンタ向け言語、ZPL(Zebra Programming Language)のコマンドを実行しています。

int PrintRawCommand()
{
	HANDLE hPrinter;

	if (!OpenPrinter((LPSTR)"Printer Name", &hPrinter, NULL))
	{
		// エラー処理

		return FALSE;
	}

	DOC_INFO_1 docInfo;
	docInfo.pDocName = (LPSTR)"Barcode sample";
	docInfo.pOutputFile = NULL;
	docInfo.pDatatype = (LPSTR)"RAW";

	LPSTR command = (LPSTR)"^XA^FO40,40^BY3^BEN,40,Y,N^FD123456789012^FS^XZ";

	if (StartDocPrinter(hPrinter, 1, (LPBYTE)&docInfo))
	{
		if (StartPagePrinter(hPrinter))
		{
			DWORD writtenBytes;
			if (WritePrinter(hPrinter, command, strlen(command), &writtenBytes))
			{
				// 印刷成功時の処理
			}
			else
			{
				// エラー処理

				return FALSE;
			}
		}
		else
		{
			// エラー処理

			return FALSE;
		}
	}
	else
	{
		// エラー処理

		return FALSE;
	}

	EndPagePrinter(hPrinter);
	EndDocPrinter(hPrinter);
	ClosePrinter(hPrinter);

	return TRUE;
}

15行目で、プリンタへ送信するデータの形式をRAW(生=Zebraのプリンターが解するコマンドを直接送信する)に指定します。17行目の文字列が実際にプリンタへ送信されるZPLのコマンドです。レイアウト、フィールド、バーコードの表現形式(EAN-13)、バーコード化したいデータ(123456789012)などを、 ^ で始まるコマンドで指定します。

これの出力結果がこうなります。

EAN-13の詳細な仕様を隅々まで探らなくても、難しい画像操作系のAPIを駆使しなくても、美しいバーコードを得ることができました。

FAX送信 製品比較検討

はじめに

イメージマジックの宮川です。

寒暖差があって、体調崩しがちですね。
風邪、気を付けてくださいね。(ひきました 笑)

さて!ということで!今回は業務で検討したFAX送信製品の比較についてです。

FAX送信 製品比較検討

弊社で開発中のWebアプリからFAX送信可能なAPIを検証しました。
基本的には、FAX送信時にFAXする対象をpdfに変換して、PDFのURLやファイル自体を送信する必要があります。
(PDF以外も対応可能)

調査したところ、以下の3製品がHITしましたので、それぞれ検証してみました。
twilio
eFax
interfax(日本サイト)

※interfaxは日本サイトと海外サイトで若干記載が異なりましたが、日本サイト版の結果を記載します。

弊社では、twilioを導入しました。理由は、pdfしか使用しない点、HTTPリクエストに対応していて、導入が簡易的だったからです。
(固定コストが安く、サービスのスイッチもしやすい。また、他サービス(電話)なども使用可能)

以下、検証結果です。参考までに。
※コストなど、社内では細かく算出しましたが、記述粒度が細かくなりすぎるため、ここでは割愛しました。

製品名 言語 形態 対応形式 特徴
twilio C#、Curl、Java、Node.js、PHP、Python、Ruby HTTPリクエスト(pdfダウンロードurlを指定) pdf 固定コストが安いが、従量が高い
eFax なし(e-mail) メール送信 pdf、doc、xls、ppt、jpg、gif、psd コストが安い
interfax Java、ASP.NET、VB.NET、Perl、PHP、C# HTTPリクエスト、メール送信 pdf、html、txt、office系 多機能だが高コスト

twilioサンプルコード(php)

以下のようなサンプルで、twilioでFAX送信できます。

<?php

// Update the path below to your autoload.php,
// see https://getcomposer.org/doc/01-basic-usage.md
require_once '/path/to/vendor/autoload.php';

use Twilio\Rest\Client;

// Find your Account Sid and Auth Token at twilio.com/console
$sid    = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$token  = "your_auth_token";
$twilio = new Client($sid, $token);

$fax = $twilio->fax->v1->faxes
  ->create("+15558675310", // to
    "https://www.twilio.com/docs/documents/25/justthefaxmaam.pdf", // mediaUrl
    array("from" => "+15017122661")
);

print($fax->sid);

参照:
Twilio Programmable Fax Quick Start

Vagrantのスナップショット機能

はじめに

こんにちは、イメージ・マジックのもあいです。
弊社開発部ではVirtualBoxを使ってローカルに開発環境を作成して開発を行っていますが、開発環境作成ではVagrantとAnsibleを使って自動で環境を作成しています。
今回は、この開発環境作成で便利なスナップショット機能を多用していましたので、どういった状況でどう使っていたのかを軽く説明します。

Vagrantとは

詳しくはWikiPedia等で確認してください。環境構築用ファイルから自動的に仮想マシンを作成して設定を行えるものです。
会社ではUbuntuをサーバOSとして使用していますので、BoxイメージはUbuntuの提供元が用意しているものを使用しています。

スナップショット機能とは

Vagrant+Ansibleで開発環境を作成するということは、Ansibleスクリプトを実行して正しい状態になることを確認します。
Ansibleスクリプトでは内容にもよりますが、設定変更が必要なときだけ設定変更を行います。
ということは、1回目と2回目で実行内容が異なるというわけです。テストではどちらの挙動も確認する必要があるのですが、
そのたびにVagrantで仮想マシンを破壊/作成を繰り返すのは非常に時間が掛かります。

そこで、VagrantにはVirtualBoxのスナップショット機能を簡単に使うコマンドが用意されていて

vagrant snapshot save foo

とすることでスナップショットを保存することができます。
この状態でAnsibleスクリプトを実行して、環境を戻したい場合は

vagrant snapshot restore foo

とすることで、スナップショットをとった状態に戻すことができます。
これでAnsibleスクリプトの再実行の時間が大幅に短縮されます。
fooとかいてあるところは名称ですので、いくつかスナップショットをとることもできます。

inputのtypeが”file”の時の選択エリアの話

はじめに

株式会社イメージ・マジックの技術ブログ、今週の担当のsoenoです。

今朝の神田は雨でした。

技術が発達すると傘を差さずにぬれずに通勤なんてことも可能になるのでしょうか。

さて、今回は仕事中にはまったcssについてです。

というか、タイトル通り<input type=”file”>で指定したはずの選択エリアの話で、

指定したつもりのエリアと食い違うことがあるという話です。

前提

状況としては結構色々やらないと発生しないのでニッチな投稿となるのですが、

– <input type=”file”>を使用している。
– カスタマイズしている。<input>は非表示にして使用。
– ボタンエリアは結構狭め。

な感じで発生します。

挙動

ボタンの外を押してもイベントが発生する。

要素にボーダーを付けたりして調べてもそれらしきサイズの要素がない。

再現可能なコードはこんな感じ


//~略~
<style>
.uploadArea{
  position: relative;
  color: #fff;
  background: #2ab1e7;
  width: 30px;
  height: 30px;
}
.uploadBtn{
  position: relative;
  height: 100%;
  width: 100%;
  opacity: 0;
  cursor: pointer;
}
</style>

//~略~

<body>
 <div class="uploadArea">
  <label for="uploader">
   <input id="uploader" type="file" class="uploadBtn">
  </label>
 </div>
</body>

//~略~

解決法

opacityを1にしてみると何が起きているかすぐにわかります。

inputのタイプがfileの時はアップロード用のボタンがあるが、それがはみ出てしまうというのが今回の不具合の理由でした。

なので修正はuploadAreaのcssに overflow: hidden;を足すだけ。

わかってみるとショックなほど簡単なことなのですが、inputエリアのタイプがfileの時はボタンを持っている。というのを忘れていると、この範囲何?誰?みたいなことになります。

最初に疑ったのはz-indexだったり、opacity:0にした時点で見つけにくくなるわで、結構はまったので取り上げてみました。

Node.jsのネイティブなアドオンを書く

はじめに

こんにちは。イメージ・マジックの矢野です。入社からもうすぐ1か月です。

Node.jsのアドオンを、何らかの理由でネイティブのコードで書かなければならないことがあります。Node.jsのエンジンであるV8のお作法に則って書くことになりますが、V8がバージョンアップして仕様が変われば、そのお作法も変わります。お作法が変わればアドオンの書き方も変えなければならず、毎度毎度書き直すのは大変。そこで、Node.jsのネイティブなアドオンの実装をイイ感じにラップして抽象化してくれる、NAN(Native Abstractions for Node.js)というものがつくられたとのこと(READMEの冒頭を読むと、クスリとさせられます)。もちろんNANを使わなくてむ作れるわけですが、せっかく便利なものがあるのですから、使わない手はありません。

準備

Node.jsのアドオンをつくるのですから、当然Node.jsはインストールされていないといけません。

NANはnode-gypでビルドするため、node-gypもインストールしておかなければいけません。さらにgypはPythonスクリプトなので、Pythonが入っていなければインストールしておかなければなりません。

ネイティブコードをビルドするために、コンパイラなども必要です。今回はWindowsを使って開発しているので、Visual Studio 2017をインストールしておきます。

作成

準備ができたところで、作業用のディレクトリを作成します。

mkdir hoge
cd hoge

NANをインストールします。

npm install --save nan

binding.gypを作成します。このテキストファイルには、アドオンの構成を記述します。ネイティブならではのアドオンをばっちり作り込むのはそれなりに大変なので、今回はOSのバージョンを取得するだけの簡単なものにしようと思います。

{
  "targets": [
    {
      "target_name": "getosversion",
      "sources": [
        "src/addon.cpp",
      ],
      "include_dirs": [
        "<!(node -e \"require('nan')\")",
      ],
    }
  ]
}

ソースファイルを格納するディレクトリ(src)をつくり、ソースファイルを形だけでも(空ファイルでOK)つくっておきます(src/addon.cpp)。

node-gypconfigureすると、上記の構成に従ってVisual StudioのC++プロジェクトやソリューションを生成してくれます。

node-gyp configure

buildディレクトリの下にいろいろファイルができています。

│  binding.gyp
│
├─build
│      binding.sln
│      config.gypi
│      getosversion.vcxproj
│      getosversion.vcxproj.filters
│
└─src
        addon.cpp

binding.slnを開くと、Visual Studioが起動しますので、addon.cppの中身を書いていきます。NANの恩恵を受けるには、ヘッダファイルnan.hをincludeしておく必要があります。
Win32 APIのGetVersion()を呼び出して、OSのバージョン情報を取得してみます。ちなみにこの関数は現在「非推奨」になっていますが、Win32がメインの記事ではないので、そのまま使ってしまいます(業務ではこんなことしませんよ)。

#include <sstream>
#include <nan.h>

NAN_METHOD(GetOsVersion)
{
	DWORD dwVersion = GetVersion();

	DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
	DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));

	DWORD dwBuild = 0;
	if (dwVersion < 0x80000000)
	{
		dwBuild = (DWORD)(HIWORD(dwVersion));
	}

	std::stringstream ss;
	ss << dwMajorVersion << "." << dwMinorVersion << "." << dwBuild;

	info.GetReturnValue().Set(Nan::New<v8::String>(ss.str()).ToLocalChecked());
}

NAN_MODULE_INIT(init)
{
	Nan::SetMethod(target, "GetOsVersion", GetOsVersion);
}

NODE_MODULE(getosversion, init)

ビルドします。

node-gyp build

もろもろビルドされます。肝心のアドオンは、build/Release/getosversion.nodeとして生成されます。

│  binding.gyp
│
├─build
│  │  binding.sln
│  │  config.gypi
│  │  getosversion.vcxproj
│  │  getosversion.vcxproj.filters
│  │
│  └─Release
│      │  getosversion.exp
│      │  getosversion.iobj
│      │  getosversion.ipdb
│      │  getosversion.lib
│      │  getosversion.map
│      │  getosversion.node
│      │  getosversion.pdb
│      │
│      └─obj
│          └─getosversion
│              │  addon.obj
│              │  vc141.pdb
│              │  win_delay_load_hook.obj
│              │
│              └─getosversion.tlog
│                      ... (省略) ...
│
└─src
        addon.cpp

これを実際に動かしてみましょう。テスト用コードをこんな感じで書いてみます(test.js)。

var addon = require('./build/Release/getosversion');

var version = addon.GetOsVersion();

console.log(result);

nodeコマンドから実行してみると、それらしい値が取れました。

node test.js
10.0.16299

PHPにおける配列作成・分解マジック

はじめに

こんにちは、イメージ・マジックの安藤です。

オフィスに置いてあるウォーターサーバーを最も頻繁に使用している気がします。水が美味しいのは素晴らしいことです。

さて、今回は現在開発しているシステムで採用されているPHP7に関して、配列の中身を複数の変数にしたり複数の変数を配列にしたりする方法についてです。

PHPにおける配列とは

本題に入る前にPHPでの配列の扱いについて軽く触れておきます。

PHPの公式リファレンスには配列について以下のように説明されています。

PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 キーに関連付けます。

つまり、PHPには厳密には連想配列しか存在しないということです。

配列のキーには数字(整数)か文字列のみ使用でき、「数字として妥当な文字」がキーに指定された場合に数字に変換されるなどの変換法則がいくつかあります。

詳しくは公式リファレンスをご覧ください。

配列の作成

基本的にarray()を使用することで任意の配列を作成することが可能です。

PHP5.4からは[]を使うこともできます。

$data = [
 [1, 'noel'],
 [2, 'michael'],
];
$data = [
 ['id' => 1, 'name' => 'noel'],
 ['id' => 2, 'name' => 'michael'],
];

既に連想配列の値が変数として用意してあり、変数名と同じキーの配列を作りたい場合はcompact()を使うことができます。

$company = 'IMAGE MAGIC';
$state = '東京';
$city = '千代田区内神田';
print_r(compact('company', 'state', 'city'));
Array
(
  [company] => IMAGE MAGIC
  [state] => 東京
  [city] => 千代田区内神田
)

配列の分解

PHP5.xやPHP7.0では通常の配列はlist()、連想配列はextract()を使用することで実現できました。

$data = [
  [1, 'noel'],
  [2, 'michael'],
];
// $id = 1, $name = 'noel'
list($id, $name) = $data[0];

foreach ($data as list($id, $name)) {}

$data = [
  'company' => 'IMAGE MAGIC',
  'state' => '東京',
  'city' => '千代田区内神田',
];
/*
 * $company = 'IMAGE MAGIC';
 * $state = '東京';
 * $city = '千代田区内神田';
 *
*/
extract($data);

PHP7.1ではlistの代わりに[]を使うことができます。

$data = [
  [1, 'noel'],
  [2, 'michael'],
];
// $id = 1, $name = 'noel'
[$id, $name] = $data[0];

foreach ($data as [$id, $name]) {}

$data = [
  'company' => 'IMAGE MAGIC',
  'state' => '東京',
  'city' => '千代田区内神田',
];

/*
 * $company = 'IMAGE MAGIC';
 * $state = '東京';
 * $city = '千代田区内神田';
 *
*/
[
  'company' => $company,
  'state' => $state,
  'city' => $city,
] = $data;

そのほか便利な操作

  • array_keys
    連想配列のキーだけを取り出して配列にする
  • array_values
    配列を0からの添え字の配列にする
  • array_key_exists
    指定したキーが配列に存在するか調べる
  • in_array
    指定した値が配列に存在するか調べる

おわりに

PHPでは配列が作りやすいので色々と便利ではありますが、mapやfilter,reduceなどが使いにくいというが少々残念なところです。
省略できる記法は省略して可読性を上げていきたいですね。

Ubuntuについて

こんにちは、イメージ・マジックの岡野です。

当社は大半のサーバでUbuntuというLinuxのOSを使用しています。日本ではCentOSが良く使われていて入社された方がUbuntuに詳しくない事もある様ですので簡単に説明しようと思います。

読み方

Ubuntuはウブントゥやウブンツと呼ばれます。元々はズールー語の言葉で「他者への思いやり」の意味らしいです。なお、Ubuntu界隈では余り一般的でない単語が結構出てきます。”multiverse”(多元宇宙論)や”Artful Aardvark“(⁠巧妙なツチブタ)など。知らない人は何の事か分からないため(私も忘れやすいため時々検索して調べます)、外部からの印象は余り良くないんじゃないかと心配になります。

当社でUbuntuを使用した経緯

2012年頃までは当社でもCentOSやRed Hatを主に使用していました。ちょうど私が担当を引き継いだサーバでRed Hat Networkのアカウントが分からず、CentOSの方も古いパッケージ(ソフトウェア)がダウンロードできないなど、OS周りで辟易としていました。サーバリプレース時に良い評判を聞いていたUbuntuへ入れ替えた所、思っていた以上に良かったです。

メリット1 自動セキュリティアップデート

サーバの場合unattended-upgradesというパッケージを入れる必要がありますが、Ubuntuではほぼ全てのパッケージについてセキュリティアップデートが提供されています。脆弱性が見つかってから速い場合には4時間で対応が完了している事もあり(確かImageTragickの脆弱性の時、実際にはアップデート自体が実行されるまでのラグが存在する)助かっています。なお全て自動で任せていては対応が遅れるため「JVN脆弱性レポート」などのSNSをwatchして手動対応することもあります。

メリット2

他にも色々ありますが長くなって来たので省略します。

デメリット

/etc/に置く設定ファイルの場所などがCentOS等と異なるため、たまに別のOSを触ると戸惑います。

また続きを書きたいと思います。