Completed
Push — master ( 6465ca...dc4b6d )
by Kacper
08:36
created

Php   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 78.85%

Importance

Changes 3
Bugs 3 Features 0
Metric Value
dl 0
loc 150
ccs 82
cts 104
cp 0.7885
rs 10
c 3
b 3
f 0
wmc 6
lcom 1
cbo 12

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getEnds() 0 18 2
A getIdentifier() 0 4 1
A getMetadata() 0 9 1
B setupRules() 0 108 2
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2016, Some right reserved.
6
 * @author Kacper "Kadet" Donat <[email protected]>
7
 *
8
 * Contact with author:
9
 * Xmpp: [email protected]
10
 * E-mail: [email protected]
11
 *
12
 * From Kadet with love.
13
 */
14
15
namespace Kadet\Highlighter\Language;
16
17
use Kadet\Highlighter\Matcher\CommentMatcher;
18
use Kadet\Highlighter\Matcher\DelegateRegexMatcher;
19
use Kadet\Highlighter\Matcher\RegexMatcher;
20
use Kadet\Highlighter\Matcher\WordMatcher;
21
use Kadet\Highlighter\Parser\CloseRule;
22
use Kadet\Highlighter\Parser\Token\LanguageToken;
23
use Kadet\Highlighter\Parser\Rule;
24
use Kadet\Highlighter\Parser\OpenRule;
25
use Kadet\Highlighter\Parser\Token\Token;
26
use Kadet\Highlighter\Parser\TokenFactory;
27
use Kadet\Highlighter\Parser\TokenFactoryInterface;
28
29
class Php extends GreedyLanguage
30
{
31
    
32
    /**
33
     * Tokenization rules
34
     */
35 1
    public function setupRules()
36
    {
37 1
        $this->rules->addMany([
38 1
            'string' => CommonFeatures::strings(['single' => '\'', 'double' => '"'], [
39 1
                'context' => ['!operator.escape', '!comment', '!string', '!expression'],
40 1
            ]),
41
42 1
            'string.heredoc' => new Rule(new RegexMatcher('/<<<\s*(\w+)\R(?P<string>.*?)\R\1;/sm', ['string' => Token::NAME, 0 => 'keyword.heredoc']), ['context' => ['!comment']]),
43 1
            'string.nowdoc'  => new Rule(new RegexMatcher('/<<<\s*\'(\w+)\'\R(?P<string>.*?)\R\1;/sm', ['string' => Token::NAME, 0 => 'keyword.nowdoc']), ['context' => ['!comment']]),
44
45 1
            'variable' => new Rule(new RegexMatcher('/(?:[^\\\]|^)(\$[a-z_]\w*)/i'), [
46 1
                'context' => ['*comment.docblock', '!string.nowdoc', '!string.single', '!comment']
47 1
            ]),
48 1
            'variable.property' => new Rule(new RegexMatcher('/(?=(?:\w|\)|\])\s*->([a-z_]\w*))/i'), [
49 1
                'priority' => -2,
50 1
                'context' => ['*comment.docblock', '!string.nowdoc', '!string.single', '!comment']
51 1
            ]),
52
53 1
            'symbol.function' => new Rule(new RegexMatcher('/function\s+([a-z_]\w+)\s*\(/i')),
54
            'symbol.class'    => [
55 1
                new Rule(new RegexMatcher('/(?:class|new|use|extends)\s+([\w\\\]+)/i')),
56 1
                new Rule(new RegexMatcher('/([\w\\\]+)::/i')),
57 1
                new Rule(new RegexMatcher('/@(?:var|property(?:-read|-write)?)(?:\s+|\s+\$\w+\s+)([^$][\w\\\]+)/i'), ['context' => ['comment.docblock']]),
58 1
            ],
59
            
60 1
            'expression.in-string' => new Rule(new RegexMatcher('/(?=(\{\$((?>[^${}]+|(?1))+)\}))/x'), [
61 1
                'context' => ['string'],
62 1
                'factory' => new TokenFactory(LanguageToken::class),
63
                'inject'  => $this
64 1
            ]),
65
66
            'symbol.class.interface' => [
67 1
                new Rule(new RegexMatcher('/interface\s+([\w\\\]+)/i')),
68 1
                new Rule(new DelegateRegexMatcher(
69 1
                    '/implements\s+((?:[\w\\\]+)(?:,\s*([\w\\\]+))+)/i',
70 1
                    function($match, TokenFactoryInterface $factory) {
71
                        foreach (preg_split('/,\s*/', $match[1][0], 0, PREG_SPLIT_OFFSET_CAPTURE) as $interface) {
72
                            yield $factory->create(Token::NAME, [
73 1
                                'pos' => $match[1][1] + $interface[1],
74
                                'length' => strlen($interface[0])]
75
                            );
76
                        }
77
                    }
78 1
                )),
79 1
            ],
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 1
                ]),
89 1
            ],
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 1
                ]),
95 1
                new Rule(new RegexMatcher('/(\\\[\'\\\])/i'), [
96 1
                    'context' => ['string', '!operator.escape']
97 1
                ]),
98 1
            ],
99
100 1
            'comment' => new Rule(new CommentMatcher(['//', '#'], [
101 1
                '$.docblock' => ['/**', '*/'],
102 1
                ['/*', '*/']
103 1
            ]), ['priority' => 4]),
104
105 1
            'symbol.annotation' => new Rule(new RegexMatcher('/[\s]+(@[\w-]+)/i'), [
106 1
                'context' => ['comment.docblock']
107 1
            ]),
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 1
                '__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 1
                'as', 'break', 'callable', 'case', 'catch',
120 1
                'class', 'clone', 'const', 'continue', 'declare',
121 1
                'default', 'die', 'do', 'echo', 'else', 'elseif',
122 1
                'empty', 'enddeclare', 'endfor', 'endforeach', 'endif',
123 1
                'endswitch', 'endwhile', 'eval', 'exit', 'extends',
124 1
                'final', 'finally', 'for', 'foreach', 'function',
125 1
                'global', 'goto', 'if', 'implements', 'include', 'include_once',
126 1
                'instanceof', 'insteadof', 'interface', 'isset', 'list',
127 1
                'namespace', 'new', 'or', 'print', 'private', 'protected',
128 1
                'public', 'require', 'require_once', 'return', 'static',
129 1
                'switch', 'throw', 'trait', 'try', 'unset', 'parent', 'self',
130 1
                '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 1
            ),
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 1
        ]);
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