IteratorTest   A
last analyzed

Complexity

Total Complexity 4

Size/Duplication

Total Lines 40
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 2
Bugs 1 Features 1
Metric Value
wmc 4
c 2
b 1
f 1
lcom 1
cbo 8
dl 0
loc 40
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A testRecursiveIterator() 0 5 1
A testRecursiveIteratorFiltered() 0 13 2
A getExample() 0 16 1
1
<?php
2
/*
3
 * Copyright (C) 2015 Michael Herold <[email protected]>
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
namespace hemio\html;
20
21
/**
22
 * Description of IteratorTest
23
 *
24
 * @author Michael Herold <[email protected]>
25
 */
26
class IteratorTest extends \Helpers
27
{
28
29
    public function testRecursiveIterator()
30
    {
31
        $doc = $this->getExample();
32
        $this->assertCount(13, $doc->getRecursiveIterator());
33
    }
34
35
    public function testRecursiveIteratorFiltered()
36
    {
37
        $doc = $this->getExample();
38
39
        $select = function (Interface_\HtmlCode $child) {
40
            return $child instanceof \hemio\html\Div;
41
        };
42
43
        foreach ($doc->getRecursiveIterator($select) as $child)
44
            $this->assertInstanceOf('\hemio\html\Div', $child);
45
46
        $this->assertCount(3, $doc->getRecursiveIterator($select));
47
    }
48
49
    public function getExample()
50
    {
51
        $doc = new Document(new Str('Title'));
52
53
        $doc->getHtml()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface hemio\html\Interface_\HtmlCode as the method addChild() does only exist in the following implementations of said interface: hemio\html\A, hemio\html\Abbr, hemio\html\Address, hemio\html\Area, hemio\html\Article, hemio\html\Aside, hemio\html\Audio, hemio\html\B, hemio\html\Bdi, hemio\html\Bdo, hemio\html\Blockquote, hemio\html\Body, hemio\html\Button, hemio\html\Canvas, hemio\html\Caption, hemio\html\Cite, hemio\html\Code, hemio\html\Col, hemio\html\Colgroup, hemio\html\Data, hemio\html\Datalist, hemio\html\Dd, hemio\html\Del, hemio\html\Dfn, hemio\html\Div, hemio\html\Dl, hemio\html\Dt, hemio\html\Em, hemio\html\Embed, hemio\html\Fieldset, hemio\html\Figcaption, hemio\html\Figure, hemio\html\Footer, hemio\html\Form, hemio\html\H1, hemio\html\H2, hemio\html\H3, hemio\html\H4, hemio\html\H5, hemio\html\H6, hemio\html\Head, hemio\html\Header, hemio\html\Html, hemio\html\I, hemio\html\Iframe, hemio\html\Ins, hemio\html\Kbd, hemio\html\Keygen, hemio\html\Label, hemio\html\Legend, hemio\html\Li, hemio\html\Map, hemio\html\Mark, hemio\html\Meter, hemio\html\Nav, hemio\html\Noscript, hemio\html\Object, hemio\html\Ol, hemio\html\Optgroup, hemio\html\Option, hemio\html\Output, hemio\html\P, hemio\html\Param, hemio\html\Pre, hemio\html\Progress, hemio\html\Q, hemio\html\Rp, hemio\html\Rt, hemio\html\Ruby, hemio\html\S, hemio\html\Samp, hemio\html\Script, hemio\html\Section, hemio\html\Select, hemio\html\Small, hemio\html\Source, hemio\html\Span, hemio\html\Strong, hemio\html\Style, hemio\html\Sub, hemio\html\Sup, hemio\html\Table, hemio\html\Tbody, hemio\html\Td, hemio\html\Template, hemio\html\Textarea, hemio\html\Tfoot, hemio\html\Th, hemio\html\Thead, hemio\html\Time, hemio\html\Title, hemio\html\Tr, hemio\html\Track, hemio\html\U, hemio\html\Ul, hemio\html\Variable, hemio\html\Video, hemio\html\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...
54
            ->addChild(new Header())
55
            ->addChild(new Div());
56
57
        $doc->getHtml()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface hemio\html\Interface_\HtmlCode as the method addChild() does only exist in the following implementations of said interface: hemio\html\A, hemio\html\Abbr, hemio\html\Address, hemio\html\Area, hemio\html\Article, hemio\html\Aside, hemio\html\Audio, hemio\html\B, hemio\html\Bdi, hemio\html\Bdo, hemio\html\Blockquote, hemio\html\Body, hemio\html\Button, hemio\html\Canvas, hemio\html\Caption, hemio\html\Cite, hemio\html\Code, hemio\html\Col, hemio\html\Colgroup, hemio\html\Data, hemio\html\Datalist, hemio\html\Dd, hemio\html\Del, hemio\html\Dfn, hemio\html\Div, hemio\html\Dl, hemio\html\Dt, hemio\html\Em, hemio\html\Embed, hemio\html\Fieldset, hemio\html\Figcaption, hemio\html\Figure, hemio\html\Footer, hemio\html\Form, hemio\html\H1, hemio\html\H2, hemio\html\H3, hemio\html\H4, hemio\html\H5, hemio\html\H6, hemio\html\Head, hemio\html\Header, hemio\html\Html, hemio\html\I, hemio\html\Iframe, hemio\html\Ins, hemio\html\Kbd, hemio\html\Keygen, hemio\html\Label, hemio\html\Legend, hemio\html\Li, hemio\html\Map, hemio\html\Mark, hemio\html\Meter, hemio\html\Nav, hemio\html\Noscript, hemio\html\Object, hemio\html\Ol, hemio\html\Optgroup, hemio\html\Option, hemio\html\Output, hemio\html\P, hemio\html\Param, hemio\html\Pre, hemio\html\Progress, hemio\html\Q, hemio\html\Rp, hemio\html\Rt, hemio\html\Ruby, hemio\html\S, hemio\html\Samp, hemio\html\Script, hemio\html\Section, hemio\html\Select, hemio\html\Small, hemio\html\Source, hemio\html\Span, hemio\html\Strong, hemio\html\Style, hemio\html\Sub, hemio\html\Sup, hemio\html\Table, hemio\html\Tbody, hemio\html\Td, hemio\html\Template, hemio\html\Textarea, hemio\html\Tfoot, hemio\html\Th, hemio\html\Thead, hemio\html\Time, hemio\html\Title, hemio\html\Tr, hemio\html\Track, hemio\html\U, hemio\html\Ul, hemio\html\Variable, hemio\html\Video, hemio\html\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...
58
            ->addChild(new Div())
59
            ->addChild(new Section())
60
            ->addChild(new Article())
61
            ->addChild(new Div());
62
63
        return $doc;
64
    }
65
}
66