Suggerimenti di tipo PHPDoc per array di oggetti?


417

Quindi, in PHPDoc si può specificare @varsopra la dichiarazione della variabile membro per suggerire il suo tipo. Quindi un IDE, per es. PHPEd, saprà con quale tipo di oggetto sta lavorando e sarà in grado di fornire una visione del codice per quella variabile.

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

Funziona alla grande fino a quando non ho bisogno di fare lo stesso con una serie di oggetti per essere in grado di ottenere un suggerimento adeguato quando ripasso questi oggetti in seguito.

Quindi, c'è un modo per dichiarare un tag PHPDoc per specificare che la variabile membro è un array di SomeObjs? @varl'array non è sufficiente e @var array(SomeObj)non sembra essere valido, ad esempio.


2
C'è qualche riferimento in questo blog dev Netbeans 6.8 che l'IDE è ora abbastanza intelligente da dedurre il tipo di membri dell'array: blogs.sun.com/netbeansphp/entry/php_templates_improved
John Carter,

3
@therefromhere: il tuo link è interrotto. Penso che il nuovo URL sia: blogs.oracle.com/netbeansphp/entry/php_templates_improved
DanielaWaranie,

Per le persone come me, che passano e cercano una risposta: se usi PHPStorm, guarda la risposta più votata: ha un suggerimento specifico! stackoverflow.com/a/1763425/1356098 (questo non significa che dovrebbe essere la risposta all'OP, dal momento che sta chiedendo PHPEd, ad esempio)
Erenor Paz,

Risposte:


337

Uso:

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

quando si digitano le variabili incorporate e

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

per le proprietà di classe.

Risposta precedente del '09 quando PHPDoc (e gli IDE come Zend Studio e Netbeans) non avevano questa opzione:

Il meglio che puoi fare è dire

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

Lo faccio molto in Zend Studio. Non so di altri redattori, ma dovrebbe funzionare.


3
Questo ha senso ma non ha funzionato per PHPEd 5.2. L'unica cosa che sono riuscito a inventare ha funzionato è foreach ($ Objs as / ** @var Test * / $ Obj), che è orribilmente brutto. :(
Artem Russakovskii,

14
Nota in Netbeans 7, sembra essere importante che hai solo un asterisco - /** @var $Obj Test */non funziona.
contrebis,

3
@contrebis: "@var" è un tag docblock valido. Quindi, anche se il tuo IDE non lo supporta all'interno di un blocco documenti "/ ** ... /" e supporta "@var" solo in "/ ... * /" - per favore, non modificare il blocco documenti corretto. Invia un problema al tracker dei bug del tuo IDE per renderlo conforme agli standard. Immagina che il tuo team di sviluppo / sviluppatori esterni / community utilizzi IDE diversi. Mantenerlo così com'è ed essere preparato per il futuro.
DanielaWaranie,

181
Assicurati di guardare sotto! Quasi non ho fatto scorrere verso il basso - sarebbe stato un GRANDE ERRORE !!! Molti IDE supporteranno una migliore sintassi! (suggerimento: @var Object[] $objectsdice che "$ objects" è una matrice di istanze di Object.)
Thom Porter,

10
/** @var TYPE $variable_name */è la sintassi corretta; non invertire l'ordine del tipo e il nome della variabile (come suggerito in precedenza nei commenti) in quanto non funzionerà in tutti i casi.
srcspider,

893

Nell'IDE PhpStorm di JetBrains, è possibile utilizzare /** @var SomeObj[] */, ad esempio:

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

La documentazione di phpdoc raccomanda questo metodo:

specificata contenente un singolo tipo, la definizione del tipo informa il lettore del tipo di ciascun elemento dell'array. Si prevede quindi un solo tipo come elemento per un determinato array.

Esempio: @return int[]


10
Ho appena scaricato e ho usato phpstorm per la settimana passata. Batte il diavolo di Aptana (che è fantastico per essere libero). Questo e 'esattamente quello che stavo cercando. In realtà, è lo stesso modo in cui lo faresti per JavaScript, avrei dovuto immaginare
Juan Mendes,

3
Grazie uomo! Questo e 'esattamente quello che stavo cercando. PHPStorm è fantastico.
Erik Schierboom,

5
Questo non funziona in Netbeans, sono deluso. Jetbrains rende alcuni strumenti molto carini.
Keyo,

10
@fishbone @Keyo ora funziona in Netbeans (almeno nella versione 7.1 notturna, forse prima), anche se sembra che tu debba usare una variabile temporanea (un bug?). Suggerimento per foreach(getSomeObjects() as $obj)non funziona, ma per$objs = getSomeObjects(); foreach($objs as $obj)
John Carter

2
Sarebbe bello avere @var Obj[string]array associativi.
donquixote,

59

Suggerimenti di Netbeans:

Ottieni il completamento del codice su $users[0]->e $this->per una matrice di classi utente.

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

Puoi anche vedere il tipo di array in un elenco di membri della classe quando esegui il completamento di $this->...


4
funziona anche in EAP PhpStorm 9: / ** * @var UserInterface [] * / var $ users = []; // Matrice di Objs che implementa un'interfaccia
Ronan,

L'ho provato in NetBeans IDE 8.0.2, ma ottengo suggerimenti dalla classe in cui mi trovo attualmente.
Wojciech Jasiński

funziona anche in Eclipse 4.6.3 (idk quale supporto per versione è stato introdotto, ma funziona, ed è quello che sto usando ora)
hanshenrik,

Questo purtroppo non funziona dopo aver utilizzato array_pop()o funzioni simili per qualche motivo. Sembra che Netbeans non si renda conto che tali funzioni restituiscono un singolo elemento dell'array di input.
William W,

29

Per specificare una variabile è una matrice di oggetti:

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

Funziona con Netbeans 7.2 (lo sto usando)

Funziona anche con:

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

Pertanto foreachnon è necessario l' uso della dichiarazione all'interno del documento .


2
Questa soluzione è più pulita della risposta accettata dal mio punto di vista, perché è possibile utilizzare foreach più volte e il suggerimento sul tipo continuerà a funzionare senza una nuova /* @var $Obj Test */annotazione ogni volta.
Henry,

Vedo due problemi qui: 1. phpdoc corretto inizia con /** 2. Il formato corretto è@var <data-type> <variable-name>
Christian

@Christian 1: la domanda principale non è phpdoc ma typehinting 2: il formato corretto non è come dici tu, anche secondo altre risposte. In effetti, vedo 2 problemi con il tuo commento e mi chiedo perché non fai la tua risposta con il formato corretto
Highmastdon,

1. Il typehinting funziona con phpdoc ... se non usi il blocco documenti, il tuo IDE non proverà a indovinare ciò che hai scritto in qualche commento casuale. 2. Il formato corretto, come hanno detto anche alcune altre risposte, è quello che ho specificato sopra; tipo di dati prima del nome della variabile . 3. Non ho scritto un'altra risposta perché la domanda non ha bisogno di un'altra e preferirei non solo modificare il codice.
Christian,

24

PSR-5: PHPDoc propone una forma di notazione in stile Generico .

Sintassi

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

I valori in una raccolta POSSONO persino essere un altro array e persino un'altra raccolta.

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

Esempi

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

Nota: se ti aspetti che un IDE esegua il code assist, è un'altra domanda se l'IDE supporta la notazione di raccolte in stile generico PHPDoc.

Dalla mia risposta a questa domanda .


La notazione generica è stata rimossa da PSR-5
azzerata il

11

Preferisco leggere e scrivere codice pulito, come indicato in "Codice pulito" di Robert C. Martin. Quando segui il suo credo non devi richiedere allo sviluppatore (come utente della tua API) di conoscere la struttura (interna) del tuo array.

L'utente API può chiedere: è un array con una sola dimensione? Gli oggetti sono sparsi su tutti i livelli di un array multidimensionale? Di quanti loop nidificati (foreach, ecc.) Devo accedere a tutti gli oggetti? Che tipo di oggetti sono "memorizzati" in quell'array?

Come hai delineato, vuoi usare quell'array (che contiene oggetti) come un array monodimensionale.

Come indicato da Nishi è possibile utilizzare:

/**
 * @return SomeObj[]
 */

per quello.

Ma ancora: attenzione: questa non è una notazione standard di blocco dei documenti. Questa notazione è stata introdotta da alcuni produttori IDE.

Ok, ok, come sviluppatore sai che "[]" è legato a un array in PHP. Ma cosa significa "qualcosa []" in un normale contesto PHP? "[]" significa: crea un nuovo elemento all'interno di "qualcosa". Il nuovo elemento potrebbe essere tutto. Ma quello che vuoi esprimere è: matrice di oggetti con lo stesso tipo ed è il tipo esatto. Come puoi vedere, il produttore IDE introduce un nuovo contesto. Un nuovo contesto che dovevi imparare. Un nuovo contesto che altri sviluppatori PHP hanno dovuto imparare (per capire i tuoi blocchi di documenti). Cattivo stile (!).

Poiché la tua matrice ha una dimensione, potresti voler chiamare quella "matrice di oggetti" un "elenco". Tenere presente che "elenco" ha un significato molto speciale in altri linguaggi di programmazione. Sarebbe molto meglio chiamarlo "raccolta" per esempio.

Ricorda: usi un linguaggio di programmazione che ti consente tutte le opzioni di OOP. Usa una classe invece di un array e rendi la tua classe percorribile come un array. Per esempio:

class orderCollection implements ArrayIterator

O se si desidera archiviare gli oggetti interni su livelli diversi all'interno di una struttura di matrice / oggetto multidimensionale:

class orderCollection implements RecursiveArrayIterator

Questa soluzione sostituisce l'array con un oggetto di tipo "orderCollection", ma finora non consente il completamento del codice all'interno dell'IDE. Va bene. Passo successivo:

Implementare i metodi introdotti dall'interfaccia con docblocks, in particolare:

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

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

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

Non dimenticare di utilizzare i suggerimenti sul tipo per:

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

Questa soluzione smette di introdurre molti:

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

in tutti i tuoi file di codice (ad es. all'interno di loop), come confermato da Zahymaka con la sua risposta. Il tuo utente API non è obbligato a introdurre quei blocchi di documenti, per avere il completamento del codice. Avere @return in un solo posto riduce la ridondanza (@var) il più possibile. Cospargere "docBlocks con @var" renderebbe il tuo codice peggiore leggibile.

Finalmente hai finito. Sembra difficile da raggiungere? Sembra di prendere un martello per rompere un dado? Non proprio, dal momento che hai familiarità con quelle interfacce e con il codice pulito. Ricorda: il tuo codice sorgente è scritto una volta / leggi molti.

Se il completamento del codice del tuo IDE non funziona con questo approccio, passa a uno migliore (ad esempio IntelliJ IDEA, PhpStorm, Netbeans) o invia una richiesta di funzione sul tracker del problema del tuo produttore IDE.

Grazie a Christian Weiss (dalla Germania) per essere stato il mio allenatore e per avermi insegnato cose fantastiche. PS: Incontriamoci con lui su XING.


questo sembra il modo "giusto", ma non riesco a farlo funzionare con Netbeans. Faccio
fehrlich l'

2
Forse nel 2012 questo non era "uno standard", ma ora è descritto come funzionalità integrata di phpDoc.
Wirone,

@Wirone sembra che phpDocumentor aggiunga questo al suo manuale come una reazione ai produttori di ide. Anche se disponi di un ampio supporto per gli strumenti, ciò non significa che sia la migliore pratica. Comincia a diffondere SomeObj [] in un numero sempre maggiore di progetti, simili a require, request_once, include e include_once fatto anni fa. Con il caricamento automatico la comparsa di tali affermazioni scende al di sotto del 5%. Speriamo che SomeObj [] scenda allo stesso ritmo entro i prossimi 2 anni a favore dell'approccio sopra.
DanielaWaranie,

1
Non capisco perché? Questa è una notazione molto semplice e chiara. Quando vedi SomeObj[]che sai che è un array bidimensionale di SomeObjistanze e poi sai cosa farne. Non credo che non segua il credo del "codice pulito".
Wirone,

Questa dovrebbe essere la risposta. Tuttavia, non tutti gli approcci di supporto IDE con @return <className>for current()e tutti i ragazzi. PhpStorm supporta quindi mi ha aiutato molto. Grazie compagno!
Pavel,

5

Utilizzare array[type]in Zend Studio.

In Zend Studio, array[MyClass]o array[int]o anche array[array[MyClass]]grande opera.


5

In NetBeans 7.0 (potrebbe anche essere inferiore) è possibile dichiarare il tipo restituito "array con oggetti Text" così come funzionerà @return Texte il suggerimento sul codice:

Modifica: aggiornato l'esempio con il suggerimento di @Bob Fanger

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

e basta usarlo:

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

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

Non è perfetto, ma è meglio lasciarlo semplicemente "misto", il che non ha alcun valore.

CONTRO è il permesso di percorrere l'array come oggetto di testo che genererà errori.


1
Io uso "@return array | Test Some description." che innesca lo stesso comportamento ma è un po 'più esplicativo.
Bob Fanger,

1
Questa è una soluzione alternativa , non una soluzione. Quello che stai dicendo qui è "questa funzione può restituire un oggetto di tipo 'Test', O un array". Tuttavia, tecnicamente non ti dice nulla su ciò che potrebbe essere nella matrice.
Byson,

5

Come DanielaWaranie menzionato nella sua risposta - c'è un modo per specificare il tipo di $ item quando l'iterazione di $ articoli in $ collectionObject: Aggiungi @return MyEntitiesClassNamealla current()e resto del Iteratore ArrayAccess-metodi che i valori di ritorno.

Boom! Non c'è bisogno di /** @var SomeObj[] $collectionObj */over foreache funziona perfettamente con l'oggetto collection, non è necessario restituire la collezione con il metodo specifico descritto come@return SomeObj[] .

Sospetto che non tutti gli IDE lo supportino ma funziona perfettamente in PhpStorm, il che mi rende più felice.

Esempio:

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
}

Quale utile stavo per aggiungere pubblicando questa risposta

Nel mio caso current()e nel resto dei interfacemetodi sono implementati inAbstract classe -collection e non so quale tipo di entità alla fine verrà archiviata nella raccolta.

Quindi, ecco il trucco: non specificare il tipo restituito in classe astratta, invece usa l'istruzione PhpDoc @method nella descrizione di una specifica classe di raccolta.

Esempio:

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

}

