【问题标题】:PHP 'instanceof' failing with class constantPHP 'instanceof' 因类常量而失败
【发布时间】:2011-03-01 11:07:17
【问题描述】:

我正在开发一个框架,我正在尝试尽可能强地键入。 (我在 PHP 中工作,并从 C# 中获取我喜欢的一些想法,并尝试在这个框架中利用它们。)我正在创建一个 Collection 类,它是域实体/对象的集合。它有点模仿 .Net 中的 List<T> 对象。

我遇到了阻碍我输入这门课程的障碍。如果我有一个 UserCollection,它应该只允许用户对象进入它。如果我有 PostCollection,它应该只允许 Post 对象。

这个框架中的所有Collection都需要具备一定的基本功能,比如add、remove、iterate。我创建了一个界面,却发现无法做到以下几点:

interface ICollection { public function add($obj) }
class PostCollection implements ICollection { public function add(Post $obj) {} }

这破坏了它对界面的遵守。但是我不能强类型化接口,因为所有集合都是同一类型。所以我尝试了以下方法:

interface ICollection { public function add($obj) }
abstract class Collection implements ICollection { const type = 'null'; }
class PostCollection extends Collection {
 const type = 'Post';
 public function add($obj) {
  if(!($obj instanceof self::type)) {
   throw new UhOhException();
  }
 }
}

当我尝试运行此代码时,我在instanceof 语句中得到syntax error, unexpected T_STRING, expecting T_VARIABLE or '$'。对该问题进行一些研究,看起来原因的根源是$obj instanceof self 对类进行测试是有效的。似乎 PHP 没有处理表达式中的整个 self::type 常量语句。在 self::type 变量周围添加括号会引发有关意外 '(' 的错误。

一个明显的解决方法是不要将type 变量设为常量。表达式 $obj instanceof $this->type 可以正常工作(当然,如果 $type 被声明为变量)。

我希望有一种方法可以避免这种情况,因为我想将值定义为常量以避免以后对变量进行任何可能的更改。关于如何实现这一点的任何想法,或者在这方面我是否将 PHP 发挥到了极限?有没有办法“转义”或者封装self::this,让PHP在处理的时候不会死机?

更新根据下面的反馈,我想到了一些尝试——下面的代码有效!谁能想到 1) 不这样做的理由,2) 最终无法奏效的原因,或 3) 更好的方法来实现这一目标?

interface ICollection { public function add($obj) }
abstract class Collection { const type = null; protected $type = self::type; }
class PostCollection extends Collection {
 const type = 'Post';
 public function add($obj) {
  if(!($obj instanceof $this->type)) {
   throw new UhOhException();
  }
 }
}

更新 #2: 将上面的代码投入生产后,事实证明它不起作用。当我测试它时,我不知道它是如何工作的,但它根本不起作用。我想我坚持使用protected 变量。

【问题讨论】:

  • 我想我回答了 1 或 2 -- 即使变量是 protected,它仍然可以在代码中更改。这样做并没有真正的安全性,因为变量$type 不会被意外更改。我所希望的就是为此提供保险。
  • 如果我理解正确,您给 instanceof 操作提供了一个字符串 ($this->type),而它应该只是一个类名(参见 php.net/manual/en/language.operators.type.php)。我认为你应该改用'is_a',因为它需要一个字符串,这就是 $this->type 是什么。
  • 不是一个真实的陈述 - 查看您链接到的页面上的示例 #5。
  • 啊,真的,我的错,谢谢你的评论。
  • 顺便说一下,2-空格缩进是异端。

标签: php oop strong-typing


【解决方案1】:

这也可以正常工作,使用静态:

<?php

interface ICollection { 
  public function add($obj); 
}
abstract class Collection implements ICollection { 
  static protected $_type = 'null'; 
}
class PostCollection extends Collection {
 static protected $_type = 'Post';
 public function add($obj) {
  if(!($obj instanceof self::$_type)) {
   throw new UhOhException();
  }
 }
}


class Post {}

$coll = new PostCollection();
$coll->add(new Post());

实际上,您可能想在Collection 类上定义您的add() 方法,这意味着您必须使用get_class() 来解决self::type 甚至self::$_type 的一些奇怪之处无论如何都想返回基本的Collection 类,所以这可能会起作用:

abstract class Collection implements ICollection { 
  const type = 'null'; 
  public function add($obj) {
   $c = get_class($this);
   $type = $c::type;
   if(!($obj instanceof $type)) {
    throw new UhOhException();
   }
  }
}

class PostCollection extends Collection {
 const type = 'Post';
}
class Post {}

$coll = new PostCollection();
$coll->add(new Post());

