Rule.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
  3. use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
  4. use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
  5. class Rule
  6. {
  7. const AUTOFILTER_RULETYPE_FILTER = 'filter';
  8. const AUTOFILTER_RULETYPE_DATEGROUP = 'dateGroupItem';
  9. const AUTOFILTER_RULETYPE_CUSTOMFILTER = 'customFilter';
  10. const AUTOFILTER_RULETYPE_DYNAMICFILTER = 'dynamicFilter';
  11. const AUTOFILTER_RULETYPE_TOPTENFILTER = 'top10Filter';
  12. private static $ruleTypes = [
  13. // Currently we're not handling
  14. // colorFilter
  15. // extLst
  16. // iconFilter
  17. self::AUTOFILTER_RULETYPE_FILTER,
  18. self::AUTOFILTER_RULETYPE_DATEGROUP,
  19. self::AUTOFILTER_RULETYPE_CUSTOMFILTER,
  20. self::AUTOFILTER_RULETYPE_DYNAMICFILTER,
  21. self::AUTOFILTER_RULETYPE_TOPTENFILTER,
  22. ];
  23. const AUTOFILTER_RULETYPE_DATEGROUP_YEAR = 'year';
  24. const AUTOFILTER_RULETYPE_DATEGROUP_MONTH = 'month';
  25. const AUTOFILTER_RULETYPE_DATEGROUP_DAY = 'day';
  26. const AUTOFILTER_RULETYPE_DATEGROUP_HOUR = 'hour';
  27. const AUTOFILTER_RULETYPE_DATEGROUP_MINUTE = 'minute';
  28. const AUTOFILTER_RULETYPE_DATEGROUP_SECOND = 'second';
  29. private static $dateTimeGroups = [
  30. self::AUTOFILTER_RULETYPE_DATEGROUP_YEAR,
  31. self::AUTOFILTER_RULETYPE_DATEGROUP_MONTH,
  32. self::AUTOFILTER_RULETYPE_DATEGROUP_DAY,
  33. self::AUTOFILTER_RULETYPE_DATEGROUP_HOUR,
  34. self::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE,
  35. self::AUTOFILTER_RULETYPE_DATEGROUP_SECOND,
  36. ];
  37. const AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY = 'yesterday';
  38. const AUTOFILTER_RULETYPE_DYNAMIC_TODAY = 'today';
  39. const AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW = 'tomorrow';
  40. const AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE = 'yearToDate';
  41. const AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR = 'thisYear';
  42. const AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER = 'thisQuarter';
  43. const AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH = 'thisMonth';
  44. const AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK = 'thisWeek';
  45. const AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR = 'lastYear';
  46. const AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER = 'lastQuarter';
  47. const AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH = 'lastMonth';
  48. const AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK = 'lastWeek';
  49. const AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR = 'nextYear';
  50. const AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER = 'nextQuarter';
  51. const AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH = 'nextMonth';
  52. const AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK = 'nextWeek';
  53. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1 = 'M1';
  54. const AUTOFILTER_RULETYPE_DYNAMIC_JANUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1;
  55. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2 = 'M2';
  56. const AUTOFILTER_RULETYPE_DYNAMIC_FEBRUARY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2;
  57. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3 = 'M3';
  58. const AUTOFILTER_RULETYPE_DYNAMIC_MARCH = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3;
  59. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4 = 'M4';
  60. const AUTOFILTER_RULETYPE_DYNAMIC_APRIL = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4;
  61. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5 = 'M5';
  62. const AUTOFILTER_RULETYPE_DYNAMIC_MAY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5;
  63. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6 = 'M6';
  64. const AUTOFILTER_RULETYPE_DYNAMIC_JUNE = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6;
  65. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7 = 'M7';
  66. const AUTOFILTER_RULETYPE_DYNAMIC_JULY = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7;
  67. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8 = 'M8';
  68. const AUTOFILTER_RULETYPE_DYNAMIC_AUGUST = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8;
  69. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9 = 'M9';
  70. const AUTOFILTER_RULETYPE_DYNAMIC_SEPTEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9;
  71. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10 = 'M10';
  72. const AUTOFILTER_RULETYPE_DYNAMIC_OCTOBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10;
  73. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11 = 'M11';
  74. const AUTOFILTER_RULETYPE_DYNAMIC_NOVEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11;
  75. const AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12 = 'M12';
  76. const AUTOFILTER_RULETYPE_DYNAMIC_DECEMBER = self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12;
  77. const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1 = 'Q1';
  78. const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2 = 'Q2';
  79. const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3 = 'Q3';
  80. const AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4 = 'Q4';
  81. const AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE = 'aboveAverage';
  82. const AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE = 'belowAverage';
  83. private static $dynamicTypes = [
  84. self::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY,
  85. self::AUTOFILTER_RULETYPE_DYNAMIC_TODAY,
  86. self::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW,
  87. self::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE,
  88. self::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR,
  89. self::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER,
  90. self::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH,
  91. self::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK,
  92. self::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR,
  93. self::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER,
  94. self::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH,
  95. self::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK,
  96. self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR,
  97. self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER,
  98. self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH,
  99. self::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK,
  100. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_1,
  101. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2,
  102. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_3,
  103. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_4,
  104. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_5,
  105. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_6,
  106. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_7,
  107. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_8,
  108. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_9,
  109. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_10,
  110. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11,
  111. self::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_12,
  112. self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1,
  113. self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2,
  114. self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3,
  115. self::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_4,
  116. self::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE,
  117. self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE,
  118. ];
  119. /*
  120. * The only valid filter rule operators for filter and customFilter types are:
  121. * <xsd:enumeration value="equal"/>
  122. * <xsd:enumeration value="lessThan"/>
  123. * <xsd:enumeration value="lessThanOrEqual"/>
  124. * <xsd:enumeration value="notEqual"/>
  125. * <xsd:enumeration value="greaterThanOrEqual"/>
  126. * <xsd:enumeration value="greaterThan"/>
  127. */
  128. const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal';
  129. const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual';
  130. const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan';
  131. const AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL = 'greaterThanOrEqual';
  132. const AUTOFILTER_COLUMN_RULE_LESSTHAN = 'lessThan';
  133. const AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL = 'lessThanOrEqual';
  134. private static $operators = [
  135. self::AUTOFILTER_COLUMN_RULE_EQUAL,
  136. self::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
  137. self::AUTOFILTER_COLUMN_RULE_GREATERTHAN,
  138. self::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL,
  139. self::AUTOFILTER_COLUMN_RULE_LESSTHAN,
  140. self::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL,
  141. ];
  142. const AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE = 'byValue';
  143. const AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT = 'byPercent';
  144. private static $topTenValue = [
  145. self::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE,
  146. self::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT,
  147. ];
  148. const AUTOFILTER_COLUMN_RULE_TOPTEN_TOP = 'top';
  149. const AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM = 'bottom';
  150. private static $topTenType = [
  151. self::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP,
  152. self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM,
  153. ];
  154. // Rule Operators (Numeric, Boolean etc)
  155. // const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2
  156. // Rule Operators (Numeric Special) which are translated to standard numeric operators with calculated values
  157. // const AUTOFILTER_COLUMN_RULE_TOPTEN = 'topTen'; // greaterThan calculated value
  158. // const AUTOFILTER_COLUMN_RULE_TOPTENPERCENT = 'topTenPercent'; // greaterThan calculated value
  159. // const AUTOFILTER_COLUMN_RULE_ABOVEAVERAGE = 'aboveAverage'; // Value is calculated as the average
  160. // const AUTOFILTER_COLUMN_RULE_BELOWAVERAGE = 'belowAverage'; // Value is calculated as the average
  161. // Rule Operators (String) which are set as wild-carded values
  162. // const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A*
  163. // const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z
  164. // const AUTOFILTER_COLUMN_RULE_CONTAINS = 'contains'; // *B*
  165. // const AUTOFILTER_COLUMN_RULE_DOESNTCONTAIN = 'notEqual'; // notEqual *B*
  166. // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values
  167. // const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan';
  168. // const AUTOFILTER_COLUMN_RULE_AFTER = 'greaterThan';
  169. // const AUTOFILTER_COLUMN_RULE_YESTERDAY = 'yesterday';
  170. // const AUTOFILTER_COLUMN_RULE_TODAY = 'today';
  171. // const AUTOFILTER_COLUMN_RULE_TOMORROW = 'tomorrow';
  172. // const AUTOFILTER_COLUMN_RULE_LASTWEEK = 'lastWeek';
  173. // const AUTOFILTER_COLUMN_RULE_THISWEEK = 'thisWeek';
  174. // const AUTOFILTER_COLUMN_RULE_NEXTWEEK = 'nextWeek';
  175. // const AUTOFILTER_COLUMN_RULE_LASTMONTH = 'lastMonth';
  176. // const AUTOFILTER_COLUMN_RULE_THISMONTH = 'thisMonth';
  177. // const AUTOFILTER_COLUMN_RULE_NEXTMONTH = 'nextMonth';
  178. // const AUTOFILTER_COLUMN_RULE_LASTQUARTER = 'lastQuarter';
  179. // const AUTOFILTER_COLUMN_RULE_THISQUARTER = 'thisQuarter';
  180. // const AUTOFILTER_COLUMN_RULE_NEXTQUARTER = 'nextQuarter';
  181. // const AUTOFILTER_COLUMN_RULE_LASTYEAR = 'lastYear';
  182. // const AUTOFILTER_COLUMN_RULE_THISYEAR = 'thisYear';
  183. // const AUTOFILTER_COLUMN_RULE_NEXTYEAR = 'nextYear';
  184. // const AUTOFILTER_COLUMN_RULE_YEARTODATE = 'yearToDate'; // <dynamicFilter val="40909" type="yearToDate" maxVal="41113"/>
  185. // const AUTOFILTER_COLUMN_RULE_ALLDATESINMONTH = 'allDatesInMonth'; // <dynamicFilter type="M2"/> for Month/February
  186. // const AUTOFILTER_COLUMN_RULE_ALLDATESINQUARTER = 'allDatesInQuarter'; // <dynamicFilter type="Q2"/> for Quarter 2
  187. /**
  188. * Autofilter Column.
  189. *
  190. * @var Column
  191. */
  192. private $parent;
  193. /**
  194. * Autofilter Rule Type.
  195. *
  196. * @var string
  197. */
  198. private $ruleType = self::AUTOFILTER_RULETYPE_FILTER;
  199. /**
  200. * Autofilter Rule Value.
  201. *
  202. * @var string
  203. */
  204. private $value = '';
  205. /**
  206. * Autofilter Rule Operator.
  207. *
  208. * @var string
  209. */
  210. private $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
  211. /**
  212. * DateTimeGrouping Group Value.
  213. *
  214. * @var string
  215. */
  216. private $grouping = '';
  217. /**
  218. * Create a new Rule.
  219. *
  220. * @param Column $pParent
  221. */
  222. public function __construct(Column $pParent = null)
  223. {
  224. $this->parent = $pParent;
  225. }
  226. /**
  227. * Get AutoFilter Rule Type.
  228. *
  229. * @return string
  230. */
  231. public function getRuleType()
  232. {
  233. return $this->ruleType;
  234. }
  235. /**
  236. * Set AutoFilter Rule Type.
  237. *
  238. * @param string $pRuleType see self::AUTOFILTER_RULETYPE_*
  239. *
  240. * @throws PhpSpreadsheetException
  241. *
  242. * @return Rule
  243. */
  244. public function setRuleType($pRuleType)
  245. {
  246. if (!in_array($pRuleType, self::$ruleTypes)) {
  247. throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
  248. }
  249. $this->ruleType = $pRuleType;
  250. return $this;
  251. }
  252. /**
  253. * Get AutoFilter Rule Value.
  254. *
  255. * @return string
  256. */
  257. public function getValue()
  258. {
  259. return $this->value;
  260. }
  261. /**
  262. * Set AutoFilter Rule Value.
  263. *
  264. * @param string|string[] $pValue
  265. *
  266. * @throws PhpSpreadsheetException
  267. *
  268. * @return Rule
  269. */
  270. public function setValue($pValue)
  271. {
  272. if (is_array($pValue)) {
  273. $grouping = -1;
  274. foreach ($pValue as $key => $value) {
  275. // Validate array entries
  276. if (!in_array($key, self::$dateTimeGroups)) {
  277. // Remove any invalid entries from the value array
  278. unset($pValue[$key]);
  279. } else {
  280. // Work out what the dateTime grouping will be
  281. $grouping = max($grouping, array_search($key, self::$dateTimeGroups));
  282. }
  283. }
  284. if (count($pValue) == 0) {
  285. throw new PhpSpreadsheetException('Invalid rule value for column AutoFilter Rule.');
  286. }
  287. // Set the dateTime grouping that we've anticipated
  288. $this->setGrouping(self::$dateTimeGroups[$grouping]);
  289. }
  290. $this->value = $pValue;
  291. return $this;
  292. }
  293. /**
  294. * Get AutoFilter Rule Operator.
  295. *
  296. * @return string
  297. */
  298. public function getOperator()
  299. {
  300. return $this->operator;
  301. }
  302. /**
  303. * Set AutoFilter Rule Operator.
  304. *
  305. * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
  306. *
  307. * @throws PhpSpreadsheetException
  308. *
  309. * @return Rule
  310. */
  311. public function setOperator($pOperator)
  312. {
  313. if (empty($pOperator)) {
  314. $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
  315. }
  316. if ((!in_array($pOperator, self::$operators)) &&
  317. (!in_array($pOperator, self::$topTenValue))) {
  318. throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.');
  319. }
  320. $this->operator = $pOperator;
  321. return $this;
  322. }
  323. /**
  324. * Get AutoFilter Rule Grouping.
  325. *
  326. * @return string
  327. */
  328. public function getGrouping()
  329. {
  330. return $this->grouping;
  331. }
  332. /**
  333. * Set AutoFilter Rule Grouping.
  334. *
  335. * @param string $pGrouping
  336. *
  337. * @throws PhpSpreadsheetException
  338. *
  339. * @return Rule
  340. */
  341. public function setGrouping($pGrouping)
  342. {
  343. if (($pGrouping !== null) &&
  344. (!in_array($pGrouping, self::$dateTimeGroups)) &&
  345. (!in_array($pGrouping, self::$dynamicTypes)) &&
  346. (!in_array($pGrouping, self::$topTenType))) {
  347. throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.');
  348. }
  349. $this->grouping = $pGrouping;
  350. return $this;
  351. }
  352. /**
  353. * Set AutoFilter Rule.
  354. *
  355. * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_*
  356. * @param string|string[] $pValue
  357. * @param string $pGrouping
  358. *
  359. * @throws PhpSpreadsheetException
  360. *
  361. * @return Rule
  362. */
  363. public function setRule($pOperator, $pValue, $pGrouping = null)
  364. {
  365. $this->setOperator($pOperator);
  366. $this->setValue($pValue);
  367. // Only set grouping if it's been passed in as a user-supplied argument,
  368. // otherwise we're calculating it when we setValue() and don't want to overwrite that
  369. // If the user supplies an argumnet for grouping, then on their own head be it
  370. if ($pGrouping !== null) {
  371. $this->setGrouping($pGrouping);
  372. }
  373. return $this;
  374. }
  375. /**
  376. * Get this Rule's AutoFilter Column Parent.
  377. *
  378. * @return Column
  379. */
  380. public function getParent()
  381. {
  382. return $this->parent;
  383. }
  384. /**
  385. * Set this Rule's AutoFilter Column Parent.
  386. *
  387. * @param Column $pParent
  388. *
  389. * @return Rule
  390. */
  391. public function setParent(Column $pParent = null)
  392. {
  393. $this->parent = $pParent;
  394. return $this;
  395. }
  396. /**
  397. * Implement PHP __clone to create a deep clone, not just a shallow copy.
  398. */
  399. public function __clone()
  400. {
  401. $vars = get_object_vars($this);
  402. foreach ($vars as $key => $value) {
  403. if (is_object($value)) {
  404. if ($key == 'parent') {
  405. // Detach from autofilter column parent
  406. $this->$key = null;
  407. } else {
  408. $this->$key = clone $value;
  409. }
  410. } else {
  411. $this->$key = $value;
  412. }
  413. }
  414. }
  415. }