]> logo.pngLogo Image SPLCMS is a simple content management system for generating static HTML pages. <p/> The user can define his own data objects and can write his own XSLT file for generating the HTML code of the page. Creating pages with SPLCMS is a simple walk-over if you know already XML and XSLT. <p/> <a href="index.html">Read more..</a> This is the SPLCMS Example page. Have a look at the real documentation! The texts here are just fillers and placeholders and will never be updated.. A short introduction into SPLCMS can be found <a href="index.html">here</a>. More information on SPL can be found on the <a href="http://www.clifford.at/">SPL Homepage</a>. This is the SPLCMS example page. It contains a short SPLCMS introduction and some text snippets from the SPL documentation as additional fillers. Have fun and happy hacking!SPLCMS is a simple Content Management System written in SPL. It is using <a href="http://en.wikipedia.org/wiki/XML">XML</a> for storing the actual content and <a href="http://en.wikipedia.org/wiki/XSLT">XSLT</a> for rendering static HTML files. <p/> The administrator can define his own objects for representing the content objects. This object definitions are stored in the <tt>classes.xml</tt> file. Users can then create and modify such content objects using the editor backend (<tt>splcms.webspl</tt>). <p/> The actual HTML files are rendered using the <tt>render.xsl</tt> XSLT file. This XSLT is processed for every output HTML file. <p/> The rendering process is triggered by clicking on the "(Re-)Render HTML Files" button in the editor backend or by manually executing the <tt>render.spl</tt> script. <p/> This example page is ment as starting point for you to create your own object classes and rendering scripts and finally a webpage.The <tt>classes.xml</tt> file contains the descriptions of the content object classes. Each content object class has a name, a list of allowed child classes, a short definition for automatically creating object titles in the editing backend and a list of parameters. This parameters are the actual data tuples assoziated with an object. <p/> Each object is represented as an XML node in the <tt>data.xml</tt> file. The class name is used as XML element name in the file. <p/> Each parameter again has a name, a type and some information for the editing backend how a parameter should be displayed in the backend. The type is eigther <tt>attribute</tt> or <tt>element</tt>. The <tt>attribute</tt> parameters are stored directly in the object XML element as XML attributes, the <tt>element</tt> parameters are stored as child elements. <p/> Have a look at the example <tt>classes.xml</tt> file. It is pretty much self explainatory.The XSLT renderer is implemented in the seperate script file <tt>render.spl</tt>. This makes it easier to make local modification to the rendering process. <p/> This script creates a list of all &lt;page&gt; elements in the <tt>data.xml</tt> file and is processing <tt>render.xsl</tt> for each &lt;page&gt; element. The output of the XSLT is then written to an HTML file named after the name= attribute of the &lt;page&gt; element. So, if you would like to use <tt>render.spl</tt> unmodified you need to specify a <tt>page</tt> class in your <tt>classes.xml</tt> and this class needs to have a <tt>name</tt> parameter of the <tt>attribute</tt> type. The HTML files are stored in the <tt>html/</tt> directory. <p/> The page name is passed as XSLT parameter <tt>page</tt> to the XSLT. <p/> The files from the <tt>static/</tt> directory (usually images and the <a href="http://en.wikipedia.org/wiki/Cascading_Style_Sheets">CSS</a> file) is automatically symlinked to the <tt>html/</tt> directory. <p/> Additional logic (such as fetching external <a href="http://en.wikipedia.org/wiki/RSS_(file_format)">RSS</a> feeds) can be easily added to the <tt>render.spl</tt> script. This sections contain the first few chapters of the SPL Language Reference Manual. Please remember that this is just the SPLCMS example data and so the information here won't be updated. <p/> Please download the recent version of the SPL Language Reference Manual from the <a href="http://www.clifford.at/spl/">SPL Homepage</a> if you want to learn SPL.. <pre> SPL is a C-like language (such as Java or PHP). Constructs such as "if" statements and "while" loops work as in those other languages. I am not explaining the basic language constructs derived from C in this manual. Please have a look at http://en.wikipedia.org/wiki/C_syntax if you have no idea of how the basic syntax of C (and other C-like languages) looks like. SPL is entirely typeless. So don't wonder how you can define a variable as "int", "float" or "array of objects". Just use the variables as "int", "float" or "array of objects" and they will be. However - the operators have types: + - * / % ** Integer addition, subtraction, multiply, divide, modulo and power operators. .+ .- .* ./ .% .** Floating point addition, subtraction, multiply, divide, modulo and power operators. == != <= < > >= Integer comparison .== .!= .<= .< .> .>= Floating point comparison ~== ~!= ~<= ~< ~> ~>= String comparison ! || && Logical NOT, OR and AND (the keywords 'not', 'or' and 'and' are also available and do exactly the same thing) ~ String concatenation SPL also has support for the usual combined operation-assign operators such as '+=' (or '~=' for appending to a string). Also the famous '++' and '--' operators (pre- and postfix) do exist in SPL. Have a look at the 'hanoi.spl' program in this directory. It is a nice example for a simple SPL program. </pre> <pre> The keyword 'debug' may be used to create debug output. The debug message itself is simply passed as argument. Since this is a language keyword (and not a function), no parentheses are required for the argument: debug "Hello World!"; The way the debug messages are diplayed depends on the host application embedding the SPL runtime. They may be printed to the console, displayed in popup windows, written to logfiles or simply be ignored. The keyword 'warning' works the same way as 'debug', but also includes a stack backtrace in the messege and the keyword 'panic' also terminates the program execution. </pre> <pre> As said already, SPL is entirely typeless. There is just the abstract construct of variables. A variable can represent everything, a simple scalar value, an array or hash, a function or an object. Variables are declared using the "var" keyword: var foobar; Values can be assign using the '=' operator: foobar = 42; You can also use the variable as array or hash by appending '[ .. ]' to the variable name (as you might be already used from other languages): foorbar[42] = 23; But more about that in the section about arrays and hashes. The keyword 'declared' can be used to check if a variable has been declared. The keyword 'defined' can be used to check if a declared variable has a value assigned. The keyword 'undef' represents an undefined value and can e.g. be used to remove the value of a variable: var x; if (declared x) debug "Variable x has been declared."; if (defined x) debug "This is never reached."; x = 42; if (defined x) debug "Now x is declared and defined."; x = undef; if (not defined x) debug "Now x is not defined again."; Using an undeclared variable (outside a 'declared' test) causes a runtime error. </pre> <pre> In SPL, there is no difference between arrays, hashes and other complex data structures like objects. A variable simply may have child variables. The children of a variable may be accessed using the dot-operator or brackets: foo.bar = 42; foo["bar"] = 42; the only difference between the two methods is that the name of the child node is limited to the lexical rules for identifiers (the regular expression /[a-zA-Z_][a-zA-Z0-9_]*/) and that children must be explicitly defined with the 'var' keyword when accessed using the dot operator. It is possible to iterate over all children of a variable with the 'foreach' statement: forach index (array) debug array[index]; where the 'index' variable is automatically defined by the foreach statement and only valid for the loop body. A foreach loop always returns the elements in the same order in which they have been created (except for elements added using the 'unshift' statement). The foreach loop usually iterates over the indexes of an array. It is also possible to iterate over the values using the foreach[] loop. So this is identical the the last example: foreach[] value (array) debug value; Arrays may be defined in-place using square brackets. There are two methods of doing so: With automatically assigned keys and with explicitly defined ones. When defining keys, the '=>' operator can be used when the key is specified as value and the ':' operator when the key is specified like a variable name. It is also possible to mix those two methods: var a1 = [ "a", "b", "c" ]; var a2 = [ "x" => "a", "y" => "b", z: "c" ]; var a3 = [ x: "a", 100 => "b", "c" ]; has the same effect as: var a1, a2, a3; a1[0] = "a"; a1[1] = "b"; a1[2] = "c"; a2["x"] = "a"; a2["y"] = "b"; a2["z"] = "c"; a3["x"] = "a"; a3[100] = "b"; a3[101] = "c"; So, when adding elements to an array without explicitly defining a key, the highest numeric key defined in the array will be incremented by one to built the key for the new element. There are two other instructions which can be used to add elements to an array: push array, 23; unshift array, 42; The push instruction adds an element to the end of the array, unshift to the beginning. The difference between these instructions is in the position the new element will have in the array, not in the key which is assigned to it. The key is the next integer value in both cases. There are also instructions to remove the first and last entry from an array: var last = pop array; var first = shift array; But it is also possible to simply remove elements by using the key: delete array[42]; Besides the 'foreach' loop, it also is possible to manually 'walk through' an array using the 'next' and 'prev' instructions: var a = array ( 3 => 'x', 5 => 'y', 7 => 'z' ); var three = next a, undef; var five = prev a, 7; var seven = next a, 5; They return the next or previous index value in the array passed as 1st parameter, relative to the index passed as 2nd parameter. If the 2nd parameter is undef, 'next' returns the first and 'prev' the last index. If there is no next or previous element, undef is returned. Additional functions for working with arrays (such as sorting) are provided by the "array" module which is described in SPLDOC (there are separate sections about SPL Modules and SPLDOC later in this document). </pre> <pre> Functions may be declared with the "function" keyword, such as: function hypot(x, y) { return ( x.**2 .+ y.**2 ) .** (1./2); } And may be called by simply using the function name and passing the arguments in parentheses: var h = hypot(5, 12); Note that the parentheses are required when calling functions in SPL. However, there are some language keywords with function-like behavior. But they don't have parentheses around their arguments because they are not functions but compiler keywords: debug, warning, panic, delete, import, load, new, return, defined, declared, pop, shift, push, unshift, throw Note that functions can be copied as any other variable. So it is e.g. possible to simply pass a callback function as parameter to another function without any syntactical black magic. It is also possible to define anonymous functions. In that case the function definition evaluates to the function pointer and can e.g. be assigned to a variable. The syntax is the same as for normal function declarations, but the function name is skipped: var hypot = function(x, y) { return ( x.**2 .+ y.**2 ) .** (1./2); } This is especially useful when functions need to be passed as arguments to other functions: array_sort_by_values(myarray, function(a,b) { return a < b; }); </pre><pre> There also is support for 'vaargs' like argument passing in SPL. The last argument in a function declaration may be prefixed with an '@' character. In that case the variable representing this argument will be an array of all remaining arguments. If an '@' character is put in front of a function argument when calling a function, that argument is interpreted as an array and an argument for each array element will be inserted. E.g.: function foo(@args) { foreach i (args) debug args[i]; } function bar(@args) { foo("one", @args, "four"); } bar("two", "three"); This is useful for writing wrapper functions and functions with a variable number of arguments. </pre><pre> SPL also supports named arguments (aka options). With this arguments, not the position in the function call but the assigned name identifies an argument. All named parameters are assigned to a hash which is declared with a '%' prefix in the function prototype. The '%' prefix also may be used when calling a function to pass all elements of a hash as named arguments. E.g.: function foo(%args) { foreach i (args) debug "$i -> ${args[i]}"; } function bar(%args) { foo(one: 1, %args, four: 4); } bar(two: 2, three: 3); This is e.g. useful for function arguments which are rarely used. When a named option is specified multiple times, the most left specification is used. That way it is possible to pass a hash with default values as last parameter using the '%' prefix. </pre> <pre> If you have no idea wht object oriented programming is, read http://en.wikipedia.org/wiki/Object_Oriented_Programming first. SPL does not really know about the difference between classes and objects. If only one instance of a class/object is needed, it is possible to simply use the name defined with the 'object' keyword. (But if you do so, no constructor will be executed.) Because of this, we don't speak about classes and objects in SPL, but of objects and instances of objects. Objects can be defined in SPL like this: object Foo { var counter; method increment_counter() { return counter++; { method init(start_value) { counter = start_value; return this; } } The method 'init' has a special meaning: It is the constructor. There are no destructors in SPL, but it is common to create a method named 'destroy' for the job and call it manually when the object isn't needed anymore. The constructor must return a self pointer. The keyword 'this' can be used to create such a pointer. Thus the "return this;" in the example above. If the new object is derived from another one, the parent object is specified right after the object name in the object definition: object Bar Foo { method decrement_counter() { return counter--; } } Objects may be instantiated using the 'new' operator. When any arguments are specified when instantiating an object, they are passed through to the object constructor as they are: var mycounter = new Foo(42); SPL does support nested functions as well as nested objects. So it is possible to define 'local objects' in your functions. It is also possible to derive one object from more than one parent. This can be done using the 'import' statement: object Foobar Foo { import Bar; [...] } But the parent objects which are 'imported' neither show up in the object derivation path of the object, nor in its reflection string. When an object or one of its instances is used as scalar variable, they evaluate to their reflection strings. E.g.: object A { } object B A { } var x = new B(); debug A; // prints: SPL Debug: A debug B; // prints: SPL Debug: A | B debug x; // prints: SPL Debug: [ A | B ] Objects and instances are automatically created as references. So they can be passed as arguments and being returned from functions without implicitly being copied by those operations. If a method is overloaded in a child object and the implementation from the parent object needs to be called in the child object, this can be done using the context dispatching operator '*': object A { method foo() { debug "Now in foo from A."; } } object B A { method foo() { debug "Now in foo from B."; *A.foo(); } } Without the '*', A.foo() would be called in the context of A and not in the context of B (or the currently active instance of B). The keyword 'static' can be used to declare static object variables. A static object variable is shared between all objects in the derivation path and all instances of this objects. The declaration works exactly the same way as for declaring normal variables in objects, just that the keyword 'static' is used instead of the keyword 'var'. There is nothing like 'private functions' in SPL. All methods (and variables) are public. If some are intended for internal use only, they must be protected by choosing a name which avoids collisions. There is a separate section on the recommended naming conventions later in this document. </pre><pre> There is no operator overloading support in SPL. Instead, there are object operators (as they are seperate integer and floating point operators too). Whenever such an object operator is used, an "operator_*" method is called in the left operand and both operands are passed as parameters. The method name is different for each object operator: (+) and (+)= are calling the method operator_add(a, b) (-) and (-)= are calling the method operator_sub(a, b) (*) and (*)= are calling the method operator_mul(a, b) (/) and (/)= are calling the method operator_div(a, b) (%) and (%)= are calling the method operator_mod(a, b) (**) and (**)= are calling the method operator_pow(a, b) (<) is calling the method operator_lt(a, b) (>) is calling the method operator_gt(a, b) (<=) is calling the method operator_le(a, b) (>=) is calling the method operator_ge(a, b) (==) is calling the method operator_eq(a, b) (!=) is calling the method operator_ne(a, b) The operator precedence is the same as for the normal integer and floating point operators. </pre><pre> SPL has an exception handling mechanism for error-reporting and -handling. The exceptions themselfs are objects. In fact, every object could be used as an exception. But it is recommended to only use objects as exceptions which have been designed for this purpose. Usually the names of such exception objects end with the "Ex" postfix. E.g. all exceptions thrown by the SQL modules are of the type "SqlEx". Exceptions may be thrown using the keyword 'throw' and are catched in 'try' blocks. Here is a small example: object MyEx { } object MySpecialEx MyEx { } try (x) { throw new MySpecialEx(); debug "** this line is never reached **"; catch MyEx: debug "Got a 'MyEx' exception. The backtrace is:\n" ~ x.backtrace; } If no 'catch' rule matches the exception object or any of its parent objects, a runtime error is printed and the execution of the program is terminated. The 'throw' instruction automatically adds the 'backtrace' variable to the exception object. This variable contains a human-readable backtrace of the current task. If the exception object has a 'description' variable, its text value will be printed as part of the runtime error for uncatched exceptions. It is considered good coding style to only use exceptions for error handling. </pre> This sections contain some chapters of the SPL Modules Reference Manual. Please remember that this is just the SPLCMS example data and so the information here won't be updated. <p/> Please download the recent version of the SPL Language Reference Manual from the <a href="http://www.clifford.at/spl/">SPL Homepage</a> if you want to learn SPL.. This module extends the built-in functionality of SPL for working with arrays. Please also have a look at the Language Reference Manual for a description of the built-in operators provided natively by SPL. This function re-indexes an array. That means, the order of the elements is kept as they are, but the keys are newly assigned (using integer, starting with 0). This function switches two elements of an array. The keys and values are nod modified by this - only the order of the elements in the array. <pre> This function sorts the passed array ussing the passed order function. The order function is called whenever this function wants to compare two elements. The key of the 1st element is passed as 1st parameter to the order_function. The key of the 2nd element as 2nd parameter. The order function must return 1 if the elements are in the wrong order. Example: var a = [ 5 => 'a', '3' => 'b', '8' => 'c' ]; array_sort_by_keys(a, function(a,b) { return a > b; }); foreach i (a) debug "$i --> ${a[i]}"; This creates the output: SPL Debug: 3 --> b SPL Debug: 5 --> a SPL Debug: 8 --> c The function is implementing a trivial bubblesort right now. So there is room for improvement... </pre> This function is pretty simmilar to array_sort_by_keys. The only difference is that it passes references to the elements which should be compared to the order function. This module provides a simple API for reading and writing files. This function reads the specified filename and returns the file content. <p/> If the file is not UTF-8 encoded, the encoding must be specified with a 2nd parameter. Valid encodings are the same as for the '#encoding' compiler pragma (see 'SPL Language Reference' for a list). <p/> A FileEx exception is thrown on I/O errors. This function writes the specified content to the specified filename. The file is created if neccassary and overwritten if it is already there. <p/> If the file should not be UTF-8 encoded, the encoding must be specified with a 3nd parameter. Valid encodings are the same as for the '#encoding' compiler pragma (see 'SPL Language Reference' for a list). <p/> A FileEx exception is thrown on I/O errors. This function removes the specified file.<br/> A FileEx exception is thrown on I/O errors. This function returns the list of files in the specified directory.<br/> A FileEx exception is thrown on I/O errors. An instance of this object is thrown on file I/O errors.