Completed
Push — master ( a9adfd...b66e97 )
by Kevin
02:14
created

Tokenizer   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 89
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 85.96%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 13
c 1
b 0
f 0
lcom 1
cbo 7
dl 0
loc 89
ccs 49
cts 57
cp 0.8596
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A tokenize() 0 16 3
B createToken() 0 35 6
A createElement() 0 22 3
1
<?php
2
3
namespace Groundskeeper\Tokens;
4
5
use Kevintweber\HtmlTokenizer\HtmlTokenizer;
6
use Kevintweber\HtmlTokenizer\Tokens\Element as BasicElement;
7
use Kevintweber\HtmlTokenizer\Tokens\Token as BasicToken;
8
9
class Tokenizer
10
{
11
    /** @var array */
12
    private $options;
13
14
    /**
15
     * Constructor
16
     */
17 10
    public function __construct(array $options = array())
18
    {
19 10
        $this->options = $options;
20 10
    }
21
22 10
    public function tokenize($html)
23
    {
24 10
        if (!is_string($html)) {
25
            throw new \InvalidArgumentException('Html must be a string.');
26
        }
27
28 10
        $tokenizer = new HtmlTokenizer($this->options['throw-on-error']);
29 10
        $basicTokenCollection = $tokenizer->parse($html);
30
31 10
        $cleanableTokens = array();
32 10
        foreach ($basicTokenCollection as $basicToken) {
33 10
            $cleanableTokens[] = $this->createToken($basicToken);
34 10
        }
35
36 10
        return $cleanableTokens;
37
    }
38
39 10
    private function createToken(BasicToken $basicToken)
40
    {
41 10
        switch ($basicToken->getType()) {
42 10
        case 'cdata':
43 2
            return new CData(
44 10
                $basicToken->getParent(),
0 ignored issues
show
Bug introduced by
It seems like $basicToken->getParent() targeting Kevintweber\HtmlTokenize...kens\Token::getParent() can also be of type object<Kevintweber\HtmlTokenizer\Tokens\Token>; however, Groundskeeper\Tokens\CData::__construct() does only seem to accept null|object<Groundskeeper\Tokens\Token>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
45 2
                $basicToken->getValue()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kevintweber\HtmlTokenizer\Tokens\Token as the method getValue() does only exist in the following implementations of said interface: Kevintweber\HtmlTokenizer\Tokens\CData, Kevintweber\HtmlTokenizer\Tokens\Comment, Kevintweber\HtmlTokenizer\Tokens\DocType, Kevintweber\HtmlTokenizer\Tokens\Text.

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...
46 2
            );
47
48 8
        case 'comment':
49 2
            return new Comment(
50 2
                $basicToken->getParent(),
0 ignored issues
show
Bug introduced by
It seems like $basicToken->getParent() targeting Kevintweber\HtmlTokenize...kens\Token::getParent() can also be of type object<Kevintweber\HtmlTokenizer\Tokens\Token>; however, Groundskeeper\Tokens\Comment::__construct() does only seem to accept null|object<Groundskeeper\Tokens\Token>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
51 2
                $basicToken->getValue()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kevintweber\HtmlTokenizer\Tokens\Token as the method getValue() does only exist in the following implementations of said interface: Kevintweber\HtmlTokenizer\Tokens\CData, Kevintweber\HtmlTokenizer\Tokens\Comment, Kevintweber\HtmlTokenizer\Tokens\DocType, Kevintweber\HtmlTokenizer\Tokens\Text.

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...
52 2
            );
53
54 6
        case 'doctype':
55 2
            return new DocType(
56 2
                $basicToken->getParent(),
0 ignored issues
show
Bug introduced by
It seems like $basicToken->getParent() targeting Kevintweber\HtmlTokenize...kens\Token::getParent() can also be of type object<Kevintweber\HtmlTokenizer\Tokens\Token>; however, Groundskeeper\Tokens\DocType::__construct() does only seem to accept null|object<Groundskeeper\Tokens\Token>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
57 2
                $basicToken->getValue()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kevintweber\HtmlTokenizer\Tokens\Token as the method getValue() does only exist in the following implementations of said interface: Kevintweber\HtmlTokenizer\Tokens\CData, Kevintweber\HtmlTokenizer\Tokens\Comment, Kevintweber\HtmlTokenizer\Tokens\DocType, Kevintweber\HtmlTokenizer\Tokens\Text.

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...
58 2
            );
59
60 4
        case 'element':
61 2
            return static::createElement($basicToken);
0 ignored issues
show
Bug introduced by
Since createElement() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of createElement() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
Compatibility introduced by
$basicToken of type object<Kevintweber\HtmlTokenizer\Tokens\Token> is not a sub-type of object<Kevintweber\HtmlTokenizer\Tokens\Element>. It seems like you assume a concrete implementation of the interface Kevintweber\HtmlTokenizer\Tokens\Token to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
62
63 2
        case 'text':
64 2
            return new Text(
65 2
                $basicToken->getParent(),
0 ignored issues
show
Bug introduced by
It seems like $basicToken->getParent() targeting Kevintweber\HtmlTokenize...kens\Token::getParent() can also be of type object<Kevintweber\HtmlTokenizer\Tokens\Token>; however, Groundskeeper\Tokens\Text::__construct() does only seem to accept null|object<Groundskeeper\Tokens\Token>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
66 2
                $basicToken->getValue()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kevintweber\HtmlTokenizer\Tokens\Token as the method getValue() does only exist in the following implementations of said interface: Kevintweber\HtmlTokenizer\Tokens\CData, Kevintweber\HtmlTokenizer\Tokens\Comment, Kevintweber\HtmlTokenizer\Tokens\DocType, Kevintweber\HtmlTokenizer\Tokens\Text.

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...
67 2
            );
68
        }
69
70
        throw new \RuntimeException(
71
            'Invalid token type: ' . $basicToken->getType()
72
        );
73
    }
74
75 2
    private function createElement(BasicElement $basicElement)
76
    {
77
        $elementClassName = 'Groundskeeper\\Tokens\\Elements\\' .
78 2
            ucfirst(strtolower($basicElement->getName()));
79 2
        if (!class_exists($elementClassName)) {
80 2
            $elementClassName = 'Groundskeeper\\Tokens\\Elements\\Element';
81 2
        }
82
83 2
        $cleanableElement = new $elementClassName(
84 2
            $basicElement->getName(),
85 2
            $basicElement->getAttributes(),
86 2
            $basicElement->getParent()
87 2
        );
88
89 2
        foreach ($basicElement->getChildren() as $basicChild) {
90
            $cleanableElement->addChild(
91
                static::importToken($basicChild)
0 ignored issues
show
Bug introduced by
The method importToken() does not seem to exist on object<Groundskeeper\Tokens\Tokenizer>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
92
            );
93 2
        }
94
95 2
        return $cleanableElement;
96
    }
97
}
98