Passed
Push — master ( 3a32ec...04e24a )
by Martijn
02:33
created

AbstractRuleset::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Vanderlee\Comprehend\Builder;
4
5
use Vanderlee\Comprehend\Core\ArgumentsTrait;
6
use Vanderlee\Comprehend\Core\Context;
7
use Vanderlee\Comprehend\Match\Success;
8
use Vanderlee\Comprehend\Parser\Parser;
9
10
/**
11
 * Description of abstract Ruleset
12
 *
13
 * @author Martijn
14
 */
15
abstract class AbstractRuleset extends Parser
16
{
17
    use ArgumentsTrait;
18
19
    /**
20
     * Constant to specify default parser for this ruleset, which is called when Ruleset is used as a Parser.
21
     */
22
    const ROOT = 'ROOT';
23
24
    /**
25
     * Name of this ruleset. Intended for use with the standard library rulesets
26
     *
27
     * @var string
28
     */
29
    protected static $name = null;
30
31
    /**
32
     * Parser to use as the default parser used when calling this Ruleset as a Parser.
33
     *
34
     * @var null|Parser
35
     */
36
    private $parser = null;
37
38
    /**
39
     * List of reserved parser names. These are used as methods instead.
40
     *
41
     * @var string[]
42
     */
43
    private static $reserved = [
44
        'define',
45
        'defined',
46
        'undefine'
47
    ];
48
49
    /**
50
     * @var Definition[]|Parser[]|callable[]
51
     */
52
    private static $staticRules = [];
53
54
    /**
55
     * @var Definition[]|Parser[]|callable[]
56
     */
57
    protected $instanceRules = [];
58
59
    /**
60
     * Ruleset constructor, defining initial instance rules.
61
     *
62
     * @param string|array $key Either a key of an initial rule to define or a [ key : definition ] array
63
     * @param null|string $definition Definition of the initial rule or `null` if `$key` is an array
64
     * @param null|string $name optional identifier for this ruleset
65
     */
66 28
    public function __construct($key = null, $definition = null, $name = null)
67
    {
68 28
        if ($key !== null) {
69 7
            self::defineRule($this->instanceRules, $key, $definition);
70
        }
71
72 28
        self::$name = $name;
73 28
    }
74
75
    /**
76
     * Set a definition
77
     *
78
     * @param Definition[]|Parser[]|callable[] $rules
79
     * @param string $key
80
     * @param Definition|Parser|callable $definition
81
     * @throws \Exception
82
     */
83 30
    protected static function setRule(&$rules, $key, $definition)
84
    {
85 30
        if (isset($rules[$key])) {
86 9
            $rule = &$rules[$key];
87 9
            if ($rule instanceof Definition) {
88
                switch (true) {
89 8
                    case $definition instanceof Definition:
90 1
                        $rule->generator  = $definition->generator;
91 1
                        $rule->validators = $definition->validators;
92 1
                        return;
93
94 7
                    case $definition instanceof Parser:
95 3
                        $rule->generator = $definition;
96 3
                        return;
97
98 5
                    case is_callable($definition):
99 2
                        $rule->generator = $definition;
100 2
                        return;
101
102 3
                    case is_array($definition) || is_string($definition) || is_int($definition):
103
                        // S-C-S Array syntax
104 2
                        $rule->generator = self::getArgument($definition);
105 2
                        return;
106
                }
107
108 1
                throw new \RuntimeException(sprintf('Cannot redefine `%2$s` using definition type `%1$s`',
109 1
                    is_object($definition)
110
                        ? get_class($definition)
111 1
                        : gettype($definition), $key));
112
            }
113
        }
114
115 23
        $rules[$key] = $definition;
116 23
    }
117
118 13
    protected static function defineRule(&$rules, $names, $definition = null)
119
    {
120 13
        if (is_array($names)) {
121 2
            foreach ($names as $key => $value) {
122 2
                self::defineRule($rules, $key, $value);
123
            }
124 2
            return;
125
        }
126
127 13
        if (in_array($names, self::$reserved)) {
128 1
            throw new \RuntimeException("Cannot define reserved name `{$names}`");
129
        }
130
131 12
        self::setRule($rules, $names, $definition);
132 12
    }
133
134
    /**
135
     * Check if a specified name is defined in the rules map provided
136
     *
137
     * @param $rules
138
     * @param $names
139
     * @return bool
140
     */
141 3
    protected static function isRuleDefined(&$rules, $names)
142
    {
143 3
        foreach ((array)$names as $key) {
144 3
            if (!isset($rules[$key])) {
145 3
                return false;
146
            }
147
        }
148
149 3
        return true;
150
    }
151
152 2
    protected static function undefineRule(&$rules, $names)
153
    {
154 2
        foreach ((array)$names as $key) {
155 2
            unset($rules[$key]);
156
        }
157 2
    }
158
159
    /**
160
     * Create a new instance of a definition
161
     *
162
     * @param $rules
163
     * @param $key
164
     * @param array $arguments
165
     * @return Implementation|Parser
166
     */
167 31
    protected static function call(&$rules, $key, $arguments = [])
168
    {
169 31
        if (!isset($rules[$key])) {
170 10
            $rules[$key] = new Definition();
171
        }
172
173 31
        $rule = $rules[$key];
174
175
        switch (true) {
176 31
            case $rule instanceof Definition:
177
                // Parser definition
178 16
                $instance = new Implementation($rule, $arguments);
179 16
                break;
180
181 19
            case is_string($rule) && isset($rules[$rule]):
182
                // Reference other rule
183 1
                $instance = static::call($rules, $rule, $arguments);
184 1
                break;
185
186 18
            case $rule instanceof Parser:
187 14
            case is_callable($rule):
188
            //case is_string($rule) && class_exists($rule) && is_subclass_of($rule, Parser::class):
189 8
            case is_array($rule):
190 7
            case is_string($rule):
191 1
            case is_int($rule):
192 17
                $instance = new RuleBinding($rules[$key], $arguments);
193 17
                break;
194
195
            default:
196 1
                throw new \RuntimeException(sprintf('Cannot define `%2$s` using definition type `%1$s`',
197 1
                    is_object($rule)
198
                        ? get_class($rule)
199 1
                        : gettype($rule), $key));
200
        }
201
202 30
        if (!$instance->hasToken()) {
203 30
            $instance->token($key, static::$name);
204
        }
205
206 30
        return $instance;
207
    }
208
209
    /**
210
     * Handle instance/object definitions
211
     *
212
     * @param string $name
213
     * @param array $arguments
214
     * @return Parser
215
     * @throws \Exception
216
     */
217 14
    public function __call($name, $arguments = [])
218
    {
219 14
        return static::call($this->instanceRules, $name, $arguments);
220
    }
221
222
    /**
223
     * Handle static/class definitions
224
     *
225
     * @param string $name
226
     * @param array $arguments
227
     * @return Parser
228
     * @throws \Exception
229
     */
230 4
    public static function __callStatic($name, $arguments = [])
231
    {
232 4
        return static::call(self::$staticRules, $name, $arguments);
233
    }
234
235 9
    public function __get($name)
236
    {
237 9
        return static::call($this->instanceRules, $name);
238
    }
239
240 1
    public function __isset($name)
241
    {
242 1
        return isset($this->instanceRules[$name]);
243
    }
244
245
    // Default parser
246
247 14
    private function initDefaultParser()
248
    {
249 14
        if ($this->parser === null) {
250 6
            if (!isset($this->instanceRules[self::ROOT])) {
251 1
                throw new \UnexpectedValueException('Missing default parser');
252
            }
253
254 5
            $this->parser = static::call($this->instanceRules, self::ROOT);
255
        }
256 12
    }
257
258 12
    protected function parse(&$input, $offset, Context $context)
259
    {
260 12
        $this->initDefaultParser();
261
262 11
        $match = $this->parser->parse($input, $offset, $context);
0 ignored issues
show
Bug introduced by
The method parse() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

262
        /** @scrutinizer ignore-call */ 
263
        $match = $this->parser->parse($input, $offset, $context);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
263 11
        if ($match instanceof Success) {
264 10
            return $this->success($input, $offset, $match->length, $match);
265
        }
266 1
        return $this->failure($input, $offset, $match->length);
267
    }
268
269 2
    public function __toString()
270
    {
271
        try {
272 2
            $this->initDefaultParser();
273 1
        } catch (\Exception $e) {
274
            // ignore
275
        }
276
277 2
        return (string)$this->parser;
278
    }
279
}
280