So you want to write your own wiki script? Look no further - soon you'll have a wiki application all of your own, in less than 100 lines of PHP. (If you don't know what a wiki is, simply put, it's a dynamic collection of articles, each authored by anonymous users. It allows provides for existing articles to be edited by any other user.) Doesn't sound too difficult, does it? I didn't think so. I'm not very good with words, so let's just jump right in. First off, open up your favorite PHP/HTML editor and throw this in there:

<?php
define('TITLE',"QuikWIKI");
define('TEMPLATE',"TEMPLATE");
define('ROOT',"pages/");
define('INDEX',"Index");
?>

What that little bit of code did was define a few global settings. TITLE is the title of your wiki, TEMPLATE is the filename of your page template, ROOT is the root directory where all of the wiki page files will be kept, and INDEX is the default wiki page. These settings will be used often throughout the script, so become familiar with them.

Next thing we're going to want to do is some quick error checking. Add this to your script:

if (!is_dir(ROOT) || !is_writeable(ROOT))
{
 printf("Either the directory %s doesn't exist, or you don't have write privileges.",ROOT);
 exit();
}

This section makes sure that you have full access to the ROOT folder we specified earlier. If not, it quits processing the script. Now that we have that out of the way, we can get to some of the juicier parts.

Now, we're going to lay out the function for displaying the pages according to a template. The template filename was defined earlier in TEMPLATE. Throw this peice of code in your script:

function renderPage($page, $content)
{
 if (!$fp = fopen(TEMPLATE,"r")) { return "Could not open template."; }
 $res = @fread($fp,filesize(TEMPLATE)); fclose($fp);
 $res = preg_replace("/\{TITLE\}/",TITLE,$res);
 $res = preg_replace("/\{PAGE\}/",$page,$res);
 $res = preg_replace("/\{HOME\}/",sprintf("<a href=\"?%s\">%s</a>",INDEX,INDEX),$res);
 if (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\">%s</a>",$page,"Cancel"),$res);
 } else {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\$\">%s</a>",$page,"Edit"),$res);
 }
 $res = preg_replace("/\{GO\}/","<form method=post action=?><input type=text name=go>" .
  "<input type=submit value=Go></form>",$res);
 $res = preg_replace("/\{CONTENT\}/",$content,$res);
 return $res;
}

Yes, this piece of code is a bit longer, and a bit more complex than the previous ones, but the idea behind it is pretty simple. All it does is read the data of the template file in, and parse it according to directives marked like {DIRECTIVE}. {TITLE} displays the Wiki's title, {PAGE} displays the page's title, {HOME} makes a link to the INDEX page, {EDIT} makes a link to edit the current page, and {CONTENT} will hold the main page content. Simple enough, moving on.

Here comes the real workhorse of the script - the content formatter. This guy is the one that does the nasty regular expressions and replacements to make wiki pages look the way they do.

function parseContent($page)
{
 $pagefile = ROOT . $page . ".txt";
 if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
 $content = @fread($fp,filesize($pagefile)); fclose($fp);
 $content = htmlentities($content);
 $content = preg_replace("/\[([0-9a-zA-Z\- \_]+)\]/","<a href=\"?$1\">$1</a>",$content);
 $content = str_replace("{b}","<b>",$content);
 $content = str_replace("{/b}","</b>",$content);
 $content = str_replace("{i}","<i>",$content);
 $content = str_replace("{/i}","</i>",$content);
 $content = str_replace("{u}","<u>",$content);
 $content = str_replace("{/u}","</u>",$content);
 $content = str_replace("{s}","<s>",$content);
 $content = str_replace("{/s}","</s>",$content);
 $content = str_replace("{pre}","<pre>",$content);
 $content = str_replace("{/pre}","</pre>",$content);
 $content = preg_replace('/^\*(.*)\n/Um',"<li>$1</li><br>", $content);
 $content = str_replace("\n","<br>",$content);
 return $content;
}

