Тип PHPDoc намекает на массив объектов?

Таким образом, в PHPDoc можно указать @var над объявлением переменной элемента, чтобы намекнуть на его тип. Затем IDE, например. PHPEd, будет знать, с каким типом объекта он работает, и сможет предоставить представление кода для этой переменной.

 <?php class Test { /** @var SomeObj */ private $someObjInstance; } ?> 

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

Итак, есть способ объявить тег PHPDoc, чтобы указать, что переменная-член является массивом SomeObj s? Массив @var недостаточно, и @var array(SomeObj) , похоже, не является допустимым.

    Лучшее, что вы можете сделать, это сказать,

     foreach ($Objs as $Obj) { /* @var $Obj Test */ // You should be able to get hinting after the preceding line if you type $Obj-> } 

    Я делаю это много в Zend Studio. Не знаю других редакторов, но он должен работать.

    В среде PhpStorm IDE из JetBrains вы можете использовать /** @var SomeObj[] */ , например:

     /** * @return SomeObj[] */ function getSomeObjects() {...} 

    Документация phpdoc рекомендует этот метод:

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

    Пример: @return int[]

    Netbeans намекает:

    Вы получаете завершение кода для $users[0]-> и для $this-> для массива классов User.

     /** * @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 предлагает форму новизны в стиле Generics.

    Синтаксис

     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 сделает помощь по коду, тогда возникает еще один вопрос о том, поддерживает ли IDE нотацию коллекций в стиле Generic стиля PHPDoc.

    Из моего ответа на этот вопрос .

    Я предпочитаю читать и писать чистый код – как указано в «Чистом коде» Роберта К. Мартина. Следуя его кредо, вы не должны требовать от разработчика (как пользователя вашего API) знать (внутреннюю) структуру вашего массива.

    Пользователь API может спросить: это только массив с одним измерением? Распространены ли объекты на всех уровнях многомерного массива? Сколько вложенных циклов (foreach и т. Д.) Мне нужно получить доступ ко всем объектам? Какие типы объектов «хранятся» в этом массиве?

    Как вы указали, вы хотите использовать этот массив (который содержит объекты) как одномерный массив.

    Как указано Ниши, вы можете использовать:

     /** * @return SomeObj[] */ 

    для этого.

    Но опять же: будьте внимательны – это не стандартная нотация докблока. Эта нотация была представлена ​​некоторыми производителями IDE.

    Хорошо, хорошо, как разработчик, вы знаете, что «[]» привязан к массиву в PHP. Но что означает «что-то []» в обычном контексте PHP? «[]» означает: создать новый элемент внутри «что-то». Новый элемент может быть всем. Но то, что вы хотите выразить, это: массив объектов одного типа и точный тип. Как вы можете видеть, производитель IDE представляет новый контекст. Новый контекст, который вам нужно было изучить. Новый контекст, который другие разработчики PHP должны были изучить (чтобы понять ваши докблоки). Плохой стиль (!).

    Поскольку ваш массив имеет один размер, вы, возможно, захотите назвать этот «массив объектов» «списком». Имейте в виду, что «список» имеет особое значение для других языков программирования. Например, было бы лучше назвать его «коллекцией».

    Помните: вы используете язык программирования, который позволяет вам использовать все опции ООП. Используйте класс вместо массива и сделайте свой класс пройденным, как массив. Например:

     class orderCollection implements ArrayIterator 

    Или если вы хотите сохранить внутренние объекты на разных уровнях в многомерной структуре массива / объекта:

     class orderCollection implements RecursiveArrayIterator 

    Это решение заменяет ваш массив объектом типа «orderCollection», но до сих пор не включает завершение кода в вашей среде IDE. Хорошо. Следующий шаг:

    Внедрить методы, которые вводятся интерфейсом с docblocks – в частности:

     /** * [...] * @return Order */ orderCollection::current() /** * [...] * @return integer Eg database identifier of the order */ orderCollection::key() /** * [...] * @return Order */ orderCollection::offsetGet() 

    Не забудьте использовать тип намека для:

     orderCollection::append(Order $order) orderCollection::offsetSet(Order $order) 

    Это решение перестает вводить много:

     /** @var $key ... */ /** @var $value ... */ 

    во всех своих файлах кода (например, в циклах), как подтвердил Займака своим ответом. Ваш пользователь API не вынужден вводить эти докблоки, чтобы иметь завершение кода. Для того, чтобы @ возвращаться только в одном месте, уменьшает избыточность (@var) как можно больше. Посыпать «docBlocks с помощью @var» сделает ваш код хуже читаемым.

    Вы закончили. Выглядит сложно? Похоже, взять кувалду, чтобы взломать орех? Не реально, поскольку вы знакомы с этими интерфейсами и с чистым кодом. Помните: ваш исходный код написан один раз / прочитал много.

    Если завершение кода вашей среды IDE не работает с этим подходом, переключитесь на лучший (например, IntelliJ IDEA, PhpStorm, Netbeans) или напишите запрос функции на трекер проблемы вашего производителя среды IDE.

    Спасибо Кристиану Вайсу (из Германии) за то, что он был моим тренером и преподавал мне такой замечательный материал. PS: Познакомьтесь с ним и с ним по XING.

    В NetBeans 7.0 (может быть и ниже) вы можете объявить возвращаемый тип «array with Text objects» так же, как @return Text и @return Text кода будет работать:

    Изменить: обновил пример с помощью предложения @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! } 

    Это не идеально, но лучше тогда просто оставить его просто «смешанным», ведьма не приносит никакой ценности.

    CONS – вам разрешено протащить массив, поскольку объект Text Object будет вызывать ошибки.

    Как упоминала DanielaWaranie в своем ответе – есть способ указать тип элемента $, когда вы выполняете итерацию над $ items в $ collectionObject: добавьте @return MyEntitiesClassName в current() и остальные ArrayAccess Iterator и ArrayAccess которые возвращают значения.

    Boom! Нет необходимости в /** @var SomeObj[] $collectionObj */ over foreach и работает правильно с объектом коллекции, нет необходимости возвращать коллекцию с помощью определенного метода, описанного как @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 -collection, и я не знаю, какие объекты будут в конечном итоге сохранены в коллекции.

    Итак, вот трюк: не указывайте тип возвращаемого значения в абстрактном классе, вместо этого используйте метод phpDoc @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 делает. Попробуйте, опубликуйте в комментариях результаты!

    Используйте array[type] в Zend Studio.

    В Zend Studio array[MyClass] или array[int] или даже array[array[MyClass]] отлично работают.

    Проблема в том, что @var может просто обозначать один тип – не содержать сложную формулу. Если у вас есть синтаксис для «массива Foo», зачем останавливать его и не добавлять синтаксис для «массива массива, который содержит 2 Foo и три бара»? Я понимаю, что список элементов, возможно, более общий, чем этот, но это скользкий наклон.

    Лично я несколько раз использовал @var Foo[] чтобы обозначить «массив Foo's», но он не поддерживается IDE.

     <?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?> <?php // Type hinting now works: $model->getImage(); ?> <?php endforeach; ?> 

    Я знаю, что опаздываю на вечеринку, но недавно я работал над этой проблемой. Я надеюсь, что кто-то это увидит, потому что принятый ответ, хотя и правильный, не самый лучший способ сделать это. По крайней мере, не в PHPStorm, я не тестировал NetBeans.

    Лучший способ заключается в расширении класса ArrayIterator вместо использования собственных типов массивов. Это позволяет вводить подсказку на уровне класса, а не на уровне экземпляра, а это означает, что вы должны иметь только PHPDoc один раз, а не весь свой код (который не только беспорядочен и нарушает DRY, но также может быть проблематичным, когда дело доходит до рефакторинг – 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 не представит типизированные массивы, если они когда-либо будут), поскольку мы объявляем тип итератора в итерируемом классе, а не в экземплярах класса, разбросанного по всему коду.

    Я не показал здесь полного решения для расширения ArrayIterator, поэтому, если вы используете эту технику, вы также можете:

    • Включите другой PHPDoc на уровне класса, если требуется, для таких методов, как offsetGet($index) и next()
    • Переместите проверку is_a($object, MyObj::class) из конструктора в частный метод
    • Вызовите эту (теперь закрытую) проверку offsetSet($index, $newval) из переопределения метода, например offsetSet($index, $newval) и append($value)

    Я нашел что-то, что работает, это может спасти жизни!

     private $userList = array(); $userList = User::fetchAll(); // now $userList is an array of User objects foreach ($userList as $user) { $user instanceof User; echo $user->getName(); }