c1a9b09a94631600cb79682fd7a83cdbd6db792e
[phancap.git] / src / phancap / Options.php
1 <?php
2 namespace phancap;
3
4 class Options
5 {
6     public $options = array(
7         /**
8          * Browser settings
9          */
10         'url' => array(
11             'title'     => 'Website URL',
12             'default'   => null,
13             'type'      => 'url',
14             'required'  => true,
15         ),
16         'bwidth' => array(
17             'title'   => 'Browser width',
18             'default' => 1024,
19             'type'    => 'int',
20             'min'     => 16,
21             'max'     => 2048,
22         ),
23         'bheight' => array(
24             'title'   => 'Browser height',
25             'default' => null,
26             'type'    => 'int',
27             'min'     => 16,
28             'max'     => 8192,
29         ),
30         /**
31          * Screenshot settings
32          */
33         'swidth' => array(
34             'title'   => 'Screenshot width',
35             'default' => null,
36             'type'    => 'int',
37             'min'     => 16,
38             'max'     => 8192,
39         ),
40         'sheight' => array(
41             'title'   => 'Screenshot height',
42             'default' => null,
43             'type'    => 'int',
44             'min'     => 16,
45             'max'     => 8192,
46         ),
47         'sformat' => array(
48             'title'   => 'Screenshot format',
49             'default' => 'png',
50             'type'    => array('png', 'jpg', 'pdf'),
51         ),
52         'smode' => array(
53             'title'   => 'Screenshot mode',
54             'default' => 'screen',
55             'type'    => array('screen', 'page'),
56         ),
57         'smaxage' => array(
58             'title'   => 'Maximum age for a screenshot',
59             'default' => null,
60             'type'    => 'age',
61             'min'     => null,
62         ),
63         /**
64          * Authentication
65          */
66         'atimestamp' => array(
67             'title'   => 'Timestamp the request has been generated',
68             'default' => null,
69             'type'    => 'skip',
70         ),
71         'atoken' => array(
72             'title'   => 'Access token (user name)',
73             'default' => null,
74             'type'    => 'skip',
75         ),
76         'asignature' => array(
77             'title'   => 'Access signature',
78             'default' => null,
79             'type'    => 'skip',
80         ),
81     );
82
83     public $values = array();
84
85     /**
86      * @var Config
87      */
88     protected $config;
89
90
91     /**
92      * Parses an array of options, validates them and writes them into
93      * $this->values.
94      *
95      * @param array $arValues Array of options, e.g. $_GET
96      */
97     public function parse($arValues)
98     {
99         foreach ($this->options as $name => $arOption) {
100             $this->values[$name] = $arOption['default'];
101             if (!isset($arValues[$name])) {
102                 if (isset($arOption['required'])) {
103                     throw new \InvalidArgumentException(
104                         $name . ' parameter missing'
105                     );
106                 }
107                 continue;
108             }
109
110             if ($arOption['type'] == 'url') {
111                 $this->values[$name] = $this->validateUrl($arValues[$name]);
112             } else if ($arOption['type'] == 'int') {
113                 $this->values[$name] = $this->validateInt(
114                     $arValues[$name], $arOption['min'], $arOption['max']
115                 );
116             } else if (gettype($arOption['type']) == 'array') {
117                 $this->values[$name] = $this->validateArray(
118                     $arValues[$name], $arOption['type']
119                 );
120             } else if ($arOption['type'] == 'age') {
121                 $this->values[$name] = $this->clamp(
122                     static::validateAge($arValues[$name]),
123                     $arOption['min'], null,
124                     true
125                 );
126             } else if ($arOption['type'] != 'skip') {
127                 throw new \InvalidArgumentException(
128                     'Unsupported option type: ' . $arOption['type']
129                 );
130             }
131             unset($arValues[$name]);
132         }
133
134         if (count($arValues) > 0) {
135             throw new \InvalidArgumentException(
136                 'Unsupported parameter: ' . implode(', ', array_keys($arValues))
137             );
138         }
139
140         $this->calcPageSize();
141     }
142
143     protected function calcPageSize()
144     {
145         if ($this->values['swidth'] === null) {
146             $this->values['swidth'] = $this->values['bwidth'];
147         }
148         if ($this->values['smode'] == 'page') {
149             return;
150         }
151
152         if ($this->values['sheight'] !== null) {
153             $this->values['bheight'] = intval(
154                 $this->values['bwidth'] / $this->values['swidth']
155                 * $this->values['sheight']
156             );
157         } else if ($this->values['bheight'] !== null) {
158             $this->values['sheight'] = intval(
159                 $this->values['swidth'] / $this->values['bwidth']
160                 * $this->values['bheight']
161             );
162         } else {
163             //no height set. use 4:3
164             $this->values['sheight'] = $this->values['swidth'] / 4 * 3;
165             $this->values['bheight'] = $this->values['bwidth'] / 4 * 3;
166         }
167     }
168
169     protected function clamp($value, $min, $max, $silent = false)
170     {
171         if ($min !== null && $value < $min) {
172             if ($silent) {
173                 $value = $min;
174             } else {
175                 throw new \InvalidArgumentException(
176                     'Value must be at least ' . $min
177                 );
178             }
179         }
180         if ($max !== null && $value > $max) {
181             if ($silent) {
182                 $value = $max;
183             } else {
184                 throw new \InvalidArgumentException(
185                     'Value may be up to ' . $min
186                 );
187             }
188         }
189         return $value;
190     }
191
192     /**
193      * Validates an age is numeric. If it is not numeric, it's interpreted as
194      * a ISO 8601 duration specification.
195      *
196      * @param string $value Age in seconds
197      *
198      * @return integer Age in seconds
199      *
200      * @throws \InvalidArgumentException
201      * @link   http://en.wikipedia.org/wiki/Iso8601#Durations
202      */
203     public static function validateAge($value)
204     {
205         if (!is_numeric($value)) {
206             //convert short notation to seconds
207             $value = 'P' . ltrim(strtoupper($value), 'P');
208             try {
209                 $interval = new \DateInterval($value);
210             } catch (\Exception $e) {
211                 throw new \InvalidArgumentException(
212                     'Invalid age: ' . $value
213                 );
214             }
215             $value = 86400 * (
216                 $interval->y * 365
217                 + $interval->m * 30
218                 + $interval->d
219             ) + $interval->h * 3600
220                 + $interval->m * 60
221                 + $interval->s;
222         }
223         return $value;
224     }
225
226     protected function validateArray($value, $options)
227     {
228         if (array_search($value, $options) === false) {
229             throw new \InvalidArgumentException(
230                 'Invalid value ' . $value . '.'
231                 . ' Allowed: ' . implode(', ', $options)
232             );
233         }
234         return $value;
235     }
236
237     protected function validateInt($value, $min, $max)
238     {
239         if (!is_numeric($value)) {
240             throw new \InvalidArgumentException(
241                 'Value must be a number'
242             );
243         }
244         $value = (int) $value;
245         return $this->clamp($value, $min, $max);
246     }
247
248     protected function validateUrl($url)
249     {
250         $parts = parse_url($url);
251         if ($parts === false) {
252             throw new \InvalidArgumentException('Invalid URL');
253         }
254         if (!isset($parts['scheme'])) {
255             $url = 'http://' . $url;
256             $parts = parse_url($url);
257         } else if ($parts['scheme'] != 'http' && $parts['scheme'] != 'https') {
258             throw new \InvalidArgumentException('Unsupported protocol');
259         }
260         if (!isset($parts['host'])) {
261             throw new \InvalidArgumentException('URL host missing');
262         }
263         return $url;
264     }
265
266     public function setConfig(Config $config)
267     {
268         $this->config = $config;
269         $this->options['smaxage']['default'] = $this->config->screenshotMaxAge;
270         $this->options['smaxage']['min']     = $this->config->screenshotMinAge;
271     }
272 }
273 ?>