If you look hard enough, you'll also notice that this controls the actual creation of pages. If a page doesn't exist, it's created dynamically.

Here is the last big block of code... the brains of the operation. This peice decides what's going to happen; whether or not the user is going to see a page, or see it's edit screen.

if ((!$_SERVER['QUERY_STRING'] || preg_match("/[(\.+|\\+)|\/+]/", $_SERVER['QUERY_STRING'])) .
 && !@$_POST['go'])
{
 printf("%s",renderPage(INDEX, parseContent(INDEX)));
} else {
 if (@$_POST['go'])
 {
  if (!preg_match("/[(\.+|\\+)|\/+]/", @$_POST['go']))
  {
   header(sprintf("Location: ?%s",@$_POST['go']));
  } else {
   header("Location: ?");
  }
 } elseif (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $page = substr($_SERVER['QUERY_STRING'], 0, strlen($_SERVER['QUERY_STRING']) - 1);
  if (!@$_POST['newcontent'])
  {
   $content = sprintf("<center><form method=post action=\"?%s\">",$_SERVER['QUERY_STRING']);
   $content .= "<textarea name=newcontent rows=15 cols=40>";
   $pagefile = ROOT . $page . ".txt";
   if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
   $editcontent = @fread($fp,filesize($pagefile)); fclose($fp);
   $content .= $editcontent . "</textarea><br><input type=submit value='Edit $page'>" . 
    "</form></center>";
   printf("%s",renderPage($page,$content));
  } else {
   $pagefile = ROOT . $page . ".txt";
   if ($fp = @fopen($pagefile,"w")) { @fwrite($fp,$_POST['newcontent']); }
   header(sprintf("Location: ?%s",$page));
  }
 } else {
  printf("%s",renderPage($_SERVER['QUERY_STRING'], parseContent($_SERVER['QUERY_STRING'])));
 }
}

Alright... what doesn't this do? This takes care of malicious page addresses (it makes sure that people don't try to access folders that shouldn't be accessed), and it decides whether or not the user wants to edit or view a page.

Now, let's take a look at the code, all put together, to see what it should look like:

<?php
define('TITLE',"QuikWIKI");
define('TEMPLATE',"TEMPLATE");
define('ROOT',"pages/");
define('INDEX',"Index");

if (!is_dir(ROOT) || !is_writeable(ROOT))
{
 printf("Either the directory %s doesn't exist, or you don't have write privileges.",ROOT);
 exit();
}

function renderPage($page, $content)
{
 if (!$fp = fopen(TEMPLATE,"r")) { return "Could not open template."; }
 $res = @fread($fp,filesize(TEMPLATE)); fclose($fp);
 $res = preg_replace("/\{TITLE\}/",TITLE,$res);
 $res = preg_replace("/\{PAGE\}/",$page,$res);
 $res = preg_replace("/\{HOME\}/",sprintf("<a href=\"?%s\">%s</a>",INDEX,INDEX),$res);
 if (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\">%s</a>",$page,"Cancel"),$res);
 } else {
  $res = preg_replace("/\{EDIT\}/",sprintf("<a href=\"?%s\$\">%s</a>",$page,"Edit"),$res);
 }
 $res = preg_replace("/\{GO\}/","<form method=post action=?><input type=text name=go>" .
  "<input type=submit value=Go></form>",$res);
 $res = preg_replace("/\{CONTENT\}/",$content,$res);
 return $res;
}

