今日も適当ダイアリー

PHP や Javascript や Symfony、BEAR.Sunday などのWeb周りのことを中心に。それ以外のことも気まぐれに投稿します。

色をアルファブレンドする

先日このブログで relColors という関連色を表示するための自作ツールをローンチしたこと書いたので、それについての話題です。
関連色を簡単に見つけられるツール「relColors」作ってみました。 - 今日も適当ダイアリー

当初の予定では、PHPのソースを公開するつもりだったのですが、勢いで作った感じで、なんとも汚いソースだったので、公開はいったん見送って、一部をここに書いていこうと思います。

(自分を含めて)なんかの時に使えるかもしれないし。てことで、今回は色をアルファブレンドしてみます。

アルファブレンドって、なんとなく特殊な処理をするのかなー、なんて思っていたりしたのですが、アルファブレンド - Wikipediaにもあるとおり、非常に単純明快なアルゴリズムで計算できました。

Value = Value0 × (1.0 − α) + Value1 × α

ただし、Value0が背景でValue1が前景である。αはα値と呼ばれる0〜1の値を取る係数でどれくらい透過させるかを表わす値である。αが、1のとき完全な不透過であり、0のとき完全な透明となる。このαがアルファチャンネルまたはマスク画像に相当する。

拍子抜けするほど、簡単です。
PHPで関数として書くと下記のような感じになります。

<?php

/**
 * 色をアルファブレンドする
 *
 * @param   array   $frontColor 前景色 (array(r, g, b) : rgb は 0〜255)
 * @param   array   $backColor  背景色 (array(r, g, b) : rgb は 0〜255)
 * @param   int     $alpha      アルファ値 (0 : 透明 〜 255 : 不透明)
 * @return  array   アルファブレンドされた色 (array(r, g, b) : rgb は 0〜255)
 */
function alphaBrend($frontColor, $backColor, $alpha)
{
    // アルファ率を 0..1 に変換
    $a = $alpha / 255;

    // 0..1の範囲に無い場合は、納める
    if ($a > 1) {
        $a = 1;
    } else if ($a < 0) {
        $a = 0;
    }

    return array(
        (int) $backColor[0] * (1 - $a) + $frontColor[0] * $a,
        (int) $backColor[1] * (1 - $a) + $frontColor[1] * $a,
        (int) $backColor[2] * (1 - $a) + $frontColor[2] * $a,
    );
}

特に言うことが無いですね。。。

関連色を簡単に見つけられるツール「relColors」作ってみました。

http://www.weekendphp.com/relColors/

デザインの色に悩むことって結構ある。基本色や背景色、前景色が決まっていても、その中間色を見つけたり、ちょっと色味の違う色を見つけるのに、Photoshopを立ち上げたり、専用のツールを起動したり……。

http://0to255.com/ を発見して、使っていたんだけど、このサイトだと、白→基準色→黒っていうパターンだけしか生成してくれない。

背景色→基準色→前景色だとか、色相(hue)違いの色も出して欲しいな〜、なんて思ったので、作ってみました。

もし良かったらどうぞ。

http://www.weekendphp.com/relColors/

PHPとMongoDBでセッション管理してみる

補足(2010-08-28追記):
下記 mongoSession クラスは一部のPHPバージョンでAPCを使用している場合、正常に動作しない場合があるようです。(静的メソッドがキャッシュに乗らない場合がある?) PHPAPCでバグ報告がされており、現行バージョンでは解決済み、とのレスもありますが、その解決済みバージョンを使用しても直らない、という人もいるようです。

前回、MongoDBとPHP mongoモジュールのインストールをしたので、PHPからmongoを操作してみたいと思います。
CentOS5.5にMongoDBをインストールしてみる - 今日も適当ダイアリー

何がいいかなぁ、と考えてみたのですが、セッションハンドラを書いてみる事にしてみます。
PHPのセッションは、デフォルトではファイル管理となりますが、それだと、冗長化構成時にネットワークディスクを使ったりしなければならなかったり問題になる場合があります。
やっぱ、同一の情報に複数のサーバーからアクセスするんなら、DBだよね。ってわけで、DBで、セッション管理をすることになるのですが、今回はMongoDBでセッション情報を保存するようなハンドラを作りたいと思います。

