Failed Conditions
Pull Request — master (#3743)
by Adrien
14:08
created

CellValue::__call()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 25
ccs 10
cts 12
cp 0.8333
rs 9.2222
c 0
b 0
f 0
cc 6
nc 5
nop 2
crap 6.1666
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Exception;
7
use PhpOffice\PhpSpreadsheet\Style\Conditional;
8
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellMatcher;
9
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
10
11
/**
12
 * @method CellValue equals($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
13
 * @method CellValue notEquals($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
14
 * @method CellValue greaterThan($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
15
 * @method CellValue greaterThanOrEqual($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
16
 * @method CellValue lessThan($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
17
 * @method CellValue lessThanOrEqual($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
18
 * @method CellValue between($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
19
 * @method CellValue notBetween($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
20
 * @method CellValue and($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
21
 */
22
class CellValue extends WizardAbstract implements WizardInterface
23
{
24
    protected const MAGIC_OPERATIONS = [
25
        'equals' => Conditional::OPERATOR_EQUAL,
26
        'notEquals' => Conditional::OPERATOR_NOTEQUAL,
27
        'greaterThan' => Conditional::OPERATOR_GREATERTHAN,
28
        'greaterThanOrEqual' => Conditional::OPERATOR_GREATERTHANOREQUAL,
29
        'lessThan' => Conditional::OPERATOR_LESSTHAN,
30
        'lessThanOrEqual' => Conditional::OPERATOR_LESSTHANOREQUAL,
31
        'between' => Conditional::OPERATOR_BETWEEN,
32
        'notBetween' => Conditional::OPERATOR_NOTBETWEEN,
33
    ];
34
35
    protected const SINGLE_OPERATORS = CellMatcher::COMPARISON_OPERATORS;
36
37
    protected const RANGE_OPERATORS = CellMatcher::COMPARISON_RANGE_OPERATORS;
38
39
    /** @var string */
40
    protected $operator = Conditional::OPERATOR_EQUAL;
41
42
    /** @var array */
43
    protected $operand = [0];
44
45
    /**
46
     * @var string[]
47
     */
48
    protected $operandValueType = [];
49
50 44
    public function __construct(string $cellRange)
51
    {
52 44
        parent::__construct($cellRange);
53
    }
54
55 42
    protected function operator(string $operator): void
56
    {
57 42
        if ((!isset(self::SINGLE_OPERATORS[$operator])) && (!isset(self::RANGE_OPERATORS[$operator]))) {
58
            throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
59
        }
60
61 42
        $this->operator = $operator;
62
    }
63
64 43
    protected function operand(int $index, mixed $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
65
    {
66 43
        if (is_string($operand)) {
67 23
            $operand = $this->validateOperand($operand, $operandValueType);
68
        }
69
70 43
        $this->operand[$index] = $operand;
71 43
        $this->operandValueType[$index] = $operandValueType;
72
    }
73
74
    /**
75
     * @return float|int|string
76
     */
77 42
    protected function wrapValue(mixed $value, string $operandValueType)
78
    {
79 42
        if (!is_numeric($value) && !is_bool($value) && null !== $value) {
80 22
            if ($operandValueType === Wizard::VALUE_TYPE_LITERAL) {
81 2
                return '"' . str_replace('"', '""', $value) . '"';
82
            }
83
84 21
            return $this->cellConditionCheck($value);
85
        }
86
87 23
        if (null === $value) {
88 1
            $value = 'NULL';
89 22
        } elseif (is_bool($value)) {
90 2
            $value = $value ? 'TRUE' : 'FALSE';
91
        }
92
93 23
        return $value;
94
    }
95
96 42
    public function getConditional(): Conditional
97
    {
98 42
        if (!isset(self::RANGE_OPERATORS[$this->operator])) {
99 35
            unset($this->operand[1], $this->operandValueType[1]);
100
        }
101 42
        $values = array_map([$this, 'wrapValue'], $this->operand, $this->operandValueType);
102
103 42
        $conditional = new Conditional();
104 42
        $conditional->setConditionType(Conditional::CONDITION_CELLIS);
105 42
        $conditional->setOperatorType($this->operator);
106 42
        $conditional->setConditions($values);
107 42
        $conditional->setStyle($this->getStyle());
108 42
        $conditional->setStopIfTrue($this->getStopIfTrue());
109
110 42
        return $conditional;
111
    }
112
113 2
    protected static function unwrapString(string $condition): string
114
    {
115 2
        if ((str_starts_with($condition, '"')) && (str_starts_with(strrev($condition), '"'))) {
116 1
            $condition = substr($condition, 1, -1);
117
        }
118
119 2
        return str_replace('""', '"', $condition);
120
    }
121
122 30
    public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
123
    {
124 30
        if ($conditional->getConditionType() !== Conditional::CONDITION_CELLIS) {
125 1
            throw new Exception('Conditional is not a Cell Value CF Rule conditional');
126
        }
127
128 29
        $wizard = new self($cellRange);
129 29
        $wizard->style = $conditional->getStyle();
130 29
        $wizard->stopIfTrue = $conditional->getStopIfTrue();
131
132 29
        $wizard->operator = $conditional->getOperatorType();
133 29
        $conditions = $conditional->getConditions();
134 29
        foreach ($conditions as $index => $condition) {
135
            // Best-guess to try and identify if the text is a string literal, a cell reference or a formula?
136 29
            $operandValueType = Wizard::VALUE_TYPE_LITERAL;
137 29
            if (is_string($condition)) {
138 20
                if (Calculation::keyInExcelConstants($condition)) {
139 3
                    $condition = Calculation::getExcelConstants($condition);
140 17
                } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '$/i', $condition)) {
141 6
                    $operandValueType = Wizard::VALUE_TYPE_CELL;
142 6
                    $condition = self::reverseAdjustCellRef($condition, $cellRange);
143
                } elseif (
144 11
                    preg_match('/\(\)/', $condition)
145 11
                    || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
146
                ) {
147 9
                    $operandValueType = Wizard::VALUE_TYPE_FORMULA;
148 9
                    $condition = self::reverseAdjustCellRef($condition, $cellRange);
149
                } else {
150 2
                    $condition = self::unwrapString($condition);
151
                }
152
            }
153 29
            $wizard->operand($index, $condition, $operandValueType);
154
        }
155
156 29
        return $wizard;
157
    }
158
159
    /**
160
     * @param string $methodName
161
     * @param mixed[] $arguments
162
     */
163 42
    public function __call($methodName, array $arguments): self
164
    {
165 42
        if (!isset(self::MAGIC_OPERATIONS[$methodName]) && $methodName !== 'and') {
166
            throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
167
        }
168
169 42
        if ($methodName === 'and') {
170 8
            if (!isset(self::RANGE_OPERATORS[$this->operator])) {
171
                throw new Exception('AND Value is only appropriate for range operators');
172
            }
173
174
            $this->operand(1, ...$arguments);
175
176 8
            return $this;
177 4
        }
178
179 4
        $this->operator(self::MAGIC_OPERATIONS[$methodName]);
180
        //$this->operand(0, ...$arguments);
181
        if (count($arguments) < 2) {
182 8
            $this->operand(0, $arguments[0]);
183
        } else {
184
            $this->operand(0, $arguments[0], $arguments[1]);
185 42
        }
186
187 42
        return $this;
188 22
    }
189
}
190