Completed
Push — master ( 8dcc19...0acb93 )
by Aleh
10s
created

CommentParser::parseDoc()   D

Complexity

Conditions 10
Paths 28

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 38
rs 4.8196
c 1
b 0
f 0
cc 10
eloc 28
nc 28
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Padawan\Parser;
4
5
use phpDocumentor\Reflection\DocBlock;
6
use phpDocumentor\Reflection\DocBlock\Tag;
7
use phpDocumentor\Reflection\DocBlock\Context;
8
use Padawan\Domain\Project\Node\Comment;
9
use Padawan\Domain\Project\Node\MethodParam;
10
use Padawan\Domain\Project\Node\Variable;
11
use Padawan\Domain\Project\Node\ClassProperty;
12
13
class CommentParser
14
{
15
    public function __construct(UseParser $useParser) {
16
        $this->useParser = $useParser;
17
    }
18
19
    /**
20
     * Parses DocComment block
21
     *
22
     * @param string $doc
23
     * @return Comment
24
     */
25
    public function parse($doc) {
26
        $text = $doc;
27
        if (is_array($doc)) {
28
            $doc = array_shift($doc);
29
            $text = $doc->getText();
30
        }
31
        $comment = new Comment(
32
            $this->trimComment($text)
33
        );
34
        $this->parseDoc($comment, $text);
35
36
        return $comment;
37
    }
38
39
    /**
40
     * Parses doc comment and populates comment entity
41
     *
42
     * @param string $text
43
     */
44
    protected function parseDoc(Comment $comment, $text) {
45
        $context = $this->getContext();
46
        try {
47
            $block = new DocBlock($text, $context);
48
            foreach ($block->getTags() AS $tag) {
49
                switch ($tag->getName()) {
50
                case "param":
51
                    $comment->addVar(
52
                        $this->createMethodParam($tag)
53
                    );
54
                    break;
55
                case "var":
56
                    $comment->addVar(
57
                        $this->createVar($tag)
58
                    );
59
                    break;
60
                case "return":
61
                    $comment->setReturn(
62
                        $this->getFQCN($tag->getType())
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getType() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\MethodTag, phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\ReturnTag, phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
63
                    );
64
                    break;
65
                case "property":
66
                case "property-read":
67
                case "property-write":
68
                    $comment->addProperty(
69
                        $this->createProperty($tag)
70
                    );
71
                    break;
72
                case "inheritdoc":
73
                    $comment->markInheritDoc();
74
                    break;
75
                }
76
            }
77
        }
78
        catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
79
80
        }
81
    }
82 View Code Duplication
    protected function createMethodParam(Tag $tag) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
        $name = trim($tag->getVariableName(), '$');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getVariableName() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
84
        $param = new MethodParam($name);
85
        $param->setType($this->getFQCN($tag->getType()));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getType() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\MethodTag, phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\ReturnTag, phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
86
        return $param;
87
    }
88 View Code Duplication
    protected function createVar(Tag $tag) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
        $name = trim($tag->getVariableName(), '$');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getVariableName() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
90
        $param = new Variable($name);
91
        $param->setType($this->getFQCN($tag->getType()));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getType() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\MethodTag, phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\ReturnTag, phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
92
        return $param;
93
    }
94 View Code Duplication
    protected function createProperty(Tag $tag) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
95
        $name = trim($tag->getVariableName(), '$');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getVariableName() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
96
        $prop = new ClassProperty;
97
        $prop->name = $name;
98
        $prop->setType($this->getFQCN($tag->getType()));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class phpDocumentor\Reflection\DocBlock\Tag as the method getType() does only exist in the following sub-classes of phpDocumentor\Reflection\DocBlock\Tag: phpDocumentor\Reflection\DocBlock\Tag\MethodTag, phpDocumentor\Reflection\DocBlock\Tag\ParamTag, phpDocumentor\Reflection...ock\Tag\PropertyReadTag, phpDocumentor\Reflection\DocBlock\Tag\PropertyTag, phpDocumentor\Reflection...ck\Tag\PropertyWriteTag, phpDocumentor\Reflection\DocBlock\Tag\ReturnTag, phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag, phpDocumentor\Reflection\DocBlock\Tag\VarTag. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
99
        return $prop;
100
    }
101
102
    /**
103
     * Creates FQN by type string
104
     *
105
     * @param string $type
106
     * @return \Padawan\Domain\Project\FQCN
107
     */
108
    protected function getFQCN($type) {
109
        return $this->useParser->parseType($type);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->useParser->parseType($type); of type Padawan\Domain\Project\FQCN|boolean adds the type boolean to the return on line 109 which is incompatible with the return type documented by Padawan\Parser\CommentParser::getFQCN of type Padawan\Domain\Project\FQCN.
Loading history...
110
    }
111
112
    /**
113
     * @return string
114
     */
115
    protected function trimComment($comment) {
116
        $lines = explode("\n", $comment);
117
        foreach ($lines AS $key => $line) {
118
            $lines[$key] = preg_replace([
119
                "/^\/\**/",
120
                "/^ *\* */",
121
                "/\**\/$/"
122
            ], "", $line);
123
        }
124
        $text = implode("\n", $lines);
125
        return utf8_encode($text);
126
    }
127
128
    /**
129
     * @return Context
130
     */
131
    protected function getContext() {
132
        $uses = $this->useParser->getUses();
133
        $namespace = $uses->getFQCN()->toString();
134
        $aliases = array_map(function($fqcn) {
135
            return $fqcn->toString();
136
        }, $uses->all());
137
        return new Context($namespace, $aliases);
138
    }
139
140
    /** @property UseParser */
141
    private $useParser;
142
}
143