エントリー

勉強会資料とか技術メモとか

勉強会に過去の勉強会資料、その他に技術メモを置きました。

技術メモは古くて役に立たないものも含まれているかも。

就職して3年が経った

この3年間のことを振り返り。

文系の大学に行ったのに何故か在学中にプログラミング(当時はPerlでCGI)にハマり、アルバイト以上従業員未満のような状態で1年ちょっと企業に属しただけで、いきなりフリーのプログラマとして活動をしていた。

冗談だったか本気だったかはともかく、複数の方から何度か会社に誘ってもらいつつも、断り続けて活動をしていた。でも4年前くらいから企業に属してみたい気持ちも大きくなってきていた。
当時は一人で完結する仕事が半分、あとはディレクターさんやデザイナーさんやコーダーさんたちと組んで進める仕事が半分、くらいだった。チームで進める方が大きな仕事をできるし、困ったら相談できるし、打ち上げとかも楽しいのでどこかに属してみたい。というぼんやりしたプラスの気持ちもありつつ。
このまま進めて10年後、20年後は本当に大丈夫なのか。というマイナスの気持ちもありつつ。

そんなときによく一緒に仕事をしていた人が、とある会社に入ることになり、そこの社長さんに僕を紹介してくれた。会ってみると人柄は良さそうだし、会社の方針や目指すところも魅力的だし、会社には神の如き技術力を持つ天才的な方がいて通称「神」らしい。
そして、その後何度か社長さんに会っているうちに会社に魅力を感じるようになり、3年前に入社させてもらうことになった。

上に書いたような経歴なので、フリーで活動していると「まともに企業に属したことのない人には分からないだろうけど云々」や「色々作ってきたと言っても、所詮フリーの個人が作ったものだし云々」なことはときどき言われた。(今になって思うと、そういう意見も分かる。)
会社に入るときも「技術の世界で生きていくなら、2年後には神の左腕ぐらいにはなっていると嬉しい」のように言われたし、見下されるとか悪い意味ではなく「これから教えていって、いつか使い物になってほしいな」という感じだった。
実際、個人の時はサーバ構築や管理は怖いから極力請けないようにしていたり…という面はあったので、神の如き技術を勉強させてもらおうとワクワクしていた。

続いて入社1~2年くらいのことを振り返っていると、わりと不満点も書いていたので割愛しておく。ざっくり書くと、最初は会社のやり方に従って制作し、1年経った頃に効率化のために自分のやり方も取り入れさせてもらい、1年半経ったくらいからフレームワーク選定やサーバ構築の方針など技術的なことの諸々は自分が決めるようになり、2年経った頃に自分が技術責任者となっていた。神は色んな面で残念な感じだった。
そして先日、3年経った。

会社の雰囲気は良いのだと思う。派遣の技術者さんが大抵1~3人くらい、アルバイトさんが1~3人くらい居てくれているけど、「こんな和気あいあいとした会社は少ない」と何度か言われている。お世辞が入っているかどうかは知らない。
ただ、最近は会社が今後の方針に迷っている感がある。数年後、「この会社はどうなっているんだろう」と考えてしまう。とは言え自分も「だからこうすべきだ」と明確な答えを持っているわけでも無いので、難しいところだけど。
あと、労働時間が長いのも悩みのタネではある。

自分のすべてをかけて会社に尽くすのか、他の会社への転職を目指すのか、フリーに戻るのか。どれも現実感がない選択肢に見えてしまう。
1つめについてはちょっと疲れた感が否めない。技術責任者とは書いたが自分ですべて決定して自分ですべて進めるわけではまったくなく、頼れる人には頼ったり場合によっては丸投げでお願いしたりすることもある。それでも、一人で仕事をしていたときに比べると常に余裕がない。
2つめと3つめは今更すぎる気がする。

今は、個人のときにはあまり触れなかった(というより避け気味だった)サーバ構築なども行うようになったけど、単純に技術力で言えば自分は全然大したことは無い。毎朝はてブを巡回しているけど、「新卒向けに作った資料です」の内容が高度すぎて焦るとかはよくある。
ただ、「整理能力に長けている」「依頼したら抜けがない」「揉めないしトラブルなく案件を回せる」と言って褒めてもらうことが他社さん含めて何度もあったので、そっち方面を伸ばせるようにする方が良いのかもしれない。(とはいえ実際はたまに抜けるし、トラブったことも無いわけでは無い。)

