Что разрешит «P» в контексте класса «MyContext»
При наличии любого PHP -класса вы можете разрешить тип в его полностью квалифицированный класс , как если бы записанный в контексте класса как подсказка типа paramter?
Предположим, что следующий класс
<?php
namespace X\Y\Z;
use A\B\C;
use D\E\F as G;
use V\W;
use J\K{L,M,N,O,P};
class MyContext {
/**
* @var P
*/
protected $p;
/**
* @var G
*/
protected $g;
public function some(W $w): C {
}
}
, используя отражение, мы можем определить, какие W
и C
метода some
are:
$c = new \ReflectionClass('X\\Y\\Z\\MyContext');
$m = $c->getMethod('some');
$w = $m->getParameters()[0];
$fqClassNameW = $w->getClass()->getName();
// result: "V\W"
$fqClassNameC = $m->getReturnType()->getName();
// result: "A\B\C"
однако как можно, используя только @var
свойств $p
и $g
, получить тот же результат?
Как можно Я программно выясняю, к чему 'P' относится в контексте этого класса?
Примеры использования: проверка аннотированных свойств для определения типов (ORM, Code-Generation и т. Д. c.) .
Решение с использованием eval()
Пока я не могу найти какой-либо способ решения этих проблем без использования eval()
:
function resolveNamespaceInClassContext(string $localClassName, string $hostClassName, string $classFileContents): string {
$hostClassNameParts = explode('\\', $hostClassName);
$hostClassName = array_pop($hostClassNameParts);
$fqClassName = null;
if(!preg_match('/^\<\?php(.*)[\s]*class[\s]*'.preg_quote($hostClassName).'[\s]*[^\{]*\{/s', $classFileContents, $matches)) {
throw new \Exception('could not match class header, please check syntax');
}
$evalSrc = $matches[1]; // contains the namespace part and use-statements
$evalSrc .= '$f = function('.$localClassName.' $x) {};'."\n";
$evalSrc .= '$r = new \\ReflectionFunction($f);'."\n";
$evalSrc .= '$p = $r->getParameters()[0];'."\n";
$evalSrc .= '$fqClassName = $p->getClass()->getName();'."\n";
try {
eval($evalSrc);
} catch(\ReflectionException $e) {
throw new \Exception('could not resolve local class type `'.$localClassName.'` in class `'.$hostClassName.'`');
}
return $fqClassName;
}
Так что это извлечение верхнего часть файла класса, которая содержит namespace
-declaration и use
-statements, а затем просто помещает вместе ra простая функция, с помощью которой мы можем разрешить тип, используя стандартное отражение.
Анализ пространства имен / import
Это пришло мне в голову, но я бы хотел избежать по этим причинам:
- Отражение всегда будет работать, даже если функции вокруг пространств имен меняются между версиями
- Анализ одного класса не говорит мне всей истории (действительно ли в текущем пространстве имен есть класс, который поэтому не имеет смысла -statement?)
- Как правило, нужно избегать реализации синтаксического анализатора, который должен идеально имитировать поведение c PHP
Есть ли лучший, более "чистый" способ?
Есть ли лучший способ сделать это без использования eval
?
Я не смог найти класс или метод Reflection, которые позволили бы сделать это во время выполнения. Я думаю, что проблема заключается просто в том, что перед выполнением кода php разрешает все типы в полностью определенные пространства имен, а затем выбрасывает информацию о том, что было импортировано и т.д. c. Таким образом, во время выполнения он не может разрешить типы по отношению к определенным namespace
- и use
-статментам.
Я знаю, что с PHP 7.4 свойства могут быть типизированы напрямую, что позволяет стандартное отражение. Однако в более низких версиях это не так.