package and depencies
This commit is contained in:
313
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
vendored
Normal file
313
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class CellMatcher
|
||||
{
|
||||
public const COMPARISON_OPERATORS = [
|
||||
Conditional::OPERATOR_EQUAL => '=',
|
||||
Conditional::OPERATOR_GREATERTHAN => '>',
|
||||
Conditional::OPERATOR_GREATERTHANOREQUAL => '>=',
|
||||
Conditional::OPERATOR_LESSTHAN => '<',
|
||||
Conditional::OPERATOR_LESSTHANOREQUAL => '<=',
|
||||
Conditional::OPERATOR_NOTEQUAL => '<>',
|
||||
];
|
||||
|
||||
public const COMPARISON_RANGE_OPERATORS = [
|
||||
Conditional::OPERATOR_BETWEEN => 'IF(AND(A1>=%s,A1<=%s),TRUE,FALSE)',
|
||||
Conditional::OPERATOR_NOTBETWEEN => 'IF(AND(A1>=%s,A1<=%s),FALSE,TRUE)',
|
||||
];
|
||||
|
||||
public const COMPARISON_DUPLICATES_OPERATORS = [
|
||||
Conditional::CONDITION_DUPLICATES => "COUNTIF('%s'!%s,%s)>1",
|
||||
Conditional::CONDITION_UNIQUE => "COUNTIF('%s'!%s,%s)=1",
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Cell
|
||||
*/
|
||||
protected $cell;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $cellRow;
|
||||
|
||||
/**
|
||||
* @var Worksheet
|
||||
*/
|
||||
protected $worksheet;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $cellColumn;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $conditionalRange;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $referenceCell;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $referenceRow;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $referenceColumn;
|
||||
|
||||
/**
|
||||
* @var Calculation
|
||||
*/
|
||||
protected $engine;
|
||||
|
||||
public function __construct(Cell $cell, string $conditionalRange)
|
||||
{
|
||||
$this->cell = $cell;
|
||||
$this->worksheet = $cell->getWorksheet();
|
||||
[$this->cellColumn, $this->cellRow] = Coordinate::indexesFromString($this->cell->getCoordinate());
|
||||
$this->setReferenceCellForExpressions($conditionalRange);
|
||||
|
||||
$this->engine = Calculation::getInstance($this->worksheet->getParent());
|
||||
}
|
||||
|
||||
protected function setReferenceCellForExpressions(string $conditionalRange): void
|
||||
{
|
||||
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($conditionalRange)));
|
||||
[$this->referenceCell] = $conditionalRange[0];
|
||||
|
||||
[$this->referenceColumn, $this->referenceRow] = Coordinate::indexesFromString($this->referenceCell);
|
||||
|
||||
// Convert our conditional range to an absolute conditional range, so it can be used "pinned" in formulae
|
||||
$rangeSets = [];
|
||||
foreach ($conditionalRange as $rangeSet) {
|
||||
$absoluteRangeSet = array_map(
|
||||
[Coordinate::class, 'absoluteCoordinate'],
|
||||
$rangeSet
|
||||
);
|
||||
$rangeSets[] = implode(':', $absoluteRangeSet);
|
||||
}
|
||||
$this->conditionalRange = implode(',', $rangeSets);
|
||||
}
|
||||
|
||||
public function evaluateConditional(Conditional $conditional): bool
|
||||
{
|
||||
// Some calculations may modify the stored cell; so reset it before every evaluation.
|
||||
$cellColumn = Coordinate::stringFromColumnIndex($this->cellColumn);
|
||||
$cellAddress = "{$cellColumn}{$this->cellRow}";
|
||||
$this->cell = $this->worksheet->getCell($cellAddress);
|
||||
|
||||
switch ($conditional->getConditionType()) {
|
||||
case Conditional::CONDITION_CELLIS:
|
||||
return $this->processOperatorComparison($conditional);
|
||||
case Conditional::CONDITION_DUPLICATES:
|
||||
case Conditional::CONDITION_UNIQUE:
|
||||
return $this->processDuplicatesComparison($conditional);
|
||||
case Conditional::CONDITION_CONTAINSTEXT:
|
||||
// Expression is NOT(ISERROR(SEARCH("<TEXT>",<Cell Reference>)))
|
||||
case Conditional::CONDITION_NOTCONTAINSTEXT:
|
||||
// Expression is ISERROR(SEARCH("<TEXT>",<Cell Reference>))
|
||||
case Conditional::CONDITION_BEGINSWITH:
|
||||
// Expression is LEFT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
|
||||
case Conditional::CONDITION_ENDSWITH:
|
||||
// Expression is RIGHT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
|
||||
case Conditional::CONDITION_CONTAINSBLANKS:
|
||||
// Expression is LEN(TRIM(<Cell Reference>))=0
|
||||
case Conditional::CONDITION_NOTCONTAINSBLANKS:
|
||||
// Expression is LEN(TRIM(<Cell Reference>))>0
|
||||
case Conditional::CONDITION_CONTAINSERRORS:
|
||||
// Expression is ISERROR(<Cell Reference>)
|
||||
case Conditional::CONDITION_NOTCONTAINSERRORS:
|
||||
// Expression is NOT(ISERROR(<Cell Reference>))
|
||||
case Conditional::CONDITION_TIMEPERIOD:
|
||||
// Expression varies, depending on specified timePeriod value, e.g.
|
||||
// Yesterday FLOOR(<Cell Reference>,1)=TODAY()-1
|
||||
// Today FLOOR(<Cell Reference>,1)=TODAY()
|
||||
// Tomorrow FLOOR(<Cell Reference>,1)=TODAY()+1
|
||||
// Last 7 Days AND(TODAY()-FLOOR(<Cell Reference>,1)<=6,FLOOR(<Cell Reference>,1)<=TODAY())
|
||||
case Conditional::CONDITION_EXPRESSION:
|
||||
return $this->processExpression($conditional);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return float|int|string
|
||||
*/
|
||||
protected function wrapValue($value)
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
} elseif ($value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
return '"' . $value . '"';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float|int|string
|
||||
*/
|
||||
protected function wrapCellValue()
|
||||
{
|
||||
return $this->wrapValue($this->cell->getCalculatedValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float|int|string
|
||||
*/
|
||||
protected function conditionCellAdjustment(array $matches)
|
||||
{
|
||||
$column = $matches[6];
|
||||
$row = $matches[7];
|
||||
|
||||
if (strpos($column, '$') === false) {
|
||||
$column = Coordinate::columnIndexFromString($column);
|
||||
$column += $this->cellColumn - $this->referenceColumn;
|
||||
$column = Coordinate::stringFromColumnIndex($column);
|
||||
}
|
||||
|
||||
if (strpos($row, '$') === false) {
|
||||
$row += $this->cellRow - $this->referenceRow;
|
||||
}
|
||||
|
||||
if (!empty($matches[4])) {
|
||||
$worksheet = $this->worksheet->getParent()->getSheetByName(trim($matches[4], "'"));
|
||||
if ($worksheet === null) {
|
||||
return $this->wrapValue(null);
|
||||
}
|
||||
|
||||
return $this->wrapValue(
|
||||
$worksheet
|
||||
->getCell(str_replace('$', '', "{$column}{$row}"))
|
||||
->getCalculatedValue()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->wrapValue(
|
||||
$this->worksheet
|
||||
->getCell(str_replace('$', '', "{$column}{$row}"))
|
||||
->getCalculatedValue()
|
||||
);
|
||||
}
|
||||
|
||||
protected function cellConditionCheck(string $condition): string
|
||||
{
|
||||
$splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
|
||||
$i = false;
|
||||
foreach ($splitCondition as &$value) {
|
||||
// Only count/replace in alternating array entries (ie. not in quoted strings)
|
||||
$i = $i === false;
|
||||
if ($i) {
|
||||
$value = (string) preg_replace_callback(
|
||||
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
|
||||
[$this, 'conditionCellAdjustment'],
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
// Then rebuild the condition string to return it
|
||||
return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
|
||||
}
|
||||
|
||||
protected function adjustConditionsForCellReferences(array $conditions): array
|
||||
{
|
||||
return array_map(
|
||||
[$this, 'cellConditionCheck'],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
protected function processOperatorComparison(Conditional $conditional): bool
|
||||
{
|
||||
if (array_key_exists($conditional->getOperatorType(), self::COMPARISON_RANGE_OPERATORS)) {
|
||||
return $this->processRangeOperator($conditional);
|
||||
}
|
||||
|
||||
$operator = self::COMPARISON_OPERATORS[$conditional->getOperatorType()];
|
||||
$conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
|
||||
$expression = sprintf('%s%s%s', (string) $this->wrapCellValue(), $operator, (string) array_pop($conditions));
|
||||
|
||||
return $this->evaluateExpression($expression);
|
||||
}
|
||||
|
||||
protected function processRangeOperator(Conditional $conditional): bool
|
||||
{
|
||||
$conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
|
||||
sort($conditions);
|
||||
$expression = sprintf(
|
||||
(string) preg_replace(
|
||||
'/\bA1\b/i',
|
||||
(string) $this->wrapCellValue(),
|
||||
self::COMPARISON_RANGE_OPERATORS[$conditional->getOperatorType()]
|
||||
),
|
||||
...$conditions
|
||||
);
|
||||
|
||||
return $this->evaluateExpression($expression);
|
||||
}
|
||||
|
||||
protected function processDuplicatesComparison(Conditional $conditional): bool
|
||||
{
|
||||
$worksheetName = $this->cell->getWorksheet()->getTitle();
|
||||
|
||||
$expression = sprintf(
|
||||
self::COMPARISON_DUPLICATES_OPERATORS[$conditional->getConditionType()],
|
||||
$worksheetName,
|
||||
$this->conditionalRange,
|
||||
$this->cellConditionCheck($this->cell->getCalculatedValue())
|
||||
);
|
||||
|
||||
return $this->evaluateExpression($expression);
|
||||
}
|
||||
|
||||
protected function processExpression(Conditional $conditional): bool
|
||||
{
|
||||
$conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
|
||||
$expression = array_pop($conditions);
|
||||
|
||||
$expression = (string) preg_replace(
|
||||
'/\b' . $this->referenceCell . '\b/i',
|
||||
(string) $this->wrapCellValue(),
|
||||
$expression
|
||||
);
|
||||
|
||||
return $this->evaluateExpression($expression);
|
||||
}
|
||||
|
||||
protected function evaluateExpression(string $expression): bool
|
||||
{
|
||||
$expression = "={$expression}";
|
||||
|
||||
try {
|
||||
$this->engine->flushInstance();
|
||||
$result = (bool) $this->engine->calculateFormula($expression);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
class CellStyleAssessor
|
||||
{
|
||||
/**
|
||||
* @var CellMatcher
|
||||
*/
|
||||
protected $cellMatcher;
|
||||
|
||||
/**
|
||||
* @var StyleMerger
|
||||
*/
|
||||
protected $styleMerger;
|
||||
|
||||
public function __construct(Cell $cell, string $conditionalRange)
|
||||
{
|
||||
$this->cellMatcher = new CellMatcher($cell, $conditionalRange);
|
||||
$this->styleMerger = new StyleMerger($cell->getStyle());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Conditional[] $conditionalStyles
|
||||
*/
|
||||
public function matchConditions(array $conditionalStyles = []): Style
|
||||
{
|
||||
foreach ($conditionalStyles as $conditional) {
|
||||
/** @var Conditional $conditional */
|
||||
if ($this->cellMatcher->evaluateConditional($conditional) === true) {
|
||||
// Merging the conditional style into the base style goes in here
|
||||
$this->styleMerger->mergeStyle($conditional->getStyle());
|
||||
if ($conditional->getStopIfTrue() === true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->styleMerger->getStyle();
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
class ConditionalDataBar
|
||||
{
|
||||
/** <dataBar> attribute */
|
||||
|
||||
/** @var null|bool */
|
||||
private $showValue;
|
||||
|
||||
/** <dataBar> children */
|
||||
|
||||
/** @var ?ConditionalFormatValueObject */
|
||||
private $minimumConditionalFormatValueObject;
|
||||
|
||||
/** @var ?ConditionalFormatValueObject */
|
||||
private $maximumConditionalFormatValueObject;
|
||||
|
||||
/** @var string */
|
||||
private $color;
|
||||
|
||||
/** <extLst> */
|
||||
|
||||
/** @var ?ConditionalFormattingRuleExtension */
|
||||
private $conditionalFormattingRuleExt;
|
||||
|
||||
/**
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getShowValue()
|
||||
{
|
||||
return $this->showValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $showValue
|
||||
*/
|
||||
public function setShowValue($showValue)
|
||||
{
|
||||
$this->showValue = $showValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMinimumConditionalFormatValueObject(): ?ConditionalFormatValueObject
|
||||
{
|
||||
return $this->minimumConditionalFormatValueObject;
|
||||
}
|
||||
|
||||
public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject)
|
||||
{
|
||||
$this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaximumConditionalFormatValueObject(): ?ConditionalFormatValueObject
|
||||
{
|
||||
return $this->maximumConditionalFormatValueObject;
|
||||
}
|
||||
|
||||
public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject)
|
||||
{
|
||||
$this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): self
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConditionalFormattingRuleExt(): ?ConditionalFormattingRuleExtension
|
||||
{
|
||||
return $this->conditionalFormattingRuleExt;
|
||||
}
|
||||
|
||||
public function setConditionalFormattingRuleExt(ConditionalFormattingRuleExtension $conditionalFormattingRuleExt)
|
||||
{
|
||||
$this->conditionalFormattingRuleExt = $conditionalFormattingRuleExt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
class ConditionalDataBarExtension
|
||||
{
|
||||
/** <dataBar> attributes */
|
||||
|
||||
/** @var int */
|
||||
private $minLength;
|
||||
|
||||
/** @var int */
|
||||
private $maxLength;
|
||||
|
||||
/** @var null|bool */
|
||||
private $border;
|
||||
|
||||
/** @var null|bool */
|
||||
private $gradient;
|
||||
|
||||
/** @var string */
|
||||
private $direction;
|
||||
|
||||
/** @var null|bool */
|
||||
private $negativeBarBorderColorSameAsPositive;
|
||||
|
||||
/** @var string */
|
||||
private $axisPosition;
|
||||
|
||||
// <dataBar> children
|
||||
|
||||
/** @var ConditionalFormatValueObject */
|
||||
private $maximumConditionalFormatValueObject;
|
||||
|
||||
/** @var ConditionalFormatValueObject */
|
||||
private $minimumConditionalFormatValueObject;
|
||||
|
||||
/** @var string */
|
||||
private $borderColor;
|
||||
|
||||
/** @var string */
|
||||
private $negativeFillColor;
|
||||
|
||||
/** @var string */
|
||||
private $negativeBorderColor;
|
||||
|
||||
/** @var array */
|
||||
private $axisColor = [
|
||||
'rgb' => null,
|
||||
'theme' => null,
|
||||
'tint' => null,
|
||||
];
|
||||
|
||||
public function getXmlAttributes()
|
||||
{
|
||||
$ret = [];
|
||||
foreach (['minLength', 'maxLength', 'direction', 'axisPosition'] as $attrKey) {
|
||||
if (null !== $this->{$attrKey}) {
|
||||
$ret[$attrKey] = $this->{$attrKey};
|
||||
}
|
||||
}
|
||||
foreach (['border', 'gradient', 'negativeBarBorderColorSameAsPositive'] as $attrKey) {
|
||||
if (null !== $this->{$attrKey}) {
|
||||
$ret[$attrKey] = $this->{$attrKey} ? '1' : '0';
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getXmlElements()
|
||||
{
|
||||
$ret = [];
|
||||
$elms = ['borderColor', 'negativeFillColor', 'negativeBorderColor'];
|
||||
foreach ($elms as $elmKey) {
|
||||
if (null !== $this->{$elmKey}) {
|
||||
$ret[$elmKey] = ['rgb' => $this->{$elmKey}];
|
||||
}
|
||||
}
|
||||
foreach (array_filter($this->axisColor) as $attrKey => $axisColorAttr) {
|
||||
if (!isset($ret['axisColor'])) {
|
||||
$ret['axisColor'] = [];
|
||||
}
|
||||
$ret['axisColor'][$attrKey] = $axisColorAttr;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMinLength()
|
||||
{
|
||||
return $this->minLength;
|
||||
}
|
||||
|
||||
public function setMinLength(int $minLength): self
|
||||
{
|
||||
$this->minLength = $minLength;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxLength()
|
||||
{
|
||||
return $this->maxLength;
|
||||
}
|
||||
|
||||
public function setMaxLength(int $maxLength): self
|
||||
{
|
||||
$this->maxLength = $maxLength;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getBorder()
|
||||
{
|
||||
return $this->border;
|
||||
}
|
||||
|
||||
public function setBorder(bool $border): self
|
||||
{
|
||||
$this->border = $border;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getGradient()
|
||||
{
|
||||
return $this->gradient;
|
||||
}
|
||||
|
||||
public function setGradient(bool $gradient): self
|
||||
{
|
||||
$this->gradient = $gradient;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDirection()
|
||||
{
|
||||
return $this->direction;
|
||||
}
|
||||
|
||||
public function setDirection(string $direction): self
|
||||
{
|
||||
$this->direction = $direction;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getNegativeBarBorderColorSameAsPositive()
|
||||
{
|
||||
return $this->negativeBarBorderColorSameAsPositive;
|
||||
}
|
||||
|
||||
public function setNegativeBarBorderColorSameAsPositive(bool $negativeBarBorderColorSameAsPositive): self
|
||||
{
|
||||
$this->negativeBarBorderColorSameAsPositive = $negativeBarBorderColorSameAsPositive;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAxisPosition()
|
||||
{
|
||||
return $this->axisPosition;
|
||||
}
|
||||
|
||||
public function setAxisPosition(string $axisPosition): self
|
||||
{
|
||||
$this->axisPosition = $axisPosition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConditionalFormatValueObject
|
||||
*/
|
||||
public function getMaximumConditionalFormatValueObject()
|
||||
{
|
||||
return $this->maximumConditionalFormatValueObject;
|
||||
}
|
||||
|
||||
public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject)
|
||||
{
|
||||
$this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConditionalFormatValueObject
|
||||
*/
|
||||
public function getMinimumConditionalFormatValueObject()
|
||||
{
|
||||
return $this->minimumConditionalFormatValueObject;
|
||||
}
|
||||
|
||||
public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject)
|
||||
{
|
||||
$this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBorderColor()
|
||||
{
|
||||
return $this->borderColor;
|
||||
}
|
||||
|
||||
public function setBorderColor(string $borderColor): self
|
||||
{
|
||||
$this->borderColor = $borderColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNegativeFillColor()
|
||||
{
|
||||
return $this->negativeFillColor;
|
||||
}
|
||||
|
||||
public function setNegativeFillColor(string $negativeFillColor): self
|
||||
{
|
||||
$this->negativeFillColor = $negativeFillColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNegativeBorderColor()
|
||||
{
|
||||
return $this->negativeBorderColor;
|
||||
}
|
||||
|
||||
public function setNegativeBorderColor(string $negativeBorderColor): self
|
||||
{
|
||||
$this->negativeBorderColor = $negativeBorderColor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAxisColor(): array
|
||||
{
|
||||
return $this->axisColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $rgb
|
||||
* @param null|mixed $theme
|
||||
* @param null|mixed $tint
|
||||
*/
|
||||
public function setAxisColor($rgb, $theme = null, $tint = null): self
|
||||
{
|
||||
$this->axisColor = [
|
||||
'rgb' => $rgb,
|
||||
'theme' => $theme,
|
||||
'tint' => $tint,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
class ConditionalFormatValueObject
|
||||
{
|
||||
private $type;
|
||||
|
||||
private $value;
|
||||
|
||||
private $cellFormula;
|
||||
|
||||
/**
|
||||
* ConditionalFormatValueObject constructor.
|
||||
*
|
||||
* @param null|mixed $cellFormula
|
||||
*/
|
||||
public function __construct($type, $value = null, $cellFormula = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->value = $value;
|
||||
$this->cellFormula = $cellFormula;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $type
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCellFormula()
|
||||
{
|
||||
return $this->cellFormula;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cellFormula
|
||||
*/
|
||||
public function setCellFormula($cellFormula)
|
||||
{
|
||||
$this->cellFormula = $cellFormula;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class ConditionalFormattingRuleExtension
|
||||
{
|
||||
const CONDITION_EXTENSION_DATABAR = 'dataBar';
|
||||
|
||||
/** <conditionalFormatting> attributes */
|
||||
private $id;
|
||||
|
||||
/** @var string Conditional Formatting Rule */
|
||||
private $cfRule;
|
||||
|
||||
/** <conditionalFormatting> children */
|
||||
|
||||
/** @var ConditionalDataBarExtension */
|
||||
private $dataBar;
|
||||
|
||||
/** @var string Sequence of References */
|
||||
private $sqref;
|
||||
|
||||
/**
|
||||
* ConditionalFormattingRuleExtension constructor.
|
||||
*/
|
||||
public function __construct($id = null, string $cfRule = self::CONDITION_EXTENSION_DATABAR)
|
||||
{
|
||||
if (null === $id) {
|
||||
$this->id = '{' . $this->generateUuid() . '}';
|
||||
} else {
|
||||
$this->id = $id;
|
||||
}
|
||||
$this->cfRule = $cfRule;
|
||||
}
|
||||
|
||||
private function generateUuid()
|
||||
{
|
||||
$chars = str_split('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx');
|
||||
|
||||
foreach ($chars as $i => $char) {
|
||||
if ($char === 'x') {
|
||||
$chars[$i] = dechex(random_int(0, 15));
|
||||
} elseif ($char === 'y') {
|
||||
$chars[$i] = dechex(random_int(8, 11));
|
||||
}
|
||||
}
|
||||
|
||||
return implode('', /** @scrutinizer ignore-type */ $chars);
|
||||
}
|
||||
|
||||
public static function parseExtLstXml($extLstXml)
|
||||
{
|
||||
$conditionalFormattingRuleExtensions = [];
|
||||
$conditionalFormattingRuleExtensionXml = null;
|
||||
if ($extLstXml instanceof SimpleXMLElement) {
|
||||
foreach ((count($extLstXml) > 0 ? $extLstXml : [$extLstXml]) as $extLst) {
|
||||
//this uri is conditionalFormattings
|
||||
//https://docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/07d607af-5618-4ca2-b683-6a78dc0d9627
|
||||
if (isset($extLst->ext['uri']) && (string) $extLst->ext['uri'] === '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
|
||||
$conditionalFormattingRuleExtensionXml = $extLst->ext;
|
||||
}
|
||||
}
|
||||
|
||||
if ($conditionalFormattingRuleExtensionXml) {
|
||||
$ns = $conditionalFormattingRuleExtensionXml->getNamespaces(true);
|
||||
$extFormattingsXml = $conditionalFormattingRuleExtensionXml->children($ns['x14']);
|
||||
|
||||
foreach ($extFormattingsXml->children($ns['x14']) as $extFormattingXml) {
|
||||
$extCfRuleXml = $extFormattingXml->cfRule;
|
||||
$attributes = $extCfRuleXml->attributes();
|
||||
if (!$attributes || ((string) $attributes->type) !== Conditional::CONDITION_DATABAR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$extFormattingRuleObj = new self((string) $attributes->id);
|
||||
$extFormattingRuleObj->setSqref((string) $extFormattingXml->children($ns['xm'])->sqref);
|
||||
$conditionalFormattingRuleExtensions[$extFormattingRuleObj->getId()] = $extFormattingRuleObj;
|
||||
|
||||
$extDataBarObj = new ConditionalDataBarExtension();
|
||||
$extFormattingRuleObj->setDataBarExt($extDataBarObj);
|
||||
$dataBarXml = $extCfRuleXml->dataBar;
|
||||
self::parseExtDataBarAttributesFromXml($extDataBarObj, $dataBarXml);
|
||||
self::parseExtDataBarElementChildrenFromXml($extDataBarObj, $dataBarXml, $ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $conditionalFormattingRuleExtensions;
|
||||
}
|
||||
|
||||
private static function parseExtDataBarAttributesFromXml(
|
||||
ConditionalDataBarExtension $extDataBarObj,
|
||||
SimpleXMLElement $dataBarXml
|
||||
): void {
|
||||
$dataBarAttribute = $dataBarXml->attributes();
|
||||
if ($dataBarAttribute->minLength) {
|
||||
$extDataBarObj->setMinLength((int) $dataBarAttribute->minLength);
|
||||
}
|
||||
if ($dataBarAttribute->maxLength) {
|
||||
$extDataBarObj->setMaxLength((int) $dataBarAttribute->maxLength);
|
||||
}
|
||||
if ($dataBarAttribute->border) {
|
||||
$extDataBarObj->setBorder((bool) (string) $dataBarAttribute->border);
|
||||
}
|
||||
if ($dataBarAttribute->gradient) {
|
||||
$extDataBarObj->setGradient((bool) (string) $dataBarAttribute->gradient);
|
||||
}
|
||||
if ($dataBarAttribute->direction) {
|
||||
$extDataBarObj->setDirection((string) $dataBarAttribute->direction);
|
||||
}
|
||||
if ($dataBarAttribute->negativeBarBorderColorSameAsPositive) {
|
||||
$extDataBarObj->setNegativeBarBorderColorSameAsPositive((bool) (string) $dataBarAttribute->negativeBarBorderColorSameAsPositive);
|
||||
}
|
||||
if ($dataBarAttribute->axisPosition) {
|
||||
$extDataBarObj->setAxisPosition((string) $dataBarAttribute->axisPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static function parseExtDataBarElementChildrenFromXml(ConditionalDataBarExtension $extDataBarObj, SimpleXMLElement $dataBarXml, $ns): void
|
||||
{
|
||||
if ($dataBarXml->borderColor) {
|
||||
$extDataBarObj->setBorderColor((string) $dataBarXml->borderColor->attributes()['rgb']);
|
||||
}
|
||||
if ($dataBarXml->negativeFillColor) {
|
||||
$extDataBarObj->setNegativeFillColor((string) $dataBarXml->negativeFillColor->attributes()['rgb']);
|
||||
}
|
||||
if ($dataBarXml->negativeBorderColor) {
|
||||
$extDataBarObj->setNegativeBorderColor((string) $dataBarXml->negativeBorderColor->attributes()['rgb']);
|
||||
}
|
||||
if ($dataBarXml->axisColor) {
|
||||
$axisColorAttr = $dataBarXml->axisColor->attributes();
|
||||
$extDataBarObj->setAxisColor((string) $axisColorAttr['rgb'], (string) $axisColorAttr['theme'], (string) $axisColorAttr['tint']);
|
||||
}
|
||||
$cfvoIndex = 0;
|
||||
foreach ($dataBarXml->cfvo as $cfvo) {
|
||||
$f = (string) $cfvo->/** @scrutinizer ignore-call */ children($ns['xm'])->f;
|
||||
/** @scrutinizer ignore-call */
|
||||
$attributes = $cfvo->attributes();
|
||||
if (!($attributes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($cfvoIndex === 0) {
|
||||
$extDataBarObj->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $attributes['type'], null, (empty($f) ? null : $f)));
|
||||
}
|
||||
if ($cfvoIndex === 1) {
|
||||
$extDataBarObj->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $attributes['type'], null, (empty($f) ? null : $f)));
|
||||
}
|
||||
++$cfvoIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function setId($id): self
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCfRule(): string
|
||||
{
|
||||
return $this->cfRule;
|
||||
}
|
||||
|
||||
public function setCfRule(string $cfRule): self
|
||||
{
|
||||
$this->cfRule = $cfRule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDataBarExt(): ConditionalDataBarExtension
|
||||
{
|
||||
return $this->dataBar;
|
||||
}
|
||||
|
||||
public function setDataBarExt(ConditionalDataBarExtension $dataBar): self
|
||||
{
|
||||
$this->dataBar = $dataBar;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSqref(): string
|
||||
{
|
||||
return $this->sqref;
|
||||
}
|
||||
|
||||
public function setSqref(string $sqref): self
|
||||
{
|
||||
$this->sqref = $sqref;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
118
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
vendored
Normal file
118
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
class StyleMerger
|
||||
{
|
||||
/**
|
||||
* @var Style
|
||||
*/
|
||||
protected $baseStyle;
|
||||
|
||||
public function __construct(Style $baseStyle)
|
||||
{
|
||||
$this->baseStyle = $baseStyle;
|
||||
}
|
||||
|
||||
public function getStyle(): Style
|
||||
{
|
||||
return $this->baseStyle;
|
||||
}
|
||||
|
||||
public function mergeStyle(Style $style): void
|
||||
{
|
||||
if ($style->getNumberFormat() !== null && $style->getNumberFormat()->getFormatCode() !== null) {
|
||||
$this->baseStyle->getNumberFormat()->setFormatCode($style->getNumberFormat()->getFormatCode());
|
||||
}
|
||||
|
||||
if ($style->getFont() !== null) {
|
||||
$this->mergeFontStyle($this->baseStyle->getFont(), $style->getFont());
|
||||
}
|
||||
|
||||
if ($style->getFill() !== null) {
|
||||
$this->mergeFillStyle($this->baseStyle->getFill(), $style->getFill());
|
||||
}
|
||||
|
||||
if ($style->getBorders() !== null) {
|
||||
$this->mergeBordersStyle($this->baseStyle->getBorders(), $style->getBorders());
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeFontStyle(Font $baseFontStyle, Font $fontStyle): void
|
||||
{
|
||||
if ($fontStyle->getBold() !== null) {
|
||||
$baseFontStyle->setBold($fontStyle->getBold());
|
||||
}
|
||||
|
||||
if ($fontStyle->getItalic() !== null) {
|
||||
$baseFontStyle->setItalic($fontStyle->getItalic());
|
||||
}
|
||||
|
||||
if ($fontStyle->getStrikethrough() !== null) {
|
||||
$baseFontStyle->setStrikethrough($fontStyle->getStrikethrough());
|
||||
}
|
||||
|
||||
if ($fontStyle->getUnderline() !== null) {
|
||||
$baseFontStyle->setUnderline($fontStyle->getUnderline());
|
||||
}
|
||||
|
||||
if ($fontStyle->getColor() !== null && $fontStyle->getColor()->getARGB() !== null) {
|
||||
$baseFontStyle->setColor($fontStyle->getColor());
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeFillStyle(Fill $baseFillStyle, Fill $fillStyle): void
|
||||
{
|
||||
if ($fillStyle->getFillType() !== null) {
|
||||
$baseFillStyle->setFillType($fillStyle->getFillType());
|
||||
}
|
||||
|
||||
//if ($fillStyle->getRotation() !== null) {
|
||||
$baseFillStyle->setRotation($fillStyle->getRotation());
|
||||
//}
|
||||
|
||||
if ($fillStyle->getStartColor() !== null && $fillStyle->getStartColor()->getARGB() !== null) {
|
||||
$baseFillStyle->setStartColor($fillStyle->getStartColor());
|
||||
}
|
||||
|
||||
if ($fillStyle->getEndColor() !== null && $fillStyle->getEndColor()->getARGB() !== null) {
|
||||
$baseFillStyle->setEndColor($fillStyle->getEndColor());
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeBordersStyle(Borders $baseBordersStyle, Borders $bordersStyle): void
|
||||
{
|
||||
if ($bordersStyle->getTop() !== null) {
|
||||
$this->mergeBorderStyle($baseBordersStyle->getTop(), $bordersStyle->getTop());
|
||||
}
|
||||
|
||||
if ($bordersStyle->getBottom() !== null) {
|
||||
$this->mergeBorderStyle($baseBordersStyle->getBottom(), $bordersStyle->getBottom());
|
||||
}
|
||||
|
||||
if ($bordersStyle->getLeft() !== null) {
|
||||
$this->mergeBorderStyle($baseBordersStyle->getLeft(), $bordersStyle->getLeft());
|
||||
}
|
||||
|
||||
if ($bordersStyle->getRight() !== null) {
|
||||
$this->mergeBorderStyle($baseBordersStyle->getRight(), $bordersStyle->getRight());
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeBorderStyle(Border $baseBorderStyle, Border $borderStyle): void
|
||||
{
|
||||
//if ($borderStyle->getBorderStyle() !== null) {
|
||||
$baseBorderStyle->setBorderStyle($borderStyle->getBorderStyle());
|
||||
//}
|
||||
|
||||
if ($borderStyle->getColor() !== null && $borderStyle->getColor()->getARGB() !== null) {
|
||||
$baseBorderStyle->setColor($borderStyle->getColor());
|
||||
}
|
||||
}
|
||||
}
|
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
vendored
Normal file
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard\WizardInterface;
|
||||
|
||||
class Wizard
|
||||
{
|
||||
public const CELL_VALUE = 'cellValue';
|
||||
public const TEXT_VALUE = 'textValue';
|
||||
public const BLANKS = Conditional::CONDITION_CONTAINSBLANKS;
|
||||
public const NOT_BLANKS = Conditional::CONDITION_NOTCONTAINSBLANKS;
|
||||
public const ERRORS = Conditional::CONDITION_CONTAINSERRORS;
|
||||
public const NOT_ERRORS = Conditional::CONDITION_NOTCONTAINSERRORS;
|
||||
public const EXPRESSION = Conditional::CONDITION_EXPRESSION;
|
||||
public const FORMULA = Conditional::CONDITION_EXPRESSION;
|
||||
public const DATES_OCCURRING = 'DateValue';
|
||||
public const DUPLICATES = Conditional::CONDITION_DUPLICATES;
|
||||
public const UNIQUE = Conditional::CONDITION_UNIQUE;
|
||||
|
||||
public const VALUE_TYPE_LITERAL = 'value';
|
||||
public const VALUE_TYPE_CELL = 'cell';
|
||||
public const VALUE_TYPE_FORMULA = 'formula';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cellRange;
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
$this->cellRange = $cellRange;
|
||||
}
|
||||
|
||||
public function newRule(string $ruleType): WizardInterface
|
||||
{
|
||||
switch ($ruleType) {
|
||||
case self::CELL_VALUE:
|
||||
return new Wizard\CellValue($this->cellRange);
|
||||
case self::TEXT_VALUE:
|
||||
return new Wizard\TextValue($this->cellRange);
|
||||
case self::BLANKS:
|
||||
return new Wizard\Blanks($this->cellRange, true);
|
||||
case self::NOT_BLANKS:
|
||||
return new Wizard\Blanks($this->cellRange, false);
|
||||
case self::ERRORS:
|
||||
return new Wizard\Errors($this->cellRange, true);
|
||||
case self::NOT_ERRORS:
|
||||
return new Wizard\Errors($this->cellRange, false);
|
||||
case self::EXPRESSION:
|
||||
case self::FORMULA:
|
||||
return new Wizard\Expression($this->cellRange);
|
||||
case self::DATES_OCCURRING:
|
||||
return new Wizard\DateValue($this->cellRange);
|
||||
case self::DUPLICATES:
|
||||
return new Wizard\Duplicates($this->cellRange, false);
|
||||
case self::UNIQUE:
|
||||
return new Wizard\Duplicates($this->cellRange, true);
|
||||
default:
|
||||
throw new Exception('No wizard exists for this CF rule type');
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
|
||||
{
|
||||
$conditionalType = $conditional->getConditionType();
|
||||
|
||||
switch ($conditionalType) {
|
||||
case Conditional::CONDITION_CELLIS:
|
||||
return Wizard\CellValue::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_CONTAINSTEXT:
|
||||
case Conditional::CONDITION_NOTCONTAINSTEXT:
|
||||
case Conditional::CONDITION_BEGINSWITH:
|
||||
case Conditional::CONDITION_ENDSWITH:
|
||||
return Wizard\TextValue::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_CONTAINSBLANKS:
|
||||
case Conditional::CONDITION_NOTCONTAINSBLANKS:
|
||||
return Wizard\Blanks::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_CONTAINSERRORS:
|
||||
case Conditional::CONDITION_NOTCONTAINSERRORS:
|
||||
return Wizard\Errors::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_TIMEPERIOD:
|
||||
return Wizard\DateValue::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_EXPRESSION:
|
||||
return Wizard\Expression::fromConditional($conditional, $cellRange);
|
||||
case Conditional::CONDITION_DUPLICATES:
|
||||
case Conditional::CONDITION_UNIQUE:
|
||||
return Wizard\Duplicates::fromConditional($conditional, $cellRange);
|
||||
default:
|
||||
throw new Exception('No wizard exists for this CF rule type');
|
||||
}
|
||||
}
|
||||
}
|
99
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
vendored
Normal file
99
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
/**
|
||||
* @method Blanks notBlank()
|
||||
* @method Blanks notEmpty()
|
||||
* @method Blanks isBlank()
|
||||
* @method Blanks isEmpty()
|
||||
*/
|
||||
class Blanks extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const OPERATORS = [
|
||||
'notBlank' => false,
|
||||
'isBlank' => true,
|
||||
'notEmpty' => false,
|
||||
'empty' => true,
|
||||
];
|
||||
|
||||
protected const EXPRESSIONS = [
|
||||
Wizard::NOT_BLANKS => 'LEN(TRIM(%s))>0',
|
||||
Wizard::BLANKS => 'LEN(TRIM(%s))=0',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $inverse;
|
||||
|
||||
public function __construct(string $cellRange, bool $inverse = false)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
protected function inverse(bool $inverse): void
|
||||
{
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
protected function getExpression(): void
|
||||
{
|
||||
$this->expression = sprintf(
|
||||
self::EXPRESSIONS[$this->inverse ? Wizard::BLANKS : Wizard::NOT_BLANKS],
|
||||
$this->referenceCell
|
||||
);
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$this->getExpression();
|
||||
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(
|
||||
$this->inverse ? Conditional::CONDITION_CONTAINSBLANKS : Conditional::CONDITION_NOTCONTAINSBLANKS
|
||||
);
|
||||
$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 (
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_CONTAINSBLANKS &&
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSBLANKS
|
||||
) {
|
||||
throw new Exception('Conditional is not a Blanks CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
$wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_CONTAINSBLANKS;
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!array_key_exists($methodName, self::OPERATORS)) {
|
||||
throw new Exception('Invalid Operation for Blanks CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->inverse(self::OPERATORS[$methodName]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
200
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
vendored
Normal file
200
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellMatcher;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
/**
|
||||
* @method CellValue equals($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue notEquals($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue greaterThan($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue greaterThanOrEqual($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue lessThan($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue lessThanOrEqual($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue between($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue notBetween($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method CellValue and($value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
*/
|
||||
class CellValue extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const MAGIC_OPERATIONS = [
|
||||
'equals' => Conditional::OPERATOR_EQUAL,
|
||||
'notEquals' => Conditional::OPERATOR_NOTEQUAL,
|
||||
'greaterThan' => Conditional::OPERATOR_GREATERTHAN,
|
||||
'greaterThanOrEqual' => Conditional::OPERATOR_GREATERTHANOREQUAL,
|
||||
'lessThan' => Conditional::OPERATOR_LESSTHAN,
|
||||
'lessThanOrEqual' => Conditional::OPERATOR_LESSTHANOREQUAL,
|
||||
'between' => Conditional::OPERATOR_BETWEEN,
|
||||
'notBetween' => Conditional::OPERATOR_NOTBETWEEN,
|
||||
];
|
||||
|
||||
protected const SINGLE_OPERATORS = CellMatcher::COMPARISON_OPERATORS;
|
||||
|
||||
protected const RANGE_OPERATORS = CellMatcher::COMPARISON_RANGE_OPERATORS;
|
||||
|
||||
/** @var string */
|
||||
protected $operator = Conditional::OPERATOR_EQUAL;
|
||||
|
||||
/** @var array */
|
||||
protected $operand = [0];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $operandValueType = [];
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
}
|
||||
|
||||
protected function operator(string $operator): void
|
||||
{
|
||||
if ((!isset(self::SINGLE_OPERATORS[$operator])) && (!isset(self::RANGE_OPERATORS[$operator]))) {
|
||||
throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $operand
|
||||
*/
|
||||
protected function operand(int $index, $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
|
||||
{
|
||||
if (is_string($operand)) {
|
||||
$operand = $this->validateOperand($operand, $operandValueType);
|
||||
}
|
||||
|
||||
$this->operand[$index] = $operand;
|
||||
$this->operandValueType[$index] = $operandValueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return float|int|string
|
||||
*/
|
||||
protected function wrapValue($value, string $operandValueType)
|
||||
{
|
||||
if (!is_numeric($value) && !is_bool($value) && null !== $value) {
|
||||
if ($operandValueType === Wizard::VALUE_TYPE_LITERAL) {
|
||||
return '"' . str_replace('"', '""', $value) . '"';
|
||||
}
|
||||
|
||||
return $this->cellConditionCheck($value);
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
$value = 'NULL';
|
||||
} elseif (is_bool($value)) {
|
||||
$value = $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
if (!isset(self::RANGE_OPERATORS[$this->operator])) {
|
||||
unset($this->operand[1], $this->operandValueType[1]);
|
||||
}
|
||||
$values = array_map([$this, 'wrapValue'], $this->operand, $this->operandValueType);
|
||||
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(Conditional::CONDITION_CELLIS);
|
||||
$conditional->setOperatorType($this->operator);
|
||||
$conditional->setConditions($values);
|
||||
$conditional->setStyle($this->getStyle());
|
||||
$conditional->setStopIfTrue($this->getStopIfTrue());
|
||||
|
||||
return $conditional;
|
||||
}
|
||||
|
||||
protected static function unwrapString(string $condition): string
|
||||
{
|
||||
if ((strpos($condition, '"') === 0) && (strpos(strrev($condition), '"') === 0)) {
|
||||
$condition = substr($condition, 1, -1);
|
||||
}
|
||||
|
||||
return str_replace('""', '"', $condition);
|
||||
}
|
||||
|
||||
public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
|
||||
{
|
||||
if ($conditional->getConditionType() !== Conditional::CONDITION_CELLIS) {
|
||||
throw new Exception('Conditional is not a Cell Value CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
|
||||
$wizard->operator = $conditional->getOperatorType();
|
||||
$conditions = $conditional->getConditions();
|
||||
foreach ($conditions as $index => $condition) {
|
||||
// Best-guess to try and identify if the text is a string literal, a cell reference or a formula?
|
||||
$operandValueType = Wizard::VALUE_TYPE_LITERAL;
|
||||
if (is_string($condition)) {
|
||||
if (Calculation::keyInExcelConstants($condition)) {
|
||||
$condition = Calculation::getExcelConstants($condition);
|
||||
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '$/i', $condition)) {
|
||||
$operandValueType = Wizard::VALUE_TYPE_CELL;
|
||||
$condition = self::reverseAdjustCellRef($condition, $cellRange);
|
||||
} elseif (
|
||||
preg_match('/\(\)/', $condition) ||
|
||||
preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
|
||||
) {
|
||||
$operandValueType = Wizard::VALUE_TYPE_FORMULA;
|
||||
$condition = self::reverseAdjustCellRef($condition, $cellRange);
|
||||
} else {
|
||||
$condition = self::unwrapString($condition);
|
||||
}
|
||||
}
|
||||
$wizard->operand($index, $condition, $operandValueType);
|
||||
}
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!isset(self::MAGIC_OPERATIONS[$methodName]) && $methodName !== 'and') {
|
||||
throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
|
||||
}
|
||||
|
||||
if ($methodName === 'and') {
|
||||
if (!isset(self::RANGE_OPERATORS[$this->operator])) {
|
||||
throw new Exception('AND Value is only appropriate for range operators');
|
||||
}
|
||||
|
||||
// Scrutinizer ignores its own suggested workaround.
|
||||
//$this->operand(1, /** @scrutinizer ignore-type */ ...$arguments);
|
||||
if (count($arguments) < 2) {
|
||||
$this->operand(1, $arguments[0]);
|
||||
} else {
|
||||
$this->operand(1, $arguments[0], $arguments[1]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->operator(self::MAGIC_OPERATIONS[$methodName]);
|
||||
//$this->operand(0, ...$arguments);
|
||||
if (count($arguments) < 2) {
|
||||
$this->operand(0, $arguments[0]);
|
||||
} else {
|
||||
$this->operand(0, $arguments[0], $arguments[1]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
111
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
vendored
Normal file
111
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
|
||||
/**
|
||||
* @method DateValue yesterday()
|
||||
* @method DateValue today()
|
||||
* @method DateValue tomorrow()
|
||||
* @method DateValue lastSevenDays()
|
||||
* @method DateValue lastWeek()
|
||||
* @method DateValue thisWeek()
|
||||
* @method DateValue nextWeek()
|
||||
* @method DateValue lastMonth()
|
||||
* @method DateValue thisMonth()
|
||||
* @method DateValue nextMonth()
|
||||
*/
|
||||
class DateValue extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const MAGIC_OPERATIONS = [
|
||||
'yesterday' => Conditional::TIMEPERIOD_YESTERDAY,
|
||||
'today' => Conditional::TIMEPERIOD_TODAY,
|
||||
'tomorrow' => Conditional::TIMEPERIOD_TOMORROW,
|
||||
'lastSevenDays' => Conditional::TIMEPERIOD_LAST_7_DAYS,
|
||||
'last7Days' => Conditional::TIMEPERIOD_LAST_7_DAYS,
|
||||
'lastWeek' => Conditional::TIMEPERIOD_LAST_WEEK,
|
||||
'thisWeek' => Conditional::TIMEPERIOD_THIS_WEEK,
|
||||
'nextWeek' => Conditional::TIMEPERIOD_NEXT_WEEK,
|
||||
'lastMonth' => Conditional::TIMEPERIOD_LAST_MONTH,
|
||||
'thisMonth' => Conditional::TIMEPERIOD_THIS_MONTH,
|
||||
'nextMonth' => Conditional::TIMEPERIOD_NEXT_MONTH,
|
||||
];
|
||||
|
||||
protected const EXPRESSIONS = [
|
||||
Conditional::TIMEPERIOD_YESTERDAY => 'FLOOR(%s,1)=TODAY()-1',
|
||||
Conditional::TIMEPERIOD_TODAY => 'FLOOR(%s,1)=TODAY()',
|
||||
Conditional::TIMEPERIOD_TOMORROW => 'FLOOR(%s,1)=TODAY()+1',
|
||||
Conditional::TIMEPERIOD_LAST_7_DAYS => 'AND(TODAY()-FLOOR(%s,1)<=6,FLOOR(%s,1)<=TODAY())',
|
||||
Conditional::TIMEPERIOD_LAST_WEEK => 'AND(TODAY()-ROUNDDOWN(%s,0)>=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(%s,0)<(WEEKDAY(TODAY())+7))',
|
||||
Conditional::TIMEPERIOD_THIS_WEEK => 'AND(TODAY()-ROUNDDOWN(%s,0)<=WEEKDAY(TODAY())-1,ROUNDDOWN(%s,0)-TODAY()<=7-WEEKDAY(TODAY()))',
|
||||
Conditional::TIMEPERIOD_NEXT_WEEK => 'AND(ROUNDDOWN(%s,0)-TODAY()>(7-WEEKDAY(TODAY())),ROUNDDOWN(%s,0)-TODAY()<(15-WEEKDAY(TODAY())))',
|
||||
Conditional::TIMEPERIOD_LAST_MONTH => 'AND(MONTH(%s)=MONTH(EDATE(TODAY(),0-1)),YEAR(%s)=YEAR(EDATE(TODAY(),0-1)))',
|
||||
Conditional::TIMEPERIOD_THIS_MONTH => 'AND(MONTH(%s)=MONTH(TODAY()),YEAR(%s)=YEAR(TODAY()))',
|
||||
Conditional::TIMEPERIOD_NEXT_MONTH => 'AND(MONTH(%s)=MONTH(EDATE(TODAY(),0+1)),YEAR(%s)=YEAR(EDATE(TODAY(),0+1)))',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
protected $operator;
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
}
|
||||
|
||||
protected function operator(string $operator): void
|
||||
{
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
protected function setExpression(): void
|
||||
{
|
||||
$referenceCount = substr_count(self::EXPRESSIONS[$this->operator], '%s');
|
||||
$references = array_fill(0, $referenceCount, $this->referenceCell);
|
||||
$this->expression = sprintf(self::EXPRESSIONS[$this->operator], ...$references);
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$this->setExpression();
|
||||
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(Conditional::CONDITION_TIMEPERIOD);
|
||||
$conditional->setText($this->operator);
|
||||
$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 ($conditional->getConditionType() !== Conditional::CONDITION_TIMEPERIOD) {
|
||||
throw new Exception('Conditional is not a Date Value CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
$wizard->operator = $conditional->getText();
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
|
||||
throw new Exception('Invalid Operation for Date Value CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->operator(self::MAGIC_OPERATIONS[$methodName]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
|
||||
/**
|
||||
* @method Errors duplicates()
|
||||
* @method Errors unique()
|
||||
*/
|
||||
class Duplicates extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const OPERATORS = [
|
||||
'duplicates' => false,
|
||||
'unique' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $inverse;
|
||||
|
||||
public function __construct(string $cellRange, bool $inverse = false)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
protected function inverse(bool $inverse): void
|
||||
{
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(
|
||||
$this->inverse ? Conditional::CONDITION_UNIQUE : Conditional::CONDITION_DUPLICATES
|
||||
);
|
||||
$conditional->setStyle($this->getStyle());
|
||||
$conditional->setStopIfTrue($this->getStopIfTrue());
|
||||
|
||||
return $conditional;
|
||||
}
|
||||
|
||||
public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
|
||||
{
|
||||
if (
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_DUPLICATES &&
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_UNIQUE
|
||||
) {
|
||||
throw new Exception('Conditional is not a Duplicates CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
$wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_UNIQUE;
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!array_key_exists($methodName, self::OPERATORS)) {
|
||||
throw new Exception('Invalid Operation for Errors CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->inverse(self::OPERATORS[$methodName]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
vendored
Normal file
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
/**
|
||||
* @method Errors notError()
|
||||
* @method Errors isError()
|
||||
*/
|
||||
class Errors extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const OPERATORS = [
|
||||
'notError' => false,
|
||||
'isError' => true,
|
||||
];
|
||||
|
||||
protected const EXPRESSIONS = [
|
||||
Wizard::NOT_ERRORS => 'NOT(ISERROR(%s))',
|
||||
Wizard::ERRORS => 'ISERROR(%s)',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $inverse;
|
||||
|
||||
public function __construct(string $cellRange, bool $inverse = false)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
protected function inverse(bool $inverse): void
|
||||
{
|
||||
$this->inverse = $inverse;
|
||||
}
|
||||
|
||||
protected function getExpression(): void
|
||||
{
|
||||
$this->expression = sprintf(
|
||||
self::EXPRESSIONS[$this->inverse ? Wizard::ERRORS : Wizard::NOT_ERRORS],
|
||||
$this->referenceCell
|
||||
);
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$this->getExpression();
|
||||
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(
|
||||
$this->inverse ? Conditional::CONDITION_CONTAINSERRORS : Conditional::CONDITION_NOTCONTAINSERRORS
|
||||
);
|
||||
$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 (
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_CONTAINSERRORS &&
|
||||
$conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSERRORS
|
||||
) {
|
||||
throw new Exception('Conditional is not an Errors CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
$wizard->inverse = $conditional->getConditionType() === Conditional::CONDITION_CONTAINSERRORS;
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!array_key_exists($methodName, self::OPERATORS)) {
|
||||
throw new Exception('Invalid Operation for Errors CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->inverse(self::OPERATORS[$methodName]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
/**
|
||||
* @method Expression formula(string $expression)
|
||||
*/
|
||||
class Expression extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $expression;
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
}
|
||||
|
||||
public function expression(string $expression): self
|
||||
{
|
||||
$expression = $this->validateOperand($expression, Wizard::VALUE_TYPE_FORMULA);
|
||||
$this->expression = $expression;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$expression = $this->adjustConditionsForCellReferences([$this->expression]);
|
||||
|
||||
$conditional = new Conditional();
|
||||
$conditional->setConditionType(Conditional::CONDITION_EXPRESSION);
|
||||
$conditional->setConditions($expression);
|
||||
$conditional->setStyle($this->getStyle());
|
||||
$conditional->setStopIfTrue($this->getStopIfTrue());
|
||||
|
||||
return $conditional;
|
||||
}
|
||||
|
||||
public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
|
||||
{
|
||||
if ($conditional->getConditionType() !== Conditional::CONDITION_EXPRESSION) {
|
||||
throw new Exception('Conditional is not an Expression CF Rule conditional');
|
||||
}
|
||||
|
||||
$wizard = new self($cellRange);
|
||||
$wizard->style = $conditional->getStyle();
|
||||
$wizard->stopIfTrue = $conditional->getStopIfTrue();
|
||||
$wizard->expression = self::reverseAdjustCellRef((string) ($conditional->getConditions()[0]), $cellRange);
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if ($methodName !== 'formula') {
|
||||
throw new Exception('Invalid Operation for Expression CF Rule Wizard');
|
||||
}
|
||||
|
||||
// Scrutinizer ignores its own recommendation
|
||||
//$this->expression(/** @scrutinizer ignore-type */ ...$arguments);
|
||||
$this->expression($arguments[0]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
164
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
vendored
Normal file
164
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
/**
|
||||
* @method TextValue contains(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method TextValue doesNotContain(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method TextValue doesntContain(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method TextValue beginsWith(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method TextValue startsWith(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
* @method TextValue endsWith(string $value, string $operandValueType = Wizard::VALUE_TYPE_LITERAL)
|
||||
*/
|
||||
class TextValue extends WizardAbstract implements WizardInterface
|
||||
{
|
||||
protected const MAGIC_OPERATIONS = [
|
||||
'contains' => Conditional::OPERATOR_CONTAINSTEXT,
|
||||
'doesntContain' => Conditional::OPERATOR_NOTCONTAINS,
|
||||
'doesNotContain' => Conditional::OPERATOR_NOTCONTAINS,
|
||||
'beginsWith' => Conditional::OPERATOR_BEGINSWITH,
|
||||
'startsWith' => Conditional::OPERATOR_BEGINSWITH,
|
||||
'endsWith' => Conditional::OPERATOR_ENDSWITH,
|
||||
];
|
||||
|
||||
protected const OPERATORS = [
|
||||
Conditional::OPERATOR_CONTAINSTEXT => Conditional::CONDITION_CONTAINSTEXT,
|
||||
Conditional::OPERATOR_NOTCONTAINS => Conditional::CONDITION_NOTCONTAINSTEXT,
|
||||
Conditional::OPERATOR_BEGINSWITH => Conditional::CONDITION_BEGINSWITH,
|
||||
Conditional::OPERATOR_ENDSWITH => Conditional::CONDITION_ENDSWITH,
|
||||
];
|
||||
|
||||
protected const EXPRESSIONS = [
|
||||
Conditional::OPERATOR_CONTAINSTEXT => 'NOT(ISERROR(SEARCH(%s,%s)))',
|
||||
Conditional::OPERATOR_NOTCONTAINS => 'ISERROR(SEARCH(%s,%s))',
|
||||
Conditional::OPERATOR_BEGINSWITH => 'LEFT(%s,LEN(%s))=%s',
|
||||
Conditional::OPERATOR_ENDSWITH => 'RIGHT(%s,LEN(%s))=%s',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
protected $operator;
|
||||
|
||||
/** @var string */
|
||||
protected $operand;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $operandValueType;
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
parent::__construct($cellRange);
|
||||
}
|
||||
|
||||
protected function operator(string $operator): void
|
||||
{
|
||||
if (!isset(self::OPERATORS[$operator])) {
|
||||
throw new Exception('Invalid Operator for Text Value CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
protected function operand(string $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
|
||||
{
|
||||
$operand = $this->validateOperand($operand, $operandValueType);
|
||||
|
||||
$this->operand = $operand;
|
||||
$this->operandValueType = $operandValueType;
|
||||
}
|
||||
|
||||
protected function wrapValue(string $value): string
|
||||
{
|
||||
return '"' . $value . '"';
|
||||
}
|
||||
|
||||
protected function setExpression(): void
|
||||
{
|
||||
$operand = $this->operandValueType === Wizard::VALUE_TYPE_LITERAL
|
||||
? $this->wrapValue(str_replace('"', '""', $this->operand))
|
||||
: $this->cellConditionCheck($this->operand);
|
||||
|
||||
if (
|
||||
$this->operator === Conditional::OPERATOR_CONTAINSTEXT ||
|
||||
$this->operator === Conditional::OPERATOR_NOTCONTAINS
|
||||
) {
|
||||
$this->expression = sprintf(self::EXPRESSIONS[$this->operator], $operand, $this->referenceCell);
|
||||
} else {
|
||||
$this->expression = sprintf(self::EXPRESSIONS[$this->operator], $this->referenceCell, $operand, $operand);
|
||||
}
|
||||
}
|
||||
|
||||
public function getConditional(): Conditional
|
||||
{
|
||||
$this->setExpression();
|
||||
|
||||
$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;
|
||||
}
|
||||
$wizard->operand = $condition;
|
||||
|
||||
return $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param mixed[] $arguments
|
||||
*/
|
||||
public function __call($methodName, $arguments): self
|
||||
{
|
||||
if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
|
||||
throw new Exception('Invalid Operation for Text Value CF Rule Wizard');
|
||||
}
|
||||
|
||||
$this->operator(self::MAGIC_OPERATIONS[$methodName]);
|
||||
//$this->operand(...$arguments);
|
||||
if (count($arguments) < 2) {
|
||||
$this->operand($arguments[0]);
|
||||
} else {
|
||||
$this->operand($arguments[0], $arguments[1]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
abstract class WizardAbstract
|
||||
{
|
||||
/**
|
||||
* @var ?Style
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $expression;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cellRange;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $referenceCell;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $referenceRow;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $stopIfTrue = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $referenceColumn;
|
||||
|
||||
public function __construct(string $cellRange)
|
||||
{
|
||||
$this->setCellRange($cellRange);
|
||||
}
|
||||
|
||||
public function getCellRange(): string
|
||||
{
|
||||
return $this->cellRange;
|
||||
}
|
||||
|
||||
public function setCellRange(string $cellRange): void
|
||||
{
|
||||
$this->cellRange = $cellRange;
|
||||
$this->setReferenceCellForExpressions($cellRange);
|
||||
}
|
||||
|
||||
protected function setReferenceCellForExpressions(string $conditionalRange): void
|
||||
{
|
||||
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($conditionalRange)));
|
||||
[$this->referenceCell] = $conditionalRange[0];
|
||||
|
||||
[$this->referenceColumn, $this->referenceRow] = Coordinate::indexesFromString($this->referenceCell);
|
||||
}
|
||||
|
||||
public function getStopIfTrue(): bool
|
||||
{
|
||||
return $this->stopIfTrue;
|
||||
}
|
||||
|
||||
public function setStopIfTrue(bool $stopIfTrue): void
|
||||
{
|
||||
$this->stopIfTrue = $stopIfTrue;
|
||||
}
|
||||
|
||||
public function getStyle(): Style
|
||||
{
|
||||
return $this->style ?? new Style(false, true);
|
||||
}
|
||||
|
||||
public function setStyle(Style $style): void
|
||||
{
|
||||
$this->style = $style;
|
||||
}
|
||||
|
||||
protected function validateOperand(string $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): string
|
||||
{
|
||||
if (
|
||||
$operandValueType === Wizard::VALUE_TYPE_LITERAL &&
|
||||
substr($operand, 0, 1) === '"' &&
|
||||
substr($operand, -1) === '"'
|
||||
) {
|
||||
$operand = str_replace('""', '"', substr($operand, 1, -1));
|
||||
} elseif ($operandValueType === Wizard::VALUE_TYPE_FORMULA && substr($operand, 0, 1) === '=') {
|
||||
$operand = substr($operand, 1);
|
||||
}
|
||||
|
||||
return $operand;
|
||||
}
|
||||
|
||||
protected static function reverseCellAdjustment(array $matches, int $referenceColumn, int $referenceRow): string
|
||||
{
|
||||
$worksheet = $matches[1];
|
||||
$column = $matches[6];
|
||||
$row = $matches[7];
|
||||
|
||||
if (strpos($column, '$') === false) {
|
||||
$column = Coordinate::columnIndexFromString($column);
|
||||
$column -= $referenceColumn - 1;
|
||||
$column = Coordinate::stringFromColumnIndex($column);
|
||||
}
|
||||
|
||||
if (strpos($row, '$') === false) {
|
||||
$row -= $referenceRow - 1;
|
||||
}
|
||||
|
||||
return "{$worksheet}{$column}{$row}";
|
||||
}
|
||||
|
||||
public static function reverseAdjustCellRef(string $condition, string $cellRange): string
|
||||
{
|
||||
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange)));
|
||||
[$referenceCell] = $conditionalRange[0];
|
||||
[$referenceColumnIndex, $referenceRow] = Coordinate::indexesFromString($referenceCell);
|
||||
|
||||
$splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
|
||||
$i = false;
|
||||
foreach ($splitCondition as &$value) {
|
||||
// Only count/replace in alternating array entries (ie. not in quoted strings)
|
||||
$i = $i === false;
|
||||
if ($i) {
|
||||
$value = (string) preg_replace_callback(
|
||||
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
|
||||
function ($matches) use ($referenceColumnIndex, $referenceRow) {
|
||||
return self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow);
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
|
||||
// Then rebuild the condition string to return it
|
||||
return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
|
||||
}
|
||||
|
||||
protected function conditionCellAdjustment(array $matches): string
|
||||
{
|
||||
$worksheet = $matches[1];
|
||||
$column = $matches[6];
|
||||
$row = $matches[7];
|
||||
|
||||
if (strpos($column, '$') === false) {
|
||||
$column = Coordinate::columnIndexFromString($column);
|
||||
$column += $this->referenceColumn - 1;
|
||||
$column = Coordinate::stringFromColumnIndex($column);
|
||||
}
|
||||
|
||||
if (strpos($row, '$') === false) {
|
||||
$row += $this->referenceRow - 1;
|
||||
}
|
||||
|
||||
return "{$worksheet}{$column}{$row}";
|
||||
}
|
||||
|
||||
protected function cellConditionCheck(string $condition): string
|
||||
{
|
||||
$splitCondition = explode(Calculation::FORMULA_STRING_QUOTE, $condition);
|
||||
$i = false;
|
||||
foreach ($splitCondition as &$value) {
|
||||
// Only count/replace in alternating array entries (ie. not in quoted strings)
|
||||
$i = $i === false;
|
||||
if ($i) {
|
||||
$value = (string) preg_replace_callback(
|
||||
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
|
||||
[$this, 'conditionCellAdjustment'],
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
|
||||
// Then rebuild the condition string to return it
|
||||
return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
|
||||
}
|
||||
|
||||
protected function adjustConditionsForCellReferences(array $conditions): array
|
||||
{
|
||||
return array_map(
|
||||
[$this, 'cellConditionCheck'],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
interface WizardInterface
|
||||
{
|
||||
public function getCellRange(): string;
|
||||
|
||||
public function setCellRange(string $cellRange): void;
|
||||
|
||||
public function getStyle(): Style;
|
||||
|
||||
public function setStyle(Style $style): void;
|
||||
|
||||
public function getStopIfTrue(): bool;
|
||||
|
||||
public function setStopIfTrue(bool $stopIfTrue): void;
|
||||
|
||||
public function getConditional(): Conditional;
|
||||
|
||||
public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): self;
|
||||
}
|
Reference in New Issue
Block a user