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

Я работаю над фреймворком, который стараюсь печатать как можно сильнее. (Я работаю в PHP и беру некоторые идеи, которые мне нравятся, из C# и пытаюсь использовать их в этой среде.) Я создаю класс 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 в выражении. Добавление круглых скобок вокруг переменной 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.


person Nathan Loding    schedule 09.06.2010    source источник
comment
Я думаю, что ответил на 1 или 2 - хотя переменная protected, ее все еще можно изменить в коде. При этом нет никакой реальной безопасности, поскольку переменная $type не будет изменена случайно. На что я надеялся, так это на страховку от этого.   -  person Nathan Loding    schedule 09.06.2010
comment
Если я правильно понимаю, вы даете операции instanceof строку ($this-›type), тогда как это должно быть просто имя класса (см. php.net/manual/en/language.operators.type.php). Я думаю, вам следует вместо этого использовать 'is_a', так как это ожидает строку, которая представляет собой $this-›type.   -  person gacrux    schedule 09.06.2010
comment
Неверное утверждение — посмотрите пример № 5 на странице, на которую вы ссылаетесь.   -  person Nathan Loding    schedule 09.06.2010
comment
Ах, правда, мой плохой, спасибо за ваш комментарий.   -  person gacrux    schedule 09.06.2010
comment
Кстати, отступ в 2 пробела — это ересь.   -  person Artefacto    schedule 09.06.2010
comment
У меня плохо с отступами - я на самом деле не кодирую так, я просто быстро печатал для примера.   -  person Nathan Loding    schedule 10.06.2010


Ответы (6)


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

<?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());
person gnarf    schedule 09.06.2010

Другой обходной путь:

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

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

person VoteyDisciple    schedule 09.06.2010
comment
или что не так со скобками? !($obj instanceof (self::type)) - person seanmonstar; 09.06.2010
comment
@seanmonstar - именно это выражение вызвало у меня ошибку в PHP. Это сработало для вас? Я получил ошибку относительно «неожиданного ('. - person Nathan Loding; 10.06.2010
comment
@ Натан, нет, я на самом деле не открывал оболочку и не проверял ее, извините. Я просто предположил, что это вопрос приоритета оператора, и он анализировал его неправильно, например ($obj instanceof self)::type - person seanmonstar; 10.06.2010
comment
@ Натан, хотя, глядя на, ты не мог бы использовать get_class($obj) == self::type? - person seanmonstar; 10.06.2010
comment
@seanmonstar - я думаю, что в конечном итоге я закончу утверждением is_a($obj, self::type). Я пытаюсь решить прямо сейчас, действительно ли я хочу идти по этому пути... - person Nathan Loding; 10.06.2010

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

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

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

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

person Charles    schedule 09.06.2010
comment
За исключением того, что я сказал, что это своего рода модель. Мне нужен контейнер для нескольких объектов одного типа и возможность легко добавлять, удалять, находить и перебирать их — и это в приложении PHP. Если бы это было приложение .Net, я бы использовал объект List<T>. В PHP я имею удовольствие создавать свои собственные варианты. - person Nathan Loding; 09.06.2010
comment
Возможно, вы захотите прочитать мой пост после первого абзаца, поскольку я продолжаю объяснять, как PHP может уже предоставить вам инструменты, необходимые для создания такого поведения без реализации конструкции из совсем другого языка. :) - person Charles; 09.06.2010
comment
Параметры SplObjectStorage и ArrayObject не совсем то, что мне нужно. Я не вижу никаких причин не пытаться создать конструкцию из одного языка в другой, если только нет технических причин, по которым это невозможно. В этом случае создание класса коллекции, который имитирует объект List в .Net, очень возможно в PHP, но это строго типизированная часть, которой нет. Преимущество PHP над .Net заключается в том, что вы можете создавать эти объекты самостоятельно. Я не пишу на одном языке на другом — я изучаю возможности одного языка, основываясь на опыте работы с другим языком. - person Nathan Loding; 10.06.2010
comment
Я не вижу никаких причин не пытаться создать конструкцию из одного языка в другой, если только... Каждый язык не связан с культурой. Эта культура часто устанавливает способ ведения дел, стандартную рабочую процедуру, общий набор идиом. Давайте на мгновение остановимся на Perl. Вполне возможно написать PHP на Perl. Вы можете создать все встроенные модули и заставить ваши программы Perl работать точно так же, как они были написаны на PHP. Однако покажите этот код Perl-программисту, и он посмотрит на вас так, как будто у вас выросла вторая голова. Способность не означает необходимость. - person Charles; 10.06.2010
comment
При этом +1 за изучение возможностей языка. Даже плохие идеи могут быть хорошими идеями, если в результате вы чему-то научились. - person Charles; 10.06.2010
comment
Единственная причина, по которой я до сих пор программирую на PHP, — это его огромная гибкость. Это также его окончательный недостаток. Но гибкость не обязательно означает отсутствие структуры в вашем коде. Я часто обнаруживал, что PHP-программистам (надеюсь, меня за это не сожгут заживо) не хватает той же структуры и планирования, что и .Net-программисту. Не поймите меня неправильно, некоторые PHP-приложения закодированы безупречно, превосходя все, что есть в .Net, а некоторые .Net-приложения ужасны. Но в целом я считаю, что гибкость PHP приводит к плохим стандартам кодирования. Это то, от чего я пытаюсь держаться подальше. - person Nathan Loding; 10.06.2010
comment
Поскольку программисты C# (и Java) имеют ограниченный набор инструментов, они разработали другой стиль создания кода по сравнению с PHP. Программисты PHP также ограничены набором инструментов, но совершенно по-другому. Обычно наоборот. Быстро и грязно делает работу в 75% случаев. Попробуйте сделать MVC в PHP, и вы не найдете ничего, что могло бы вам помочь без нового или стороннего кода, который неизбежно идет вразрез с тем, как обычно работает PHP. Это благословение и проклятие. Кроме того, если вам нужна настоящая гибкость, я призываю вас работать и с Perl, он позволит вам делать то, что PHP не может. Лось отличный. - person Charles; 10.06.2010

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

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

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

person Artefacto    schedule 09.06.2010
comment
@Nathan Loding Ваш новый код дал мне PHP Notice: Undefined property: PostCollection::$type. - person Artefacto; 09.06.2010
comment
Я сделал опечатку! Определение класса должно быть class PostCollection extends Collection -- я обновлю его, хороший улов. - person Nathan Loding; 09.06.2010

это должно быть так

общедоступная функция добавления (ICollection $ obj) {

}

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

person Syed    schedule 09.06.2010
comment
Это хорошая мысль — в данном случае это будет объект Entity, но хорошая основа. - person Nathan Loding; 10.06.2010

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

person gacrux    schedule 09.06.2010
comment
Это неверное утверждение. Посмотрите пример № 5 (использование instanceof с другими переменными) на php.net/manual. /en/language.operators.type.php. - person Nathan Loding; 09.06.2010