Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
AFPTreeNode
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 toDebugString
n/a
0 / 0
n/a
0 / 0
1
 toDebugStringInner
n/a
0 / 0
n/a
0 / 0
5
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\Parser;
4
5use MediaWiki\Extension\AbuseFilter\Parser\Exception\InternalException;
6
7/**
8 * Represents a node of a parser tree.
9 */
10class AFPTreeNode {
11    // Each of the constants below represents a node corresponding to a level
12    // of the parser, from the top of the tree to the bottom.
13
14    // ENTRY is always one-element and thus does not have its own node.
15
16    // SEMICOLON is a many-children node, denoting that the nodes have to be
17    // evaluated in order and the last value has to be returned.
18    public const SEMICOLON = 'SEMICOLON';
19
20    // ASSIGNMENT (formerly known as SET) is a node which is responsible for
21    // assigning values to variables.  ASSIGNMENT is a (variable name [string],
22    // value [tree node]) tuple, INDEX_ASSIGNMENT (which is used to assign
23    // values at array offsets) is a (variable name [string], index [tree node],
24    // value [tree node]) tuple, and ARRAY_APPEND has the form of (variable name
25    // [string], value [tree node]).
26    public const ASSIGNMENT = 'ASSIGNMENT';
27    public const INDEX_ASSIGNMENT = 'INDEX_ASSIGNMENT';
28    public const ARRAY_APPEND = 'ARRAY_APPEND';
29
30    // CONDITIONAL represents both a ternary operator and an if-then-else-end
31    // construct.  The format is (condition, evaluated-if-true, evaluated-in-false).
32    // The first two are tree nodes, the last one can be a node, or null if there's no else.
33    public const CONDITIONAL = 'CONDITIONAL';
34
35    // LOGIC is a logic operator accepted by AFPData::boolOp.  The format is
36    // (operation, left operand, right operand).
37    public const LOGIC = 'LOGIC';
38
39    // COMPARE is a comparison operator accepted by AFPData::boolOp.  The format is
40    // (operation, left operand, right operand).
41    public const COMPARE = 'COMPARE';
42
43    // SUM_REL is either '+' or '-'.  The format is (operation, left operand,
44    // right operand).
45    public const SUM_REL = 'SUM_REL';
46
47    // MUL_REL is a multiplication-related operation accepted by AFPData::mulRel.
48    // The format is (operation, left operand, right operand).
49    public const MUL_REL = 'MUL_REL';
50
51    // POW is an exponentiation operator.  The format is (base, exponent).
52    public const POW = 'POW';
53
54    // BOOL_INVERT is a boolean inversion operator.  The format is (operand).
55    public const BOOL_INVERT = 'BOOL_INVERT';
56
57    // KEYWORD_OPERATOR is one of the binary keyword operators supported by the
58    // filter language.  The format is (keyword, left operand, right operand).
59    public const KEYWORD_OPERATOR = 'KEYWORD_OPERATOR';
60
61    // UNARY is either unary minus or unary plus.  The format is (operator, operand).
62    public const UNARY = 'UNARY';
63
64    // ARRAY_INDEX is an operation of accessing an array by an offset.  The format
65    // is (array, offset).
66    public const ARRAY_INDEX = 'ARRAY_INDEX';
67
68    // Since parenthesis only manipulate precedence of the operators, they are
69    // not explicitly represented in the tree.
70
71    // FUNCTION_CALL is an invocation of built-in function.  The format is a
72    // tuple where the first element is a function name, and all subsequent
73    // elements are the arguments.
74    public const FUNCTION_CALL = 'FUNCTION_CALL';
75
76    // ARRAY_DEFINITION is an array literal.  The $children field contains tree
77    // nodes for the values of each of the array element used.
78    public const ARRAY_DEFINITION = 'ARRAY_DEFINITION';
79
80    // ATOM is a node representing a literal.  The only element of $children is a
81    // token corresponding to the literal.
82    public const ATOM = 'ATOM';
83
84    // BINOP is a combination of LOGIC (^), COMPARE (<=, <, etc.),
85    // SUM_REL (+, -), MUL_REL (*, /, %), POW (**),
86    // KEYWORD_OPERATOR (like, rlike, etc.), and ARRAY_INDEX ([]).
87    // The format is (operator, operand, operand).
88    // Currently, it's only used in SyntaxChecker
89    // & and | which is in LOGIC is not in BINOP because it affects
90    // control flow.
91    public const BINOP = 'BINOP';
92
93    /** @var string Type of the node, one of the constants above */
94    public $type;
95    /**
96     * Parameters of the value. Typically it is an array of children nodes,
97     * which might be either strings (for parametrization of the node) or another
98     * node. In case of ATOM it's a parser token.
99     * @var AFPTreeNode[]|string[]|AFPToken
100     */
101    public $children;
102
103    /** @var int Position used for error reporting. */
104    public $position;
105
106    /**
107     * @param string $type
108     * @param (AFPTreeNode|null)[]|string[]|AFPToken $children
109     * @param int $position
110     */
111    public function __construct( $type, $children, $position ) {
112        $this->type = $type;
113        $this->children = $children;
114        $this->position = $position;
115    }
116
117    /**
118     * @return string
119     * @codeCoverageIgnore
120     */
121    public function toDebugString() {
122        return implode( "\n", $this->toDebugStringInner() );
123    }
124
125    /**
126     * @return array
127     * @codeCoverageIgnore
128     */
129    private function toDebugStringInner() {
130        if ( $this->type === self::ATOM ) {
131            return [ "ATOM({$this->children->type} {$this->children->value})" ];
132        }
133
134        $align = static function ( $line ) {
135            return '  ' . $line;
136        };
137
138        $lines = [ $this->type ];
139        // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach children is array here
140        foreach ( $this->children as $subnode ) {
141            if ( $subnode instanceof AFPTreeNode ) {
142                $sublines = array_map( $align, $subnode->toDebugStringInner() );
143            } elseif ( is_string( $subnode ) ) {
144                $sublines = [ "  {$subnode}" ];
145            } else {
146                throw new InternalException( "Each node parameter has to be either a node or a string" );
147            }
148
149            $lines = array_merge( $lines, $sublines );
150        }
151        return $lines;
152    }
153}