diff --git a/.gitignore b/.gitignore
index a7fc91d6..8c4f6bd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,6 @@
.*.un~
nbproject
tmp/
-
clover.xml
composer.lock
coveralls-upload.json
diff --git a/src/Generator/InterfaceGenerator.php b/src/Generator/InterfaceGenerator.php
new file mode 100644
index 00000000..9d0c82fa
--- /dev/null
+++ b/src/Generator/InterfaceGenerator.php
@@ -0,0 +1,728 @@
+getName());
+ $ig->setSourceContent($ig->getSourceContent());
+ $ig->setSourceDirty(false);
+
+ if ($reflection->getDocComment() != '') {
+ $ig->setDocBlock(DocBlockGenerator::fromReflection($reflection->getDocBlock()));
+ }
+
+ // set the namespace
+ if ($reflection->inNamespace()) {
+ $ig->setNamespaceName($reflection->getNamespaceName());
+ }
+
+ /* @var \Zend\Code\Reflection\ClassReflection[] $parentInterfaces */
+ $parentInterfaces = $reflection->getParentInterfaces();
+ $interfaceNames = array();
+ if ($parentInterfaces) {
+ foreach ($parentInterfaces as $parentInterface) {
+ $interfaceNames[] = $parentInterface->getName();
+ }
+ }
+
+ $ig->setExtendedInterfaces($interfaceNames);
+
+ $constants = array();
+ foreach ($reflection->getConstants() as $name => $value) {
+ $constants[] = array(
+ 'name' => $name,
+ 'value' => $value
+ );
+ }
+
+ $ig->addConstants($constants);
+
+ $methods = array();
+ foreach ($reflection->getMethods() as $reflectionMethod) {
+ $className = ($ig->getNamespaceName()) ? $ig->getNamespaceName() . "\\" . $ig->getName() : $ig->getName();
+ if ($reflectionMethod->getDeclaringInterface()->getName() == $className) {
+ $methods[] = MethodDeclarationGenerator::fromReflection($reflectionMethod);
+ }
+ }
+
+ $ig->addMethods($methods);
+
+ return $ig;
+ }
+
+ /**
+ * Generate from array.
+ *
+ * @configkey name string [required] Class Name
+ * @configkey filegenerator FileGenerator File generator that holds this class
+ * @configkey namespacename string The namespace for this class
+ * @configkey docblock string The docblock information
+ * @configkey flags int Flags, one of InterfaceGenerator::FLAG_ABSTRACT InterfaceGenerator::FLAG_FINAL
+ * @configkey extendedclass string Class which this class is extending
+ * @configkey implementedinterfaces
+ * @configkey properties
+ * @configkey methods
+ *
+ * @throws InvalidArgumentException
+ *
+ * @param array $array
+ *
+ * @return InterfaceGenerator
+ */
+ public static function fromArray(array $array)
+ {
+ if (!isset($array['name'])) {
+ throw new InvalidArgumentException(
+ 'Interface generator requires that a name is provided for this interface'
+ );
+ }
+
+ $cg = new static($array['name']);
+ foreach ($array as $name => $value) {
+ // normalize key
+ switch (strtolower(str_replace(array('.', '-', '_'), '', $name))) {
+ case 'containingfile':
+ $cg->setContainingFileGenerator($value);
+ break;
+ case 'namespacename':
+ $cg->setNamespaceName($value);
+ break;
+ case 'docblock':
+ $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
+ $cg->setDocBlock($docBlock);
+ break;
+ case 'flags':
+ $cg->setFlags($value);
+ break;
+ case 'extendedinterfaces':
+ $cg->setExtendedInterfaces($value);
+ break;
+ case 'constants':
+ $cg->addConstants($value);
+ break;
+ case 'methods':
+ $cg->addMethods($value);
+ break;
+ }
+ }
+
+ return $cg;
+ }
+
+ /**
+ * @param string $name
+ * @param string $namespaceName
+ * @param array|string $flags
+ * @param array $parents
+ * @param array $methods
+ * @param DocBlockGenerator $docBlock
+ */
+ public function __construct(
+ $name = null,
+ $namespaceName = null,
+ $flags = null,
+ $parents = array(),
+ $methods = array(),
+ $docBlock = null
+ ) {
+ if ($name !== null) {
+ $this->setName($name);
+ }
+ if ($namespaceName !== null) {
+ $this->setNamespaceName($namespaceName);
+ }
+ if ($flags !== null) {
+ $this->setFlags($flags);
+ }
+ if (is_array($parents)) {
+ $this->setExtendedInterfaces($parents);
+ }
+ if ($methods !== array()) {
+ $this->addMethods($methods);
+ }
+ if ($docBlock !== null) {
+ $this->setDocBlock($docBlock);
+ }
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return InterfaceGenerator
+ */
+ public function setName($name)
+ {
+ if (strstr($name, '\\')) {
+ $namespace = substr($name, 0, strrpos($name, '\\'));
+ $name = substr($name, strrpos($name, '\\') + 1);
+ $this->setNamespaceName($namespace);
+ }
+
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $namespaceName
+ *
+ * @return InterfaceGenerator
+ */
+ public function setNamespaceName($namespaceName)
+ {
+ $this->namespaceName = $namespaceName;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNamespaceName()
+ {
+ return $this->namespaceName;
+ }
+
+ /**
+ * @param FileGenerator $fileGenerator
+ *
+ * @return InterfaceGenerator
+ */
+ public function setContainingFileGenerator(FileGenerator $fileGenerator)
+ {
+ $this->containingFileGenerator = $fileGenerator;
+
+ return $this;
+ }
+
+ /**
+ * @return FileGenerator
+ */
+ public function getContainingFileGenerator()
+ {
+ if (!is_object($this->containingFileGenerator)) {
+ $this->containingFileGenerator = new FileGenerator();
+ }
+ return $this->containingFileGenerator;
+ }
+
+ /**
+ * @param DocBlockGenerator $docBlock
+ *
+ * @return InterfaceGenerator
+ */
+ public function setDocBlock(DocBlockGenerator $docBlock)
+ {
+ $this->docBlock = $docBlock;
+
+ return $this;
+ }
+
+ /**
+ * @return DocBlockGenerator
+ */
+ public function getDocBlock()
+ {
+ return $this->docBlock;
+ }
+
+ /**
+ * @param array|string $flags
+ *
+ * @return InterfaceGenerator
+ */
+ public function setFlags($flags)
+ {
+ if (is_array($flags)) {
+ $flagsArray = $flags;
+ $flags = 0x00;
+ foreach ($flagsArray as $flag) {
+ $flags |= $flag;
+ }
+ }
+ // check that visibility is one of three
+ $this->flags = $flags;
+
+ return $this;
+ }
+
+ /**
+ * @param string $flag
+ *
+ * @return InterfaceGenerator
+ */
+ public function addFlag($flag)
+ {
+ $this->setFlags($this->flags | $flag);
+
+ return $this;
+ }
+
+ /**
+ * @param string $flag
+ *
+ * @return InterfaceGenerator
+ */
+ public function removeFlag($flag)
+ {
+ $this->setFlags($this->flags & ~$flag);
+
+ return $this;
+ }
+
+ /**
+ * @param array|string $implementedInterfaces
+ *
+ * @return InterfaceGenerator
+ */
+ public function setExtendedInterfaces($implementedInterfaces)
+ {
+ // Ignore empty parameters...
+ if (!empty($implementedInterfaces)) {
+ // Convert to array
+ if (!is_array($implementedInterfaces)) {
+ $implementedInterfaces = array($implementedInterfaces);
+ }
+ $this->extendedInterfaces = $implementedInterfaces;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getExtendedInterfaces()
+ {
+ return $this->extendedInterfaces;
+ }
+
+ /**
+ * @alias setExtendedInterfaces
+ * @param array $implementedInterfaces
+ *
+ * @return InterfaceGenerator
+ */
+ public function setImplementedInterfaces(array $implementedInterfaces)
+ {
+ return $this->setExtendedInterfaces($implementedInterfaces);
+ }
+
+ /**
+ * @alias getExtendedInterfaces
+ *
+ * @return array
+ */
+ public function getImplementedInterfaces()
+ {
+ return $this->getExtendedInterfaces();
+ }
+
+ /**
+ * @param string $constantName
+ *
+ * @return PropertyGenerator|false
+ */
+ public function getConstant($constantName)
+ {
+ if (isset($this->constants[$constantName])) {
+ return $this->constants[$constantName];
+ }
+
+ return false;
+ }
+
+ /**
+ * @return PropertyGenerator[] indexed by constant name
+ */
+ public function getConstants()
+ {
+ return $this->constants;
+ }
+
+ /**
+ * @param string $constantName
+ *
+ * @return bool
+ */
+ public function hasConstant($constantName)
+ {
+ return isset($this->constants[$constantName]);
+ }
+
+ /**
+ * Add constant from PropertyGenerator.
+ *
+ * @param PropertyGenerator $constant
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return InterfaceGenerator
+ */
+ public function addConstantFromGenerator(PropertyGenerator $constant)
+ {
+ $constantName = $constant->getName();
+
+ if (isset($this->constants[$constantName])) {
+ throw new InvalidArgumentException(sprintf(
+ 'A constant by name %s already exists in this class.',
+ $constantName
+ ));
+ }
+
+ if (!$constant->isConst()) {
+ throw new InvalidArgumentException(sprintf(
+ 'The value %s is not defined as a constant.',
+ $constantName
+ ));
+ }
+
+ $this->constants[$constantName] = $constant;
+
+ return $this;
+ }
+
+ /**
+ * Add Constant.
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return InterfaceGenerator
+ */
+ public function addConstant($name, $value)
+ {
+ if (!is_string($name)) {
+ throw new InvalidArgumentException(sprintf(
+ '%s expects string for name',
+ __METHOD__
+ ));
+ }
+
+ if (!is_string($value) && !is_numeric($value)) {
+ throw new InvalidArgumentException(sprintf(
+ '%s expects value for constant, value must be a string or numeric',
+ __METHOD__
+ ));
+ }
+
+ return $this->addConstantFromGenerator(new PropertyGenerator($name, $value, PropertyGenerator::FLAG_CONSTANT));
+ }
+
+ /**
+ * @param PropertyGenerator[]|array[] $constants
+ *
+ * @return InterfaceGenerator
+ */
+ public function addConstants(array $constants)
+ {
+ foreach ($constants as $constant) {
+ if ($constant instanceof PropertyGenerator) {
+ $this->addConstantFromGenerator($constant);
+ } else {
+ if (is_array($constant)) {
+ call_user_func_array(array($this, 'addConstant'), $constant);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add method declaration
+ *
+ * @param array $methods
+ *
+ * @return InterfaceGenerator
+ */
+ public function addMethods(array $methods)
+ {
+ foreach ($methods as $method) {
+ if ($method instanceof MethodDeclarationGenerator) {
+ $this->addMethodFromGenerator($method);
+ } else {
+ if (is_string($method)) {
+ $this->addMethod($method);
+ } elseif (is_array($method)) {
+ call_user_func_array(array($this, 'addMethod'), $method);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add method declaration from scalars.
+ *
+ * @param string $name
+ * @param array $parameters
+ * @param int $flags
+ * @param string $body
+ * @param string $docBlock
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return InterfaceGenerator
+ */
+ public function addMethod(
+ $name = null,
+ array $parameters = array(),
+ $flags = MethodDeclarationGenerator::FLAG_PUBLIC,
+ $body = null,
+ $docBlock = null
+ ) {
+ if (!is_string($name)) {
+ throw new InvalidArgumentException(sprintf(
+ '%s::%s expects string for name',
+ get_class($this),
+ __FUNCTION__
+ ));
+ }
+
+ return $this->addMethodFromGenerator(new MethodDeclarationGenerator($name, $parameters, $flags, $body, $docBlock));
+ }
+
+ /**
+ * Add Method from MethodDeclarationGenerator.
+ *
+ * @param MethodDeclarationGenerator $method
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return InterfaceGenerator
+ */
+ public function addMethodFromGenerator(MethodDeclarationGenerator $method)
+ {
+ $methodName = $method->getName();
+
+ if ($this->hasMethod($methodName)) {
+ throw new InvalidArgumentException(sprintf(
+ 'A method by name %s already exists in this class.',
+ $methodName
+ ));
+ }
+
+ $this->methods[strtolower($methodName)] = $method;
+
+ return $this;
+ }
+
+ /**
+ * @return MethodDeclarationGenerator[]
+ */
+ public function getMethods()
+ {
+ return $this->methods;
+ }
+
+ /**
+ * @param string $methodName
+ *
+ * @return MethodDeclarationGenerator|false
+ */
+ public function getMethod($methodName)
+ {
+ return $this->hasMethod($methodName) ? $this->methods[strtolower($methodName)] : false;
+ }
+
+ /**
+ * @param string $methodName
+ *
+ * @return InterfaceGenerator
+ */
+ public function removeMethod($methodName)
+ {
+ if ($this->hasMethod($methodName)) {
+ unset($this->methods[strtolower($methodName)]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param string $methodName
+ *
+ * @return bool
+ */
+ public function hasMethod($methodName)
+ {
+ return isset($this->methods[strtolower($methodName)]);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isSourceDirty()
+ {
+ if (($docBlock = $this->getDocBlock()) && $docBlock->isSourceDirty()) {
+ return true;
+ }
+
+ foreach ($this->getMethods() as $method) {
+ if ($method->isSourceDirty()) {
+ return true;
+ }
+ }
+
+ return parent::isSourceDirty();
+ }
+
+ /**
+ * @inherit Zend\Code\Generator\GeneratorInterface
+ */
+ public function generate()
+ {
+ if (!$this->isSourceDirty()) {
+ $output = $this->getSourceContent();
+ if (!empty($output)) {
+ return $output;
+ }
+ }
+
+ $output = '';
+
+ if (null !== ($namespace = $this->getNamespaceName())) {
+ $output .= "namespace {$namespace};".self::LINE_FEED.self::LINE_FEED;
+ }
+
+ $uses = $this->getUses();
+ if (!empty($uses)) {
+ foreach ($uses as $use) {
+ $use = (!array($use)) ? array($use) : $use;
+ $useClass = "use {$use[0]}";
+ if (isset($use[1])) {
+ $useClass .= " as {$use[1]}";
+ }
+ if (!empty($useClass)) {
+ $output .= $useClass.';'.self::LINE_FEED;
+ }
+ }
+ $output .= self::LINE_FEED;
+ }
+
+ if (null !== ($docBlock = $this->getDocBlock())) {
+ $docBlock->setIndentation('');
+ $output .= $docBlock->generate();
+ }
+
+ $output .= "interface {$this->getName()}";
+
+ $implemented = $this->getExtendedInterfaces();
+ if (!empty($implemented)) {
+ $output .= ' extends '.implode(', ', $implemented);
+ }
+
+ $output .= self::LINE_FEED.'{'.self::LINE_FEED.self::LINE_FEED;
+
+ $constants = $this->getConstants();
+ foreach ($constants as $constant) {
+ $output .= $constant->generate().self::LINE_FEED.self::LINE_FEED;
+ }
+
+ $methods = $this->getMethods();
+ foreach ($methods as $method) {
+ $output .= $method->generate().self::LINE_FEED;
+ }
+
+ $output .= self::LINE_FEED.'}'.self::LINE_FEED;
+
+ return $output;
+ }
+
+ /**
+ * Add "use" class or interface
+ *
+ * @param string $use
+ * @param string|null $useAlias
+ *
+ * @return InterfaceGenerator
+ */
+ public function addUse($use, $useAlias = null)
+ {
+ $this->getContainingFileGenerator()->setUse($use, $useAlias);
+ }
+
+ /**
+ * Get "uses" of classes and interfaces
+ *
+ * @return array
+ */
+ public function getUses()
+ {
+ return $this->getContainingFileGenerator()->getUses();
+ }
+}
diff --git a/src/Generator/MethodDeclarationGenerator.php b/src/Generator/MethodDeclarationGenerator.php
new file mode 100644
index 00000000..823a7b9a
--- /dev/null
+++ b/src/Generator/MethodDeclarationGenerator.php
@@ -0,0 +1,269 @@
+isPublic()) {
+ throw new InvalidArgumentException('Interfaces can only contain public methods!');
+ }
+
+ $method->setSourceDirty(false);
+ if ($reflectionMethod->getDocComment() != '') {
+ $method->setDocBlock(DocBlockGenerator::fromReflection($reflectionMethod->getDocBlock()));
+ }
+
+ $method->setStatic($reflectionMethod->isStatic());
+ $method->setName($reflectionMethod->getName());
+
+ foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
+ $method->setParameter(ParameterGenerator::fromReflection($reflectionParameter));
+ }
+
+ return $method;
+ }
+
+ /**
+ * Generate from array.
+ *
+ * @configkey name string [required] Class Name
+ * @configkey docblock string The docblock information
+ * @configkey flags int Flags, one of MethodGenerator::FLAG_ABSTRACT MethodGenerator::FLAG_FINAL
+ * @configkey parameters string Class which this class is extending
+ * @configkey body string
+ * @configkey abstract bool
+ * @configkey final bool
+ * @configkey static bool
+ * @configkey visibility string
+ *
+ * @throws InvalidArgumentException
+ *
+ * @param array $array
+ *
+ * @return MethodDeclarationGenerator
+ */
+ public static function fromArray(array $array)
+ {
+ if (!isset($array['name'])) {
+ throw new InvalidArgumentException(
+ 'Method generator requires that a name is provided for this object'
+ );
+ }
+
+ $method = new static($array['name']);
+ $method->setVisibility(self::VISIBILITY_PUBLIC);
+ foreach ($array as $name => $value) {
+ // normalize key
+ switch (strtolower(str_replace(array('.', '-', '_'), '', $name))) {
+ case 'docblock':
+ $docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
+ $method->setDocBlock($docBlock);
+ break;
+ case 'flags':
+ $method->setFlags($value);
+ break;
+ case 'parameters':
+ $method->setParameters($value);
+ break;
+ case 'static':
+ $method->setStatic($value);
+ break;
+ }
+ }
+
+ return $method;
+ }
+
+ /**
+ * @param string $name
+ * @param array $parameters
+ * @param int $flags
+ * @param string $body
+ * @param DocBlockGenerator|string $docBlock
+ */
+ public function __construct(
+ $name = null,
+ array $parameters = array(),
+ $flags = self::FLAG_PUBLIC,
+ $body = null,
+ $docBlock = null
+ ) {
+ if ($name) {
+ $this->setName($name);
+ }
+ if ($parameters) {
+ $this->setParameters($parameters);
+ }
+ if ($flags !== self::FLAG_PUBLIC) {
+ $this->setFlags($flags);
+ }
+ if ($docBlock) {
+ $this->setDocBlock($docBlock);
+ }
+ }
+
+ /**
+ * Export pre configured method generator.
+ *
+ * @return MethodGenerator
+ */
+ public function getMethodImplementation()
+ {
+ $generator = new MethodGenerator($this->getName());
+ $generator->setBody('// TODO: Implement logic');
+ $generator->setParameters($this->getParameters());
+ $generator->setIndentation($this->getIndentation());
+ $generator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC);
+ $generator->setStatic($this->isStatic());
+ $generator->setFinal($this->isFinal());
+ $generator->setDocBlock($this->getDocBlock());
+
+ return $generator;
+ }
+
+ /**
+ * @param array $parameters
+ *
+ * @return $this
+ */
+ public function setParameters(array $parameters)
+ {
+ foreach ($parameters as $parameter) {
+ $this->setParameter($parameter);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param ParameterGenerator|array|string $parameter
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return $this
+ */
+ public function setParameter($parameter)
+ {
+ if (is_string($parameter)) {
+ $parameter = new ParameterGenerator($parameter);
+ }
+
+ if (is_array($parameter)) {
+ $parameter = ParameterGenerator::fromArray($parameter);
+ }
+
+ if (!$parameter instanceof ParameterGenerator) {
+ throw new InvalidArgumentException(sprintf(
+ '%s is expecting either a string, array or an instance of %s\ParameterGenerator',
+ __METHOD__,
+ __NAMESPACE__
+ ));
+ }
+
+ $this->parameters[$parameter->getName()] = $parameter;
+
+ return $this;
+ }
+
+ /**
+ * @return ParameterGenerator[]
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '';
+
+ $indent = $this->getIndentation();
+
+ if (($docBlock = $this->getDocBlock()) !== null) {
+ $docBlock->setIndentation($indent);
+ $output .= $docBlock->generate();
+ }
+
+ $output .= $indent;
+
+ $output .= self::VISIBILITY_PUBLIC
+ .(($this->isStatic()) ? ' static' : '')
+ .' function '.$this->getName().'(';
+
+ $parameters = $this->getParameters();
+ $parameterOutput = array();
+ if (!empty($parameters)) {
+ foreach ($parameters as $parameter) {
+ $parameterOutput[] = $parameter->generate();
+ }
+
+ $output .= implode(', ', $parameterOutput);
+ }
+ $output .= ');';
+
+ return $output;
+ }
+
+ /**
+ * @param string $visibility
+ * @return AbstractMemberGenerator
+ */
+ public function setVisibility($visibility)
+ {
+ if ($visibility !== self::VISIBILITY_PUBLIC) {
+ throw new RuntimeException("Method declarations for interfaces must be public.");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Method declarations for interfaces are always public.
+ *
+ * @return string
+ */
+ public function getVisibility()
+ {
+ return self::VISIBILITY_PUBLIC;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->generate();
+ }
+}
diff --git a/src/Generator/MethodGenerator.php b/src/Generator/MethodGenerator.php
index 1f405dbc..8a5909a4 100644
--- a/src/Generator/MethodGenerator.php
+++ b/src/Generator/MethodGenerator.php
@@ -280,6 +280,7 @@ public function generate()
. ' function ' . $this->getName() . '(';
$parameters = $this->getParameters();
+ $parameterOutput = array();
if (!empty($parameters)) {
foreach ($parameters as $parameter) {
$parameterOutput[] = $parameter->generate();
diff --git a/src/Reflection/InterfaceReflection.php b/src/Reflection/InterfaceReflection.php
new file mode 100644
index 00000000..65876a8b
--- /dev/null
+++ b/src/Reflection/InterfaceReflection.php
@@ -0,0 +1,221 @@
+getFileName());
+
+ return $instance;
+ }
+
+ /**
+ * Return the classes DocBlock reflection object.
+ *
+ * @return DocBlockReflection
+ *
+ * @throws ExceptionInterface for missing DocBock or invalid reflection class
+ */
+ public function getDocBlock()
+ {
+ if (isset($this->docBlock)) {
+ return $this->docBlock;
+ }
+
+ if ('' == $this->getDocComment()) {
+ return false;
+ }
+
+ $this->docBlock = new DocBlockReflection($this);
+
+ return $this->docBlock;
+ }
+
+ /**
+ * @param AnnotationManager $annotationManager
+ *
+ * @return AnnotationCollection
+ */
+ public function getAnnotations(AnnotationManager $annotationManager)
+ {
+ $docComment = $this->getDocComment();
+
+ if ($docComment == '') {
+ return false;
+ }
+
+ if ($this->annotations) {
+ return $this->annotations;
+ }
+
+ $fileScanner = $this->createFileScanner($this->getFileName());
+ $nameInformation = $fileScanner->getClassNameInformation($this->getName());
+
+ if (!$nameInformation) {
+ return false;
+ }
+
+ $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
+
+ return $this->annotations;
+ }
+
+ /**
+ * Return the start line of the class.
+ *
+ * @param bool $includeDocComment
+ *
+ * @return int
+ */
+ public function getStartLine($includeDocComment = false)
+ {
+ if ($includeDocComment && $this->getDocComment() != '') {
+ return $this->getDocBlock()->getStartLine();
+ }
+
+ return parent::getStartLine();
+ }
+
+ /**
+ * Return the contents of the class.
+ *
+ * @param bool $includeDocBlock
+ *
+ * @return string
+ */
+ public function getContents($includeDocBlock = true)
+ {
+ $fileName = $this->getFileName();
+
+ if (false === $fileName || !file_exists($fileName)) {
+ return '';
+ }
+
+ $filelines = file($fileName);
+ $startnum = $this->getStartLine($includeDocBlock);
+ $endnum = $this->getEndLine();
+
+ // Ensure we get between the open and close braces
+ $lines = array_slice($filelines, $startnum, $endnum);
+ array_unshift($lines, $filelines[$startnum - 1]);
+
+ return strstr(implode('', $lines), '{');
+ }
+
+ /**
+ * Get all reflection objects of parent interfaces.
+ *
+ * @return InterfaceReflection[]
+ */
+ public function getParentInterfaces()
+ {
+ if (parent::isInterface()) {
+ $phpReflections = parent::getInterfaces();
+ $zendReflections = array();
+ while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
+ $instance = new self($phpReflection->getName());
+ $zendReflections[] = $instance;
+ unset($phpReflection);
+ }
+ unset($phpReflections);
+
+ return $zendReflections;
+ }
+
+ return array();
+ }
+
+ /**
+ * Return method reflection by name.
+ *
+ * @param string $name
+ *
+ * @return MethodDeclarationReflection
+ */
+ public function getMethod($name)
+ {
+ $method = new MethodDeclarationReflection($this->getName(), parent::getMethod($name)->getName());
+
+ return $method;
+ }
+
+ /**
+ * Get reflection objects of all methods.
+ *
+ * @param int $filter
+ *
+ * @return MethodDeclarationReflection[]
+ */
+ public function getMethods($filter = -1)
+ {
+ $methods = array();
+ foreach (parent::getMethods($filter) as $method) {
+ $instance = new MethodDeclarationReflection($this->getName(), $method->getName());
+ $methods[] = $instance;
+ }
+
+ return $methods;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return parent::__toString();
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return parent::__toString();
+ }
+
+ /**
+ * Creates a new FileScanner instance.
+ *
+ * By having this as a seperate method it allows the method to be overridden
+ * if a different FileScanner is needed.
+ *
+ * @param string $filename
+ *
+ * @return FileScanner
+ */
+ protected function createFileScanner($filename)
+ {
+ return new FileScanner($filename);
+ }
+}
diff --git a/src/Reflection/MethodDeclarationReflection.php b/src/Reflection/MethodDeclarationReflection.php
new file mode 100644
index 00000000..ca1fa788
--- /dev/null
+++ b/src/Reflection/MethodDeclarationReflection.php
@@ -0,0 +1,455 @@
+getDocComment()) {
+ return false;
+ }
+
+ $instance = new DocBlockReflection($this);
+
+ return $instance;
+ }
+
+ /**
+ * @param AnnotationManager $annotationManager
+ *
+ * @return AnnotationScanner
+ */
+ public function getAnnotations(AnnotationManager $annotationManager)
+ {
+ if (($docComment = $this->getDocComment()) == '') {
+ return false;
+ }
+
+ if ($this->annotations) {
+ return $this->annotations;
+ }
+
+ $cachingFileScanner = $this->createFileScanner($this->getFileName());
+ $nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringInterface()->getName());
+
+ if (!$nameInformation) {
+ return false;
+ }
+
+ $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
+
+ return $this->annotations;
+ }
+
+ /**
+ * Get start line (position) of method.
+ *
+ * @param bool $includeDocComment
+ *
+ * @return int
+ */
+ public function getStartLine($includeDocComment = false)
+ {
+ if ($includeDocComment) {
+ if ($this->getDocComment() != '') {
+ return $this->getDocBlock()->getStartLine();
+ }
+ }
+
+ return parent::getStartLine();
+ }
+
+ /**
+ * Get reflection of declaring class.
+ *
+ * @return InterfaceReflection
+ */
+ public function getDeclaringInterface()
+ {
+ $phpReflection = parent::getDeclaringClass();
+ $zendReflection = new InterfaceReflection($phpReflection->getName());
+ unset($phpReflection);
+
+ return $zendReflection;
+ }
+
+ /**
+ * (PHP 5)
+ * Gets declaring class for the reflected method.
+ * @link http://php.net/manual/en/reflectionmethod.getdeclaringclass.php
+ * @return \ReflectionClass A ReflectionClass object of the class that the
+ * reflected method is part of.
+ */
+ final public function getDeclaringClass()
+ {
+ throw new \LogicException("Method not supported for method declarations. Use getDeclaringInterface() instead.");
+ }
+
+ /**
+ * Get method prototype.
+ *
+ * @return array
+ */
+ public function getPrototype($format = self::PROTOTYPE_AS_ARRAY)
+ {
+ $returnType = 'mixed';
+ $docBlock = $this->getDocBlock();
+ if ($docBlock) {
+ /** @var ReturnTag $return */
+ $return = $docBlock->getTag('return');
+ $returnTypes = $return->getTypes();
+ $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0];
+ }
+
+ $declaringClass = $this->getDeclaringInterface();
+ $prototype = array(
+ 'namespace' => $declaringClass->getNamespaceName(),
+ 'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1),
+ 'name' => $this->getName(),
+ 'visibility' => ($this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected')),
+ 'return' => $returnType,
+ 'arguments' => array(),
+ );
+
+ $parameters = $this->getParameters();
+ foreach ($parameters as $parameter) {
+ $prototype['arguments'][$parameter->getName()] = array(
+ 'type' => $parameter->getType(),
+ 'required' => !$parameter->isOptional(),
+ 'by_ref' => $parameter->isPassedByReference(),
+ 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
+ );
+ }
+
+ if ($format == static::PROTOTYPE_AS_STRING) {
+ $line = $prototype['visibility'].' '.$prototype['return'].' '.$prototype['name'].'(';
+ $args = array();
+ foreach ($prototype['arguments'] as $name => $argument) {
+ $argsLine = ($argument['type'] ? $argument['type'].' ' : '').($argument['by_ref'] ? '&' : '').'$'.$name;
+ if (!$argument['required']) {
+ $argsLine .= ' = '.var_export($argument['default'], true);
+ }
+ $args[] = $argsLine;
+ }
+ $line .= implode(', ', $args);
+ $line .= ');';
+
+ return $line;
+ }
+
+ return $prototype;
+ }
+
+ /**
+ * Get all method parameter reflection objects.
+ *
+ * @return ParameterReflection[]
+ */
+ public function getParameters()
+ {
+ $phpReflections = parent::getParameters();
+ $zendReflections = array();
+ while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
+ $instance = new ParameterReflection(
+ array($this->getDeclaringInterface()->getName(), $this->getName()),
+ $phpReflection->getName()
+ );
+ $zendReflections[] = $instance;
+ unset($phpReflection);
+ }
+ unset($phpReflections);
+
+ return $zendReflections;
+ }
+
+ /**
+ * Get method contents.
+ *
+ * @param bool $includeDocBlock
+ *
+ * @return string
+ */
+ public function getContents($includeDocBlock = true)
+ {
+ $docComment = $this->getDocComment();
+ $content = ($includeDocBlock && !empty($docComment)) ? $docComment."\n" : '';
+ $content .= $this->extractMethodContents();
+
+ return $content;
+ }
+
+ /**
+ * Get method body.
+ *
+ * @return string
+ */
+ public function getBody()
+ {
+ return;
+ }
+
+ /**
+ * Tokenize method string and return concatenated body.
+ *
+ * @return string
+ */
+ protected function extractMethodContents()
+ {
+ $fileName = $this->getFileName();
+
+ if ((class_exists($this->class) && false === $fileName) || !file_exists($fileName)) {
+ return '';
+ }
+
+ $lines = array_slice(
+ file($fileName, FILE_IGNORE_NEW_LINES),
+ $this->getStartLine() - 1,
+ ($this->getEndLine() - ($this->getStartLine() - 1)),
+ true
+ );
+
+ $functionLine = implode("\n", $lines);
+ $tokens = token_get_all(' $token) {
+ $tokenType = (is_array($token)) ? token_name($token[0]) : $token;
+ $tokenValue = (is_array($token)) ? $token[1] : $token;
+
+ switch ($tokenType) {
+ case 'T_FINAL':
+ case 'T_ABSTRACT':
+ case 'T_PUBLIC':
+ case 'T_PROTECTED':
+ case 'T_PRIVATE':
+ case 'T_STATIC':
+ case 'T_FUNCTION':
+ // check to see if we have a valid function
+ // then check if we are inside function and have a closure
+ if ($this->isValidFunction($tokens, $key, $this->getName())) {
+ // if first instance of tokenType grab prefixed whitespace and append to body
+ if ($capture === false) {
+ $body .= $this->extractPrefixedWhitespace($tokens, $key);
+ }
+ $body .= $tokenValue;
+ $capture = true;
+ }
+ break;
+ }
+ }
+
+ //remove ending whitespace and return
+ return rtrim($body).';';
+ }
+
+ /**
+ * Take current position and find any whitespace.
+ *
+ * @param array $haystack
+ * @param int $position
+ *
+ * @return string
+ */
+ protected function extractPrefixedWhitespace($haystack, $position)
+ {
+ $content = '';
+ $count = count($haystack);
+ if ($position + 1 == $count) {
+ return $content;
+ }
+
+ for ($i = $position - 1;$i >= 0;$i--) {
+ $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
+ $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i];
+
+ //search only for whitespace
+ if ($tokenType == 'T_WHITESPACE') {
+ $content .= $tokenValue;
+ } else {
+ break;
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Test for ending brace.
+ *
+ * @param array $haystack
+ * @param int $position
+ *
+ * @return bool
+ */
+ protected function isEndingBrace($haystack, $position)
+ {
+ $count = count($haystack);
+
+ //advance one position
+ $position = $position + 1;
+
+ if ($position == $count) {
+ return true;
+ }
+
+ for ($i = $position;$i < $count; $i++) {
+ $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
+ switch ($tokenType) {
+ case 'T_FINAL':
+ case 'T_ABSTRACT':
+ case 'T_PUBLIC':
+ case 'T_PROTECTED':
+ case 'T_PRIVATE':
+ case 'T_STATIC':
+ return true;
+
+ case 'T_FUNCTION':
+ // If a function is encountered and that function is not a closure
+ // then return true. otherwise the function is a closure, return false
+ if ($this->isValidFunction($haystack, $i)) {
+ return true;
+ }
+
+ return false;
+
+ case '}':
+ case ';';
+ case 'T_BREAK':
+ case 'T_CATCH':
+ case 'T_DO':
+ case 'T_ECHO':
+ case 'T_ELSE':
+ case 'T_ELSEIF':
+ case 'T_EVAL':
+ case 'T_EXIT':
+ case 'T_FINALLY':
+ case 'T_FOR':
+ case 'T_FOREACH':
+ case 'T_GOTO':
+ case 'T_IF':
+ case 'T_INCLUDE':
+ case 'T_INCLUDE_ONCE':
+ case 'T_PRINT':
+ case 'T_STRING':
+ case 'T_STRING_VARNAME':
+ case 'T_THROW':
+ case 'T_USE':
+ case 'T_VARIABLE':
+ case 'T_WHILE':
+ case 'T_YIELD':
+
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Test to see if current position is valid function or
+ * closure. Returns true if it's a function and NOT a closure.
+ *
+ * @param array $haystack
+ * @param int $position
+ * @param string $functionName
+ *
+ * @return bool
+ */
+ protected function isValidFunction($haystack, $position, $functionName = null)
+ {
+ $isValid = false;
+ $count = count($haystack);
+ for ($i = $position + 1; $i < $count; $i++) {
+ $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
+ $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i];
+
+ //check for occurance of ( or
+ if ($tokenType == 'T_STRING') {
+ //check to see if function name is passed, if so isValidDataSource against that
+ if ($functionName !== null && $tokenValue != $functionName) {
+ $isValid = false;
+ break;
+ }
+
+ $isValid = true;
+ break;
+ } elseif ($tokenValue == '(') {
+ break;
+ }
+ }
+
+ return $isValid;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return parent::__toString();
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return parent::__toString();
+ }
+
+ /**
+ * Creates a new FileScanner instance.
+ *
+ * By having this as a seperate method it allows the method to be overridden
+ * if a different FileScanner is needed.
+ *
+ * @param string $filename
+ *
+ * @return CachingFileScanner
+ */
+ protected function createFileScanner($filename)
+ {
+ return new CachingFileScanner($filename);
+ }
+}
diff --git a/test/Generator/InterfaceGeneratorTest.php b/test/Generator/InterfaceGeneratorTest.php
new file mode 100644
index 00000000..f6582bb4
--- /dev/null
+++ b/test/Generator/InterfaceGeneratorTest.php
@@ -0,0 +1,453 @@
+setName('FooInterface');
+ $this->assertEquals($interfaceGenerator->getName(), 'FooInterface');
+ }
+
+ public function testClassDocBlockAccessors()
+ {
+ $docBlockGenerator = new DocBlockGenerator();
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setDocBlock($docBlockGenerator);
+ $this->assertSame($docBlockGenerator, $interfaceGenerator->getDocBlock());
+ }
+
+ public function testExtendedInterfacesAccessors()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setExtendedInterfaces(array('FooInterface', 'BarInterface'));
+ $this->assertEquals($interfaceGenerator->getExtendedInterfaces(), array('FooInterface', 'BarInterface'));
+ }
+
+ public function testMethodAccessors()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethods(array(
+ 'doSomething',
+ new MethodDeclarationGenerator('doSomethingElse')
+ ));
+
+ $methods = $interfaceGenerator->getMethods();
+ $this->assertEquals(count($methods), 2);
+ $this->isInstanceOf(current($methods), '\Zend\Code\Generator\PhpMethod');
+
+ $method = $interfaceGenerator->getMethod('doSomething');
+ $this->isInstanceOf($method, '\Zend\Code\Generator\PhpMethod');
+ $this->assertEquals($method->getName(), 'doSomething');
+
+ // add a new property
+ $interfaceGenerator->addMethod('pretendYouAreATrain');
+ $this->assertEquals(count($interfaceGenerator->getMethods()), 3);
+ }
+
+ public function testSetMethodNoMethodOrArrayThrowsException()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $this->setExpectedException(
+ 'Zend\Code\Generator\Exception\ExceptionInterface',
+ 'Zend\Code\Generator\InterfaceGenerator::addMethod expects string for name'
+ );
+
+ $interfaceGenerator->addMethod(true);
+ }
+
+ public function testSetMethodNameAlreadyExistsThrowsException()
+ {
+ $methodA = new MethodDeclarationGenerator();
+ $methodA->setName("foo");
+ $methodB = new MethodDeclarationGenerator();
+ $methodB->setName("foo");
+
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethodFromGenerator($methodA);
+
+ $this->setExpectedException(
+ 'Zend\Code\Generator\Exception\InvalidArgumentException',
+ 'A method by name foo already exists in this class.'
+ );
+
+ $interfaceGenerator->addMethodFromGenerator($methodB);
+ }
+
+ /**
+ * @group ZF-7361
+ */
+ public function testHasMethod()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethod('methodOne');
+
+ $this->assertTrue($interfaceGenerator->hasMethod('methodOne'));
+ }
+
+ public function testRemoveMethod()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethod('methodOne');
+ $this->assertTrue($interfaceGenerator->hasMethod('methodOne'));
+
+ $interfaceGenerator->removeMethod('methodOne');
+ $this->assertFalse($interfaceGenerator->hasMethod('methodOne'));
+ }
+
+
+ public function testToString()
+ {
+ $interfaceGenerator = InterfaceGenerator::fromArray(array(
+ 'name' => 'SampleInterface',
+ 'extendedInterfaces' => array('FooInterface', 'BarInterface'),
+ 'constants' => array(
+ array('FOO', 0),
+ array('name' => 'BAR', 'value' => 1)
+ ),
+ 'methods' => array(
+ array('name' => 'baz')
+ ),
+ ));
+
+ $expectedOutput = <<generate();
+ $this->assertEquals($expectedOutput, $output, $output);
+ }
+
+ /**
+ * @group 4988
+ */
+ public function testNonNamespaceInterfaceReturnsAllMethods()
+ {
+ require_once __DIR__ . '/../TestAsset/NonNamespaceInterface.php';
+
+ $refl = new InterfaceReflection('NonNamespaceInterface');
+ $interfaceGenerator = InterfaceGenerator::fromReflection($refl);
+ $this->assertCount(1, $interfaceGenerator->getMethods());
+ }
+
+ /**
+ * @group ZF-9602
+ */
+ public function testSetextendedclassShouldIgnoreEmptyClassnameOnGenerate()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator
+ ->setName('MyInterface')
+ ->setExtendedInterfaces('');
+
+ $expected = <<assertEquals($expected, $interfaceGenerator->generate());
+ }
+
+ /**
+ * @group namespace
+ */
+ public function testCodeGenerationShouldTakeIntoAccountNamespacesFromReflection()
+ {
+ $refl = new InterfaceReflection('ZendTest\Code\Generator\TestAsset\InterfaceWithNamespace');
+ $interfaceGenerator = InterfaceGenerator::fromReflection($refl);
+ $this->assertEquals('ZendTest\Code\Generator\TestAsset', $interfaceGenerator->getNamespaceName());
+ $this->assertEquals('InterfaceWithNamespace', $interfaceGenerator->getName());
+ $expected = <<generate();
+ $this->assertEquals($expected, $received, $received);
+ }
+
+ /**
+ * @group namespace
+ */
+ public function testSetNameShouldDetermineIfNamespaceSegmentIsPresent()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\Namespaced\FooInterface');
+ $this->assertEquals('My\Namespaced', $interfaceGenerator->getNamespaceName());
+ }
+
+ /**
+ * @group namespace
+ */
+ public function testPassingANamespacedNameShouldGenerateANamespaceDeclaration()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\Namespaced\FunClass');
+ $received = $interfaceGenerator->generate();
+ $this->assertContains('namespace My\Namespaced;', $received, $received);
+ }
+
+ /**
+ * @group namespace
+ */
+ public function testPassingANamespacedClassnameShouldGenerateAClassnameWithoutItsNamespace()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\Namespaced\FooBarInterface');
+ $received = $interfaceGenerator->generate();
+ $this->assertContains('interface FooBarInterface', $received, $received);
+ }
+
+ /**
+ * @group ZF2-151
+ */
+ public function testAddUses()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\FooInterface');
+ $interfaceGenerator->addUse('My\First\Use\FooInterface');
+ $interfaceGenerator->addUse('My\Second\Use\FooInterface', 'MyAlias');
+ $generated = $interfaceGenerator->generate();
+
+ $this->assertContains('use My\First\Use\FooInterface;', $generated);
+ $this->assertContains('use My\Second\Use\FooInterface as MyAlias;', $generated);
+ }
+
+ /**
+ * @group 4990
+ */
+ public function testAddOneUseTwiceOnlyAddsOne()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\FooInterface');
+ $interfaceGenerator->addUse('My\First\Use\FooInterface');
+ $interfaceGenerator->addUse('My\First\Use\FooInterface');
+ $generated = $interfaceGenerator->generate();
+
+ $this->assertCount(1, $interfaceGenerator->getUses());
+
+ $this->assertContains('use My\First\Use\FooInterface;', $generated);
+ }
+
+ /**
+ * @group 4990
+ */
+ public function testAddOneUseWithAliasTwiceOnlyAddsOne()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('My\FooInterface');
+ $interfaceGenerator->addUse('My\First\Use\FooInterface', 'MyAlias');
+ $interfaceGenerator->addUse('My\First\Use\FooInterface', 'MyAlias');
+ $generated = $interfaceGenerator->generate();
+
+ $this->assertCount(1, $interfaceGenerator->getUses());
+
+ $this->assertContains('use My\First\Use\FooInterface as MyAlias;', $generated);
+ }
+
+ public function testCreateFromArrayWithDocBlockFromArray()
+ {
+ $interfaceGenerator = InterfaceGenerator::fromArray(array(
+ 'name' => 'SampleInterface',
+ 'docblock' => array(
+ 'shortdescription' => 'foo',
+ ),
+ ));
+
+ $docBlock = $interfaceGenerator->getDocBlock();
+ $this->assertInstanceOf('Zend\Code\Generator\DocBlockGenerator', $docBlock);
+ }
+
+ public function testCreateFromArrayWithDocBlockInstance()
+ {
+ $interfaceGenerator = InterfaceGenerator::fromArray(array(
+ 'name' => 'SampleInterface',
+ 'docblock' => new DocBlockGenerator('foo'),
+ ));
+
+ $docBlock = $interfaceGenerator->getDocBlock();
+ $this->assertInstanceOf('Zend\Code\Generator\DocBlockGenerator', $docBlock);
+ }
+
+ public function testHasMethodInsensitive()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethod('methodOne');
+
+ $this->assertTrue($interfaceGenerator->hasMethod('methodOne'));
+ $this->assertTrue($interfaceGenerator->hasMethod('MethoDonE'));
+ }
+
+ public function testRemoveMethodInsensitive()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->addMethod('methodOne');
+
+ $interfaceGenerator->removeMethod('METHODONe');
+ $this->assertFalse($interfaceGenerator->hasMethod('methodOne'));
+ }
+
+ public function testGenerateInterfaceAndAddMethod()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+ $interfaceGenerator->setName('MyInterface');
+ $interfaceGenerator->addMethod('methodOne');
+
+ $expected = <<generate();
+ $this->assertEquals($expected, $output);
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testCanAddConstant()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->setName('My\MyInterface');
+ $interfaceGenerator->addConstant('X', 'value');
+
+ $this->assertTrue($interfaceGenerator->hasConstant('X'));
+
+ $constant = $interfaceGenerator->getConstant('X');
+
+ $this->assertInstanceOf('Zend\Code\Generator\PropertyGenerator', $constant);
+ $this->assertTrue($constant->isConst());
+ $this->assertEquals($constant->getDefaultValue()->getValue(), 'value');
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testCanAddConstantsWithArrayOfGenerators()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->addConstants(array(
+ new PropertyGenerator('X', 'value1', PropertyGenerator::FLAG_CONSTANT),
+ new PropertyGenerator('Y', 'value2', PropertyGenerator::FLAG_CONSTANT)
+ ));
+
+ $this->assertCount(2, $interfaceGenerator->getConstants());
+ $this->assertEquals($interfaceGenerator->getConstant('X')->getDefaultValue()->getValue(), 'value1');
+ $this->assertEquals($interfaceGenerator->getConstant('Y')->getDefaultValue()->getValue(), 'value2');
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testCanAddConstantsWithArrayOfKeyValues()
+ {
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->addConstants(array(
+ array( 'name'=> 'X', 'value' => 'value1'),
+ array('name' => 'Y', 'value' => 'value2')
+ ));
+
+ $this->assertCount(2, $interfaceGenerator->getConstants());
+ $this->assertEquals($interfaceGenerator->getConstant('X')->getDefaultValue()->getValue(), 'value1');
+ $this->assertEquals($interfaceGenerator->getConstant('Y')->getDefaultValue()->getValue(), 'value2');
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testAddConstantThrowsExceptionWithInvalidName()
+ {
+ $this->setExpectedException('InvalidArgumentException');
+
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->addConstant(array(), 'value1');
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testAddConstantThrowsExceptionWithInvalidValue()
+ {
+ $this->setExpectedException('InvalidArgumentException');
+
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->addConstant('X', null);
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testAddConstantThrowsExceptionOnDuplicate()
+ {
+ $this->setExpectedException('InvalidArgumentException');
+
+ $interfaceGenerator = new InterfaceGenerator();
+
+ $interfaceGenerator->addConstant('X', 'value1');
+ $interfaceGenerator->addConstant('X', 'value1');
+ }
+
+ /**
+ * @group 6274
+ */
+ public function testConstantsAddedFromReflection()
+ {
+ $reflector = new InterfaceReflection('ZendTest\Code\Generator\TestAsset\InterfaceWithConstants');
+ $interfaceGenerator = InterfaceGenerator::fromReflection($reflector);
+ $constant = $interfaceGenerator->getConstant('FOO');
+ $this->assertEquals($constant->getDefaultValue()->getValue(), 1);
+ $constant = $interfaceGenerator->getConstant('BAR');
+ $this->assertEquals($constant->getDefaultValue()->getValue(), '2');
+ $constant = $interfaceGenerator->getConstant('FOOBAR');
+ $this->assertEquals($constant->getDefaultValue()->getValue(), 0x20);
+ }
+}
diff --git a/test/Generator/MethodDeclarationGeneratorTest.php b/test/Generator/MethodDeclarationGeneratorTest.php
new file mode 100644
index 00000000..eec32fb5
--- /dev/null
+++ b/test/Generator/MethodDeclarationGeneratorTest.php
@@ -0,0 +1,151 @@
+isInstanceOf($methodGenerator, '\Zend\Code\Generator\PhpMethod');
+ }
+
+ public function testMethodParameterAccessors()
+ {
+ $methodGenerator = new MethodDeclarationGenerator();
+ $methodGenerator->setParameters(array('one'));
+ $params = $methodGenerator->getParameters();
+ $param = array_shift($params);
+ $this->assertTrue($param instanceof \Zend\Code\Generator\ParameterGenerator,
+ 'Failed because $param was not instance of Zend\Code\Generator\ParameterGenerator');
+ }
+
+
+ public function testDocBlock()
+ {
+ $docblockGenerator = new \Zend\Code\Generator\DocBlockGenerator();
+
+ $method = new MethodDeclarationGenerator();
+ $method->setDocBlock($docblockGenerator);
+ $this->assertTrue($docblockGenerator === $method->getDocBlock());
+ }
+
+
+ public function testMethodFromReflection()
+ {
+ $refl = new MethodDeclarationReflection('ZendTest\Code\Generator\TestAsset\InterfaceWithConstants', 'someMethod');
+
+ $methodGenerator = MethodDeclarationGenerator::fromReflection($refl);
+ $target = <<assertEquals($target, (string) $methodGenerator);
+ }
+
+ /**
+ * @group ZF-6444
+ */
+ public function testMethodWithStaticModifierIsEmitted()
+ {
+ $methodGenerator = new MethodDeclarationGenerator();
+ $methodGenerator->setName('foo');
+ $methodGenerator->setParameters(array('one'));
+ $methodGenerator->setStatic(true);
+
+ $expected = <<assertEquals($expected, $methodGenerator->generate());
+ }
+
+ /**
+ * @group ZF-7205
+ */
+ public function testMethodCanHaveDocBlock()
+ {
+ $methodGeneratorProperty = new MethodDeclarationGenerator(
+ 'someFoo',
+ array(),
+ MethodDeclarationGenerator::FLAG_STATIC | MethodDeclarationGenerator::FLAG_PUBLIC,
+ null,
+ '@var string $someVal This is some val'
+ );
+
+ $expected = <<assertEquals($expected, $methodGeneratorProperty->generate());
+ }
+
+ /**
+ * @group ZF-7268
+ */
+ public function testDefaultValueGenerationDoesNotIncludeTrailingSemicolon()
+ {
+ $method = new MethodDeclarationGenerator('setOptions');
+ $default = new ValueGenerator();
+ $default->setValue(array());
+
+ $param = new ParameterGenerator('options', 'array');
+ $param->setDefaultValue($default);
+
+ $method->setParameter($param);
+ $generated = $method->generate();
+ $this->assertRegexp('/array \$options = array\(\)\)/', $generated, $generated);
+ }
+
+ public function testCreateFromArray()
+ {
+ $methodGenerator = MethodDeclarationGenerator::fromArray(array(
+ 'name' => 'SampleMethod',
+ 'body' => 'foo',
+ 'docblock' => array(
+ 'shortdescription' => 'foo',
+ ),
+ 'static' => true,
+ 'visibility' => MethodDeclarationGenerator::VISIBILITY_PUBLIC,
+ ));
+
+ $this->assertEquals('SampleMethod', $methodGenerator->getName());
+ $this->assertInstanceOf('Zend\Code\Generator\DocBlockGenerator', $methodGenerator->getDocBlock());
+ $this->assertTrue($methodGenerator->isStatic());
+ $this->assertEquals(MethodDeclarationGenerator::VISIBILITY_PUBLIC, $methodGenerator->getVisibility());
+ }
+
+ public function testShouldThrowExceptionsForNonSupportedMethods()
+ {
+ $methodGenerator = new MethodDeclarationGenerator();
+
+ // Abstract methods are not supported for interface method declarations.
+ $this->setExpectedException(
+ 'Zend\Code\Generator\Exception\RuntimeException',
+ "Method declarations for interfaces must be public."
+ );
+ $methodGenerator->setVisibility($methodGenerator::VISIBILITY_PROTECTED);
+ }
+}
diff --git a/test/Generator/TestAsset/InterfaceWithConstants.php b/test/Generator/TestAsset/InterfaceWithConstants.php
new file mode 100644
index 00000000..1f6f073e
--- /dev/null
+++ b/test/Generator/TestAsset/InterfaceWithConstants.php
@@ -0,0 +1,22 @@
+getMethod('doSomething');
+ $this->assertInstanceOf('Zend\Code\Reflection\MethodDeclarationReflection', $methodByName);
+
+ $methodsAll = $reflectionInterface->getMethods();
+ $this->assertEquals(2, count($methodsAll));
+
+ $firstMethod = array_shift($methodsAll);
+ $this->assertEquals('doSomething', $firstMethod->getName());
+ }
+
+
+ public function testParentInterfacesReturn()
+ {
+ $reflectionInterface = new InterfaceReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface3');
+ $interfaces = $reflectionInterface->getParentInterfaces();
+ $this->assertEquals(2, count($interfaces));
+ $this->assertEquals('ZendTest\Code\Reflection\TestAsset\TestSampleInterface4', $interfaces[0]->getName());
+ $this->assertEquals('ZendTest\Code\Reflection\TestAsset\TestSampleInterface5', $interfaces[1]->getName());
+ }
+
+ public function testGetContentsReturnsContents()
+ {
+ $reflectionInterface = new InterfaceReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface2');
+ $target = <<getContents();
+ $this->assertEquals(trim($target), trim($contents));
+ }
+
+
+ public function testStartLine()
+ {
+ $reflectionInterface = new InterfaceReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface1');
+
+ $this->assertEquals(18, $reflectionInterface->getStartLine());
+ $this->assertEquals(5, $reflectionInterface->getStartLine(true));
+ }
+
+ public function testGetDeclaringFileReturnsFilename()
+ {
+ $reflectionInterface = new InterfaceReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface1');
+ $this->assertContains('TestSampleInterface1.php', $reflectionInterface->getDeclaringFile()->getFileName());
+ }
+
+ public function testGetAnnotationsWithNoNameInformations()
+ {
+ $reflectionInterface = new InjectableClassReflection(
+ // TestSampleClass5 has the annotations required to get to the
+ // right point in the getAnnotations method.
+ 'ZendTest\Code\Reflection\TestAsset\TestSampleInterface1'
+ );
+
+ $annotationManager = new \Zend\Code\Annotation\AnnotationManager();
+
+ $fileScanner = $this->getMockBuilder('Zend\Code\Scanner\FileScanner')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reflectionInterface->setFileScanner($fileScanner);
+
+ $fileScanner->expects($this->any())
+ ->method('getClassNameInformation')
+ ->will($this->returnValue(false));
+
+ $this->assertFalse($reflectionInterface->getAnnotations($annotationManager));
+ }
+
+ public function testGetContentsReturnsEmptyContentsOnEvaldCode()
+ {
+ $className = uniqid('InterfaceReflectionTestGenerated');
+
+ eval('name' . 'space ' . __NAMESPACE__ . '; inter' . 'face ' . $className . '{}');
+
+ $reflectionInterface = new InterfaceReflection(__NAMESPACE__ . '\\' . $className);
+
+ $this->assertSame('', $reflectionInterface->getContents());
+ }
+
+ public function testGetContentsReturnsEmptyContentsOnInternalCode()
+ {
+ $reflectionInterface = new ClassReflection('Iterator');
+ $this->assertSame('', $reflectionInterface->getContents());
+ }
+}
diff --git a/test/Reflection/MethodDeclarationReflectionTest.php b/test/Reflection/MethodDeclarationReflectionTest.php
new file mode 100644
index 00000000..1ceb3b38
--- /dev/null
+++ b/test/Reflection/MethodDeclarationReflectionTest.php
@@ -0,0 +1,106 @@
+assertInstanceOf('Zend\Code\Reflection\InterfaceReflection', $method->getDeclaringInterface());
+ }
+
+ public function testParameterReturn()
+ {
+ $method = new MethodDeclarationReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface1', 'doSomething');
+ $parameters = $method->getParameters();
+ $this->assertEquals(5, count($parameters));
+ $this->assertInstanceOf('Zend\Code\Reflection\ParameterReflection', array_shift($parameters));
+ }
+
+ public function testStartLine()
+ {
+ $reflectionMethod = new MethodDeclarationReflection('ZendTest\Code\Reflection\TestAsset\TestSampleInterface1', 'doSomething');
+
+ $this->assertEquals(36, $reflectionMethod->getStartLine());
+ $this->assertEquals(20, $reflectionMethod->getStartLine(true));
+ }
+
+ public function testGetPrototypeMethod()
+ {
+ $reflectionMethod = new MethodDeclarationReflection(
+ 'ZendTest\Code\Reflection\TestAsset\TestSampleInterface1',
+ 'doSomethingElse'
+ );
+ $prototype = array(
+ 'namespace' => 'ZendTest\Code\Reflection\TestAsset',
+ 'class' => 'TestSampleInterface1',
+ 'name' => 'doSomethingElse',
+ 'visibility' => 'public',
+ 'return' => 'int',
+ 'arguments' => array(
+ 'one' => array(
+ 'type' => 'int',
+ 'required' => true,
+ 'by_ref' => false,
+ 'default' => null,
+ ),
+ 'two' => array(
+ 'type' => 'int',
+ 'required' => false,
+ 'by_ref' => false,
+ 'default' => 2,
+ ),
+ 'three' => array(
+ 'type' => 'string',
+ 'required' => false,
+ 'by_ref' => false,
+ 'default' => 'three',
+ ),
+ ),
+ );
+ $this->assertEquals($prototype, $reflectionMethod->getPrototype());
+ $this->assertEquals(
+ 'public int doSomethingElse(int $one, int $two = 2, string $three = \'three\');',
+ $reflectionMethod->getPrototype(MethodDeclarationReflection::PROTOTYPE_AS_STRING)
+ );
+ }
+
+ public function testGetAnnotationsWithNoNameInformations()
+ {
+ $reflectionMethod = new InjectableMethodReflection(
+ // TestSampleClass5 has the annotations required to get to the
+ // right point in the getAnnotations method.
+ 'ZendTest\Code\Reflection\TestAsset\TestSampleInterface1',
+ 'orDoNothingAtAll'
+ );
+
+ $annotationManager = new \Zend\Code\Annotation\AnnotationManager();
+
+ $fileScanner = $this->getMockBuilder('Zend\Code\Scanner\CachingFileScanner')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reflectionMethod->setFileScanner($fileScanner);
+
+ $fileScanner->expects($this->any())
+ ->method('getClassNameInformation')
+ ->will($this->returnValue(false));
+
+ $this->assertFalse($reflectionMethod->getAnnotations($annotationManager));
+ }
+}
diff --git a/test/Reflection/TestAsset/TestSampleInterface1.php b/test/Reflection/TestAsset/TestSampleInterface1.php
new file mode 100644
index 00000000..d751e161
--- /dev/null
+++ b/test/Reflection/TestAsset/TestSampleInterface1.php
@@ -0,0 +1,67 @@
+