【讨论】:

    【解决方案2】:

    应该是这样的

    公共函数添加(ICollection $obj){

    }

    现在,如果您尝试向函数 add 添加一个不是 Icollection 实例的对象(这是一个示例),那么它将失败,甚至在您使用 instanceof 进行检查之前。

    【讨论】:

    • 这是个好主意——在这种情况下,它将是一个实体对象,但是一个很好的基础。
    【解决方案3】:

    我也对这种行为感到惊讶,但这应该可行:

    $type = self::type;
    if(!($obj instanceof $type)) {
        throw new UhOhException();
    }
    

    编辑:

    你可以的

    abstract class Collection {
        const type = null;
        protected $type = self::type;
    }
    class PostCollection extends Collection {
        const type = "User";
        public function add($obj) {
            if(!($obj instanceof $this->type)) {
                throw new WhateverException();
            }
        }
    }
    

    但是您正在打开复杂度计。这会产生额外的开销,即为PostCollection 的每个实例创建一个实例$type 变量(不,您不能只将static 添加到$type 属性中)。

    【讨论】:

    • @Nathan Loding 你的新代码给了我PHP Notice: Undefined property: PostCollection::$type
    • 我打错了!类定义应该是class PostCollection extends Collection——我会更新它,很好。
    【解决方案4】:

    我正在创建一个 Collection 类,它是域实体/对象的集合。它有点模仿 .Net 中的 List&lt;T&gt; 对象。

    用另一种语言编写一种语言通常不是一个好主意。你不需要 PHP 中的集合。

    如果您要继续走这条路,也许您应该考虑使用 PHP 提供的工具。例如,ArrayObject,您可以从它继承并覆盖所需的方法,以确保只有正确键入的内容才能进入数组。 ArrayObjects 可以在 PHP 中任何可以使用普通数组的地方使用。此外,已经为您编写了底层的点点滴滴。

    您可能对The rest of the Standard PHP Library 感兴趣,尤其是SplObjectStorage 类。

    【讨论】:

    • 除了我说它“有点模仿”。我需要一个容器来容纳多个相同类型的对象,并且能够轻松地添加、删除、查找和迭代它们——它位于 PHP 应用程序中。如果这是一个 .Net 应用程序,我会使用 List&lt;T&gt; 对象。在 PHP 中,我有幸在此基础上创建自己的变体。
    • 除了第一段之外,您可能还想阅读我的文章,因为我将继续解释 PHP 如何可能已经为您提供了创建此行为所需的工具,而无需使用非常不同的语言实现构造。 :)
    • SplObjectStorage 和 ArrayObject 选项并不是我想要的。我看不出有任何理由不尝试创建从一种语言到另一种语言的构造,除非存在无法完成的技术原因。在这种情况下,在 PHP 中创建一个模仿 .Net 中的 List 对象的集合类是非常可能的,但强类型部分不是。 PHP over .Net 的乐趣在于您可以自己构建这些对象。我不是在用另一种语言编写一种语言——我是在根据使用另一种语言的经验探索一种语言的可能性。
    • “我看不出有什么理由不尝试从一种语言到另一种语言的构造,除非……” 每种语言都有一种文化。这种文化经常建立一种做事方式、一种标准的操作程序、一套通用的习语。让我们暂时选择 Perl。用 Perl 编写 PHP 是完全可能的。您可以创建每一个内建函数并使您的 Perl 程序的功能与用 PHP 编写的完全一样。不过,将这段代码展示给 Perl 程序员,他们会看着你,就好像你长出了第二个脑袋一样。能够并不意味着应该。
    • 话虽如此,+1 用于探索语言的功能。如果你从中学到了一些东西,即使是“坏主意”也可能是好主意。
    【解决方案5】:

    尝试使用 PHP 的 is_a 函数而不是 instanceof,因为它需要一个字符串作为类名。

    【讨论】:

    【解决方案6】:

    另一种解决方法是:

    $type = self::type;
    if (!($obj instanceof $type))
    

    仅仅因为它是一个常量并不意味着你不能暂时将它放入一个变量中以满足解析器的要求。

    【讨论】:

    • 或者括号有什么问题? !($obj instanceof (self::type))
    • @seanmonstar - 这个确切的表达式在 PHP 中给我带来了错误。它对你有用吗?我收到有关“意外的”(“”的错误。
    • @Nathan 不,我实际上并没有打开外壳并对其进行测试,抱歉。我只是假设这是运算符优先级的问题,并且它以错误的方式解析它,例如 ($obj instanceof self)::type
    • @Nathan 不过,看看,你能不能不用get_class($obj) == self::type
    • @seanmonstar - 我想我最终会得到is_a($obj, self::type) 声明。我现在正试图决定这是否是我最终要走的路线......
    猜你喜欢
    • 1970-01-01
    • 2013-04-22
    • 2015-11-12
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 2018-03-12
    相关资源
    最近更新 更多