c8bfa76473e2f2673942c5443e663a8c6499531e.svn-base 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2014 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Worksheet
  23. * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. /**
  28. * PHPExcel_Worksheet_AutoFilter
  29. *
  30. * @category PHPExcel
  31. * @package PHPExcel_Worksheet
  32. * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
  33. */
  34. class PHPExcel_Worksheet_AutoFilter
  35. {
  36. /**
  37. * Autofilter Worksheet
  38. *
  39. * @var PHPExcel_Worksheet
  40. */
  41. private $_workSheet = NULL;
  42. /**
  43. * Autofilter Range
  44. *
  45. * @var string
  46. */
  47. private $_range = '';
  48. /**
  49. * Autofilter Column Ruleset
  50. *
  51. * @var array of PHPExcel_Worksheet_AutoFilter_Column
  52. */
  53. private $_columns = array();
  54. /**
  55. * Create a new PHPExcel_Worksheet_AutoFilter
  56. *
  57. * @param string $pRange Cell range (i.e. A1:E10)
  58. * @param PHPExcel_Worksheet $pSheet
  59. */
  60. public function __construct($pRange = '', PHPExcel_Worksheet $pSheet = NULL)
  61. {
  62. $this->_range = $pRange;
  63. $this->_workSheet = $pSheet;
  64. }
  65. /**
  66. * Get AutoFilter Parent Worksheet
  67. *
  68. * @return PHPExcel_Worksheet
  69. */
  70. public function getParent() {
  71. return $this->_workSheet;
  72. }
  73. /**
  74. * Set AutoFilter Parent Worksheet
  75. *
  76. * @param PHPExcel_Worksheet $pSheet
  77. * @return PHPExcel_Worksheet_AutoFilter
  78. */
  79. public function setParent(PHPExcel_Worksheet $pSheet = NULL) {
  80. $this->_workSheet = $pSheet;
  81. return $this;
  82. }
  83. /**
  84. * Get AutoFilter Range
  85. *
  86. * @return string
  87. */
  88. public function getRange() {
  89. return $this->_range;
  90. }
  91. /**
  92. * Set AutoFilter Range
  93. *
  94. * @param string $pRange Cell range (i.e. A1:E10)
  95. * @throws PHPExcel_Exception
  96. * @return PHPExcel_Worksheet_AutoFilter
  97. */
  98. public function setRange($pRange = '') {
  99. // Uppercase coordinate
  100. $cellAddress = explode('!',strtoupper($pRange));
  101. if (count($cellAddress) > 1) {
  102. list($worksheet,$pRange) = $cellAddress;
  103. }
  104. if (strpos($pRange,':') !== FALSE) {
  105. $this->_range = $pRange;
  106. } elseif(empty($pRange)) {
  107. $this->_range = '';
  108. } else {
  109. throw new PHPExcel_Exception('Autofilter must be set on a range of cells.');
  110. }
  111. if (empty($pRange)) {
  112. // Discard all column rules
  113. $this->_columns = array();
  114. } else {
  115. // Discard any column rules that are no longer valid within this range
  116. list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->_range);
  117. foreach($this->_columns as $key => $value) {
  118. $colIndex = PHPExcel_Cell::columnIndexFromString($key);
  119. if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
  120. unset($this->_columns[$key]);
  121. }
  122. }
  123. }
  124. return $this;
  125. }
  126. /**
  127. * Get all AutoFilter Columns
  128. *
  129. * @throws PHPExcel_Exception
  130. * @return array of PHPExcel_Worksheet_AutoFilter_Column
  131. */
  132. public function getColumns() {
  133. return $this->_columns;
  134. }
  135. /**
  136. * Validate that the specified column is in the AutoFilter range
  137. *
  138. * @param string $column Column name (e.g. A)
  139. * @throws PHPExcel_Exception
  140. * @return integer The column offset within the autofilter range
  141. */
  142. public function testColumnInRange($column) {
  143. if (empty($this->_range)) {
  144. throw new PHPExcel_Exception("No autofilter range is defined.");
  145. }
  146. $columnIndex = PHPExcel_Cell::columnIndexFromString($column);
  147. list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->_range);
  148. if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
  149. throw new PHPExcel_Exception("Column is outside of current autofilter range.");
  150. }
  151. return $columnIndex - $rangeStart[0];
  152. }
  153. /**
  154. * Get a specified AutoFilter Column Offset within the defined AutoFilter range
  155. *
  156. * @param string $pColumn Column name (e.g. A)
  157. * @throws PHPExcel_Exception
  158. * @return integer The offset of the specified column within the autofilter range
  159. */
  160. public function getColumnOffset($pColumn) {
  161. return $this->testColumnInRange($pColumn);
  162. }
  163. /**
  164. * Get a specified AutoFilter Column
  165. *
  166. * @param string $pColumn Column name (e.g. A)
  167. * @throws PHPExcel_Exception
  168. * @return PHPExcel_Worksheet_AutoFilter_Column
  169. */
  170. public function getColumn($pColumn) {
  171. $this->testColumnInRange($pColumn);
  172. if (!isset($this->_columns[$pColumn])) {
  173. $this->_columns[$pColumn] = new PHPExcel_Worksheet_AutoFilter_Column($pColumn, $this);
  174. }
  175. return $this->_columns[$pColumn];
  176. }
  177. /**
  178. * Get a specified AutoFilter Column by it's offset
  179. *
  180. * @param integer $pColumnOffset Column offset within range (starting from 0)
  181. * @throws PHPExcel_Exception
  182. * @return PHPExcel_Worksheet_AutoFilter_Column
  183. */
  184. public function getColumnByOffset($pColumnOffset = 0) {
  185. list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->_range);
  186. $pColumn = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $pColumnOffset - 1);
  187. return $this->getColumn($pColumn);
  188. }
  189. /**
  190. * Set AutoFilter
  191. *
  192. * @param PHPExcel_Worksheet_AutoFilter_Column|string $pColumn
  193. * A simple string containing a Column ID like 'A' is permitted
  194. * @throws PHPExcel_Exception
  195. * @return PHPExcel_Worksheet_AutoFilter
  196. */
  197. public function setColumn($pColumn)
  198. {
  199. if ((is_string($pColumn)) && (!empty($pColumn))) {
  200. $column = $pColumn;
  201. } elseif(is_object($pColumn) && ($pColumn instanceof PHPExcel_Worksheet_AutoFilter_Column)) {
  202. $column = $pColumn->getColumnIndex();
  203. } else {
  204. throw new PHPExcel_Exception("Column is not within the autofilter range.");
  205. }
  206. $this->testColumnInRange($column);
  207. if (is_string($pColumn)) {
  208. $this->_columns[$pColumn] = new PHPExcel_Worksheet_AutoFilter_Column($pColumn, $this);
  209. } elseif(is_object($pColumn) && ($pColumn instanceof PHPExcel_Worksheet_AutoFilter_Column)) {
  210. $pColumn->setParent($this);
  211. $this->_columns[$column] = $pColumn;
  212. }
  213. ksort($this->_columns);
  214. return $this;
  215. }
  216. /**
  217. * Clear a specified AutoFilter Column
  218. *
  219. * @param string $pColumn Column name (e.g. A)
  220. * @throws PHPExcel_Exception
  221. * @return PHPExcel_Worksheet_AutoFilter
  222. */
  223. public function clearColumn($pColumn) {
  224. $this->testColumnInRange($pColumn);
  225. if (isset($this->_columns[$pColumn])) {
  226. unset($this->_columns[$pColumn]);
  227. }
  228. return $this;
  229. }
  230. /**
  231. * Shift an AutoFilter Column Rule to a different column
  232. *
  233. * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range.
  234. * Nor does it verify whether any column rule already exists at $toColumn, but will simply overrideany existing value.
  235. * Use with caution.
  236. *
  237. * @param string $fromColumn Column name (e.g. A)
  238. * @param string $toColumn Column name (e.g. B)
  239. * @return PHPExcel_Worksheet_AutoFilter
  240. */
  241. public function shiftColumn($fromColumn=NULL,$toColumn=NULL) {
  242. $fromColumn = strtoupper($fromColumn);
  243. $toColumn = strtoupper($toColumn);
  244. if (($fromColumn !== NULL) && (isset($this->_columns[$fromColumn])) && ($toColumn !== NULL)) {
  245. $this->_columns[$fromColumn]->setParent();
  246. $this->_columns[$fromColumn]->setColumnIndex($toColumn);
  247. $this->_columns[$toColumn] = $this->_columns[$fromColumn];
  248. $this->_columns[$toColumn]->setParent($this);
  249. unset($this->_columns[$fromColumn]);
  250. ksort($this->_columns);
  251. }
  252. return $this;
  253. }
  254. /**
  255. * Test if cell value is in the defined set of values
  256. *
  257. * @param mixed $cellValue
  258. * @param mixed[] $dataSet
  259. * @return boolean
  260. */
  261. private static function _filterTestInSimpleDataSet($cellValue,$dataSet)
  262. {
  263. $dataSetValues = $dataSet['filterValues'];
  264. $blanks = $dataSet['blanks'];
  265. if (($cellValue == '') || ($cellValue === NULL)) {
  266. return $blanks;
  267. }
  268. return in_array($cellValue,$dataSetValues);
  269. }
  270. /**
  271. * Test if cell value is in the defined set of Excel date values
  272. *
  273. * @param mixed $cellValue
  274. * @param mixed[] $dataSet
  275. * @return boolean
  276. */
  277. private static function _filterTestInDateGroupSet($cellValue,$dataSet)
  278. {
  279. $dateSet = $dataSet['filterValues'];
  280. $blanks = $dataSet['blanks'];
  281. if (($cellValue == '') || ($cellValue === NULL)) {
  282. return $blanks;
  283. }
  284. if (is_numeric($cellValue)) {
  285. $dateValue = PHPExcel_Shared_Date::ExcelToPHP($cellValue);
  286. if ($cellValue < 1) {
  287. // Just the time part
  288. $dtVal = date('His',$dateValue);
  289. $dateSet = $dateSet['time'];
  290. } elseif($cellValue == floor($cellValue)) {
  291. // Just the date part
  292. $dtVal = date('Ymd',$dateValue);
  293. $dateSet = $dateSet['date'];
  294. } else {
  295. // date and time parts
  296. $dtVal = date('YmdHis',$dateValue);
  297. $dateSet = $dateSet['dateTime'];
  298. }
  299. foreach($dateSet as $dateValue) {
  300. // Use of substr to extract value at the appropriate group level
  301. if (substr($dtVal,0,strlen($dateValue)) == $dateValue)
  302. return TRUE;
  303. }
  304. }
  305. return FALSE;
  306. }
  307. /**
  308. * Test if cell value is within a set of values defined by a ruleset
  309. *
  310. * @param mixed $cellValue
  311. * @param mixed[] $ruleSet
  312. * @return boolean
  313. */
  314. private static function _filterTestInCustomDataSet($cellValue, $ruleSet)
  315. {
  316. $dataSet = $ruleSet['filterRules'];
  317. $join = $ruleSet['join'];
  318. $customRuleForBlanks = isset($ruleSet['customRuleForBlanks']) ? $ruleSet['customRuleForBlanks'] : FALSE;
  319. if (!$customRuleForBlanks) {
  320. // Blank cells are always ignored, so return a FALSE
  321. if (($cellValue == '') || ($cellValue === NULL)) {
  322. return FALSE;
  323. }
  324. }
  325. $returnVal = ($join == PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND);
  326. foreach($dataSet as $rule) {
  327. if (is_numeric($rule['value'])) {
  328. // Numeric values are tested using the appropriate operator
  329. switch ($rule['operator']) {
  330. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL :
  331. $retVal = ($cellValue == $rule['value']);
  332. break;
  333. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL :
  334. $retVal = ($cellValue != $rule['value']);
  335. break;
  336. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN :
  337. $retVal = ($cellValue > $rule['value']);
  338. break;
  339. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL :
  340. $retVal = ($cellValue >= $rule['value']);
  341. break;
  342. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN :
  343. $retVal = ($cellValue < $rule['value']);
  344. break;
  345. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL :
  346. $retVal = ($cellValue <= $rule['value']);
  347. break;
  348. }
  349. } elseif($rule['value'] == '') {
  350. switch ($rule['operator']) {
  351. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL :
  352. $retVal = (($cellValue == '') || ($cellValue === NULL));
  353. break;
  354. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL :
  355. $retVal = (($cellValue != '') && ($cellValue !== NULL));
  356. break;
  357. default :
  358. $retVal = TRUE;
  359. break;
  360. }
  361. } else {
  362. // String values are always tested for equality, factoring in for wildcards (hence a regexp test)
  363. $retVal = preg_match('/^'.$rule['value'].'$/i',$cellValue);
  364. }
  365. // If there are multiple conditions, then we need to test both using the appropriate join operator
  366. switch ($join) {
  367. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR :
  368. $returnVal = $returnVal || $retVal;
  369. // Break as soon as we have a TRUE match for OR joins,
  370. // to avoid unnecessary additional code execution
  371. if ($returnVal)
  372. return $returnVal;
  373. break;
  374. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND :
  375. $returnVal = $returnVal && $retVal;
  376. break;
  377. }
  378. }
  379. return $returnVal;
  380. }
  381. /**
  382. * Test if cell date value is matches a set of values defined by a set of months
  383. *
  384. * @param mixed $cellValue
  385. * @param mixed[] $monthSet
  386. * @return boolean
  387. */
  388. private static function _filterTestInPeriodDateSet($cellValue, $monthSet)
  389. {
  390. // Blank cells are always ignored, so return a FALSE
  391. if (($cellValue == '') || ($cellValue === NULL)) {
  392. return FALSE;
  393. }
  394. if (is_numeric($cellValue)) {
  395. $dateValue = date('m',PHPExcel_Shared_Date::ExcelToPHP($cellValue));
  396. if (in_array($dateValue,$monthSet)) {
  397. return TRUE;
  398. }
  399. }
  400. return FALSE;
  401. }
  402. /**
  403. * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching
  404. *
  405. * @var array
  406. */
  407. private static $_fromReplace = array('\*', '\?', '~~', '~.*', '~.?');
  408. private static $_toReplace = array('.*', '.', '~', '\*', '\?');
  409. /**
  410. * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation
  411. *
  412. * @param string $dynamicRuleType
  413. * @param PHPExcel_Worksheet_AutoFilter_Column &$filterColumn
  414. * @return mixed[]
  415. */
  416. private function _dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
  417. {
  418. $rDateType = PHPExcel_Calculation_Functions::getReturnDateType();
  419. PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC);
  420. $val = $maxVal = NULL;
  421. $ruleValues = array();
  422. $baseDate = PHPExcel_Calculation_DateTime::DATENOW();
  423. // Calculate start/end dates for the required date range based on current date
  424. switch ($dynamicRuleType) {
  425. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK :
  426. $baseDate = strtotime('-7 days',$baseDate);
  427. break;
  428. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK :
  429. $baseDate = strtotime('-7 days',$baseDate);
  430. break;
  431. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH :
  432. $baseDate = strtotime('-1 month',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  433. break;
  434. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH :
  435. $baseDate = strtotime('+1 month',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  436. break;
  437. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER :
  438. $baseDate = strtotime('-3 month',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  439. break;
  440. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER :
  441. $baseDate = strtotime('+3 month',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  442. break;
  443. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR :
  444. $baseDate = strtotime('-1 year',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  445. break;
  446. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR :
  447. $baseDate = strtotime('+1 year',gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  448. break;
  449. }
  450. switch ($dynamicRuleType) {
  451. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY :
  452. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY :
  453. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW :
  454. $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(strtotime('+1 day',$baseDate));
  455. $val = (int) PHPExcel_Shared_Date::PHPToExcel($baseDate);
  456. break;
  457. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE :
  458. $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(strtotime('+1 day',$baseDate));
  459. $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0,0,0,1,1,date('Y',$baseDate)));
  460. break;
  461. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR :
  462. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR :
  463. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR :
  464. $maxVal = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0,0,0,31,12,date('Y',$baseDate)));
  465. ++$maxVal;
  466. $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0,0,0,1,1,date('Y',$baseDate)));
  467. break;
  468. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER :
  469. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER :
  470. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER :
  471. $thisMonth = date('m',$baseDate);
  472. $thisQuarter = floor(--$thisMonth / 3);
  473. $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(gmmktime(0,0,0,date('t',$baseDate),(1+$thisQuarter)*3,date('Y',$baseDate)));
  474. ++$maxVal;
  475. $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0,0,0,1,1+$thisQuarter*3,date('Y',$baseDate)));
  476. break;
  477. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH :
  478. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH :
  479. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH :
  480. $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(gmmktime(0,0,0,date('t',$baseDate),date('m',$baseDate),date('Y',$baseDate)));
  481. ++$maxVal;
  482. $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0,0,0,1,date('m',$baseDate),date('Y',$baseDate)));
  483. break;
  484. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK :
  485. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK :
  486. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK :
  487. $dayOfWeek = date('w',$baseDate);
  488. $val = (int) PHPExcel_Shared_Date::PHPToExcel($baseDate) - $dayOfWeek;
  489. $maxVal = $val + 7;
  490. break;
  491. }
  492. switch ($dynamicRuleType) {
  493. // Adjust Today dates for Yesterday and Tomorrow
  494. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY :
  495. --$maxVal;
  496. --$val;
  497. break;
  498. case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW :
  499. ++$maxVal;
  500. ++$val;
  501. break;
  502. }
  503. // Set the filter column rule attributes ready for writing
  504. $filterColumn->setAttributes(array( 'val' => $val,
  505. 'maxVal' => $maxVal
  506. )
  507. );
  508. // Set the rules for identifying rows for hide/show
  509. $ruleValues[] = array( 'operator' => PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL,
  510. 'value' => $val
  511. );
  512. $ruleValues[] = array( 'operator' => PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN,
  513. 'value' => $maxVal
  514. );
  515. PHPExcel_Calculation_Functions::setReturnDateType($rDateType);
  516. return array(
  517. 'method' => '_filterTestInCustomDataSet',
  518. 'arguments' => array( 'filterRules' => $ruleValues,
  519. 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND
  520. )
  521. );
  522. }
  523. private function _calculateTopTenValue($columnID,$startRow,$endRow,$ruleType,$ruleValue) {
  524. $range = $columnID.$startRow.':'.$columnID.$endRow;
  525. $dataValues = PHPExcel_Calculation_Functions::flattenArray(
  526. $this->_workSheet->rangeToArray($range,NULL,TRUE,FALSE)
  527. );
  528. $dataValues = array_filter($dataValues);
  529. if ($ruleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
  530. rsort($dataValues);
  531. } else {
  532. sort($dataValues);
  533. }
  534. return array_pop(array_slice($dataValues,0,$ruleValue));
  535. }
  536. /**
  537. * Apply the AutoFilter rules to the AutoFilter Range
  538. *
  539. * @throws PHPExcel_Exception
  540. * @return PHPExcel_Worksheet_AutoFilter
  541. */
  542. public function showHideRows()
  543. {
  544. list($rangeStart,$rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->_range);
  545. // The heading row should always be visible
  546. // echo 'AutoFilter Heading Row ',$rangeStart[1],' is always SHOWN',PHP_EOL;
  547. $this->_workSheet->getRowDimension($rangeStart[1])->setVisible(TRUE);
  548. $columnFilterTests = array();
  549. foreach($this->_columns as $columnID => $filterColumn) {
  550. $rules = $filterColumn->getRules();
  551. switch ($filterColumn->getFilterType()) {
  552. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_FILTER :
  553. $ruleValues = array();
  554. // Build a list of the filter value selections
  555. foreach($rules as $rule) {
  556. $ruleType = $rule->getRuleType();
  557. $ruleValues[] = $rule->getValue();
  558. }
  559. // Test if we want to include blanks in our filter criteria
  560. $blanks = FALSE;
  561. $ruleDataSet = array_filter($ruleValues);
  562. if (count($ruleValues) != count($ruleDataSet))
  563. $blanks = TRUE;
  564. if ($ruleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_FILTER) {
  565. // Filter on absolute values
  566. $columnFilterTests[$columnID] = array(
  567. 'method' => '_filterTestInSimpleDataSet',
  568. 'arguments' => array( 'filterValues' => $ruleDataSet,
  569. 'blanks' => $blanks
  570. )
  571. );
  572. } else {
  573. // Filter on date group values
  574. $arguments = array(
  575. 'date' => array(),
  576. 'time' => array(),
  577. 'dateTime' => array(),
  578. );
  579. foreach($ruleDataSet as $ruleValue) {
  580. $date = $time = '';
  581. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
  582. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== ''))
  583. $date .= sprintf('%04d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
  584. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
  585. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != ''))
  586. $date .= sprintf('%02d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
  587. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
  588. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== ''))
  589. $date .= sprintf('%02d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
  590. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
  591. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== ''))
  592. $time .= sprintf('%02d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
  593. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
  594. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== ''))
  595. $time .= sprintf('%02d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
  596. if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
  597. ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== ''))
  598. $time .= sprintf('%02d',$ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
  599. $dateTime = $date . $time;
  600. $arguments['date'][] = $date;
  601. $arguments['time'][] = $time;
  602. $arguments['dateTime'][] = $dateTime;
  603. }
  604. // Remove empty elements
  605. $arguments['date'] = array_filter($arguments['date']);
  606. $arguments['time'] = array_filter($arguments['time']);
  607. $arguments['dateTime'] = array_filter($arguments['dateTime']);
  608. $columnFilterTests[$columnID] = array(
  609. 'method' => '_filterTestInDateGroupSet',
  610. 'arguments' => array( 'filterValues' => $arguments,
  611. 'blanks' => $blanks
  612. )
  613. );
  614. }
  615. break;
  616. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER :
  617. $customRuleForBlanks = FALSE;
  618. $ruleValues = array();
  619. // Build a list of the filter value selections
  620. foreach($rules as $rule) {
  621. $ruleType = $rule->getRuleType();
  622. $ruleValue = $rule->getValue();
  623. if (!is_numeric($ruleValue)) {
  624. // Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards
  625. $ruleValue = preg_quote($ruleValue);
  626. $ruleValue = str_replace(self::$_fromReplace,self::$_toReplace,$ruleValue);
  627. if (trim($ruleValue) == '') {
  628. $customRuleForBlanks = TRUE;
  629. $ruleValue = trim($ruleValue);
  630. }
  631. }
  632. $ruleValues[] = array( 'operator' => $rule->getOperator(),
  633. 'value' => $ruleValue
  634. );
  635. }
  636. $join = $filterColumn->getJoin();
  637. $columnFilterTests[$columnID] = array(
  638. 'method' => '_filterTestInCustomDataSet',
  639. 'arguments' => array( 'filterRules' => $ruleValues,
  640. 'join' => $join,
  641. 'customRuleForBlanks' => $customRuleForBlanks
  642. )
  643. );
  644. break;
  645. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER :
  646. $ruleValues = array();
  647. foreach($rules as $rule) {
  648. // We should only ever have one Dynamic Filter Rule anyway
  649. $dynamicRuleType = $rule->getGrouping();
  650. if (($dynamicRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
  651. ($dynamicRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)) {
  652. // Number (Average) based
  653. // Calculate the average
  654. $averageFormula = '=AVERAGE('.$columnID.($rangeStart[1]+1).':'.$columnID.$rangeEnd[1].')';
  655. $average = PHPExcel_Calculation::getInstance()->calculateFormula($averageFormula,NULL,$this->_workSheet->getCell('A1'));
  656. // Set above/below rule based on greaterThan or LessTan
  657. $operator = ($dynamicRuleType === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
  658. ? PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
  659. : PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
  660. $ruleValues[] = array( 'operator' => $operator,
  661. 'value' => $average
  662. );
  663. $columnFilterTests[$columnID] = array(
  664. 'method' => '_filterTestInCustomDataSet',
  665. 'arguments' => array( 'filterRules' => $ruleValues,
  666. 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR
  667. )
  668. );
  669. } else {
  670. // Date based
  671. if ($dynamicRuleType{0} == 'M' || $dynamicRuleType{0} == 'Q') {
  672. // Month or Quarter
  673. sscanf($dynamicRuleType,'%[A-Z]%d', $periodType, $period);
  674. if ($periodType == 'M') {
  675. $ruleValues = array($period);
  676. } else {
  677. --$period;
  678. $periodEnd = (1+$period)*3;
  679. $periodStart = 1+$period*3;
  680. $ruleValues = range($periodStart,periodEnd);
  681. }
  682. $columnFilterTests[$columnID] = array(
  683. 'method' => '_filterTestInPeriodDateSet',
  684. 'arguments' => $ruleValues
  685. );
  686. $filterColumn->setAttributes(array());
  687. } else {
  688. // Date Range
  689. $columnFilterTests[$columnID] = $this->_dynamicFilterDateRange($dynamicRuleType, $filterColumn);
  690. break;
  691. }
  692. }
  693. }
  694. break;
  695. case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER :
  696. $ruleValues = array();
  697. $dataRowCount = $rangeEnd[1] - $rangeStart[1];
  698. foreach($rules as $rule) {
  699. // We should only ever have one Dynamic Filter Rule anyway
  700. $toptenRuleType = $rule->getGrouping();
  701. $ruleValue = $rule->getValue();
  702. $ruleOperator = $rule->getOperator();
  703. }
  704. if ($ruleOperator === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
  705. $ruleValue = floor($ruleValue * ($dataRowCount / 100));
  706. }
  707. if ($ruleValue < 1) $ruleValue = 1;
  708. if ($ruleValue > 500) $ruleValue = 500;
  709. $maxVal = $this->_calculateTopTenValue($columnID,$rangeStart[1]+1,$rangeEnd[1],$toptenRuleType,$ruleValue);
  710. $operator = ($toptenRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
  711. ? PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
  712. : PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
  713. $ruleValues[] = array( 'operator' => $operator,
  714. 'value' => $maxVal
  715. );
  716. $columnFilterTests[$columnID] = array(
  717. 'method' => '_filterTestInCustomDataSet',
  718. 'arguments' => array( 'filterRules' => $ruleValues,
  719. 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR
  720. )
  721. );
  722. $filterColumn->setAttributes(
  723. array('maxVal' => $maxVal)
  724. );
  725. break;
  726. }
  727. }
  728. // echo 'Column Filter Test CRITERIA',PHP_EOL;
  729. // var_dump($columnFilterTests);
  730. //
  731. // Execute the column tests for each row in the autoFilter range to determine show/hide,
  732. for ($row = $rangeStart[1]+1; $row <= $rangeEnd[1]; ++$row) {
  733. // echo 'Testing Row = ',$row,PHP_EOL;
  734. $result = TRUE;
  735. foreach($columnFilterTests as $columnID => $columnFilterTest) {
  736. // echo 'Testing cell ',$columnID.$row,PHP_EOL;
  737. $cellValue = $this->_workSheet->getCell($columnID.$row)->getCalculatedValue();
  738. // echo 'Value is ',$cellValue,PHP_EOL;
  739. // Execute the filter test
  740. $result = $result &&
  741. call_user_func_array(
  742. array('PHPExcel_Worksheet_AutoFilter',$columnFilterTest['method']),
  743. array(
  744. $cellValue,
  745. $columnFilterTest['arguments']
  746. )
  747. );
  748. // echo (($result) ? 'VALID' : 'INVALID'),PHP_EOL;
  749. // If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
  750. if (!$result)
  751. break;
  752. }
  753. // Set show/hide for the row based on the result of the autoFilter result
  754. // echo (($result) ? 'SHOW' : 'HIDE'),PHP_EOL;
  755. $this->_workSheet->getRowDimension($row)->setVisible($result);
  756. }
  757. return $this;
  758. }
  759. /**
  760. * Implement PHP __clone to create a deep clone, not just a shallow copy.
  761. */
  762. public function __clone() {
  763. $vars = get_object_vars($this);
  764. foreach ($vars as $key => $value) {
  765. if (is_object($value)) {
  766. if ($key == '_workSheet') {
  767. // Detach from worksheet
  768. $this->{$key} = NULL;
  769. } else {
  770. $this->{$key} = clone $value;
  771. }
  772. } elseif ((is_array($value)) && ($key == '_columns')) {
  773. // The columns array of PHPExcel_Worksheet_AutoFilter objects
  774. $this->{$key} = array();
  775. foreach ($value as $k => $v) {
  776. $this->{$key}[$k] = clone $v;
  777. // attach the new cloned Column to this new cloned Autofilter object
  778. $this->{$key}[$k]->setParent($this);
  779. }
  780. } else {
  781. $this->{$key} = $value;
  782. }
  783. }
  784. }
  785. /**
  786. * toString method replicates previous behavior by returning the range if object is
  787. * referenced as a property of its parent.
  788. */
  789. public function __toString() {
  790. return (string) $this->_range;
  791. }
  792. }