Ora, l'uso delle classi:

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

Ancora una volta: sospetto che non tutti gli IDE lo supportino, ma PhpStorm lo fa. Prova il tuo, pubblica nei commenti i risultati!


Buono per averlo spinto così lontano, ma sfortunatamente posso ancora risolvermi per specializzare una collezione per sostituire i buoni vecchi tipi generici di java .... yuck '
Sebas

Grazie. Come è possibile digitare un metodo statico?
Yevgeniy Afanasyev il

3

So di essere in ritardo alla festa, ma ho lavorato su questo problema di recente. Spero che qualcuno lo veda perché la risposta accettata, sebbene corretta, non lo è il modo migliore per farlo. Almeno non in PHPStorm, non ho testato NetBeans.

Il modo migliore prevede l'estensione della classe ArrayIterator piuttosto che l'utilizzo di tipi di array nativi. Questo ti permette di digitare un suggerimento a livello di classe piuttosto che a livello di istanza, il che significa che devi PHPDoc solo una volta, non in tutto il codice (che non è solo disordinato e viola DRY, ma può anche essere problematico quando si tratta di refactoring - PHPStorm ha l'abitudine di perdere PHPDoc durante il refactoring)

Vedi il codice qui sotto:

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

La chiave qui è PHPDoc che @method MyObj current()sovrascrive il tipo restituito ereditato da ArrayIterator (che è mixed). L'inclusione di questo PHPDoc significa che quando si scorre le proprietà della classe usandoforeach($this as $myObj) , otteniamo quindi il completamento del codice quando ci riferiamo alla variabile$myObj->...

Per me, questo è il modo più semplice per raggiungere questo obiettivo (almeno fino a quando PHP non introdurrà Typed Arrays, se mai lo faranno), poiché stiamo dichiarando il tipo di iteratore nella classe iterabile, non su istanze della classe sparse nel codice.

Non ho mostrato qui la soluzione completa per l'estensione di ArrayIterator, quindi se usi questa tecnica, potresti anche voler:

  • Includi altri PHPDoc a livello di classe come richiesto, per metodi come offsetGet($index)enext()
  • Spostare il controllo di integrità is_a($object, MyObj::class)dal costruttore in un metodo privato
  • Chiamare questo controllo di integrità (ora privato) da sostituzioni di metodi come offsetSet($index, $newval)eappend($value)

Soluzione molto bella e pulita! :)
Marko Šutija

