Использование PHP для редактирования другого PHP-скрипта и назначения данного метода - PullRequest
1 голос
/ 13 октября 2019

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

<?php
$scriptString = <<<'EOT'
<?php

namespace Foo;

/**
 * Foo
 */
class Foo extends Bar
{
    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $stuff;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->stuff = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Add addBla.
     *
     * @param \Abc\Bla $bla
     *
     * @return Foo
     */
    public function addBla(\Abc\Bla $bla)
    {
        $this->bla[] = $bla;

        return $this;
    }

    /**
     * Remove bla.
     *
     * @param \Abc\Bla $bla
     *
     * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
     */
    public function removeBBa(\Abc\Bla $bla)
    {
        return $this->bla->removeElement($bla);
    }

   /**
     * Get $hello.
     *
     * @return \Bcd\Hello
     */
    public function getHello()
    {
        return $this->hello;
    }
}
EOT;

Я пытаюсь реализовать следующие две функции removeMethod() и selectMethod()

$methodTarget='addBla';
$methodTarget="public function $methodTarget(";

//returns a string with the method and its associated comments/annotations removed
$stringWithoutMethod=removeMethod($scriptString, $methodTarget);

//returns the target method and the method's associated comments/annotations
$stringMethod=selectMethod($scriptString, $methodTarget);

