majkel89/tcpdf-warper

View on GitHub
src/org/majkel/tcpdfwarper/generator/ClassDefinition.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php
/**
 * Created by PhpStorm.
 * User: MichaƂ (majkel) Kowalik <maf.michal@gmail.com>
 * Date: 1/17/2015
 * Time: 20:44
 */

namespace org\majkel\tcpdfwarper\generator;

use ReflectionMethod;

/**
 * Class ClassDefinition
 * @package org\majkel\tcpdfwarper\generator
 */
class ClassDefinition {

    /** @var string */
    public $name;
    /** @var string */
    public $className;
    /** @var string */
    public $method;
    /** @var Argument[] */
    public $defaultParameters;
    /** @var Argument[] */
    public $requiredArguments;
    /** @var ClassMetaMethod[] */
    public $metaMethods;
    /** @var string[] */
    public $doc;
    /** @var string */
    public $returnType;
    /** @var string */
    public $returnDoc;

    /**
     * @param string $name
     * @return string
     */
    public static function snakeToCamel($name) {
        return preg_replace_callback('#_([A-Za-z])#', function($matches){
            return strtoupper($matches[1]);
        }, trim($name, '_'));
    }

    /**
     * @param string $comment
     * @param int $offset
     * @return string
     */
    protected static function extractFromOffsetToTag($comment, $offset = 0) {
        $position = preg_match('#\s+@[a-zA-Z]+\s+#', $comment, $matches, PREG_OFFSET_CAPTURE, $offset)
            ? $matches[0][1] : strlen($comment);
        return rtrim(substr($comment, $offset, $position - $offset));
    }

    /**
     * @param string $classDoc
     * @return string[]
     */
    protected static function cleanClassDoc($classDoc) {
        return preg_split('#\s*[\r\n]+\s*\*\s*#', $classDoc);
    }

    /**
     * @param string $comment
     * @return string
     */
    protected static function cleanComment($comment) {
        return trim(preg_replace(array('#\s*[\r\n]+\s*\*\s*#', '#\s+#'), ' ', $comment));
    }

    /**
     * @param string $comment
     * @return array type, doc
     * @throws GeneratorException
     */
    protected function setupReturn($comment) {
        if (!preg_match('#\*\s+@return\s+(\w+)\s+#', $comment, $matches, PREG_OFFSET_CAPTURE)) {
            $this->returnDoc = '';
            $this->returnType = 'void';
            return;
        }
        $this->returnDoc = self::extractFromOffsetToTag($comment, $matches[0][1] + strlen($matches[0][0]));
        $this->returnType = $matches[1][0] ? $matches[1][0] : 'mixed';
    }

    /**
     * @param string $comment
     * @param string $parameter
     * @return array type, offset
     * @throws GeneratorException
     */
    protected static function findParam($comment, $parameter) {
        if (!preg_match('#\*\s+@param\s+(([a-zA-Z]+)\s+)?\$'
                .$parameter.'\s+\(([a-zA-Z]+)\)\s+#', $comment, $matches, PREG_OFFSET_CAPTURE))
        {
            throw new GeneratorException("Failed to math property regex");
        }
        $type = $matches[2][0] ? $matches[2][0] : $matches[3][0];
        $offset = $matches[0][1] + strlen($matches[0][0]);
        return array($type, $offset);
    }

    /**
     * @param string $linePrefix
     * @return string
     */
    public function getClassDoc($linePrefix = "\n") {
        return implode($linePrefix, $this->doc);
    }

    /**
     * @param string[] $additionalDoc
     * @param string $class
     * @throws GeneratorException
     */
    protected function parse($additionalDoc, $class = '\TCPDF') {
        $method = new ReflectionMethod($class, $this->method);
        $docComment = $method->getDocComment();
        $offset = 3;

        $this->doc = self::extractFromOffsetToTag($docComment, $offset);
        $this->doc = self::cleanClassDoc($this->doc);

        $this->setupReturn($docComment);

        $parameters = $method->getParameters();

        $this->requiredArguments = array();
        $this->defaultParameters = array();

        foreach ($parameters as $parameter) {
            $name = $parameter->getName();
            $item = new Argument;
            $item->name = $this->snakeToCamel($name);
            $item->required = !$parameter->isOptional();
            if ($parameter->isDefaultValueAvailable()) {
                if ($parameter->isDefaultValueConstant()) {
                    $item->value = $parameter->getDefaultValueConstantName();
                }
                else {
                    $realValue = $parameter->getDefaultValue();
                    $item->value = is_null($realValue) ? 'null'
                            : (is_array($realValue) && empty($realValue) ? 'array()'
                                    : var_export($realValue, true));
                }
            }
            else {
                $this->requiredArguments[$name] = $item;
                $item->value = 'null';
            }

            list($item->type, $offset) = self::findParam($docComment, $name);
            $doc = self::extractFromOffsetToTag($docComment, $offset);
            if (isset($additionalDoc[$name])) {
                $doc .= $additionalDoc[$name];
            }
            $item->doc = self::cleanComment($doc);

            $this->defaultParameters[$name] = $item;
        }
    }

    /**
     * @param ConfigMetaMethod[] $metaMethodsConfig
     * @throws GeneratorException
     */
    protected function generateMetaMethods($metaMethodsConfig) {
        $this->metaMethods = array();
        foreach ($metaMethodsConfig as $methodConfig) {
            $item = new ClassMetaMethod();
            $item->doc = $methodConfig->doc;
            $item->name = $methodConfig->name;
            $item->arguments = array();
            foreach ($methodConfig->args as $arg) {
                if (!isset($this->defaultParameters[$arg])) {
                    throw new GeneratorException("Argument `$arg` does not exists.");
                }
                $item->arguments[$arg] = $this->defaultParameters[$arg];
            }
            $this->metaMethods[] = $item;
        }
    }

    /**
     * @return ClassDefinition
     */
    protected static function newSelf() {
        return new static();
    }

    /**
     * @param ConfigItem $config
     * @return ClassDefinition
     */
    public static function fromConfigItem(ConfigItem $config) {
        $class = static::newSelf();
        $class->name = $config->name;
        $class->className = $config->className;
        $class->method = $config->method;
        $class->parse($config->additionalDoc);
        $class->generateMetaMethods($config->metaMethods);
        return $class;
    }

}