rules.php -
index
<?php
/* RULES.PHP - website "rules" manager/handler
See file RULES for an explanation of the how and the why of this code.
See file MODULES for information about THIS modules.
*/
setmodule();
_rules_config();
/* rules - return the rules array */
function rules() {
global $rules;
return $rules;
}
/* do_rules - this function runs the site */
function do_rules($rule) {
global $rules;
debug("'$rule'");
if (!isset($rules[$rule])) {
error("rule '$rule' not found");
return;
}
foreach ($rules[$rule] as $rulestr) {
// substitute any $vars with $_GETs
$rulestr = _getargs($rulestr);
// convert any super globals
if (preg_match('/{\$[_A-Z]*\[/',$rulestr)) {
$rulestr = "\$rulestr = \"$rulestr\";";
eval($rulestr);
}
// get function name and any argument(s)
$args = explode(" ",$rulestr);
$name = $args[0];
array_shift($args);
debug("rule '$name'");
debug($args);
// and call the function
if (function_exists($name))
call_user_func_array($name,$args);
else
error("in rule '$rule', function '$name' not found");
// If an error occurs here it would be due to a function name mis-match; i.e.
// someone changed a function name and did not change the RULES.INI file, or
// typo...
// RULES.PHP/.INI are limited to code execution paths -- no data is shared
// between this module and any other module. There are only two entry points
// to this module; when it gets included it self-initializes (and does
// nothing else) and there is one function called: this one (do_rules).
// There is a complete separation of code and data. This module knows nothing
// about the architecture of the website, and only INDEX.PHP includes it and
// calls this function.
// Well, the above two paragraphs were true until we added GET argument
// handling, so there is the default rule argument of 'op'.
}
}
/* _loadrules - the rules array is an associative array of arrays */
// The RULES.INI file is like:
//
// [read]
// function argument
// ...
//
// and is read into the $rules array.
function _loadrules($file, $replace = TRUE) {
global $rules;
if (!($data = @file($file))) {
error(htmlgeterror('RULES'),'fatal');
return;
}
$data = _readrules($data);
foreach ($data as $rule => $value) {
if ($replace)
unset($rules[$rule]);
if (!is_array($value))
$rules[$rule] = $value;
else
foreach ($value as $_)
$rules[$rule][] = $_;
}
}
/* _readrules - parse the RULES data file */
function _readrules($file) {
$section = "";
while (list (, $l) = each ($file)) {
$l = chop($l);
if (preg_match('/^;/',$l) || $l == '')
continue;
if (preg_match('/^\[(.*)\]/',$l,$res)) {
$section = $res[1];
continue;
}
if (!$section) {
preg_match('/(.*)\s*=\s*(.*)/',$l,$res);
$data[trim($res[1])] = $res[2];
}
else
$data[$section][] = $l;
}
return $data;
}
/* _getargs - convert arguments */
// replace '$var' with "$_GET['var']" - var must be as the RE
function _getargs($_) {
$re = '/\$([a-zA-Z]+)/';
if (preg_match_all($re,$_,$res)) {
foreach ($res[0] as $k => $v) {
$t = @$_GET[$res[1][$k]];
$_ = str_replace($v,$t,$_);
}
}
return $_;
}
/*
Validate all inputs - the $rules array is a list of "op", for operation - and
we do as much validation as we can here, before any of the other code even
runs. We can't do it all, like for "out of range" values like a page number
for example because we are not fully configured (can't query the database), so
we just make sure numbers are numbers and things like that.
Ideally, there should be a "rules validation list" in some kind of INI file
format, e.g. "id = [0-9]+" or something. But for right now let's just hardcode
some stuff.
*/
function _rules_config() {
global $rules;
// first, load the rules list
_loadrules("rules.ini");
// check for invalid 'op'
if (!isset($_GET['op']) || !isset($rules[$_GET['op']]))
$_GET['op'] = $rules['default'];
if ($_GET['op'] == 'preview') // internal only
$_GET['op'] = $rules['default'];
// 'arg' is checked by the CONFIG.PHP code
/*
This is one way to validate the GET data: do it all here and "change the rule"
if something is weird... the other way, as for 'page', is to do it where the
action occurs (for example, see displaypage() in DISPLAY.PHP).
The benefit of doing things here is that the validation code is "all in one
place", and it can reduce overall code size and complexity (in that there are
not disparate validation coding spread throughout the code).
The benefit of doing validation "where is as needed" is that you have more
control and you can have more specific error handling (and error messages).
Also, for this code, we try to do all the validations at one or two particular
points in the code path: i.e. here at the start of the code, or in the
display functions in DISPLAY.PHP -- again, trying "catch" invalid data early
and in obvious places.
*/
$op = $_GET['op'];
if ($op == 'grok' || $op == 'submit') {
if (!isset($_GET['id']) || !is_numeric($_GET['id']))
$_GET['op'] = $rules['default'];
}
// it would be better to just always reset if 'id' is not right, except that:
// 'page' is separate because it's 'id' can be alpha-numeric; also, we like it
// better validated right before the page is to be displayed...
// if we made all pages numeric, we'd still like to keep it separate
/*
if ($_GET['op'] == 'page') {
if (!($id = @$_GET['id']) || preg_match('/[^a-zA-Z0-9]+/',$id))
$_GET['op'] = $rules['default'];
}
*/
}
?>