Completed
Push — master ( 45e66e...a2014d )
by Kacper
03:25
created

Validator::_validate()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
eloc 19
nc 16
nop 3
dl 0
loc 30
rs 4.909
c 0
b 0
f 0
ccs 23
cts 23
cp 1
crap 9
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 47
    public function __construct(array $rules = []) {
38 47
        $this->setRules($rules);
39 47
    }
40
41 30
    public function validate(Context $context, $additional = []) {
42 30
        return $this->_validate($context->stack, $additional + $this->_rules);
43
    }
44
45 45
    public function setRules($rules)
46
    {
47 45
        if(empty($rules)) {
48 35
            $this->_rules = [ 'none' => Validator::CONTEXT_IN_ONE_OF ];
49 35
        } else {
50 12
            foreach ($rules as $key => $rule) {
51 12
                list($plain, $type)   = $this->_parse($rule);
52 12
                $this->_rules[$plain] = $type;
53 12
            }
54
        }
55 45
    }
56
57 28
    private function _clean($rule, &$required)
58
    {
59 28
        if (strpos($rule, '.') !== false) {
60
            foreach (array_filter(array_keys($required), function ($key) use ($rule) {
61 10
                return fnmatch($key . '.*', $rule);
62 10
            }) as $remove) {
63 9
                unset($required[$remove]);
64 10
            }
65 10
        }
66 28
    }
67
68 32
    protected function _validate($context, $rules, $result = false) {
69 32
        if(empty($context)) {
70 26
            $context = ['none'];
71 26
        }
72
73 32
        foreach($rules as $rule => &$type) {
74 30
            $matched = $this->_matches($context, $rule, $type);
75
76 30
            if ($type & Validator::CONTEXT_NOT_IN) {
77 14
                if ($matched) {
78 10
                    return false;
79
                }
80 13
                $result = true;
81 30
            } elseif ($type & Validator::CONTEXT_IN) {
82 16
                if (!$matched) {
83 12
                    return false;
84
                }
85 15
                $result = true;
86
87 15
                $this->_clean($rule, $rules);
88 28
            } elseif ($type & Validator::CONTEXT_IN_ONE_OF) {
89 22
                if ($matched) {
90 21
                    $result = true;
91 21
                    $this->_clean($rule, $rules);
92 21
                }
93 22
            }
94 32
        }
95
96 31
        return $result;
97
    }
98
99 12
    private function _parse($rule)
100
    {
101
        $types = [
102 12
            '!' => Validator::CONTEXT_NOT_IN,
103 12
            '+' => Validator::CONTEXT_IN,
104 12
            '*' => Validator::CONTEXT_IN_ONE_OF,
105 12
            '@' => Validator::CONTEXT_EXACTLY,
106
//            '^' => Validator::CONTEXT_ON_TOP,
107
            '~' => Validator::CONTEXT_REGEX
108 12
        ];
109
110 12
        if (!isset($types[$rule[0]])) {
111 4
            return [$rule, Validator::CONTEXT_IN];
112
        }
113
114 10
        $type = 0;
115 10
        $pos  = 0;
116 10
        foreach (str_split($rule) as $pos => $char) {
117 10
            if (!isset($types[$char])) {
118 10
                break;
119
            }
120
121 10
            $type |= $types[$char];
122 10
        }
123
124 10
        $rule = substr($rule, $pos);
125
126 10
        if($type & self::CONTEXT_REGEX) {
127 2
            $rule = "/^$rule(\\.\\w+)?/i";
128 2
        }
129
130 10
        return [$rule, $type];
131
    }
132
133 30
    private function _matches($context, $rule, $type) {
134 30
        if($type & self::CONTEXT_EXACTLY) {
135 1
            return in_array($rule, $context, true);
136 29
        } 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 27
            if(in_array($rule, $context, true)) {
145 27
                return true;
146
            }
147
148 18
            foreach($context as $item) {
149 18
                if(fnmatch("$rule.*", $item)) {
150 7
                    return true;
151
                }
152 18
            }
153 18
            return false;
154
        }
155
    }
156
157 37
    public static function everywhere()
158
    {
159 37
        static $validator;
160 37
        if (!$validator) {
161 21
            $validator = new DelegateValidator(function () {
162 21
                return true;
163
            });
164
        }
165
166 37
        return $validator;
167
    }
168
}
169