Passed
Branch master (f3f280)
by Maciej
03:18
created

Php   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Test Coverage

Coverage 77.61%

Importance

Changes 3
Bugs 3 Features 0
Metric Value
eloc 88
dl 0
loc 146
ccs 52
cts 67
cp 0.7761
rs 10
c 3
b 3
f 0
wmc 6

4 Methods

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