divineniiquaye/rade-di

View on GitHub
src/Builder/CodePrinter.php

Summary

Maintainability
A
25 mins
Test Coverage
A
100%
<?php

declare(strict_types=1);

/*
 * This file is part of DivineNii opensource projects.
 *
 * PHP version 7.4 and above required
 *
 * @author    Divine Niiquaye Ibok <divineibok@gmail.com>
 * @copyright 2021 DivineNii (https://divinenii.com/)
 * @license   https://opensource.org/licenses/BSD-3-Clause License
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Rade\DI\Builder;

use PhpParser\PrettyPrinter\Standard;

/**
 * A custom Php-Parser printer.
 *
 * @author Divine Niiquaye Ibok <divineibok@gmail.com>
 */
class CodePrinter extends Standard
{
    public const COMMENT = <<<'COMMENT'
/**
 * @internal This class has been auto-generated by the Rade DI.
 */
COMMENT;

    /**
     * Pretty print nodes.
     *
     * @param \PhpParser\Node[]   $stmts
     * @param array<string,mixed> $options
     */
    public static function print(array $stmts, array $options = []): string
    {
        $printer = new self($options + ['maxLineLength' => 200, 'shortArraySyntax' => true]);
        $printerCode = $printer->prettyPrintFile($stmts);

        return \str_replace("\n\n\n", "\n\n", $printerCode);
    }

    protected function pStmt_Declare(\PhpParser\Node\Stmt\Declare_ $node): string
    {
        return parent::pStmt_Declare($node) . $this->nl;
    }

    protected function pStmt_TraitUse(\PhpParser\Node\Stmt\TraitUse $node): string
    {
        if (\count($node->traits) > 5) {
            return 'use ' . $this->pImplode($node->traits, ',' . $this->nl . '    ')
             . (empty($node->adaptations)
                ? ';'
                : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}') . "\n";
        }

        return parent::pStmt_TraitUse($node) . "\n";
    }

    protected function pStmt_Property(\PhpParser\Node\Stmt\Property $node): string
    {
        if ('tags' === $node->props[0]->name->name) {
            $item = $node->props[0]->default;

            if (isset($item->items)) {
                foreach ($item->items as $item) {
                    $item->setAttribute('multiline', true);
                }
            }
        }

        return parent::pStmt_Property($node) . "\n";
    }

    protected function pStmt_ClassMethod(\PhpParser\Node\Stmt\ClassMethod $node): string
    {
        $classMethod = parent::pStmt_ClassMethod($node);

        if (null !== $node->returnType) {
            $classMethod = \str_replace(') :', '):', $classMethod);
        }

        $this->indent();
        $classMethod = \str_replace(["{\n" . ($nl = \strrev($this->nl)), $nl], ["{\n", "\n"], $classMethod);
        $this->outdent();

        return $classMethod . "\n"; // prefer spaces instead of tab
    }

    protected function pStmt_Class(\PhpParser\Node\Stmt\Class_ $node): string
    {
        return \str_replace("\n" . \strrev($this->nl) . '}', "\n}", parent::pStmt_Class($node));
    }

    protected function pScalar_String(\PhpParser\Node\Scalar\String_ $node): string
    {
        if (\Nette\Utils\Validators::isType($node->value)) {
            return $this->pExpr_ConstFetch(new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name($node->value . '::class')));
        }

        return parent::pScalar_String($node);
    }

    protected function pMaybeMultiline(array $nodes, bool $trailingComma = false): string
    {
        if (\count($nodes) > 5 || (isset($nodes[0]) && $nodes[0]->getAttribute('multiline'))) {
            resolve_multiline:
            return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
        }

        $multilineCode = parent::pMaybeMultiline($nodes, $trailingComma);

        if (\strlen($multilineCode) > $this->options['maxLineLength']) {
            goto resolve_multiline;
        }

        return $multilineCode;
    }
}