PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Conditional is not a Text Value CF Rule conditional

Error message

Conditional is not a Text Value CF Rule conditional

What it means

Wizard\TextValue::fromConditional() rebuilds a TextValue wizard from an existing Conditional, but only for the four text condition types in its OPERATORS map: containsText, notContainsText, beginsWith and endsWith. Other condition types - including blanks and expression rules that test text - are rejected up front.

Source

Thrown at src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php:115

        $conditional = new Conditional();
        $conditional->setConditionType(self::OPERATORS[$this->operator]);
        $conditional->setOperatorType($this->operator);
        $conditional->setText(
            $this->operandValueType !== Wizard::VALUE_TYPE_LITERAL
                ? $this->cellConditionCheck($this->operand)
                : $this->operand
        );
        $conditional->setConditions([$this->expression]);
        $conditional->setStyle($this->getStyle());
        $conditional->setStopIfTrue($this->getStopIfTrue());

        return $conditional;
    }

    public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
    {
        if (!in_array($conditional->getConditionType(), self::OPERATORS, true)) {
            throw new Exception('Conditional is not a Text Value CF Rule conditional');
        }

        $wizard = new self($cellRange);
        $wizard->operator = (string) array_search($conditional->getConditionType(), self::OPERATORS, true);
        $wizard->style = $conditional->getStyle();
        $wizard->stopIfTrue = $conditional->getStopIfTrue();

        // Best-guess to try and identify if the text is a string literal, a cell reference or a formula?
        $wizard->operandValueType = Wizard::VALUE_TYPE_LITERAL;
        $condition = $conditional->getText();
        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '$/i', $condition)) {
            $wizard->operandValueType = Wizard::VALUE_TYPE_CELL;
            $condition = self::reverseAdjustCellRef($condition, $cellRange);
        } elseif (
            preg_match('/\(\)/', $condition)
            || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
        ) {
            $wizard->operandValueType = Wizard::VALUE_TYPE_FORMULA;

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Check getConditionType() is one of containsText, notContainsText, beginsWith, endsWith before calling
  2. Use Wizard::fromConditional() for automatic dispatch
  3. If the rule is an expression-based text test, convert it with Wizard\Expression::fromConditional() instead
  4. Guard batch loops with try/catch

Example fix

// before
$wizard = Wizard\TextValue::fromConditional($conditional); // throws unless text type

// after
use PhpOffice\PhpSpreadsheet\Style\Conditional;

if (in_array($conditional->getConditionType(), [
    Conditional::CONDITION_CONTAINSTEXT, Conditional::CONDITION_NOTCONTAINSTEXT,
    Conditional::CONDITION_BEGINSWITH, Conditional::CONDITION_ENDSWITH,
], true)) {
    $wizard = Wizard\TextValue::fromConditional($conditional);
} else {
    $wizard = Wizard::fromConditional($conditional);
}
Defensive patterns

Strategy: type-guard

Validate before calling

use PhpOffice\PhpSpreadsheet\Style\Conditional;

if (in_array($conditional->getConditionType(), [
    Conditional::CONDITION_CONTAINSTEXT, Conditional::CONDITION_NOTCONTAINSTEXT,
    Conditional::CONDITION_BEGINSWITH, Conditional::CONDITION_ENDSWITH,
], true)) {
    $wizard = Wizard\TextValue::fromConditional($conditional, $cellRange);
} else {
    $wizard = Wizard::fromConditional($conditional, $cellRange);
}

Type guard

function isTextValueConditional(Conditional $conditional): bool
{
    return in_array($conditional->getConditionType(), [
        Conditional::CONDITION_CONTAINSTEXT,
        Conditional::CONDITION_NOTCONTAINSTEXT,
        Conditional::CONDITION_BEGINSWITH,
        Conditional::CONDITION_ENDSWITH,
    ], true);
}

Try / catch

use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;

try {
    $wizard = Wizard\TextValue::fromConditional($conditional, $cellRange);
} catch (PhpSpreadsheetException $e) {
    $wizard = Wizard::fromConditional($conditional, $cellRange);
}

Prevention

When it happens

Trigger: Calling TextValue::fromConditional() on a Conditional whose type is containsBlanks, expression, cellIs or timePeriod; passing a SEARCH()-based expression rule that behaves like 'contains' but was saved as CONDITION_EXPRESSION.

Common situations: Text filters implemented as expression formulas in imported files; generic rule editors assuming any text-related rule is a TextValue rule; mixed-rule conversion loops.

Related errors


AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17). Data as JSON: /api/errors/dafbbf32b60b9406. Report an issue: GitHub.