HOKYPOKY.BLOG

PHPのデザインパターン

プログラムにおける常套句ってやつなのかな。
PHP以外のプログラムでも考え方として使えるからとりあえずここら辺はおさえておきたいところ。
ただコレを知ってれば良いってわけでもなく結局ケースバイケースだとは思うけど、技としてはこういうの大事ね。

【Singleton】
全体で同じ物を使い回すためのクラス。オブジェクトの数が1つ、または一定の少数の場合に使う。
例はDBのコネクション

class MyDbConnection {
    protected static $dbConnection;
    protected function __construct()
    {
        self::$dbConnection = new PDO("sqlite:db.sqlite","","");
    }
    public static function getDbConnection()
    {
        if(!self::$dbConnection) {
            self::$dbConnection = new self();
        }
        return self::$dbConnection;
    }
}

コンストラクタがprotectedになっている。
クラス自体をnewできないので、staticで保存された値が全インスタンスで共有される。

【Abstract Factory】
受け取った値や、現在の状況で返すオブジェクトを生成するためのクラス。
例は自動販売機

class Factory
{
    public static function createProduct($type, $name, $price)
    {
        switch($type) {
            case 'Cola' :
                return new Cola($name, $price);
                break;
            case 'Tea' :
                return new Tea($name, $price);
                break;
            default :
                return null;
                break;
        }
    }
}

もっとFactoryClassに条件分岐いれたりするといい。

$cocacola = Factory::createProduct("Cola", "コカコーラ", 120);
$heytea = Factory::createProduct("Tea", "お〜い!お茶",150);

【Composite】
再帰的な構造を表現する。

/* スーパークラス */
abstract class DirectoryFile
{
    protected $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function getName()
    {
        return $this->name;
    }
    abstract public function add(DirectoryFile $child);
    abstract public function remove(DirectoryFile $child);
    abstract public function getChildren();
    abstract public function display($level);
}
/* サブクラス - File */
class File extends DirectoryFile
{
    /* ファイルの場合,add/removeできない */
    public function add(DirectoryFile $child)
    {
        echo("ファイルには子要素は追加できません");
        return null;
    }
    public function remove(DirecotryFile $child)
    {
        echo("ファイルには子要素がありません")
        return null;
    }
    public function getChildren()
    {
        return null;
    }
    public function display($level)
    {
        echo (str_repeat('    ', $level) . $this->name . "\n");
    }
}
/* サブクラス - Dir */
class Dir extends DirectoryFile
{
    private $files;
    public function __construct($name)
    {
        praent::__construct($name);
        $this->files = array();
    }
    public function add(DirectoryFile $child)
    {
        $this->files[] = $child;
        return $this; //自分を返すことでチェーンできる。
    }
    public function remove(DirecotryFile $child)
    {
        return array_splice($this->files, array_search($child, $this->files, true), 1);
    }
    public function getChildren()
    {
        return $this->files;
    }
    public function display($level)
    {
        echo (str_repeat('    ', $level) . $this->name . "\n");
        foreach($this->files as $child)
        {
            $child->display($level + 1);
        }
    }
}

【Iterator】
反復を隠蔽するパターン。

IteratorとIteratorAggregateはPHPの内部実装なので書かなくてもいいですが、以下のようになっています。

interface Iterator
{
    public function rewind();
    public function next();
    public function current();
    public function valid();
    public function key();
}
interface IteratorAggregate
{
   public function getIterator();
}

Iteratorを使ったクラス定義をします。


class NominalList implements IteratorAggregate
{
    private $members;
    public function __construct()
    {
        $this->members = array();
    }
    public function add($member)
    {
        $this->members[] = $member;
    }
    public function getMember($index)
    {
        if ( $index < 0 || $index >= count($this->members) ) {
            return null;
        } else {
            return $this->members[$index];
        }
    }
    public function getIterator()
    {
        return new NominalListIterator($this);
    }
}
class NominalListIterator implements Iterator
{
    private $index = 0;
    private $obj;
    public function __construct(NominalList $obj)
    {
        $this->obj = $obj;
    }
    public function rewind()
    {
        $this->index = 0;
    }
    public function next()
    {
       $this->index += 1;
    }
    public function current()
    {
        return $this->obj->getMember($this->index);
    }
    public function valid()
    {
        return ( $this->obj->getMember($this->index) ? true : false );
    }
    public function key()
    {
        return $this->index;
    }
}

こうすることで、NominalListがIteratorインターフェイスに沿っているので、
NominalListにaddした値を取得することができます。

$nl = new NominalList();
$nl->add("foo");
$nl->add("bar");
$nl->add("hoge");

foreach($nl as $key => $val) {
    echo $key . " = " . $val . "\n";
}

Iteratorはちょっと難しいのでこの辺をみるといいかも。
http://d.hatena.ne.jp/hnw/20090523

PHPのpublic, protected, privateによるクラス変数の挙動について

