Skip to content

Instantly share code, notes, and snippets.

@drupol
Last active May 10, 2021 13:34
Show Gist options
  • Save drupol/11a0e396722a80a72e674d2cb6f22659 to your computer and use it in GitHub Desktop.
Save drupol/11a0e396722a80a72e674d2cb6f22659 to your computer and use it in GitHub Desktop.
Simple example of nikic/php-parser importer in loophp/phptree
<?php
declare(strict_types=1);
include __DIR__ . '/vendor/autoload.php';
use drupol\launcher\Launcher;
use loophp\phptree\Exporter\Image;
use PhpParser\ParserFactory;
use loophp\phptree\Importer\NikicPhpParserImporter;
$code = file_get_contents(__DIR__ . '/src/Cart.php');
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$tree = (new NikicPhpParserImporter())->import($parser->parse($code));
$exporter = new Image();
$imagePath = $exporter->setFormat('png')->export($tree);
Launcher::open($imagePath);
@drupol
Copy link
Author

drupol commented Feb 26, 2020

Result:

graphviz5FHuyZ

@inksong
Copy link

inksong commented May 10, 2021

Hello there! Your work is great!
I encountered a practical problem when using your work,
that is, the location of functions and variables cannot be automatically located.
Is it possible to generate structured information with specific lineNum variables? Such as json?
This can greatly improve the usability of your work! Thx by the way!

@drupol
Copy link
Author

drupol commented May 10, 2021

Hello !

Something like this?

<?php

declare(strict_types=1);

include __DIR__ . '/vendor/autoload.php';

use loophp\launcher\Launcher;
use loophp\phptree\Exporter\Image;
use loophp\phptree\Importer\ImporterInterface;
use PhpParser\ParserFactory;
use loophp\phptree\Node\AttributeNode;
use loophp\phptree\Node\AttributeNodeInterface;
use PhpParser\Node;
use loophp\phptree\Node\NodeInterface;

final class NikicPhpParserWithLineNumbers implements ImporterInterface {
    /**
     * @param Node[] $data
     *
     * @throws Exception
     */
    public function import($data): NodeInterface
    {
        return $this->parseNode($this->createNode(['label' => 'root']), ...$data);
    }

    private function createNode(array $attributes): AttributeNodeInterface
    {
        if (array_key_exists('astNode', $attributes)) {
            $attributes['label'] = sprintf(
                '%s at line %s',
                $attributes['astNode']->getType(),
                $attributes['astNode']->getLine()
            );
        }

        return new AttributeNode($attributes);
    }

    /**
     * @return array<int, Node>
     */
    private function getAllNodeChildren(Node $astNode): array
    {
        /** @var array<int, array<int, Node>> $astNodes */
        $astNodes = array_map(
            static function (string $subNodeName) use ($astNode): array {
                $subNodes = $astNode->{$subNodeName};

                if (!is_array($subNodes)) {
                    $subNodes = [$subNodes];
                }

                return array_filter(
                    $subNodes,
                    'is_object'
                );
            },
            $astNode->getSubNodeNames()
        );

        return array_merge(...$astNodes);
    }

    /**
     * @param Node ...$astNodes
     */
    private function parseNode(AttributeNodeInterface $parent, Node ...$astNodes): NodeInterface
    {
        return array_reduce(
            $astNodes,
            function (AttributeNodeInterface $carry, Node $astNode): NodeInterface {
                return $carry
                    ->add(
                        $this->parseNode(
                            $this->createNode([
                                'label' => $astNode->getType(),
                                'astNode' => $astNode,
                            ]),
                            ...$this->getAllNodeChildren($astNode)
                        )
                    );
            },
            $parent
        );
    }
}

$code = file_get_contents(__DIR__ . '/src/Exporter/FancyExporter.php');

$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);

$tree = (new NikicPhpParserWithLineNumbers())->import($parser->parse($code));

$exporter = new Image();
$filename = tempnam(sys_get_temp_dir(), 'graph-');

file_put_contents($filename, $exporter->setFormat('svg')->export($tree));

Launcher::open($filename);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment