用于定义类类型的参数

时间:2015-08-26 18:29:50

标签: php

我有一个构造函数要求一种类,但它没有将其定义为类型提示。你可以传递你想要的任何东西,它会接受它。有没有办法将类类型传递给构造函数,而在add()方法中它只接受该类型?

目前我拥有的是能够将任何内容传递给构造函数,例如intstringbool等。有没有办法让它成为构造函数只接受类类型?

class Main{

    protected $items = [];
    protected $type  = '';

    public function __construct($type){
        $this->type = $type;
    }

    public function add($object){
        if($object instanceof $this->type){
            $this->items[] = $object;
        }
    }
}

class Test{}
class Awesome{}

$main1 = new Main(Test::class);
$main2 = new Main(Awesome::class);

// Successful:
$main1->add(new Test());
// Fail:
$main1->add(new Awesome());


// Successful:
$main2->add(new Awesome());
// Fail:
$main2->add(new Test());

如果我在C#中这样做,它看起来像这样:

Main main1 = new Main<Test>();
Main main2 = new Main<Awesome>();

基本上它表示add()只允许Test的实例。有没有办法做一些

4 个答案:

答案 0 :(得分:2)

Php不支持类似声明的模板,例如C ++。

你能够实现这一目标的最好方法是传递一个lambda,然后使用该lambda来验证传入的参数。

<?php
class Test {
    private $validator = null;

    public function __construct($validator) {
        $this->validator = $validator;
    }

    public function add($value) {
        $func = $this->validator;
        $validated = $func($value);

        echo $validated ? 'OK' : 'NG';
    }
}

$obj = new Test(function($value) { 
    return is_int($value);
});

$obj->add(11);
$obj->add('string');

另一种可能性是传递类型,例如&#34;的ClassName&#34;在您的构造函数中,使用get_class()gettype()进行验证。

将来可能有更聪明的解决方案,因为你可以写匿名课程,但我还没有真正考虑过这一点,但最终他们的工作方式与lambdas类似。

答案 1 :(得分:1)

  

是否有办法使构造函数只接受类   类型?

都能跟得上!
在PHP中是不可能的。至少不像C# 您需要设置类型提示或设置任何类型。 但是,有一个更接近的解决方案,以便在实例化类时只接受类:使用ReflectionClass

class Main {

    protected $items = [];
    protected $type  = null;

    public function __construct($type) {
        $reflector  = new ReflectionClass($type);
        $this->type = $reflector->getName(); # or: $this->type = $type;   
    }

    public function add($object) {       
        if($object instanceof $this->type) {
            $this->items[] = $object;                
        }        
    }    
}

由于ReflectionClass构造函数参数只接受包含要反映的类名称的字符串,因此可以利用它,因此传递标量字符串将导致异常。

$main = new Main(Test::class); # Okay!
$main = new Main('Test');      # Okay!

然而

$main = new Main('bool');
// Results
# PHP Fatal error:  Uncaught exception 'ReflectionException' 
# with message 'Class bool does not exist' in ...

答案 2 :(得分:1)

  

基本上它表示add()只允许Test的实例。

只需在函数定义中添加参数名称之前的类型,就可以在PHP中实现此目的(类似于C / C++ / {{1} } types):

C#

PHP 5接受classesinterfacesarraycallable作为type hints。如果class Main { protected $items = []; public function add(Test $object) { $this->items[] = $object; } } 是一个类,则Test接受类Main::add()及其子类的对象。如果Test是一个界面,则方法Test会接受implement Main::add()或其中一个孩子的对象。

PHP 7 (即将推出到您附近的服务器) introduces类型提示标量类型。

Test不支持与PHP模板或C++泛型相似的任何内容。如果要创建一个使用类型为C#的对象的类和另一个具有相同行为但使用类型为A的对象的类,则可以使用多个选项,但它们都不如模板那么优雅/泛型:

  1. 创建两个具有相同行为的类,一个用于类型为B的对象,另一个用于类型为A的对象;在两个类的方法的参数列表中使用不同的类型提示(BA)来强制分离 - 不可扩展;
  2. 与您的代码类似的东西,使用允许的类名作为字符串属性并在任何操作上检查它;您还可以使用class_exists()验证构造函数的参数 - 代码变得混乱,测试结果不太可读;
  3. 使用OOP多态性;从同一个类B扩展AB,或者更好的是,使TA实现相同的接口B。 PHP接口可以为空,也不需要声明任何内容;用于类型提示的空接口是PHP中的常见做法。

    然后编写单个类I并使用Main作为接受对象的所有方法的类型提示。它会接受IA这两种类型的对象,但是如果你还在B中声明了函数(并在IA中实现它们)然后在B中使用它们你可以确定没有任何中断(Main成为I与它接受的对象之间的契约方式的契约。

  4. 我会选择#3选项,因为它得到了解释器的最大帮助;它验证每个具有类型提示的函数调用的参数类型,并触发可恢复的致命错误(在PHP 5中)或抛出异常(在PHP 7中)。

    此外,一些IDE和静态代码分析工具可以在不运行代码的情况下验证调用并帮助您修复它。

答案 3 :(得分:0)

将构造函数更改为:

public function __construct(Type $type){
    $this->type = $type;
}

这是基于$typeType的实例的假设。