PHP Mongoエクステンションの詳細については、http://jp.php.net/mongoで確認してください。

続きを読む

CentOS5.5にMongoDBをインストールしてみる

この記事で説明している yum リポジトリはすでに使えなくなっています。
下記記事で、改めて説明を行っていますので、そちらも併せてご覧いただければと思います。
http://blog.madapaja.net/2011/09/20-mongodb-1-mongodbjp.html

ドキュメント指向データベースのMongoDBが面白そうだから、インストールしてみます。

続きを読む

相対パスから絶対URL(http://〜〜)への変換

とあるURLの相対パス(./ とか ../../hoge/ とか)から絶対URL(http://〜〜)の情報を取得したいような場合、PHPではそのような関数などはないため、自力でなんとか解決しなければなりません。

自分のために作ったけど、自分のための備忘録代わりに投稿しておきます。

ソースは以下の通り。

続きを読む

プログラム言語を始める時にはフレームワークに手を出さない

PHPに限らず、プログラム言語に手を出すときには、特定のフレームワークから入らずに、純粋に言語から勉強し始める事をお勧めします。
昨今のフレームワークは手間を減らし、安全で保守のしやすいプログラムを開発しやすくするために、多くの機能を搭載しています。URLの処理や変数などのクリーニング、DB操作に加えて、スケルトン(雛形)の自動生成機能なども搭載しているため、確かにいったんそのフレームワークを習得し、正しく利用すれば、手間の軽減と、安全性の確保が出来るように設計されています。
本来行わなくてはいけない処理をフレームワーク側で(半)自動的に行ってくれているため、その言語に固有の問題や、ちょっとしたテクニックを習得する機会が失われてしまうのも事実です。
もちろん、そういった行わなければいけない処理や言語自体の癖などを理解しているプログラマが使う分には問題ないのですが、初めて触れる言語の習得の際に、特定のフレームワークから入ると、それらを理解しないまま使う事になるため、ちょっと深い操作などを行おうとした際に躓いたり、問題のあるソースを書く可能性が高くなります。
ある言語を始める際には、フレームワークに拠らないプログラミングから勉強し始めましょう。

PHPは型がないのではなく、自動型変換を行ってくれる言語です

PHPの長所と短所の両方としてあげられる項目として、「PHPは型を意識せずにプログラミングできる」というようなことが言われる場合があります。
PHPは、基本的に場合に応じて、型を自動的に変換(キャスト)するため、初心者にとって入りやすいポイント、とも言えるし、何らかの事態の際にはまりやすいポイントとも言えると思う。
ここで大切なのは、型がない訳ではなく、内部的に自動的に変換しているだけで、実際には明確に型がある、ということだ。
たとえば、こんなソースを書いたとしよう。

<?php
    // search_word に値があれば
    if ($_GET['search_word']) {
        // 処理を行う
    }

条件判定文で、変数に値があるかどうかを判定しているのだが、この_GET変数はGET値が自動的に代入されているため、文字列型(string)(もしくは未定義値)が入る。
で、この変数が、未定義だった場合や、空文字("")の場合は、($_GET['search_word']) は false になるので、正常な挙動である。
ただし、"0"が入っていた場合も false になってしまうのである。
PHP: 論理型 (boolean) - Manual
"0"の場合は true になるべきなら、

<?php
    // search_word に値があれば
    if ($_GET['search_word'] != "") {
        // 処理を行う
    }

としなければならない。

なお、下記のように、数値形式の文字列の場合は、数値として解釈され、両方ともtureになってしまうので、注意が必要だ。

<?php
    if ("58" == "0x3A") {
        echo "true";
    }

    if ("0.01" == "1e-2") {
        echo "true";
    }

このような場合には、「===」(値と型の両方が一致する=自動的な型変換が行われずに評価される)演算子を使わなければならない。


また、数値と文字列の比較も注意が必要。

<?php
    if (0 == "hoge") {
        echo "true";
    }

    if (1 == "1hoge") {
        echo "true";
    }

    if (1 == " 1hoge") {
        echo "true";
    }

    if (1 == "1.0hoge") {
        echo "true";
    }

    if (1 == " 1hoge") {
        echo "true";
    }

    if (58 == "0x3Ahoge") {
        echo "true";
    }

これらは全てtrueに評価される。
なぜなら、数値と文字列の比較の場合は、文字列を数値に型変換して比較が行われるからである。
文字列の数値への変換規則については、PHP: 文字列 - Manualを参照しよう。

これらを回避するためには、状況に応じて、「===」を使うとか、数値をstring型へキャストするとか、strcmp関数を使うとかする必要がある。


意外に、この自動的な型変換ではまるケースがあるので、きちんと意識する必要がある。
きちんと、型の比較について理解しよう。
http://jp.php.net/manual/ja/types.comparisons.php

PHPでシングルトンを利用

デザインパターンのひとつに、シングルトンパターンというものがあります。

これは、アプリケーションに対して、一度に1つしかオブジェクトのインスタンスを許可したくない場合に使うもので、例えば、アプリケーション内で1つのデータベースハンドルを使いまわす、とかそういうような用途に使えるものです。

PHPのシングルトンパターンについてググると、下記のようなサンプルが出てきます。
(若干違う場合があるかも知れませんが、大枠は同じ形のはずです。)

<?php

class Singleton
{

    /** @var object 自分自身のインタンスを保持 */
    private static $instance = null;

    /**
     * コンストラクタ
     * private なので、自分自身以外からはアクセス付加
     * (このクラス外で、$obj = new Singleton(); とすると、エラーになる)
     */
    private function __construct() {
    }

    /**
     * このクラスのインスタンスを取得する
     *
     * @return object このクラスのインスタンス
     */
    public static function get()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function message()
    {
        echo "This is Singleton Class.<br />\n";
    }
}

$obj = Singleton::get();
$obj->message();

多くのサイトで、インスタンスを取得する関数(上記では、Singleton::get())を使って取得したインスタンスを変数(上記では$obj)に代入してから、その変数を使い、操作をしています。
もちろん、これはやり方の1つなのですが、

<?php
$obj = Singleton::get();
$obj->message();

の部分は、

<?php
Singleton::get()->message();

のように書くこともできます。

このように処理した方が、わかりやすい場合もありますので、こちらの方法も覚えておきましょう。

(使う、使わないは別にしても)いろんなやり方を知っておくといいです。

PHPでもif文の代わりに三項演算子(条件演算子)が利用できる

タイトルの通りなのですが、PHPでも三項演算子(条件演算子)が利用できるので、特定の場合では、if文を条件演算子に置き換えたほうが、可読性が増す場合があります。
そこで、三項演算子を知らない人のために、ここでちょっと解説しておきます。

条件 ? 真の場合の値 : 偽の場合の値

構文は上記の通りです。
で、これを利用すると条件によって異なる値を変数などに適用する際に、簡潔に記述することができます。

例えば、

<?php
    if (preg_match('\A\d+\z', $_GET['id'])) {
        // $_GET['id'] が数値のみなら、それを $id とする
        $id = $_GET['id'];
    } else {
        // 入力値が不正ならデフォルト値(1)
        $id = 1;
    }

のように、ユーザー入力のチェックを行い、入力チェックに通らなければ、デフォルト値を使うような記述を三項演算子で書き換えると、簡潔に記述することができます。

<?php
    $id = (preg_match('\A\d+\z', $_GET['id'])) ? $_GET['id'] : 1;

もちろん、return 文などでも使うことが可能です。

<?php
    function getid() {
        return (preg_match('\A\d+\z', $_GET['id'])) ? $_GET['id'] : 1;
    }

ただし、あくまでも三項演算子は式なので、値としてはではなく、式の結果として評価されます。
よって、結果をリファレンスとして返す関数では下記のように記述する事はできません。(期待している動作は行われません。)

    function &get($str) {
        return (isset($_POST['mode'])) ? $_POST['mode'] : $_GET['mode']
    }

    $get = &get('id');

上記コードは、下記のように書けば、期待している通りに動作します。

    function &get($str) {
        if (isset($_POST[$str])) {
            return $_POST[$str];
        } else {
            return $_GET[$str];
        }
    }

    $get = &get('id');

PHP で生データから POST/GET の全データを取得する

PHP で POST を取得するには、通常、$_POST 変数が使われるが、下記のような配列になっていない フォーム の場合、同じ変数に格納しようとするため、全データを取得することができません。

<form method="post">
  <input value="AAAAA" name="a" />
  <inpu value="BBBBB" name="a" />
  <input value="CCCCC" name="a" />
  <input value="CCCCC" name="a" />
  <input value="D&E" name="a" />

  <input type="submit" />
</form>

もちろん、下記のように HTML にて、PHP で取得時に配列に格納されるよう name 属性を変更すれば取得できます。

<form method="post">
  <input value="AAAAA" name="a[]" />
  <inpu value="BBBBB" name="a[]" />
  <input value="CCCCC" name="a[]" />
  <input value="CCCCC" name="a[]" />
  <input value="D&E" name="a[]" />

  <input type="submit" />
</form>

ただ、最近の案件で HTML を変更できないケースがあり、そのときに使った関数を忘れないようにメモ。

<?php
// array jGetInputData( [ string $type , [ string $str , [ bool $del_null_byte ] ] ] )
//   $type          : 'POST' / 'GET'(デフォルトは POST)
//   $str           : 生データを指定したい場合
//   $del_null_byte : ヌルバイトを削除するか(デフォルトは TRUE)
//   return         : POST / GET データを配列で返します。
function jGetInputData($type = 'post', $str = null, $del_null_byte = true) {
  if ($str === null) {
    switch (strtolower($type)) {
      case 'get': // GET の生データを取得
        $str = $_SERVER["QUERY_STRING"];
        break;
      case 'post': // POST の生データを取得
      default:
        $str = file_get_contents("php://input");
    }
  }

  if (!is_string($str)) return false;
  if (trim($str) === '') return array(); // 何もなし

  // 「&」が含まれていれば分解
  $datas = (strpos($str, '&') === false) ? array($str) : explode('&', $str);
  $rtn = array();

  foreach($datas as $data) {
    $name = null;
    $val = null;

    if (strpos($data, '=') === false) {
      $name = urldecode($data);
    } else {
      $_tmp = explode('=', $data, 2);
      $name = urldecode($_tmp[0]);
      $val = urldecode($_tmp[1]);
    }

    if (!isset($rtn[$name])) {
      // アイテム名が存在しなければそのまま代入
      $rtn[$name] = $val;
    } else {
      if (!is_array($rtn[$name])) {
        // アイテム名の変数が存在し、
        // アイテム名の配列が存在しなければ配列にする
        $rtn[$name] = array($rtn[$name]);
      }
      // 新しい値を配列に追加
      $rtn[$name][] = $val;
    }
  }

  if ($del_null_byte) {
    $rtn = jDelNullbyte($rtn);
  }

  return $rtn;
}

function jDelNullbyte($data) {
    if (is_array($data)) {
        return array_map('jDelNullbyte', $data);
    }

    return str_replace("\0", '', $data);
}

使う場合は

<?php
  $_POST = jGetInputData();

のような感じで。

<form method="post">
  <input value="AAAAA" name="a" />
  <inpu value="BBBBB" name="a" />
  <input value="CCCCC" name="a" />
  <input value="CCCCC" name="a" />
  <input value="D&E" name="a" />
  <input value="F" name="a.b" />
  <input value="G" name="あいう=えお" />
  <input value="H" name="" />
  <input value="" name="I" />
  <input value="JJJJJ" name="a[]" />

  <input type="submit" />
</form>

に対して

<?php
  var_dump(jGetInputData());

をすると、

array(6) {
  ["a"]=>
  array(4) {
    [0]=>
    string(5) "AAAAA"
    [1]=>
    string(5) "CCCCC"
    [2]=>
    string(5) "CCCCC"
    [3]=>
    string(3) "D&E"
  }
  ["a.b"]=>
  string(1) "F"
  ["あいう=えお"]=>
  string(1) "G"
  [""]=>
  string(1) "H"
  ["I"]=>
  string(0) ""
  ["a[]"]=>
  string(5) "JJJJJ"
}

のように返ります。

何らかの事情で HTML を変更できない、もしくは変更せずに PHP からその値を取得したい場合があれば、自由に改変して使っていただいて結構です。

なお、上記関数を使用した場合、name 属性で使用した文字列がそのまま配列のアイテム名になり、[]を使用しても配列として展開されないので注意。

間違いがありましたら、コメントください。