少なくとも、技術面重視で行くのは良くないだろう。「プログラマ35歳定年説」みたいな話はよく聞くけど、イチ依頼者&管理者の立場としても35歳あたりからやりづらさを感じることが増える。仮に技術的にすごくても、一筋縄ではいかないというか。フリーに戻って一人で作るようになると、僕もややこしい人になりそうな気がするのは怖い。
もちろん例外な人もたくさんいて、諸々の歴史的事情やクライアントさんの都合も汲み取った上で「こんな風にするのはどうでしょうか?」と控えめに提案してくれて、それがとても勉強になることもある。が、理想を言えばそういう頼りになる人は作業依頼先よりも管理とか責任者とか上司的なところに居てほしいと思う。そして、技術面もそれ以外も頼りたい。

「石の上にも三年」という言葉があるので、3年間は何があっても辞めずに仕事に専念しようと思っていた。なので個人制作も控えめにしていたけど、いろんな道を考えられるようにするためにも、個人制作や勉強会の参加なども再開したいと思う。辞める前提とかでは全然無いし、むしろ回り回って会社の利益になると思っている。
時間を作れるのかは不明だけど、作ろうと思えばきっと何とかなるだろう。それに去年は忙しすぎたけど、今年はいくらかマシになる…はず。(願望)
細かいことは置いておいて、やっぱり何か作るのは楽しい。個人で作っていると、反応がダイレクトに来るのも面白い。仕事が制作ではなく管理がメインでも、自分でまったく作らなくなるとつまらなくなりそうなので、個人制作で発散したい。
とりあえず、いったん過去に作ったものをざっとメンテナンス&整理したい。Swiftいじりながら。

つらつら書いたけど、3年という(自分の中の)節目に振り返りのエントリー。何を言いたいというより、少し方向転換するので振り返りたかっただけ。

Android版GPUImageで画像加工

画像や動画にエフェクトをかけることができるライブラリである、GPUImageをAndroidから使ってみたのでメモ。

サンプル実行

NDK(Android Native Development Kit)のインストールが必要

GradleでNDKを自動インストールしてくれるプロジェクトもあるが、GPUImage for Android のサンプルはそうなっていない。Android Studio で

ファイル → 設定 → SDK Tools → NDK

にチェックを入れて適用すると、NDKがインストールされる。インストールされたら

C:\Users\ユーザ名\AndroidStudioProjects\GPUImage\local.properties

ndk.dir=C\:\\Users\\ユーザ名\\AppData\\Local\\Android\\Sdk\\ndk-bundle

のパスが正しいことを確認する。

さらに

C:\Users\ユーザ名\AndroidStudioProjects\GPUImage\library\build.gradle

の12行目を編集。(gitからバージョンコードを取得している?ようだが、取れずにエラーになるので適当な数値を設定)

versionCode "git rev-list origin/master --count".execute().text.toInteger()
↓
versionCode 1

まだ謎エラーが出るようなら、

ビルド → Clean Project
ビルド → Rebuild Project

を試す。問題がなくなると追加ライブラリのインストールを求められたのでインストール。その後実行すると、サンプルを実機にインストールできた。

プロジェクトに組み込み

C:\Users\ユーザ名\AndroidStudioProjects\プロジェクト名\app\build.gradle

のdependenciesに以下を追加

//GPUImage
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'

これだけでプロジェクトにGPUImageが組み込まれるので、あとは呼び出すだけ。以下Intentから画像を選択して、その結果にフィルタをかけて表示するサンプルの抜粋。

