@blog.justoneplanet.info

日々勉強

ZendFrameworkビュースクリプトでのbodyタグ

以下のように記述するとcssで装飾しやすい気がする。

■コントローラ

public function init(){
    $this->_view->module     = $this->_getParam('module');
    $this->_view->controller = $this->_getParam('controller');
    $this->_view->action     = $this->_getParam('action');
}

■ビュー

<body class="<?php print($this->controller); ?>" id="<?php print($this->controller); ?>_<?php print($this->action); ?>">

ちなみに以下のようになっている。

print($this->module);//モジュール名
print($this->controller);//コントローラ名
print($this->action);//アクション名

出力例

<body class="category" id="category_register">

参考

以下の例の場合を考えてみる。

<body class="<?php print($this->module); ?> <?php print($this->controller); ?> <?php print($this->action); ?>">
出力例
<body class="category" id="category_register">

一見素晴らしいが、IEが以下のセレクタに対応していないため使用できない。

body.admin.category.register {
	background-color: red;
}
body.register {
	background-color: blue;
}

上述のように記述するとIE6のみbodyの背景色がblueになるはずだ。

fileコマンド

fileの種類を表示する。

file index.html
#index.html: UTF-8 Unicode HTML document text, with very long lines

JavaScriptでマルチスレッドプログラミング

JavaScriptで複雑な演算を行った場合、結果が帰ってくるまで他の処理は行えない。それを可能にするには以下の様なコードを使う。

■Workerオブジェクト

ブラウザでローカルのパスを叩いても実行されないので、以下のファイルをサーバ上に置いて実行する。

メイン

var worker = new Worker('sample.js');
worker.onerror = function(error){
    alert(error.message);//alert is not defined
    alert(error.filename);//file:///C:Desktop/sample.js
    alert(error.lineno);//1
}
worker.onmessage = function(event){
    alert('finished');
}

sample.js

alert('test');

エラーになる。sample.jsではwindowオブジェクトを始めブラウザが生成するオブジェクトにはアクセスできない。従って、以下の様なサンプルを用いる。

メイン

3628800とalertされるはずだ。

var worker = new Worker('sample.js');
worker.onerror = function(){
    alert('error');
}
worker.onmessage = function(event){
    alert(event.data);
}

sample.js

var n = 10;
var func = function(n){
    if(n === 1){
        return 1;
    }
    else{
        return n * func(n - 1);
    }
}
var result = func(n);
postMessage(result);

onmessageで受け取った引数のメンバ

  • data : 3628800
  • QueryInterface : function QueryInterface() { [native code] }
  • type : message
  • target : [object Worker]
  • currentTarget : [object Worker]
  • eventPhase : 2
  • bubbles : false
  • cancelable : false
  • timeStamp : 1263220393472000
  • stopPropagation : function stopPropagation() { [native code] }
  • preventDefault : function preventDefault() { [native code] }
  • initEvent : function initEvent() { [native code] }
  • CAPTURING_PHASE : 1
  • AT_TARGET : 2
  • BUBBLING_PHASE : 3
  • origin :
  • source : null
  • initMessageEvent : function initMessageEvent() { [native code] }

以下のコードで調査した。

var worker = new Worker('sample.js');
worker.onerror = function(){
    alert('error');
}
worker.onmessage = function(event){
    alert(event.data);
    for(var i in event){
        var data   = document.createTextNode(' : ' + event[i]);
        var member = document.createTextNode(i);
        var li     = document.createElement('li');
        li.appendChild(member);
        li.appendChild(data);
        document.getElementById('members').appendChild(li);
    }
}

残念ながらWorkerオブジェクトが使えるのは今のところFirefoxだけである。IE8、Chrome、Opera10、Safari4ではいずれも使えない。

また以下のようにpostMessageメソッドを用いてworkerにメッセージを投げることもできる。

var worker = new Worker('./sample.js');
worker.onerror = function(errro){
    console.log(error);
}
worker.onmessage = function(e){
    console.log(e.data);// 120
}
worker.postMessage("5");

投げられたメッセージをonmessageで受け取り計算結果をpostMessageで投げることができる。

