Php: получить подсказку типа переменной с использованием отражения

class Expense {

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

Я хотел бы получить тип подсказки переменной в моем классе, используя отражение, потому что значение по умолчанию равно null.

Ответ 1

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

Выход

string(3) "int"

Ответ 2

Получить полный Docblock:

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

$doc = $reflection->getDocComment();

Ответ 3

Немного предупреждения - PHP-ускорители и некоторые библиотеки (т.е. symfony core) не просто компилируют комментарии, нередко на втором запуске.

Ответ 4

Если комментарии PHPDoc оказываются отсутствующими или ненадежными, вы можете напечатать подсказку для всех свойств класса, если они имеют соответствующий метод получения.

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;
}

По логике, для каждого свойства класса должен быть только один метод получения.

Если я дам дамп свойств некоторого класса:

  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"

Вот что вы получаете:

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

Ограничения + обходной путь

Если у свойства нет какого-либо метода получения, вы можете начать поиск методов установки (или методов, которые начинаются с 'add', 'is', 'remove'), при условии, что аргументы метода имеют подсказку типа. Вы также можете включить частную собственность в свой поиск $methods = $reflectionClass->getMethods(); // no filter

Я предлагаю расширить, как это, прежде чем вернуться:

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

Конечно, это не на 100% пуленепробиваемый, но, надеюсь, это поможет. :-)

Последнее: PHP 7.4 представляет ReflectionParameter :: getType Таким образом, вы можете отказаться от приведенной выше операции со строками и просто написать:

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