Passed
Push — dev ( 8f807e...d72849 )
by Yan
02:28
created

Parser::boot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Lincable\Parsers;
4
5
use LogicException;
6
use Illuminate\Support\Collection;
7
use Lincable\Concerns\BuildClassnames;
8
use Lincable\Contracts\Formatters\Formatter;
9
use Illuminate\Contracts\Container\Container;
10
use Lincable\Exceptions\NotDynamicOptionException;
11
use Lincable\Contracts\Parsers\ParameterInterface;
12
13
abstract class Parser
14
{
15
    use BuildClassnames;
16
17
    /**
18
     * List with availables formatters.
19
     * 
20
     * @var \Illuminate\Support\Collection
21
     */
22
    protected $formatters;
23
24
    /**
25
     * The application container implementation.
26
     * 
27
     * @var \Illuminate\Contracts\Container\Container
28
     */
29
    protected $app;
30
31
    /**
32
     * Return the formatter call for the matches on parse.
33
     * 
34
     * @param  array $matches
35
     * @return Lincable\Contracts\Parsers\ParameterInterface
0 ignored issues
show
Bug introduced by
The type Lincable\Parsers\Lincabl...sers\ParameterInterface was not found. Did you mean Lincable\Contracts\Parsers\ParameterInterface? If so, make sure to prefix the type with \.
Loading history...
36
     */
37
    abstract protected function parseMatches(array $matches): ParameterInterface;
38
39
    /**
40
     * Return the dynamic regex pattern.
41
     * 
42
     * @return string
43
     */
44
    abstract protected function getDynamicPattern(): string;
45
46
    /**
47
     * Boot the parser with the container executing initial tasks.
48
     * 
49
     * @param  \Illuminate\Contracts\Container\Container|null $app
50
     * @return void
51
     */
52
    public function boot(Container $app = null)
53
    {
54
        $this->formatters = collect();
55
        $this->app = $app;
56
    }
57
58
    /**
59
     * Push a new formatter to collection. 
60
     *
61
     * @param  mixed $formatter
62
     * @param  string $name
63
     * @return this
64
     */
65
    public function addFormatter($formatter, string $name = null)
66
    {
67
        $this->formatters->put(
68
            $name ?: $this->nameFromClass($formatter, 'Formatter'),
69
            $formatter
70
        );
71
        return $this;
72
    }
73
74
    /**
75
     * Parse an option through formatters.
76
     * 
77
     * @throws Lincable\Exceptions\NotDynamicOption
78
     * 
79
     * @param  string $option
80
     * @return mixed
81
     */
82
    public function parse(string $option)
83
    {
84
        if ($this->shouldParse($option)) {
85
            
86
            // Now that we have verified the option is dynamic and has 
87
            // matches, we get the Option object from the implemented
88
            // method to deal with the matches.  
89
            $parameter =  $this->parseMatches($this->getMatches($option));
90
91
            // Return the content of the option executed.
92
            return $this->runForParameter($parameter);
93
        }
94
95
        throw new NotDynamicOptionException("Can not parse non dynamics parameter [$option].");
96
    }
97
98
    /**
99
     * Append a list of formatters. 
100
     *
101
     * @param  mixed $formatters
102
     * @return this
103
     */
104
    public function addFormatters($formatters)
105
    {
106
        array_walk($formatters, function ($formatter, $name) {
107
            $this->addFormatter($formatter, is_int($name) ? null : $name);
108
        });
109
110
        return $this;
111
    }
112
113
    /**
114
     * Set the list with the new formatters.
115
     * 
116
     * @param  \Illuminate\Support\Collection $formatters
117
     * @return this
118
     */
119
    public function setFormatters(Collection $formatters)
120
    {
121
        $this->formatters = $formatters;
122
        return $this;
123
    }
124
125
    /**
126
     * Return the formatters collection. 
127
     *
128
     * @return \Illuminate\Support\Collection
129
     */
130
    public function getFormatters()
131
    {
132
        return $this->formatters;
133
    }
134
135
    /**
136
     * Return the containter instance.
137
     * 
138
     * @return \Illuminate\Contracts\Container\Container
139
     */
140
    public function getContainer()
141
    {
142
        return $this->app;
143
    }
144
145
    /**
146
     * Set the new container instance. 
147
     *
148
     * @param  \Illuminate\Contracts\Container\Container $app
149
     * @return this
150
     */
151
    public function setContainer(Container $app)
152
    {
153
        $this->app = $app;
154
        return $this;
155
    }
156
157
    /**
158
     * Return the first formatter that matches the option name. 
159
     *
160
     * @param  string $option
161
     * @return mixed
162
     */
163
    public function findFormatter(string $option)
164
    {
165
        return $this->formatters->get($option);
166
    }
167
168
    /**
169
     * Determine wheter the parameter should be parsed.
170
     * 
171
     * @param  string $option
172
     * @return bool
173
     */
174
    protected function shouldParse(string $option)
175
    {
176
        return $this->isParameterDynamic($option) 
177
            && $this->getMatches($option);
178
    }
179
180
    /**
181
     * Run the parser for the parameter.
182
     *
183
     * @param  Lincable\Contracts\Parsers\ParameterInterface $parameter
184
     * @return mixed
185
     */
186
    protected function runForParameter(ParameterInterface $parameter) 
187
    {
188
        // Get parameter value name.
189
        $name = $parameter->getValue();
190
191
        if ($formatter = $this->findFormatter($name)) {
192
            
193
            // Get a container callable.
194
            $callable = $this->resolveFormatter($formatter); 
195
196
            // Execute the formatter class with the params.
197
            return $this->callFormatter(
198
                $callable, 
199
                $parameter->getParams()
200
            );
201
        }
202
203
        throw new LogicException("Call to undefined formatter [{$name}].");
204
    }
205
206
    /**
207
     * Resolve the formatter call using the container with the array parameters.
208
     * The formatter argument should be a callable for the container. 
209
     *
210
     * @param  mixed $formatter
211
     * @param  array $params
212
     * @return mixed
213
     */
214
    protected function callFormatter($formatter, array $params = [])
215
    {
216
        return $this->getContainer()->call($formatter, $params);
217
    }
218
219
    /**
220
     * Resolve a formatter to a container callable.
221
     * 
222
     * @param  mixed $formatter
223
     * @return mixed
224
     */
225
    protected function resolveFormatter($formatter)
226
    {
227
        if (is_callable($formatter)) {
228
229
            // Just return the closure.  
230
            return $formatter;
231
        }
232
233
        if (is_string($formatter)) {
234
235
            // Try to create formatter instance using dependency injection.
236
            $formatter = $this->getContainer()->make($formatter);
237
        }
238
239
        return [$formatter, 'format'];
240
    }
241
242
    /**
243
     * Determine wheter the parameter is dynamic.
244
     * 
245
     * @param  string $parameter
246
     * @return bool
247
     */
248
    public function isParameterDynamic(string $parameter)
249
    {
250
        return (bool) preg_match($this->getDynamicPattern(), $parameter);
251
    }
252
253
    /**
254
     * Return the matches.
255
     * 
256
     * @param  string $parameter
257
     * @return array
258
     */
259
    public function getMatches(string $parameter)
260
    {
261
        $isDynamic = preg_match(
262
            $this->getDynamicPattern(), 
263
            $parameter, 
264
            $matches
265
        );
266
267
        if ($isDynamic) {
268
269
            // The parameter is dynamic and has matches, so remove the first 
270
            // match, that only matches the whole word. 
271
            array_shift($matches);
272
        } 
273
        
274
        return $matches;
275
    }
276
}