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, поскольку ожидает, что строка будет именем класса.