Podpowiedzi typu PHPDoc dla tablicy obiektów?

417

Tak więc w PHPDoc można wskazać @varpowyżej deklaracji zmiennej składowej, aby wskazać jej typ. Następnie IDE, np. PHPEd, będzie wiedział z jakiego typu obiektem współpracuje i będzie w stanie zapewnić wgląd w kod dla tej zmiennej.

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

Działa to świetnie, dopóki nie muszę zrobić tego samego z szeregiem obiektów, aby móc uzyskać odpowiednią wskazówkę podczas iteracji przez te obiekty później.

Czy istnieje sposób na zadeklarowanie znacznika PHPDoc w celu określenia, że ​​zmienna składowa jest tablicą SomeObjs? @vartablica jest niewystarczająca i @var array(SomeObj)na przykład wydaje się nieprawidłowa.

Artem Russakovskii
źródło
2
W blogu deweloperskim
John Carter
3
@therefromhere: twój link jest uszkodzony. Myślę, że nowy adres URL to: blogs.oracle.com/netbeansphp/entry/php_templates_improved
DanielaWaranie
Dla osób takich jak ja, przechodzenie i szukanie odpowiedzi: jeśli korzystasz z PHPStorm, spójrz na najczęściej głosowaną odpowiedź: ma konkretną wskazówkę! stackoverflow.com/a/1763425/1356098 (nie oznacza to, że powinna to być odpowiedź dla PO, ponieważ prosi on na przykład o PHPEd)
Erenor Paz

Odpowiedzi:

337

Posługiwać się:

/* @var $objs Test[] */
foreach ($objs as $obj) {
    // Typehinting will occur after typing $obj->
}

podczas wpisywania zmiennych wbudowanych oraz

class A {
    /** @var Test[] */
    private $items;
}

dla właściwości klasy.

Poprzednia odpowiedź z '09, kiedy PHPDoc (i IDE, takie jak Zend Studio i Netbeans) nie miały tej opcji:

Najlepsze, co możesz zrobić, to powiedzieć:

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

Często to robię w Zend Studio. Nie wiem o innych edytorach, ale powinno działać.

