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