Kévin Dunglas
Contributed by Kévin Dunglas in #15858

PHP is a weakly typed programming language. This means that you don't have to declare the type of the variables (e.g. int, bool) and you can store different kinds of data (e.g. numbers and strings) in the same variable.

Developers have partially overcome this constraint thanks to type hints, phpDoc and, starting from PHP 7, return type declarations. However, in some cases is still necessary to know the type of the PHP properties. That's why Symfony has added a new component called PropertyInfo.

Given a PHP class, this component gets information about all its properties by introspecting several metadata providers, such as Doctrine ORM mapping, phpDoc comments, PHP type hints, serializer metadata, etc.

Consider the following PHP class of a Doctrine entity, which contains different types of properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @Entity
 */
class FooClass
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    public $id;

    /**
     * This is a date (short description).
     *
     * With a long description.
     *
     * @var \DateTime
     */
    public $foo;

    private $bar;

    public function setBar(\SplFileInfo $bar)
    {
        $this->bar = $bar;
    }
}

Before using the PropertyInfo component to extract information, you must create and configure the metadata extractors to use (ReflectionExtractor, PhpDocExtractor, DoctrineExtractor and SerializerExtractor are built-in, but you can create your own extractors too):

1
2
3
4
5
6
7
8
use Symfony\Component\PropertyInfo\PropertyInfo;

$propertyInfo = new PropertyInfo(
    array($reflectionExtractor),
    array($doctrineExtractor, $phpDocExtractor, $reflectionExtractor),
    array($phpDocExtractor),
    array($reflectionExtractor)
);

Now you can introspect each property type as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$propertyInfo->getTypes('FooClass', 'foo');
// array(1) {
//   [0] =>
//   class Symfony\Component\PropertyInfo\Type#36 (6) {
//     private $builtinType => string(6) "object"
//     private $nullable => bool(false)
//     private $class => string(8) "DateTime"
//     private $collection => bool(false)
//     private $collectionKeyType => NULL
//     private $collectionValueType => NULL
//   }
// }

$propertyInfo->getTypes('FooClass', 'id');
// array(1) {
//   [0] =>
//   class Symfony\Component\PropertyInfo\Type#36 (6) {
//     private $builtinType => string(3) "int"
//     private $nullable => bool(false)
//     private $class => NULL
//     private $collection => bool(false)
//     private $collectionKeyType => NULL
//     private $collectionValueType => NULL
//   }
// }

$propertyInfo->getTypes('FooClass', 'bar');
// array(1) {
//   [0] =>
//   class Symfony\Component\PropertyInfo\Type#245 (6) {
//     private $builtinType => string(6) "object"
//     private $nullable => bool(false)
//     private $class => string(11) "SplFileInfo"
//     private $collection => bool(false)
//     private $collectionKeyType => NULL
//     private $collectionValueType => NULL
//   }
// }

This component includes other utilities to extract useful information:

1
2
3
4
5
6
7
8
9
10
11
12
13
$propertyInfo->getProperties('FooClass');
// ['id', 'foo', 'Bar']

$propertyInfo->isReadable('FooClass', 'id');   // true
$propertyInfo->isReadable('FooClass', 'bar');  // false
$propertyInfo->isWritable('FooClass', 'foo');  // true
$propertyInfo->isWritable('FooClass', 'bar');  // true

$propertyInfo->getShortDescription('FooClass', 'foo');
// "This is a date (short description)."

$propertyInfo->getLongDescription('FooClass', 'foo');
// "With a long description."
Published in #Living on the edge