Zahymaka
źródło
3
To ma sens, ale nie działało w PHPEd 5.2. Jedyne, co udało mi się wymyślić, to zadziałało, to foreach ($ Objs as / ** @var Test * / $ Obj), co jest okropnie brzydkie. :(
Artem Russakovskii
14
Uwaga w Netbeans 7 wydaje się być ważna, /** @var $Obj Test */ponieważ masz tylko jedną gwiazdkę - nie działa.
contrebis,
3
@contrebis: „@var” jest prawidłowym znacznikiem docblock. Więc nawet jeśli twoje IDE nie obsługuje go w docblocku „/ ** ... /” i obsługuje „@var” tylko w „/ ... * /” - proszę nie zmieniaj swojego poprawnego docblocka. Zgłoś problem do narzędzia do śledzenia błędów swojego IDE, aby IDE było zgodne ze standardami. Wyobraź sobie, że twój zespół programistów / zewnętrzni programiści / społeczność używają różnych IDE. Zachowaj tak, jak jest i przygotuj się na przyszłość.
DanielaWaranie
181
Upewnij się, że spojrzysz poniżej! Prawie nie przewinąłem w dół - byłby WIELKI BŁĄD !!! Wiele IDE będzie obsługiwać lepszą składnię! (wskazówka: @var Object[] $objectsmówi, że „$ objects” to tablica instancji Object.)
Thom Porter,
10
/** @var TYPE $variable_name */jest poprawną składnią; nie odwracaj kolejności typów i nazw zmiennych (jak sugerowano wcześniej w komentarzach), ponieważ nie będzie to działać we wszystkich przypadkach.
srcspider
893

W PhpStorm IDE z JetBrains możesz użyć /** @var SomeObj[] */np .:

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

Dokumentacja phpdoc zaleca tę metodę:

określona jako zawierająca jeden typ, definicja typu informuje czytelnika o typie każdego elementu tablicy. Tylko jeden typ jest wtedy oczekiwany jako element dla danej tablicy.

Przykład: @return int[]

Nishi
źródło
10
Właśnie pobrałem i używałem phpstorm przez ostatni tydzień. Uderza cholera z Aptana (która jest świetna do bycia wolnym). Właśnie tego szukałem. Właściwie to powinienem się domyślać, że robisz to w JavaScript
Juan Mendes,
3
Dzięki stary! Właśnie tego szukałem. PHPStorm jest fantastyczny.
Erik Schierboom
5
To nie działa w Netbeans, jestem rozczarowany. Jetbrains tworzą bardzo ładne narzędzia.
Keyo
10
@fishbone @Keyo działa teraz w Netbeans (przynajmniej w wersji 7.1 co najmniej, może wcześniej), choć wydaje się, że musisz użyć zmiennej tymczasowej (błąd?). Podpowiedź do foreach(getSomeObjects() as $obj)nie działa, ale działa$objs = getSomeObjects(); foreach($objs as $obj)
John Carter,
2
Byłoby miło mieć @var Obj[string]tablice asocjacyjne.
donquixote
59

Wskazówki dotyczące Netbeans:

Uzyskujesz uzupełnianie kodu dla tablicy klas użytkownika $users[0]->i $this->dla niej.

/**
 * @var User[]
 */
var $users = array();

Możesz także zobaczyć typ tablicy na liście członków klasy po zakończeniu $this->...

użytkownik1491819
źródło
4
działa również w PhpStorm 9 EAP: / ** * @var UserInterface [] * / var $ users = []; // Array of Objs implementujący interfejs
Ronan
Próbowałem tego w NetBeans IDE 8.0.2, ale dostaję sugestie z klasy, w której aktualnie jestem.
Wojciech Jasiński
działa również w Eclipse 4.6.3 (idk, która wersja obsługi została wprowadzona, ale działa i jest to, czego teraz używam)
hanshenrik
Niestety array_pop()z jakiegoś powodu to nie działa po użyciu lub podobnych funkcji. Wydaje się, że Netbeans nie zdaje sobie sprawy, że te funkcje zwracają pojedynczy element tablicy wejściowej.
William W
29

Aby określić zmienną, jest tablica obiektów:

$needles = getAllNeedles();
/* @var $needles Needle[] */
$needles[1]->...                        //codehinting works

Działa to w Netbeans 7.2 (używam go)

Współpracuje również z:

$needles = getAllNeedles();
/* @var $needles Needle[] */
foreach ($needles as $needle) {
    $needle->...                        //codehinting works
}

Dlatego stosowanie deklaracji wewnątrz foreachnie jest konieczne.

Highmastdon
źródło
2
Moim zdaniem to rozwiązanie jest czystsze niż zaakceptowana odpowiedź, ponieważ można używać Foreach wiele razy, a podpowiedzi typów będą działać za /* @var $Obj Test */każdym razem bez nowej adnotacji.
Henry
Widzę tutaj dwa problemy: 1. właściwy phpdoc zaczyna się od /** 2. Prawidłowy format to@var <data-type> <variable-name>
Christian
@Christian 1: głównym pytaniem nie jest phpdoc, ale typowanie 2: poprawny format nie jest taki, jak mówisz, nawet według innych odpowiedzi. W rzeczywistości widzę 2 problemy z twoim komentarzem i zastanawiam się, dlaczego nie tworzysz własnej odpowiedzi we właściwym formacie
Highmastdon
1. Typehinting działa z phpdoc ... jeśli nie używasz docblock, twoje IDE nie będzie próbowało zgadywać tego, co napisałeś w losowym komentarzu. 2. Prawidłowy format, jak również niektóre inne odpowiedzi, podałem powyżej; typ danych przed nazwą zmiennej . 3. Nie napisałem kolejnej odpowiedzi, ponieważ pytanie nie potrzebuje innej i wolę nie tylko edytować kod.
Christian
24

PSR-5: PHPDoc proponuje formę notacji typu Generics.

Składnia

Type[]
Type<Type>
Type<Type[, Type]...>
Type<Type[|Type]...>

Wartości w kolekcji MOGĄ być nawet inną tablicą, a nawet inną kolekcją.

Type<Type<Type>>
Type<Type<Type[, Type]...>>
Type<Type<Type[|Type]...>>

Przykłady

<?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> */

Uwaga: Jeśli spodziewasz się, że IDE wykona asystę kodu, to kolejne pytanie, czy IDE obsługuje notację kolekcji w stylu PHPDoc.

Od mojej odpowiedzi do to pytanie .

Gerard Roche
źródło
Ogólna notacja została usunięta z PSR-5
zored
11

Wolę czytać i pisać czysty kod - jak opisano w „Clean Code” Roberta C. Martina. Podążając za jego credo, nie powinieneś wymagać od programisty (jako użytkownika twojego API) znajomości (wewnętrznej) struktury twojej tablicy.

Użytkownik interfejsu API może zapytać: Czy to tablica z tylko jednym wymiarem? Czy obiekty są rozmieszczone na wszystkich poziomach tablicy wielowymiarowej? Ile zagnieżdżonych pętli (foreach itp.) Muszę uzyskać dostęp do wszystkich obiektów? Jakie obiekty są „przechowywane” w tej tablicy?

Jak opisano, chcesz użyć tej tablicy (która zawiera obiekty) jako tablicy jednowymiarowej.

Jak opisano w Nishi, możesz użyć:

/**
 * @return SomeObj[]
 */

za to.

Ale znowu: bądź świadomy - to nie jest standardowa notacja docblock. Notacja ta została wprowadzona przez niektórych producentów IDE.

Dobra, dobra, jako programista wiesz, że „[]” jest powiązane z tablicą w PHP. Ale co oznacza „coś []” w normalnym kontekście PHP? „[]” oznacza: utwórz nowy element w „czymś”. Nowym elementem może być wszystko. Ale to, co chcesz wyrazić, to: tablica obiektów tego samego typu i dokładny typ. Jak widać, producent IDE wprowadza nowy kontekst. Nowy kontekst, którego musiałeś się nauczyć. Nowy kontekst, którego musieli się nauczyć inni programiści PHP (aby zrozumieć twoje docblocks). Zły styl (!).

Ponieważ twoja tablica ma jeden wymiar, możesz nazwać tę „tablicę obiektów” „listą”. Pamiętaj, że „lista” ma szczególne znaczenie w innych językach programowania. Na przykład lepiej byłoby nazwać to „kolekcją”.

Pamiętaj: używasz języka programowania, który udostępnia wszystkie opcje OOP. Użyj klasy zamiast tablicy i spraw, aby Twoja klasa mogła przechodzić jak tablica. Na przykład:

class orderCollection implements ArrayIterator

Lub jeśli chcesz przechowywać obiekty wewnętrzne na różnych poziomach w wielowymiarowej strukturze tablicy / obiektu:

class orderCollection implements RecursiveArrayIterator

To rozwiązanie zastępuje tablicę obiektem typu „orderCollection”, ale jak dotąd nie włącza uzupełniania kodu w IDE. W porządku. Następny krok:

Zaimplementuj metody wprowadzane przez interfejs za pomocą docblocks - w szczególności:

/**
 * [...]
 * @return Order
 */
orderCollection::current()

/**
 * [...]
 * @return integer E.g. database identifier of the order
 */
orderCollection::key()

/**
 * [...]
 * @return Order
 */
orderCollection::offsetGet()

Nie zapomnij użyć podpowiedzi typu dla:

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

To rozwiązanie przestaje wprowadzać wiele:

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

w całym pliku kodu (np. w pętli), jak Zahymaka potwierdził swoją odpowiedzią. Użytkownik interfejsu API nie jest zmuszony do wprowadzenia tych bloków dokumentów w celu ukończenia kodu. Posiadanie @ powrotu tylko w jednym miejscu zmniejsza nadmiarowość (@var) tak bardzo, jak to możliwe. Posypanie „docBlocks @var” sprawi, że twój kod będzie najgorzej czytelny.

W końcu skończone. Wygląda na trudny do osiągnięcia? Wygląda na to, że bierzesz młot, żeby zgnieść orzecha? Nie do końca, ponieważ znasz te interfejsy i czysty kod. Pamiętaj: Twój kod źródłowy jest zapisywany raz / przeczytany wiele.

Jeśli uzupełnianie kodu w twoim IDE nie działa z tym podejściem, przełącz się na lepsze (np. IntelliJ IDEA, PhpStorm, Netbeans) lub złóż żądanie funkcji na monitorze problemów producenta IDE.

Podziękowania dla Christiana Weissa (z Niemiec) za bycie moim trenerem i za nauczenie mnie tak wspaniałych rzeczy. PS: Spotkajmy się ze mną na XING.

DanielaWaranie
źródło
wygląda to na „właściwą” drogę, ale nie mogę zmusić go do pracy z Netbeans. Zrobił mały przykład: imgur.com/fJ9Qsro
fehrlich,
2
Być może w 2012 roku nie było to „standardem”, ale teraz jest opisywane jako wbudowana funkcjonalność phpDoc.
Wirone
@Wirone wygląda na to, że phpDocumentor dodaje to do swojego podręcznika jako reakcję na ide producentów. Nawet jeśli masz szerokie wsparcie narzędziowe, nie oznacza to, że jest to najlepsza praktyka. Zaczyna się rozprzestrzeniać SomeObj [] w coraz większej liczbie projektów, podobnie jak wiele lat temu wymagało to wymagania, wymaganie, włączanie i włączanie. Przy automatycznym ładowaniu wygląd tych instrukcji spada poniżej 5%. Mam nadzieję, że SomeObj [] spadnie do tego samego wskaźnika w ciągu najbliższych 2 lat na korzyść powyższego podejścia.
DanielaWaranie
1
Nie rozumiem dlaczego? Jest to bardzo prosty i jasny zapis. Kiedy zobaczysz, SomeObj[]że wiesz, że jest to dwuwymiarowa tablica SomeObjinstancji, a następnie wiesz, co z tym zrobić. Nie sądzę, że nie jest zgodny z credo „czystego kodu”.
Wirone
To powinna być odpowiedź. Jednak nie wszystkie metody wsparcia IDE @return <className>dla current()wszystkich i dla wszystkich. PhpStorm obsługuje, więc bardzo mi pomogło. Dzięki stary!
Pavel
5

Użyj array[type]w Zend Studio.

W Zend Studio, array[MyClass]lub array[int]lub nawet array[array[MyClass]]doskonale.

Erick Robertson
źródło
5

W NetBeans 7.0 (może być również niższy) możesz zadeklarować typ zwracany „tablica z obiektami tekstowymi” tak, jak @return Textdziała podpowiedź do kodu:

Edycja: zaktualizowano przykład sugestią @Bob Fanger

/**
 * get all Tests
 *
 * @return Test|Array $tests
 */
public function getAllTexts(){
    return array(new Test(), new Test());
}

i po prostu użyj go:

$tests =  $controller->getAllTests();
//$tests->         //codehinting works!
//$tests[0]->      //codehinting works!

foreach($tests as $text){
    //$test->      //codehinting works!
}

Nie jest idealny, ale lepiej jest po prostu pozostawić go „mieszanego”, co nie przynosi żadnej wartości.

CONS jest dozwolone, aby traktować tablicę jako obiekt tekstowy, który będzie generował błędy.

d.raev
źródło
1
Używam „@return array | Test Some description.” co wyzwala to samo zachowanie, ale jest nieco bardziej objaśniające.
Bob Fanger
1
Jest to obejście , a nie rozwiązanie. Mówisz tutaj: „ta funkcja może zwrócić obiekt typu„ Test ”LUB tablicę”. Jednak technicznie nic nie mówi o tym, co może być w tablicy.
Byson,
5

Jak DanielaWaranie wymienione w jej odpowiedź - nie ma sposobu, aby określić typ elementu $ podczas iteracji nad $ w $ collectionObject pozycje: Dodaj @return MyEntitiesClassNamedo current()a reszta Iteratori ArrayAccess-methods których wartości zwrotnych.

Bum! Nie ma potrzeby /** @var SomeObj[] $collectionObj */kończenia foreachi działa poprawnie z obiektem kolekcji, nie ma potrzeby zwracania kolekcji za pomocą określonej metody opisanej jako @return SomeObj[].

Podejrzewam, że nie wszystkie IDE go obsługują, ale działa on doskonale w PhpStorm, co czyni mnie szczęśliwszym.

Przykład:

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
}

