Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.68% covered (success)
98.68%
75 / 76
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
InRow
98.68% covered (success)
98.68%
75 / 76
75.00% covered (warning)
75.00%
3 / 4
32
0.00% covered (danger)
0.00%
0 / 1
 characters
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 startTag
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
12
 endTag
97.78% covered (success)
97.78%
44 / 45
0.00% covered (danger)
0.00%
0 / 1
18
 endDocument
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Wikimedia\RemexHtml\TreeBuilder;
4
5use Wikimedia\RemexHtml\Tokenizer\Attributes;
6
7/**
8 * The "in row" insertion mode
9 */
10class InRow extends InsertionMode {
11    private static $tableRowContext = [
12        'tr' => true,
13        'template' => true,
14        'html' => true,
15    ];
16
17    public function characters( $text, $start, $length, $sourceStart, $sourceLength ) {
18        $this->dispatcher->inTable->characters( $text, $start, $length,
19            $sourceStart, $sourceLength );
20    }
21
22    public function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
23        $builder = $this->builder;
24        $stack = $builder->stack;
25        $dispatcher = $this->dispatcher;
26
27        switch ( $name ) {
28            case 'th':
29            case 'td':
30                $builder->clearStackBack( self::$tableRowContext, $sourceStart );
31                $builder->insertElement( $name, $attrs, false, $sourceStart, $sourceLength );
32                $dispatcher->switchMode( Dispatcher::IN_CELL );
33                $builder->afe->insertMarker();
34                break;
35
36            case 'caption':
37            case 'col':
38            case 'colgroup':
39            case 'tbody':
40            case 'tfoot':
41            case 'thead':
42            case 'tr':
43                if ( !$stack->isInTableScope( 'tr' ) ) {
44                    $builder->error( "<$name> should close the tr but it is not in scope",
45                        $sourceStart );
46                    // Ignore
47                    return;
48                }
49                $builder->clearStackBack( self::$tableRowContext, $sourceStart );
50                $builder->pop( $sourceStart, 0 );
51                $dispatcher->switchMode( Dispatcher::IN_TABLE_BODY )
52                    ->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength );
53                break;
54
55            default:
56                $dispatcher->inTable->startTag( $name, $attrs, $selfClose,
57                    $sourceStart, $sourceLength );
58        }
59    }
60
61    public function endTag( $name, $sourceStart, $sourceLength ) {
62        $builder = $this->builder;
63        $stack = $builder->stack;
64        $dispatcher = $this->dispatcher;
65
66        switch ( $name ) {
67            case 'tr':
68                if ( !$stack->isInTableScope( 'tr' ) ) {
69                    $builder->error( '</tr> found but no tr element in scope', $sourceStart );
70                    // Ignore
71                    return;
72                }
73                $builder->clearStackBack( self::$tableRowContext, $sourceStart );
74                $builder->pop( $sourceStart, $sourceLength );
75                $dispatcher->switchMode( Dispatcher::IN_TABLE_BODY );
76                break;
77
78            case 'table':
79                if ( !$stack->isInTableScope( 'tr' ) ) {
80                    $builder->error( "</table> should close the tr but it is not in scope",
81                        $sourceStart );
82                    // Ignore
83                    return;
84                }
85                $builder->clearStackBack( self::$tableRowContext, $sourceStart );
86                $builder->pop( $sourceStart, 0 );
87                $dispatcher->switchMode( Dispatcher::IN_TABLE_BODY )
88                    ->endTag( $name, $sourceStart, $sourceLength );
89                break;
90
91            case 'tbody':
92            case 'tfoot':
93            case 'thead':
94                if ( !$stack->isInTableScope( $name ) ) {
95                    $builder->error( "</$name> encountered but there is no $name element in scope",
96                        $sourceStart );
97                    return;
98                }
99                if ( !$stack->isInTableScope( 'tr' ) ) {
100                    return;
101                }
102                $builder->clearStackBack( self::$tableRowContext, $sourceStart );
103                $builder->pop( $sourceStart, 0 );
104                $dispatcher->switchMode( Dispatcher::IN_TABLE_BODY )
105                    ->endTag( $name, $sourceStart, $sourceLength );
106                break;
107
108            case 'body':
109            case 'caption':
110            case 'col':
111            case 'colgroup':
112            case 'html':
113            case 'td':
114            case 'th':
115                $builder->error( "</$name> encountered in row mode, ignoring", $sourceStart );
116                return;
117
118            default:
119                $dispatcher->inTable->endTag( $name, $sourceStart, $sourceLength );
120        }
121    }
122
123    public function endDocument( $pos ) {
124        $this->dispatcher->inTable->endDocument( $pos );
125    }
126}