Разрешить тип к его полностью определенному имени класса в контексте другого класса (только по его имени) - PullRequest
1 голос
/ 06 марта 2020

Что разрешит «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 свойства могут быть типизированы напрямую, что позволяет стандартное отражение. Однако в более низких версиях это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...