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;
}
精彩评论