Przydało mi się dodać opublikowanie tej odpowiedzi

W moim przypadku current()reszta interface-metod jest zaimplementowana w Abstractklasie -collection i nie wiem, jakiego rodzaju jednostki zostaną ostatecznie zapisane w kolekcji.

Oto sztuczka: nie określaj typu zwracanego w klasie abstrakcyjnej, zamiast tego użyj instancji PhpDoc @methodw opisie konkretnej klasy kolekcji.

Przykład:

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...

}

Teraz użycie klas:

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

Jeszcze raz: podejrzewam, że nie wszystkie IDE go obsługują, ale PhpStorm je obsługuje. Wypróbuj swoje, opublikuj w komentarzach wyniki!

Pavel
źródło
Kupon za posuwanie się tak daleko, ale niestety wciąż mogę się zdecydować na specjalizację kolekcji, która zastąpi stare dobre, ogólne typy java .... fuj
Sebas
Dziękuję Ci. Jak wpisać tekst statyczny?
Jewgienij Afanasiew
3

Wiem, że jestem spóźniony na imprezę, ale ostatnio pracowałem nad tym problemem. Mam nadzieję, że ktoś to zobaczy, ponieważ zaakceptowana odpowiedź, choć poprawna, nie jest najlepszym sposobem, aby to zrobić. Przynajmniej nie w PHPStorm, chociaż nie testowałem NetBeans.

