开发者

php: get variable type hint using reflection

class Expense {

    /**
     * @开发者_C百科var int
     */
    private $id;
}

I would like to obtain the type hint of a variable in my class, using reflection, because the default value is null.


Try:

<?php
class Expense {

    /**
     * @var int
     */
    private $id;
}

$refClass = new ReflectionClass('Expense');
foreach ($refClass->getProperties() as $refProperty) {
    if (preg_match('/@var\s+([^\s]+)/', $refProperty->getDocComment(), $matches)) {
        list(, $type) = $matches;
        var_dump($type);
    }
}

Output:

string(3) "int"


For PHP 7.4

$reflection = new \ReflectionProperty('className', 'propertyName');
echo $reflection->getType()->getName();


Obtain the complete Docblock:

$reflection = new ReflectionProperty('Expense', 'id');

$doc = $reflection->getDocComment();


If PHPDoc comments prove to be missing or unreliable, you can type hint all the properties of a class provided they have a matching getter.

public function getClassPropertiesType(string $className): array {
    $reflectionClass = new \ReflectionClass($className);

    $reflectionProperties = $reflectionClass->getProperties();
    $properties = [];
    foreach ($reflectionProperties as $reflectionProperty) {
        $properties[] = $reflectionProperty->getName();
    }

    $methods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
    $results = [];

    foreach ($properties as $property) {
        foreach ($methods as $method) {
            // get only methods that have 0 parameter and start with 'get'
            if ($method->getNumberOfParameters() === 0 && 
                  strpos($method->getName(),'get') !== false && 
                  stripos($method->getName(), $property) !== false) {

                $results[$property] = (string)$method->getReturnType();
            }
        }
    }

    return $results;
}

Logically, there should be only one getter for each property of the class.

If I dump the properties of some class:

  0 => "id"
  1 => "email"
  2 => "password"
  3 => "firstName"
  4 => "lastName"
  5 => "gender"
  6 => "position"
  7 => "isActive"
  9 => "dateEmployedFrom"
  10 => "dateEmployedTo"
  11 => "dateOfBirth"
  12 => "ssn"
  13 => "mobilePhone"
  14 => "homePhone"
  15 => "address"
  16 => "zipCode"
  17 => "city"
  18 => "country"

This is what you get:

  "id" => "int"
  "email" => "string"
  "password" => "string"
  "firstName" => "string"
  "lastName" => "string"
  "gender" => "bool"
  "position" => "string"
  "isActive" => "bool"
  "dateEmployedFrom" => "DateTimeInterface"
  "dateEmployedTo" => "DateTimeInterface"
  "dateOfBirth" => "DateTimeInterface"
  "ssn" => "string"
  "mobilePhone" => "string"
  "homePhone" => "string"
  "address" => "string"
  "zipCode" => "string"
  "city" => "string"
  "country" => "string"

Limitations + workaround

If a property doesn't have any getter, you can start looking for setters (or methods that start with 'add', 'is', 'remove'), provided the method's arguments are type hinted. You could also include private properties in your search $methods = $reflectionClass->getMethods(); // no filter

I suggest expanding like this, before returning:

        $missingProperties = array_diff_key(array_flip($properties), $results);

        if (!empty($missingProperties)) { // some properties are missing

            foreach ($missingProperties as $missingProperty => $val) {
                // get only methods that have 1 parameter and start with 'set'
                if ($method->getNumberOfParameters() === 1 && strpos($method->getName(), 'set') !== false) { 
                    $parameters = $method->getParameters();

                    // if not already in results, and parameter is required 
                    // and is a class property
                    if(!array_key_exists($parameters[0]->getName(), $results) &&
                                !$parameters[0]->isOptional() && 
                                in_array($parameters[0]->getName(), $properties, true)) {

                        $string = $parameters[0]->__toString();

                        $string = substr($string, strlen('Parameter #0 [ <required> '));
                        $pos = strpos($string, ' '); // get first space after type
                        $string = substr($string, 0, $pos); // get type

                        $results[$parameters[0]->getName()] = $string;
                    }
                }
            }
        }

Of course, this is not 100% bulletproof, but hopefully it will help. :-)

Last: PHP 7.4 introduces ReflectionParameter::getType So, you could drop the above string manipulation and just write:

                $type = $parameters[0]->getType();
                $results[$parameters[0]->getName()] = $type->__toString();


A bit of a warning - PHP accelerators and some libraries themselves (i.e. symfony core) strip comments, quite often on the second run.


You can use the ReflectionDocBlock.

Installation

composer require phpdocumentor/reflection-docblock

Usage:

$factory  = \phpDocumentor\Reflection\DocBlockFactory::createInstance();

$reflectionClass = new ReflectionClass(MyClass::class);
$property = $reflectionClass->getProperty('foo');

$docBlock = $factory->create($property->getDocComment());

//it returns an array, as a property might declare many types
//for example @var int|string|null
$types = $docBlock->getTagsByName('var')[0]->getType()

See the reference of ReflectionClass and ReflectionProperty


In PHP7.4

    public function getKeysAndTypes(): array
    {
        $return = [];
        $reflectionClass = new \ReflectionClass(static::class);
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
            if ($reflectionProperty->isPublic()) {
                $return[$reflectionProperty->getName()] = $reflectionProperty->getType()->getName();
            }
        }
        return $return;
    }

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