PHPで無名クラスっぽいもの定義するAnonymousClassを作った

JavaScriptのfunctionにインスパイアされたようなようなものになった。
 
AnonymousClass \ パッケージ \ Openpear
http://openpear.org/package/AnonymousClass
 

AnonymousClassの特徴・制限

・メソッドは全てClosure Objectで、インスタンス変数として格納する。
・メソッドを定義する時は、第1引数はAnonymousClassインスタンス自身を受け取る必要がある。
・定義出来るものは全てpublic。
・staticに出来ない。(prototypeでstaticっぽく振る舞う事は出来る)
・prototypeで継承が出来る。
・プロトタイプチェーンも出来る。
・変数もメソッドもオーバーライドし放題。
 

主なメソッド

AnonymousClass::create(Closure $construct=null, AnonymousClass $prototype=null)

無名クラスを定義する。
第1引数は、定義する無名クラスのコンストラクタ。
第2引数は、プロトタイプ。
 

AnonymousClass->__invoke($args ...)

定義した無名クラスをインスタンス化する。
引数はAnonymousClassインスタンス自身がunshiftされて、そのまま無名クラスのコンストラクタに渡される。
 

サンプル

いろんな書き方が出来るから、いろんな書き方をした。

<?php

require_once 'AnonymousClass.php';


// Animal

$animal = AnonymousClass::create(function($self) {
	$self->cry_count = 0;

	$self->cry = function($self) {
		echo "unknown...\n";
	};
});
$animal->hello = function($self) {
	echo "Hello ".$self->name."!\n";
	$self->cry();
};


// Dog

$dog = AnonymousClass::create(function($self, $name) {
	$self->name = $name;
	$self->cry = function($self) {
		echo "wan!\n";
		$self->cry_count++;
	};
});
$dog->prototype = $animal();

$pochi = $dog('pochi');
$pochi->hello();


// Cat

$cat = $animal();
$cat->cry = function($self) {
	echo "nyaa!\n";
	$self->cry_count++;
};

$tama = $cat();
$tama->name = "tama";
$tama->hello();

 

実行結果
Hello pochi
wan!
Hello tama
nyaa!

 

JavaScriptで書くと

こんな感じ。

// Animal

var animal = function() {
	this.cry_count = 0;

	this.cry = function() {
		console.log("unknown...");
	};
};
animal.prototype.hello = function() {
	console.log("Hello "+this.name);
	this.cry();
};


// Dog

var dog = function(name) {
	this.name = name;
	this.cry = function() {
		console.log("wan!");
		this.cry_count++;
	};
};
dog.prototype = new animal();

var pochi = new dog("pochi");
pochi.hello();


// Cat

var cat = new animal();
cat.cry = function() {
	console.log("nyaa!");
	this.cry_count++;
};

var tama = (function() {var F=function(){}; F.prototype=cat; return new F();})();
tama.name = "tama";
tama.hello();

 

インストール

Pear
# pear channel-discover openpear.org
# pear install openpear/AnonymousClass

http://openpear.org/package/AnonymousClass
 

Github
# git clone git://github.com/eth0jp/php-AnonymousClass.git

https://github.com/eth0jp/php-AnonymousClass
 

ボツ案 その1

stdClassを使う。
インスタンス変数のClosure Objectを直接実行しようとしても、stdClassに定義されたメソッドを実行しようとしてしまって見つからない。
だから->__invoke()をつける。
冗長。

<?php
$mumei_class = function(){
    $self = new stdClass();
    $self->_total = 0;

    $self->add = function($x) use($self) {
        $self->_total += $x;
    };

    $self->total = function() use($self) {
        return $self->_total;
    };

    return $self;
};
$a = $mumei_class();
$a->add->__invoke(10);
$a->add->__invoke(10);
$a->add->__invoke(10);
echo $a->total->__invoke()."\n";

 

ボツ案 その2

配列を使う。
直接実行出来るけど、配列丸出しで格好悪い。
ちゃちゃっと何かやりたい時にはこれでもいい気がしなくもない。

<?php
$mumei_class = function() {
    $self = array();
    $self['_total'] = 0;

    $self['add'] = function($x) use(&$self) {
        $self['_total'] += $x;
    };

    $self['total'] = function() use(&$self) {
        return $self['_total'];
    };

    return $self;
};
$a = $mumei_class();
$a['add'](10);
$a['add'](10);
echo $a['total']()."\n";