this is not a blog

I Reckon This Must be the Place, I Reckon

Details of the architecture

THIS code should be considered a curiosity. If it was in the state it is in now ten years ago I think it would have caught on. It can do about 90% of what many popular Blog/CMS applications do in 1/10th the size — and with no globals, no classes and no Javascript.

It is near truly customizable, with data arrays (sometimes as INI files) controlling most important features. It has three ways to create web templates. It has an almost complete separation of code and data: no inline PHP or print statements or SQL strings strewn throughout the code for example.

I think all that has great merit.

But... It's too late. The Internet ain't for small APIs but for massive, distributed Javascript that can take months to debug and is fixed "by magic".

Sigh

  1. Well, there are two classes now, but they're really small; and there is now about 40 lines of Javascript, but only for "Admin"; and a few uses of $GLOBALS.
  2. To catch on... I will still work on it. The really interesting thing about this code? It gets smaller with each release even as more features get added. I like that.

Um, This is Weird

The code that runs this site, wirtten in PHP, used to be available for download. And then it wasn't. And it isn't.

So much of the following is pretty much meaningless. I will get around to putting up the code at some point in the future...

Got No Class

This code has no class, as in no PHP class is defined. Instead, something like this is done:

PHP code function config($var NULL$val NULL) {
static 
$config = array();

    if (
$config == array()) {
       
$config parse_ini_file('config.ini',TRUE);
       
// more here
   
}

    if (
$var === NULL)
        return 
$config;

    if (
$val !== NULL)
        return 
$config[$var] = $val;

    if (isset(
$config[$var]))
        return 
$config[$var];

    return 
'';
}

This way, config() has the basic class attribute of having "properties" that can be "set" and "get".

This simple convention eliminates entirely the use of global, something that WikiMedia code declares as "evil" while using thousands of them. sigh

The config() function can also safely recurse, i.e. the initialization code block can call itself.

This is how this code--with two exceptions in the Admin code that will go away--does not use any globals.

  1. And then, the PHP $GLOBALS superglobal is used.

Where to Start

There are many places to start a creative writing adventure (writing code is after all, or should be, an adventure) — one need not start at the beginning. But at the beginning for this little side path is a good place to start.

Start Architecture

The INDEX.PHP here is only 50 lines long but it has a specific architecture — we are working on a name, but for now call it Complete. All of the code's include files are there; the entire application loads and is initialized there; the last function call runs the site and at that point the application is in a working state and all URL data has been validated.

This Completeness means that the start process is not spread out over multiple files in multiple directories (more on that to come), and this all stems from a kind of learning disability we share: being easily confused. We never liked having to view multiple files just to "see" the basics of an application's starting process. We never liked seeing nested include files. We like code as APIs all neatly lined up in columns and rows.

We like small, complete, single file APIs, not because we are smart or anything, on the contrary, we need them to understand what we are doing.

Of course, it also helps to have code that has no globals and no classes — more on that later.

A Hint of Style

Something else of note in INDEX.PHP is a bit of a coding style we use that we call Lazy Programming, an example of which is how the included files are included:

PHP code foreach (glob('inc/*.php') as $inc)
        include(
$inc);

There was at first a list of the files to be included, and every so often one would change or a new one added — and the list had to be edited each time. Ah, no. Let the code do the work. This one small thing saves time. Rename a file? Rename the file. Add a file? Add the file.

There two types of include files. Files in INC/ are complete, standalone APIs, creating and using no data. Files in MOD/ are considered "Modules", which do create data and are "the application's API".

Modules were at first included by a list too, and they were order dependent, as each one initialized itself when included, and some modules called other modules. A list of files had to be managed. To avoid that, modules have an initialization function to be called after including them, thereby eliminating any load order dependencies and the "manual labor" of managing a list of file names:

PHP code foreach (glob('mod/*.php') as $inc) {
        include(
$inc);
       
modules_set($inc);
}
modules_init();

Where the modules() functions are part of a Module API that, although very basic as currently implemented, is for managing the modules — testing, debugging, statistics, etc.

Summary

These ideas of Completeness and Laziness are memes used throughout the code, and are a good introduction to this code.

The code is, though, far from finished, but it does work. There are odd things, complicated things and strange things (and occasionaly dumb things slip through).

More of the code architecture will be detailed here in the future.

  1. As soon as an image editor or whatever the program is that is required is acquired drawings will be made.

There Are Rules Here

This site operates through "rules" defined in an INI encoded file, RULES.INI. Here is an excerpt:

        default = read
        [read]
        displayhtml open
        displayentries
        displayhtml close
        [post]
        displayhtml open
        displayentry
        displayhtml close

A "rule" is simply ?rule in the URL. (Internally a rule is known as op, actually converted to $_GET['op'].)

The end of INDEX.PHP reads the $_GET variable which is then handed off to the function that runs the site:

PHP code $op getvar('op');
rules_run($op);

The function getvar() replaces the assignment "is set GET variable GET variable else null", which may seem odd but it reduces over all code size and complexity (something that is fun to do).

A rule is made up of functions. A rule of:

        [phpinfo]
        phpinfo

would run the PHP function phpinfo() and exit with a URL of ?phpinfo.

Rule Arguments

A rule can be assigned an argument, ?post=1, and is internally stored as $_GET['id'], which is read by getvar('id').

