FunctionStringFormater   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 79
ccs 0
cts 27
cp 0
rs 10
c 0
b 0
f 0
wmc 11
lcom 1
cbo 6

2 Methods

Rating   Name   Duplication   Size   Complexity  
B pass() 0 42 10
A getRegister() 0 6 1
1
<?php
2
3
namespace PHPSA\Analyzer\Pass\Expression\FunctionCall;
4
5
use PHPSA\Context;
6
use PhpParser\Node\Expr\Array_;
7
use PhpParser\Node\Expr\FuncCall;
8
use PHPSA\Analyzer\Helper\DefaultMetadataPassTrait;
9
10
class FunctionStringFormater extends AbstractFunctionCallAnalyzer
11
{
12
    use DefaultMetadataPassTrait;
13
14
    const DESCRIPTION = 'Format string has same number of placeholders as parameters are passed into and forbid invalid type formats.';
15
16
    /**
17
     * @var array functions
18
     */
19
    protected static $functions = [
20
        'printf' => 'printf',
21
        'sprintf' => 'sprintf',
22
        'vprintf' => 'vprintf',
23
        'vsprintf' => 'vsprintf'
24
    ];
25
    /**
26
     * Placeholders for type format
27
     * @var array
28
     */
29
    protected $placeholders = [];
30
31
    /**
32
     * @param FuncCall $funcCall
33
     * @param Context $context
34
     * @return bool
35
     */
36
    public function pass(FuncCall $funcCall, Context $context)
37
    {
38
        $functionName = $this->resolveFunctionName($funcCall, $context);
39
        if ($functionName && isset(self::$functions[$functionName]) && $funcCall->args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $funcCall->args of type PhpParser\Node\Arg[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
40
            $args = $funcCall->args;
41
42
            $formatCE = $context->getExpressionCompiler()->compile($args[0]);
43
            if ($formatCE->isString() && $formatCE->isCorrectValue()) {
44
                // get invalid placeholders
45
                preg_match_all("/(?<!\x25)\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?[^bcdeEufFgGosxX(%)]/", $formatCE->getValue(), $this->placeholders);
46
                if (count($this->placeholders[0]) > 0) {
47
                    $context->notice(
48
                        'function_format_type_invalid',
49
                        sprintf('Unexpected type format in %s function string', $functionName),
50
                        $funcCall
51
                    );
52
                } else {
53
                    // get valid placesholders
54
                    preg_match_all("/(?<!\x25)\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([bcdeEufFgGosxX])/", $formatCE->getValue(), $this->placeholders);
55
                    if ($args[1]->value instanceof Array_) {
56
                        if (count($this->placeholders[0]) !== count($args[1]->value->items)) {
57
                            $context->notice(
58
                                'function_array_length_invalid',
59
                                sprintf('Unexpected length of array passed to %s', $functionName),
60
                                $funcCall
61
                            );
62
                        }
63
                    } else {
64
                        if (count($this->placeholders[0]) !== (count($args) - 1)) {
65
                            $context->notice(
66
                                'function_arguments_length_invalid',
67
                                sprintf('Unexpected length of arguments passed to %s', $functionName),
68
                                $funcCall
69
                            );
70
                        }
71
                    }
72
                }
73
            }
74
        }
75
76
        return true;
77
    }
78
79
    /**
80
     * @return array
81
     */
82
    public function getRegister()
83
    {
84
        return [
85
            FuncCall::class
86
        ];
87
    }
88
}
89