Ariadne Internals
- Ariadne >
- Library >
- Ariadne 2.4 >
- Howto's >
- Extending Ariadne >
Between entering a URL in the browser and the resulting page, Ariadne does quite a bit of processing. It is important to understand which steps Ariadne undertakes before that output is actually generated when you want to design your own Ariadne application. Important issues like security and performance can only be decided upon when you have at least some understanding of the internal processing needed for certain tasks.
The main functional parts through which a browser request is handled and a respons generated is:
browser > loader > store > objects > templates > output

The main tasks or functions of these parts are:
Loader:
- translates url requests to ariadne get requests
- sessions
- language
- retrieves/sets credentials (default: encrypted cookie)
- abstraction to web functions (headers, cache, etc)
- manages caching
Store:
- Finds and instantiates objects via an abstract interface. This interface can be translated to sql (MySQL, PostgreSQL) or to other database systems, even filebased in case of ariadne export files.
- Emulates a filesystem like structure, with inheritance and search indexes
- Any type of data can be stored anywhere in the store.
Objects:
- Object oriented system with PHP classes, using inheritance.
- Datatypes and integrity checks on user input is defined per class.
- Classes can implement extra functionality, e.g. to generate thumbnails from images.
Templates:
- Only part of ariadne to generate output, this is here page design is handled.
- System templates for often used functionality and for the management interface. These cannot be redefined (This is partly because of security considerations, partly to make sure the management interface cannot be broken by user designed templates).
- Object templates can be created via the management interface to generate the site layout. Can be redefined at any point in the data tree.
- Templates are inherited by descendant classes. Object templates are only inherited down the data tree (or filesystem structure), not globally.
Following a simple request from the browser request to the output.
Given the following request:
http://www.example.com/ariadne/loader.php/view.html
These are the steps Ariadne takes to generate the resulting page.
1. Webserver
The request reaches the webserver.
The webserver will rewrite the
requested url to something which corresponds with a real filename on the local
system, e.g. (with Apache):
/var/www/example.com/ariadne/loader.php/view.html
Then it will start from the left and try to access each part (Apache skips the parts before the document root). When it comes at loader.php, it will notice that it isn't a directory, but a file. At that point it regards the rest of the url as extra information, for use by/in that file.
As loader.php has an extension of .php, which should be assigned to a specific handler for PHP, it will start the PHP engine with that file as input. The script will then have the following information handed to it:
PATH_TRANSLATED = /var/www/example.com/ariadne/loader.php
PATH_INFO = /view.html
QUERY_STRING =
2. Loader
The loader is now running and checks the PATH_INFO variable to see which page is requested. It cuts the last part, after the last '/', from the PATH_INFO variable, and regards that as the template. If '/' is the last character, it will default to 'view.html'.
Then it will try to find a session id. Session id's are always the first part of the path. A Session id looks like this: '/-Se2N-'. The 4 characters are random base64 characters. If found, it is also cut from the PATH_INFO.
The last piece of information that can be part of the PATH_INFO is the language select. Languages are encoded as their two letter codes, e.g. English is 'en', Dutch is 'nl', etc. If found, it again is cut from the PATH_INFO, including the leading '/' ('/en', or '/nl').
The remaining part then is the path in the Ariadne store to the object the user requested.
The loader will now check the cache on the filesystem to see if that object, with the same template and the same query information (QUERY_STRING, empty in this case) is already stored there. If so, the 'freshness' is checked and if the image is still fresh, it will return that image, including HTTP headers to the browser. Freshness is stored as the 'last change' time or 'ctime' of the object, which must be in the future for the image to be considered fresh.
If no image was found, then the Ariadne store is initialized and the request passed on to the store via a call to the get() function. e.g.:
$store->call('view.html',Array(),$store->get('/'))
ariadne/www/loader.php, line 94.
(POST and GET variables are merged into an array, in this case an empty one.)
3. Store
The first store function to be called it $store->get(), as that is an argument to the $store->call() function.
$store->get('/')
ariadne/lib/stores/mysqlstore.phtml, line 200.
This instructs the store to find and retrieve the data of the object with path '/'. In the case of the MySQL and PostgreSQL stores, it will return a pointer to the result set of a SQL query.
$store->call('view.html', Array(), $store->get('/'))
ariadne/lib/stores/mysqlstore.phtml, line 134.
This instructs the store to instantiate the objects listed in the given result set (from $store->get) and call the template 'view.html' with no arguments on each of them. In this case only one object is in the result set. Each object is instantiated using the $store->newobject() function. Given the type of the object (which is also retrieved by $store->get()) it loads the corresponding PHP class definition and creates a new object of that class. If the class is a descendant of another class, the class definition file itself makes sure that that class definition is loaded first.
Finally the function 'call()' is called in the instantiated object. e.g.:
$object->call('view.html',Array())
ariadne/lib/stores/mysqlstore.phtml, line 160.
4. Objects
$object->call() is defined at the class pobject, the root class of all Ariadne classes (ariadne/lib/objects/pobject.phtml, line 18.)
It has two arguments, the template (or function) to call and the arguments to that template.
Call() will set some information needed by the template. It will push the arguments on a stack, which allows object templates to get at them. It will then instantiate the arguments in the current scope, which allows class templates to get at them. Then it will set the requested and real language in $this->reqnls and $this->nls respectively. (If a requested language is not available, the real language will be set to the default for that object.)
Then it will create the shortcut $this->nlsdata pointing to the data for the language set in $this->nls. Finally it will try to find the template in the list of templates for it's class.
Each class has a directory with all the class templates for that class (ariadne/lib/templates/{class-name}/). If the template is not in there, Call searches for a default template, named 'default.phtml'. If that isn't available either, it will retry both steps in the directory of it's parent class.
In the root class (pobject) directory, 'default.phtml' must be defined. This template checks whether the given template might actually be the (file)name of an object in a directory. If so, $object->get() is called with that filename and the 'view.html' template.
Otherwise, the system assumes that the requested template is an object template which is only defined in the Ariadne store, not on the filesystem. It checks the login credentials and grants of the current web user, and if they check out, will call $object->CheckConfig('view.html', Array()) to call the object template (if it exists).
If the object template is simply not defined, CheckConfig() will return true and default.phtml calls the template "user.notfound.html", which shows a message saying the object wasn't found.We'll get to the specifics of CheckLogin() and CheckConfig() later.
In this case the requested template 'view.html' is defined as a class template in the templates directory of the class (pdir, the class of the Ariadne root), and $object->call() includes ('runs') the template.
After the template is done, $object->call() pops the arguments from the stack. It then checks whether either $ARCurrent->arResult or $arResult is set. If so, it returns that variable to the calling template or function.
But first we'll continue with the template.
5. Templates
The template requested is the 'view.html' template of the root of Ariadne, which is a directory, type 'pdir'.
The template is defined at ariadne/lib/templates/pdir/view.html
The first thing it does (which is specific to the pdir class) is to check whether a child object with the filename 'index.html' is exists. If so, instead of showing the contents of the directory, it will call the view.html template of that object instead, using $this->get("index.html/", "view.html").
$this points to the current object, the root, with class pdir. It inherits class ppage, which inherits class pobject. The function get() is defined in the class pobject, and calls the store exactly as the loader would.
In this case we'll assume that no such object exists. Then the template goes on with the line:
if ($this->CheckLogin("read") &&
$this->CheckConfig($arCallFunction, $arCallArgs) ) {
This line is a very important part of any class template. The first part, CheckLogin, takes care of most of the security of Ariadne. The second part, CheckConfig, takes care of most of the transparant features (object templates, language configuration, cache configuration, etc.).
5.1. $this->CheckLogin("read")
This function is defined in ariadne/lib/objects/pobject.phtml, line 383.
When run the first time, it will acquire the login name, kept in a session, which the loader keeps track of. If no login is not set, the user is assumed to be a public user, with the special (hardcoded) login 'public'.
If a login is set, the login and password, available in the session data, are checked against the credentials the browser sends with each request in the form of a cookie. This cookie contains an encrypted combination of the login, password and session id. If this check fails, the login again reverts to 'public'.
Then, with the now validated login, the function searches for the user object (puser) with that login in the store. It then checks whether the passwords match. If the user is not found, or the passwords do not match, the login prompt is again displayed, with a suitable message.
It is important to note that Ariadne does not keep an unencrypted version of each users' password. It uses the standard Unix technique of storing a one way encrypted version of the password, then match that version with a newly encrypted version of the password that the user entered.
The next step is to check whether the user login is the special user 'admin'. In that case no grants are read from the store, as this user is hardcoded to always have all grants. Otherwise, the function calls a utility function, GetValidGrants(), which returns a list of all grants affecting the current object for the current user.
Finally the requested grant is checked against the list of all valid grants. If the requested grant is present in the list (or the user is 'admin'), the function returns true. In every other case it will return false, after having already called the login template (lib/templates/pobject/user.login.html).
Note: if CheckLogin is run again during handling of the same request, e.g. in multiple templates, it will not display a login template again. Only the first call to CheckLogin can display a login screen, for obvious reasons.
GetValidGrants()
This function, at line 317 in lib/objects/pobject.phtml, handles the retrieval of all grants affecting the current object for the current user.
Grants given to a user or group are stored both in the user or group object and in the object's the grants affect. GetValidGrants() only checks the grants stored in the user or group objects however. The rest is used for management of grants only. (This is less than optimal, but as grants checking is such an integral part of Ariadne, we think that the speed gains outweigh the disadvantage of duplicate storage.)
GetValidGrants() first checks the grants listed in the user object. It will search for grants defined at the path which corresponds best with the path of the current object, it's nearest parent. e.g.:
If grants are defined at /dir/a/, /dir/a/b/ and /dir/c/, and the current object has path /dir/a/b/d/, then the valid user grants for this object are the grants defined at /dir/a/b/.
Then GetValidGrants() lists the groups of which the user is a member and adds all grants defined for these groups between the path where the user grants were defined and the current object. e.g.
If group grants are defined at /dir/a/, /dir/a/b/ and /dir/a/b/d/, then only the grants in the last object (incidentily also the current object) are merged with the user grants.
5.2. $this->CheckConfig($arCallFunction, $arCallArgs)
The first thing CheckConfig() does is get all configuration data from it's parents. This data is cached, for each path, in $ARConfig->cache. It includes configuration data about available and default languages, object templates, caching and psite's and their root URL's. The configuration data is collected via the template 'system.get.config.phtml', defined in lib/templates/pobject/.
Using this data CheckConfig() set's the correct values for $this->nls and $this->nlsdata, if no language was specifically set. Otherwise it checks the object to see if the requested language is available in the object. If not, it will show a language select dialog with the languages that are available, using the nls widget at lib/widgets/nls/languageselect.phtml.
Then, if caching is enabled and the current user is 'public', the output buffer is started. Later, after the entire page has been generated and the loader finishes up, the loader will see that the output buffer is filled and save the content of the buffer in the public cache.
The next step is to check for object templates. First the current object is checked for 'local' object templates. These templates are only available for the current object, they are not passed on to children of the object at which they are defined. These templates are defined in $this->data->pinp[]. If the requested template is not defined there, the templates defined at the parents are checked. These templates, defined as 'default' in the template editor, are available in $this->data->templates, in the object at which they are defined. The system.get.config.phtml template makes sure that each template so defined, is available in the {$ARConfig->cache[$this->path]}->templates (or $config->templates, which is a shortcut) list for each object they affect.
CheckConfig() first checks this list with the class of the current object, if not found, it will then search the list again with the class from which the current class inherited, untill the class is pobject or the requested template is found. If no template is found, CheckConfig() finishes and returns true. If an object template is found however, then the (compiled to PHP) template is included and CheckConfig() returns false.
6. Finishing touches.
Templates generate all the output, but that doesn't mean that processing stops there. The loader still has a few tasks left.
First the loader checks whether any Ariadne objects have actually been found. The $store->call() function keeps a counter of all objects accessed. If this counter is still 0 when the loader checks it, the loader searches for the best fitting path that does exist and calls the template 'user.notfound.html' on it. This allows you to redefine this template for a single site or directory in Ariadne so that it will fit in your layout, or give some directory specific information.
Then the store is closed, and any information saved in $ARCurrent->session (e.g. using putsessionvar() in object templates) stored in the session store.
Finally it checks whether a cache image needs to be written to disk. As mentioned before, the output buffer feature of PHP is used to get the cache image. The loader gets the contents from the buffer and writes them to standard output (the browser). Then it saves these contents as a cache image, with the requested URL as the filename, on disk. It uses cache information that CheckConfig() stored in $ARCurrent->cachetime to calculate the 'freshness' of the image (when the cache expires).
That's it, I hope this document is helpfull to you, at least it should make the debug output a little easier to understand when programming in Ariadne. If you need any more explanation, you can always try our developer newsgroup muze.ariadne.dev

