Completed
Push — master ( b4ff9c...fbe6de )
by Colin
01:00
created

ListItemParser   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 68
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 7
dl 0
loc 68
ccs 26
cts 26
cp 1
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getBlock() 0 4 1
A isContainer() 0 4 1
A tryContinue() 0 25 5
A canContain() 0 15 3
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
13
14
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
15
use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
16
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
17
use League\CommonMark\Node\Block\AbstractBlock;
18
use League\CommonMark\Node\Block\Paragraph;
19
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
20
use League\CommonMark\Parser\Block\BlockContinue;
21
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
22
use League\CommonMark\Parser\Cursor;
23
24
final class ListItemParser extends AbstractBlockContinueParser
25
{
26
    /** @var ListItem */
27
    private $block;
28
29
    /** @var bool */
30
    private $hadBlankLine = false;
31
32 282
    public function __construct(ListData $listData)
33
    {
34 282
        $this->block = new ListItem($listData);
35 282
    }
36
37
    /**
38
     * @return ListItem
39
     */
40 282
    public function getBlock(): AbstractBlock
41
    {
42 282
        return $this->block;
43
    }
44
45 264
    public function isContainer(): bool
46
    {
47 264
        return true;
48
    }
49
50 258
    public function canContain(AbstractBlock $childBlock): bool
51
    {
52 258
        if ($this->hadBlankLine) {
53
            // We saw a blank line in this list item, that means the list block is loose.
54
            //
55
            // spec: if any of its constituent list items directly contain two block-level elements with a blank line
56
            // between them
57 84
            $parent = $this->block->parent();
58 84
            if ($parent instanceof ListBlock) {
59 84
                $parent->setTight(false);
60
            }
61
        }
62
63 258
        return true;
64
    }
65
66 222
    public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
67
    {
68 222
        if ($cursor->isBlank()) {
69 129
            if ($this->block->firstChild() === null) {
70
                // Blank line after empty list item
71 6
                return BlockContinue::none();
72
            }
73
74 123
            $activeBlock = $activeBlockParser->getBlock();
75
            // If the active block is a code block, blank lines in it should not affect if the list is tight.
76 123
            $this->hadBlankLine = $activeBlock instanceof Paragraph || $activeBlock instanceof ListItem;
77 123
            $cursor->advanceToNextNonSpaceOrTab();
78
79 123
            return BlockContinue::at($cursor);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \League\CommonMar...kContinue::at($cursor); (self) is incompatible with the return type declared by the interface League\CommonMark\Parser...rInterface::tryContinue of type League\CommonMark\Parser\Block\BlockContinue|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
80
        }
81
82 219
        $contentIndent = $this->block->getListData()->markerOffset + $this->getBlock()->getListData()->padding;
83 219
        if ($cursor->getIndent() >= $contentIndent) {
84 126
            $cursor->advanceBy($contentIndent, true);
85
86 126
            return BlockContinue::at($cursor);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \League\CommonMar...kContinue::at($cursor); (self) is incompatible with the return type declared by the interface League\CommonMark\Parser...rInterface::tryContinue of type League\CommonMark\Parser\Block\BlockContinue|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
87
        }
88
89 138
        return BlockContinue::none();
90
    }
91
}
92