web-dev-qa-db-fra.com

Type de PHPDoc faisant allusion à un tableau d'objets?

Ainsi, dans PHPDoc, vous pouvez spécifier @var au-dessus de la déclaration de variable membre pour indiquer son type. Puis un IDE, par ex. PHPEd, saura quel type d'objet il travaille et sera en mesure de fournir un aperçu du code pour cette variable.

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

Cela fonctionne très bien jusqu'à ce que je doive faire la même chose avec un tableau d'objets pour pouvoir obtenir un indice approprié lorsque j'itérerai ces objets plus tard.

Alors, y a-t-il un moyen de déclarer une balise PHPDoc pour spécifier que la variable membre est un tableau de SomeObjs? @var le tableau ne suffit pas et @var array(SomeObj) ne semble pas être valide, par exemple.

393
Artem Russakovskii

tilisez:

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

lors de la saisie de variables en ligne, et

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

pour les propriétés de classe.

Réponse précédente de '09 lorsque PHPDoc (et des IDE comme Zend Studio et Netbeans) n'avaient pas cette option:

Le mieux que vous puissiez faire est de dire,

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

Je le fais souvent dans Zend Studio. Je ne sais pas sur les autres éditeurs, mais cela devrait fonctionner.

304
Zahymaka

Dans le PhpStorm IDE de JetBrains, vous pouvez utiliser /** @var SomeObj[] */, par exemple:

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

Le documentation phpdoc recommande cette méthode:

spécifiée contenant un seul type, la définition de Type informe le lecteur du type de chaque élément du tableau. Un seul type est alors attendu en tant qu'élément pour un tableau donné.

Exemple: @return int[]

880
Nishi

Astuces Netbeans:

Vous obtenez la complétion de code sur $users[0]-> et pour $this-> pour un tableau de classes d'utilisateurs.

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

Vous pouvez également voir le type du tableau dans une liste de membres de la classe lorsque vous complétez $this->...

57
user1491819

Pour spécifier une variable est un tableau d'objets:

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

Cela fonctionne dans Netbeans 7.2 (je l'utilise)

Fonctionne aussi avec:

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

Par conséquent, l'utilisation de la déclaration à l'intérieur de la foreach n'est pas nécessaire.

29
Highmastdon

PSR-5: PHPDoc propose une forme de notation de type générique.

Syntaxe

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

Les valeurs dans une collection PEUVENT même être un autre tableau et même une autre collection.

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

Exemples

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

Remarque: Si vous vous attendez à ce que IDE assiste le code, la question suivante est de savoir si la IDE prend en charge la notation de collections PHPDoc de style générique.

De ma réponse à cette question .

20
Gerard Roche

Je préfère lire et écrire du code propre - comme indiqué dans "Clean Code" de Robert C. Martin. En suivant son credo, vous ne devriez pas demander au développeur (en tant qu'utilisateur de votre API) de connaître la structure (interne) de votre tableau.

L'utilisateur de l'API peut demander: S'agit-il d'un tableau avec une seule dimension? Les objets sont-ils répartis sur tous les niveaux d'un tableau multidimensionnel? Combien de boucles imbriquées (foreach, etc.) dois-je accéder à tous les objets? Quels types d'objets sont "stockés" dans ce tableau?

Comme vous l'avez expliqué, vous souhaitez utiliser ce tableau (qui contient des objets) comme un tableau à une dimension.

Comme décrit par Nishi, vous pouvez utiliser:

/**
 * @return SomeObj[]
 */

pour ça.

Mais encore une fois: soyez conscient - ce n'est pas une notation standard de docblock. Cette notation a été introduite par certains IDE producteurs.

D'accord, d'accord, en tant que développeur, vous savez que "[]" est lié à un tableau en PHP. Mais que signifie "quelque chose []" dans un contexte PHP normal? "[]" signifie: créer un nouvel élément dans "quelque chose". Le nouvel élément pourrait être tout. Mais ce que vous voulez exprimer, c’est: un tableau d’objets du même type et du type exact. Comme vous pouvez le constater, le producteur IDE introduit un nouveau contexte. Un nouveau contexte que vous avez dû apprendre. Un nouveau contexte que les autres PHP développeurs ont dû apprendre (pour comprendre vos docblocks). Mauvais style (!).

Parce que votre tableau a une dimension, vous voudrez peut-être appeler ce "tableau d'objets" une "liste". Sachez que "liste" a une signification particulière dans d'autres langages de programmation. Ce serait mieux de l'appeler "collection" par exemple.

N'oubliez pas: vous utilisez un langage de programmation qui vous permet d'utiliser toutes les options de la programmation orientée objet. Utilisez une classe au lieu d'un tableau et rendez votre classe traversable comme un tableau. Par exemple.:

class orderCollection implements ArrayIterator

Ou si vous souhaitez stocker les objets internes à différents niveaux dans une structure multidimensionnelle tableau/objet:

class orderCollection implements RecursiveArrayIterator

Cette solution remplace votre tableau par un objet de type "orderCollection", mais n'active pas la complétion de code dans votre IDE jusqu'à présent. D'accord. L'étape suivante:

Implémentez les méthodes introduites par l'interface avec docblocks - en particulier:

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

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

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

N'oubliez pas d'utiliser l'indication de type pour:

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

Cette solution cesse d'introduire beaucoup de:

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

partout dans vos fichiers de code (par exemple, au sein de boucles), comme l'a confirmé Zahymaka avec sa réponse. Votre utilisateur d'API n'est pas obligé d'introduire ce docblocks, pour avoir l'achèvement de code. Avoir @retour sur un seul endroit réduit autant que possible la redondance (@var). Saupoudrer "docBlocks avec @var" rendrait votre code moins lisible.

Enfin, vous avez terminé. Cela semble difficile à atteindre? On dirait prendre un marteau pour casser une noix? Pas vraiment, puisque vous connaissez bien les interfaces et le code propre. Rappelez-vous: votre code source est écrit une fois/lu beaucoup.

Si l'achèvement du code de votre IDE ne fonctionne pas avec cette approche, passez à une meilleure (par exemple, IntelliJ IDEA, PhpStorm, Netbeans) ou déposez une demande de fonctionnalité sur le suivi des problèmes de votre IDE producteur.

Merci à Christian Weiss (de l’Allemagne) d’avoir été mon entraîneur et de m’avoir appris un si bon travail. PS: Rencontrez-moi et lui sur XING.

12
DanielaWaranie

tilisez array[type] dans Zend Studio.

Dans Zend Studio, array[MyClass] ou array[int] ou même array[array[MyClass]] fonctionnent parfaitement.

5
Erick Robertson

Comme DanielaWaranie l'a mentionné dans sa réponse, il existe un moyen de spécifier le type d'élément $ lorsque vous effectuez une itération sur des éléments $ dans $ collectionObject: Ajoutez @return MyEntitiesClassName à current() et le reste de Iterator et ArrayAccess- méthodes qui retournent des valeurs.

Boom! Pas besoin dans /** @var SomeObj[] $collectionObj */ sur foreach, et fonctionne correctement avec un objet collection, pas besoin de retourner une collection avec une méthode spécifique décrite par @return SomeObj[].

Je suppose que tous les IDE ne le supportent pas, mais cela fonctionne parfaitement dans PhpStorm, ce qui me rend plus heureux.

Exemple:

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
}

Quelle utilité j'allais ajouter en postant cette réponse

Dans mon cas, current() et le reste de interface- les méthodes sont implémentées dans la classe Abstract- collection et je ne sais pas quel type d'entités seront éventuellement stockées dans la collection.

Voici donc l'astuce: ne spécifiez pas le type de retour dans la classe abstraite, utilisez plutôt l'instance PhpDoc @method dans la description de la classe de collection spécifique.

Exemple:

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

}