Najlepszym sposobem jest rozszerzenie klasy ArrayIterator zamiast używania rodzimych typów tablic. Pozwala to na wpisywanie podpowiedzi na poziomie klasy, a nie na poziomie instancji, co oznacza, że ​​musisz PHPDoc tylko raz, a nie w całym kodzie (który jest nie tylko bałagan i narusza DRY, ale może również powodować problemy, jeśli chodzi o refaktoryzacja - PHPStorm ma zwyczaj nieobecności PHPDoc podczas refaktoryzacji)

Zobacz kod poniżej:

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

Kluczem tutaj jest PHPDoc @method MyObj current()przesłaniający typ zwracany odziedziczony z ArrayIterator (który jest mixed). Włączenie tego PHPDoc oznacza, że ​​gdy iterujemy po właściwościach klasy foreach($this as $myObj), otrzymujemy zakończenie kodu w odniesieniu do zmiennej$myObj->...

Dla mnie jest to najładniejszy sposób na osiągnięcie tego (przynajmniej dopóki PHP nie wprowadzi Tablic Typowanych, jeśli w ogóle to zrobią), ponieważ deklarujemy typ iteratora w klasie iterowalnej, a nie w instancjach klasy rozproszonej po całym kodzie.

Nie pokazałem tutaj kompletnego rozwiązania dla rozszerzenia ArrayIterator, więc jeśli używasz tej techniki, możesz również:

  • W razie potrzeby dołącz inne klasy PHPDoc dla metod takich jak offsetGet($index) inext()
  • Przesuń test poczytalności is_a($object, MyObj::class) z konstruktora na metodę prywatną
  • Wywołaj to (obecnie prywatne) sprawdzenie poprawności z zastąpień metod, takich jak offsetSet($index, $newval)iappend($value)
