Completed
Push — wip-public-release ( 7c11a5...54ec9d )
by Bogdan
05:19
created

FunctionSpecBuilder::getThrowsList()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.4285
cc 3
eloc 7
nc 2
nop 1
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the pinepain/php-v8-js-sandbox PHP library.
5
 *
6
 * Copyright (c) 2016-2017 Bogdan Padalko <[email protected]>
7
 *
8
 * Licensed under the MIT license: http://opensource.org/licenses/MIT
9
 *
10
 * For the full copyright and license information, please view the
11
 * LICENSE file that was distributed with this source or visit
12
 * http://opensource.org/licenses/MIT
13
 */
14
15
16
namespace Pinepain\JsSandbox\Specs\Builder;
17
18
19
use Pinepain\JsSandbox\Specs\Builder\Exceptions\FunctionSpecBuilderException;
20
use Pinepain\JsSandbox\Specs\FunctionSpec;
21
use Pinepain\JsSandbox\Specs\FunctionSpecInterface;
22
use Pinepain\JsSandbox\Specs\ParametersList;
23
use Pinepain\JsSandbox\Specs\ParametersListInterface;
24
use Pinepain\JsSandbox\Specs\ReturnSpec\AnyReturnSpec;
25
use Pinepain\JsSandbox\Specs\ReturnSpec\ReturnSpecInterface;
26
use Pinepain\JsSandbox\Specs\ReturnSpec\VoidReturnSpec;
27
use Pinepain\JsSandbox\Specs\ThrowSpec\EchoThrowSpec;
28
use Pinepain\JsSandbox\Specs\ThrowSpec\ThrowSpecList;
29
use Pinepain\JsSandbox\Specs\ThrowSpec\ThrowSpecListInterface;
30
31
32
class FunctionSpecBuilder implements FunctionSpecBuilderInterface
33
{
34
    /**
35
     * @var ParameterSpecBuilderInterface
36
     */
37
    private $builder;
38
    /**
39
     * @var array
40
     */
41
    private $return_types;
42
    /**
43
     * @var string
44
     */
45
    private $default_return_type = 'any';
46
47
48
    public function __construct(ParameterSpecBuilderInterface $builder)
49
    {
50
        $this->builder = $builder;
51
52
        $this->return_types = [
53
            'any'  => new AnyReturnSpec(),
54
            'void' => new VoidReturnSpec(),
55
        ];
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function build(string $definition): FunctionSpecInterface
62
    {
63
        $definition = trim($definition);
64
65
        if (!$definition) {
66
            throw new FunctionSpecBuilderException('Definition must be non-empty string');
67
        }
68
69
        if (preg_match('/^(?<return>\w+\b)?\s*(?<needs_context>\!)?\s*\(\s*(?<params>([^,]+)(?:\s*,\s*[^,]+)*)?\s*\)\s*(?<throws>(\\\\?[a-z][\w\\\\]+)(?:\s*\|\s*(?-1))*)?\s*$/i', $definition, $matches)) {
70
71
            $needs_context = isset($matches['needs_context']) && $matches['needs_context'];
72
73
            $params = $this->getParametersList($matches['params'] ?? '');
74
            $return = $this->getReturnType(($matches['return'] ?? '') ?: $this->default_return_type);
75
            $throws = $this->getThrowsList($matches['throws'] ?? '');
76
77
            return new FunctionSpec($params, $throws, $return, $needs_context);
78
        }
79
80
        throw new FunctionSpecBuilderException("Unable to parse definition: '{$definition}'");
81
    }
82
83
    protected function getReturnType(string $definition): ReturnSpecInterface
84
    {
85
        if (!isset($this->return_types[$definition])) {
86
            throw new FunctionSpecBuilderException("Invalid return type: '{$definition}'");
87
        }
88
89
        return $this->return_types[$definition];
90
    }
91
92
    protected function getParametersList(string $definition): ParametersListInterface
93
    {
94
        $params = [];
95
96
        if ($definition) {
97
            $raw_params_definition = explode(',', $definition);
98
            foreach ($raw_params_definition as $param_definition) {
99
                $params[] = $this->builder->build(trim($param_definition));
100
            }
101
        }
102
103
        return new ParametersList(...$params);
104
    }
105
106
    protected function getThrowsList(string $definition): ThrowSpecListInterface
107
    {
108
        $specs = [];
109
110
        if ($definition) {
111
            $classes = array_filter(array_map('\trim', explode('|', $definition)));
112
113
            foreach ($classes as $class) {
114
                $specs[] = new EchoThrowSpec($class);
115
            }
116
        }
117
118
        return new ThrowSpecList(...$specs);
119
    }
120
}
121