<?php 
/** 
 * Created by PhpStorm. 
 * User: Micha? (majkel) Kowalik <[email protected]> 
 * 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; 
    } 
 
} 
 
 |