PosttypeCompiler   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Test Coverage

Coverage 84.44%

Importance

Changes 0
Metric Value
eloc 76
dl 0
loc 277
ccs 76
cts 90
cp 0.8444
rs 9.2
c 0
b 0
f 0
wmc 40

18 Methods

Rating   Name   Duplication   Size   Complexity  
A withConfiguration() 0 4 1
A bind() 0 4 1
A needsToCompile() 0 3 1
A __construct() 0 3 1
A wantsAllPosttypes() 0 3 2
A validArgument() 0 3 1
A compileLimit() 0 3 1
A getReturnType() 0 13 3
A getPosttype() 0 8 2
A compileType() 0 9 2
A build() 0 20 4
A addArgument() 0 14 3
A applyConfiguration() 0 11 3
A getCompilingMethod() 0 5 2
A compileWheres() 0 8 4
A getFormatter() 0 25 4
A setDefaults() 0 11 3
A addArrayOfArguments() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like PosttypeCompiler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PosttypeCompiler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Sanderdekroon\Parlant\Compiler;
4
5
use Closure;
6
use Exception;
7
use Sanderdekroon\Parlant\Container;
8
use Sanderdekroon\Parlant\Grammar\PosttypeGrammar;
9
use Sanderdekroon\Parlant\Formatter\FormatterInterface;
10
use Sanderdekroon\Parlant\Configurator\ConfiguratorInterface;
11
12
class PosttypeCompiler
13
{
14
    use CompilesMeta, CompilesTaxonomies;
15
16
    protected $grammar;
17
    protected $bindings;
18
    protected $arguments = [];
19
    protected $container;
20
    protected $configuration;
21
22 35
    public function __construct(PosttypeGrammar $grammar)
23
    {
24 35
        $this->grammar = $grammar;
25 35
    }
26
27
28
    public function setDefaults(array $defaults)
29
    {
30
        $validArguments = $this->grammar->getArguments();
31
32
        foreach ($defaults as $name => $default) {
33
            if (in_array($name, $validArguments)) {
34
                $this->arguments[$name] = $default;
35
            }
36
        }
37
38
        return $this;
39
    }
40
41
    /**
42
     * Bind the bindings to this class.
43
     * @param  Container $bindings
44
     * @return $this
45
     */
46 28
    public function bind(Container $bindings)
47
    {
48 28
        $this->bindings = $bindings;
49 28
        return $this;
50
    }
51
52
    /**
53
     * Pass the configuration to this class
54
     * @param  ConfiguratorInterface $configuration
55
     * @return $this
56
     */
57 28
    public function withConfiguration(ConfiguratorInterface $configuration)
58
    {
59 28
        $this->configuration = $configuration;
60 28
        return $this;
61
    }
62
63
    /**
64
     * Build the query arguments and pass it to an formatter.
65
     * @return mixed
66
     */
67 28
    public function build()
68
    {
69 28
        $this->applyConfiguration();
70 28
        $this->addArgument(['post_type' => $this->getPosttype()]);
71
72 28
        $bindings = $this->bindings->all();
73
74 28
        foreach ($bindings as $name => $binding) {
75 28
            if ($this->needsToCompile($name)) {
76 15
                $this->compileType($name, $binding);
77
            }
78
79 28
            if ($this->validArgument($name)) {
80 28
                $this->addArgument($name, $binding); //@todo validation/sanitation
81
            }
82
        }
83
84 28
        $formatter = $this->getFormatter();
85
86 28
        return $formatter->output($this->arguments);
87
    }
88
89
    /**
90
     * Check if the supplied name is a valid query argument
91
     * @param  string $name
92
     * @return bool
93
     */
94 28
    protected function validArgument($name)
95
    {
96 28
        return in_array($name, $this->grammar->getArguments());
97
    }
98
99
    /**
100
     * Check if the supplied query name needs compiling.
101
     * @param  string $name
102
     * @return bool
103
     */
104 28
    protected function needsToCompile($name)
105
    {
106 28
        return in_array($name, $this->grammar->getQueryTypes());
107
    }
108
109
    /**
110
     * Returns the output formatter
111
     * @return FormatterInterface
112
     */
113 28
    protected function getFormatter()
114
    {
115 28
        $type = $this->getReturnType();
116
117
        // The developer has supplied an function that has to be used as a formatter.
118
        // Note: this is not preferred, as we cannot check the existance of an
119
        // output method after the function call.
120 28
        if ($type instanceof Closure) {
121
            return call_user_func($type);
122
        }
123
124
        // If it's not an closure, we're expecing a fully namespaced classname. We'll
125
        // create a new instance of that class and check if it implements the
126
        // FormatterInterface. If it does, we'll return the instance.
127 28
        if (!class_exists($type)) {
128
            throw new Exception('Could not compile: formatter could not be instantiated.'); //Replace with CompilerException
129
        }
130
131 28
        $formatter = new $type;
132
133 28
        if (!$formatter instanceof FormatterInterface) {
134
            throw new Exception('Could not compile: formatter does not implement FormatterInterface.');
135
        }
136
137 28
        return $formatter;
138
    }
139
140
    /**
141
     * Get the configured return type.
142
     * @return string|closure
143
     */
144 28
    private function getReturnType()
145
    {
146 28
        $type = $this->configuration->get('return');
147
148 28
        if (is_callable($type)) {
149
            return $type;
150
        }
151
152 28
        if (array_key_exists($type, $this->grammar->getFormatters())) {
153 28
            return $this->grammar->getFormatter($type);
154
        }
155
156
        return $type;
157
    }
158
159
    /**
160
     * Before the arguments are compiled and added, apply the configuration paramaters.
161
     * @return $this
162
     */
163 28
    protected function applyConfiguration()
164
    {
165 28
        foreach ($this->grammar->getArguments() as $argument) {
166 28
            if (!$this->configuration->has($argument)) {
167 28
                continue;
168
            }
169
170 28
            $this->addArgument($argument, $this->configuration->get($argument));
171
        }
172
173 28
        return $this;
174
    }
175
176
    /**
177
     * Get the compiling method. Basically prepends 'compile'.
178
     * @param  string $type
179
     * @return string|false       Method name, or false if it does not exist.
180
     */
181 15
    private function getCompilingMethod($type)
182
    {
183 15
        $name = 'compile'.ucfirst($type);
184
185 15
        return method_exists($this, $name) ? $name : false;
186
    }
187
188
    /**
189
     * Compile the supplied type to valid query arguments.
190
     * @param  string $type
191
     * @param  array $arguments
192
     * @return array
193
     */
194 15
    private function compileType($type, $arguments)
195
    {
196 15
        $method = $this->getCompilingMethod($type);
197
198 15
        if ($method === false) {
199
            throw new \BadMethodCallException('Invalid querytype requested. Method compile'.ucfirst((string)$type).' does not exist!');
200
        }
201
202 15
        return $this->$method($arguments);
203
    }
204
205
    /**
206
     * Compile all the where's to valid query arguments
207
     * @param  array $wheres
208
     * @return array
209
     */
210 3
    protected function compileWheres($wheres)
211
    {
212 3
        if (empty($wheres) || !is_array($wheres)) {
213
            return;
214
        }
215
        
216 3
        foreach ($wheres as $where) {
217 3
            $this->addArgument([$where['column'] => $where['value']]);
218
        }
219 3
    }
220
221
    /**
222
     * Compile limit to valid query arguments. Basically transforms limit to posts_per_page.
223
     * @return void
224
     */
225 1
    protected function compileLimit($limit)
226
    {
227 1
        $this->addArgument('posts_per_page', (int)$limit);
228 1
    }
229
230
    /**
231
     * Get the requested post type string.
232
     * @return string
233
     */
234 28
    protected function getPosttype()
235
    {
236 28
        $posttype = $this->bindings->get('post_type');
237 28
        if ($this->wantsAllPosttypes($posttype)) {
238
            return 'any';
239
        }
240
241 28
        return $posttype;
242
    }
243
244
    /**
245
     * Determine if the developer wants all posttypes or not.
246
     * @param  string $posttype
247
     * @return bool
248
     */
249 28
    private function wantsAllPosttypes($posttype)
250
    {
251 28
        return empty($posttype) || $posttype == '*';
252
    }
253
254
    /**
255
     * Add an argument to the arguments list.
256
     * @param string|array    $name
257
     * @param mixed     $argument
258
     * @param bool      $multidimensional
259
     * @return $this
260
     */
261 28
    protected function addArgument($name, $argument = null, $multidimensional = false)
262
    {
263 28
        if (is_array($name)) {
264 28
            $this->addArrayOfArguments($name);
265 28
            return $this;
266
        }
267
268 28
        if ($multidimensional) {
269 11
            $this->arguments = array_merge_recursive($this->arguments, [$name => $argument]);
270 11
            return $this;
271
        }
272
273 28
        $this->arguments = array_merge($this->arguments, [$name => $argument]);
274 28
        return $this;
275
    }
276
277
    /**
278
     * Add an array of arguments to the list.
279
     * @param array $arguments
280
     * @param $this
281
     */
282 28
    protected function addArrayOfArguments($arguments, $multidimensional = false)
283
    {
284 28
        foreach ($arguments as $name => $argument) {
285 28
            $this->addArgument($name, $argument, $multidimensional);
286
        }
287
288 28
        return $this;
289
    }
290
}
291