root/trunk/System/Daemon/Options.php

Revision 181, 16.1 kB (checked in by kevin, 5 months ago)

Fixed bug: Strict Standards: Non-static method System_Daemon_Options::_replaceVars() cannot be called statically

  • Property svn:keywords set to Id
Line 
1<?php
2/* vim: set noai expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
3/**
4 * System_Daemon turns PHP-CLI scripts into daemons.
5 *
6 * PHP version 5
7 *
8 * @category  System
9 * @package   System_Daemon
10 * @author    Kevin van Zonneveld <kevin@vanzonneveld.net>
11 * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
12 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD Licence
13 * @version   SVN: Release: $Id$
14 * @link      http://trac.plutonia.nl/projects/system_daemon
15 */
16
17/**
18 * Mechanism for validating, getting and setting a predefined set of options.
19 *
20 * @category  System
21 * @package   System_Daemon
22 * @author    Kevin van Zonneveld <kevin@vanzonneveld.net>
23 * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
24 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD Licence
25 * @version   SVN: Release: $Id$
26 * @link      http://trac.plutonia.nl/projects/system_daemon
27 *
28 */
29class System_Daemon_Options
30{
31    /**
32     * Keep track of active state for all Options
33     *
34     * @var array
35     */
36    protected $_options = array();
37   
38    /**
39     * Definitions for all Options
40     *
41     * @var array
42     */
43    protected $_definitions = array();
44   
45    /**
46     * Wether all the options have been initialized
47     *
48     * @var boolean
49     */
50    protected $_isInitialized = false;
51
52    /**
53     * Holds errors
54     *
55     * @var array
56     */
57    public $errors = array();
58   
59   
60    /**
61     * Constructor
62     *
63     * @param array $definitions The predefined option definitions
64     */
65    public function __construct($definitions) 
66    {
67        if (!is_array($definitions) || !count($definitions)) {
68            return false;
69        }
70       
71        $this->_definitions = $definitions;
72    }
73   
74    /**
75     * Retrieves any option found in $_definitions
76     *
77     * @param string $name Name of the Option
78     *
79     * @return boolean
80     */
81    public function getOption($name)
82    {
83        if (!isset($this->_options[$name])) {
84            return null;
85        }
86        return $this->_options[$name];
87    }//end getOption()   
88   
89    /**
90     * Gets an array of options found in $_definitions
91     *
92     * @return array
93     */
94    public function getOptions()
95    {
96        return $this->_options;
97    }//end getOptions()   
98   
99    /**
100     * Sets any option found in $_definitions
101     *
102     * @param string $name  Name of the Option
103     * @param mixed  $value Value of the Option
104     *
105     * @return boolean
106     */
107    public function setOption($name, $value)
108    {
109        $success = true;
110        // Not validated?
111        if (!$this->_validate($name, $value, $reason)) {
112            // Default not used or failed as well!
113            $this->errors[] = "Option ".$name." invalid: ".$reason;
114            $success        = false;
115        }
116       
117        $this->_options[$name] = $value;
118        return $success;
119    }//end setOption()
120       
121    /**
122     * Sets an array of options found in $_definitions
123     *
124     * @param array $use_options Array with Options
125     *
126     * @return boolean
127     */
128    public function setOptions($use_options)
129    {
130        $success = true;
131        foreach ($use_options as $name=>$value) {
132            if (!$this->setOption($name, $value)) {
133                $success = false;
134            }
135        }
136        return $success;
137    }//end setOptions()
138   
139    /**
140     * Wether options are initialized
141     *
142     * @return boolean
143     */
144    public function isInitialized()
145    {
146        return $this->_isInitialized;
147    }//end isInitialized()       
148   
149    /**
150     * Checks if all the required options are set & met.
151     * Initializes, sanitizes & defaults unset variables
152     *
153     * @param boolean $premature Whether to do a premature option init
154     *
155     * @return mixed integer or boolean
156     */
157    public function init($premature=false) 
158    {
159        // If already initialized, skip
160        if (!$premature && $this->isInitialized()) {
161            return true;
162        }       
163       
164        $options_met = 0;
165       
166        foreach ($this->_definitions as $name=>$definition) {
167            // Skip non-required options
168            if (!isset($definition["required"]) 
169                || $definition["required"] !== true ) {
170                continue;
171            }
172           
173            // Required options remain
174            if (!isset($this->_options[$name])) {               
175                if (!$this->_setDefault($name) && !$premature) {
176                    $this->errors[] = "Required option: ".$name. 
177                        " not set. No default value available either.";
178                    return false;
179                } 
180            }
181           
182            $options_met++;
183        }
184               
185        if (!$premature) {
186            $this->_isInitialized = true;
187        }
188       
189        return $options_met;
190       
191    }//end init()       
192   
193   
194   
195    /**
196     * Validates any option found in $_definitions
197     *
198     * @param string $name    Name of the Option
199     * @param mixed  $value   Value of the Option
200     * @param string &$reason Why something does not validate
201     *
202     * @return boolean
203     */
204    protected function _validate($name, $value, &$reason="")
205    {
206        $reason = false;
207       
208        if (!$reason && !isset($this->_definitions[$name])) {
209            $reason = "Option ".$name." not found in definitions";
210        }
211       
212        $definition = $this->_definitions[$name];
213       
214        if (!$reason && !isset($definition["type"])) {
215            $reason = "Option ".$name.":type not found in definitions";
216        }
217       
218        // Compile array of allowd main & subtypes
219        $_allowedTypes = $this->_allowedTypes($definition["type"]);
220       
221        // Loop over main & subtypes to detect matching format
222        if (!$reason) {
223            $type_valid = false;
224            foreach ($_allowedTypes as $type_a=>$sub_types) {
225                foreach ($sub_types as $type_b) {
226                   
227                    // Determine range based on subtype
228                    // Range is used to contain an integer or strlen
229                    // between min-max
230                    $parts = explode("-", $type_b);
231                    $from  = $to = false;
232                    if (count($parts) == 2 ) {
233                        $from   = $parts[0];
234                        $to     = $parts[1];
235                        $type_b = "range";
236                    }
237           
238                    switch ($type_a) {
239                    case "boolean":
240                        $type_valid = is_bool($value);
241                        break;
242                    case "object":
243                        $type_valid = is_object($value) || is_resource($value);
244                        break;
245                    case "string":
246                        switch ($type_b) {
247                        case "email":
248                            $exp  = "^[a-z0-9]+([._-][a-z0-9]+)*@([a-z0-9]+";
249                            $exp .= "([._-][a-z0-9]+))+$";
250                            if (eregi($exp, $value)) {
251                                $type_valid = true;
252                            }
253                            break;
254                        case "unix":
255                            if ($this->strIsUnix($value)) {
256                                $type_valid = true;
257                            }
258                            break;
259                        case "unix_filepath":
260                            if ($this->strIsUnixFile($value)) {
261                                $type_valid = true;
262                            }
263                            break;
264                        case "existing_dirpath":
265                            if (is_dir($value)) {
266                                $type_valid = true;
267                            }
268                            break;
269                        case "existing_filepath":
270                            if (is_file($value)) {
271                                $type_valid = true;
272                            }
273                            break;
274                        case "creatable_filepath":
275                            if (is_dir(dirname($value)) 
276                                && is_writable(dirname($value))) {
277                                $type_valid = true;
278                            }
279                            break;
280                        case "normal":
281                        default: 
282                            // String?
283                            if (!is_resource($value) && !is_array($value) 
284                                && !is_object($value)) {
285                                // Range?
286                                if ($from === false && $to === false) {
287                                    $type_valid = true;
288                                } else {
289                                    // Enfore range as well
290                                    if (strlen($value) >= $from 
291                                        && strlen($value) <= $to) {
292                                        $type_valid = true;
293                                    }
294                                }
295                            }
296                            break;
297                        }
298                        break;
299                    case "number":
300                        switch ($type_b) {
301                        default:
302                        case "normal":
303                            // Numeric?
304                            if (is_numeric($value)) {
305                                // Range ?
306                                if ($from === false && $to === false) {
307                                    $type_valid = true;
308                                } else {
309                                    // Enfore range as well
310                                    if ($value >= $from && $value <= $to) {
311                                        $type_valid = true;
312                                    }
313                                }
314                            }
315                            break;                           
316                        }
317                        break;
318                    default:
319                        $this->errors[] =  "Type ".
320                            $type_a." not defined";
321                        break;
322                    }               
323                }
324            }
325        }
326       
327        if (!$type_valid) {
328            $reason = "Option ".$name." does not match type: ".
329                $definition["type"]."";
330        }
331       
332        if ($reason !== false) {
333            $this->errors[] = $reason;
334            return false;
335        }
336       
337        return true;
338    }//end _validate()   
339
340    /**
341     * Sets any option found in $_definitions to its default value
342     *
343     * @param string $name Name of the Option
344     *
345     * @return boolean
346     */
347    protected function _setDefault($name)
348    {
349        if (!isset($this->_definitions[$name])) {
350            return false;
351        }       
352        $definition = $this->_definitions[$name];
353
354        if (!isset($definition["type"])) {
355            return false;
356        }
357        if (!isset($definition["default"])) {
358            return false;
359        }
360       
361        // Compile array of allowd main & subtypes
362        $_allowedTypes = $this->_allowedTypes($definition["type"]);       
363       
364        $type  = $definition["type"];
365        $value = $definition["default"];
366
367        if (isset($_allowedTypes["string"]) && !is_bool($value)) {
368            // Replace variables
369            $value = preg_replace_callback('/\{([^\{\}]+)\}/is', 
370                array($this, "_replaceVars"), $value);
371           
372            // Replace functions
373            $value = preg_replace_callback('/\@([\w_]+)\(([^\)]+)\)/is', 
374                array($this, "_replaceFuncs"), $value);
375        }
376                       
377        $this->_options[$name] = $value;
378        return true;
379    }//end _setDefault()   
380   
381    /**
382     * Callback function to replace variables in defaults
383     *
384     * @param array $matches Matched functions
385     *
386     * @return string
387     */
388    protected function _replaceVars($matches)
389    {
390        // Init
391        $allowedVars = array(
392            "SERVER.SCRIPT_NAME", 
393            "OPTIONS.*"
394        );
395        $filterVars  = array(
396            "SERVER.SCRIPT_NAME"=>array("realpath")
397        );
398       
399        $fullmatch          = array_shift($matches);
400        $fullvar            = array_shift($matches);
401        $parts              = explode(".", $fullvar);
402        list($source, $var) = $parts;
403        $var_use            = false;
404        $var_key            = $source.".".$var; 
405       
406        // Allowed
407        if (!in_array($var_key, $allowedVars) 
408            && !in_array($source.".*", $allowedVars)) {
409            return "FORBIDDEN_VAR_".$var_key;
410        }
411       
412        // Mapping of textual sources to real sources
413        if ($source == "SERVER") {
414            $source_use = &$_SERVER;
415        } elseif ($source == "OPTIONS") {
416            $source_use = &$this->_options; 
417        } else {
418            $source_use = false;
419        }
420       
421        // Exists?
422        if ($source_use === false) {
423            return "UNUSABLE_VARSOURCE_".$source;
424        }
425        if (!isset($source_use[$var])) { 
426            return "NONEXISTING_VAR_".$var_key;     
427        }
428       
429        $var_use = $source_use[$var];
430       
431        // Filtering
432        if (isset($filterVars[$var_key]) && is_array($filterVars[$var_key])) {
433            foreach ($filterVars[$var_key] as $filter_function) {
434                if (!function_exists($filter_function)) {
435                    return "NONEXISTING_FILTER_".$filter_function;
436                }
437                $var_use = call_user_func($filter_function, $var_use);
438            }
439        }       
440       
441        return $var_use;       
442    }//end _replaceVars()
443   
444    /**
445     * Callback function to replace function calls in defaults
446     *
447     * @param array $matches Matched functions
448     *
449     * @return string
450     */
451    protected function _replaceFuncs($matches)
452    {
453        $allowedFunctions = array("basename", "dirname");
454       
455        $fullmatch = array_shift($matches);
456        $function  = array_shift($matches);
457        $arguments = $matches;
458       
459        if (!in_array($function, $allowedFunctions)) {
460            return "FORBIDDEN_FUNCTION_".$function;           
461        }
462       
463        if (!function_exists($function)) {
464            return "NONEXISTING_FUNCTION_".$function; 
465        }
466       
467        return call_user_func_array($function, $arguments);
468    }//end _replaceFuncs()
469
470    /**
471     * Compile array of allowed types
472     *
473     * @param string $str String that contains allowed type information
474     *
475     * @return array     
476     */
477    protected function _allowedTypes($str)
478    {
479        $allowed_types = array();
480        $raw_types     = explode("|", $str);
481        foreach ($raw_types as $raw_type) {
482            $raw_subtypes = explode("/", $raw_type);
483            $type_a       = array_shift($raw_subtypes);
484            if (!count($raw_subtypes)) {
485                $raw_subtypes = array("normal");
486            } 
487            $allowed_types[$type_a] = $raw_subtypes;
488        }
489        return $allowed_types;
490    }   
491   
492
493    /**
494     * Check if a string has a unix proof file-format (stripped spaces,
495     * special chars, etc)
496     *
497     * @param string $str What string to test for unix compliance
498     *
499     * @return boolean
500     */
501    protected function strIsUnixFile( $str )
502    {
503        return preg_match('/^[a-z0-9_\.\/\-]+$/', $str);
504    }//end strIsUnixFile()
505   
506    /**
507     * Check if a string has a unix proof format (stripped spaces,
508     * special chars, etc)
509     *
510     * @param string $str What string to test for unix compliance
511     *
512     * @return boolean
513     */   
514    protected function strIsUnix( $str )
515    {
516        return preg_match('/^[a-z0-9_]+$/', $str);
517    }//end strIsUnix()
518
519    /**
520     * Convert a string to a unix proof format (strip spaces,
521     * special chars, etc)
522     *
523     * @param string $str What string to make unix compliant
524     *
525     * @return string
526     */
527    protected function strToUnix( $str )
528    {
529        return preg_replace('/[^0-9a-z_]/', '', strtolower($str));
530    }//end strToUnix()
531       
532}//end class
533?>
Note: See TracBrowser for help on using the browser.