XmlParser::checkBodyStructure()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 12
cts 12
cp 1
rs 9.6
c 0
b 0
f 0
cc 4
nc 6
nop 2
crap 4
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the feed-io package.
4
 *
5
 * (c) Alexandre Debril <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace FeedIo\Parser;
12
13
use FeedIo\Parser;
14
use FeedIo\RuleSet;
15
use FeedIo\FeedInterface;
16
use FeedIo\Feed\NodeInterface;
17
use FeedIo\ParserAbstract;
18
use FeedIo\Reader\Document;
19
use FeedIo\Parser\MissingFieldsException;
20
use FeedIo\Parser\UnsupportedFormatException;
21
22
/**
23
 * Parses a DOM document if its format matches the parser's standard
24
 *
25
 * Depends on :
26
 *  - FeedIo\StandardAbstract
27
 *  - Psr\Log\LoggerInterface
28
 *
29
 */
30
class XmlParser extends ParserAbstract
31
{
32
33
    /**
34
     * @param $tagName
35
     * @return bool
36
     */
37 12
    public function isItem(string $tagName) : bool
38
    {
39 12
        return (strtolower($this->standard->getItemNodeName()) === strtolower($tagName));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class FeedIo\StandardAbstract as the method getItemNodeName() does only exist in the following sub-classes of FeedIo\StandardAbstract: FeedIo\Standard\Atom, FeedIo\Standard\Rdf, FeedIo\Standard\Rss, FeedIo\Standard\XmlAbstract. 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...
40
    }
41
42
    /**
43
     * @param  Document                       $document
44
     * @param  FeedInterface                  $feed
45
     * @return \FeedIo\FeedInterface
46
     * @throws Parser\MissingFieldsException
47
     * @throws Parser\UnsupportedFormatException
48
     */
49 12
    public function parseContent(Document $document, FeedInterface $feed) : FeedInterface
50
    {
51 12
        $element = $this->standard->getMainElement($document->getDOMDocument());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class FeedIo\StandardAbstract as the method getMainElement() does only exist in the following sub-classes of FeedIo\StandardAbstract: FeedIo\Standard\Atom, FeedIo\Standard\Rdf, FeedIo\Standard\Rss, FeedIo\Standard\XmlAbstract. 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...
52
53 12
        $this->parseNode($feed, $element, $this->standard->getFeedRuleSet());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class FeedIo\StandardAbstract as the method getFeedRuleSet() does only exist in the following sub-classes of FeedIo\StandardAbstract: FeedIo\Standard\Atom, FeedIo\Standard\Rdf, FeedIo\Standard\Rss, FeedIo\Standard\XmlAbstract. 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...
54
55 12
        return $feed;
56
    }
57
58
    /**
59
     * @param Document $document
60
     * @param iterable $mandatoryFields
61
     * @throws MissingFieldsException
62
     * @return bool
63
     */
64 15
    public function checkBodyStructure(Document $document, iterable $mandatoryFields) : bool
65
    {
66 15
        $errors = array();
67
68 15
        $element = $document->getDOMDocument()->documentElement;
69 15
        foreach ($mandatoryFields as $field) {
70 9
            $list = $element->getElementsByTagName($field);
71 9
            if (0 === $list->length) {
72 2
                $errors[] = $field;
73
            }
74
        }
75
76 15
        if (!empty($errors)) {
77 2
            $message = "missing mandatory field(s) : ".implode(',', $errors);
78 2
            $this->logger->warning($message);
79 2
            throw new MissingFieldsException($message);
80
        }
81
82 13
        return true;
83
    }
84
85
    /**
86
     * @param  NodeInterface $item
87
     * @param  \DOMElement   $element
88
     * @param  RuleSet       $ruleSet
89
     * @return NodeInterface
90
     */
91 13
    public function parseNode(NodeInterface $item, \DOMElement $element, RuleSet $ruleSet) : NodeInterface
92
    {
93 13
        foreach ($element->childNodes as $node) {
94 12
            if ($node instanceof \DOMElement) {
95 12
                $this->handleNode($item, $node, $ruleSet);
96
            }
97
        }
98
99 13
        return $item;
100
    }
101
102
    /**
103
     * @param NodeInterface $item
104
     * @param \DOMElement $node
105
     * @param RuleSet $ruleSet
106
     */
107 12
    protected function handleNode(NodeInterface $item, \DOMElement $node, RuleSet $ruleSet) : void
108
    {
109 12
        if ($this->isItem($node->tagName) && $item instanceof FeedInterface) {
110 11
            $newItem = $this->parseNode($item->newItem(), $node, $this->standard->getItemRuleSet());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class FeedIo\StandardAbstract as the method getItemRuleSet() does only exist in the following sub-classes of FeedIo\StandardAbstract: FeedIo\Standard\Atom, FeedIo\Standard\Rdf, FeedIo\Standard\Rss, FeedIo\Standard\XmlAbstract. 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...
111 11
            $this->addValidItem($item, $newItem);
112
        } else {
113 12
            $rule = $ruleSet->get($node->tagName);
114 12
            $rule->setProperty($item, $node);
115
        }
116 12
    }
117
}
118