ArgumentsExtractor::extractUnrolled()   B
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 0
cts 16
cp 0
rs 8.439
c 0
b 0
f 0
cc 5
eloc 17
nc 3
nop 3
crap 30
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the pinepain/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\Wrappers\FunctionComponents;
17
18
19
use Pinepain\JsSandbox\Exceptions\NativeException;
20
use Pinepain\JsSandbox\Extractors\Definition\ExtractorDefinitionInterface;
21
use Pinepain\JsSandbox\Extractors\ExtractorException;
22
use Pinepain\JsSandbox\Extractors\ExtractorInterface;
23
use Pinepain\JsSandbox\Specs\FunctionSpecInterface;
24
use Pinepain\JsSandbox\Specs\Parameters\ParameterSpecInterface;
25
use V8\Context;
26
use V8\FunctionCallbackInfo;
27
use V8\Value;
28
29
30
class ArgumentsExtractor implements ArgumentsExtractorInterface
31
{
32
    /**
33
     * @var ExtractorInterface
34
     */
35
    private $extractor;
36
37
    public function __construct(ExtractorInterface $extractor)
38
    {
39
        $this->extractor = $extractor;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function extract(FunctionCallbackInfo $args, FunctionSpecInterface $spec): array
46
    {
47
        $parameters_list = $spec->getParameters();
48
        // this check should be done on higher level
49
50
        // this check is handled as missed required parameter exception
51
        // like https://wiki.php.net/rfc/too_few_args
52
        // We run this check as it doesn't make sense to start args unpacking if we know that function call requirements are not fulfilled
53
        if ($args->length() < $parameters_list->getNumberOfRequiredParameters()) {
54
            throw new NativeException("Too few arguments passed: expected at least {$parameters_list->getNumberOfRequiredParameters()}, given {$args->length()}");
55
        }
56
57
        // TODO: this is rather warning then exception
58
        //Method call uses 3 parameters, but method signature uses 3 parameters
59
        if ($args->length() > $parameters_list->getNumberOfParameters() && !$parameters_list->isVariadic()) {
60
            throw new NativeException("Too many arguments passed: expected no more than {$parameters_list->getNumberOfParameters()}, given {$args->length()}");
61
        }
62
63
        return $this->extractUnrolled($args->getContext(), $args->arguments(), $parameters_list->getParameters());
64
    }
65
66
    /**
67
     * @param Context                  $context
68
     * @param Value[]                  $arguments
69
     * @param ParameterSpecInterface[] $specs
70
     *
71
     * @return array
72
     * @throws NativeException
73
     */
74
    public function extractUnrolled(Context $context, array $arguments, array $specs)
75
    {
76
        $extracted_args = [];
77
        $position       = 0;
78
79
        /** @var ParameterSpecInterface $spec */
80
        foreach ($specs as $spec) {
81
            if ($spec->isVariadic()) {
82
                // current argument is variadic and the rest of passed arguments goes to it
83
                $variadic_args  = $this->extractVariadicArguments($context, $arguments, $spec->getExtractorDefinition(), $position);
84
                $extracted_args = array_merge($extracted_args, $variadic_args);
85
                // variadic argument is the last possible argument
86
                break;
87
            }
88
89
            // if no arguments left, we interpret any left as undefined
90
            if (empty($arguments)) {
91
92
                if (!$spec->isOptional()) {
93
                    throw new NativeException("Missing argument {$position}");
94
                }
95
96
                $extracted_args[] = $spec->getDefaultValue();
97
            } else {
98
                // extract current argument
99
                $argument         = array_shift($arguments);
100
                $extracted_args[] = $this->extractPlainArgument($context, $argument, $spec->getExtractorDefinition(), $position);
101
            }
102
103
            $position++;
104
        }
105
106
        return $extracted_args;
107
    }
108
109
    /**
110
     * @param Context                      $context
111
     * @param Value[]                      $arguments
112
     * @param ExtractorDefinitionInterface $definition
113
     * @param int                          $position
114
     *
115
     * @return array
116
     */
117
    public function extractVariadicArguments(Context $context, array $arguments, ExtractorDefinitionInterface $definition, int $position): array
118
    {
119
        $out = [];
120
121
        foreach ($arguments as $argument) {
122
            $out[] = $this->extractPlainArgument($context, $argument, $definition, $position++);
123
        }
124
125
        return $out;
126
    }
127
128
    /**
129
     * @param Context                      $context
130
     * @param Value                        $argument
131
     * @param ExtractorDefinitionInterface $definition
132
     * @param int                          $position
133
     *
134
     * @return mixed
135
     * @throws NativeException
136
     */
137
    public function extractPlainArgument(Context $context, Value $argument, ExtractorDefinitionInterface $definition, int $position)
138
    {
139
        try {
140
            return $this->extractor->extract($context, $argument, $definition);
141
        } catch (ExtractorException $e) {
142
            throw new NativeException("Failed to extract argument {$position}: {$e->getMessage()}");
143
        }
144
    }
145
}
146