В общем, как это ни грустно.... но PHP: <?php abstract class A {} class B extends A { public $id; } abstract class C { abstract public function say(A $msg); } class D extends C { public function say(B $msg) { var_dump($msg); } } $b = new B; $b->id = 'sample'; $d = new D; $d->say($b); Такое работать не будет. Несмотря на то, что B необтрузивно реализует все интерфейсы A. Так что для, желающих наследования с typehinting о последнем придется позаботиться самостоятельно Например вот так PHP: <?php class D extends C { public function say(A $msg) { if (! ($msg instanceof B)) { throw new Exception('Instance of B required'); } else { var_dump($msg); } } } А так хотелось без лишних телодвижений отделить структуру модели от методов работы с ней
Сложный вопрос и мне лень на него искать ответ. Для функциональности достаточно наследования. А хотелось именно typehinting
Брр. Они абсолютно одинаковые с точки зрения интерфейса и выполняемой функции (добавить/изменить/удалить) И абсолютно плевать, как они там внутри это все делают, если входные и выходные параметры соответствуют интерфейсам, а функция соответствует заявленной. Разница даже не в типе принимаемых данных. А в их структуре. Условно говоря мне нужен на вход массив ровно с четырьмя/пятью/двадцатью элементами и конкретными именованными ключами. И я хочу это проконтролировать. Выяснилось, что кроме явного проброса Exception, этого не сделать никак. Внутренняя обработка абсолютно идентична у всех классов потомков. Мы берем этот массив и маппим по внутренним правилам на структуру БД. Была бы готовая структура БД - проблем бы не было Но увы, приходится плясать от того что она обязательно изменится несколько десятков раз. В конечном счете решится это дело кодогенерацией, как обычно, но мне банально лень
Удивительного нет вообще. Есть лишь сожаление, что не получится уменьшить себе работы А ведет оно себя вполне логично. Потомок должен свободно заменять предка. А вот уже два соседних потомка - не обязательно могут заменить друг друга. Поэтому переопределение интерфейса и не работает во избежание ошибок. Что касается A и B разные типы - это не совсем так. PHP: <?php abstract class A {} class B extends A { public $id; } class R extends A { } $b = new B; $r = new R; var_dump($b instanceof A); //true var_dump($r instanceof A); //true var_dump($r instanceof B); //false
чего-то не понял из примера, почему а и б - это не разные типы (или не совсем разные). как же ж одинаковые, если одна объявлена с параметром объект типа А, а другая - типа Б?
То, что у них один родитель, нам ничего не говорит относительно типов. Каждый класс - это отдельный тип, независимо от родителя, даже если они полностью идентичны: PHP: <?php class A {} class B {} class C { public function property(A $obj) { echo 1; } } $c = new C; $a = new A; $b = new B; $c->property($a); //1 $c->property($b); //Catchable fatal error: Argument 1 passed to C::property() must be an instance of A, instance of B given
Потому что B наследует и реализует все отличительные особенности A. Поэтому при проверке принадлежит ли B к типу A - ответ будет утвердительным. R и B примером ниже это уже разные типы. Но суть не в этой фигне. Вопрос, который побудил меня это проверить я описал ниже. Я просто хотел переложить часть работы на синтаксический анализатор языка. Выяснилось, что неявно перегрузить метод нельзя! Т.е. только magic методы или способ приведенный мной. Хм. Есть мысля. А что если сделать два интерфейса с разным определением одного и того же метода. И попробовать создать класс на их базе... Сейчас проверю.
Все. Низя. PHP: <?php interface AE { public function say(E $msg); } interface AB { public function say(B $msg); } class XA implements AE, AB { public function say(E $msg) { var_dump($msg); } public function say(B $msg) { var_dump($msg); } } Ну значит так тому и быть.
конечно низя. Это ж не ява, где значения по умолчанию реализуются перегрузкой [js]public int method(int a, int b, int c) { return a + b + c; } public int method(int a, int b) { return method(a, b, 2); } public int method(int a) { return method(a, 1); } [/js] а меня немного удивляет такое: PHP: <?php class A { CONST B = 'foo'; } class C extends A {} C::B // а нет тут ничего!
Перегрузка нужна не только для значений по-умолчанию. В принципе все оно реализуется и на PHP через __call() Вот только автокомплита не будет - а это большой минус.
о да, описанное в первом сообщении меня огорчило, когда я взял свой класс Map из obj-php и постарался сделать из него специализированное хранилище объектов, переопределив метод public function put ($var) так: public function put (MyObject $object). Типа того. Очень огорчило, что нельзя.
Большинство IDE понимают PHPDoc: PHP: <?php /** * Some class * @method int borp() borp(int $int1, int $int2) multiply two integers */ class Magician { function __call($method, $params) { if ($method == 'borp' && count($params) == 2) return $params[0] * $params[1]; } }
Понимать понимают. Но крайне криво. Т.е. таким образом можно дать знать что метод есть. И не более того. А какие у него параметры, что он возвращает - ничерта не показывается.
Твое кунг-фу круче моего? Не вопрос. Покажи мне, как прописать в phpDoc типы аргументов для методов которые вызываются только через __call(), и я тебе скажу спасибо.
Simpliest Ну извините, это скриптовый язык - вам шашечки или ехать? Объявите методы нормальным образом, как public со всеми phpDoc's, если надо - используйте интерфейсы и будет вам счастье блин. Не нужно подходить через Ж к задаче только потому, что язык умеет вот эту крутую фитчу, она новая и я должен её впихнуть! Я уже не говорю, что сам мануал настаивает на обдуманном использовании этой функциональности, т.к. она достаточно тормознутая и нужно сто раз подумать, прежде чем её использовать.
Еще ни разу не использовал __get, __set, __call, ArrayAccess. Ну не было необходимости и все тут. Зато все время использую get_called_class, которого нет в <5.30 и он у меня через Ж написан (парсит файл через регуоярки).
IMHO __call относится к той же чепухе, что и goto и register_globals, хотя это все разные фичи, я говорю о том что это лишняя чепуха. Лучше написать код проще но длинее.
Не-не. Вот смотри, есть объект машина. У машины, по определению есть двигатель, колеса, руль - все это объекты. Связать их можно через машину. По отдельности они так и останутся запчастями. Соответственно, доступ к этим объектам должен быть через машину, чтобы понять, что запчасти именно её. Из чего следует, что у объекта машина должны быть методы, возвращающие объекты её колес, руля, двигателя. Когда таких объектов мало, можно в классе машины их описать, когда много - не имеет смысла. Для этого и существует _call PHP: <?php class Car { function __call($method) { //получаем данные для этого того, чтобы создать экземпляр класса конкретной запчасти машины именно этой модели return new $method($args); } } Таким, образом, взаимодействие запчастей друг на друга будет проще: $mycar->колеса->передняя_ось->поворот($mycar->руль->поворот(60 градусов))
Не извиню. Вы изволили сказать что это возможно. А ездить я предпочитаю на кодогенераторах, потому что, несмотря на все удобство __get/__set/__call в определенных моментах, они чрезвычайно медленны. Может и лучше, но ты потом модернизировать эту лапшу пробовал? В моем случае задача решилась через проброс исключения. И несколько некорректный phpDoc (хотя и A и B это банальные DTO без методов вовсе) PHP: <?php /** * @param B $data */ function update(A $data) { if (! ($data instanceof B)) { throw new Exception('Instance of B required'); } } Более правильным же решением будет генерация пустого DTO с соответствующим phpDoc. Но пока переписывать лень.
Это опять спор о ООП и функциях. Объектом может быть только машина, а двигатель, колеса и руль могут быть методами, и чтобы избежать копипаста, характеристики получать из единой сигнатуры. Наверное, во мне все еще живет стереотип, что много объектов - это плохо
а у меня сайчас такая галиматья начала писаться. PHP: <?php include_once 'includes/common/class.Modulator.php'; // прописываем схему модуля $scheme = array( 'module' => 'news', 'defaultSubModules' => array('entries') ); // типа смотрим на подмодуль switch (AdminRouter::instance()->getActiveSubmodules(0)) { case 'categories': include_once 'includes/admin/modules/news/categories/class.ManagerNewsCategories.php'; include_once 'includes/admin/modules/news/categories/class.ListerNewsCategories.php'; $scheme['main'] = 'ManagerNewsCategories'; $scheme['lister'] = 'ListerNewsCategories'; $scheme['subModule'] = array('categories'); break; case 'entries': default: include_once 'includes/admin/modules/news/entries/class.ManagerNews.php'; include_once 'includes/admin/modules/news/entries/class.ListerNews.php'; $scheme['main'] = 'ManagerNews'; $scheme['lister'] = 'ListerNews'; $scheme['subModule'] = array('entries'); break; } // вызываем наш модуль с заданной схемой new Modulator($scheme); этот Modulator создаст экземпляр класса (ManagerNews или ManagerNewsCategories), добавит свою инстанцию туда через метод setModulator, потом, возможно создастся ListerNews или ListerNewsCategories и в него тоже будет помещена инстанция Модулятора. Вот такое гавно получается. Сам не знаю, как к такому пришел. Просто некоторыми данными между Lister* и Manager* нужно обмениваться, поэтому так и получилось. зыыы: руль - не метод. Руль тогда уж свойство. А крутить руль - метод.