{"record":{"id":"f7fe6b6f13adc543","repo":"PHPOffice/PhpSpreadsheet","slug":"and-value-is-only-appropriate-for-range-operators","errorCode":null,"errorMessage":"AND Value is only appropriate for range operators","messagePattern":"AND Value is only appropriate for range operators","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php","lineNumber":164,"sourceCode":"                    $condition = self::reverseAdjustCellRef($condition, $cellRange);\n                } else {\n                    $condition = self::unwrapString($condition);\n                }\n            }\n            $wizard->operand($index, $condition, $operandValueType);\n        }\n\n        return $wizard;\n    }\n\n    /**\n     * @param mixed[] $arguments\n     */\n    public function __call(string $methodName, array $arguments): self\n    {\n        if ($methodName === 'and') {\n            if (!isset(self::RANGE_OPERATORS[$this->operator])) {\n                throw new Exception('AND Value is only appropriate for range operators');\n            }\n\n            $this->operand(1, ...$arguments);\n\n            return $this;\n        }\n\n        if (!isset(self::MAGIC_OPERATIONS[$methodName])) {\n            throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');\n        }\n\n        $this->operator(self::MAGIC_OPERATIONS[$methodName]);\n        //$this->operand(0, ...$arguments);\n        if (count($arguments) < 2) {\n            $this->operand(0, $arguments[0]);\n        } else {\n            /** @var string */\n            $arg1 = $arguments[1];","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php#L146-L182","documentation":"In the CellValue wizard, and() supplies the second bound of a two-sided comparison, so it is only legal after a range operator - between() or notBetween(). The wizard tracks the currently selected operator; calling and() while a single-sided operator (equal, greaterThan, lessThan, ...) is active throws because a second operand would be meaningless. A fresh CellValue wizard defaults to the 'equal' operator, so calling and() before any operator call also throws.","triggerScenarios":"Chaining ->greaterThan(5)->and(10); calling ->and($x) before any operator (default operator is equal, not a range operator); translating SQL 'BETWEEN ... AND ...' habits onto non-between operators.","commonSituations":"Developers expressing 'value > 5 and value < 10' who reach for greaterThan + and instead of between; refactoring conditions where the operator changed but the and() call remained; conditions assembled dynamically from fragments.","solutions":["Use between(5)->and(10) or notBetween(5)->and(10) for range checks","For 'greater than 5 and less than 10' logic, express it as between with adjusted bounds, or build an Expression rule with an AND(...) formula","Never call and() unless the immediately preceding operator call was between()/notBetween()","Catch the exception when operator chains are assembled dynamically"],"exampleFix":"// before\n$wizard = (new Wizard('A1:E10'))->newRule(Wizard::CELL_VALUE);\n$wizard->greaterThan(5)->and(10);   // throws: greaterThan is not a range operator\n\n// after\n$wizard = (new Wizard('A1:E10'))->newRule(Wizard::CELL_VALUE);\n$wizard->between(5)->and(10);       // between ... and ...","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"use PhpOffice\\PhpSpreadsheet\\Exception as PhpSpreadsheetException;\n\ntry {\n    $wizard->between($min)->and($max);\n} catch (PhpSpreadsheetException $e) {\n    // operator was not between/notBetween when and() ran\n    $wizard = (new Wizard($range))->newRule(Wizard::CELL_VALUE);\n    $wizard->between($min)->and($max);\n}","preventionTips":["Only call and() immediately after between() or notBetween()","Express 'x > a and x < b' as between(a)->and(b), not greaterThan + and","Remember a fresh CellValue wizard defaults to 'equal', so a leading and() always throws","When conditions are assembled dynamically, encode operator/operand pairs as data and expand them in fixed order"],"tags":["php","phpspreadsheet","conditional-formatting","wizard","cell-value","operator-chaining","fluent-interface"],"backgroundTag":"invalid-operator-chaining","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}