PHP 'instanceof' не работает с константой класса

Я работаю над фреймворком, который я пытаюсь ввести так сильно, насколько это возможно. (Я работаю в PHP и беру некоторые из идей, которые мне нравятся на С#, и пытаюсь использовать их в этой структуре.) Я создаю класс Collection, который представляет собой совокупность объектов/объектов домена. Он вроде бы моделируется после объекта List<T> в .Net.

Я столкнулся с препятствием, которое мешает мне печатать этот класс. Если у меня есть UserCollection, он должен включать только объекты User. Если у меня есть PostCollection, он должен разрешать только объекты Post.

Все коллекции в этой структуре должны иметь определенные базовые функции, такие как добавление, удаление, итерация. Я создал интерфейс, но обнаружил, что не могу сделать следующее:

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();
  }
 }
}

Когда я пытаюсь запустить этот код, я получаю syntax error, unexpected T_STRING, expecting T_VARIABLE or '$' в инструкции instanceof. Небольшое исследование проблемы, и, похоже, корень причины заключается в том, что $obj instanceof self допустим для тестирования против класса. Похоже, что PHP не обрабатывает весь оператор self::type constant в выражении. Добавление скобок вокруг переменной 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

Это также работает правильно, используя static:

<?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());

И вообще, вы, вероятно, захотите определить свой метод add() в классе Collection в любом случае, что означает, что вам нужно будет использовать 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

Другой способ:

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

Просто потому, что константа не означает, что вы не можете поставить ее в переменной на мгновение, чтобы удовлетворить синтаксический анализатор.

Ответ 3

Я создаю класс Collection, который представляет собой совокупность объектов/объектов домена. Он вроде бы моделируется после объекта List<T> в .Net.

Как правило, не рекомендуется писать один язык на другом языке. Вам не нужны коллекции в PHP.

Если вы продолжите работу по этой дороге, возможно, вам стоит подумать об использовании инструментов, предоставленных вам PHP. Например, ArrayObject, который вы можете наследовать и переопределять необходимые методы, чтобы гарантировать, что в массив попадают только правильно введенные элементы. ArrayObjects можно использовать в любом месте PHP, где может использоваться обычный массив. Кроме того, основные биты и куски уже написаны для вас.

Остальная часть стандартной библиотеки PHP может вас заинтересовать, SplObjectStorage в частности.

Ответ 4

Я тоже удивлен этим поведением, но это должно работать:

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

EDIT:

Вы могли бы сделать

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();
        }
    }
}

Но вы включаете complicometer. У этого есть дополнительные накладные расходы на создание переменной экземпляра $type для каждого экземпляра PostCollection (и нет, вы не можете просто добавить static в свойство $type).

Ответ 5

он должен быть следующим образом

public function add (ICollection $obj) {

}

теперь, если вы попытаетесь добавить объект к добавлению функции, который не является экземпляром Icollection (это пример), тогда он потерпит неудачу, прежде чем вы даже проведете проверку с помощью экземпляра.

Ответ 6

Попробуйте использовать PHP is_a вместо instanceof, поскольку ожидает, что строка будет именем класса.