2

Il problema è che @varpuò semplicemente indicare un singolo tipo: non contiene una formula complessa. Se hai avuto una sintassi per "array di Foo", perché fermarsi qui e non aggiungere una sintassi per "array di array, che contiene 2 Foo e tre Bar"? Capisco che un elenco di elementi è forse più generico di quello, ma è una pendenza scivolosa.

Personalmente, ho usato alcune volte @var Foo[]per indicare "un array di Foo", ma non è supportato da IDE.


5
Una delle cose che adoro di C / C ++ è che tiene traccia dei tipi fino a questo livello. Sarebbe una pendenza molto piacevole da scivolare giù.
Brilliand,

2
È supportato da Netbeans 7.2 (almeno questa è la versione che uso), ma con un po ajustment vale a dire: /* @var $foo Foo[] */. Ho appena scritto una risposta al riguardo. Questo può essere utilizzato anche all'interno di foreach(){}circuiti
Highmastdon


-5

Ho trovato qualcosa che funziona, può salvare vite!

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

11
l'unico problema è che introduce codice aggiuntivo da eseguire, che viene utilizzato esclusivamente dal tuo IDE. È invece molto meglio definire un suggerimento per tipo all'interno dei commenti.
Ben Rowe,

1
Wow, funziona alla grande. Si finirebbe con un codice aggiuntivo ma sembra innocuo. Inizierò a fare: $ x instanceof Y; // typehint
Igor Nadj,

3
Passa a un IDE che ti fornisce il completamento del codice in base a blocchi di documenti o ispezioni. Se non si desidera cambiare il file IDE, richiedere una funzione sul tracker dei problemi dell'IDE.
DanielaWaranie,

1
Se ciò genera un'eccezione se il tipo non è corretto, può essere utile per il controllo del tipo di runtime. Se ...
lilbyrdie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.