PHPdoc - defining object properties for an object of stdClass
Asked Answered
Z

5

30

I am trying to figure out if it is possible to use PHPdoc to define the object properties being returned by a function or a object method.

Say I have the following class:

class SomeClass {
    public function staffDetails($id){

        $object = new stdClass();
        $object->type = "person";
        $object->name = "dave";
        $object->age = "46";        

        return $object;
    }
}

Now, it is easy enough to define input parameters.

 /**
 * Get Staff Member Details
 * 
 * @param   string  $id    staff id number
 * 
 * @return  object
 */

class SomeClass {
    public function staffDetails($id){
        $object = new stdClass();
        $object->type = "person";
        $object->name = "dave";
        $object->age = "46";        

        return $object;
    }
}

The question is is there a similar thing for defining properties of the output object (of a stdClass) returned by the method in question. So that another programmer does not have to open this class and manually look into the method to see what the return object is returning?

Zondra answered 15/9, 2012 at 7:57 Comment(11)
Why don't you just have a StaffDetails class with type,name and age properties? Then you can use @param StaffDetailsIngrowing
if you dont want to make a concrete type for the stdClass you can still write @return \stdClass holding type, name and age or explain so in the long description of the Doc Block. At least it's documented then. That won't make your IDE magically know the properties though.Podium
no - I dont want the IDE to know the properties. I just would like them nicely documented. The object I am actually using has about 40 variables within it so I really wanted to know if the was a way to lay them out nicely in a table like input parameters appear. Otherwise if just using a long description it gets messy and hard to read.Zondra
@l_t Well, you can always document them in the DocBlock's Long Description. But an object having 40 variables is a good indicator for an object knowing too much anyway. You can likely split that object into 4 to 10 individual objects. Look out for properties that can be grouped. Extract them into their own class. Associate the main class with that class then, so you'll get a nice dedicated object graph in the end.Podium
@Podium Thanks- The method in question is a grouping of many different classes. I did it like this so the front end developer can just call one class method and get an object back with all the cleaned / pre-processed data back needed for that page (in this case product data). Is that generally not a good idea?Zondra
@l_t I see. You turned your object graph into a Data Transfer Object. That's a valid approach. You could still give it a type though, e.g. ProductDetails and then either document it by giving it public properties or by adding @property annotationsPodium
@artlung Are you asking for the @return for the class? Do we both on the same page? Class can have any number of methods which can return any type results.Gulfweed
@Gulfweed An alternative to @return stdClass whereby the members of that class are enumerated.Agostino
@Agostino to me it is still unclear, probably you should better set another question on SO and clarify what exactly is your expectations.Gulfweed
@Gulfweed I appreciate that, but I really do think this question asks the question pretty fully. I'll think about how I'd form the question differently and consider it.Agostino
I have it answered here #65946533Joan
P
16

Here it is 4 years later, and there still does not appear to be a way to annotate the properties of a stdClass object as originally described in your question.

Collections had been proposed in PSR-5, but that appears to have been shot down: https://github.com/php-fig/fig-standards/blob/211063eed7f4d9b4514b728d7b1810d9b3379dd1/proposed/phpdoc.md#collections

It seems there are only two options available:

Option 1:

Create a normal class representing your data object and annotate the properties.

class MyData
{
    /**
     * This is the name attribute.
     * @var string
     */
    public $name;

    /**
     * This is the age attribute.
     * @var integer
     */
    public $age;
}

Option 2:

Create a generic Struct type class as suggested by Gordon and extend it as your data object, using the @property annotation to define what generic values are possible to access with __get and __set.

class Struct
{
    /**
     * Private internal struct attributes
     * @var array
     */
    private $attributes = [];

    /**
     * Set a value
     * @param string $key
     * @param mixed $value
     */
    public function __set($key, $value)
    {
        $this->attributes[$key] = $value;
    }

    /**
     * Get a value
     * @param string $key
     * @return mixed
     */
    public function __get($key)
    {
        return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
    }

    /**
     * Check if a key is set
     * @param string $key
     * @return boolean
     */
    public function __isset($key)
    {
        return isset($this->attributes[$key]) ? true : false;
    }
}

/**
 * @property string $name
 * @property integer $age
 */
class MyData extends Struct
{
    // Can optionally add data mutators or utility methods here
}
Piracy answered 27/12, 2016 at 18:50 Comment(4)
If you go with option 1, you can declare the class in a file scanned by your IDE but ignored by your project. State that the method returns a StaffMember, but keep using stdClass in the implementation.Cida
How can you have a file scanned by your IDE but ignored by your project with NetBeans?Joan
This is an old question -- I would recommend asking a new one.Piracy
It might also be a good idea to implement the Serializable and JsonSerializable interfaceCavorilievo
W
9

