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