0

I'm refactoring a huge amount of code which has all function documented in simple comments like this:

//This is foo funtion
function foo($foo)
{
}

And my goal is to make it looks like this:

  /**
     * This is foo funtion
     * @param $foo - foo param
     * @return mixed
     */
    function foo($foo)
    {
    }

Is there any automation tools which can do this? Or it's should be done only manually?

9
  • If you have an IDE like PHPStorm, you better do it manually. If you place the cursor (in a empty line) one line above function definition e.g. function foo() and the type /** and press enter, the block will be generated and you only have to copy the text from //foobar to the new block. Automation can break things or do the wrong stuff. Commented Dec 15, 2022 at 14:10
  • @Foobar, So it's better to do it manually? And is there any way in PHPStorm to find all undocumented functions? Commented Dec 15, 2022 at 14:19
  • @Foobar You can make an global regex search: Find all functions that are not have */ before the definiton line. I will test it now. Commented Dec 15, 2022 at 14:23
  • This is not related directly to your literal question, but I personally remove most of those comments when I find them and instead prefer to use type declarations at the PHP level which I find to be self-documenting. That's just an opinion, of course, and depending on your version of PHP and what you are documenting (specifically arrays) you sometimes still need the docblock hints. Commented Dec 15, 2022 at 14:23
  • @Chris Haas, in my case, I also use autodoc generators like phpDocumentor. But thanks for advice. I'll take into account this. Commented Dec 15, 2022 at 14:30

1 Answer 1

1

I think the only way of such refactoring is to create your custom code. It doesn't seem to difficulte to create one, but obviously, it can cause more problems than it will solve. If the project doesn't have a quite inclusive test suite, I think it will be way more safer to make it manually. Also, it will be a great idea to test your automation code before you mess with your real code. This means long time to spend. Nevertheless, the code down below is an idead of how it can be done. Finally, I think implementing typehint instead of PHPDocs will make your app nicer and safer, but of course, your choise.

class Refactor
{
    public function handle(string $path)
    {
        $this->refactorFiles($this->listAllFiles($path));
    }

    public function listAllFiles(string $path): array
    {
        $files = [];

        foreach ($this->listFolderContent($path) as $item) {
            $pointer = $path . DIRECTORY_SEPARATOR .  $item;

            if (is_dir($pointer)) {
                $files = [...$files, ...$this->listAllFiles($pointer)];
            } else {
                $files[] = $pointer;
            }
        }

        return $files;
    }

    private function listFolderContent(string $path)
    {
        return array_diff(scandir($path), array_merge(['.', '..']));
    }

    private function refactorFiles(array $files): void
    {
        array_map(fn ($file) => $this->refactorFile($file), $files);
    }

    private function refactorFile(string $file): void
    {
        $content = file($file);
        $newContent = [];

        foreach ($content as $i => $line) {
            if (!str_contains($line, 'function')) {
                $newContent[] = $line;
                continue;
            };

            $firstIndex = $this->indexOfTheLineWhereTheCommentStarts($content, $i);
            $comments = $this->getComment($content, $firstIndex, $i - 1);
            $newContent = [...$newContent, ...$this->buildDocBlock($comments, $line)];
        }

        file_put_contents($file, implode('', $newContent));
    }

    private function indexOfTheLineWhereTheCommentStarts(array $content, int $index)
    {
        $prevIndex = $index - 1;
        $prevLine = $content[$prevIndex];

        while (str_contains($prevLine, '//')) {
            $prevIndex = $prevIndex - 1;
            $prevLine = $content[$prevIndex];
        }

        return $prevIndex;
    }

    private function getComment(array $content, int $firstIndex, int $lastIndex): array
    {
        return array_map(
            fn ($commentLine) => trim(str_replace('//', '', $commentLine)),
            array_slice($content, $firstIndex, $lastIndex - $firstIndex + 1)
        );
    }

    private function buildDocBlock(array $comments, string $line): array
    {
        return array_map(
            fn ($line) => "    $line",
            [
                '/**',
                ...$this->convertCommentLines($comments),
                ' *',
                ...$this->setParameters($line),
                ' * @return mixed',
                ' */'
            ]
        );
    }

    private function convertCommentLines(array $comments): array
    {
        return array_map(fn ($line) => ' * ' . trim(str_replace('//', '', $line)), $comments);
    }

    private function setParameters(string $line): array
    {
        return array_map(
            fn ($param) => ' * @param ' . trim(explode('=', $param)[0]),
            $this->isolateParams($line)
        );
    }

    private function isolateParams(string $line): array
    {
        return explode(',', explode(')', explode('(', $line)[1] ?? '')[0]);
    }
}



Sign up to request clarification or add additional context in comments.

1 Comment

In conclusion, pretty simular to what Foobar's said but thanks for code. I've made decision to do it manulally, because as you said it's safer. For me, it's first experience in old code refactoring. It's looks like that write clean code from start is much more easer than refactorng old legacy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.