Тип PHPDoc намекает на массив объектов?
Итак, в PHPDoc можно указать @var
над объявлением переменной-члена, чтобы намекнуть на ее тип. Затем IDE, например. PHPEd, будет знать, с каким типом объекта он работает, и сможет обеспечить понимание кода для этой переменной.
<?php
class Test
{
/** @var SomeObj */
private $someObjInstance;
}
?>
это отлично работает, пока мне не нужно сделать то же самое с массивом объектов, чтобы иметь возможность получить правильный намек, когда я повторяю эти объекты позже.
Итак, есть ли способ объявить тег PHPDoc, чтобы указать, что переменная-член представляет собой массив SomeObj
s? @var
массив не хватает, а @var array(SomeObj)
не кажется действительным, например.
13 ответов:
лучшее, что вы можете сделать, это сказать,
foreach ($Objs as $Obj) { /* @var $Obj Test */ // You should be able to get hinting after the preceding line if you type $Obj-> }
Я делаю это много в Zend Studio. Не знаю, как другие редакторы, но это должно работать.
в IDE PhpStorm от JetBrains вы можете использовать
/** @var SomeObj[] */
, например:/** * @return SomeObj[] */ function getSomeObjects() {...}
The документация phpdoc рекомендует этот метод:
задано содержит один тип, определение типа информирует читателя о типе каждого элемента массива. Только один тип ожидается в качестве элемента для данного массива.
пример:
@return int[]
подсказки Netbeans:
вы получаете завершение кода на
$users[0]->
и$this->
для различных классов пользователей./** * @var User[] */ var $users = array();
вы также можете увидеть тип массива в списке членов класса, когда вы делаете завершение
$this->...
для указания переменной используется массив объектов:
$needles = getAllNeedles(); /* @var $needles Needle[] */ $needles[1]->... //codehinting works
это работает в Netbeans 7.2 (я использую его)
работает также с:
$needles = getAllNeedles(); /* @var $needles Needle[] */ foreach ($needles as $needle) { $needle->... //codehinting works }
поэтому использование объявления внутри
foreach
не надо.
PSR-5: PHPDoc предлагает форму нотации в стиле дженериков.
синтаксис
Type[] Type<Type> Type<Type[, Type]...> Type<Type[|Type]...>
значения в коллекции могут быть даже другим массивом и даже другой коллекцией.
Type<Type<Type>> Type<Type<Type[, Type]...>> Type<Type<Type[|Type]...>>
примеры
<?php $x = [new Name()]; /* @var $x Name[] */ $y = new Collection([new Name()]); /* @var $y Collection<Name> */ $a = new Collection(); $a[] = new Model_User(); $a->resetChanges(); $a[0]->name = "George"; $a->echoChanges(); /* @var $a Collection<Model_User> */
Примечание: Если вы ожидаете, что IDE будет делать code assist, то это еще один вопрос о том, поддерживает ли IDE нотацию коллекций общего стиля PHPDoc.
от моего ответа до этот вопрос.
Я предпочитаю читать и писать чистый код - как описано в "чистом коде" Роберта С. Мартина. Следуя его кредо, вы не должны требовать от разработчика (как пользователя вашего API) знать (внутреннюю) структуру вашего массива.
пользователь API может спросить: это массив только с одним измерением? Распределены ли объекты по всем уровням многомерного массива? Сколько вложенных циклов (foreach и др.) мне нужно получить доступ ко всем объектам? В каком типе объектов "хранятся" этот массив?
как вы указали, вы хотите использовать этот массив (который содержит объекты) в качестве одномерного массива.
как указано Nishi вы можете использовать:
/** * @return SomeObj[] */
для этого.
но опять же: имейте в виду-это не стандартная нотация docblock. Эта нотация была введена некоторыми производителями IDE.
Хорошо, хорошо, как разработчик вы знаете, что "[]" привязан к массиву в PHP. Но что означает "что-то[]" в обычном контексте PHP? "[] "означает: создать новый элемент внутри "что-то". Новый элемент может быть всем. Но то, что вы хотите выразить: массив объектов с тем же типом и его точным типом. Как вы можете видеть, производитель IDE вводит новый контекст. Новый контекст, который вы должны были изучить. Новый контекст другие разработчики PHP должны были узнать (чтобы понять ваши docblocks). Плохой стиль (!).
поскольку Ваш массив имеет одно измерение, вы, возможно, захотите назвать этот "массив объектов" "списком". Имейте в виду, что "список" имеет очень особое значение в других языках программирования. Например, было бы лучше назвать его "коллекцией".
помните: вы используете язык программирования, который позволяет вам все возможности ООП. Используйте класс вместо массива и сделайте свой класс проходимым как массив. Например:
class orderCollection implements ArrayIterator
или если вы хотите хранить внутренние объекты на разных уровнях в многомерном массиве / структуре объекта:
class orderCollection implements RecursiveArrayIterator
это решение заменяет массив объектом типа "orderCollection", но пока не разрешает завершение кода в вашей среде IDE. Окей. Следующий шаг:
реализуйте методы, которые вводятся интерфейсом с docblocks-particular:
/** * [...] * @return Order */ orderCollection::current() /** * [...] * @return integer E.g. database identifier of the order */ orderCollection::key() /** * [...] * @return Order */ orderCollection::offsetGet()
не забудьте использовать подсказку типа для:
orderCollection::append(Order $order) orderCollection::offsetSet(Order $order)
это решение перестает вводить много:
/** @var $key ... */ /** @var $value ... */
во всех ваших файлах кода (например, в циклах), как подтвердил Захимака своим ответом. Ваш API-интерфейс пользователя не заставляют представить, что для установки, код завершения. Чтобы иметь @return только в одном месте, уменьшите избыточность (@var)как можно больше. Посыпать "docBlocks with @var" сделает ваш код хуже читаемым.
наконец-то вы закончили. Выглядит трудно достичь? Похоже на кувалду, чтобы расколоть орех? Не очень, так как вы знакомы с этими интерфейсами и с чистым кодом. Помните: ваш исходный код пишется один раз / читается много.
если код завершение вашей IDE не работает с этим подходом, переключитесь на лучший (например, IntelliJ IDEA, PhpStorm, Netbeans) или отправьте запрос на функцию в трекер проблем вашего производителя IDE.
спасибо Кристиану Вайсу (из Германии) за то, что он был моим тренером и научил меня таким замечательным вещам. PS: встретимся с ним на сине.
Как упоминалось в ее ответе DanielaWaranie-есть способ указать тип $item при итерации над $ items в $collectionObject: Add
@return MyEntitiesClassName
доcurrent()
а остальныеIterator
иArrayAccess
-методы, которые возвращают значения.бум! не нужно
/** @var SomeObj[] $collectionObj */
overforeach
, и работает прямо с объектом коллекции, нет необходимости возвращать коллекцию с определенным методом, описанным как@return SomeObj[]
.я подозреваю, что не все IDE поддерживают его, но это отлично работает в PhpStorm, что делает меня счастливее.
пример:
Class MyCollection implements Countable, Iterator, ArrayAccess { /** * @return User */ public function current() { return $this->items[$this->cursor]; } //... implement rest of the required `interface` methods and your custom }
что полезного я собирался добавить, разместив этот ответ
в моем случае
current()
а остальныеinterface
-методы реализованы вAbstract
- класс коллекции, и я не знаю, какие объекты в конечном итоге будут храниться в коллекции.Итак, вот трюк: не указывайте тип возврата в абстрактном классе, вместо этого используйте PHPDoc instuction
@method
в описании конкретного класса коллекции.пример:
Class User { function printLogin() { echo $this->login; } } Abstract Class MyCollection implements Countable, Iterator, ArrayAccess { protected $items = []; public function current() { return $this->items[$this->cursor]; } //... implement rest of the required `interface` methods and your custom //... abstract methods which will be shared among child-classes } /** * @method User current() * ...rest of methods (for ArrayAccess) if needed */ Class UserCollection extends MyCollection { function add(User $user) { $this->items[] = $user; } // User collection specific methods... }
теперь, использование классов:
$collection = new UserCollection(); $collection->add(new User(1)); $collection->add(new User(2)); $collection->add(new User(3)); foreach ($collection as $user) { // IDE should `recognize` method `printLogin()` here! $user->printLogin(); }
еще раз: я подозреваю, что не все IDE поддерживают его, но PhpStorm делает. Попробуйте ваш, напишите в комментарии результаты!
в NetBeans 7.0 (может быть и ниже) вы можете объявить возвращаемый тип "массив с текстовыми объектами" так же, как
@return Text
и код намекая будет работать:Edit: обновил пример с предложением @Bob Fanger
/** * get all Tests * * @return Test|Array $tests */ public function getAllTexts(){ return array(new Test(), new Test()); }
и просто использовать это:
$tests = $controller->getAllTests(); //$tests-> //codehinting works! //$tests[0]-> //codehinting works! foreach($tests as $text){ //$test-> //codehinting works! }
это не идеально, но лучше тогда просто оставить его просто "смешанным", ведьма не приносит никакой ценности.
минусы вы можете протектора массива как текстовый объект ведьма будет бросьте ошибки.
использовать
array[type]
в студии Zend.В Студии Zend,
array[MyClass]
илиarray[int]
или дажеarray[array[MyClass]]
работа отличная.
проблема в том, что
@var
можно просто обозначить один тип-не содержит сложной формулы. Если у вас был синтаксис для "array of Foo", почему бы не остановиться там и не добавить синтаксис для "array of array, который содержит 2 Foo и три бара"? Я понимаю, что список элементов, возможно, более общий, чем это, но это скользкий путь.лично я несколько раз использовал
@var Foo[]
для обозначения "массива Foo", но он не поддерживается IDE.
<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?> <?php // Type hinting now works: $model->getImage(); ?> <?php endforeach; ?>
Я знаю, что опаздываю на вечеринку, но я недавно работал над этой проблемой. Я надеюсь, что кто-то видит это, потому что принятый ответ, хотя и правильный,не лучший способ, которым вы можете это сделать. По крайней мере, не в PHPStorm, я не тестировал NetBeans.
лучший способ заключается в расширении класса ArrayIterator вместо использования собственных типов массивов. Это позволяет вводить подсказку на уровне класса, а не на уровне экземпляра, что означает, что вам нужно только PHPDoc один раз, а не во всем коде (который не только грязный и нарушает сухой, но также может быть проблематичным, когда речь заходит о рефакторинге-PHPStorm имеет привычку пропускать PHPDoc при рефакторинге)
посмотреть код ниже:
class MyObj { private $val; public function __construct($val) { $this->val = $val; } public function getter() { return $this->val; } } /** * @method MyObj current() */ class MyObjCollection extends ArrayIterator { public function __construct(Array $array = []) { foreach($array as $object) { if(!is_a($object, MyObj::class)) { throw new Exception('Invalid object passed to ' . __METHOD__ . ', expected type ' . MyObj::class); } } parent::__construct($array); } public function echoContents() { foreach($this as $key => $myObj) { echo $key . ': ' . $myObj->getter() . '<br>'; } } } $myObjCollection = new MyObjCollection([ new MyObj(1), new MyObj('foo'), new MyObj('blah'), new MyObj(23), new MyObj(array()) ]); $myObjCollection->echoContents();
ключевым моментом здесь является PHPDoc
@method MyObj current()
переопределение возвращаемого типа, унаследованного от ArrayIterator (который являетсяmixed
). Включение этого PHPDoc означает, что когда мы перебираем свойства класса с помощьюforeach($this as $myObj)
, то мы получим код завершение при обращении к переменной$myObj->...
для меня это самый аккуратный способ достичь этого (по крайней мере, пока PHP не введет типизированные массивы, если они когда-либо это сделают), поскольку мы объявляем тип итератора в классе iterable, а не на экземплярах класса, разбросанных по всему коду.
Я не показал здесь полное решение для расширения ArrayIterator, так что если вы используете эту технику, вы также можете:
- включить другой класс-уровень PHPDoc по мере необходимости, для таких методов, как
offsetGet($index)
иnext()
- переместить проверку здравомыслия
is_a($object, MyObj::class)
из конструктора в отдельный метод- вызовите эту (теперь частную) проверку здравомыслия из переопределений методов, таких как
offsetSet($index, $newval)
иappend($value)