samsonframework/generator

View on GitHub
src/AbstractGenerator.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php declare(strict_types=1);
/**
 * Created by Vitaly Iegorov <egorov@samsonos.com>.
 * on 03.09.16 at 11:37
 */
namespace samsonframework\generator;

/**
 * Abstract code generator.
 *
 * @author Vitaly Egorov <egorov@samsonos.com>
 */
abstract class AbstractGenerator
{
    /** @var AbstractGenerator Parent class generator */
    protected $parent;

    /** @var array Generated code grouped by generator class name */
    protected $generatedCode = [];

    /** @var int Indentation level */
    protected $indentation = 0;

    /** @var bool Flag that class has already had generated its code */
    protected $isGenerated = false;

    /**
     * AbstractGenerator constructor.
     *
     * @param AbstractGenerator $parent Parent generator
     */
    public function __construct(AbstractGenerator $parent = null)
    {
        $this->parent = $parent;
    }

    /**
     * Close current generator and return parent.
     *
     * @return AbstractGenerator|ClassGenerator|FunctionGenerator|MethodGenerator|PropertyGenerator|ClassConstantGenerator|ConditionGenerator|IfGenerator
     */
    public function end() : AbstractGenerator
    {
        // Generate code
        $generatedCode = $this->code();

        // Avoid creating empty strings
        if (!$this->isGenerated && $generatedCode !== '') {
            // Create array item
            $class = get_class($this);
            if (!array_key_exists($class, $this->parent->generatedCode)) {
                $this->parent->generatedCode[$class] = [];
            }

            // Pass generated code to parent
            $this->parent->generatedCode[$class][] = $generatedCode;

            // Set flag that we already generated code for this class
            $this->isGenerated = true;
        }

        return $this->parent;
    }

    /**
     * Generate code.
     *
     * @return string Generated code
     */
    abstract public function code(): string;

    /**
     * Set Comments block.
     *
     * @return CommentsGenerator Comments block generator
     */
    public function defComment() : CommentsGenerator
    {
        return (new CommentsGenerator($this))->setIndentation($this->indentation);
    }

    /**
     * Decrease indentation.
     *
     * @param int $indentation
     *
     * @return $this|AbstractGenerator|ClassGenerator
     */
    public function setIndentation(int $indentation): AbstractGenerator
    {
        $this->indentation = $indentation;

        return $this;
    }

    /**
     * Build nested class code array.
     *
     * @param string $className     Nested class name
     * @param array  $formattedCode Collection of code
     *
     * @return array Collection of code with added nested class code
     */
    protected function buildNestedCode(string $className, array $formattedCode = []): array
    {
        $code = $this->getNestedCode($className);
        if ($code !== '') {
            $formattedCode[] = $code;
        }

        return $formattedCode;
    }

    /**
     * Get generated nested code.
     *
     * @param string $className Nested class name
     *
     * @return string Generated nested code or empty string
     */
    protected function getNestedCode(string $className): string
    {
        if (array_key_exists($className, $this->generatedCode)) {
            return ltrim(implode("", $this->generatedCode[$className]), "\n");
        } else {
            return '';
        }
    }

    /**
     * Generate correct value.
     *
     * Method handles arrays, numerics, strings and constants.
     *
     * @param mixed $value Value
     *
     * @return mixed Value
     */
    protected function parseValue($value)
    {
        // If item value is array - recursion
        if (is_array($value)) {
            return $this->arrayValue($value);
        } elseif (is_numeric($value) || is_float($value)) {
            return $value;
        } elseif ($value === null) {
            return null;
        } else {
            try { // Try to evaluate
                eval('$value2 = ' . $value . ';');
                return $value;
            } catch (\Throwable $e) { // Consider it as a string
                return '\''.$value.'\'';
            }
        }
    }

    /**
     * Get array values definition.
     *
     * @param array $items Array key-value pairs collection
     *
     * @return string Array value definition
     */
    protected function arrayValue(array $items = array())
    {
        $result = ['['];
        if (count($items)) {
            $this->increaseIndentation();

            // Iterate array items
            foreach ($items as $key => $value) {
                // Start array key definition
                $result[] = "\n"
                    . $this->indentation($this->indentation)
                    . $this->parseValue($key)
                    . ' => '
                    . $this->parseValue($value)
                    . ',';
            }

            $this->decreaseIndentation();
        }
        $result[] = "\n".$this->indentation($this->indentation).']';

        return implode('', $result);
    }

    /**
     * Increase indentation.
     *
     * @return $this|AbstractGenerator
     */
    public function increaseIndentation(): AbstractGenerator
    {
        return $this->setIndentation($this->indentation + 1);
    }

    /**
     * Get indentation string.
     *
     * @param int $indentation Code level
     *
     * @return string Indentation string
     */
    protected function indentation(int $indentation = 0): string
    {
        return implode('', $indentation > 0 ? array_fill(0, $indentation, '    ') : []);
    }

    /**
     * Decrease indentation.
     *
     * @return $this|AbstractGenerator
     */
    public function decreaseIndentation(): AbstractGenerator
    {
        return $this->setIndentation($this->indentation - 1);
    }
}