function parseContent($page)
{
 $pagefile = ROOT . $page . ".txt";
 if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
 $content = @fread($fp,filesize($pagefile)); fclose($fp);
 $content = htmlentities($content);
 $content = preg_replace("/\[([0-9a-zA-Z\- \_]+)\]/","<a href=\"?$1\">$1</a>",$content);
 $content = str_replace("{b}","<b>",$content);
 $content = str_replace("{/b}","</b>",$content);
 $content = str_replace("{i}","<i>",$content);
 $content = str_replace("{/i}","</i>",$content);
 $content = str_replace("{u}","<u>",$content);
 $content = str_replace("{/u}","</u>",$content);
 $content = str_replace("{s}","<s>",$content);
 $content = str_replace("{/s}","</s>",$content);
 $content = str_replace("{pre}","<pre>",$content);
 $content = str_replace("{/pre}","</pre>",$content);
 $content = preg_replace('/^\*(.*)\n/Um',"<li>$1</li><br>", $content);
 $content = str_replace("\n","<br>",$content);
 return $content;
}

if ((!$_SERVER['QUERY_STRING'] || preg_match("/[(\.+|\\+)|\/+]/", $_SERVER['QUERY_STRING'])) .
 && !@$_POST['go'])
{
 printf("%s",renderPage(INDEX, parseContent(INDEX)));
} else {
 if (@$_POST['go'])
 {
  if (!preg_match("/[(\.+|\\+)|\/+]/", @$_POST['go']))
  {
   header(sprintf("Location: ?%s",@$_POST['go']));
  } else {
   header("Location: ?");
  }
 } elseif (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING']) - 1) == "$")
 {
  $page = substr($_SERVER['QUERY_STRING'], 0, strlen($_SERVER['QUERY_STRING']) - 1);
  if (!@$_POST['newcontent'])
  {
   $content = sprintf("<center><form method=post action=\"?%s\">",$_SERVER['QUERY_STRING']);
   $content .= "<textarea name=newcontent rows=15 cols=40>";
   $pagefile = ROOT . $page . ".txt";
   if (!$fp = @fopen($pagefile,"r")) { $fp = @fopen($pagefile,"w"); }
   $editcontent = @fread($fp,filesize($pagefile)); fclose($fp);
   $content .= $editcontent . "</textarea><br><input type=submit value='Edit $page'>" . 
    "</form></center>";
   printf("%s",renderPage($page,$content));
  } else {
   $pagefile = ROOT . $page . ".txt";
   if ($fp = @fopen($pagefile,"w")) { @fwrite($fp,$_POST['newcontent']); }
   header(sprintf("Location: ?%s",$page));
  }
 } else {
  printf("%s",renderPage($_SERVER['QUERY_STRING'], parseContent($_SERVER['QUERY_STRING'])));
 }
}
?>

It's a beauty, isn't it? That's enough of that. Hopefully, now you more understand the internal workings of a wiki script, and you probably realize that creating one isn't as complicated as you thought it would be. Just for reference, here's an example template file that you could use for your brand-new wiki:

<html>
 <head>
  <title>{TITLE} - {PAGE}</title>
  <style>
   * { font-family: "courier new"; font-size: 12; }
   a { color: gray; }
   form { display: inline; }
   .header { border: none; }
   .content { border: solid gray 1px; padding: 10px 10px 10px 10px; }
   .footer { border: none; }
  </style>
 </head>
 <body>
  <div class=header>
   <b>{TITLE} - {PAGE}</b>
  </div>
  <br>
  <div class=content>
   {CONTENT} 
  </div>
  <br>
  <div class=footer>
   Options: {HOME} | {EDIT} | {GO}
  </div>
 </body>
</html> 

Well, there you have it. A fully-functional wiki ready to go. As far as using your wiki goes, it's pretty straightforward. Look at the code to see the markup available. You can use the 'Go' bar to jump to certain pages, and if they don't exist, they will be created. If you pay attention, you'll notice that a $ at the end of a URL means that the page is to be edited. Just study the source, you'll see what's going on.

The Wiki used in this tutorial is QuikWIKI, a small wiki script that I wrote. It's available at http://box43.net/phunk/qw/ - enjoy.

UPDATE: I removed the tabs, and replaced them with spaces. It now does not stretch the screen, even on 800x600.