Completed
Branch 0.8-dev (c4d6ef)
by Kacper
02:50
created

Validator::validate()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

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