以下がテストコード。
Mainクラスでそれぞれ$public, $protected, $privateを定義。
また、mainSetVal()というメソッドがある。
SubクラスはMainクラスを拡張したもの。
SubクラスはsubSetVal()というメソッドを追加した。
$instanceはsubクラスのインスタンス。

<?php //test private protected

class Master
{
    public      $_public;
    protected   $_protected;
    private     $_private;
    function masterSetVal ($val) {
        $this->_public    = $val;
        $this->_protected = $val;
        $this->_private   = $val;
    }
}
class Sub extends Master {
    function subSetVal ($val) {
        $this->_public    = $val;
        $this->_protected = $val;
        $this->_private   = $val;
    }
}

$instance = new Sub();

/**
 * use Master's method
 */
echo "-------------------------------------------------- masterSetVal\n";

$instance->masterSetVal("master");
var_dump($instance);

/**
 * use Sub's method
 */
echo "-------------------------------------------------- subSetVal\n";

$instance->subSetVal("sub");
var_dump($instance);

で、結果がこれ。

-------------------------------------------------- masterSetVal
object(Sub)#1 (3) {
  ["_public"]=>
  string(6) "master"
  ["_protected:protected"]=>
  string(6) "master"
  ["_private:private"]=>
  string(6) "master"
}
-------------------------------------------------- subSetVal
object(Sub)#1 (4) {
  ["_public"]=>
  string(3) "sub"
  ["_protected:protected"]=>
  string(3) "sub"
  ["_private:private"]=>
  string(6) "master"
  ["_private"]=>
  string(3) "sub"                  // public変数として増えてる。
}

子クラス内で、まぁpublicは変更されるとして
protectedはextendsしてるからこれもクラス内利用であれば変更されて
privateはMasterクラスでしか変更できない。しかもその場合子クラスにはpublicの変数があてられるので注意。

とりあえずPHPはインスタンスになるとなかなか拡張できないことがわかりやした。

  • 拡張はクラスのextendsでサブクラスを作る。
  • インスタンスにしたら基本オプション的な数値のみ変える。

ってことでいいのかな?
なんかなー、多重継承が気持ち悪い。たどるの大変。

function Class (){
    this.hello = "world";
    this.method = function () {
        alert(this.hello);
    }
}
var instance = new Class();
instance.method2 = function () {
    alert("method2");
}
instance.method();
instance.method2();

JavaScriptってやっぱこういうとこ良いよね。

関係ないけどビデオは涼宮ハルヒの憂鬱。
これさ、公式なんだってさ。ネットで著作権だなんだとか言ってるなか逆に公開していくスタイルはかっこいいと思う。それが成功するかはしらないけど。

テレビに対してネットがやった事は、絵に対して写真がやったようなもの。
残酷だけど、音楽業界もテレビ業界も、そのときになったらそのときの立ち振る舞いが有るのだと思う。
それはネットも同じ。

PHPで添付ファイル付きメールを送る

仕事でファイル付きメールを送るフォームを作成したときに、めちゃくちゃ調べたのでそのまとめ。

  1. input type=”file”でファイルアップロードができる。
  2. そのときformにenctype=”multipart/form-data”という属性をつける。
  3. メールのヘッダーはContent-Type: multipart/mixed;boundary=”任意の文字列”とする。
    multipart/mixedというのは複数のコンテントタイプが混ざってるということ。
    また、このときboundaryという任意の文字列は日付やらをmd5ハッシュしたものなどにする。
  4. 3で指定したboundaryの値の頭に–をつけたものが区切り線となる。
    この区切り線というのはContent-Typeの区切りです。要はテキストのContent-Typeと画像のContent-Type。 

以上をふまえてソース

sendmail.html

<form action="sendmail.php" enctype="multipart/form-data" method="post"> <input id="file" name="file" type="file" /> <input type="submit" /> </form>sendmail.php

$sendTo = "user@email.add"; //送信先(ユーザー)メールアドレス
$sendFrom = "admin@email.add"; //送信元(アドミン)メールアドレス
$subject = "dummy subject"; //任意のタイトル
$message = "画像を添付します"; //任意のメッセージ
$boundary = md5(uniqid( random() )); //boundaryIDを発行

$_FILES["file"]["name"];
$_FILES["file"]["type"];
$_FILES["file"]["tmp_name"];

