Jump to content

Using PHP to modify a given PHP script


NotionCommotion

Recommended Posts

For right or wrong, I've been using XML to define my Doctrine entities.  When the XML changes, my entities need to change.  I've automated the process with a simple PHP script which allows me to swap some text to modify one of the auto-generated methods, add methods, etc.  Kind of clunky but it works well enough.

Now, I recently had the need to remove a given method in its entirety from a given class.  I "could" just add the entire string which represents the method name, earlier comments, and script for the method to my parsing script to my parsing script and remove it, but would like to be a little more concise.

For instance, say I have $script as follows:

$script = <<<'EOT'
class SomeEntityClass
{
   //... more stuff above

    public function getSomething()
    {
        return $this->something;
    }

    /**
     * Get foos.
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getFoos()
    {
        return $this->foos;
    }

    /**
     * Get somethingElse.
     *
     */
    //... more stuff below
}
EOT;

How would you recommend creating the function: removeFunction($script, 'getFoos') which would remove the given function along with its comments from the $script string and result with?

$script = <<<'EOT'
class SomeEntityClass
{
    //... more stuff above

    public function getSomething()
    {
        return $this->something;
    }

    /**
     * Get somethingElse.
     *
     */
    //... more stuff below
  
 }
EOT;

Assuming regex is the way to go, I was thinking of something like the following, but could use some help with the regex expression.

function removeFunction($script, $method)
{
    $method="public function $method(";
    //regex to remove script with which starts after pattern "{\n" OR "**/", contains $method, and is located before pattern "\n{" OR "\n    /**"
    return $script;
}

Thanks

Link to comment
Share on other sites

9 hours ago, NotionCommotion said:

I agree they shouldn't be but they are and thus the desire to automate.

Implied in my reply was "you should look into why this is and deal with it accordingly". It's a symptom of a problem.

If there are business needs that are changing, the problem is that the business doesn't know what it wants to do, and there needs to be more planning and thinking before rushing into development. If there are programming needs that change then... well, actually, there needs to be more planning and thinking before rushing into development for that too.

That aside, in your shoes, I would regenerate the code every time. Use those files as a base and put your own implementation into, for example, traits - a thing that can be easily added into a class.

Link to comment
Share on other sites

Unfortunately, I am the cause of the problem, but I am getting it under control.  Trying to improve my skills regarding entities and modelling and have been making some changes along the way.  Before automating the process, I would make some changes to the XML file, regenerate the entities, and it was time consuming to identified what was changed.  Using annotations in the PHP file wouldn't result in these issues, but I think doing so brings other baggage.  Below is what I ended up adding to my "create_entities.php" file which I run from the command line.

 

/**
* Returns an array of each method script.
*
* @param string $script
* @param array $methods.  Sequential
*
* @param array (associate array where keys are method names and value method script
* */
function extractMethods(string $script, array $methods):array
{
    $methods = getMethods($script, $methods);
    $script = explode(PHP_EOL, $script);
    foreach ($methods as $methodName=>$lns) {
        $methods[$methodName]= implode(PHP_EOL, array_slice($script, $lns[0], $lns[1]));
    }
    return $methods;
}

/**
* Removes or replaces a given function.
*
* @param array $methods.  If an element is associative, replaces with the vvalue, else removes
*
* @return string (script)
*/
function replaceFunction(string $script, array $methods):string
{
    $methodNames=[];
    foreach($methods as $key=>$name){
        $methodNames[]=is_int($key)?$name:$key;
    }
    echo("Removing or replacing methods ".implode(', ', $methodNames).PHP_EOL);
    $methodNames = getMethods($script, $methodNames);
    //$columns = array_column($methodNames, 0);
    //array_multisort($columns, SORT_DESC, $methodNames);
    uasort($methodNames, function($a, $b) {return $b[0] <=> $a[0];});
    //$script = preg_split("/\r\n|\n|\r/", $script);
    $script = explode(PHP_EOL, $script);
    foreach ($methodNames as $methodName=>$lns) {
        array_splice($script, $lns[0]-1, $lns[1]+1, $methods[$methodName]??null);
    }
    return implode(PHP_EOL, $script);
}

/**
* Returns the line number and length of methods.
*
* @param string $script
* @param array $methods.  Sequential
*
* @param array (associate array where keys are method names and value is start line number and number of lines
* */
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;
}


 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.