Генерация PHP-кода (из токенов Parser) - PullRequest
9 голосов
/ 21 февраля 2011

Есть ли какое-либо доступное решение для (повторного) генерирования PHP-кода из токенов анализатора , возвращаемых token_get_all? Также приветствуются другие решения для генерации PHP-кода, желательно с соответствующим лексером / парсером (если есть).

Ответы [ 4 ]

2 голосов
/ 18 октября 2015

В категории "другие решения" вы можете попробовать PHP Parser .

Анализатор превращает исходный код PHP в абстрактное синтаксическое дерево .... Кроме того, вы можете преобразовать синтаксическое дерево обратно в PHP-код.

2 голосов
/ 21 февраля 2011

Из моего комментария:

Кто-нибудь видит потенциальную проблему, если я просто напишу большой переключатель заявление для преобразования токенов обратно в их строковые представления (т.е. T_DO, чтобы 'сделать'), сопоставьте это с токены, соединяйте пробелы и ищите какой-то PHP-код довольно-печатный решение?

После некоторых поисков я нашел в этом вопросе самодельное решение PHP, которое на самом деле использует интерфейс PHP Tokenizer, а также некоторые инструменты форматирования кода PHP, которые являются более настраиваемыми (но требуют решения как описано выше).

Они могут быть использованы для быстрой реализации решения. Я отправлю сюда, когда найду время, чтобы приготовить это.


Решение с PHP_Beautifier

Это быстрое решение, которое я приготовил, я оставлю его здесь как часть вопроса. Обратите внимание, что он требует от вас открыть класс PHP_Beautifier, изменив все (возможно, не все, но это проще), то есть приватный на защищенный , чтобы вы могли фактически использовать внутренняя работа PHP_Beautifier (в противном случае было бы невозможно повторно использовать функциональные возможности PHP_Beautifier без повторной реализации половины их кода).

Пример использования класса:

file: main.php

<?php
// read some PHP code (the file itself will do)
$phpCode = file_get_contents(__FILE__);

// create a new instance of PHP2PHP
$php2php = new PHP2PHP();

// tokenize the code (forwards to token_get_all)
$phpCode = $php2php->php2token($phpCode);

// print the tokens, in some way
echo join(' ', array_map(function($token) {
  return (is_array($token))
    ? ($token[0] === T_WHITESPACE)
      ? ($token[1] === "\n")
        ? "\n"
        : ''
      : token_name($token[0])
    : $token;
}, $phpCode));

// transform the tokens back into legible PHP code
$phpCode = $php2php->token2php($phpCode);
?>

Поскольку PHP2PHP расширяет PHP_Beautifier, он допускает ту же тонкую настройку под тем же API, который использует PHP_Beautifier. Сам класс:

file: PHP2PHP.php

class PHP2PHP extends PHP_Beautifier {

  function php2token($phpCode) {
    return token_get_all($phpCode);
  }

  function token2php(array $phpToken) {

    // prepare properties
    $this->resetProperties();
    $this->aTokens = $phpToken;
    $iTotal        = count($this->aTokens);
    $iPrevAssoc    = false;

    // send a signal to the filter, announcing the init of the processing of a file
    foreach($this->aFilters as $oFilter)
      $oFilter->preProcess();

    for ($this->iCount = 0;
         $this->iCount < $iTotal;
         $this->iCount++) {
      $aCurrentToken = $this->aTokens[$this->iCount];
      if (is_string($aCurrentToken))
        $aCurrentToken = array(
          0 => $aCurrentToken,
          1 => $aCurrentToken
        );

      // ArrayNested->off();
      $sTextLog = PHP_Beautifier_Common::wsToString($aCurrentToken[1]);

      // ArrayNested->on();
      $sTokenName = (is_numeric($aCurrentToken[0])) ? token_name($aCurrentToken[0]) : '';
      $this->oLog->log("Token:" . $sTokenName . "[" . $sTextLog . "]", PEAR_LOG_DEBUG);
      $this->controlToken($aCurrentToken);
      $iFirstOut           = count($this->aOut); //5
      $bError              = false;
      $this->aCurrentToken = $aCurrentToken;
      if ($this->bBeautify) {
        foreach($this->aFilters as $oFilter) {
          $bError = true;
          if ($oFilter->handleToken($this->aCurrentToken) !== FALSE) {
            $this->oLog->log('Filter:' . $oFilter->getName() , PEAR_LOG_DEBUG);
            $bError = false;
            break;
          }
        }
      } else {
        $this->add($aCurrentToken[1]);
      }
      $this->controlTokenPost($aCurrentToken);
      $iLastOut = count($this->aOut);
      // set the assoc
      if (($iLastOut-$iFirstOut) > 0) {
        $this->aAssocs[$this->iCount] = array(
          'offset' => $iFirstOut
        );
        if ($iPrevAssoc !== FALSE)
          $this->aAssocs[$iPrevAssoc]['length'] = $iFirstOut-$this->aAssocs[$iPrevAssoc]['offset'];
        $iPrevAssoc = $this->iCount;
      }
      if ($bError)
        throw new Exception("Can'process token: " . var_dump($aCurrentToken));
    } // ~for

    // generate the last assoc
    if (count($this->aOut) == 0)
        throw new Exception("Nothing on output!");

    $this->aAssocs[$iPrevAssoc]['length'] = (count($this->aOut) -1) - $this->aAssocs[$iPrevAssoc]['offset'];

    // post-processing
    foreach($this->aFilters as $oFilter)
      $oFilter->postProcess();
    return $this->get();
  }
}
?>
1 голос
/ 21 февраля 2011

Если я не ошибаюсь http://pear.php.net/package/PHP_Beautifier использует token_get_all (), а затем переписывает поток.Он использует кучу таких методов, как t_else и t_close_brace для вывода каждого токена.Может быть, вы можете угнать это для простоты.

0 голосов
/ 02 марта 2011

Смотрите наш PHP интерфейс . Это полноценный синтаксический анализатор PHP, автоматически создающий AST, и соответствующий prettyprinter, который генерирует скомпилированный код PHP с оригинальными комментариями. (РЕДАКТИРОВАТЬ 12/2011: Посмотрите этот SO-ответ для более подробной информации о том, что нужно для того, чтобы довольно распечатать из AST, которые являются просто организованной версией токенов: https://stackoverflow.com/a/5834775/120163)

Внешний интерфейс построен на основе нашего DMS Software Reengineering Toolkit , позволяющего анализировать и преобразовывать AST PHP (а затем и через код prettyprinter).

...