本周实现了一个基于PHP的动态泛型方案。现在PHP可以按照下面的例子来使用泛型了:
<?php
require_once 'FrameworkDSW/System.php';
/**
* @param <T, P> <--这里是推荐的phpDoc定义方式
*/
class TChild extends TObject {
/**
* @param T $p
*/
public function Func($p) {
try {
TType::Type($p, $this->GenericArg('T'));
}
catch (EInvalidTypeCasting $e) {
echo get_class($e) . ' ($p is <div style="border: 1px solid"> ' . var_export($p, true) . '</div>)<br/>';
}
}
/**
* @param T $t
* @param P $p
*/
public function FuncTwo($t, $p) {
try {
TType::Type($t, $this->GenericArg('T'));
}
catch (EInvalidTypeCasting $e) {
echo get_class($e) . ' ($t is <div style="border: 1px solid"> ' . var_export($t, true) . '</div>)<br/>';
}
try {
TType::Type($p, $this->GenericArg('P'));
}
catch (EInvalidTypeCasting $e) {
echo get_class($e) . ' ($p is <div style="border: 1px solid"> ' . var_export($p, true) . '</div>)<br/>';
}
}
}
echo '<p>TEST 1</p>';
TChild::PrepareGeneric(array ('T' => 'integer')); //相当于TChild<T: integer>
$obj = new TChild();
$obj->Func(0);
$obj->Func(new TObject()); //此处出错!不是integer
$obj->Func(true); //不出错,因为true是可以转换成1的,根据PHP的==号规则
$obj->Func('string'); //不出错,因为可以转换成0,根据PHP的==号规则
echo '<p>TEST 2</p>';
TChild::PrepareGeneric(array ('T' => 'integer', 'P' => 'TObject'));
$obj = new TChild();
$obj->FuncTwo(0, new TObject());
$obj->FuncTwo(new TObject(), 0); //出错,TObject不是integer,0不是TObject
class TComplex extends TObject {
/**
*
* @param T $t
*/
public function Func($t) {
TType::Type($t, $this->GenericArg('T'));
}
}
echo '<p>TEST 3</p>';
TChild::PrepareGeneric(array ('T' => 'integer', 'P' => 'boolean'));
$c = new TChild();
TComplex::PrepareGeneric(array ('T' => array ('TChild' => array ('T' => 'integer', 'P' => 'boolean'))));
$obj = new TComplex();
$obj->Func($c);
$obj->Func(true);//出错:true不是TChild<T: integer, P: boolean>
注意我们的框架中的泛型是动态泛型。所谓的动态泛型就是在新建对象或者调用静态方法时,可以动态的指定泛型参数,而定义类或者静态方法时不需要指定所需要的泛型参数定义(虽然这个不是好习惯对于我来说,但是保证了PHP的灵活性),也就是说,在Java中需要这样定义和使用:
class Sample<T, P> {
public static <Q> void Foo(Q param) { /*...*/ }
public static void main(String[] args) {
Sample<String, Integer> s = new Sample<String, Integer>(); //类的泛型
Sample.<Integer>Foo(100); //泛型方法
}
}
而现在在PHP里面可以这样使用:
/**
* @param <T, P>
*/
class TSample extends TObject {
/**
* @param Q $param
*/
public static function Foo($param) { /*...*/ }
}
TSample::PrepareGeneric(array ('T' => 'string', 'P' => 'integer'));
$s = new TSample();
TSample::PrepareGeneric(array ('X' => 'string', 'Y' => array( 'TSample' => array ('Z' => 'string'))));
$s2 = new TSample();
//动态就在这里体现出来了,现在$s2拥有的泛型参数和$s是完全不同的。
//$s的是<T: string, P: integer>
//$s2的是<X: string, Y: TSample<Z: string>>
TSample::PrepareGeneric(array ('Q' => 'integer'));
TSample::Foo(100);
TSample::PrepareGeneric(array ('P' => 'string'));
TSample::Foo(100);
//这个是方法泛型的动态。
最后,附上相关的API:
void TObject::PrepareGeneric(array $Args)
//为下次创建对象或调用静态方法准备泛型的参数,如果需要的话。
mixed TObject::GenericArgs()
//返回当前对象所有泛型参数的值。
mixed TObject::GenericArg(string $ArgName)
//返回当前对象特定泛型参数名称的泛型参数值。
mixed TObject::StaticGenericArgs()
//返回当前类的泛型参数值,泛型静态方法用。
mixed TObject::StaticGenericArgs(string $ArgName)
//返回当前类的某一泛型参数的值,泛型静态方法用。
boolean TObject::IsInstanceOf(mixed $Type)
//判断当前对象是不是$Type描述的类型。
mixed TObject::ObjectType()
//返回当前对象的类型信息。
void TType::Type(mixed &$Var, mixed $Type)
//确保$Var是$Type类型的变量。
void TType::Obejct(object &$Var, mixed $Type)
//确保$Var是$Type类型的对象。
接下来将要实现的是统一数据库访问接口和对容器框架的泛型化……