Как это лучше всего реализовать? При регулярном выражении, пожалуйста, порекомендуйте соответствующий шаблон для цели от {\n или **/ до \n} или \n * /**

РЕДАКТИРОВАТЬ. Основываясь на комментарии Казимира и Ипполита относительно token_get_all(), я создал следующий скрипт. Хотя это интригует, не уверен, куда идти с этим. Есть мысли?

<?php
$script=file_get_contents(__DIR__.'/test_token_get_sample.php');

$test1 = debug($script);
$test2 = debug($script, TOKEN_PARSE);
echo('test1 count: '.count($test1).'  test2 count: '.count($test2).PHP_EOL);
$diffs=array_diff($test1, $test2);    //empty array
echo ('differences: '.PHP_EOL.implode(PHP_EOL, $diffs));

echo(PHP_EOL.'test without TOKEN_PARSE: '.PHP_EOL.implode(PHP_EOL, $test1));

function debug($script, bool $flag=null):array
{
    $tokens = token_get_all($script, $flag);
    $output=[];
    foreach ($tokens as $token) {
        if (is_string($token)) {
            $output[] = 'simple 1-character token: '.$token;
        } else {
            list($id, $text) = $token;
            $name= token_name($id);
            $output[] = "token array: id: $id name: $name text: $text";
        }
    }
    return $output;
}

Ответы [ 2 ]

0 голосов
/ 13 октября 2019

Основано на комментарии CasimiretHippolyte ...

<?php
function extractMethods(string $script, array $methods):array
{
    $methods = getMethods($script, $methods);
    $script = preg_split(PHP_EOL, $script);
    foreach ($methods as $methodName=>$lns) {
        $methods[$methodName]= implode(PHP_EOL, array_slice($script, $lns[0], $lns[1]));
    }
    return $methods;
}

function removeMethods(string $script, array $methods):string
{
    $methods = getMethods($script, $methods);
    $methods=array_values($methods);
    $script = preg_split(PHP_EOL, $script);
    for ($i=count($methods)-1; $i>=0; $i--) {
        array_splice($script, $methods[$i][0]-1, $methods[$i][1]+1);
    }
    return implode(PHP_EOL, $script);
}

function getMethods(string $script, array $methods):array
{
    $tokens = token_get_all($script);
    if(count($tokens)!==count(token_get_all($script, TOKEN_PARSE))) exit('Investigate TOKEN_PARSE');
    $count=count($tokens);
    $eof=$count - array_search('}',array_reverse($tokens));
    $output=[];
    foreach ($tokens as $index=>$token) {
        if (is_array($token) && $token[0]===T_FUNCTION) { //346
            for ($nameIndex = $index+1; $nameIndex <= $count; $nameIndex++) {
                if (is_array($tokens[$nameIndex]) && $tokens[$nameIndex][0]===T_STRING) { //319
                    if(in_array($tokens[$nameIndex][1], $methods)) {
                        for ($lastIndex = $nameIndex+1; $lastIndex <= $count; $lastIndex++) {
                            if($lastIndex===$eof || (is_array($tokens[$lastIndex]) && $tokens[$lastIndex][0]===T_DOC_COMMENT)) { //378
                                for ($endIndex = $lastIndex-1; $endIndex > 0; $endIndex--) {
                                    if (is_array($tokens[$endIndex])) {
                                        break;
                                    }
                                }
                                for ($startIndex = $index-1; $startIndex > 0; $startIndex--) {
                                    if (is_array($tokens[$startIndex]) && $tokens[$startIndex][0]===T_DOC_COMMENT) { //378
                                        $output[$tokens[$nameIndex][1]]=[$tokens[$startIndex][2], $tokens[$endIndex][2]-$tokens[$startIndex][2]];
                                        break(3);
                                    }
                                }
                                exit('Initial comment not found');
                            }
                        }
                    }
                    break;
                    exit('Next comment or closing tag not found');
                }
            }
        }
    }
    if($error = array_diff($methods, array_keys($output))){
        exit(implode(', ', $error).' not found.');
    }
    return $output;
}
0 голосов
/ 13 октября 2019

Раньше я делал что-то похожее на функцию динамического создания в файлах PHP. Вы можете разбить свой файл на массивы строк и анализировать каждую строку, пока не найдете нужную позицию. Для меня у меня был уникальный хеш в файле, который сказал мне, где начать добавлять мои функции.

Как только вы найдете место, которое ищете, просто переберите строки функций, которые вы хотите добавить, а затем вставьте их в исходный массив строк. Затем вы можете join ваш массив, используя PHP_EOL char, чтобы восстановить исходный файл и записать его в файл php.

const INSERT_AFTER_KEY = "SOMETHING NO SANE HUMAN WOULD WRITE";

$fileContent = file_get_contents(OUTPUT_FILE);
// here we are splitting the files into lines.
$fileLines = preg_split('/\r\n|\n|\r/', trim($fileContent));
// we are using a copy of the lines because we can't split into a foreach 
//https://stackoverflow.com/questions/11587894/php-will-using-array-splice-on-an-array-thats-the-subject-of-a-foreach-cause
$linesToWrite = $fileLines;
// we are parsing each line of the original file.
foreach($fileLines as $index => $line) {
    // we found the function we need to remove.
    if(strpos($line, "function $functionName") !== -1) {
        // we will count the opening bracket and remove line until we're back to 0.
        $openingBracket = 1;
        $lineToRemove = 1;
        // saving a copy of the index for later.
        $indexStart = $index
        while($openingBracket > 0) {
            if(strpos($line, "{") !== -1) {
                // we found an opening bracket, adding it to the total.
                $openingBracket++;
            }
            if(strpos($line, "}" !== -1) {
                // we found a closing bracking, removing it from the total;
                $openingBracket--;
            }
            $lineToRemove++;
        }
        // since the openingBracket is down to 0, we have the number of line the function takes, we need to remove them from the copy. We will use the copy to create our final file.
        array_splice($linesToWrite, $indexStart, $lineToRemove);
        break;
    }
}


// we write the lines to the file.
$textToWrite = join(PHP_EOL, $linesToWrite);
$outputFile = fopen(OUTPUT_FILE, 'w') or die("Could not open file " . OUTPUT_FILE);
fwrite($outputFile, $textToWrite);
fclose($outputFile);

Обратите внимание, что этот код не проверен и может потребовать дополнительной работы, но это общая идея.

...