summaryrefslogtreecommitdiffstats
path: root/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php871
-rw-r--r--vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php380
-rw-r--r--vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php449
3 files changed, 1700 insertions, 0 deletions
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
new file mode 100644
index 0000000..877b3d1
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
@@ -0,0 +1,871 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
+
+class AutoFilter
+{
+ /**
+ * Autofilter Worksheet.
+ *
+ * @var Worksheet
+ */
+ private $workSheet;
+
+ /**
+ * Autofilter Range.
+ *
+ * @var string
+ */
+ private $range = '';
+
+ /**
+ * Autofilter Column Ruleset.
+ *
+ * @var AutoFilter\Column[]
+ */
+ private $columns = [];
+
+ /**
+ * Create a new AutoFilter.
+ *
+ * @param string $pRange Cell range (i.e. A1:E10)
+ * @param Worksheet $pSheet
+ */
+ public function __construct($pRange = '', ?Worksheet $pSheet = null)
+ {
+ $this->range = $pRange;
+ $this->workSheet = $pSheet;
+ }
+
+ /**
+ * Get AutoFilter Parent Worksheet.
+ *
+ * @return Worksheet
+ */
+ public function getParent()
+ {
+ return $this->workSheet;
+ }
+
+ /**
+ * Set AutoFilter Parent Worksheet.
+ *
+ * @param Worksheet $pSheet
+ *
+ * @return $this
+ */
+ public function setParent(?Worksheet $pSheet = null)
+ {
+ $this->workSheet = $pSheet;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Range.
+ *
+ * @return string
+ */
+ public function getRange()
+ {
+ return $this->range;
+ }
+
+ /**
+ * Set AutoFilter Range.
+ *
+ * @param string $pRange Cell range (i.e. A1:E10)
+ *
+ * @return $this
+ */
+ public function setRange($pRange)
+ {
+ // extract coordinate
+ [$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true);
+
+ if (strpos($pRange, ':') !== false) {
+ $this->range = $pRange;
+ } elseif (empty($pRange)) {
+ $this->range = '';
+ } else {
+ throw new PhpSpreadsheetException('Autofilter must be set on a range of cells.');
+ }
+
+ if (empty($pRange)) {
+ // Discard all column rules
+ $this->columns = [];
+ } else {
+ // Discard any column rules that are no longer valid within this range
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+ foreach ($this->columns as $key => $value) {
+ $colIndex = Coordinate::columnIndexFromString($key);
+ if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
+ unset($this->columns[$key]);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get all AutoFilter Columns.
+ *
+ * @return AutoFilter\Column[]
+ */
+ public function getColumns()
+ {
+ return $this->columns;
+ }
+
+ /**
+ * Validate that the specified column is in the AutoFilter range.
+ *
+ * @param string $column Column name (e.g. A)
+ *
+ * @return int The column offset within the autofilter range
+ */
+ public function testColumnInRange($column)
+ {
+ if (empty($this->range)) {
+ throw new PhpSpreadsheetException('No autofilter range is defined.');
+ }
+
+ $columnIndex = Coordinate::columnIndexFromString($column);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+ if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
+ throw new PhpSpreadsheetException('Column is outside of current autofilter range.');
+ }
+
+ return $columnIndex - $rangeStart[0];
+ }
+
+ /**
+ * Get a specified AutoFilter Column Offset within the defined AutoFilter range.
+ *
+ * @param string $pColumn Column name (e.g. A)
+ *
+ * @return int The offset of the specified column within the autofilter range
+ */
+ public function getColumnOffset($pColumn)
+ {
+ return $this->testColumnInRange($pColumn);
+ }
+
+ /**
+ * Get a specified AutoFilter Column.
+ *
+ * @param string $pColumn Column name (e.g. A)
+ *
+ * @return AutoFilter\Column
+ */
+ public function getColumn($pColumn)
+ {
+ $this->testColumnInRange($pColumn);
+
+ if (!isset($this->columns[$pColumn])) {
+ $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
+ }
+
+ return $this->columns[$pColumn];
+ }
+
+ /**
+ * Get a specified AutoFilter Column by it's offset.
+ *
+ * @param int $pColumnOffset Column offset within range (starting from 0)
+ *
+ * @return AutoFilter\Column
+ */
+ public function getColumnByOffset($pColumnOffset)
+ {
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+ $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $pColumnOffset);
+
+ return $this->getColumn($pColumn);
+ }
+
+ /**
+ * Set AutoFilter.
+ *
+ * @param AutoFilter\Column|string $pColumn
+ * A simple string containing a Column ID like 'A' is permitted
+ *
+ * @return $this
+ */
+ public function setColumn($pColumn)
+ {
+ if ((is_string($pColumn)) && (!empty($pColumn))) {
+ $column = $pColumn;
+ } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
+ $column = $pColumn->getColumnIndex();
+ } else {
+ throw new PhpSpreadsheetException('Column is not within the autofilter range.');
+ }
+ $this->testColumnInRange($column);
+
+ if (is_string($pColumn)) {
+ $this->columns[$pColumn] = new AutoFilter\Column($pColumn, $this);
+ } elseif (is_object($pColumn) && ($pColumn instanceof AutoFilter\Column)) {
+ $pColumn->setParent($this);
+ $this->columns[$column] = $pColumn;
+ }
+ ksort($this->columns);
+
+ return $this;
+ }
+
+ /**
+ * Clear a specified AutoFilter Column.
+ *
+ * @param string $pColumn Column name (e.g. A)
+ *
+ * @return $this
+ */
+ public function clearColumn($pColumn)
+ {
+ $this->testColumnInRange($pColumn);
+
+ if (isset($this->columns[$pColumn])) {
+ unset($this->columns[$pColumn]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Shift an AutoFilter Column Rule to a different column.
+ *
+ * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range.
+ * Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value.
+ * Use with caution.
+ *
+ * @param string $fromColumn Column name (e.g. A)
+ * @param string $toColumn Column name (e.g. B)
+ *
+ * @return $this
+ */
+ public function shiftColumn($fromColumn, $toColumn)
+ {
+ $fromColumn = strtoupper($fromColumn);
+ $toColumn = strtoupper($toColumn);
+
+ if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
+ $this->columns[$fromColumn]->setParent();
+ $this->columns[$fromColumn]->setColumnIndex($toColumn);
+ $this->columns[$toColumn] = $this->columns[$fromColumn];
+ $this->columns[$toColumn]->setParent($this);
+ unset($this->columns[$fromColumn]);
+
+ ksort($this->columns);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Test if cell value is in the defined set of values.
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $dataSet
+ *
+ * @return bool
+ */
+ private static function filterTestInSimpleDataSet($cellValue, $dataSet)
+ {
+ $dataSetValues = $dataSet['filterValues'];
+ $blanks = $dataSet['blanks'];
+ if (($cellValue == '') || ($cellValue === null)) {
+ return $blanks;
+ }
+
+ return in_array($cellValue, $dataSetValues);
+ }
+
+ /**
+ * Test if cell value is in the defined set of Excel date values.
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $dataSet
+ *
+ * @return bool
+ */
+ private static function filterTestInDateGroupSet($cellValue, $dataSet)
+ {
+ $dateSet = $dataSet['filterValues'];
+ $blanks = $dataSet['blanks'];
+ if (($cellValue == '') || ($cellValue === null)) {
+ return $blanks;
+ }
+
+ if (is_numeric($cellValue)) {
+ $dateValue = Date::excelToTimestamp($cellValue);
+ if ($cellValue < 1) {
+ // Just the time part
+ $dtVal = date('His', $dateValue);
+ $dateSet = $dateSet['time'];
+ } elseif ($cellValue == floor($cellValue)) {
+ // Just the date part
+ $dtVal = date('Ymd', $dateValue);
+ $dateSet = $dateSet['date'];
+ } else {
+ // date and time parts
+ $dtVal = date('YmdHis', $dateValue);
+ $dateSet = $dateSet['dateTime'];
+ }
+ foreach ($dateSet as $dateValue) {
+ // Use of substr to extract value at the appropriate group level
+ if (substr($dtVal, 0, strlen($dateValue)) == $dateValue) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Test if cell value is within a set of values defined by a ruleset.
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $ruleSet
+ *
+ * @return bool
+ */
+ private static function filterTestInCustomDataSet($cellValue, $ruleSet)
+ {
+ $dataSet = $ruleSet['filterRules'];
+ $join = $ruleSet['join'];
+ $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
+
+ if (!$customRuleForBlanks) {
+ // Blank cells are always ignored, so return a FALSE
+ if (($cellValue == '') || ($cellValue === null)) {
+ return false;
+ }
+ }
+ $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
+ foreach ($dataSet as $rule) {
+ $retVal = false;
+
+ if (is_numeric($rule['value'])) {
+ // Numeric values are tested using the appropriate operator
+ switch ($rule['operator']) {
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+ $retVal = ($cellValue == $rule['value']);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+ $retVal = ($cellValue != $rule['value']);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
+ $retVal = ($cellValue > $rule['value']);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
+ $retVal = ($cellValue >= $rule['value']);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
+ $retVal = ($cellValue < $rule['value']);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
+ $retVal = ($cellValue <= $rule['value']);
+
+ break;
+ }
+ } elseif ($rule['value'] == '') {
+ switch ($rule['operator']) {
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+ $retVal = (($cellValue == '') || ($cellValue === null));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+ $retVal = (($cellValue != '') && ($cellValue !== null));
+
+ break;
+ default:
+ $retVal = true;
+
+ break;
+ }
+ } else {
+ // String values are always tested for equality, factoring in for wildcards (hence a regexp test)
+ $retVal = preg_match('/^' . $rule['value'] . '$/i', $cellValue);
+ }
+ // If there are multiple conditions, then we need to test both using the appropriate join operator
+ switch ($join) {
+ case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR:
+ $returnVal = $returnVal || $retVal;
+ // Break as soon as we have a TRUE match for OR joins,
+ // to avoid unnecessary additional code execution
+ if ($returnVal) {
+ return $returnVal;
+ }
+
+ break;
+ case AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND:
+ $returnVal = $returnVal && $retVal;
+
+ break;
+ }
+ }
+
+ return $returnVal;
+ }
+
+ /**
+ * Test if cell date value is matches a set of values defined by a set of months.
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $monthSet
+ *
+ * @return bool
+ */
+ private static function filterTestInPeriodDateSet($cellValue, $monthSet)
+ {
+ // Blank cells are always ignored, so return a FALSE
+ if (($cellValue == '') || ($cellValue === null)) {
+ return false;
+ }
+
+ if (is_numeric($cellValue)) {
+ $dateValue = date('m', Date::excelToTimestamp($cellValue));
+ if (in_array($dateValue, $monthSet)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching.
+ *
+ * @var array
+ */
+ private static $fromReplace = ['\*', '\?', '~~', '~.*', '~.?'];
+
+ private static $toReplace = ['.*', '.', '~', '\*', '\?'];
+
+ /**
+ * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
+ *
+ * @param string $dynamicRuleType
+ * @param AutoFilter\Column $filterColumn
+ *
+ * @return mixed[]
+ */
+ private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
+ {
+ $rDateType = Functions::getReturnDateType();
+ Functions::setReturnDateType(Functions::RETURNDATE_PHP_NUMERIC);
+ $val = $maxVal = null;
+
+ $ruleValues = [];
+ $baseDate = DateTime::DATENOW();
+ // Calculate start/end dates for the required date range based on current date
+ switch ($dynamicRuleType) {
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+ $baseDate = strtotime('-7 days', $baseDate);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+ $baseDate = strtotime('-7 days', $baseDate);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+ $baseDate = strtotime('-1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+ $baseDate = strtotime('+1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+ $baseDate = strtotime('-3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+ $baseDate = strtotime('+3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+ $baseDate = strtotime('-1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+ $baseDate = strtotime('+1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ }
+
+ switch ($dynamicRuleType) {
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+ $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+ $val = (int) Date::PHPToExcel($baseDate);
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE:
+ $maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+ $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+ $maxVal = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 31, 12, date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+ $thisMonth = date('m', $baseDate);
+ $thisQuarter = floor(--$thisMonth / 3);
+ $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), (1 + $thisQuarter) * 3, date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1 + $thisQuarter * 3, date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+ $maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), date('m', $baseDate), date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+ $dayOfWeek = date('w', $baseDate);
+ $val = (int) Date::PHPToExcel($baseDate) - $dayOfWeek;
+ $maxVal = $val + 7;
+
+ break;
+ }
+
+ switch ($dynamicRuleType) {
+ // Adjust Today dates for Yesterday and Tomorrow
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+ --$maxVal;
+ --$val;
+
+ break;
+ case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+ ++$maxVal;
+ ++$val;
+
+ break;
+ }
+
+ // Set the filter column rule attributes ready for writing
+ $filterColumn->setAttributes(['val' => $val, 'maxVal' => $maxVal]);
+
+ // Set the rules for identifying rows for hide/show
+ $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val];
+ $ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxVal];
+ Functions::setReturnDateType($rDateType);
+
+ return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]];
+ }
+
+ private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue)
+ {
+ $range = $columnID . $startRow . ':' . $columnID . $endRow;
+ $dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
+
+ $dataValues = array_filter($dataValues);
+ if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
+ rsort($dataValues);
+ } else {
+ sort($dataValues);
+ }
+
+ return array_pop(array_slice($dataValues, 0, $ruleValue));
+ }
+
+ /**
+ * Apply the AutoFilter rules to the AutoFilter Range.
+ *
+ * @return $this
+ */
+ public function showHideRows()
+ {
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
+
+ // The heading row should always be visible
+ $this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
+
+ $columnFilterTests = [];
+ foreach ($this->columns as $columnID => $filterColumn) {
+ $rules = $filterColumn->getRules();
+ switch ($filterColumn->getFilterType()) {
+ case AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER:
+ $ruleType = null;
+ $ruleValues = [];
+ // Build a list of the filter value selections
+ foreach ($rules as $rule) {
+ $ruleType = $rule->getRuleType();
+ $ruleValues[] = $rule->getValue();
+ }
+ // Test if we want to include blanks in our filter criteria
+ $blanks = false;
+ $ruleDataSet = array_filter($ruleValues);
+ if (count($ruleValues) != count($ruleDataSet)) {
+ $blanks = true;
+ }
+ if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_FILTER) {
+ // Filter on absolute values
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInSimpleDataSet',
+ 'arguments' => ['filterValues' => $ruleDataSet, 'blanks' => $blanks],
+ ];
+ } else {
+ // Filter on date group values
+ $arguments = [
+ 'date' => [],
+ 'time' => [],
+ 'dateTime' => [],
+ ];
+ foreach ($ruleDataSet as $ruleValue) {
+ $date = $time = '';
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
+ ) {
+ $date .= sprintf('%04d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
+ }
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
+ ) {
+ $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
+ }
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
+ ) {
+ $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
+ }
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
+ ) {
+ $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
+ }
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
+ ) {
+ $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
+ }
+ if (
+ (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
+ ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
+ ) {
+ $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
+ }
+ $dateTime = $date . $time;
+ $arguments['date'][] = $date;
+ $arguments['time'][] = $time;
+ $arguments['dateTime'][] = $dateTime;
+ }
+ // Remove empty elements
+ $arguments['date'] = array_filter($arguments['date']);
+ $arguments['time'] = array_filter($arguments['time']);
+ $arguments['dateTime'] = array_filter($arguments['dateTime']);
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInDateGroupSet',
+ 'arguments' => ['filterValues' => $arguments, 'blanks' => $blanks],
+ ];
+ }
+
+ break;
+ case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER:
+ $customRuleForBlanks = false;
+ $ruleValues = [];
+ // Build a list of the filter value selections
+ foreach ($rules as $rule) {
+ $ruleValue = $rule->getValue();
+ if (!is_numeric($ruleValue)) {
+ // Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards
+ $ruleValue = preg_quote($ruleValue);
+ $ruleValue = str_replace(self::$fromReplace, self::$toReplace, $ruleValue);
+ if (trim($ruleValue) == '') {
+ $customRuleForBlanks = true;
+ $ruleValue = trim($ruleValue);
+ }
+ }
+ $ruleValues[] = ['operator' => $rule->getOperator(), 'value' => $ruleValue];
+ }
+ $join = $filterColumn->getJoin();
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => ['filterRules' => $ruleValues, 'join' => $join, 'customRuleForBlanks' => $customRuleForBlanks],
+ ];
+
+ break;
+ case AutoFilter\Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER:
+ $ruleValues = [];
+ foreach ($rules as $rule) {
+ // We should only ever have one Dynamic Filter Rule anyway
+ $dynamicRuleType = $rule->getGrouping();
+ if (
+ ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
+ ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
+ ) {
+ // Number (Average) based
+ // Calculate the average
+ $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
+ $average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
+ // Set above/below rule based on greaterThan or LessTan
+ $operator = ($dynamicRuleType === AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
+ ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
+ : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
+ $ruleValues[] = [
+ 'operator' => $operator,
+ 'value' => $average,
+ ];
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
+ ];
+ } else {
+ // Date based
+ if ($dynamicRuleType[0] == 'M' || $dynamicRuleType[0] == 'Q') {
+ $periodType = '';
+ $period = 0;
+ // Month or Quarter
+ sscanf($dynamicRuleType, '%[A-Z]%d', $periodType, $period);
+ if ($periodType == 'M') {
+ $ruleValues = [$period];
+ } else {
+ --$period;
+ $periodEnd = (1 + $period) * 3;
+ $periodStart = 1 + $period * 3;
+ $ruleValues = range($periodStart, $periodEnd);
+ }
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInPeriodDateSet',
+ 'arguments' => $ruleValues,
+ ];
+ $filterColumn->setAttributes([]);
+ } else {
+ // Date Range
+ $columnFilterTests[$columnID] = $this->dynamicFilterDateRange($dynamicRuleType, $filterColumn);
+
+ break;
+ }
+ }
+ }
+
+ break;
+ case AutoFilter\Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER:
+ $ruleValues = [];
+ $dataRowCount = $rangeEnd[1] - $rangeStart[1];
+ foreach ($rules as $rule) {
+ // We should only ever have one Dynamic Filter Rule anyway
+ $toptenRuleType = $rule->getGrouping();
+ $ruleValue = $rule->getValue();
+ $ruleOperator = $rule->getOperator();
+ }
+ if ($ruleOperator === AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
+ $ruleValue = floor($ruleValue * ($dataRowCount / 100));
+ }
+ if ($ruleValue < 1) {
+ $ruleValue = 1;
+ }
+ if ($ruleValue > 500) {
+ $ruleValue = 500;
+ }
+
+ $maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, $rangeEnd[1], $toptenRuleType, $ruleValue);
+
+ $operator = ($toptenRuleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
+ ? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
+ : AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
+ $ruleValues[] = ['operator' => $operator, 'value' => $maxVal];
+ $columnFilterTests[$columnID] = [
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_OR],
+ ];
+ $filterColumn->setAttributes(['maxVal' => $maxVal]);
+
+ break;
+ }
+ }
+
+ // Execute the column tests for each row in the autoFilter range to determine show/hide,
+ for ($row = $rangeStart[1] + 1; $row <= $rangeEnd[1]; ++$row) {
+ $result = true;
+ foreach ($columnFilterTests as $columnID => $columnFilterTest) {
+ $cellValue = $this->workSheet->getCell($columnID . $row)->getCalculatedValue();
+ // Execute the filter test
+ $result = $result &&
+ call_user_func_array(
+ [self::class, $columnFilterTest['method']],
+ [$cellValue, $columnFilterTest['arguments']]
+ );
+ // If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
+ if (!$result) {
+ break;
+ }
+ }
+ // Set show/hide for the row based on the result of the autoFilter result
+ $this->workSheet->getRowDimension($row)->setVisible($result);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ if ($key === 'workSheet') {
+ // Detach from worksheet
+ $this->{$key} = null;
+ } else {
+ $this->{$key} = clone $value;
+ }
+ } elseif ((is_array($value)) && ($key == 'columns')) {
+ // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
+ $this->{$key} = [];
+ foreach ($value as $k => $v) {
+ $this->{$key}[$k] = clone $v;
+ // attach the new cloned Column to this new cloned Autofilter object
+ $this->{$key}[$k]->setParent($this);
+ }
+ } else {
+ $this->{$key} = $value;
+ }
+ }
+ }
+
+ /**
+ * toString method replicates previous behavior by returning the range if object is
+ * referenced as a property of its parent.
+ */
+ public function __toString()
+ {
+ return (string) $this->range;
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
new file mode 100644
index 0000000..bcde501
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
@@ -0,0 +1,380 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
+
+class Column
+{
+ const AUTOFILTER_FILTERTYPE_FILTER = 'filters';
+ const AUTOFILTER_FILTERTYPE_CUSTOMFILTER = 'customFilters';
+ // Supports no more than 2 rules, with an And/Or join criteria
+ // if more than 1 rule is defined
+ const AUTOFILTER_FILTERTYPE_DYNAMICFILTER = 'dynamicFilter';
+ // Even though the filter rule is constant, the filtered data can vary
+ // e.g. filtered by date = TODAY
+ const AUTOFILTER_FILTERTYPE_TOPTENFILTER = 'top10';
+
+ /**
+ * Types of autofilter rules.
+ *
+ * @var string[]
+ */
+ private static $filterTypes = [
+ // Currently we're not handling
+ // colorFilter
+ // extLst
+ // iconFilter
+ self::AUTOFILTER_FILTERTYPE_FILTER,
+ self::AUTOFILTER_FILTERTYPE_CUSTOMFILTER,
+ self::AUTOFILTER_FILTERTYPE_DYNAMICFILTER,
+ self::AUTOFILTER_FILTERTYPE_TOPTENFILTER,
+ ];
+
+ // Multiple Rule Connections
+ const AUTOFILTER_COLUMN_JOIN_AND = 'and';
+ const AUTOFILTER_COLUMN_JOIN_OR = 'or';
+
+ /**
+ * Join options for autofilter rules.
+ *
+ * @var string[]
+ */
+ private static $ruleJoins = [
+ self::AUTOFILTER_COLUMN_JOIN_AND,
+ self::AUTOFILTER_COLUMN_JOIN_OR,
+ ];
+
+ /**
+ * Autofilter.
+ *
+ * @var AutoFilter
+ */
+ private $parent;
+
+ /**
+ * Autofilter Column Index.
+ *
+ * @var string
+ */
+ private $columnIndex = '';
+
+ /**
+ * Autofilter Column Filter Type.
+ *
+ * @var string
+ */
+ private $filterType = self::AUTOFILTER_FILTERTYPE_FILTER;
+
+ /**
+ * Autofilter Multiple Rules And/Or.
+ *
+ * @var string
+ */
+ private $join = self::AUTOFILTER_COLUMN_JOIN_OR;
+
+ /**
+ * Autofilter Column Rules.
+ *
+ * @var array of Column\Rule
+ */
+ private $ruleset = [];
+
+ /**
+ * Autofilter Column Dynamic Attributes.
+ *
+ * @var array of mixed
+ */
+ private $attributes = [];
+
+ /**
+ * Create a new Column.
+ *
+ * @param string $pColumn Column (e.g. A)
+ * @param AutoFilter $pParent Autofilter for this column
+ */
+ public function __construct($pColumn, ?AutoFilter $pParent = null)
+ {
+ $this->columnIndex = $pColumn;
+ $this->parent = $pParent;
+ }
+
+ /**
+ * Get AutoFilter column index as string eg: 'A'.
+ *
+ * @return string
+ */
+ public function getColumnIndex()
+ {
+ return $this->columnIndex;
+ }
+
+ /**
+ * Set AutoFilter column index as string eg: 'A'.
+ *
+ * @param string $pColumn Column (e.g. A)
+ *
+ * @return $this
+ */
+ public function setColumnIndex($pColumn)
+ {
+ // Uppercase coordinate
+ $pColumn = strtoupper($pColumn);
+ if ($this->parent !== null) {
+ $this->parent->testColumnInRange($pColumn);
+ }
+
+ $this->columnIndex = $pColumn;
+
+ return $this;
+ }
+
+ /**
+ * Get this Column's AutoFilter Parent.
+ *
+ * @return AutoFilter
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Set this Column's AutoFilter Parent.
+ *
+ * @param AutoFilter $pParent
+ *
+ * @return $this
+ */
+ public function setParent(?AutoFilter $pParent = null)
+ {
+ $this->parent = $pParent;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Type.
+ *
+ * @return string
+ */
+ public function getFilterType()
+ {
+ return $this->filterType;
+ }
+
+ /**
+ * Set AutoFilter Type.
+ *
+ * @param string $pFilterType
+ *
+ * @return $this
+ */
+ public function setFilterType($pFilterType)
+ {
+ if (!in_array($pFilterType, self::$filterTypes)) {
+ throw new PhpSpreadsheetException('Invalid filter type for column AutoFilter.');
+ }
+
+ $this->filterType = $pFilterType;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Multiple Rules And/Or Join.
+ *
+ * @return string
+ */
+ public function getJoin()
+ {
+ return $this->join;
+ }
+
+ /**
+ * Set AutoFilter Multiple Rules And/Or.
+ *
+ * @param string $pJoin And/Or
+ *
+ * @return $this
+ */
+ public function setJoin($pJoin)
+ {
+ // Lowercase And/Or
+ $pJoin = strtolower($pJoin);
+ if (!in_array($pJoin, self::$ruleJoins)) {
+ throw new PhpSpreadsheetException('Invalid rule connection for column AutoFilter.');
+ }
+
+ $this->join = $pJoin;
+
+ return $this;
+ }
+
+ /**
+ * Set AutoFilter Attributes.
+ *
+ * @param string[] $attributes
+ *
+ * @return $this
+ */
+ public function setAttributes(array $attributes)
+ {
+ $this->attributes = $attributes;
+
+ return $this;
+ }
+
+ /**
+ * Set An AutoFilter Attribute.
+ *
+ * @param string $pName Attribute Name
+ * @param string $pValue Attribute Value
+ *
+ * @return $this
+ */
+ public function setAttribute($pName, $pValue)
+ {
+ $this->attributes[$pName] = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Column Attributes.
+ *
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Get specific AutoFilter Column Attribute.
+ *
+ * @param string $pName Attribute Name
+ *
+ * @return string
+ */
+ public function getAttribute($pName)
+ {
+ if (isset($this->attributes[$pName])) {
+ return $this->attributes[$pName];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get all AutoFilter Column Rules.
+ *
+ * @return Column\Rule[]
+ */
+ public function getRules()
+ {
+ return $this->ruleset;
+ }
+
+ /**
+ * Get a specified AutoFilter Column Rule.
+ *
+ * @param int $pIndex Rule index in the ruleset array
+ *
+ * @return Column\Rule
+ */
+ public function getRule($pIndex)
+ {
+ if (!isset($this->ruleset[$pIndex])) {
+ $this->ruleset[$pIndex] = new Column\Rule($this);
+ }
+
+ return $this->ruleset[$pIndex];
+ }
+
+ /**
+ * Create a new AutoFilter Column Rule in the ruleset.
+ *
+ * @return Column\Rule
+ */
+ public function createRule()
+ {
+ $this->ruleset[] = new Column\Rule($this);
+
+ return end($this->ruleset);
+ }
+
+ /**
+ * Add a new AutoFilter Column Rule to the ruleset.
+ *
+ * @return $this
+ */
+ public function addRule(Column\Rule $pRule)
+ {
+ $pRule->setParent($this);
+ $this->ruleset[] = $pRule;
+
+ return $this;
+ }
+
+ /**
+ * Delete a specified AutoFilter Column Rule
+ * If the number of rules is reduced to 1, then we reset And/Or logic to Or.
+ *
+ * @param int $pIndex Rule index in the ruleset array
+ *
+ * @return $this
+ */
+ public function deleteRule($pIndex)
+ {
+ if (isset($this->ruleset[$pIndex])) {
+ unset($this->ruleset[$pIndex]);
+ // If we've just deleted down to a single rule, then reset And/Or joining to Or
+ if (count($this->ruleset) <= 1) {
+ $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Delete all AutoFilter Column Rules.
+ *
+ * @return $this
+ */
+ public function clearRules()
+ {
+ $this->ruleset = [];
+ $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if ($key === 'parent') {
+ // Detach from autofilter parent
+ $this->parent = null;
+ } elseif ($key === 'ruleset') {
+ // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
+ $this->ruleset = [];
+ foreach ($value as $k => $v) {
+ $cloned = clone $v;
+ $cloned->setParent($this); // attach the new cloned Rule to this new cloned Autofilter Cloned object
+ $this->ruleset[$k] = $cloned;
+ }
+ } elseif (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
new file mode 100644
index 0000000..a893a85
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
@@ -0,0 +1,449 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+
+class Rule
+{
+ const AUTOFILTER_RULETYPE_FILTER = 'filter';
+ const AUTOFILTER_RULETYPE_DATEGROUP = 'dateGroupItem';
+ const AUTOFILTER_RULETYPE_CUSTOMFILTER = 'customFilter';
+ const AUTOFILTER_RULETYPE_DYNAMICFILTER = 'dynamicFilter';
+ const AUTOFILTER_RULETYPE_TOPTENFILTER = 'top10Filter';
+
+ private static $ruleTypes = [
+ // Currently we're not handling
+ // colorFilter
+ // extLst
+ // iconFilter
+ self::AUTOFILTER_RULETYPE_FILTER,
+ self::AUTOFILTER_RULETYPE_DATEGROUP,
+ self::AUTOFILTER_RULETYPE_CUSTOMFILTER,
+ self::AUTOFILTER_RULETYPE_DYNAMICFILTER,
+ self::AUTOFILTER_RULETYPE_TOPTENFILTER,
+ ];
+
+ const AUTOFILTER_RULETYPE_DATEGROUP_YEAR = 'year';
+ const AUTOFILTER_RULETYPE_DATEGROUP_MONTH = 'month';
+ const AUTOFILTER_RULETYPE_DATEGROUP_DAY = 'day';
+ const AUTOFILTER_RULETYPE_DATEGROUP_HOUR = 'hour';
+ const AUTOFILTER_RULETYPE_DATEGROUP_MINUTE = 'minute';
+ const AUTOFILTER_RULETYPE_DATEGROUP_SECOND = 'second';
+
+ private static $dateTimeGroups = [
+ self::AUTOFILTER_RULETYPE_DATEGROUP_YEAR,
+ self::AUTOFILTER_RULETYPE_DATEGROUP_MONTH,
+ self::AUTOFILTER_RULETYPE_DATEGROUP_DAY,
+ self::AUTOFILTER_RULETYPE_DATEGROUP_HOUR,
+ self::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE,
+ self::AUTOFILTER_RULETYPE_DATEGROUP_SECOND,
+ ];
+
+ const AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY = 'yesterday';
+ const AUTOFILTER_RULETYPE_DYNAMIC_TODAY = 'today';
+ const AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW = 'tomorrow';
+ const AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE = 'yearToDate';
+ const AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR = 'thisYear';
+ const AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER = 'thisQuarter';
+ const AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH = 'thisMonth';
+ const AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK = 'thisWeek';
+ const AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR = 'lastYear';
+ const AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER = 'lastQuarter';
+ const AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH = 'lastMonth';
+ const AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK = 'lastWeek';
+ const AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR = 'nextYear';
+ const AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER = 'nextQuarter';
+ const AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH = 'nextMonth';
+ const AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK = 'nextWeek';
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1 = 'M1';
+ const AUTOFILTER_RULETYPE_DYNAMIC_JANUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2 = 'M2';
+ const AUTOFILTER_RULETYPE_DYNAMIC_FEBRUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3 = 'M3';
+ const AUTOFILTER_RULETYPE_DYNAMIC_MARCH = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4 = 'M4';
+ const AUTOFILTER_RULETYPE_DYNAMIC_APRIL = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5 = 'M5';
+ const AUTOFILTER_RULETYPE_DYNAMIC_MAY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6 = 'M6';
+ const AUTOFILTER_RULETYPE_DYNAMIC_JUNE = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7 = 'M7';
+ const AUTOFILTER_RULETYPE_DYNAMIC_JULY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8 = 'M8';
+ const AUTOFILTER_RULETYPE_DYNAMIC_AUGUST = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9 = 'M9';
+ const AUTOFILTER_RULETYPE_DYNAMIC_SEPTEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10 = 'M10';
+ const AUTOFILTER_RULETYPE_DYNAMIC_OCTOBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11 = 'M11';
+ const AUTOFILTER_RULETYPE_DYNAMIC_NOVEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11;
+ const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12 = 'M12';
+ const AUTOFILTER_RULETYPE_DYNAMIC_DECEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12;
+ const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1 = 'Q1';
+ const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2 = 'Q2';
+ const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3 = 'Q3';
+ const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4 = 'Q4';
+ const AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE = 'aboveAverage';
+ const AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE = 'belowAverage';
+
+ private static $dynamicTypes = [
+ self::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_TODAY,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE,
+ self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE,
+ ];
+
+ /*
+ * The only valid filter rule operators for filter and customFilter types are:
+ * <xsd:enumeration value="equal"/>
+ * <xsd:enumeration value="lessThan"/>
+ * <xsd:enumeration value="lessThanOrEqual"/>
+ * <xsd:enumeration value="notEqual"/>
+ * <xsd:enumeration value="greaterThanOrEqual"/>
+ * <xsd:enumeration value="greaterThan"/>
+ */
+ const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal';
+ const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual';
+ const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan';
+ const AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL = 'greaterThanOrEqual';
+ const AUTOFILTER_COLUMN_RULE_LESSTHAN = 'lessThan';
+ const AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL = 'lessThanOrEqual';
+
+ private static $operators = [
+ self::AUTOFILTER_COLUMN_RULE_EQUAL,
+ self::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
+ self::AUTOFILTER_COLUMN_RULE_GREATERTHAN,
+ self::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL,
+ self::AUTOFILTER_COLUMN_RULE_LESSTHAN,
+ self::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL,
+ ];
+
+ const AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE = 'byValue';
+ const AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT = 'byPercent';
+
+ private static $topTenValue = [
+ self::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE,
+ self::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT,
+ ];
+
+ const AUTOFILTER_COLUMN_RULE_TOPTEN_TOP = 'top';
+ const AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM = 'bottom';
+
+ private static $topTenType = [
+ self::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP,
+ self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM,
+ ];
+
+ // Rule Operators (Numeric, Boolean etc)
+// const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2
+ // Rule Operators (Numeric Special) which are translated to standard numeric operators with calculated values
+// const AUTOFILTER_COLUMN_RULE_TOPTEN = 'topTen'; // greaterThan calculated value
+// const AUTOFILTER_COLUMN_RULE_TOPTENPERCENT = 'topTenPercent'; // greaterThan calculated value
+// const AUTOFILTER_COLUMN_RULE_ABOVEAVERAGE = 'aboveAverage'; // Value is calculated as the average
+// const AUTOFILTER_COLUMN_RULE_BELOWAVERAGE = 'belowAverage'; // Value is calculated as the average
+ // Rule Operators (String) which are set as wild-carded values
+// const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A*
+// const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z
+// const AUTOFILTER_COLUMN_RULE_CONTAINS = 'contains'; // *B*
+// const AUTOFILTER_COLUMN_RULE_DOESNTCONTAIN = 'notEqual'; // notEqual *B*
+ // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values
+// const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan';
+// const AUTOFILTER_COLUMN_RULE_AFTER = 'greaterThan';
+// const AUTOFILTER_COLUMN_RULE_YESTERDAY = 'yesterday';
+// const AUTOFILTER_COLUMN_RULE_TODAY = 'today';
+// const AUTOFILTER_COLUMN_RULE_TOMORROW = 'tomorrow';
+// const AUTOFILTER_COLUMN_RULE_LASTWEEK = 'lastWeek';
+// const AUTOFILTER_COLUMN_RULE_THISWEEK = 'thisWeek';
+// const AUTOFILTER_COLUMN_RULE_NEXTWEEK = 'nextWeek';
+// const AUTOFILTER_COLUMN_RULE_LASTMONTH = 'lastMonth';
+// const AUTOFILTER_COLUMN_RULE_THISMONTH = 'thisMonth';
+// const AUTOFILTER_COLUMN_RULE_NEXTMONTH = 'nextMonth';
+// const AUTOFILTER_COLUMN_RULE_LASTQUARTER = 'lastQuarter';
+// const AUTOFILTER_COLUMN_RULE_THISQUARTER = 'thisQuarter';
+// const AUTOFILTER_COLUMN_RULE_NEXTQUARTER = 'nextQuarter';
+// const AUTOFILTER_COLUMN_RULE_LASTYEAR = 'lastYear';
+// const AUTOFILTER_COLUMN_RULE_THISYEAR = 'thisYear';
+// const AUTOFILTER_COLUMN_RULE_NEXTYEAR = 'nextYear';
+// const AUTOFILTER_COLUMN_RULE_YEARTODATE = 'yearToDate'; // <dynamicFilter val="40909" type="yearToDate" maxVal="41113"/>
+// const AUTOFILTER_COLUMN_RULE_ALLDATESINMONTH = 'allDatesInMonth'; // <dynamicFilter type="M2"/> for Month/February
+// const AUTOFILTER_COLUMN_RULE_ALLDATESINQUARTER = 'allDatesInQuarter'; // <dynamicFilter type="Q2"/> for Quarter 2
+
+ /**
+ * Autofilter Column.
+ *
+ * @var Column
+ */
+ private $parent;
+
+ /**
+ * Autofilter Rule Type.
+ *
+ * @var string
+ */
+ private $ruleType = self::AUTOFILTER_RULETYPE_FILTER;
+
+ /**
+ * Autofilter Rule Value.
+ *
+ * @var string
+ */
+ private $value = '';
+
+ /**
+ * Autofilter Rule Operator.
+ *
+ * @var string
+ */
+ private $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
+
+ /**
+ * DateTimeGrouping Group Value.
+ *
+ * @var string
+ */
+ private $grouping = '';
+
+ /**
+ * Create a new Rule.
+ *
+ * @param Column $pParent
+ */
+ public function __construct(?Column $pParent = null)
+ {
+ $this->parent = $pParent;
+ }
+
+ /**
+ * Get AutoFilter Rule Type.
+ *
+ * @return string
+ */
+ public function getRuleType()
+ {
+ return $this->ruleType;
+ }
+
+ /**
+ * Set AutoFilter Rule Type.
+ *
+ * @param string $pRuleType see self::AUTOFILTER_RULETYPE_*
+ *
+ * @return $this
+ */
+ public function setRuleType($pRuleType)
+ {
+ if (!in_array($pRuleType, self::$ruleTypes)) {
+ throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
+ }
+
+ $this->ruleType = $pRuleType;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Rule Value.
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set AutoFilter Rule Value.
+ *
+ * @param string|string[] $pValue
+ *
+ * @return $this
+ */
+ public function setValue($pValue)
+ {
+ if (is_array($pValue)) {
+ $grouping = -1;
+ foreach ($pValue as $key => $value) {
+ // Validate array entries
+ if (!in_array($key, self::$dateTimeGroups)) {
+ // Remove any invalid entries from the value array
+ unset($pValue[$key]);
+ } else {
+ // Work out what the dateTime grouping will be
+ $grouping = max($grouping, array_search($key, self::$dateTimeGroups));
+ }
+ }
+ if (count($pValue) == 0) {
+ throw new PhpSpreadsheetException('Invalid rule value for column AutoFilter Rule.');
+ }
+ // Set the dateTime grouping that we've anticipated
+ $this->setGrouping(self::$dateTimeGroups[$grouping]);
+ }
+ $this->value = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Rule Operator.
+ *
+ * @return string
+ */
+ public function getOperator()
+ {
+ return $this->operator;
+ }
+
+ /**
+ * Set AutoFilter Rule Operator.
+ *
+ * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
+ *
+ * @return $this
+ */
+ public function setOperator($pOperator)
+ {
+ if (empty($pOperator)) {
+ $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
+ }
+ if (
+ (!in_array($pOperator, self::$operators)) &&
+ (!in_array($pOperator, self::$topTenValue))
+ ) {
+ throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.');
+ }
+ $this->operator = $pOperator;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Rule Grouping.
+ *
+ * @return string
+ */
+ public function getGrouping()
+ {
+ return $this->grouping;
+ }
+
+ /**
+ * Set AutoFilter Rule Grouping.
+ *
+ * @param string $pGrouping
+ *
+ * @return $this
+ */
+ public function setGrouping($pGrouping)
+ {
+ if (
+ ($pGrouping !== null) &&
+ (!in_array($pGrouping, self::$dateTimeGroups)) &&
+ (!in_array($pGrouping, self::$dynamicTypes)) &&
+ (!in_array($pGrouping, self::$topTenType))
+ ) {
+ throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
+ }
+ $this->grouping = $pGrouping;
+
+ return $this;
+ }
+
+ /**
+ * Set AutoFilter Rule.
+ *
+ * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
+ * @param string|string[] $pValue
+ * @param string $pGrouping
+ *
+ * @return $this
+ */
+ public function setRule($pOperator, $pValue, $pGrouping = null)
+ {
+ $this->setOperator($pOperator);
+ $this->setValue($pValue);
+ // Only set grouping if it's been passed in as a user-supplied argument,
+ // otherwise we're calculating it when we setValue() and don't want to overwrite that
+ // If the user supplies an argumnet for grouping, then on their own head be it
+ if ($pGrouping !== null) {
+ $this->setGrouping($pGrouping);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get this Rule's AutoFilter Column Parent.
+ *
+ * @return Column
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Set this Rule's AutoFilter Column Parent.
+ *
+ * @param Column $pParent
+ *
+ * @return $this
+ */
+ public function setParent(?Column $pParent = null)
+ {
+ $this->parent = $pParent;
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ if ($key == 'parent') {
+ // Detach from autofilter column parent
+ $this->$key = null;
+ } else {
+ $this->$key = clone $value;
+ }
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}