Passed
Push — master ( 4c697a...778310 )
by Kacper
02:53
created

Rule::getLanguage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2015, Some right reserved.
6
 *
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\Parser;
17
18
19
use Kadet\Highlighter\Language\Language;
20
use Kadet\Highlighter\Matcher\MatcherInterface;
21
22
/**
23
 * Class Rule
24
 *
25
 * @package Kadet\Highlighter\Parser
26
 *
27
 * @property Language              $language
28
 * @property Language              $inject
29
 * @property integer               $priority
30
 * @property string                $type
31
 * @property TokenFactoryInterface $factory
32
 */
33
class Rule
34
{
35
    const CONTEXT_IN        = 1;
36
    const CONTEXT_NOT_IN    = 2;
37
    const CONTEXT_IN_ONE_OF = 4;
38
    const CONTEXT_EXACTLY   = 8;
39
40
    private $_matcher;
41
    private $_context = [];
42
43
    private $_default = true;
44
45
    private $_options;
46
47
    /**
48
     * @param MatcherInterface|null $matcher
49
     * @param array                 $options
50
     */
51 13
    public function __construct(MatcherInterface $matcher = null, array $options = [])
52
    {
53 13
        $this->_matcher = $matcher;
54
55
        // Default options:
56 13
        $options = array_merge([
57 13
            'context'  => [],
58 13
            'priority' => 1,
59 13
            'language' => false,
60 13
            'factory'  => new TokenFactory('\Kadet\Highlighter\Parser\Token'),
61 13
        ], $options);
62
63 13
        $this->setContext($options['context']);
64 13
        $this->_options = $options;
65
66 13
        $this->factory->setRule($this);
67 13
    }
68
69 13
    public function setContext($rules)
70
    {
71 13
        $this->_context = [];
72 13
        foreach ($rules as $key => $rule) {
73 5
            if (!is_int($key)) {
74
                continue;
75
            }
76
77 5
            list($plain, $type) = $this->_getContextRule($rule);
78 5
            $this->_context[$plain] = $type;
79 13
        }
80 13
    }
81
82 5
    private function _getContextRule($rule)
83
    {
84
        $types = [
85 5
            '!' => self::CONTEXT_NOT_IN,
86 5
            '+' => self::CONTEXT_IN,
87 5
            '*' => self::CONTEXT_IN_ONE_OF,
88 5
            '@' => self::CONTEXT_EXACTLY,
89 5
        ];
90
91 5
        if (!isset($types[$rule[0]])) {
92 2
            return [$rule, self::CONTEXT_IN];
93
        }
94
95 4
        $type = 0;
96 4
        $pos = 0;
97 4
        foreach (str_split($rule) as $pos => $char) {
98 4
            if (!isset($types[$char])) {
99 3
                break;
100
            }
101
102 4
            if ($types[$char] == self::CONTEXT_IN_ONE_OF) {
103 1
                $this->_default = false;
104 1
            }
105
106 4
            $type |= $types[$char];
107 4
        }
108
109 4
        return [substr($rule, $pos), $type];
110
    }
111
112
    /**
113
     * @param $source
114
     *
115
     * @return Token[]
116
     */
117 3
    public function match($source)
118
    {
119 3
        return $this->_matcher !== null ? $this->_matcher->match($source, $this->factory) : [];
120
    }
121
122 6
    public function validateContext($context, array $additional = [])
123
    {
124 6
        $required = array_merge($additional, $this->_context);
125
126 6
        if (empty($required)) {
127 1
            return count($context) === 0;
128
        }
129
130 5
        $result = $this->_default;
131
132 5
        reset($required);
133 5
        while (list($rule, $type) = each($required)) {
134 5
            $matched = !($type & self::CONTEXT_EXACTLY) ?
135
                count(array_filter($context, function ($a) use ($rule) {
136 5
                    return $a === $rule || fnmatch($rule . '.*', $a);
137 5
                })) > 0 :
138 5
                in_array($rule, $context, true);
139
140 5
            if ($type & self::CONTEXT_NOT_IN) {
141 2
                if ($matched) {
142 1
                    return false;
143
                }
144 2
                $result = true;
145 5
            } elseif ($type & self::CONTEXT_IN) {
146 2
                if (!$matched) {
147 2
                    return false;
148
                }
149 2
                $result = true;
150
151 2
                $this->_unsetUnnecessaryRules($rule, $required);
152 3
            } elseif ($type & self::CONTEXT_IN_ONE_OF) {
153 1
                if ($matched) {
154 1
                    $result = true;
155 1
                    $this->_unsetUnnecessaryRules($rule, $required);
156 1
                }
157 1
            }
158 5
        }
159
160 5
        return $result;
161
    }
162
163 3
    private function _unsetUnnecessaryRules($rule, &$required)
164
    {
165 3
        if (strpos($rule, '.') !== false) {
166 1
            foreach (array_filter(array_keys($this->_context), function ($key) use ($rule) {
167 1
                return fnmatch($key . '.*', $rule);
168 1
            }) as $remove) {
169 1
                unset($required[$remove]);
170 1
            }
171 1
        }
172 3
    }
173
174 13
    public function __get($option)
175
    {
176 13
        return $this->_options[$option];
177
    }
178
179
    public function __set($option, $value)
180
    {
181
        return $this->_options[$option] = $value;
182
    }
183
}