I'm creating a randomizer engine (you can find it here: http://minaphpprojekt.szandor.com/rannames/) that takes a template file and spits out random strings based on that. It is primarily built for short strings such as names, but I want it to be powerful enough to do more advanced stuff. I'm almost there, but I have a small problem that could potentially kill the server if a badly written template file is used.
First, let's look at a sample template:
Code: Select all
m:Description This template generates potions. Only the physical attributes of the potion are given, but in time it will define the effects of the potion as well.
.a A {DENSITY} {COLOR} {FORM}, smelling like {SMELL} and tasting like {TASTE}.
.b A {DENSITY} {COLOR} {FORM}, smelling and tasting like {TASTE}.
.c A tasteless, {DENSITY} {COLOR} {FORM}, smelling like {SMELL}.
.d An odorless, {DENSITY} {COLOR} {FORM}, tasting like {TASTE}.
.e A {DENSITY} {COLOR} {FORM} without odor or taste.
[SMELL]
{THING}
{STATE} {THING}
[TASTE]
{THING}
{STATE} {THING}
{THING} and {THING}
[FORM]
draught
elixir
fluid
liquid
mixture
potion
substance
tonic
[DENSITY]
bright
bubbling
chunky
clear
dark
fizzing
glowing
milky
muddy
oily
slimy
smoking
sparkling
swirling
thick
thin
viscious
watery
weak
[COLOR]
amber
black
blue
brown
cobolt
copper
ebony
emerald
gold
green
ivory
jade
obsidian
ochre
orange
pink
purple
red
rose
ruby
silver
teal
translucent
transparent
violet
white
yellow
[STATE]
ancient
boiled
bottled
cultivated
faded
fermented
fresh
hot
old
ripe
salty
smoky
sour
stored
strange
strong
sweetened
watered down
well tended
[THING]
acid
acorns
alchohol
ale
apples
blood
blueberries
boots
brandy
bread
cabbage
candy
caramel
carrots
cherries
chocolate
cinnamon
cloth
coffee
dust
eggs
fish
garlic
grapes
grass
herbs
honey
iron
lamp oil
leather
lemon
licorice
lime
liquor
mead
meat
metal
milk
mint
mud
oil
onions
paper
peaches
peanuts
pears
peppar
pepper
pus
rasberries
rhubarbs
roses
rot
rum
salt
spice
strawberries
sugar
sweat
tea
tomatoes
urine
vanilla
vomit
walnuts
water
wax
wine
woolThis is the code of the randomizer engine:
Code: Select all
<?
// First we open the template file for reading.
$file = fopen($namefile, "r") or exit("Unable to open file!");
// As long as we don't reach the end of the file, the following is run on each line.
while(!feof($file)) {
// We go to the next line (or the first line, if run for the first time).
$nextline = fgets($file);
// Two consecutive slashes and anything after is removed, as are empty lines and beginning and trailing whitespaces.
$nextline = preg_replace("/\/\/.+|(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "", $nextline);
$nextline = preg_replace("/^[ \t]+|[ \t]+$|\n$/", "", $nextline);
// If, after this, the line is not empty...
if ($nextline != "") {
// If the line begins with "m:" it is metadata and placed in the $metainfo array.
if (preg_match('/^\m\:/',$nextline)) {
$metatitle = preg_replace('/^\m\:/','',$nextline);
$nextline = $metatitle;
$metatitle = preg_replace('/(?=\s).*/','',$metatitle);
$nextline = preg_replace('/^\S*/','',$nextline);
$nextline = preg_replace('/^\s*/','',$nextline);
$metainfo[$metatitle] = $nextline;
// If the line begins with a dot it is a pattern and is placed in the $patterns array
} elseif (preg_match('/^\./',$nextline)) {
$pattitle = preg_replace('/^\./','',$nextline);
$pattitle = preg_replace('/(?= ).*/','',$pattitle);
$nextline = preg_replace('/^[^\n|^ ]*(?= )/','',$nextline);
$nextline = preg_replace('/^ /','',$nextline);
$patterns[$pattitle] = $nextline;
// If the line begins with a square bracket it is a list header and is saved in a variable for use soon.
} elseif (preg_match('/^\[.*/',$nextline)) {
$nextline = preg_replace('/^\[/','',$nextline);
$nextline = preg_replace('/\]$/','',$nextline);
$sectline = $nextline;
// If the line is neither of the above it's considered a part of a list and is placed in a multidimensional array under the previously saved header.
} else {
$elements[$sectline][] = $nextline;
}
}
}
$tags = $elements;
// When it's all over, we close the file. We won't be needing it anymore.
fclose($file);
// This function does the substituting.
function randel($anelement) {
global $elements; // We need this to be global.
$anelement = preg_replace("/\{|\}/","",$anelement); // We strip away the curly brackets.
$anelement = implode($anelement); // It needs to be a string, not an array.
$anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; // Since array_rand() can't be used on multi-dimensional arrays, we count the number of values in the selected sub-array, ...
$anelement2 = rand(0,$anelement2); // ... randomly choose a number ...
$theelement = $elements[$anelement][$anelement2]; // ... and then call the sub array.
if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) {
$theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel,$theelement);
}
return $theelement;
}
?>Code: Select all
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<?
include 'settings.php'; // Loads the settings.
include_once 'lang' . DIRECTORY_SEPARATOR . $settings[lang] . '.php'; // Loads the language of the GUI.
?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="<? echo $settings[lang]; ?>" xml:lang="<? echo $settings['lang']; ?>">
<head>
<title><? echo $lang['title']; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="rannames.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1><? echo $lang['title']; ?></h1>
<p><? echo $lang['intro']; ?></p>
<form method="post" action="<? $_SERVER['PHP_SELF'] ?>">
<p>
<select name="template">
<?
if (array_key_exists('_submit_check', $_POST)) { // If a template has been set by the user,
$namefile = "templates/" . $_POST['template']; // that template is active.
} else { // Otherwise,
$namefile = "templates/" . $settings['def_temp']; // the default template is active.
}
// This will find all documents with the ".name" file ending in the folder "templates" and list them in an array.
if ($handle = opendir('templates')) { // We open the folder.
$counter = 0; // The initial position of the array to write to.
while (false !== ($file = readdir($handle))) { // Loops as long as there are files to list in the folder.
if (preg_match('/^.+\.name$/',$file)) { // Makes sure only ".name"-files are processed.
$templatedir[$counter] = $file; // The actual writing of the array.
$counter = $counter + 1; // The next position of the array to write to.
}
}
}
closedir($handle); // Then we close the directory.
sort($templatedir); // Let's sort the list alphabetically.
foreach ($templatedir as $key => $val) { // For every value in the array,
if ($val != $_POST['template']) { // if the template is not the active one,
echo "\t\t\t\t\t<option value=\"$val\">$val</option>\n"; // we add it to the list of options.
} else { // If it´s the active template,
echo "\t\t\t\t\t<option value=\"$val\" selected=\"selected\">$val</option>\n"; // we make it pre-selected.
}
}
?>
</select>
</p>
<p><input style="width:3em;" name="quantity" maxlength="3" value="<? if ($_POST['quantity'] != "") { // If the user has set a quantity of names to list,
echo $_POST['quantity']; // we write out that quantity.
} else { // Otherwise,
echo "10"; // we use a default.
} ?>" /><input type="hidden" name="_submit_check" value="1" /></p>
<p><button type="submit"><? echo $lang['button']; ?></button></p>
</form>
<h2><? echo $lang['metadata']; ?></h2>
<?
require 'engine.php'; // We'll need the actual randomizer engine now.
if (isset($metainfo)) {
echo "<dl>\n";
foreach($metainfo as $metatitle => $metatext) {
echo "<dt>" . $metatitle . "</dt>\n";
echo "<dd>" . $metatext . "</dd>\n";
}
echo "</dl>\n";
} else {
echo "<p style=\"font-style:italic;\">No meta info.</p>\n";
}
/*if (isset($metainfo)) {
echo "<pre>";
print_r ($metainfo);
echo "</pre>";
} else {
echo "<p>No meta info.</p>\n";
}*/
?>
<h2><? echo $lang['list']; ?></h2>
<ul id="namelist">
<?
// If the user has set a quantity of names to list, we randomize a name that number of times. Otherwise, we do it ten times.
if ($_POST['quantity'] != "") {
$b = $_POST['quantity'];
} else {
$b = 10;
}
for($a = 0;$a != $b;$a++) { // This loops the number of times specified above.
$thepatterntitle = array_rand($patterns); // We choose a random pattern,
$thepattern = $patterns[$thepatterntitle]; // and write it to a variable.
$name = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel,$thepattern); // This replaces the pattern with a random name, based on that pattern.
echo "\t\t\t<li>" . $name . "</li>\n"; // Then we just print it on the page.
}
?>
</ul>
<?
// This lists all availible patterns in the template file.
/* echo "<h2>" . $lang['patterns'] . "</h2>\n<ul>";
foreach($patterns as $patnum => $patpat) {
echo "<li>" . $patpat . "</li>\n";
}
echo "</ul>"; */
echo "<h2>Input file</h2>\n";
echo "<pre class=\"box\">\n";
$file = fopen($namefile, "r") or exit("Unable to open file!");
while(!feof($file)) {
// $nextline = fgets($file);
echo fgets($file);
}
echo "</pre>";
?>
</body>
</html>Can it be done? If so, how?
(I suppose I could create 50 identical functions and call one after another, but that doesn't seem to be very correct.)