Passed
Branch 0.8-dev (82421f)
by Kacper
02:45
created

Validator   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
c 6
b 0
f 0
dl 0
loc 148
wmc 32
lcom 1
cbo 2
rs 9.6
ccs 89
cts 89
cp 1

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A validate() 0 3 1
A setRules() 0 11 3
A _clean() 0 10 3
D _validate() 0 30 9
B _parse() 0 33 5
C _matches() 0 23 8
A everywhere() 0 11 2
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2016, 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\Validator;
17
18
19
use Kadet\Highlighter\Parser\Context;
20
21
class Validator
22
{
23
    const CONTEXT_NOT_IN    = 2;
24
    const CONTEXT_IN        = 1;
25
    const CONTEXT_IN_ONE_OF = 4;
26
    const CONTEXT_EXACTLY   = 8;
27
    const CONTEXT_ON_TOP    = 16;
28
    const CONTEXT_REGEX     = 32;
29
30
    private $_rules = [];
31
32
    /**
33
     * Validator constructor.
34
     *
35
     * @param array $rules
36
     */
37 32
    public function __construct(array $rules = []) {
38 32
        $this->setRules($rules);
39 32
    }
40
41 19
    public function validate(Context $context, $additional = []) {
42 19
        return $this->_validate($context->stack, $additional + $this->_rules);
43
    }
44
45 31
    public function setRules($rules)
46
    {
47 31
        if(empty($rules)) {
48 23
            $this->_rules = [ 'none' => Validator::CONTEXT_IN_ONE_OF ];
49 23
        } else {
50 9
            foreach ($rules as $key => $rule) {
51 9
                list($plain, $type)     = $this->_parse($rule);
52 9
                $this->_rules[$plain] = $type;
53 9
            }
54
        }
55 31
    }
56
57 17
    private function _clean($rule, &$required)
58
    {
59 17
        if (strpos($rule, '.') !== false) {
60
            foreach (array_filter(array_keys($required), function ($key) use ($rule) {
61 2
                return fnmatch($key . '.*', $rule);
62 2
            }) as $remove) {
63 1
                unset($required[$remove]);
64 2
            }
65 2
        }
66 17
    }
67
68 22
    protected function _validate($context, $rules, $result = false) {
69 22
        if(empty($context)) {
70 16
            $context = ['none'];
71 16
        }
72
73 22
        while(list($rule, $type) = each($rules)) {
74 19
            $matched = $this->_matches($context, $rule, $type);
75
76 19
            if ($type & Validator::CONTEXT_NOT_IN) {
77 3
                if ($matched) {
78 3
                    return false;
79
                }
80 2
                $result = true;
81 19
            } elseif ($type & Validator::CONTEXT_IN) {
82 6
                if (!$matched) {
83 4
                    return false;
84
                }
85 6
                $result = true;
86
87 6
                $this->_clean($rule, $rules);
88 17
            } elseif ($type & Validator::CONTEXT_IN_ONE_OF) {
89 12
                if ($matched) {
90 12
                    $result = true;
91 12
                    $this->_clean($rule, $rules);
92 12
                }
93 12
            }
94 19
        }
95
96 21
        return $result;
97
    }
98
99 9
    private function _parse($rule)
100
    {
101
        $types = [
102 9
            '!' => Validator::CONTEXT_NOT_IN,
103 9
            '+' => Validator::CONTEXT_IN,
104 9
            '*' => Validator::CONTEXT_IN_ONE_OF,
105 9
            '@' => Validator::CONTEXT_EXACTLY,
106
//            '^' => Validator::CONTEXT_ON_TOP,
107
            '~' => Validator::CONTEXT_REGEX
108 9
        ];
109
110 9
        if (!isset($types[$rule[0]])) {
111 3
            return [$rule, Validator::CONTEXT_IN];
112
        }
113
114 7
        $type = 0;
115 7
        $pos  = 0;
116 7
        foreach (str_split($rule) as $pos => $char) {
117 7
            if (!isset($types[$char])) {
118 7
                break;
119
            }
120
121 7
            $type |= $types[$char];
122 7
        }
123
124 7
        $rule = substr($rule, $pos);
125
126 7
        if($type & self::CONTEXT_REGEX) {
127 2
            $rule = "/^$rule(\\.\\w+)?/i";
128 2
        }
129
130 7
        return [$rule, $type];
131
    }
132
133 19
    private function _matches($context, $rule, $type) {
134 19
        if($type & self::CONTEXT_EXACTLY) {
135 1
            return in_array($rule, $context, true);
136 18
        } elseif($type & self::CONTEXT_REGEX) {
137 2
            foreach($context as $item) {
138 2
                if(preg_match($rule, $item)) {
139 2
                    return true;
140
                }
141 2
            }
142 2
            return false;
143
        } else {
144 16
            if(in_array($rule, $context, true)) {
145 16
                return true;
146
            }
147
148 7
            foreach($context as $item) {
149 7
                if(fnmatch("$rule.*", $item)) {
150 1
                    return true;
151
                }
152 7
            }
153 7
            return false;
154
        }
155
    }
156
157 17
    public static function everywhere()
158
    {
159 17
        static $validator;
160 17
        if (!$validator) {
161 12
            $validator = new DelegateValidator(function () {
162 12
                return true;
163 1
            });
164 1
        }
165
166 17
        return $validator;
167
    }
168
}
169