nette / php-generator

🐘 Generates neat PHP code for you. Supports new PHP 8.3 features.
https://doc.nette.org/php-generator
Other
2.11k stars 138 forks source link

Parsing a Class doesn't includes method body. #59

Closed DarkGhostHunter closed 4 years ago

DarkGhostHunter commented 4 years ago

Version: 3.3.4

Bug Description

Using an existing class won't register method bodies at all.

Steps To Reproduce

Create one class with a method body, and then parse it manually.

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}
use App\Providers\EventServiceProvider;

$class = \Nette\PhpGenerator\ClassType::from(EventServiceProvider::class);

echo $class->getMethod('boot')->getBody(); // ''

Expected Behavior

use App\Providers\EventServiceProvider;

$class = \Nette\PhpGenerator\ClassType::from(EventServiceProvider::class);

echo $class->getMethod('boot')->getBody(); //  'parent::boot();\n\n//'

Possible Solution

Add this method to retrieve the body inside the \PhpGenerator\Factory::fromMethodReflection:

protected function getMethodBody(\ReflectionMethod $from) : string
{
    $lines = file($from->getFileName());
    $string = '';

    for ($start = $from->getStartLine()+1, $end = $from->getEndLine()-1; $start < $end; $start++) {
        $string .= $lines[$start];
    }

    return $string;
}

From there, the only thing would be to "unpad" each line.

divineniiquaye commented 4 years ago

Should have a proper way of returning the exact same body.

Your code is OK. But I think, it can be better, Try this:

This code is using "Strings" class from Nette Utils

protected function getMethodBody(\ReflectionMethod $from) : string
{
    if (false === $from->getFileName()) {
        return '';
    }

    $codeLines = file($from->getFileName());
    $lineOfCode = array_slice($codeLines, $from->getStartLine() + 1, $from->getEndLine() - $from->getStartLine() - 2);

    $unindentedLines = array_map(function ($lineOfCode) {
        // Search for 2 tabs indent, then begin from a new line
        if (Strings::startsWith($lineOfCode, "\t")) {
            return Strings::substring($lineOfCode, strlen("\t\t"));
        }

        // preserve all other lines of code in body.
        return $lineOfCode;
    }, $lineOfCode);

    return "\n".implode('', $unindentedLines);
}
dg commented 4 years ago

This is duplicated to #4. Because content can't be extracted 100% correctly (for example in minified code or due to bad coding style), I don't want to try. So please add this functionality in your code.