Passed
Push — master ( 1d0ea2...0f9d68 )
by Kacper
05:34
created

Php   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Test Coverage

Coverage 77.94%

Importance

Changes 4
Bugs 3 Features 0
Metric Value
eloc 88
c 4
b 3
f 0
dl 0
loc 146
ccs 53
cts 68
cp 0.7794
rs 10
wmc 6

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getMetadata() 0 7 1
A getEnds() 0 17 2
A getIdentifier() 0 3 1
B setupRules() 0 105 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Highlighter
7
 *
8
 * Copyright (C) 2016, Some right reserved.
9
 * @author Kacper "Kadet" Donat <[email protected]>
10
 *
11
 * Contact with author:
12
 * Xmpp: [email protected]
13
 * E-mail: [email protected]
14
 *
15
 * From Kadet with love.
16
 */
17
18
namespace Kadet\Highlighter\Language;
19
20
use Kadet\Highlighter\Matcher\CommentMatcher;
21
use Kadet\Highlighter\Matcher\DelegateRegexMatcher;
22
use Kadet\Highlighter\Matcher\RegexMatcher;
23
use Kadet\Highlighter\Matcher\WordMatcher;
24
use Kadet\Highlighter\Parser\CloseRule;
25
use Kadet\Highlighter\Parser\OpenRule;
26
use Kadet\Highlighter\Parser\Rule;
27
use Kadet\Highlighter\Parser\Token\LanguageToken;
28
use Kadet\Highlighter\Parser\Token\Token;
29
use Kadet\Highlighter\Parser\TokenFactory;
30
use Kadet\Highlighter\Parser\TokenFactoryInterface;
31
32
class Php extends GreedyLanguage
33
{
34
    
35
    /**
36
     * Tokenization rules
37
     */
38 1
    public function setupRules()
39
    {
40 1
        $this->rules->addMany([
41 1
            'string' => CommonFeatures::strings(['single' => '\'', 'double' => '"'], [
42 1
                'context' => ['!operator.escape', '!comment', '!string', '!expression'],
43
            ]),
44
45 1
            'string.heredoc' => new Rule(new RegexMatcher('/<<<\s*(\w+)\R(?P<string>.*?)\R\1;/sm', ['string' => Token::NAME, 0 => 'keyword.heredoc']), ['context' => ['!comment']]),
46 1
            'string.nowdoc'  => new Rule(new RegexMatcher('/<<<\s*\'(\w+)\'\R(?P<string>.*?)\R\1;/sm', ['string' => Token::NAME, 0 => 'keyword.nowdoc']), ['context' => ['!comment']]),
47
48 1
            'variable' => new Rule(new RegexMatcher('/(?:[^\\\]|^)(\$[a-z_]\w*)/i'), [
49 1
                'context' => ['*comment.docblock', '!string.nowdoc', '!string.single', '!comment']
50
            ]),
51 1
            'variable.property' => new Rule(new RegexMatcher('/(?=(?:\w|\)|\])\s*->([a-z_]\w*))/i'), [
52 1
                'priority' => -2,
53
                'context' => ['*comment.docblock', '!string.nowdoc', '!string.single', '!comment']
54
            ]),
55
56 1
            'symbol.function' => new Rule(new RegexMatcher('/function\s+([a-z_]\w+)\s*\(/i')),
57
            'symbol.class'    => [
58 1
                new Rule(new RegexMatcher('/(?:class|new|use|extends)\s+([\w\\\]+)/i')),
59 1
                new Rule(new RegexMatcher('/([\w\\\]+)::/i')),
60 1
                new Rule(new RegexMatcher('/@(?:var|property(?:-read|-write)?)(?:\s+|\s+\$\w+\s+)([^$][\w\\\]+)/i'), ['context' => ['comment.docblock']]),
61
            ],
62
            
63 1
            'expression.in-string' => new Rule(new RegexMatcher('/(?=(\{\$((?>[^${}]+|(?1))+)\}))/x'), [
64 1
                'context' => ['string'],
65 1
                'factory' => new TokenFactory(LanguageToken::class),
66 1
                'inject'  => $this
67
            ]),
68
69
            'symbol.class.interface' => [
70 1
                new Rule(new RegexMatcher('/interface\s+([\w\\\]+)/i')),
71 1
                new Rule(new DelegateRegexMatcher(
72 1
                    '/implements\s+((?:[\w\\\]+)(?:,\s*([\w\\\]+))+)/i',
73 1
                    function ($match, TokenFactoryInterface $factory) {
74
                        foreach (preg_split('/,\s*/', $match[1][0], 0, PREG_SPLIT_OFFSET_CAPTURE) as $interface) {
75
                            yield $factory->create(Token::NAME, [
76
                                'pos' => $match[1][1] + $interface[1],
77
                                'length' => strlen($interface[0])]);
78
                        }
79 1
                    }
80
                )),
81
            ],
82
83
            'symbol.namespace' => [
84
                /*new Rule(new RegexMatcher('/(\\\{0,2}(?:\w+\\\{1,2})+)\w+/i'), [
85
                    'context' => ['*symbol', '*none']
86
                ]),*/
87
88 1
                new Rule(new RegexMatcher('/namespace\s*(\\\{0,2}(?:\w+\\\{1,2})+\w+);/i'), [
89 1
                    'context' => ['*symbol', '*none']
90
                ]),
91
            ],
92
93
            'operator.escape' => [
94 1
                new Rule(new RegexMatcher('/(\\\(?:x[0-9a-fA-F]{1,2}|u\{[0-9a-fA-F]{1,6}\}|[0-7]{1,3}|[^\'\\\]))/i'), [
95 1
                    'context' => ['string.double', '!operator.escape']
96
                ]),
97 1
                new Rule(new RegexMatcher('/(\\\[\'\\\])/i'), [
98 1
                    'context' => ['string', '!operator.escape']
99
                ]),
100
            ],
101
102 1
            'comment' => new Rule(new CommentMatcher(['//', '#'], [
103 1
                '$.docblock' => ['/**', '*/'],
104
                ['/*', '*/']
105 1
            ]), ['priority' => 4]),
106
107 1
            'symbol.annotation' => new Rule(new RegexMatcher('/[\s]+(@[\w-]+)/i'), [
108 1
                'context' => ['comment.docblock']
109
            ]),
110
111 1
            'call' => new Rule(new RegexMatcher('/([a-z_]\w*)\s*\(/i'), ['priority' => -1]),
112
113 1
            'constant' => new Rule(new WordMatcher(array_merge([
114 1
                '__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__',
115
                '__LINE__', '__METHOD__', '__NAMESPACE__', '__TRAIT__',
116 1
            ], array_keys(get_defined_constants(true)["Core"]))), ['priority' => -2]),
117 1
            'constant.static' => new Rule(new RegexMatcher('/(?:[\w\\\]+::|const\s+)(\w+)/i'), ['priority' => -2]),
118
119 1
            'keyword' => new Rule(new WordMatcher([
120 1
                '__halt_compiler', 'abstract', 'and', 'array',
121
                'as', 'break', 'callable', 'case', 'catch',
122
                'class', 'clone', 'const', 'continue', 'declare',
123
                'default', 'die', 'do', 'echo', 'else', 'elseif',
124
                'empty', 'enddeclare', 'endfor', 'endforeach', 'endif',
125
                'endswitch', 'endwhile', 'eval', 'exit', 'extends',
126
                'final', 'finally', 'for', 'foreach', 'function',
127
                'global', 'goto', 'if', 'implements', 'include', 'include_once',
128
                'instanceof', 'insteadof', 'interface', 'isset', 'list',
129
                'namespace', 'new', 'or', 'print', 'private', 'protected',
130
                'public', 'require', 'require_once', 'return', 'static',
131
                'switch', 'throw', 'trait', 'try', 'unset', 'parent', 'self',
132
                'use', 'var', 'while', 'xor', 'yield'
133 1
            ]), ['context' => ['!string', '!variable', '!comment']]),
134
135 1
            'keyword.cast' => new Rule(
136 1
                new RegexMatcher('/(\((?:int|integer|bool|boolean|float|double|real|string|array|object|unset)\))/')
137
            ),
138
139 1
            'delimiter' => new Rule(new RegexMatcher('/(<\?php|<\?=|\?>)/')),
140 1
            'number'    => new Rule(new RegexMatcher('/(-?(?:0[0-7]+|0[xX][0-9a-fA-F]+|0b[01]+|\d+))/')),
141
142 1
            'operator.punctuation' => new Rule(new WordMatcher([',', ';'], ['separated' => false]), ['priority' => 0]),
143
        ]);
144 1
    }
145
146
    /** {@inheritdoc} */
147 5
    public function getEnds($embedded = false)
148
    {
149 5
        return $embedded ? [
150
            new OpenRule(new RegexMatcher('/(<\?php|<\?=)/si'), [
151
                'factory'  => new TokenFactory(LanguageToken::class),
152
                'priority' => 1000,
153
                'context'  => ['*'],
154
                'inject'   => $this,
155
                'language' => null
156
            ]),
157
            new CloseRule(new RegexMatcher('/(\?>|$)/'), [
158
                'context'  => ['!string', '!comment'],
159
                'priority' => 1000,
160
                'factory'  => new TokenFactory(LanguageToken::class),
161
                'language' => $this
162
            ])
163 5
        ] : parent::getEnds(false);
164
    }
165
166 6
    public function getIdentifier()
167
    {
168 6
        return 'php';
169
    }
170
171
    public static function getMetadata()
172
    {
173
        return [
174
            'name'      => ['php'],
175
            'mime'      => ['text/x-php', 'application/x-php'],
176
            'extension' => ['*.php', '*.phtml', '*.inc', '*.php?'],
177
            'injectable' => true
178
        ];
179
    }
180
}
181