Suggerimento di tipo PHPDoc per l'arrays di oggetti?

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

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

Questo funziona alla grande fino a quando ho bisogno di fare lo stesso con una serie di oggetti per essere in grado di get un suggerimento adeguato quando iterare attraverso quegli oggetti in seguito.

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

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 editor, ma dovrebbe funzionare.

Nell'IDE PhpStorm di JetBrains, puoi usare /** @var SomeObj[] */ , ad esempio:

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

La documentazione di phpdoc raccomanda questo metodo:

specificato contenente un singolo tipo, la definizione Tipo informa il lettore del tipo di ciascun elemento dell'arrays. Quindi un solo tipo è previsto come elemento per un determinato arrays.

Esempio: @return int[]

Suggerimenti di Netbeans:

Ottieni il completamento del codice su $users[0]-> e per $this-> per un arrays di classi User.

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

Puoi anche vedere il tipo di arrays in un elenco di membri della class quando completi $this->...

Per specificare una variabile è una matrix di oggetti:

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

Funziona in Netbeans 7.2 (lo sto usando)

Funziona anche con:

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

Pertanto l'uso della dichiarazione all'interno del foreach non è necessario.

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 collezione POSSONO anche essere un altro arrays e anche un'altra Collection.

 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 si aspetta un IDE per l'assistenza codice, si tratta di un'altra domanda se l'IDE support la notazione di raccolte in stile generico di PHPDoc.

Dalla mia risposta a questa domanda .

Preferisco leggere e scrivere codice pulito, come descritto in "Codice pulito" di Robert C. Martin. Quando si segue il suo credo non si dovrebbe richiedere allo sviluppatore (come utente della propria API) di conoscere la struttura (interna) del proprio arrays.

L'utente API può chiedere: è una matrix con una sola dimensione? Gli oggetti sono sparsi su tutti i livelli di un arrays multidimensionale? Quanti loop annidati (foreach, ecc.) Ho bisogno di accedere a tutti gli oggetti? Che tipo di oggetti sono "memorizzati" in quell'arrays?

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

Come delineato da Nishi puoi usare:

 /** * @return SomeObj[] */ 

per quello.

Ma ancora: essere consapevoli – questa non è una notazione standard del docblock. Questa notazione è stata introdotta da alcuni produttori di IDE.

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

Dato che la tua matrix ha una dimensione, potresti voler call quella "matrix di oggetti" una "list". Essere consapevoli del fatto che "elenco" ha un significato molto speciale in altri linguaggi di programmazione. Ad esempio, sarebbe meglio chiamarlo "raccolta".

Ricorda: si utilizza un linguaggio di programmazione che consente tutte le opzioni di OOP. Usa una class invece di una matrix e rendi la tua class percorribile come una matrix. Per esempio:

 class orderCollection implements ArrayIterator 

Oppure se si desidera archiviare gli oggetti interni su diversi livelli all'interno di una struttura di arrays / object multidimensionale:

 class orderCollection implements RecursiveArrayIterator 

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

Implementa i methods che vengono introdotti dall'interface con docblocks – particolare:

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

Non dimenticare di usare il suggerimento tipo per:

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

Questa soluzione smette di introdurre molti:

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