onmessage = function(e){
    var n = e.data;
    var func = function(n){
        if(n === 1){
            return 1;
        }
        else{
            return n * func(n - 1);
        }
    }
    var result = func(n);
    postMessage(result);
}

参考

Zend_Exceptionのメソッド

以下の通り。

array(10) {
  [0] => &object(ReflectionMethod)#54 (2) {
    ["name"] => string(7) "__clone"
    ["class"] => string(9) "Exception"
  }
  [1] => &object(ReflectionMethod)#55 (2) {
    ["name"] => string(11) "__construct"
    ["class"] => string(9) "Exception"
  }
  [2] => &object(ReflectionMethod)#56 (2) {
    ["name"] => string(10) "getMessage"
    ["class"] => string(9) "Exception"
  }
  [3] => &object(ReflectionMethod)#57 (2) {
    ["name"] => string(7) "getCode"
    ["class"] => string(9) "Exception"
  }
  [4] => &object(ReflectionMethod)#58 (2) {
    ["name"] => string(7) "getFile"
    ["class"] => string(9) "Exception"
  }
  [5] => &object(ReflectionMethod)#59 (2) {
    ["name"] => string(7) "getLine"
    ["class"] => string(9) "Exception"
  }
  [6] => &object(ReflectionMethod)#60 (2) {
    ["name"] => string(8) "getTrace"
    ["class"] => string(9) "Exception"
  }
  [7] => &object(ReflectionMethod)#61 (2) {
    ["name"] => string(11) "getPrevious"
    ["class"] => string(9) "Exception"
  }
  [8] => &object(ReflectionMethod)#62 (2) {
    ["name"] => string(16) "getTraceAsString"
    ["class"] => string(9) "Exception"
  }
  [9] => &object(ReflectionMethod)#63 (2) {
    ["name"] => string(10) "__toString"
    ["class"] => string(9) "Exception"
  }
}

二分木(Binary Tree)

全ての接点(ノード)が左と右の子を持つ事ができる。

■Node

まずはノードを定義(二分木用)。

function Node(key, element){
    this._key        = key;
    this._parentNode = null;
    this._lChildNode = null;
    this._rChildNode = null;
}
Node.prptotype = {
    "getKey" : function(){
        return this._key;
    },
    "hasParent" : function(){
        return (this._parentNode != null);
    },
    "getParent" : function(){
        return this._parentNode;
    },
    "setParent" : function(element){
        this._parentNode = element;
    },
    "isEmpty" : function(){
        return ((this._lChildNode == null) && (this._rChildNode == null));
    },
    "hasLeftChild" : function(){
        return (this._lChildNode != null);
    },
    "hasRightChild" : function(){
        return (this._rChildNode != null);
    },
    "getLeftChild" : function(){
        return this._lChildNode;
    },
    "getRightChild" : function(){
        return this._lChildNode;
    },
    "setLeftChild" : function(element){
        this._lChildNode = element;
    },
    "setRightChild" : function(element){
        this._lChildNode = element;
    }
}

なるほど!

■二分木

ノードを利用して二分木を定義。

function BinaryTree(){
    this._root = new Node(null, null);
}
BinaryTree.prototype = {
    "isEmpty" : function(){
        return (this._root.getKey() == null);
    },
    "isRoot" : function(node){
        return (this._root == node);
    },
    "getRoot" : function(){
        return this._root;
    },
    "replaceRoot" : function(node){
        this._root = node;
    },
    "hasParent" : function(node){
        return (node.hasParent());
    },
    "getParent" : function(node){
        return node.getParent();
    },
    "setParent" : function(node, element){
        return node.setParent(element);
    },
    "isEmpty" : function(node){
        return node.isEmpty();
    },
    "hasLeftChild" : function(node){
        return node.hasLeftChild();
    },
    "hasRightChild" : function(node){
        return node.hasRightChild();
    },
    "getLeftChild" : function(node){
        return node.getLeftChild();
    },
    "getRightChild" : function(node){
        return node.getRightChild();
    },
    "setLeftChild" : function(node, element){
        return node.setLeftChild(element);
    },
    "setRightChild" : function(node, element){
        return node.setRightChild(element);
    }
}

デザインパターンでいうところのadapterパターンだ。

参考

