Completed
Push — master ( fef96a...9f8b44 )
by Kevin
03:02
created

AbstractToken   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 10
Bugs 1 Features 7
Metric Value
wmc 22
c 10
b 1
f 7
lcom 1
cbo 5
dl 0
loc 110
ccs 47
cts 47
cp 1
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getDepth() 0 4 1
A getParent() 0 4 1
A setParent() 0 11 2
A getType() 0 4 1
B cleanChildTokens() 0 18 6
A __toString() 0 4 1
B __construct() 0 15 6
A hasAncestor() 0 13 4
1
<?php
2
3
namespace Groundskeeper\Tokens;
4
5
use Groundskeeper\Configuration;
6
use Psr\Log\LoggerInterface;
7
8
/**
9
 * A base class for all tokens.
10
 */
11
abstract class AbstractToken implements Token
12
{
13
    /** @var Configuration */
14
    protected $configuration;
15
16
    /** @var int */
17
    private $depth;
18
19
    /** @var null|Token */
20
    private $parent;
21
22
    /** @var string */
23
    private $type;
24
25
    /**
26
     * Constructor
27
     */
28 84
    public function __construct($type, Configuration $configuration)
29
    {
30 1
        if ($type !== Token::CDATA
31 84
            && $type !== Token::COMMENT
32 84
            && $type !== Token::DOCTYPE
33 84
            && $type !== Token::ELEMENT
34 84
            && $type !== Token::TEXT) {
35 1
            throw new \InvalidArgumentException('Invalid type: ' . $type);
36
        }
37
38 83
        $this->configuration = $configuration;
39 83
        $this->depth = 0;
40 83
        $this->parent = null;
41 83
        $this->type = $type;
42 83
    }
43
44
    /**
45
     * Required by the Token interface.
46
     */
47 59
    public function getDepth()
48
    {
49 59
        return $this->depth;
50
    }
51
52
    /**
53
     * Required by the Token interface.
54
     */
55 34
    public function getParent()
56
    {
57 34
        return $this->parent;
58
    }
59
60
    /**
61
     * Chainable setter for 'parent'.
62
     */
63 58
    public function setParent(Token $parent = null)
64
    {
65 58
        $this->depth = 0;
66 58
        if ($parent instanceof Token) {
67 58
            $this->depth = $parent->getDepth() + 1;
68 58
        }
69
70 58
        $this->parent = $parent;
71
72 58
        return $this;
73
    }
74
75 3
    public function hasAncestor(Element $element)
76
    {
77 3
        if ($this->parent === null) {
78 1
            return false;
79
        }
80
81 3
        if ($this->parent->getType() == Token::ELEMENT &&
82 3
            $this->parent->getName() == $element->getName()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Groundskeeper\Tokens\Token as the method getName() does only exist in the following implementations of said interface: Groundskeeper\Tokens\Element, Groundskeeper\Tokens\ElementTypes\ClosedElement, Groundskeeper\Tokens\ElementTypes\OpenElement, Groundskeeper\Tokens\Elements\A, Groundskeeper\Tokens\Elements\Abbr, Groundskeeper\Tokens\Elements\Address, Groundskeeper\Tokens\Elements\Article, Groundskeeper\Tokens\Elements\Aside, Groundskeeper\Tokens\Elements\B, Groundskeeper\Tokens\Elements\Base, Groundskeeper\Tokens\Elements\Bdi, Groundskeeper\Tokens\Elements\Bdo, Groundskeeper\Tokens\Elements\Blockquote, Groundskeeper\Tokens\Elements\Body, Groundskeeper\Tokens\Elements\Br, Groundskeeper\Tokens\Elements\Cite, Groundskeeper\Tokens\Elements\Code, Groundskeeper\Tokens\Elements\Data, Groundskeeper\Tokens\Elements\Dd, Groundskeeper\Tokens\Elements\Del, Groundskeeper\Tokens\Elements\Dfn, Groundskeeper\Tokens\Elements\Div, Groundskeeper\Tokens\Elements\Dl, Groundskeeper\Tokens\Elements\Dt, Groundskeeper\Tokens\Elements\Em, Groundskeeper\Tokens\Elements\Figcaption, Groundskeeper\Tokens\Elements\Figure, Groundskeeper\Tokens\Elements\Footer, Groundskeeper\Tokens\Elements\H1, Groundskeeper\Tokens\Elements\H2, Groundskeeper\Tokens\Elements\H3, Groundskeeper\Tokens\Elements\H4, Groundskeeper\Tokens\Elements\H5, Groundskeeper\Tokens\Elements\H6, Groundskeeper\Tokens\Elements\Head, Groundskeeper\Tokens\Elements\Header, Groundskeeper\Tokens\Elements\Hgroup, Groundskeeper\Tokens\Elements\Hr, Groundskeeper\Tokens\Elements\Html, Groundskeeper\Tokens\Elements\I, Groundskeeper\Tokens\Elements\Ins, Groundskeeper\Tokens\Elements\Kbd, Groundskeeper\Tokens\Elements\Li, Groundskeeper\Tokens\Elements\Link, Groundskeeper\Tokens\Elements\Main, Groundskeeper\Tokens\Elements\Mark, Groundskeeper\Tokens\Elements\Meta, Groundskeeper\Tokens\Elements\Nav, Groundskeeper\Tokens\Elements\Noscript, Groundskeeper\Tokens\Elements\Ol, Groundskeeper\Tokens\Elements\P, Groundskeeper\Tokens\Elements\Pre, Groundskeeper\Tokens\Elements\Q, Groundskeeper\Tokens\Elements\Rp, Groundskeeper\Tokens\Elements\Rt, Groundskeeper\Tokens\Elements\Ruby, Groundskeeper\Tokens\Elements\S, Groundskeeper\Tokens\Elements\Samp, Groundskeeper\Tokens\Elements\Script, Groundskeeper\Tokens\Elements\Section, Groundskeeper\Tokens\Elements\Small, Groundskeeper\Tokens\Elements\Span, Groundskeeper\Tokens\Elements\Strong, Groundskeeper\Tokens\Elements\Style, Groundskeeper\Tokens\Elements\Sub, Groundskeeper\Tokens\Elements\Sup, Groundskeeper\Tokens\Elements\Template, Groundskeeper\Tokens\Elements\Time, Groundskeeper\Tokens\Elements\Title, Groundskeeper\Tokens\Elements\U, Groundskeeper\Tokens\Elements\Ul, Groundskeeper\Tokens\Elements\Wbr.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
83 2
            return true;
84
        }
85
86 1
        return $this->parent->hasAncestor($element);
87
    }
88
89
    /**
90
     * Required by the Token interface.
91
     */
92 46
    public function getType()
93
    {
94 46
        return $this->type;
95
    }
96
97 64
    public static function cleanChildTokens(Configuration $configuration, array &$children, LoggerInterface $logger)
98
    {
99 64
        if ($configuration->get('clean-strategy') == Configuration::CLEAN_STRATEGY_NONE) {
100 52
            return true;
101
        }
102
103 63
        foreach ($children as $key => $child) {
104 59
            if ($child instanceof Cleanable) {
105 53
                $isClean = $child->clean($logger);
106 53
                if (!$isClean  && $configuration->get('clean-strategy') !== Configuration::CLEAN_STRATEGY_LENIENT) {
107 9
                    $logger->debug('Unable to fix.  Removing ' . $child);
108 9
                    unset($children[$key]);
109 9
                }
110 53
            }
111 63
        }
112
113 63
        return true;
114
    }
115
116 5
    public function __toString()
117
    {
118 5
        return ucfirst($this->getType());
119
    }
120
}
121