[BUGFIX] Follow-Up: Fix major bug in Enumeration::equals + Tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Type / Enumeration.php
1 <?php
2 namespace TYPO3\CMS\Core\Type;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Thomas Maroschik <tmaroschik@dfau.de>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Type\Exception;
31
32 /**
33 * Abstract class for Enumeration.
34 * Inspired by SplEnum.
35 *
36 * The prefix "Abstract" has been left out by intention because
37 * a "type" is abstract by definition.
38 */
39 abstract class Enumeration {
40
41 /**
42 * @var mixed
43 */
44 protected $value;
45
46 /**
47 * @var array
48 */
49 protected static $enumConstants;
50
51 /**
52 * @param mixed $value
53 * @throws Exception\InvalidEnumerationValueException
54 */
55 public function __construct($value = NULL) {
56 if ($value === NULL && !defined('static::__default')) {
57 throw new Exception\InvalidEnumerationValueException(
58 sprintf('A value for %s is required if no __default is defined.', __CLASS__),
59 1381512753
60 );
61 }
62 if ($value === NULL) {
63 $value = static::__default;
64 }
65 $this->loadValues();
66 if (!$this->isValid($value)) {
67 throw new Exception\InvalidEnumerationValueException(
68 sprintf('Invalid value %s for %s', $value, __CLASS__),
69 1381512761
70 );
71 }
72 $this->setValue($value);
73 }
74
75 /**
76 * @throws \Exception
77 * @internal param string $class
78 */
79 protected function loadValues() {
80 $class = get_called_class();
81
82 if (isset(static::$enumConstants[$class])) {
83 return;
84 }
85
86 $reflection = new \ReflectionClass($class);
87 $constants = $reflection->getConstants();
88 $defaultValue = NULL;
89 if (isset($constants['__default'])) {
90 $defaultValue = $constants['__default'];
91 unset($constants['__default']);
92 }
93 if (empty($constants)) {
94 throw new Exception\InvalidEnumerationValueException(
95 sprintf(
96 'No enumeration constants defined for "%s"', $class
97 ),
98 1381512807
99 );
100 }
101 foreach ($constants as $constant => $value) {
102 if (!is_int($value) && !is_string($value)) {
103 throw new Exception\InvalidEnumerationDefinitionException(
104 sprintf(
105 'Constant value must be of type integer or string; constant=%s; type=%s',
106 $constant,
107 is_object($value) ? get_class($value) : gettype($value)
108 ),
109 1381512797
110 );
111 }
112 }
113 $constantValueCounts = array_count_values($constants);
114 arsort($constantValueCounts, SORT_NUMERIC);
115 $constantValueCount = current($constantValueCounts);
116 $constant = key($constantValueCounts);
117 if ($constantValueCount > 1) {
118 throw new Exception\InvalidEnumerationDefinitionException(
119 sprintf(
120 'Constant value is not unique; constant=%s; value=%s; enum=%s',
121 $constant, $constantValueCount, $class
122 ),
123 1381512859
124 );
125 }
126 if ($defaultValue !== NULL) {
127 $constants['__default'] = $defaultValue;
128 }
129 static::$enumConstants[$class] = $constants;
130 }
131
132 /**
133 * Set the Enumeration value to the associated enumeration value by a loose comparison.
134 * The value, that is used as the enumeration value, will be of the same type like defined in the enumeration
135 *
136 * @param mixed $value
137 * @throws Exception\InvalidEnumerationValueException
138 */
139 protected function setValue($value) {
140 $enumKey = array_search($value, static::$enumConstants[get_called_class()]);
141 if ($enumKey === FALSE) {
142 throw new Exception\InvalidEnumerationValueException(
143 sprintf('Invalid value %s for %s', $value, __CLASS__),
144 1381615295
145 );
146 }
147 $this->value = static::$enumConstants[get_called_class()][$enumKey];
148 }
149
150 /**
151 * Check if the value on this enum is a valid value for the enum
152 *
153 * @param mixed $value
154 * @return boolean
155 */
156 protected function isValid($value) {
157 return in_array($value, static::$enumConstants[get_called_class()]);
158 }
159
160 /**
161 * Get the valid values for this enum
162 * Defaults to constants you define in your subclass
163 * override to provide custom functionality
164 *
165 * @param boolean $include_default
166 * @return array
167 */
168 public function getConstants($include_default = FALSE) {
169 $enumConstants = static::$enumConstants[get_called_class()];
170 if (!$include_default) {
171 unset($enumConstants['__default']);
172 }
173 return $enumConstants;
174 }
175
176 /**
177 * Cast value to enumeration type
178 *
179 * @param mixed $value Value that has to be casted
180 * @return Enumeration
181 */
182 public static function cast($value) {
183 $currentClass = get_called_class();
184 if (!is_object($value) || get_class($value) !== $currentClass) {
185 $value = new $currentClass($value);
186 }
187 return $value;
188 }
189
190 /**
191 * Compare if the value of the current object value equals the given value
192 *
193 * @param mixed $value default
194 * @return boolean
195 */
196 public function equals($value) {
197 $value = static::cast($value);
198 return $this == $value;
199 }
200
201 /**
202 * @return string
203 */
204 public function __toString() {
205 return (string)$this->value;
206 }
207 }