su tutti i tuoi file di codice (ad esempio all'interno di loop), come Zahymaka ha confermato con la sua / la sua risposta. Il tuo utente API non è obbligato a introdurre i docblock per il completamento del codice. Avere @return su un solo posto riduce la ridondanza (@var) come mutch il più ansible. Cospargere "docBlocks con @var" renderebbe il tuo codice più leggibile.

Finalmente hai finito. Sembra difficile da raggiungere? Sembra di prendere un martello per rompere un dado? Non proprio, dal momento che conosci le interfacce e il codice pulito. Ricorda: il tuo codice sorgente è scritto una volta / letto 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 funzionalità sul tracker di problemi del tuo produttore IDE.

Grazie a Christian Weiss (dalla Germania) per essere il mio allenatore e per avermi insegnato una cosa fantastica. PS: Meet me and him su XING.

In NetBeans 7.0 (potrebbe anche essere inferiore) si potrebbe dichiarare il tipo di return "arrays con oggetti di text" proprio come @return Text e il suggerimento sul codice functionrà:

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

 /** * get all Tests * * @return Test|Array $tests */ public function getAllTexts(){ return arrays(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 che lasciarlo semplicemente "misto", la strega non port alcun valore.

È CONS, è consentito calpestare la matrix come una strega di oggetti di text genera errori.

Come ha citato DanielaWaranie nella sua risposta – c'è un modo per specificare il tipo di $ item quando si itera su $ items in $ collectionObject: Add @return MyEntitiesClassName su current() e rest of the Iterator e ArrayAccess -methods che restituiscono i valori.

Boom! Non c'è bisogno di /** @var SomeObj[] $collectionObj */ over foreach , e funziona bene con l'object collection, non c'è bisogno di restituire collection con un 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 } 

Che utile avrei aggiunto per postare questa risposta

Nel mio caso current() e il resto interface -methods sono implementati nella class Abstract -collection e non so quale tipo di entity framework; verranno alla fine archiviate nella collezione.

Quindi ecco il trucco: non specificare il tipo restituito in class astratta, invece usa phpDoc @method nella descrizione della class di raccolta specifica.

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-classs } /** * @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 `receachze` method `printLogin()` here! $user->printLogin(); } 

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

Usa arrays[type] in Zend Studio.

In Zend Studio, arrays[MyClass] o arrays[int] o persino arrays[arrays[MyClass]] funzionano alla grande.

Il problema è che @var può semplicemente denotare un singolo tipo – Non contiene una formula complessa. Se hai una syntax per "arrays of Foo", perché fermarsi lì e non aggiungere una syntax per "arrays di arrays, che contiene 2 Foo e tre Bar"? Capisco che una list di elementi sia forse più generica di quella, ma è una pendenza scivolosa.

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

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

So di essere in ritardo per la festa, ma di recente ho lavorato su questo problema. Spero che qualcuno lo veda perché la risposta accettata, anche se corretta, non è il modo migliore per farlo. Non in PHPStorm, alless, non ho ancora provato NetBeans.

Il modo migliore prevede l'estensione della class ArrayIterator piuttosto che l'utilizzo di tipi di arrays nativi. Questo ti permette di digitare suggerimenti a livello di class piuttosto che a livello di istanza, il che significa che devi solo PHPDoc una volta, non attraverso il tuo codice (che non è solo disordinato e viola DRY, ma può anche essere problematico quando si tratta di refactoring – PHPStorm ha l'abitudine di mancare 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 $arrays = []) { foreach($arrays as $object) { if(!is_a($object, MyObj::class)) { throw new Exception('Invalid object passed to ' . __METHOD__ . ', expected type ' . MyObj::class); } } parent::__construct($arrays); } 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(arrays()) ]); $myObjCollection->echoContents(); 

La chiave qui è PHPDoc @method MyObj current() sovrascrive il tipo di return ereditato da ArrayIterator (che è mixed ). L'inclusione di questo PHPDoc significa che quando eseguiamo un'iterazione sulle properties; della class utilizzando foreach($this as $myObj) , otteniamo il completamento del codice quando ci si riferisce alla variabile $myObj->...

Per me, questo è il modo migliore per get questo risultato (alless fino a quando PHP non introdurrà i Typed Arrays, se mai lo fanno), poiché stiamo dichiarando il tipo iteratore nella class iterable, non sulle istanze della class sparse nel codice.

Non ho mostrato qui la soluzione completa per estendere ArrayIterator, quindi se usi questa tecnica, potresti anche voler:

  • Includere altri PHPDoc di livello di class come richiesto, per methods come offsetGet($index) e next()
  • Sposta il controllo di is_a($object, MyObj::class) dal constructor in un metodo privato
  • Chiama questo controllo di sanità (ora privato) dalle sovrascritture del metodo come offsetSet($index, $newval) e append($value)

Ho trovato qualcosa che funziona, può salvare vite!

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