e_i_pi
źródło
Bardzo ładne i czyste rozwiązanie! :)
Marko Šutija
2

Problem polega na tym, że @varmoże oznaczać tylko jeden typ - nie może zawierać złożonej formuły. Jeśli miałeś składnię dla „tablicy Foo”, dlaczego miałbyś się tam zatrzymać i nie dodawać składni dla „tablicy tablicy, która zawiera 2 Foo i trzy paski”? Rozumiem, że lista elementów jest być może bardziej ogólna, ale jest to śliskie nachylenie.

Osobiście czasami @var Foo[]zdarzało mi się oznaczać „tablicę Foo”, ale nie jest to obsługiwane przez IDE.

troelskn
źródło
5
Jedną z rzeczy, które uwielbiam w C / C ++ jest to, że faktycznie śledzi typy do tego poziomu. Byłoby to bardzo przyjemne zjazd.
Brilliand
2
Jest obsługiwany przez Netbeans 7.2 (przynajmniej to wersja używam), ale z odrobiną ajustment mianowicie: /* @var $foo Foo[] */. Właśnie napisałem o tym poniżej odpowiedź. Można tego również użyć w foreach(){}pętlach
Highmastdon
1
<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?>
    <?php
    // Type hinting now works:
    $model->getImage();
    ?>
<?php endforeach; ?>
Scott Hovestadt
źródło
5
które IDE to obsługują?
philfreo
21
To jest bardzo brzydkie. Pożegnaj się z czyszczeniem kodu, gdy zaczynasz programować w ten sposób.
halfpastfour.am
Spójrz raczej na moją odpowiedź ze zdefiniowaniem zawartości tablicy: stackoverflow.com/a/14110784/431967
Highmastdon
-5

Znalazłem coś, co działa, może uratować życie!

private $userList = array();
$userList = User::fetchAll(); // now $userList is an array of User objects
foreach ($userList as $user) {
   $user instanceof User;
   echo $user->getName();
}
eupho
źródło
11
Jedynym problemem jest to, że wprowadza dodatkowy kod do wykonania, który jest używany wyłącznie przez IDE. Zamiast tego o wiele lepiej jest zdefiniować podpowiedzi w komentarzach.
Ben Rowe
1
Wow, działa świetnie. Otrzymasz dodatkowy kod, ale wydaje się on nieszkodliwy. Zacznę robić: $ x instanceof Y; // typehint
Igor Nadj
3
Przełącz się na IDE, które umożliwia uzupełnianie kodu na podstawie bloków dokumentów lub inspekcji. Jeśli nie chcesz przełączać pliku IDE, prośba o funkcję w narzędziu do śledzenia problemów w twoim IDE.
DanielaWaranie
1
Jeśli spowoduje to wyjątek, jeśli typ jest niepoprawny, może być przydatny do sprawdzania typu środowiska wykonawczego. Jeśli ...
lilbyrdie,