IEではJavaScriptでobject要素にembedを挿入できない

以下のコードを実行した時にIE6~8でエラーが発生する。appendChildやreplaceChildを使用してもできない。

var objectElm = document.createElement('object');
objectElm.setAttribute('classid', 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000');
objectElm.setAttribute('codebase', codebase);
objectElm.setAttribute('width', '240');
objectElm.setAttribute('height', '320');
var embedElm = document.createElement('embed');
embedElm.src = url;
embedElm.setAttribute('quality', 'hign');
embedElm.setAttribute('pluginspage', pluginUrl);
embedElm.setAttribute('type', 'application/x-shockwave-flash');
embedElm.setAttribute('width', '240');
embedElm.setAttribute('height', '320');
objectElm.appendChild(param1);
objectElm.appendChild(param2);
objectElm.appendChild(embedElm);//error - invalid argument(引数が無効です。)

クライアント側で

■解決策1

条件分岐して、IEのみobject要素に包括せず、embedのみを出力する。

var embedElm = document.createElement('embed');
embedElm.src = url;
embedElm.setAttribute('quality', 'hign');
embedElm.setAttribute('pluginspage', pluginUrl);
embedElm.setAttribute('type', 'application/x-shockwave-flash');
embedElm.setAttribute('width', '240');
embedElm.setAttribute('height', '320');
objectElm.appendChild(param1);
objectElm.appendChild(param2);
if(!!(window.attachEvent && !window.opera)){
    document.getElementById('flash').appendChild(embedElm);
}
else{
    objectElm.appendChild(embedElm);
    document.getElementById('flash').appendChild(objectElm);
}

■解決策2

object要素の後ろに殻のembed要素を挿入する。恐らくオリジナル。

var embedElm = document.createElement('embed');
embedElm.src = url;
embedElm.setAttribute('quality', 'hign');
embedElm.setAttribute('pluginspage', pluginUrl);
embedElm.setAttribute('type', 'application/x-shockwave-flash');
embedElm.setAttribute('width', '240');
embedElm.setAttribute('height', '320');
objectElm.appendChild(param1);
objectElm.appendChild(param2);
document.getElementById('flash').appendChild(objectElm);
if(!!(window.attachEvent && !window.opera)){
    document.getElementById('flash').innerHTML += '<embed></embed>';
}
else{
    objectElm.appendChild(embedElm);
}

なぜかチャント動くようになる。

参考

Javascriptで文字列として</script>を使う

以下のXHTML1.0トランジショナルで考える。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無題ドキュメント</title>
<script type="text/javascript">
//<![CDATA[
alert('</script>');
//]]>
</script>
</head>

<body>
</body>
</html>

以下のalert内の閉じタグが、jsの閉じタグとして認識されてしまい、残念ながら正しく実行されない。

alert('</script>');

■解決策

以下のようにエスケープする。

alert('<\/script>');

もしくは以下のように文字列を結合する。

alert('</' + 'script>');

また、Chrome、Firefox、OperaでXMLパーサーが正しくCDATAセクションを認識するように、ファイルの拡張子をxhtmlにすることも有効な解決策ではあるが、この手法ではIEが正しく認識できない。

Facade Pattern

■定義

サブシステム内に存在する複数のインターフェースに1つの統一インターフェースを与える。facadeパターンはサブシステムの利用を容易にするための高レベルインターフェースを定義する。窓口的な感じ。

■コード

クライアントコード

$order = new Order();

$itemDao = ItemDao::getInstance(); //itemデータアクセスオブジェクト
$order->addItem(new OrderItem($itemDao->findById(1), 2));
$order->addItem(new OrderItem($itemDao->findById(2), 1));
$order->addItem(new OrderItem($itemDao->findById(3), 3));

OrderManager::order($order);// 注文処理

Order.php

class Order {
    private $items;
    public function __construct() {
        $this->items = array();
    }
    public function addItem(OrderItem $orderItem) {
        $this->items[$orderItem->getItem()->getId()] = $orderItem;
    }
    public function getItem() {
        return $this->items;
    }
}

OrderItem.php

class OrderItem {
    private $item;
    private $amount;
    public function __construct(Item $item, $amount) {
        $this->item   = item;
        $this->amount = amount;
    }
    public function getItem() {
        return $this->item;
    }
    public function getAmount() {
        return $this->amount;
    }
}

Item.php

class Item {
    private $id;
    private $name;
    private $price;
    public function __construct($id, $name, $price) {
        $this->id    = $id;
        $this->name  = $name;
        $this->price = $price;
    }
    public function getId() {
        return $this->id;
    }
    public function getName() {
        return $this->name;
    }
    public function getPrice() {
        return $this->price;
    }
}

OrderManager.php

class OrderManager {
    public static function order(Order $order) {
        $itemDao = ItemDao::getInstance();
        foreach($order->getItems() as $orderItem) {
            $itemDao->setAsign($orderItem);
        }
        OrderDao::createOrder($order);
    }
}

ItemDao.php

/**
 * シングルトン
 */
class ItemDao {
    private static $instance;
    private $items;
    private function __construct() {
        // 以下の構造を生成する
        // $this->items[アイテムのID] = アイテム
    }
    public static function getInstance() {
        if(!isset(self::instance)) {
            self::instance = new ItemDao();
        }
        return self::instance;
    }
    public function findById($itemId) {
        if(array_key_exists($itemId, $this->items)) {
            return $this->items[$itemId]
        }
        else {
            return null;
        }
    }
    public function setAside(OrderItem $orderItem) {
        return $orderItem->getItem()->getName;
    }
}

OrderDao.php

class OrderDao {
    public static function createOrder(Order $order) {
        // orderをループしてゴニョゴニョ
    }
}

PHPでデザインパターン:FactoryMethod(ファクトリーメソッド)

メリットは「生成と処理を分離し利用側の変更なしに処理(クラス)を追加したりできる」ことである。

■コード

今回使用するのは以下のxmlとcsvファイルの2種類とする。

xml

<data>
    <person>
        <name>John</name>
        <occupation>racer</occupation>
    </person>
    <person>
        <name>Mike</name>
        <occupation>banker</occupation>
    </person>
    <person>
        <name>Nick</name>
        <occupation>runner</occupation>
    </person>
</data>

csv

John,racer
Mike,banker
Nick,runner

template methodで処理の枠だけ定義する。

interface Printer
{
    public function read();
    public function output();
}

以下のCsvFilePrinterクラスとXmlFilePrinterクラスで具体的な処理を記述する。

class CsvFilePrinter implements Printer
{
    private $_filename;
    private $_fh;
    public function __construct($filename)
    {
        $this->_filename = $filename;
    }
    public function read()
    {
        $this->_fh = fopen($this->_filename, 'r');
    }
    public function output()
    {
        print("<table>");
        while($data = fgetcsv($this->_fh)){
            print("<tr>");
            for($i = 0, $n = count($data); $i < $n; $i++){
                print("<td>{$data[$i]}</td>");
            }
            print("</tr>");
        }
        print("</table>");
        fclose($this->_fh);
    }
}
class XmlFilePrinter implements Printer
{
    private $_filename;
    private $_rh;
    public function __construct($filename)
    {
        $this->_filename = $filename;
    }
    public function read()
    {
        $this->_rh = simplexml_load_file($this->_filename);
    }
    public function output()
    {
        print('<table>');
        foreach($this->_rh->person as $person){
            print('<tr>');
            print("<td>{$person->name}</td>");
            print("<td>{$person->occupation}</td>");
            print('</tr>');
        }
        print('</table>');
    }
}

Factory部分

分岐してインスタンスを返すようになっている。

class PrinterFactory
{
    public function getPrinter($filename)
    {
        if(preg_match('/\.csv$/i', $filename)){
            return new CsvFilePrinter($filename);
        }
        elseif(preg_match('/\.xml$/i', $filename)){
            return new XmlFilePrinter($filename);
        }
    }
}

クライアントコード

以下のように中身を意識することなく使用できる。

$printer = new PrinterFactory();
$data = $printer->getPrinter('./dat.xml');
$data->read();
$data->output();
/*
John	racer
Mike	banker
Nick	runner
*/
$data = $printer->getPrinter('./dat.csv');
$data->read();
$data->output();
/*
John	racer
Mike	banker
Nick	runner
*/