diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..79f9173 --- /dev/null +++ b/.php_cs @@ -0,0 +1,28 @@ +in(__DIR__ . '/src') + ->exclude('Generated') +; +return Symfony\CS\Config\Config::create() + ->fixers( + array( + 'controls-spaces', + 'braces', + 'elseif', + 'eof_ending', + 'extra_empty_lines', + 'function_declaration', + 'include', + 'indentation', + 'linefeed', + 'php_closing_tag', + 'psr0', + 'short_tag', + 'trailing_spaces', + 'unused_use', + 'visibility' + ) + ) + ->finder($finder) +; diff --git a/composer.json b/composer.json index 2e46d77..551056b 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,22 @@ { - "name": "tomaszdurka/codegenerator", - "authors": [ - { - "name": "Tomasz Durka", - "email": "tomasz@durka.pl" + "name": "tomaszdurka/codegenerator", + "authors": [ + { + "name": "Tomasz Durka", + "email": "tomasz@durka.pl" + } + ], + "require": { + "php": ">=5.4" + }, + "require-dev": { + "fabpot/php-cs-fixer": "^1.9", + "phpunit/phpunit": "~3.7.10", + "satooshi/php-coveralls": "~0.6.1" + }, + "autoload": { + "psr-0": { + "CodeGenerator\\": "source/" + } } - ], - "require-dev": { - "phpunit/phpunit": "~3.7.10", - "satooshi/php-coveralls": "~0.6.1" - }, - "autoload": { - "psr-0": { - "CodeGenerator\\": "source/" - } - } } diff --git a/source/CodeGenerator/ArrayBlock.php b/source/CodeGenerator/ArrayBlock.php index 4db1451..3bce1c2 100644 --- a/source/CodeGenerator/ArrayBlock.php +++ b/source/CodeGenerator/ArrayBlock.php @@ -2,28 +2,30 @@ namespace CodeGenerator; -class ArrayBlock extends Block { - +class ArrayBlock extends Block +{ /** @var array */ - private $_value; + private $value; /** * @param array $value */ - public function __construct(array $value = null) { - $this->_value = (array) $value; + public function __construct(array $value = null) + { + $this->value = (array)$value; } /** * @return string */ - public function dump() { - $entries = array(); + protected function dumpContent() + { + $entries = []; $isAssociative = $this->isAssociative(); - foreach ($this->_value as $key => $value) { + foreach ($this->value as $key => $value) { $line = ''; if ($isAssociative) { - $line .= $key . ' => '; + $line .= '\'' . $key . '\' => '; } $value = new ValueBlock($value); $line .= $value->dump(); @@ -31,18 +33,20 @@ public function dump() { } $content = implode(', ', $entries); if (strlen($content) < 100) { - return 'array(' . $content . ')'; + return '[' . $content . ']'; } else { $content = implode(",\n", $entries); - return $this->_dumpLine( - 'array(', - $this->_indent($content), - ')' + + return $this->dumpLine( + '[', + $this->indent($content), + ']' ); } } - public function isAssociative() { - return (bool) count(array_filter(array_keys($this->_value), 'is_string')); + public function isAssociative() + { + return (bool)count(array_filter(array_keys($this->value), 'is_string')); } } diff --git a/source/CodeGenerator/Block.php b/source/CodeGenerator/Block.php index 98918a0..7f185d1 100644 --- a/source/CodeGenerator/Block.php +++ b/source/CodeGenerator/Block.php @@ -2,94 +2,166 @@ namespace CodeGenerator; -abstract class Block { - +abstract class Block implements GeneratorConstants +{ /** @var string */ - protected static $_indentation = ' '; + protected static $indentation = ' '; + /** @var DocBlock */ + protected $docBlock; /** + * @param string $indentation + */ + public static function setIndentation($indentation) + { + self::$indentation = (string)$indentation; + } + + /** + * @param string $className + * * @return string */ - abstract public function dump(); + protected static function normalizeClassName($className) + { + if (strpos($className, '\\') !== 0) { + $className = '\\' . $className; + } + + return $className; + } - public function __toString() { + /** + * @param DocBlock $docBlock + * + * @return $this + */ + public function setDocBlock($docBlock) + { + $this->docBlock = $docBlock; + + return $this; + } + + /** + * @return string + */ + public function __toString() + { return $this->dump(); } + /** + * @return string + */ + public function dump() + { + if ($this->docBlock) { + $docBlockText = $this->docBlock->dump(); + if ($docBlockText) { + $docBlockText .= PHP_EOL; + } + + return $docBlockText . '' . $this->dumpContent(); + } + + return $this->dumpContent(); + } + + /** + * @return string + */ + abstract protected function dumpContent(); + /** * @param string $content + * * @return string */ - protected function _indent($content) { - return preg_replace('/(:?^|[\n])/', '$1' . self::$_indentation, $content); + protected function indent($content) + { + return preg_replace('/(:?^|[\n])/', '$1' . self::$indentation, $content); } /** - * @param string $content - * @param boolean|null $untilUnsafe + * @param string $content + * @param bool|null $untilUnsafe + * * @return string */ - protected function _outdent($content, $untilUnsafe = null) { - $indentation = self::$_indentation; + protected function outdent($content, $untilUnsafe = null) + { + $indentation = self::$indentation; if (!$indentation) { return $content; } $lines = explode(PHP_EOL, $content); if ($untilUnsafe) { - $nonemptyLines = array_filter($lines, function ($line) { - return (bool) trim($line); - }); - $unsafeLines = array_filter($nonemptyLines, function ($line) use ($indentation) { - return strpos($line, $indentation) !== 0; - }); + $nonemptyLines = array_filter( + $lines, + function ($line) { + return (bool)trim($line); + } + ); + $unsafeLines = array_filter( + $nonemptyLines, + function ($line) use ($indentation) { + return strpos($line, $indentation) !== 0; + } + ); if (count($unsafeLines) || !count($nonemptyLines)) { return $content; } } foreach ($lines as $key => $line) { - $lines[$key] = preg_replace('/^' . preg_quote(self::$_indentation) . '/', '$1', $line); + $lines[$key] = preg_replace('/^' . preg_quote(self::$indentation) . '/', '$1', $line); } $content = implode(PHP_EOL, $lines); if ($untilUnsafe) { - $content = $this->_outdent($content, $untilUnsafe); + $content = $this->outdent($content, $untilUnsafe); } + return $content; } /** * @param string $line , $line, $line + * * @return string */ - protected function _dumpLine($line) { + protected function dumpLine($line) + { $lines = func_get_args(); - return $this->_dumpLines($lines); - } - /** - * @param string[] $lines - * @return string - */ - protected function _dumpLines(array $lines) { - return implode(PHP_EOL, array_filter($lines, function ($element) { - return !is_null($element); - })); + return $this->dumpLines($lines); } /** - * @param string $indentation + * @param \Reflector $reflection */ - public static function setIndentation($indentation) { - self::$_indentation = (string) $indentation; + protected function setDocBlockFromReflection(\Reflector $reflection) + { + $block = DocBlock::createMethodDocBlockFromReflection($reflection); + if ($block) { + $this->setDocBlock($block); + } } /** - * @param string $className + * @param string[] $lines + * * @return string */ - protected static function _normalizeClassName($className) { - if (strpos($className, '\\') !== 0) { - $className = '\\' . $className; - } - return $className; + protected function dumpLines(array $lines) + { + return implode( + PHP_EOL, + array_filter( + $lines, + function ($element) { + return !is_null($element); + } + ) + ); } } diff --git a/source/CodeGenerator/ClassBlock.php b/source/CodeGenerator/ClassBlock.php index ef0e804..eb0fadb 100644 --- a/source/CodeGenerator/ClassBlock.php +++ b/source/CodeGenerator/ClassBlock.php @@ -2,42 +2,46 @@ namespace CodeGenerator; -class ClassBlock extends Block { - +class ClassBlock extends Block +{ /** @var string */ - private $_name; + private $name; /** @var string */ - private $_namespace; + private $namespace; /** @var string */ - private $_parentClassName; + private $parentClassName; + + /** @var string[] */ + private $interfaces; /** @var string[] */ - private $_interfaces; + private $uses = []; /** @var string[] */ - private $_uses = array(); + private $importUses = []; /** @var ConstantBlock[] */ - private $_constants = array(); + private $constants = []; /** @var PropertyBlock[] */ - private $_properties = array(); + private $properties = []; /** @var MethodBlock[] */ - private $_methods = array(); + private $methods = []; - /** @var boolean */ - private $_abstract; + /** @var bool */ + private $abstract; /** - * @param string $name + * @param string $name * @param string|null $parentClassName - * @param array|null $interfaces + * @param array|null $interfaces */ - public function __construct($name, $parentClassName = null, array $interfaces = null) { - $this->_name = (string) $name; + public function __construct($name, $parentClassName = null, array $interfaces = null) + { + $this->name = (string)$name; if (null !== $parentClassName) { $this->setParentClassName($parentClassName); } @@ -47,181 +51,247 @@ public function __construct($name, $parentClassName = null, array $interfaces = } /** - * @return string + * @param string $parentClassName */ - public function getName() { - return $this->_name; + public function setParentClassName($parentClassName) + { + $this->parentClassName = (string)$parentClassName; } /** - * @param string $parentClassName + * @param string[] $interfaces */ - public function setParentClassName($parentClassName) { - $this->_parentClassName = (string) $parentClassName; + public function setInterfaces(array $interfaces) + { + foreach ($interfaces as $interface) { + $this->addInterface($interface); + } } /** - * @param string $namespace + * @param string $interface */ - public function setNamespace($namespace) { - $this->_namespace = (string) $namespace; + public function addInterface($interface) + { + $this->interfaces[] = $interface; } /** - * @param string[] $interfaces + * @param \ReflectionClass $reflection + * + * @return ClassBlock */ - public function setInterfaces(array $interfaces) { - foreach ($interfaces as $interface) { - $this->addInterface($interface); + public static function buildFromReflection(\ReflectionClass $reflection) + { + $class = new self($reflection->getShortName()); + $class->setNamespace($reflection->getNamespaceName()); + $reflectionParentClass = $reflection->getParentClass(); + if ($reflectionParentClass) { + $class->setParentClassName($reflectionParentClass->getName()); } + $class->setAbstract($reflection->isAbstract()); + if ($interfaces = $reflection->getInterfaceNames()) { + if ($reflectionParentClass) { + $parentInterfaces = $reflection->getParentClass()->getInterfaceNames(); + $interfaces = array_diff($interfaces, $parentInterfaces); + } + $class->setInterfaces($interfaces); + } + foreach ($reflection->getMethods() as $reflectionMethod) { + if ($reflectionMethod->getDeclaringClass() == $reflection) { + $method = MethodBlock::buildFromReflection($reflectionMethod); + $class->addMethod($method); + } + } + foreach ($reflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->getDeclaringClass() == $reflection) { + $property = PropertyBlock::buildFromReflection($reflectionProperty); + $class->addProperty($property); + } + } + foreach ($reflection->getConstants() as $name => $value) { + if (!$reflection->getParentClass() || ($reflection->getParentClass() && !$reflection->getParentClass()->hasConstant( + $name + )) + ) { + $class->addConstant(new ConstantBlock($name, $value)); + } + } + + return $class; } /** - * @param boolean $abstract + * @param string $namespace */ - public function setAbstract($abstract) { - $this->_abstract = (bool) $abstract; + public function setNamespace($namespace) + { + $this->namespace = (string)$namespace; } /** - * @param string $name + * @return string */ - public function addUse($name) { - $this->_uses[] = $name; + public function getNamespace() + { + return $this->namespace; } /** - * @param ConstantBlock $constant + * @param bool $abstract */ - public function addConstant(ConstantBlock $constant) { - $this->_constants[$constant->getName()] = $constant; + public function setAbstract($abstract) + { + $this->abstract = (bool)$abstract; + } + + /** + * @param MethodBlock $method + */ + public function addMethod(MethodBlock $method) + { + $this->methods[$method->getName()] = $method; } /** * @param PropertyBlock $property */ - public function addProperty(PropertyBlock $property) { - $this->_properties[$property->getName()] = $property; + public function addProperty(PropertyBlock $property) + { + $this->properties[$property->getName()] = $property; } /** - * @param MethodBlock $method + * @param ConstantBlock $constant */ - public function addMethod(MethodBlock $method) { - $this->_methods[$method->getName()] = $method; + public function addConstant(ConstantBlock $constant) + { + $this->constants[$constant->getName()] = $constant; } /** - * @param string $interface + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function addUse($name) + { + $this->uses[] = $name; + } + + /** + * @param string $name + */ + public function addImportUse($name) + { + $this->importUses[] = $name; + } + + /** + * @return string */ - public function addInterface($interface) { - $this->_interfaces[] = $interface; + public function dump() + { + return $this->dumpContent(); } /** * @return string */ - public function dump() { - $lines = array(); - $lines[] = $this->_dumpHeader(); - foreach ($this->_uses as $use) { + protected function dumpContent() + { + $lines = []; + $lines[] = $this->dumpHeader(); + foreach ($this->uses as $use) { + $lines[] = $this->indent("use {$use};"); $lines[] = ''; - $lines[] = $this->_indent("use ${use};"); } - foreach ($this->_constants as $constant) { + foreach ($this->constants as $constant) { + $lines[] = $this->indent($constant->dump()); $lines[] = ''; - $lines[] = $this->_indent($constant->dump()); } - foreach ($this->_properties as $property) { + foreach ($this->properties as $property) { + $lines[] = $this->indent($property->dump()); $lines[] = ''; - $lines[] = $this->_indent($property->dump()); } - foreach ($this->_methods as $method) { + foreach ($this->methods as $method) { + $lines[] = $this->indent($method->dump()); $lines[] = ''; - $lines[] = $this->_indent($method->dump()); } - $lines[] = $this->_dumpFooter(); - return $this->_dumpLines($lines); + if (!empty($this->uses) || !empty($this->constants) || !empty($this->properties) || !empty($this->methods)) { + array_pop($lines); + } + + $lines[] = $this->dumpFooter(); + + return $this->dumpLines($lines); } /** * @return string */ - private function _dumpHeader() { - $lines = array(); - if ($this->_namespace) { - $lines[] = 'namespace ' . $this->_namespace . ';'; + private function dumpHeader() + { + $lines = []; + if ($this->namespace) { + $lines[] = 'namespace ' . $this->namespace . ';'; + $lines[] = ''; + } + if (count($this->importUses)) { + foreach ($this->importUses as $import) { + $lines[] = 'use ' . $import . ';'; + } $lines[] = ''; } + + if ($this->docBlock) { + $lines[] = $this->docBlock->dump(); + } + $classDeclaration = ''; - if ($this->_abstract) { - $classDeclaration .= 'abstract '; + if ($this->abstract) { + $classDeclaration .= self::KEYWORD_ABSTRACT . ' '; } - $classDeclaration .= 'class ' . $this->_name; - if ($this->_parentClassName) { - $classDeclaration .= ' extends ' . $this->_getParentClassName(); + $classDeclaration .= 'class ' . $this->name; + if ($this->parentClassName) { + $classDeclaration .= ' extends ' . $this->getParentClassName(); } - if ($this->_interfaces) { - $classDeclaration .= ' implements ' . implode(', ', $this->_getInterfaces()); + if ($this->interfaces) { + $classDeclaration .= ' implements ' . implode(', ', $this->getInterfaces()); } - $classDeclaration .= ' {'; $lines[] = $classDeclaration; - return $this->_dumpLines($lines); + $lines[] = '{'; + + return $this->dumpLines($lines); } /** - * @return string[] + * @return string */ - private function _getInterfaces() { - return array_map(array('\\CodeGenerator\\ClassBlock', '_normalizeClassName'), $this->_interfaces); + private function getParentClassName() + { + return self::normalizeClassName($this->parentClassName); } /** - * @return string + * @return string[] */ - private function _getParentClassName() { - return self::_normalizeClassName($this->_parentClassName); + private function getInterfaces() + { + return array_map(['\\CodeGenerator\\ClassBlock', 'normalizeClassName'], $this->interfaces); } /** * @return string */ - private function _dumpFooter() { + private function dumpFooter() + { return '}'; } - - public static function buildFromReflection(\ReflectionClass $reflection) { - $class = new self($reflection->getShortName()); - $class->setNamespace($reflection->getNamespaceName()); - $reflectionParentClass = $reflection->getParentClass(); - if ($reflectionParentClass) { - $class->setParentClassName($reflectionParentClass->getName()); - } - $class->setAbstract($reflection->isAbstract()); - if ($interfaces = $reflection->getInterfaceNames()) { - if ($reflectionParentClass) { - $parentInterfaces = $reflection->getParentClass()->getInterfaceNames(); - $interfaces = array_diff($interfaces, $parentInterfaces); - } - $class->setInterfaces($interfaces); - } - foreach ($reflection->getMethods() as $reflectionMethod) { - if ($reflectionMethod->getDeclaringClass() == $reflection) { - $method = MethodBlock::buildFromReflection($reflectionMethod); - $class->addMethod($method); - } - } - foreach ($reflection->getProperties() as $reflectionProperty) { - if ($reflectionProperty->getDeclaringClass() == $reflection) { - $property = PropertyBlock::buildFromReflection($reflectionProperty); - $class->addProperty($property); - } - } - foreach ($reflection->getConstants() as $name => $value) { - if (!$reflection->getParentClass()->hasConstant($name)) { - $class->addConstant(new ConstantBlock($name, $value)); - } - } - return $class; - } } diff --git a/source/CodeGenerator/ConstantBlock.php b/source/CodeGenerator/ConstantBlock.php index d7dc031..11dab15 100644 --- a/source/CodeGenerator/ConstantBlock.php +++ b/source/CodeGenerator/ConstantBlock.php @@ -2,31 +2,34 @@ namespace CodeGenerator; -class ConstantBlock extends Block { - +class ConstantBlock extends Block +{ /** @var string */ - private $_name; + private $name; /** @var string|int */ - private $_value; + private $value; /** - * @param string $name + * @param string $name * @param string|int $value */ - public function __construct($name, $value) { - $this->_name = (string) $name; - $this->_value = $value; + public function __construct($name, $value) + { + $this->name = (string)$name; + $this->value = $value; } /** * @return string */ - public function getName() { - return $this->_name; + public function getName() + { + return $this->name; } - public function dump() { - return 'const ' . $this->_name . ' = ' . var_export($this->_value, true) . ';'; + protected function dumpContent() + { + return 'const ' . $this->name . ' = ' . var_export($this->value, true) . ';'; } } diff --git a/source/CodeGenerator/DocBlock.php b/source/CodeGenerator/DocBlock.php new file mode 100644 index 0000000..6445c18 --- /dev/null +++ b/source/CodeGenerator/DocBlock.php @@ -0,0 +1,202 @@ +getDocComment(); + if (!$string) { + return null; + } + + return self::createDocBlockFromCommentString($string); + } + + /** + * @param $string + * + * @return DocBlock + */ + public static function createDocBlockFromCommentString($string) + { + + // cleanup + $string = trim($string); + if (substr($string, 0, 3) === self::DOC_BLOCK_START) { + $string = substr($string, 3); + } + if (substr($string, -2) === self::DOC_BLOCK_END) { + $string = substr($string, 0, strlen($string) - strlen(self::DOC_BLOCK_END)); + } + $string = rtrim($string); + $string = trim($string, "\n"); + + $block = new self(); + $lines = explode(PHP_EOL, $string); + + foreach ($lines as $line) { + if (self::isTag($line) && preg_match('/(\s?\*\s?)*(@([\w]+))(?=\s|$)/', $line, $matches)) { + $tag = $matches[3]; + $content = trim(str_replace($matches[0], '', $line)); + $block->addText('@' . $tag . ' ' . $content); + } else { + if (!preg_match('/(\s?\*\s?)(.*)/', $line, $matches)) { + continue; + } + $block->addText($matches[2]); + } + } + + return $block; + } + + /** + * @param $tagName + * + * @return bool + */ + protected static function isTag($tagName) + { + return 0 !== preg_match('/(^[\\*\s*]*@)/', $tagName); + } + + /** + * @param $text + * + * @return $this + */ + public function addText($text) + { + $splitText = str_split($text, 72); + + foreach ($splitText as $line) { + $this->texts[] = $line; + } + + return $this; + } + + /** + * @param $tagName + * @param $content + * + * @return $this + */ + public function addTag($tagName, $content) + { + if (substr($tagName, 0, 1) === '@') { + $tagName = substr($tagName, 1); + } + if (!in_array($tagName, self::$allowedTags, true)) { + return $this; + } + + $this->tags[] = ['tag' => '@' . $tagName, 'content' => $content]; + + return $this; + } + + /** + * @return string + */ + public function dump() + { + return $this->dumpContent(); + } + + /** + * @return string + */ + protected function dumpContent() + { + if (!count($this->texts) && !count($this->tags)) { + return ''; + } + + $lines = []; + $lines[] = self::DOC_BLOCK_START; + if (count($this->texts)) { + $line = ''; + foreach ($this->texts as $line) { + $lines[] = rtrim(' * ' . $line); + } + if ($line !== '' && count($this->tags)) { + // if last line was already empty, do not add another one + $lines[] = ' ' . self::DOC_BLOCK_INDENT; + } + } + + foreach ($this->tags as $row) { + $content = ' ' . $row['tag'] . ' ' . $row['content']; + $lines[] = ' ' . self::DOC_BLOCK_INDENT . $content; + } + $lines[] = ' ' . self::DOC_BLOCK_END; + + return $this->dumpLines($lines); + } +} diff --git a/source/CodeGenerator/FileBlock.php b/source/CodeGenerator/FileBlock.php index b80fc33..a69bc18 100644 --- a/source/CodeGenerator/FileBlock.php +++ b/source/CodeGenerator/FileBlock.php @@ -2,26 +2,29 @@ namespace CodeGenerator; -class FileBlock extends Block { - +class FileBlock extends Block +{ /** @var Block[] */ - private $_blocks = array(); + private $blocks = []; /** * @param Block $block */ - public function addBlock(Block $block) { - $this->_blocks[] = $block; + public function addBlock(Block $block) + { + $this->blocks[] = $block; } - public function dump() { - $lines = array(); + protected function dumpContent() + { + $lines = []; $lines[] = '_blocks as $block) { + foreach ($this->blocks as $block) { $lines[] = ''; $lines[] = $block->dump(); } $lines[] = ''; - return $this->_dumpLines($lines); + + return $this->dumpLines($lines); } } diff --git a/source/CodeGenerator/FunctionBlock.php b/source/CodeGenerator/FunctionBlock.php index 354014e..c8e7e72 100644 --- a/source/CodeGenerator/FunctionBlock.php +++ b/source/CodeGenerator/FunctionBlock.php @@ -2,24 +2,20 @@ namespace CodeGenerator; -class FunctionBlock extends Block { - +class FunctionBlock extends Block +{ /** @var string|null */ - protected $_name; - - /** @var ParameterBlock[] */ - private $_parameters = array(); - + protected $name; /** @var string */ - protected $_code; - - /** @var string|null */ - protected $_docBlock; + protected $code; + /** @var ParameterBlock[] */ + private $parameters = []; /** * @param callable|string|null $body */ - public function __construct($body = null) { + public function __construct($body = null) + { if (null !== $body) { if ($body instanceof \Closure) { $this->extractFromClosure($body); @@ -30,57 +26,32 @@ public function __construct($body = null) { } /** - * @param string $name - */ - public function setName($name) { - $this->_name = (string) $name; - } - - /** - * @return string|null - */ - public function getName() { - return $this->_name; - } - - /** - * @param ParameterBlock $parameter - * @throws \Exception - */ - public function addParameter(ParameterBlock $parameter) { - if (array_key_exists($parameter->getName(), $this->_parameters)) { - throw new \Exception('Parameter `' . $parameter->getName() . '` is already set.'); - } - $this->_parameters[$parameter->getName()] = $parameter; - } - - /** - * @param string $code + * @param \Closure $closure */ - public function setCode($code) { - if (null !== $code) { - $code = $this->_outdent((string) $code, true); - } - $this->_code = $code; + public function extractFromClosure(\Closure $closure) + { + $this->extractFromReflection(new \ReflectionFunction($closure)); } /** - * @param string|null $docBlock + * @param \ReflectionFunctionAbstract $reflection */ - public function setDocBlock($docBlock) { - if (null !== $docBlock) { - $docBlock = (string) $docBlock; - } - $this->_docBlock = $docBlock; + public function extractFromReflection(\ReflectionFunctionAbstract $reflection) + { + $this->setBodyFromReflection($reflection); + $this->setParametersFromReflection($reflection); + $this->setDocBlockFromReflection($reflection); } /** * @param \ReflectionFunctionAbstract $reflection */ - public function setBodyFromReflection(\ReflectionFunctionAbstract $reflection) { + protected function setBodyFromReflection(\ReflectionFunctionAbstract $reflection) + { /** @var $reflection \ReflectionMethod */ if (is_a($reflection, '\\ReflectionMethod') && $reflection->isAbstract()) { - $this->_code = null; + $this->code = null; + return; } $file = new \SplFileObject($reflection->getFileName()); @@ -107,10 +78,22 @@ public function setBodyFromReflection(\ReflectionFunctionAbstract $reflection) { $this->setCode($code); } + /** + * @param string $code + */ + public function setCode($code) + { + if (null !== $code) { + $code = $this->outdent((string)$code, true); + } + $this->code = $code; + } + /** * @param \ReflectionFunctionAbstract $reflection */ - public function setParametersFromReflection(\ReflectionFunctionAbstract $reflection) { + protected function setParametersFromReflection(\ReflectionFunctionAbstract $reflection) + { foreach ($reflection->getParameters() as $reflectionParameter) { $parameter = ParameterBlock::buildFromReflection($reflectionParameter); $this->addParameter($parameter); @@ -118,68 +101,70 @@ public function setParametersFromReflection(\ReflectionFunctionAbstract $reflect } /** - * @param \ReflectionFunctionAbstract $reflection + * @param ParameterBlock $parameter + * + * @throws \Exception */ - public function setDocBlockFromReflection(\ReflectionFunctionAbstract $reflection) { - $docBlock = $reflection->getDocComment(); - if ($docBlock) { - $docBlock = preg_replace('/([\n\r])(' . self::$_indentation . ')+/', '$1', $docBlock); - $this->setDocBlock($docBlock); + public function addParameter(ParameterBlock $parameter) + { + if (array_key_exists($parameter->getName(), $this->parameters)) { + throw new \Exception('Parameter `' . $parameter->getName() . '` is already set.'); } + $this->parameters[$parameter->getName()] = $parameter; } - public function dump() { - return $this->_dumpLine( - $this->_dumpDocBlock(), - $this->_dumpHeader() . $this->_dumpBody() - ); + /** + * @return string|null + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = (string)$name; } /** * @return string */ - protected function _dumpDocBlock() { - return $this->_docBlock; + protected function dumpContent() + { + return $this->dumpLine( + $this->dumpHeader() . $this->dumpBody() + ); } /** * @return string */ - protected function _dumpHeader() { + protected function dumpHeader() + { $content = 'function'; - if ($this->_name) { - $content .= ' ' . $this->_name; + if ($this->name) { + $content .= ' ' . $this->name; } $content .= '('; - $content .= implode(', ', $this->_parameters); + $content .= implode(', ', $this->parameters); $content .= ')'; + return $content; } /** * @return string */ - protected function _dumpBody() { - $code = $this->_code; + protected function dumpBody() + { + $code = $this->code; if ($code) { - $code = $this->_indent($code); + $code = $this->indent($code); } - return $this->_dumpLine(' {', $code, '}'); - } - /** - * @param \ReflectionFunctionAbstract $reflection - */ - public function extractFromReflection(\ReflectionFunctionAbstract $reflection) { - $this->setBodyFromReflection($reflection); - $this->setParametersFromReflection($reflection); - $this->setDocBlockFromReflection($reflection); - } - - /** - * @param \Closure $closure - */ - public function extractFromClosure(\Closure $closure) { - $this->extractFromReflection(new \ReflectionFunction($closure)); + return $this->dumpLine('', '{', $code, '}'); } } diff --git a/source/CodeGenerator/GeneratorConstants.php b/source/CodeGenerator/GeneratorConstants.php new file mode 100644 index 0000000..7524364 --- /dev/null +++ b/source/CodeGenerator/GeneratorConstants.php @@ -0,0 +1,12 @@ +name = (string)$name; + + if (!is_null($parentInterfaceNames)) { + $this->setParentInterfaceNames($parentInterfaceNames); + } + } + + /** + * @param string $name + */ + public function addUse($name) + { + $this->uses[] = $name; + } + + /** + * @param string $name + */ + public function addImportUse($name) + { + $this->importUses[] = $name; + } + + /** + * @param array $parentInterfaceNames + */ + public function setParentInterfaceNames(array $parentInterfaceNames) + { + foreach ($parentInterfaceNames as $parentInterfaceName) { + $this->addParentInterfaceName($parentInterfaceName); + } + } + + /** + * @param string $parentInterfaceName + */ + public function addParentInterfaceName($parentInterfaceName) + { + $this->parentInterfaceNames[] = (string)$parentInterfaceName; + } + + /** + * @param \ReflectionClass $reflection + * + * @return InterfaceBlock + */ + public static function buildFromReflection(\ReflectionClass $reflection) + { + $class = new self($reflection->getShortName()); + $class->setNamespace($reflection->getNamespaceName()); + $reflectionParentInterfaces = $reflection->getInterfaces(); + + if (!empty($reflectionParentInterfaces)) { + $interfaces = []; + foreach ($reflectionParentInterfaces as $reflectionParentInterface) { + $interfaces[] = $reflectionParentInterface->getName(); + } + $class->setParentInterfaceNames($interfaces); + } + + foreach ($reflection->getMethods() as $reflectionMethod) { + if ($reflectionMethod->getDeclaringClass() == $reflection) { + $method = InterfaceMethodBlock::buildFromReflection($reflectionMethod); + $class->addMethod($method); + } + } + + $constants = $reflection->getConstants(); + if (count($constants)) { + $parentConstants = self::getAllConstantsOfParentInterfaces($reflection); + foreach ($constants as $name => $value) { + if (!in_array($name, $parentConstants)) { + $class->addConstant(new ConstantBlock($name, $value)); + } + } + } + + return $class; + } + + /** + * @param string $namespace + */ + public function setNamespace($namespace) + { + $this->namespace = (string)$namespace; + } + + /** + * @param InterfaceMethodBlock $method + */ + public function addMethod(InterfaceMethodBlock $method) + { + $this->methods[$method->getName()] = $method; + } + + /** + * @param \ReflectionClass $reflection + * + * @return array + */ + protected static function getAllConstantsOfParentInterfaces(\ReflectionClass $reflection) + { + $parentConstants = []; + $parentInterfaces = $reflection->getInterfaces(); + foreach ($parentInterfaces as $parentInterface) { + $parentInterfaceConstants = $parentInterface->getConstants(); + $constantNames = array_keys($parentInterfaceConstants); + $parentConstants += $constantNames; + } + + return $parentConstants; + } + + /** + * @param ConstantBlock $constant + */ + public function addConstant(ConstantBlock $constant) + { + $this->constants[$constant->getName()] = $constant; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return string + */ + protected function dumpContent() + { + $lines = []; + $lines[] = $this->dumpHeader(); + foreach ($this->uses as $use) { + $lines[] = $this->indent("use ${use};"); + $lines[] = ''; + } + foreach ($this->constants as $constant) { + $lines[] = $this->indent($constant->dump()); + $lines[] = ''; + } + foreach ($this->methods as $method) { + $lines[] = $this->indent($method->dump()); + $lines[] = ''; + } + if (!empty($this->constants) || !empty($this->methods)) { + array_pop($lines); + } + + $lines[] = $this->dumpFooter(); + + return $this->dumpLines($lines); + } + + /** + * @return string + */ + private function dumpHeader() + { + $lines = []; + if ($this->namespace) { + $lines[] = 'namespace ' . $this->namespace . ';'; + $lines[] = ''; + } + if (count($this->importUses)) { + foreach ($this->importUses as $import) { + $lines[] = 'use ' . $import . ';'; + } + $lines[] = ''; + } + $classDeclaration = 'interface ' . $this->name; + if ($this->parentInterfaceNames) { + $classDeclaration .= ' extends ' . $this->getParentInterfaces(); + } + $lines[] = $classDeclaration; + $lines[] = '{'; + + return $this->dumpLines($lines); + } + + /** + * @return string + */ + private function getParentInterfaces() + { + $cleaned = []; + foreach ($this->parentInterfaceNames as $parentInterfaceName) { + $cleaned[] = self::normalizeClassName($parentInterfaceName); + } + + return implode(', ', $cleaned); + } + + /** + * @return string + */ + private function dumpFooter() + { + return '}'; + } + + /** + * @param $namespacedClassOrInterfaceName + */ + public function extractConstantsFromOtherClassOrInterface($namespacedClassOrInterfaceName) + { + $reflection = new \ReflectionClass($namespacedClassOrInterfaceName); + $constants = $reflection->getConstants(); + + foreach ($constants as $name => $val) { + $this->addConstant(new ConstantBlock($name, $val)); + } + } +} diff --git a/source/CodeGenerator/InterfaceMethodBlock.php b/source/CodeGenerator/InterfaceMethodBlock.php new file mode 100644 index 0000000..586e9af --- /dev/null +++ b/source/CodeGenerator/InterfaceMethodBlock.php @@ -0,0 +1,44 @@ +setName($name); + parent::__construct(); + } + + /** + * @param \ReflectionMethod $reflection + * + * @return InterfaceMethodBlock + */ + public static function buildFromReflection(\ReflectionMethod $reflection) + { + $method = new self($reflection->getName()); + $method->extractFromReflection($reflection); + + return $method; + } + + /** + * @return string + */ + protected function dumpHeader() + { + return self::VISIBILITY_PUBLIC . ' ' . parent::dumpHeader(); + } + + /** + * @return string + */ + protected function dumpBody() + { + return ';'; + } +} diff --git a/source/CodeGenerator/MethodBlock.php b/source/CodeGenerator/MethodBlock.php index 90ae557..cbb659a 100644 --- a/source/CodeGenerator/MethodBlock.php +++ b/source/CodeGenerator/MethodBlock.php @@ -2,24 +2,25 @@ namespace CodeGenerator; -class MethodBlock extends FunctionBlock { - +class MethodBlock extends FunctionBlock +{ /** @var string */ - private $_visibility; + private $visibility; - /** @var boolean */ - private $_static; + /** @var bool */ + private $static; - /** @var boolean */ - private $_abstract; + /** @var bool */ + private $abstract; /** - * @param string $name + * @param string $name * @param callable|string|null $body */ - public function __construct($name, $body = null) { + public function __construct($name, $body = null) + { $this->setName($name); - $this->setVisibility('public'); + $this->setVisibility(self::VISIBILITY_PUBLIC); $this->setStatic(false); $this->setAbstract(false); parent::__construct($body); @@ -28,28 +29,45 @@ public function __construct($name, $body = null) { /** * @param string $visibility */ - public function setVisibility($visibility) { - $this->_visibility = (string) $visibility; + public function setVisibility($visibility) + { + $this->visibility = (string)$visibility; + } + + /** + * @param bool $static + */ + public function setStatic($static) + { + $this->static = (bool)$static; } /** - * @param boolean $static + * @param bool $abstract */ - public function setStatic($static) { - $this->_static = (bool) $static; + public function setAbstract($abstract) + { + $this->abstract = (bool)$abstract; } /** - * @param boolean $abstract + * @param \ReflectionMethod $reflection + * + * @return MethodBlock */ - public function setAbstract($abstract) { - $this->_abstract = (bool) $abstract; + public static function buildFromReflection(\ReflectionMethod $reflection) + { + $method = new self($reflection->getName()); + $method->extractFromReflection($reflection); + + return $method; } /** * @param \ReflectionFunctionAbstract $reflection */ - public function extractFromReflection(\ReflectionFunctionAbstract $reflection) { + public function extractFromReflection(\ReflectionFunctionAbstract $reflection) + { parent::extractFromReflection($reflection); if ($reflection instanceof \ReflectionMethod) { $this->setVisibilityFromReflection($reflection); @@ -61,59 +79,56 @@ public function extractFromReflection(\ReflectionFunctionAbstract $reflection) { /** * @param \ReflectionMethod $reflection */ - public function setVisibilityFromReflection(\ReflectionMethod $reflection) { + public function setVisibilityFromReflection(\ReflectionMethod $reflection) + { if ($reflection->isPublic()) { - $this->setVisibility('public'); + $this->setVisibility(self::VISIBILITY_PUBLIC); } if ($reflection->isProtected()) { - $this->setVisibility('protected'); + $this->setVisibility(self::VISIBILITY_PROTECTED); } if ($reflection->isPrivate()) { - $this->setVisibility('private'); + $this->setVisibility(self::VISIBILITY_PRIVATE); } } /** * @param \ReflectionMethod $reflection */ - public function setAbstractFromReflection(\ReflectionMethod $reflection) { - $this->setAbstract($reflection->isAbstract()); + public function setStaticFromReflection(\ReflectionMethod $reflection) + { + $this->setStatic($reflection->isStatic()); } /** * @param \ReflectionMethod $reflection */ - public function setStaticFromReflection(\ReflectionMethod $reflection) { - $this->setStatic($reflection->isStatic()); + public function setAbstractFromReflection(\ReflectionMethod $reflection) + { + $this->setAbstract($reflection->isAbstract()); } - protected function _dumpHeader() { + protected function dumpHeader() + { $code = ''; - if ($this->_abstract) { - $code .= 'abstract '; + if ($this->abstract) { + $code .= self::KEYWORD_ABSTRACT . ' '; } - $code .= $this->_visibility; - if ($this->_static) { - $code .= ' static'; + $code .= $this->visibility; + if ($this->static) { + $code .= ' ' . self::KEYWORD_STATIC; } - $code .= ' ' . parent::_dumpHeader(); + $code .= ' ' . parent::dumpHeader(); + return $code; } - protected function _dumpBody() { - if ($this->_abstract) { + protected function dumpBody() + { + if ($this->abstract) { return ';'; } - return parent::_dumpBody(); - } - /** - * @param \ReflectionMethod $reflection - * @return MethodBlock - */ - public static function buildFromReflection(\ReflectionMethod $reflection) { - $method = new self($reflection->getName()); - $method->extractFromReflection($reflection); - return $method; + return parent::dumpBody(); } } diff --git a/source/CodeGenerator/ParameterBlock.php b/source/CodeGenerator/ParameterBlock.php index 4e35fad..93cf099 100644 --- a/source/CodeGenerator/ParameterBlock.php +++ b/source/CodeGenerator/ParameterBlock.php @@ -2,110 +2,145 @@ namespace CodeGenerator; -class ParameterBlock extends Block { - +class ParameterBlock extends Block +{ /** @var string */ - private $_name; + private $name; /** @var string|null */ - private $_type; + private $type; /** @var mixed */ - private $_defaultValue; + private $defaultValue; - /** @var boolean */ - private $_optional; + /** @var bool */ + private $optional; - /** @var boolean */ - private $_passedByReference; + /** @var bool */ + private $passedByReference; /** - * @param string $name - * @param string|null $type - * @param null $optional - * @param mixed|null $defaultValue - * @param boolean|null $passedByReference + * @param string $name + * @param string|null $type + * @param null $optional + * @param mixed|null $defaultValue + * @param bool|null $passedByReference + * * @throws \Exception + * * @internal param bool|null $isOptional */ - public function __construct($name, $type = null, $optional = null, $defaultValue = null, $passedByReference = null) { - $this->_name = (string) $name; + public function __construct($name, $type = null, $optional = null, $defaultValue = null, $passedByReference = null) + { + $this->name = (string)$name; if (null !== $type) { - $this->_type = (string) $type; + $this->type = (string)$type; } - $this->_optional = (bool) $optional; + $this->optional = (bool)$optional; if (null !== $defaultValue) { - if (!$this->_optional) { + if (!$this->optional) { throw new \Exception('Cannot set default value for non-optional parameter'); } - $this->_defaultValue = $defaultValue; + $this->defaultValue = $defaultValue; } - $this->_passedByReference = (bool) $passedByReference; + $this->passedByReference = (bool)$passedByReference; + } + + /** + * @param \ReflectionParameter $reflection + * + * @return ParameterBlock + */ + public static function buildFromReflection(\ReflectionParameter $reflection) + { + $type = null; + if ($reflection->isCallable()) { + $type = 'callable'; + } + if ($reflection->isArray()) { + $type = 'array'; + } + if ($reflection->getClass()) { + $type = $reflection->getClass()->getName(); + } + $defaultValue = null; + if ($reflection->isDefaultValueAvailable()) { + $defaultValue = $reflection->getDefaultValue(); + } + + return new self( + $reflection->getName(), + $type, + $reflection->isOptional(), + $defaultValue, + $reflection->isPassedByReference() + ); } /** * @return string */ - public function getName() { - return $this->_name; + public function getName() + { + return $this->name; + } + + /** + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * @return boolean + */ + public function isOptional() + { + return $this->optional; } /** * @return string */ - public function dump() { + protected function dumpContent() + { $content = ''; - if ($this->_type) { - $content .= $this->_getType() . ' '; + if ($this->type) { + $content .= $this->getType() . ' '; } - if ($this->_passedByReference) { + if ($this->passedByReference) { $content .= '&'; } - $content .= '$' . $this->_name; - if ($this->_optional) { + $content .= '$' . $this->name; + if ($this->optional) { $content .= ' = ' . $this->_dumpDefaultValue(); } - return $content; - } - protected function _dumpDefaultValue() { - if (null === $this->_defaultValue) { - return 'null'; - } - $value = new ValueBlock($this->_defaultValue); - return $value->dump(); + return $content; } /** * @return null|string */ - protected function _getType() { - $type = $this->_type; + public function getType() + { + $type = $this->type; if (!in_array($type, [null, 'array', 'callable'], true)) { - $type = self::_normalizeClassName($type); + $type = self::normalizeClassName($type); } + return $type; } - /** - * @param \ReflectionParameter $reflection - * @return ParameterBlock - */ - public static function buildFromReflection(\ReflectionParameter $reflection) { - $type = null; - if ($reflection->isCallable()) { - $type = 'callable'; - } - if ($reflection->isArray()) { - $type = 'array'; - } - if ($reflection->getClass()) { - $type = $reflection->getClass()->getName(); - } - $defaultValue = null; - if ($reflection->isDefaultValueAvailable()) { - $defaultValue = $reflection->getDefaultValue(); + protected function _dumpDefaultValue() + { + if (null === $this->defaultValue) { + return 'null'; } - return new self($reflection->getName(), $type, $reflection->isOptional(), $defaultValue, $reflection->isPassedByReference()); + $value = new ValueBlock($this->defaultValue); + + return $value->dump(); } } diff --git a/source/CodeGenerator/PropertyBlock.php b/source/CodeGenerator/PropertyBlock.php index c18f621..0cc5bc3 100644 --- a/source/CodeGenerator/PropertyBlock.php +++ b/source/CodeGenerator/PropertyBlock.php @@ -2,122 +2,97 @@ namespace CodeGenerator; -class PropertyBlock extends Block { - +class PropertyBlock extends Block +{ /** @var string */ - private $_name; - + private $name; /** @var string */ - private $_visibility; + private $visibility; - /** @var mixed */ - private $_defaultValue; + /** @var bool */ + private $static; - /** @var string|null */ - protected $_docBlock; + /** @var mixed */ + private $defaultValue; /** * @param string $name */ - public function __construct($name) { - $this->_name = (string) $name; - $this->setVisibility('public'); - } - - /** - * @return string - */ - public function getName() { - return $this->_name; + public function __construct($name) + { + $this->name = (string)$name; + $this->setVisibility(self::VISIBILITY_PUBLIC); + $this->setStatic(false); } /** * @param string $visibility */ - public function setVisibility($visibility) { - $this->_visibility = (string) $visibility; + public function setVisibility($visibility) + { + $this->visibility = (string)$visibility; } /** - * @param mixed $value + * @param boolean $static */ - public function setDefaultValue($value) { - $this->_defaultValue = $value; + public function setStatic($static) + { + $this->static = (bool)$static; } /** - * @param string|null $docBlock + * @param \ReflectionProperty $reflection + * @return PropertyBlock */ - public function setDocBlock($docBlock) { - if (null !== $docBlock) { - $docBlock = (string) $docBlock; - } - $this->_docBlock = $docBlock; - } + public static function buildFromReflection(\ReflectionProperty $reflection) + { + $property = new self($reflection->getName()); + $property->extractFromReflection($reflection); - public function dump() { - return $this->_dumpLine( - $this->_dumpDocBlock(), - $this->_dumpValue() - ); + // $property->setDefaultValue($reflection->getValue()); + return $property; } /** * @param \ReflectionProperty $reflection */ - public function extractFromReflection(\ReflectionProperty $reflection) { - $this->_setVisibilityFromReflection($reflection); - $this->_setDefaultValueFromReflection($reflection); - $this->_setDocBlockFromReflection($reflection); + public function extractFromReflection(\ReflectionProperty $reflection) + { + $this->setStaticFromReflection($reflection); + $this->setVisibilityFromReflection($reflection); + $this->setDefaultValueFromReflection($reflection); + $this->setDocBlockFromReflection($reflection); } - /** - * @return string - */ - protected function _dumpDocBlock() { - return $this->_docBlock; - } - - /** - * @return string - */ - protected function _dumpValue() { - $content = $this->_visibility . ' $' . $this->_name; - if (null !== $this->_defaultValue) { - $value = new ValueBlock($this->_defaultValue); - $content .= ' = ' . $value->dump(); + protected function setStaticFromReflection(\ReflectionProperty $reflection) + { + if ($reflection->isStatic()) { + $this->setStatic(true); } - $content .= ';'; - return $content; } /** * @param \ReflectionProperty $reflection */ - protected function _setVisibilityFromReflection(\ReflectionProperty $reflection) { + protected function setVisibilityFromReflection(\ReflectionProperty $reflection) + { if ($reflection->isPublic()) { - $this->setVisibility('public'); + $this->setVisibility(self::VISIBILITY_PUBLIC); } if ($reflection->isProtected()) { - $this->setVisibility('protected'); + $this->setVisibility(self::VISIBILITY_PROTECTED); } if ($reflection->isPrivate()) { - $this->setVisibility('private'); - } - } - - protected function _setDocBlockFromReflection(\ReflectionProperty $reflection) { - $docBlock = $reflection->getDocComment(); - if ($docBlock) { - $docBlock = preg_replace('/([\n\r])(' . self::$_indentation . ')+/', '$1', $docBlock); - $this->setDocBlock($docBlock); + $this->setVisibility(self::VISIBILITY_PRIVATE); } } /** * @param \ReflectionProperty $reflection */ - protected function _setDefaultValueFromReflection(\ReflectionProperty $reflection) { + protected function setDefaultValueFromReflection(\ReflectionProperty $reflection) + { $defaultProperties = $reflection->getDeclaringClass()->getDefaultProperties(); $value = $defaultProperties[$this->getName()]; if (null !== $value) { @@ -126,13 +101,42 @@ protected function _setDefaultValueFromReflection(\ReflectionProperty $reflectio } /** - * @param \ReflectionProperty $reflection - * @return PropertyBlock + * @return string */ - public static function buildFromReflection(\ReflectionProperty $reflection) { - $property = new self($reflection->getName()); - $property->extractFromReflection($reflection); - // $property->setDefaultValue($reflection->getValue()); - return $property; + public function getName() + { + return $this->name; + } + + /** + * @param mixed $value + */ + public function setDefaultValue($value) + { + $this->defaultValue = $value; + } + + protected function dumpContent() + { + return $this->dumpLine( + $this->_dumpValue() + ); + } + + /** + * @return string + */ + protected function _dumpValue() + { + $content = $this->visibility; + $content .= ($this->static) ? ' ' . self::KEYWORD_STATIC : ''; + $content .= ' $' . $this->name; + if (null !== $this->defaultValue) { + $value = new ValueBlock($this->defaultValue); + $content .= ' = ' . $value->dump(); + } + $content .= ';'; + + return $content; } } diff --git a/source/CodeGenerator/TraitBlock.php b/source/CodeGenerator/TraitBlock.php new file mode 100644 index 0000000..2180460 --- /dev/null +++ b/source/CodeGenerator/TraitBlock.php @@ -0,0 +1,149 @@ +name = (string)$name; + } + + /** + * @param \ReflectionClass $reflection + * + * @return TraitBlock + */ + public static function buildFromReflection(\ReflectionClass $reflection) + { + $class = new self($reflection->getShortName()); + $class->setNamespace($reflection->getNamespaceName()); + + foreach ($reflection->getMethods() as $reflectionMethod) { + if ($reflectionMethod->getDeclaringClass() == $reflection) { + $method = MethodBlock::buildFromReflection($reflectionMethod); + $class->addMethod($method); + } + } + + foreach ($reflection->getProperties() as $reflectionProperty) { + if ($reflectionProperty->getDeclaringClass() == $reflection) { + $property = PropertyBlock::buildFromReflection($reflectionProperty); + $class->addProperty($property); + } + } + + return $class; + } + + /** + * @param string $namespace + */ + public function setNamespace($namespace) + { + $this->namespace = (string)$namespace; + } + + /** + * @param MethodBlock $method + */ + public function addMethod(MethodBlock $method) + { + $this->methods[$method->getName()] = $method; + } + + /** + * @param PropertyBlock $property + */ + public function addProperty(PropertyBlock $property) + { + $this->properties[$property->getName()] = $property; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function addUse($name) + { + $this->uses[] = $name; + } + + /** + * @return string + */ + protected function dumpContent() + { + $lines = []; + $lines[] = $this->dumpHeader(); + foreach ($this->uses as $use) { + $lines[] = $this->indent("use ${use};"); + $lines[] = ''; + } + foreach ($this->properties as $property) { + $lines[] = $this->indent($property->dump()); + $lines[] = ''; + } + foreach ($this->methods as $method) { + $lines[] = $this->indent($method->dump()); + $lines[] = ''; + } + if (!empty($this->uses) || !empty($this->properties) || !empty($this->methods)) { + array_pop($lines); + } + $lines[] = $this->dumpFooter(); + + return $this->dumpLines($lines); + } + + /** + * @return string + */ + private function dumpHeader() + { + $lines = []; + if ($this->namespace) { + $lines[] = 'namespace ' . $this->namespace . ';'; + $lines[] = ''; + } + $classDeclaration = 'trait ' . $this->name; + + $lines[] = $classDeclaration; + $lines[] = '{'; + + return $this->dumpLines($lines); + } + + /** + * @return string + */ + private function dumpFooter() + { + return '}'; + } +} diff --git a/source/CodeGenerator/ValueBlock.php b/source/CodeGenerator/ValueBlock.php index b41233a..72c29c0 100644 --- a/source/CodeGenerator/ValueBlock.php +++ b/source/CodeGenerator/ValueBlock.php @@ -2,26 +2,30 @@ namespace CodeGenerator; -class ValueBlock extends Block { - +class ValueBlock extends Block +{ /** @var mixed */ - private $_value; + private $value; /** * @param mixed $value */ - public function __construct($value) { - $this->_value = $value; + public function __construct($value) + { + $this->value = $value; } /** * @return string */ - public function dump() { - if (is_array($this->_value)) { - $array = new ArrayBlock($this->_value); + protected function dumpContent() + { + if (is_array($this->value)) { + $array = new ArrayBlock($this->value); + return $array->dump(); } - return var_export($this->_value, true); + + return var_export($this->value, true); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ff79b74..719bbeb 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,6 +1,6 @@ addPsr4('CodeGeneratorMocks\\', __DIR__ . '/mocks'); -$autoLoader->addPsr4('CodeGeneratorHelpers\\', __DIR__ . '/helpers'); +$autoLoader = require __DIR__.'/../vendor/autoload.php'; +$autoLoader->addPsr4('CodeGeneratormocks\\', __DIR__.'/mocks'); +$autoLoader->addPsr4('CodeGeneratorHelpers\\', __DIR__.'/helpers'); diff --git a/tests/helpers/TestHelper.php b/tests/helpers/TestHelper.php index d9e81bf..939f1b4 100644 --- a/tests/helpers/TestHelper.php +++ b/tests/helpers/TestHelper.php @@ -1,21 +1,24 @@ setAccessible(true); - /** - * @param Block $object - * @param string $methodName - * @param array|null $arguments - * @return mixed - */ - public static function invokeMethod(Block $object, $methodName, array $arguments = null) { - $arguments = (array) $arguments; - $reflection = new \ReflectionMethod(get_class($object), $methodName); - $reflection->setAccessible(true); - return $reflection->invokeArgs($object, $arguments); - } + return $reflection->invokeArgs($object, $arguments); + } } diff --git a/tests/mocks/MockAbstractClass.php b/tests/mocks/MockAbstractClass.php index 8733d91..0ff113a 100644 --- a/tests/mocks/MockAbstractClass.php +++ b/tests/mocks/MockAbstractClass.php @@ -1,8 +1,8 @@ foo); } - public function withTypeHinting(\Countable $countable, array $array, callable $callable) { + public function withTypeHinting(\Countable $countable, array $array, callable $callable) + { } - public function defaultValues($defaultValue = null, $defaultArray = array()) { + public function defaultValues($defaultValue = null, $defaultArray = []) + { } - public function withReferenceParam(&$param) { + public function withReferenceParam(&$param) + { } - protected function abstractMethod() { + protected function abstractMethod() + { } - private function _foo() { + private function _foo() + { // comment // indentation // back } - - public static function staticMethod() { - } } diff --git a/tests/mocks/MockDocBlock.php b/tests/mocks/MockDocBlock.php new file mode 100644 index 0000000..ba19cf0 --- /dev/null +++ b/tests/mocks/MockDocBlock.php @@ -0,0 +1,6 @@ +/** + * Class MockDocBlock + * + * @package CodeGeneratormocks + * @author Christian Burgas + */ \ No newline at end of file diff --git a/tests/mocks/MockInterface.php b/tests/mocks/MockInterface.php new file mode 100644 index 0000000..6464153 --- /dev/null +++ b/tests/mocks/MockInterface.php @@ -0,0 +1,12 @@ +foo); + } + + public function withTypeHinting(\Countable $countable, array $array, callable $callable) + { + echo 1; + } + + public function defaultValues($defaultValue = null, $defaultArray = []) + { + echo 2; + } + + public function withReferenceParam(&$param) + { + } + + protected function abstractMethod() + { + } + + private function _foo() + { + // comment + // indentation + // back + } +} diff --git a/tests/mocks/MockTraitTwo.php b/tests/mocks/MockTraitTwo.php new file mode 100644 index 0000000..9590ba3 --- /dev/null +++ b/tests/mocks/MockTraitTwo.php @@ -0,0 +1,17 @@ +assertNotRegExp("/\n/", $array->dump()); $this->_assertSame($value, $array); } - public function testDumpLong() { - $value = array_fill(0, 100, 'foo'); - $array = new ArrayBlock($value); - $this->assertRegExp("/\n /", $array->dump()); - $this->assertCount(count($value) + 2, explode("\n", $array->dump())); - $this->_assertSame($value, $array); - } - /** - * @param array $expected + * @param array $expected * @param ArrayBlock $actual */ - private function _assertSame(array $expected, ArrayBlock $actual) { - $code = 'return ' . $actual->dump() . ';'; + private function _assertSame(array $expected, ArrayBlock $actual) + { + $code = 'return '.$actual->dump().';'; $evaluatedActual = eval($code); $this->assertSame($expected, $evaluatedActual); } + + public function testDumpLong() + { + $value = array_fill(0, 100, 'foo'); + $array = new ArrayBlock($value); + $this->assertRegExp("/\n /", $array->dump()); + $this->assertCount(count($value) + 2, explode("\n", $array->dump())); + $this->_assertSame($value, $array); + } } diff --git a/tests/source/CodeGenearator/BlockTest.php b/tests/source/CodeGenearator/BlockTest.php index 9715559..7e55bbe 100644 --- a/tests/source/CodeGenearator/BlockTest.php +++ b/tests/source/CodeGenearator/BlockTest.php @@ -6,54 +6,58 @@ use CodeGenerator\FileBlock; use CodeGeneratorHelpers\TestHelper; -class CG_BlockTest extends \PHPUnit_Framework_TestCase { - - public function testOutdent() { +class BlockTest extends \PHPUnit_Framework_TestCase +{ + public function testOutdent() + { $block = new FileBlock(); - $cases = array( - " foo" => "foo", - "foo" => "foo", + $cases = [ + ' foo' => 'foo', + 'foo' => 'foo', " foo\nbar" => "foo\nbar", - " foo" => " foo", - ); + ' foo' => ' foo', + ]; foreach ($cases as $input => $expected) { - $output = TestHelper::invokeMethod($block, '_outdent', array($input)); + $output = TestHelper::invokeMethod($block, 'outdent', [$input]); $this->assertSame($expected, $output); } } - public function testOutdentUntilSafe() { + public function testOutdentUntilSafe() + { $block = new FileBlock(); - $cases = array( - " foo\nbar" => " foo\nbar", + $cases = [ + " foo\nbar" => " foo\nbar", " foo\n bar" => " foo\nbar", - " foo" => "foo", - ); + ' foo' => 'foo', + ]; foreach ($cases as $input => $expected) { - $output = TestHelper::invokeMethod($block, '_outdent', array($input, true)); + $output = TestHelper::invokeMethod($block, 'outdent', [$input, true]); $this->assertSame($expected, $output); } } - public function testIndent() { + public function testIndent() + { $block = new FileBlock(); - $cases = array( - "foo\nbar" => " foo\n bar", + $cases = [ + "foo\nbar" => " foo\n bar", " foo\n bar" => " foo\n bar", - ); + ]; foreach ($cases as $input => $expected) { - $output = TestHelper::invokeMethod($block, '_indent', array($input, true)); + $output = TestHelper::invokeMethod($block, 'indent', [$input, true]); $this->assertSame($expected, $output); } } - public function testSetIndentation() { + public function testSetIndentation() + { Block::setIndentation(' '); $block = new FileBlock(); - $output = TestHelper::invokeMethod($block, '_indent', array("foo", true)); - $this->assertSame(" foo", $output); - $output = TestHelper::invokeMethod($block, '_outdent', array(" foo\n bar", true)); + $output = TestHelper::invokeMethod($block, 'indent', ['foo', true]); + $this->assertSame(' foo', $output); + $output = TestHelper::invokeMethod($block, 'outdent', [" foo\n bar", true]); $this->assertSame("foo\n bar", $output); Block::setIndentation(' '); diff --git a/tests/source/CodeGenearator/ClassTest.php b/tests/source/CodeGenearator/ClassTest.php index c8d9288..0a58069 100644 --- a/tests/source/CodeGenearator/ClassTest.php +++ b/tests/source/CodeGenearator/ClassTest.php @@ -5,10 +5,11 @@ use CodeGenerator\ClassBlock; use CodeGenerator\FileBlock; -class CG_ClassTest extends \PHPUnit_Framework_TestCase { - - public function testDump() { - $classes = array('CodeGeneratorMocks\\MockAbstractClass', 'CodeGeneratorMocks\\MockClass'); +class ClassTest extends \PHPUnit_Framework_TestCase +{ + public function testDump() + { + $classes = ['CodeGeneratormocks\\MockAbstractClass', 'CodeGeneratormocks\\MockClass']; foreach ($classes as $className) { $file = new FileBlock(); @@ -22,9 +23,10 @@ public function testDump() { } } - public function testGetName() { + public function testGetName() + { $className = 'Foo'; - $class = new ClassBlock($className, 'Bar', array('Countable')); + $class = new ClassBlock($className, 'Bar', ['Countable']); $this->assertSame($className, $class->getName()); } } diff --git a/tests/source/CodeGenearator/DocBlockTest.php b/tests/source/CodeGenearator/DocBlockTest.php new file mode 100644 index 0000000..1f7afc0 --- /dev/null +++ b/tests/source/CodeGenearator/DocBlockTest.php @@ -0,0 +1,55 @@ +addText('Class MockDocBlock'); + $block->addTag('package', 'CodeGeneratormocks'); + $block->addTag('author', 'Christian Burgas'); + + $expected = file_get_contents(__DIR__ . '/../../mocks/MockDocBlock.php'); + + self::assertEquals($expected, $block->dump()); + } + + public function testFromMultiLineString() + { + $docBlock = DocBlock::createDocBlockFromCommentString($this->exampleToParseOne); + + self::assertEquals($this->exampleToParseOne, $docBlock->dump()); + } + + public function testFromString() + { + $docBlock = DocBlock::createDocBlockFromCommentString($this->exampleToParseTwo); + + self::assertEquals($this->exampleToParseTwoExpected, $docBlock->dump()); + } + + +} diff --git a/tests/source/CodeGenearator/FunctionTest.php b/tests/source/CodeGenearator/FunctionTest.php index a762d3e..d673a9b 100644 --- a/tests/source/CodeGenearator/FunctionTest.php +++ b/tests/source/CodeGenearator/FunctionTest.php @@ -4,22 +4,24 @@ use CodeGenerator\FunctionBlock; -class CG_FunctionTest extends \PHPUnit_Framework_TestCase { - - public function testExtractFromClosure() { +class FunctionTest extends \PHPUnit_Framework_TestCase +{ + public function testExtractFromClosure() + { $closure = function ($a, $b) { return $a * $b; }; $function = new FunctionBlock($closure); - eval('$multiply = ' . $function->dump() . ';'); - /** @var $multiply \Closure */ + eval('$multiply = '.$function->dump().';'); + /* @var $multiply \Closure */ $this->assertSame(12, $multiply(3, 4)); } - public function testSetCodeString() { + public function testSetCodeString() + { $function = new FunctionBlock('return true;'); - eval('$true = ' . $function->dump() . ';'); - /** @var $true \Closure */ + eval('$true = '.$function->dump().';'); + /* @var $true \Closure */ $this->assertTrue($true()); } } diff --git a/tests/source/CodeGenearator/InterfaceTest.php b/tests/source/CodeGenearator/InterfaceTest.php new file mode 100644 index 0000000..f7f6e7b --- /dev/null +++ b/tests/source/CodeGenearator/InterfaceTest.php @@ -0,0 +1,71 @@ +addBlock($reflectedClass); + + $actual = $file->dump(); + $expected = file_get_contents($reflectionClass->getFileName()); + $this->assertSame($expected, $actual); + } + + public function testDumpSmall() + { + $file = new FileBlock(); + + $reflectionClass = new \ReflectionClass('CodeGeneratormocks\\MockInterfaceTwo'); + $reflectedClass = InterfaceBlock::buildFromReflection($reflectionClass); + $file->addBlock($reflectedClass); + + $actual = $file->dump(); + $expected = file_get_contents($reflectionClass->getFileName()); + $this->assertSame($expected, $actual); + } + + public function testDumpExtract() + { + $expected = <<addBlock($interface); + $interface->setNamespace('CodeGeneratormocks'); + $interface->extractConstantsFromOtherClassOrInterface('CodeGeneratormocks\\MockInterfaceTwo'); + $interface->addConstant(new ConstantBlock('BAR', 'test')); + + $actual = $file->dump(); + $this->assertSame($expected, $actual); + } + + public function testGetName() + { + $className = 'Foo'; + $class = new InterfaceBlock($className, ['Bar']); + $this->assertSame($className, $class->getName()); + } +} diff --git a/tests/source/CodeGenearator/TraitTest.php b/tests/source/CodeGenearator/TraitTest.php new file mode 100644 index 0000000..351cd20 --- /dev/null +++ b/tests/source/CodeGenearator/TraitTest.php @@ -0,0 +1,58 @@ +addBlock($reflectedClass); + + $actual = $file->dump(); + $expected = file_get_contents($reflectionClass->getFileName()); + $this->assertSame($expected, $actual); + } + + public function testGetName() + { + $className = 'Foo'; + $class = new TraitBlock($className); + $this->assertSame($className, $class->getName()); + } + + public function testByHand() + { + $file = new FileBlock(); + + $trait = new TraitBlock('TestTrait'); + $trait->addUse('\\CodeGeneratormocks\\MockCompositeTrait'); + $trait->addMethod(new MethodBlock('testMethod', 'echo 1;')); + + $file->addBlock($trait); + + $expected = <<assertSame($expected, $file->dump()); + } +}