You have only two way to document the structure of the result class.

1.One can describe the structure in a comment text. For example:

class SomeClass 
{
    /**
     * Getting staff detail.
     * Result object has following structure:
     * <code>
     * $type - person type
     * $name - person name
     * $age - person age
     * </code>
     * @param string $id staff id number
     *
     * @return stdClass
     *
     */
    public function staffDetails($id){
        $object = new stdClass();
        $object->type = "person";
        $object->name = "dave";
        $object->age = "46";
        return $object;
    }
}

2.One can create a data type that will inheritance stdClass and it will have an annotation of a result object. For example:

/**
 * @property string $type Person type
 * @property string $name Person name
 * @property integer $age Person age
 */
class DTO extends stdClass
{}

And use it in your other classes

class SomeClass {

    /**
     * Getting staff detail.
     *
     * @param string $id staff id number
     *
     * @return DTO
     *
     */
    public function staffDetails($id){

        $object = new DTO();
        $object->type = "person";
        $object->name = "dave";
        $object->age = "46";

        return $object;
    }
}

In my opinion, this way is better than a description in the text comment because it makes the code more obvious

Warty answered 11/10, 2018 at 11:25 Comment(6)
option 1 - would not help your IDE to work with it, option 2 - would break PSR conventionJoan
@YevgeniyAfanasyev yes option 1 couldn't help to IDE, it only can help a developer who will work with your code - he/she will see the structure at least. option 2 - what PSR do you mean?Warty
The PSR4 spec says: >The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.Joan
@YevgeniyAfanasyev It doesn't relate to PSR-4 because there aren't any files in my answer. It is only a simple code example.Warty
It looks as if you suggested to put 2 classes in one fileJoan
@YevgeniyAfanasyev Yep, I separated code in the example, thanks :)Warty
J
6

If you are using PHP 7, you can define anonymous class.

class SomeClass {
    public function staffDetails($id){
        $object = (new class() extends stdClass {
                public /** @var string  */ $type;
                public /** @var string  */ $name;
                public /** @var int     */ $age;
            });

        $object->type = "person";
        $object->name = "dave";
        $object->age  = 46;        

        return $object;
    }
}

It is working for my IDE (tested in NetBeans)

Joan answered 29/1, 2021 at 0:56 Comment(1)
This also works in VSCode using the PHP Intelephense extension.Delate
M
1

The hack I use for autocomplete in PhpStorm:

  1. Create some meta file which will contain some classes to describe your structures. The file is never included and structures have their own name rules in order not to mess them with real existing classes:
<?php
/*
meta.php
never included
*/

/**
 * @property string $type
 * @property string $name
 * @property string $age
 */
class StaffDetails_meta {}
  1. Use the meta class as a return value in your real code PHPDoc:
<?php
/*
SomeClass.php
eventually included
*/

class SomeClass
{
    
    /**
     * Get Staff Member Details
     * 
     * @param   string  $id    staff id number
     * 
     * @return StaffDetails_meta
     */
    public function staffDetails($id)
    {
        $object = new stdClass();
        $object->type = "person";
        $object->name = "dave";
        $object->age = "46";        

        return $object;
    }
}
  1. Congratulations, this will make your IDE autocomplete your code when you'd typing something like (new SomeClass)->staffDetails('staff_id')->

P.S.: I know, almost 10 years passed but still actual

Monandrous answered 28/7, 2022 at 23:11 Comment(1)
If you're going to do that, you might as well just create an actual StaffDetails class and return that instead.Skellum
K
0

With for example json_decode it's harder to use own classes instead of stdClass, but in my case I just created dummy file with class definitions, which really isn't loaded and I'm adding own classes as @return (works for intelephense on vscode).

PHPdocObjects.php

/**
 * class only for PHPdoc (do not include)
 */
class Member {
    /** @var string */
    public $type;
    /** @var string */
    public $name;
    /** @var string */
    public $age;
}

/**
 * Other format
 *
 * @property string $type;
 * @property string $name;
 * @property string $age;
 */
class MemberAlt {}

SomeClass.php

 /**
 * Get Staff Member Details
 * 
 * @param   string  $id    staff id number
 * 
 * @return  Member  I'm in fact stdClass
 */

class SomeClass {
    public function staffDetails($id){
        $object = json_decode('{"type":"person","name":"dave","age":"46"}');
        return $object;
    }
}
Knurly answered 17/6, 2019 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.