if(file_exists($_FILES["file"]["tmp_name"])){
#題名をISO-2022-JPエンコード
    $subject = mb_encode_mimeheader($subject, "ISO-2022-JP", "B");

    $header  = "";
    $header .= "From: $sendFrom\n";
    $header .= "Content-Type: multipart/mixed;boundary=\"$boundary\"\n";
    $header .= "X-Mailer: PHP/".phpversion()."\n";
    $header .= "MIME-version : 1.0\n";

    $body  = "";
#テキスト追加
    $body .= "--$boundary"; //頭に--をつけた$boundaryで区切る
    $body .= "Content-Type: text/plain;charset=ISO-2022-JP;format=followed";
    $body .= "Content-Transfer-Encoding: 7bit\n";
    $body .= "\n"; //テキスト部のheader終了
    $body .= "$message\n";
    $body .= "\n"; //テキスト部のbody終了
#添付ファイル追加
    $body .= "--$boundary"; //頭に--をつけた$boundaryで区切る
    $body .= "Content-Type: ".$_FILES["file"]["type"].";name=\"$_FILES["file"]["name"]\"\n";
    $body .= "Content-Transfer-Encoding: base64\n";
    $body .= "Conteint-Disposition: attachment;filename=\"$_FILES["file"]["name"]\"\n";
    $body .= "\n"; //添付ファイル部のheader終了
#ファイルをエンコードする
//-----
    $fp = fopen($_FILES["file"]["tmp_name"], "r") or die("error"); //添付ファイルを開く
    $contents = fread($fp, filesize($_FILES["file"]["tmp_name"])); //添付ファイルを読む
    fclose($fp);	//添付ファイルを閉じる
    $f_encoded = chunk_split(base64_encode($contents));	//添付ファイルをbase64エンコードする
//-----
    $body .= "$f_encoded\n";
    $body .= "\n"; //添付ファイル部のbody終了

    mail($sendTo, $subject, $body, $header);
    echo "success!";
}else{
    echo "error";
}
?>

PHPの\nとか足す感じがめちゃくちゃきらいです。
まーきっとそれはボクがへちょいからでしょう。いいやり方は覚えていきたいものの、PHPはあんまりやらないようがんばろうと思います。

今回の動画はUnderworld。そいや昔どこかでストレイテナーがBorn Slippyをバンドでコピーしてた動画をみたんだけれども見つかりませんでした。
こういう曲を生演奏で再現されるとキュンとする。

PHPで携帯サイト – Vodafoneエラー (WJ46048E)

とりあえず、よくわからん人はダウンタウンの携帯トークで和んでください。
そして焦ってる人もこれみて和んでください。
Soundを押すと音がでますから。

—————————————- 8<

携帯サイトでフォーム入力あるサイトを作成したのだけど、ハマった。
携帯サイトは必ずといっていいほどハマる...

まず、javascriptがつかえないからボクの得意分野がどうのこうのというのはなんとかしたとして、問題はタイトルのエラー。

Vodafoneの3G(スリージー)携帯で”WJ46048E”というエラーがでました。
レスポンスが不正です。(WJ46048E)

色々ヘッダーをいじくって分かったのは、

@header(‘Content-Type: text/html; charset=utf-8′);
@header(‘Transfer-Encoding: chunked’);

この2行。2行目は余裕で外していいとして、1行目は、逆にcharsetとか入れない方がいいみたい。というかvadafoneの方で変換してるのだろうか。

@header(‘Content-Type: text/html;’);

にしたら無事解決。
良かった良かった。

jPhoneからiPhoneにしたわけでVodafoneはよくわからんです。

YouTubeのidからFLVのパスを取得する

実はこのブログのムービーモードは、背景にYouTubeのビデオが流れています。

こういうサイトを作るには、YouTubeにある動画のFLVパス(実際の動画ファイルへのパス)が必要になってきます。

昔ニコニコ動画と同じ方法が結構簡単にできたんでメモ。

YouTubeのIDをPHPにGETで渡すとFLVパスを返す

こんな感じのクエリを送る。

http://your.domain.com/to/PHP/path/api.php?id=CufzjUfINgA

PHPのソースはこう。

mb_internal_encoding('UTF-8');
$pattern = '/var swfArgs = (.*)"t": "(.*)", "hl"/';
$video_id = $_GET["id"];
$path = "http://www.youtube.com/watch?v=".$video_id;
$source =  file_get_contents($path);
preg_match_all($pattern, $source, $retVal);
$t = $retVal[2][0];
echo "http://www.youtube.com/get_video?video_id=".$video_id."&t=".$t;
  1. YouTubeにアクセス
  2. ソースを引っこ抜いてくる
  3. 正規表現でFLVのパスを取得するのに必要な情報をあつめる(パースする)
  4. FLVパスにしてecho

そういえば最近YouTubeが実はH.264形式でFLVを返してくれることがわかりました。

ついでにH.264形式でのパスを返してくれるソースはこちら

mb_internal_encoding('UTF-8');
$pattern = '/var swfArgs = (.*)"t": "(.*)", "hl"/';
$video_id = $_GET["id"];
$path = "http://www.youtube.com/watch?v=".$video_id;
$source =  file_get_contents($path);
preg_match_all($pattern, $source, $retVal);
$t = $retVal[2][0];
echo "http://www.youtube.com/get_video?video_id=".$video_id."&t=".$t."&fmt=18";

FLVパスの最後に&fmt=18としただけです。でもこれ、アップロードが遅くて再生がとまっちゃうのと、描画にスペックくっちゃうので却下。もうちょいアップのスピードくれたらなー。

あとこれはあまりやりすぎると、YouTubeからアクセス禁止になる。それこそニコニコ動画みたいに。