PHP类型提示:支持数组,不支持对象?

24

我是否漏掉了什么,或者在 PHP 5.x 中真的没有支持泛型对象类型提示的功能?

我觉得很奇怪的是,数组提示被支持,而对象提示不被支持,至少不是开箱即用的。

我希望有类似这样的功能:

function foo(object $o)

就像我们已经有的一样:

function foo(array $o)

可能使用的示例:对象集合类的方法。

解决方法:使用一个由所有类实现的接口“Object”,或将所有类扩展自通用类“Object”,并编写类似于以下内容的代码:

function foo(Object $o)

这还真不太可爱。

使用 stdClass 作为类型提示无效:

Catchable fatal error: Argument 1 passed to c::add() must be an instance of stdClass, instance of b given


1
不要使用具体类型进行类型提示。可以使用接口,或者了解一下鸭子类型。 - Ionuț G. Stan
6
PHP是一种非常不一致的语言,在其中对象不像Java那样从单个类继承。事实上,通用对象没有类型提示确实是一个疏忽,但我认为你必须超越这一点并为其他奇怪的事情做好准备。 - Ionuț G. Stan
9
数组类型提示比is_array()函数更好,因为它可以确保参数是一个真正的数组,而不是任何其他类型的变量。由于支持数组类型提示,我希望对象也能得到同样的支持。 - Marius Burz
4
在每个函数中进行is_array检查并抛出异常比设置全局异常处理程序更容易?你一定在开玩笑。 - salmatron
@SalmanPK 全局错误处理程序意味着您无法在调用站点处理异常,这有时是可取的。您无法使用此类类型提示将对函数的调用包装在 try/catch 块中。至少上次我检查时不可能。 - Ionuț G. Stan
显示剩余5条评论
10个回答

7
由于类型提示应使客户端代码适应您的API,因此接受接口的解决方案似乎刚刚好。看看这个方式:yourMethod(array $input)yourMethod()提供一个数组来使用,因此您知道适用的哪些本地函数,并且可以被yourMethod()使用。如果您像这样指定方法:yourSecondMethod(yourInterface $input),您也会了解可以应用于$input的方法,因为您了解/可以查找随接口yourInterface附带的一组规则。在您的情况下,接受任何对象都是错误的,因为您无法知道在输入上使用哪些方法。例如:
function foo(Object $o) {
    return $o->thisMethodMayOrMayNotExist();
}

(不意味着语法有效)


2
这并不适用于对象集合类,它将充当一个(伪)容器,不会调用其子元素的任何方法,也不会使用它们的任何属性/属性。 知道变量是一个对象也使得可以在其上使用许多本地函数,所以我真的看不出你的观点。 - Marius Burz
2
使用像SplObjectStorage或ArrayObject这样的类型提示怎么样? - chelmertz
有 \stdClass 用于简单的容器对象。 - Kzqai

6
我理解你的痛苦,但我也无法找到合适的方法。
尽管其他帖子的作者提出了一些不同的看法,但要求“对象”类型提示是完全有道理的;他们只是没有考虑到需要它的情况。
我正在尝试使用反射API进行一些工作,因此我不关心传递给我的函数的是什么类。我只关心它是一个对象。我不想要一个整数、浮点数、字符串或数组。我需要一个对象。鉴于反射现在是PHP的一部分,要求对象类型提示是非常合理的。

6
不行,这是不可能的。我没有漏掉任何东西。

自 PHP 7.2 开始,您终于可以声明您想要的方式:function functionName(object $someObjectVariable)请参阅以下页面上名为“有效类型”的表格: (https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) - Tomeg

4

在进行对象类型转换时,您不能仅使用“object”这个词……您必须定义您期望的是哪个对象。

来源:http://php.net/manual/en/language.oop5.typehinting.php

class MyClass
{
    /**
     * A test function
     *
     * First parameter must be an object of type OtherClass
     */
    public function test(OtherClass $otherclass) {
        echo $otherclass->var;
    }


    /**
     * Another test function
     *
     * First parameter must be an array
     */
    public function test_array(array $input_array) {
        print_r($input_array);
    }
}

// Another example class
class OtherClass {
    public $var = 'Hello World';
}

3
实现这个的最佳方式是创建一个退化接口,名为Object。退化接口意味着它没有定义方法。
interface Object {

   // leave blank

}

然后在您的基类中,您可以实现Object

class SomeBase implements Object {

   // your implementation

}

现在您可以按照您想要的方式调用您的函数。
function myFunc (Object $obj);

myFunc($someBase);

如果你传递任何从你的Object接口继承的对象,这个类型提示会通过。如果你传递一个数组、整数、字符串等,类型提示将失败。

不幸的是,这可能就是你在PHP中能够得到的最接近的方法,但这意味着你创建的每个类都必须实现“Object”接口,这对于像这样简单的事情来说确实是很多的额外工作。啊哈哈哈,PHP... - mAAdhaTTah
4
OP已经在问题中描述了这种策略作为可能的解决方法,但它不太好用;这种方法无法通过内置类或第三方库定义的类的实例。 - Mark Amery

2
这是另一个需要的示例...
我创建了一个类来实现记录锁定。记录是多种不同对象类型之一。锁定类有几个方法需要一个对象(要锁定的对象),但不关心它是什么类型的对象。
例如:
public static function lockRecord($record, User $user, $timeout=null)
{
    if(!is_object($record)) throw new \InvalidException("Argument 1 must be an object.");

    $lock=new Lock();
    $lock->setRecord($record);
    $lock->setUser($user);
    $lock->setTimeout($timeout);
    $lock->activate();
    return($lock);
}

你会发现我的解决方案是使用is_object()并抛出异常,但我更希望能够使用类型提示来完成它。
虽然这不是世界末日,但我认为这很遗憾。

1

在PHP中,对象不是其他面向对象语言中的某些StdClass或Object的子类。因此,无法对对象进行类型提示。但我理解你的意思,因为有时候你想确保传递的是对象,所以我想唯一的方法就是手动引发问题。

public function yourFunction($object){
    if(is_object($object)){
        //TODO: do something
    }else{
        throw new InvalidArgumentException;
    }
}

从php 5.4开始,还有一个类型提示callable。参见php手册http://php.net/manual/zh/language.types.callable.php

0
public static function cloneObject($source)
{
    if ($source === null)
    {
        return null;
    }

    return unserialize(serialize($source));
}

这就是你需要它的地方。


4
为了提高您的回答质量,请解释您的帖子如何/为什么解决了问题。 - Mick MacCallum

0
为什么要提示object,而不是提示实际的类名呢?这样会更加有用。同时,还需要记住的是,你不能提示intfloatboolstringresource

3
为什么要打一个数组呢?记住其他那些不能进行提示的类型……由于不存在所有 PHP 类都是从中继承的基类,因此可以像提示对象一样提示数组是有意义的。这是我的观点,但我怀疑我在这方面并不孤单。 - Marius Burz
1
仅仅因为你无法为泛型对象想到一个类型提示实例,并不意味着这个需求从未存在过。Rikki的评论是一个完美的例子。在我看来,无法为int、float、bool、string和resource进行类型提示也是一个疏忽。有很多情况下,我更喜欢明确限制参数只能是整数。所以,我被迫使用“is_int”之类的无聊调用。 - Nathan Crause

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接