Rule Conditionals

Rule functions can be conditional. If a function returns a value it is stored. A following function can have an indicator to base it's execution on. The post rule is actually preceded by:

        [post]
        validate
        !notfound

where validate() (a function in RULES.PHP) checks the value of getvar('id') for being numerical and existing in the database. If it returns FALSE, the function notfound() will be executed. The other conditionals are ? and :. The ? is for functions that return TRUE, and : is for an "else" construct:

        [test]
        is_numeric $a
        ?display $a is a number
        :display $a is not a number

Since echo() and print() are not functions they cannot be used as rules; display() is a defined function that prints all of it's arguments and so does the equivalent.

Summary

The RULES.PHP file is only 250 lines long, and the rules algorithm is only 60 lines, and this is the best introduction to this code: small and efficient, simple and versatile. And we try real hard to make it so.

  1. A possible change may be to enclose arguments in quotes: display "this is a string".
  2. Though it probably ain't perfect.

The Next Little Thing

Adding up INDEX.PHP and RULES.PHP we are at about 350 lines of code (of course, a few hundred other lines is a few other files were executed to read the configuration files and initialize all the data — but that was all).

The next step is the running of the rule. The default rule is read and it is three functions, two of which simply and only display an HTML template:

        displayhtml open
        displayhtml close

Those are almost identical to:

        readfile htm/open.html
        readfile htm/close.html

Except that the HTML includes PHP variables which are properly displayed (the "template engine" will be explained later). The function displayentries() is slightly more elaborate. Basically:

        foreach (records as record)
        	displayhtml record
        displayhtml prevnext

in a typical "10 posts per page with Next and Previous links at the end" fashion.

The display code is the code's largest module at about 725 lines total — the functions mentioned here are about 175 lines — and it mainly does all the work.

Data/Code Separation

We like the idea of Decoupling Content Management, and that is something we have been trying to do, but this is not fully there yet. Our path is also very different than the other approaches to the decoupling.

A few graphics for the basic picture may help. The Monolithic approach:

Content
Management
System
Database

and the Decoupled:

Web Editing Tool
Web Framework
Content Repository

We have been calling it "Data/Code Separation". We look at representation of content solely at the content level. And all we want to manipulate content — write it, save it, view it — is an API. And the API should know nothing about the content.

Our approach to a Content Repository has been along the lines of file_put_contents() file_get_contents(). Which is exactly what our File Mode code uses. There is "on top of that" a directory structure and a naming convention — and that's it.

For MySQL Mode the database is simply an INT and a MEDIUMTEXT to achieve the near exact equivalent of file_put_contents() file_get_contents(). The repository API knows nothing about the data.

We chose a named field document format:

        title:Title
        date:March 1, 2014
        I like plankton.

Our approach to the Web Framework part has also been along the lines of an API that knows nothing about the data and there is one basic API: display_contents().

There is though, the data. The data is made up of named fields in a PHP array format:

        $data['title'] = 'Title';
        $data['date'] = 'March 1, 2014';
        $data['body'] = 'I like plankton.';

And the Representation, the "view", is a true HTML template:

        <div>
        <h1>{$data['title']}</h1>
        <div>{$data['date']}</div>
        <div>{$data['body']}</div>
        </div>

Nothing like RDFa is used as we want to reduce data as much as possible. In a way, the HTML too knows nothing about the data as the HTML is just a template. The presentation API is barely more than a print statement.

And the code? Each API is only a few hundred lines long.

And the goal? Truly reusable APIs that anyone can use for any PHP application in only hundreds of lines of code.

  1. This is not distributed scaling, except that it is as far as file_put_contents() and file_get_contents() are.
  2. And a file_delete_contents().
  3. Actually not the name used but that fits in this discussion.

The Defining Data

Key portions of this code are driven by data. The "Rules" data described above is just one example of what this is: replacing code with data.

That seems not right in that code has to be devised and written to support that data. But here are the cool parts, the algorithms remain the same and they just follow the data:

You can change the site by changing the data.
You can change the site in minutes.
You can change the site live with a simple HTML form.

That is what we mean by this motto of ours:

You can modify the code without modifying the code.

Another data array that works like this is in INC/INPUTS.PHP for parsing POST data. The post (records, MOD/RECORD.PHP) is half-way done like this and will be finished soon. The HTML templates are also almost done like this and will be finished soon.

And that is what makes this code very small and very interesting.

To Be or Not to Be

Yeah, the site's lame. But over the last two years of tweaking this code – adding considerable new features and interfaces – the code base has pretty much remained the same size.

All along I've been calling this an experiment. I want to see how to write "old style", structural code, with the following design criteria:

I have nearly achieved that in full in what I call the "Core Code". For the Admin code – especially the new "Admin Extensions" – I relax the no in-line HTML rule somewhat.

The code is split up into several "sections". By that I mean:

"All the DATA are 'here'. All the HTML is 'here'. All the SQL is 'here'."

With 'here' meaning a single location

Having all the HTML in one source file (as an array) turned out to be rather a pain to maintain – but now there are three ways to use HTML (strings and templates): in an array, in a function or in a files.

Which brings me to this aphorism:

The DATA drives the code.

So finally, as the code has become stable, I can now start writing more about this code here.

Quothe the programmer. "Later more..."