// Intentを取得
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_GALLERY);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_GALLERY && resultCode == RESULT_OK) try {
        InputStream in = getContentResolver().openInputStream(data.getData());
        Bitmap img = BitmapFactory.decodeStream(in);
        in.close();

        // GPUImageで画像を加工
        GPUImage mGPUImage = new GPUImage(this);
        mGPUImage.setFilter(new GPUImageSobelEdgeDetection());
        mGPUImage.setImage(img);
        img = mGPUImage.getBitmapWithFilterApplied();

        // 画像を表示
        imageViewer.setImageBitmap(img);

        Toast.makeText(MainActivity.this, "画像を選択しました", Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

AndroidでHTTP通信

2年くらい前にAndroidをさわったときは org.apache.http でHTTPリクエストしていたけど、今は使えなくなっているらしい。

どうやら、OkHttpというのを使うのがイマドキらしい。ので試してみた。

確かに org.apache.http を使うよりも、超シンプルなコードになるのでいい感じ。以前作った org.apache.http を簡単に扱えるようにするクラスが用済みになってしまったけど、無事に通信できたので良しとする。

以下、上に書かれた内容と同じだけど自分の作業メモ。

compile 'com.squareup.okhttp:okhttp:2.5.0'

でライブラリを取り込んで、マニフェストファイルで

<uses-permission android:name="android.permission.INTERNET" />

を指定して、以下のコードで通信テスト。リソースファイルは略。

package org.refirio.request;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.IOException;

public class MainActivity extends Activity {

    private TextView message;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        message = (TextView)this.findViewById(R.id.message);

        // データ取得(GET)
        Button button_get = (Button)this.findViewById(R.id.button_get);
        button_get.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                new AsyncTask<Void, Void, String>() {
                    @Override
                    protected String doInBackground(Void... params) {
                        String result = null;

                        // リクエストオブジェクトを作って
                        Request request = new Request.Builder()
                                .url("http://httpbin.org/headers")
                                .get()
                                .build();

                        // クライアントオブジェクトを作って
                        OkHttpClient client = new OkHttpClient();

                        // リクエストして結果を受け取って
                        try {
                            Response response = client.newCall(request).execute();
                            result = response.body().string();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        // 返す
                        return result;
                    }

                    @Override
                    protected void onPostExecute(String result) {
                        message.setText("Result: " + result);
                    }
                }.execute();

            }

        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Google App EngineでPHPを動作させるメモ

Google App Engineを少しだけ触ってみたのでメモ。

参考サイト

参考にはなったけど、色々端折られた個人メモみたいなので、自分用に改めてメモ。

環境構築

あらかじめWindowsに、Python2.7.xをインストールしておく。(GAEのツール自体がPythonで書かれているため、PHPを動作させる場合でも必要。)

プロジェクト作成

https://console.developers.google.com/project から、プロジェクト名「GAE Test Project」、プロジェクトID「refirio-php-test」で新規にプロジェクトを作成する。(プロジェクト名とプロジェクトIDは任意のものを指定する。)

次に https://console.developers.google.com/project/refirio-php-test/start/appengine にアクセスして解説に従う。

Google App Engine SDK for PHP をインストールし、サンプルコードをダウンロードする。サンプルコードは C:\localhost\home\gae\public_html\refirio-php-test とか、ローカルの適当なディレクトリ内に配置する。

次にサンプルコードを編集する。.playground の内容は以下のとおり。

{
  "project_name": "PHP TEST - refirio",
  "project_description": "Starter code for a Google App Engine app in PHP.",
  "show_files": [
    "helloworld.php",
    "app.yaml"
  ],
  "read_only_demo_url": "https://refirio-php-test.appspot.com/",
  "download_filename": "appengine-try-php.zip"
}

app.yaml の内容は以下のとおり。

application: refirio-php-test
version: 1
runtime: php55
api_version: 1

handlers:
- url: /.*
  script: helloworld.php

helloworld.php の内容は以下のとおり。

<?php

echo 'Hello, GAE!';

公開

デスクトップに作成されたショートカットから「Google App Engine Launcher」を起動する。ここからローカル環境でのテスト実行や、本番環境へのデプロイができる。

デプロイが完了すると、以下のURLからアクセスできる。

Basic認証設定ツール

100番煎じどころではないと思われるツールを作ったので置いておきます。

Basic認証をかけたいディレクトリ内に basic.php などの名前でサーバにアップロードしてアクセスすると、作るべき .htaccess.htpasswd の場所と内容を表示します。先頭に並んでいる test1 とかの部分は、認証させるユーザ名とパスワードです。適当に変更&増減できます。

.htaccess の雛形を作ってくれたり .htpasswd の内容を作ってくれたりするツールは多いけど、AuthUserFile の内容を調べたりするのも面倒なのでそれも自動で。_(:3 」∠ )_

<?php

$users = array(
  'test1' => '1234',
  'test2' => '2345',
  'test3' => '3456',
);
$path = dirname($_SERVER['SCRIPT_FILENAME']);

?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Basic Authentication</title>
</head>
<body>
<h1>Basic Authentication</h1>
<h2><?php h($path) ?>/.htaccess</h2>
<pre>AuthUserFile <?php h($path) ?>/.htpasswd
AuthGroupFile /dev/null
AuthName "Basic"
AuthType Basic
require valid-user
&lt;Files ~ "^.(htpasswd|htaccess)$"&gt;
deny from all
&lt;/Files&gt;</pre>
<h2><?php h($path) ?>/.htpasswd</h2>
<pre><?php

foreach ($users as $username => $password) {
  t($username . ':' . crypt($password) . "\n");
}

?></pre>
</body>
</html>
<?php

function h($data)
{
  $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
  $data = nl2br($data);

  echo $data;
}
function t($data)
{
  $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');

  echo $data;
}

?>

Webアプリケーションへの同時アクセス対策メモ

Webアプリケーションへの同時アクセス対策メモ

XAMPPの Apache Bench で同時アクセスを再現するメモ。テストした環境はWindows7。Apache Bench の使い方は以下を参考に。

XAMPPをCドライブ直下にインストールした場合、以下の手順で Apache Bench を使える。

> cd C:\xampp\apache\bin
> ab -n 15 -c 15 http://localhost/~test/ab/

「-n 15 -c 15」で「15人が同時にアクセスする(合計15回アクセス)」状況がテストできる。(「-n 15 -c 1」なら「1人が合計15回アクセスする」状況になるので、同時アクセスにはならない。)

以下で同時アクセスのテストとその対策を行う。

ファイルを使った例

<?php

/*
 * No.2をカウントアップ(最大値100)
 */

if ($fp = fopen('logs/count.log', 'r+')) {
  $data = '';
  while ($line = fgets($fp)) {
    list($id, $count) = explode("\t", rtrim($line));

    if ($id == 2) {
      if ($count < 100) {
        $data .= "$id\t" . ++$count . "\n";
      } else {
        exit('Limit');
      }
    } else {
      $data .= $line;
    }
  }

  rewind($fp);
  fwrite($fp, $data);
  ftruncate($fp, ftell($fp));

  fclose($fp);
} else {
  exit('Error');
}

exit('Complete');

logs/count.log の内容は以下のとおり。

1	10
2	20
3	30
4	40
5	50

これで上のPHPプログラムにアクセスすると、2の行が1カウントアップされる。何度もリロードすると、その回数だけカウントアップされる。

> ab -n 30 -c 30 http://localhost/~test/ab/count.php

30人の同時アクセスを発生させてみると、正しくカウントできないのが確認できる。

<?php

/*
 * No.2をカウントアップ(最大値100)
 */

if ($fp = fopen('logs/count.log', 'r+')) {
  flock($fp, LOCK_EX);  //ファイルロック

  $data = '';
  while ($line = fgets($fp)) {
    list($id, $count) = explode("\t", rtrim($line));

    if ($id == 2) {
      if ($count < 100) {
        $data .= "$id\t" . ++$count . "\n";
      } else {
        exit('Limit');
      }
    } else {
      $data .= $line;
    }
  }

  rewind($fp);
  fwrite($fp, $data);
  ftruncate($fp, ftell($fp));

  fclose($fp);
} else {
  exit('Error');
}

exit('Complete');

ファイルロックを行えば、正しくカウントされるようになる。

データベースを使った例

<?php

/*
 * No.2をカウントアップ(最大値100)
 */

try {
  $pdo = new PDO('mysql:dbname=test;host=localhost', 'root', '1234', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
  $pdo->query('SET NAMES utf8');

  $stmt = $pdo->prepare('SELECT count FROM count_test WHERE id = :id');
  $stmt->bindValue(':id', 2, PDO::PARAM_INT);
  $stmt->execute();

  $data = $stmt->fetch(PDO::FETCH_ASSOC);

  if ($data['count'] < 100) {
    $stmt = $pdo->prepare('UPDATE count_test SET count = count + 1 WHERE id = :id');
    $stmt->bindValue(':id', 2, PDO::PARAM_INT);
    $stmt->execute();
  }
} catch (PDOException $e) {
  exit($e->getMessage());
}

exit('Complete');

count_test の定義は以下のとおり。

CREATE TABLE count_test(
  id    INT UNSIGNED NOT NULL,
  count INT UNSIGNED,
  PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

これで上のPHPプログラムにアクセスすると、2の行が1カウントアップされる。何度もリロードすると、その回数だけカウントアップされる。

ab -n 200 -c 200 http://localhost/~test/ab/count.php

200人の同時アクセスを発生させてみると、正しくカウントできないのが確認できる。

<?php

/*
 * No.2をカウントアップ(最大値100)
 */

try {
  $pdo = new PDO('mysql:dbname=test;host=localhost', 'root', '1234', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
  $pdo->query('SET NAMES utf8');

  $pdo->beginTransaction();  //トランザクション開始

  $stmt = $pdo->prepare('SELECT count FROM count_test WHERE id = :id FOR UPDATE');  //行ロック
  $stmt->bindValue(':id', 2, PDO::PARAM_INT);
  $stmt->execute();

  $data = $stmt->fetch(PDO::FETCH_ASSOC);

  if ($data['count'] < 100) {
    $stmt = $pdo->prepare('UPDATE count_test SET count = count + 1 WHERE id = :id');
    $stmt->bindValue(':id', 2, PDO::PARAM_INT);
    $stmt->execute();
  }

  $pdo->commit();  //コミット
} catch (PDOException $e) {
  $pdo->rollBack();  //ロールバック

  exit($e->getMessage());
}

exit('Complete');

トランザクションと行ロックを行えば、正しくカウントされるようになる。

電話発信用のリンクをPCページで解除

スマートフォン向けページでは、tel: でリンクすれば電話をかけることができます。

<a href="tel:090-1234-5678">090-1234-5678</a>

ただしこの機能はPCサイトでは不要です。この対策に、以下のJavaScriptで電話番号のリンクを解除することができます。

var ua = navigator.userAgent;
if (ua.indexOf('iPhone') < 0 && ua.indexOf('Android') < 0) {
  $('a[href^="tel:"]').each(function() {
    $(this).contents().unwrap();
  });
}

処理の内容は「iPhoneでもAndroidでもなければ、リンク先が tel: が始まるリンクを解除する」としています。

Windows Phone も考慮するならuserAgentの判定は要改良ですが、ひとまずこれで。

UNIXコマンドでサーバ監視して結果をグラフ表示

サーバの監視は普段Zabbixで「Load Average」「CUP Utilization」「Memory」「Disk Space」「Traffic」を監視しているけど、これらを監視する仕組みを一から自作したのでメモ。言わば超簡易Zabbix。

作る理由はただの興味。実際の監視は引き続きZabbixを使うのです。(`・ω・´)

大まかな方針は以下のとおり。

  • uptime などのUNIXコマンドをcronで5分ごとに実行して、結果をファイルに記録する
  • 結果をPHPプログラムで集計する
  • jqPlotでグラフ表示

監視には以下のコマンドを使ってみます。他にもっと適切なコマンドとかがあれば、こっそり教えてくれると喜びます。

■Load Average

$ uptime
 00:00:01 up 170 days,  5:24,  0 users,  load average: 0.00, 0.00, 0.00

■CUP Utilization

$ mpstat 10 5
Linux 2.6.32-504.1.3.el6.x86_64 (www.example.com) 	2015年05月23日 	_x86_64_	(2 CPU)

00時00分01秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
00時00分11秒  all    7.74    0.00    1.40    1.35    0.00    0.05    0.65    0.00   88.81
00時00分21秒  all    0.55    0.00    0.40    0.05    0.00    0.00    0.15    0.00   98.85
00時00分31秒  all    0.15    0.00    0.15    1.25    0.00    0.00    0.25    0.00   98.20
00時00分41秒  all    0.20    0.00    0.30    0.65    0.00    0.05    0.15    0.00   98.65
00時00分51秒  all    0.15    0.00    0.20    0.05    0.00    0.00    0.30    0.00   99.30
平均値:   all    1.76    0.00    0.49    0.67    0.00    0.02    0.30    0.00   96.76

■Memory

$ vmstat 10 5
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 7  2 259344 124540 174536 274200    3    1    56    63    1    1  1  0 97  1  1	
 0  0 259344 100656 174536 274276    0    0     0   236  383  269  8  1 89  1  1	
 0  0 259344 129760 174536 274272    0    0     0   209  148  155  1  0 99  0  0	
 0  0 259344 129884 174536 274272    0    0     0   233  121  143  0  0 98  1  0	
 0  0 259344 129760 174544 274272    0    0     0   324  127  141  0  0 99  1  0	

■Disk Space

$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/vda3      100762004 4012036  91624800   5% /
tmpfs             510084       0    510084   0% /dev/shm
/dev/vda1         243823  130875    100148  57% /boot

■Traffic

$ cat /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo: 22940909905 393345389    0    0    0     0          0         0 22940909905 393345389    0    0    0     0       0          0
  eth0: 20118935741 311131513    0    0    0     0          0         0 517575153 2149502    0    0    0     0       0          0
  eth1:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
  eth2:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

vmstatmpstat については、計測間隔と計測回数を指定しないと意図した値を得られなかった。また、/proc/net/dev に記録されているのは総トラフィック量なので、その時のトラフィック量を調べるには差分を計算する必要がある。

以下、具体的な記録方法。

まずはroot権限で、コマンドの結果を格納する場所を作ります。また、結果ファイルをPHPから読み出せるようにしておきます。(PHPはwebmasterの権限で実行するという想定。)

# cd /var/log
# mkdir command
# chown webmaster. command

以降はwebmasterユーザで作業。まずは command 内に、コマンドの結果を格納する場所を作ります。

$ mkdir uptime
$ mkdir mpstat_10_5
$ mkdir vmstat_10_5
$ mkdir df
$ mkdir cat_proc_net_dev

結果は /var/log/command/uptime/20150307/2215.log のようなディレクトリ名・ファイル名で格納するものとします。試しにコマンドを実行。

$ mkdir /var/log/command/uptime/`date +'%Y%m%d'`/
$ uptime > /var/log/command/uptime/`date +'%Y%m%d'`/`date +'%H%M'`.log

$ mkdir /var/log/command/mpstat_10_5/`date +'%Y%m%d'`/
$ mpstat 10 5 > /var/log/command/mpstat_10_5/`date +'%Y%m%d'`/`date +'%H%M'`.log

$ mkdir /var/log/command/vmstat_10_5/`date +'%Y%m%d'`/
$ vmstat 10 5 > /var/log/command/vmstat_10_5/`date +'%Y%m%d'`/`date +'%H%M'`.log

$ mkdir /var/log/command/df/`date +'%Y%m%d'`/
$ df > /var/log/command/df/`date +'%Y%m%d'`/`date +'%H%M'`.log

$ mkdir /var/log/command/cat_proc_net_dev/`date +'%Y%m%d'`/
$ cat /proc/net/dev > /var/log/command/cat_proc_net_dev/`date +'%Y%m%d'`/`date +'%H%M'`.log

問題なければ、これらのコマンドをcronに登録。(mkdir では、翌日用のディレクトリを作成するように変更しているので注意。当日用のディレクトリを作成して即結果を保存、は「ディレクトリがありません」のエラーになったため。)

# vi /etc/crontab

00 00 * * * webmaster mkdir /var/log/command/uptime/`date -d '1 days' +'\%Y\%m\%d'`/
*/5 * * * * webmaster uptime > /var/log/command/uptime/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log

00 00 * * * webmaster mkdir /var/log/command/mpstat_10_5/`date -d '1 days' +'\%Y\%m\%d'`/
*/5 * * * * webmaster mpstat 10 5 > /var/log/command/mpstat_10_5/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log

00 00 * * * webmaster mkdir /var/log/command/vmstat_10_5/`date -d '1 days' +'\%Y\%m\%d'`/
*/5 * * * * webmaster vmstat 10 5 > /var/log/command/vmstat_10_5/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log

00 00 * * * webmaster mkdir /var/log/command/df/`date -d '1 days' +'\%Y\%m\%d'`/
*/5 * * * * webmaster df > /var/log/command/df/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log

00 00 * * * webmaster mkdir /var/log/command/cat_proc_net_dev/`date -d '1 days' +'\%Y\%m\%d'`/
*/5 * * * * webmaster cat /proc/net/dev > /var/log/command/cat_proc_net_dev/`date +'\%Y\%m\%d'`/`date +'\%H\%M'`.log

これでコマンドの実行結果がどんどん記録されていくので、あとはPHPで読み出すプログラムを作成すればOK。

Windows + コマンドプロンプト + PHPUnit でユニットテスト

今更ながらPHPUnitを試したのでメモ。

概要

PHPUnit3で始めるユニットテストが解りやすかったが情報が古い。でも、概要をつかむには良さそうです。

大まかな内容としては「◯◯の命令を△△の形式で呼び出して、□□の結果が返ってくればOK」というコードをたくさん書いたプログラムを準備しておき、それを実行すれば各命令が正しく実行されたかを知ることができるというもの。(プログラム作成中に不具合を作ってしまっても、即座に検知できる…かもしれない。)

大人数で各々が大量の部品を作るような開発では、上手く導入するととても活躍してくれそう。一人/少人数で作成しているようなプログラムでは、使いどころが難しいかも。(自分が解っていないだけかも。)

導入自体は簡単。

導入

自分の環境に合わせて「Windows+コマンドプロンプト+PHPUnit」でPHPプログラムのテストをしてみたので、手順をメモ。PHPUnit のインストールを主に参考にしています。

まずは公式サイトから phpunit.phar の安定版を入手する。

任意のディレクトリ内(C:\xxx\phpunit とする)に、入手した phpunit.phar を配置する。

C:\xxx\phpunit 内に phpunit.cmd を作成し、以下の内容を記述する。(PHPへのパスは環境に合わせて設定する。)

@C:\xampp\php\php.exe "%~dp0phpunit.phar" %*

コマンドプロンプトで C:\xxx\phpunit 内に移動し、以下のコマンドを実行。以下のようにバージョン情報が表示されれば成功。

C:\xxx\phpunit>phpunit --version
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

サンプルで動作確認

Getting Started with PHPUnit を参考にテスト。

上ページの内容を参考に C:\xxx\phpunit\src\Money.php (テストしたいクラス)を作成。次に C:\xxx\phpunit\tests\MoneyTest.php (テスト用プログラム)も作成。

コマンドプロンプトから、以下のようにコマンドを実行するとテストできる。

C:\xxx\phpunit>phpunit --bootstrap src/Money.php tests/MoneyTest
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

.

Time: 31 ms, Memory: 4.00Mb

OK (1 test, 1 assertion)

上のように表示されればテスト成功。

自作のプログラムで動作確認

PHPUnit 用のテストの書き方 を参考にテスト。

まずは C:\xxx\phpunit\src\calculate.php を作成し、掛け算と割り算の計算結果を返す命令を定義。これらの命令が正しく動作しているかテストするものとする。

<?php
function multiplication($x, $y) {
  return $x * $y;
}
function division($x, $y) {
  return $x / $y;
}

C:\xxx\phpunit\tests\calculate.php を作成し、各命令をテストするコードを書く。(計算結果が正しく返ってくればテスト成功とする。)

<?php
require_once 'C:/localhost/home/test/public_html/phpunit/src/calculate.php';
class Calculate extends PHPUnit_Framework_TestCase
{
    public function testCalculate()
    {
        $this->assertEquals(8, multiplication(4, 2));
        $this->assertEquals(2, division(4, 2));
    }
}

コマンドプロンプトからテスト実行。

C:\xxx\phpunit>phpunit tests/calculate
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

.

Time: 30 ms, Memory: 4.00Mb

OK (1 test, 2 assertions)

上のように表示されればテスト成功。試しに関数の内容を間違ったものにしてテストすると。

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

のような結果が表示されるので、正しくテストできていることが確認できる。

ページ移動

ユーティリティ

カテゴリー

検索

エントリー検索フォーム
キーワード

過去ログ

過去ログ表示フォーム
キーワード

Feed