Maintenant, utilisation des cours:

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

Encore une fois: je suppose que tous IDE ne le supportent pas, mais PhpStorm le fait. Essayez le vôtre, postez en commentaire les résultats!

5
Pavel

Dans NetBeans 7.0 (peut également être inférieur), vous pouvez déclarer le type de retour "tableau avec des objets Texte" exactement comme @return Text et l'indication de code fonctionnera:

Edit: mis à jour l'exemple avec la suggestion de @Bob Fanger

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

et juste l'utiliser:

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

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

Ce n’est pas parfait, mais il vaut mieux le laisser simplement "mélangé", ce qui n’apporte aucune valeur.

CONS est que vous êtes autorisé à parcourir le tableau en tant qu'objet texte qui générera des erreurs.

5
d.raev

Je sais que je suis en retard à la fête, mais je travaille sur ce problème récemment. J'espère que quelqu'un le verra car la réponse acceptée, bien que correcte, est pas la meilleure façon de le faire. Pas dans PHPStorm au moins, je n’ai pas encore testé NetBeans.

La meilleure façon consiste à étendre la classe ArrayIterator plutôt que d'utiliser des types de tableaux natifs. Cela vous permet de saisir indice au niveau de la classe plutôt qu'au niveau de l'instance, ce qui signifie que vous ne devez utiliser qu'une seule fois PHPDoc, et non pas dans votre code (ce qui est non seulement désordonné et viole DRY, mais peut également être problématique lorsqu'il s'agit de refactoring - PHPStorm a l'habitude de manquer PHPDoc lors du refactoring)

Voir le code ci-dessous:

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 clé ici est PHPDoc @method MyObj current() remplaçant le type de retour hérité de ArrayIterator (qui est mixed). L'inclusion de ce PHPDoc signifie que lorsque nous parcourons les propriétés de la classe à l'aide de foreach($this as $myObj), nous obtenons l'achèvement du code lorsque nous nous référons à la variable $myObj->...

Pour moi, c’est le moyen le plus simple d’y parvenir (du moins jusqu’à ce que PHP introduise des tableaux typés, s’ils le font déjà), car nous déclarons le type itérateur dans la classe itérable, et non dans des instances du type. classe dispersée dans le code.

Je n'ai pas montré ici la solution complète pour étendre ArrayIterator, donc si vous utilisez cette technique, vous voudrez peut-être aussi:

  • Incluez d'autres PHPDoc de niveau classe si nécessaire, pour des méthodes telles que offsetGet($index) et next()
  • Déplacez le test de cohérence is_a($object, MyObj::class) du constructeur dans une méthode privée
  • Appelez ce contrôle de cohérence (maintenant privé) à partir de substitutions de méthodes telles que offsetSet($index, $newval) et append($value)
3
e_i_pi

Le problème est que @var ne peut désigner qu'un seul type - Ne contient pas de formule complexe. Si vous aviez une syntaxe pour "array of Foo", pourquoi ne pas arrêter ici et ne pas ajouter de syntaxe pour "array of array, qui contient 2 Foo et trois Bar" "? Je comprends qu'une liste d'éléments est peut-être plus générique que cela, mais c'est une pente glissante.

Personnellement, j'ai parfois utilisé @var Foo[] pour désigner "un tableau de Foo", mais il n'est pas supporté par l'IDE.

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