Per r38036: also fix FormOptions
[lhc/web/wiklou.git] / includes / FormOptions.php
1 <?php
2 /**
3 * Helper class to keep track of options when mixing links and form elements.
4 *
5 * @author Niklas Laxström
6 * @copyright Copyright © 2008, Niklas Laxström
7 */
8
9 class FormOptions implements ArrayAccess {
10 const AUTO = -1; //! Automatically detects simple data types
11 const STRING = 0;
12 const INT = 1;
13 const BOOL = 2;
14 const INTNULL = 3; //! Useful for namespace selector
15
16 protected $options = array();
17
18 # Setting up
19
20 public function add( $name, $default, $type = self::AUTO ) {
21 $option = array();
22 $option['default'] = $default;
23 $option['value'] = null;
24 $option['consumed'] = false;
25
26 if ( $type !== self::AUTO ) {
27 $option['type'] = $type;
28 } else {
29 $option['type'] = self::guessType( $default );
30 }
31
32 $this->options[$name] = $option;
33 }
34
35 public function delete( $name ) {
36 $this->validateName( $name, true );
37 unset($this->options[$name]);
38 }
39
40 public static function guessType( $data ) {
41 if ( is_bool($data) ) {
42 return self::BOOL;
43 } elseif( is_int($data) ) {
44 return self::INT;
45 } elseif( is_string($data) ) {
46 return self::STRING;
47 } else {
48 throw new MWException( 'Unsupported datatype' );
49 }
50 }
51
52 # Handling values
53
54 public function validateName( $name, $strict = false ) {
55 if ( !isset($this->options[$name]) ) {
56 if ( $strict ) {
57 throw new MWException( "Invalid option $name" );
58 } else {
59 return false;
60 }
61 }
62 return true;
63 }
64
65 public function setValue( $name, $value, $force = false ) {
66 $this->validateName( $name, true );
67 if ( !$force && $value === $this->options[$name]['default'] ) {
68 // null default values as unchanged
69 $this->options[$name]['value'] = null;
70 } else {
71 $this->options[$name]['value'] = $value;
72 }
73 }
74
75 public function getValue( $name ) {
76 $this->validateName( $name, true );
77 return $this->getValueReal( $this->options[$name] );
78 }
79
80 protected function getValueReal( $option ) {
81 if ( $option['value'] !== null ) {
82 return $option['value'];
83 } else {
84 return $option['default'];
85 }
86 }
87
88 public function reset( $name ) {
89 $this->validateName( $name, true );
90 $this->options[$name]['value'] = null;
91 }
92
93 public function consumeValue( $name ) {
94 $this->validateName( $name, true );
95 $this->options[$name]['consumed'] = true;
96 return $this->getValueReal( $this->options[$name] );
97 }
98
99 public function consumeValues( /*Array*/ $names ) {
100 $out = array();
101 foreach ( $names as $name ) {
102 $this->validateName( $name, true );
103 $this->options[$name]['consumed'] = true;
104 $out[] = $this->getValueReal( $this->options[$name] );
105 }
106 return $out;
107 }
108
109 # Validating values
110
111 public function validateIntBounds( $name, $min, $max ) {
112 $this->validateName( $name, true );
113
114 if ( $this->options[$name]['type'] !== self::INT )
115 throw new MWException( "Option $name is not of type int" );
116
117 $value = $this->getValueReal( $this->options[$name] );
118 $value = max( $min, min( $max, $value ) );
119
120 $this->setValue( $name, $value );
121 }
122
123 # Getting the data out for use
124
125 public function getUnconsumedValues( $all = false ) {
126 $values = array();
127 foreach ( $this->options as $name => $data ) {
128 if ( !$data['consumed'] ) {
129 if ( $all || $data['value'] !== null ) {
130 $values[$name] = $this->getValueReal( $data );
131 }
132 }
133 }
134 return $values;
135 }
136
137 public function getChangedValues() {
138 $values = array();
139 foreach ( $this->options as $name => $data ) {
140 if ( $data['value'] !== null ) {
141 $values[$name] = $data['value'];
142 }
143 }
144 return $values;
145 }
146
147 public function getAllValues() {
148 $values = array();
149 foreach ( $this->options as $name => $data ) {
150 $values[$name] = $this->getValueReal( $data );
151 }
152 return $values;
153 }
154
155 # Reading values
156
157 public function fetchValuesFromRequest( WebRequest $r, $values = false ) {
158 if ( !$values ) {
159 $values = array_keys($this->options);
160 }
161
162 foreach ( $values as $name ) {
163 $default = $this->options[$name]['default'];
164 $type = $this->options[$name]['type'];
165
166 switch( $type ) {
167 case self::BOOL:
168 $value = $r->getBool( $name, $default ); break;
169 case self::INT:
170 $value = $r->getInt( $name, $default ); break;
171 case self::STRING:
172 $value = $r->getText( $name, $default ); break;
173 case self::INTNULL:
174 $value = $r->getIntOrNull( $name ); break;
175 default:
176 throw new MWException( 'Unsupported datatype' );
177 }
178
179 if ( $value !== null ) {
180 $this->options[$name]['value'] = $value === $default ? null : $value;
181 }
182 }
183 }
184
185 /* ArrayAccess methods */
186 public function offsetExists( $name ) {
187 return isset($this->options[$name]);
188 }
189
190 public function offsetGet( $name ) {
191 return $this->getValue( $name );
192 }
193
194 public function offsetSet( $name, $value ) {
195 return $this->setValue( $name, $value );
196 }
197
198 public function offsetUnset( $name ) {
199 return $this->delete( $name );
200 }
201
202 }