Variables |
Variables are used to store different types of data within a script. In SiphonScript, local variables do not need to be declared prior to use - just assign a value to a variable and it is automatically created (see: Scope section below). When using WebSiphon, any URL, POST arguments or cookies are automatically converted into variables which are useable in a script. This is very helpful, whereas any regular HTML form which points to a WebSiphon template as an action will receive the user-supplied form data as SiphonScript variables. In addition, there are a number of built-in variables that contain standard CGI parameters and other environment data such as file paths.
The examples below show several different variable assignments:
Examples:
message = "Hello world!"; the_beast = 666; weekdays = ["Monday" "Tuesday" "Wednesday" "Thursday" "Friday"];Naming Variables
In SiphonScript, all variables are case sensitive. There is no set limit to the length of a variable name, but it is generally good practice to keep identifier names as short as possible.NOTE: Variable names in SiphonScript must start with a letter, but can contain numbers as long as one isn't the first character:
// valid variable names - will compile a1 = 1; a2 = 2; a2b = 3; // invalid variable names - will not compile 1a = 1; 2a = 2; 2a1b = 3;Also, SiphonScript variables may not contain the "." character (a dot/period).
// invalid variable names - will not compile foo.bar = 2; sunshine.1 = 3;See the language keywords page for a list of reserved words and the list of built-in variables for variable names that are reserved. These should not be used for variables or user-defined function names (see: functions).
Using Variables in HTML
in many cases, you will want to quickly insert the value of a varible into your HTML. In some other languages this is called variable substitution or When referring to the value that a variable contains within an HTML block, you must enclose the variable name in braces. So, in order to display the value of a variable "cheese_whiz" in your HTML you would insert "{cheese_whiz}" within your text. Note that within a SiphonScript block it is not necessary to use braces when referring to a variable's contents, using them will cause an "unexpected character found" error when trying to execute the template.Example Template:
<HTML> <HEAD> <TITLE>{message}</TITLE> </HEAD> <BODY> <H1>{message}</H1> <P> The sign of the beast is {the_beast}.</P> <P> The days of a work week are: {weekdays}</P> </BODY> </HTML>Resulting HTML:
<HTML> <HEAD> <TITLE>Hello world!</TITLE> </HEAD> <BODY> <H1>Hello world!</H1> <P> The sign of the beast is 666.</P> <P> The days of a work week are: Monday Tuesday Wednesday Thursday Friday</P> </BODY> </HTML>Remember, insertion tokens (braces) used in an HTML block are equivalent to using a print command while in a SiphonScript block. For example,
{ currentDate() }
is syntactically equivalent to<< print currentDate(); >>
Optional insertion tokens and escape notation
With WebSiphon v2.0.2, <?= and ?> are now synonymous with { and }. The optional escape sequences can be selected via the new #option directive.<< #option sgml-escape << template_name: <?=template_name?> >> #option brace-escape << template_name: {template_name} >> >>You can switch between the regular and optional insertion tokens anywhere in your template. The #option directive will stay in effect until it encounters another #option directive or until the end of the template. After the script executes, the #option directive reverts to the default state of using the { and } insertion tokens. If you need to use the optional insertion tokens in your entire template, simply place the #option directive at the very top of your template and you;re all set.
GET and POST arguments
This next examples show how variables are automatically created when passed to a template from URL or POST arguments.Example:
// assume the template is being called with the arguments // template.t?a=1&b=2&c=Hello%20World! print a; >> 1 print b; >> 2 print c; >> Hello WorldIf you have multiple arguments with the same name, then those arguments are turned into a list:
Example:
// with template.t?a=1&a=2&a=3&b=4 printList (a); >> [1, 2, 3] print b; >> 4This works the same with POST arguments - anything posted from a form. Note how mulitple items from a series of checkboxes with the same name are converted to a list. Also note that this example uses the built-in variable "template_name" to direct the form action to itself for processing.
Example Form:
Example Form as HTML (note the form item values):
<FORM ACTION="{template_name}" METHOD="POST"> textItem: <input type="text" name="textItem" value="this is some text"><br> <input type="checkbox" name="checkboxItem" value="1" checked> Checkbox 1<br> <input type="checkbox" name="checkboxItem" value="2"> Checkbox 2<br> <input type="checkbox" name="checkboxItem" value="3" checked> Checkbox 3<br> <input type="checkbox" name="checkboxItem" value="4" checked> Checkbox 4<br> <select name="selectItem"> <option value="selectme">Select me! <option value="selectme2" selected>Selection Number 2! </select><br> <input type="submit" VALUE="Submit Form"> </FORM>Resulting values after submitting form (with the supplied values):
print textItem; >> this is some text printList (checkboxItem); >> [1, 3, 4] print selectItem; >> Selection Number 2!Note: If a form field name or a URL argument name includes an invalid character, WebSiphon will convert that character to an underscore (See: Naming Variables above for invalid characters).
Cookies
If you've been skipping ahead in the documentation, you may have noticed the functions setCookie() and removeCookie() but no function for getting the value of a cookie. This is because cookies are another kind of automatic variable which is sent by the browser on each request (if a cookie has already been set).// assume you set a cookie like this setCookie ( "myCookie", "Hello, my name is Slim Shady"); // later if that same browser accesses other // scripts on your site, the variable myCookie will be available. print myCookie; >> Hello, my name is Slim ShadyYou may also want to detect if a cookie is present before doing anything. You can use the defined() function to see if the cookie is present. If so, defined() will return "true".
if defined(myCookie) print myCookie; end if;Global Variables
Global variables are persistant, meaning that they retain their values between both script/template executions and WebSiphon sessions. They are stored in the Global Data file, and saved to disk every 5 minutes and when WebSiphon quits. They are automatically loaded during WebSiphon's initialization and kept in memory. If you want to make absolutely sure globals are safely saved to disk, use the saveGlobals() function.To declare a variable as global, use the global keyword as shown in the examples below.
Examples:
// This statement will declare the variable "my_global_var" as // global. The function defined(my_global_var) will return false // if my_global_var hasn't actually been used yet. global my_global_var; // This statement will declare a global variable "x" and will // always set its value to 20. global x = 20; // Here, we are declaring three different global variables, "i", // "j", and "k". The global variable "k" will always be set to // 10 as its value. global i, j, k=10;Once a variable is declared as being global, it is used just like any other variable in SiphonScript. The only difference is that any value the global holds will be available to every template and its value will be saved even if WebSiphon is quit and restart.
Because global variables are available to all templates, it is important to understand that all globals must have a unique name associated with it or a conflict will occur. For instance, if two different templates each use a global named "counter" both will be modifying the value of the same variable.
One way to prevent variable identifier conflicts is to use the local keyword to specifically designate a variable as local to the current template, even if there is a global variable defined with the same name (see the section below on Scope for more information). This is particularly helpful when creating function libraries to prevent any potential problems.
Example:
<< global a=10; function hello() local a=20; print "Hello! a is equal to " & a; end function; print "The global a is still equal to " & a; >>When a template with the above script in it is accessed, the first value printed is "Hello! a is equal to 20" and the second value printed is "The global a is still equal to 10". Since we explicity declare the variable a as local to the hello() function, it does not affect the value of the global variable a.
For further discussion on how functions work with SiphonScript, reference the SiphonScript Functions section of this documentation.
Scope |
A scope is essentially the space where variables exist within a function. Every script starts with one scope, the 'main' or 'top' scope. Think of it as every time your script runs, it is within a function block called 'main'. Any subroutines (functions) also have their own individual scopes. For example:
function foo(x) appendLog(x); end function;creates another scope. This scope becomes the current scope whenever you call foo().
In SiphonScript you have local and global variables. These terms refer to the scope of the variable. The difference between global and local variables is that when a particular scope completes execution, the local variables of that scope are discarded. We'll get into more detail below.
In some cases you must explicitly declare the variables you are working with. In other cases you do not. The first case are 'automatic variables' which are automatically declared for you. These can be thought of as being implicitly declared:
- Variables handed to the script from CGI parameters, POST args, GET args, cookie values, etc. These are created by WebSiphon before your script starts executing.
- Variables created by simply referring to them. eg:
x = 3;creates a variable called 'x', and assigns 3 to it. In these cases there is no need to declare the variable explicitly.
If you did want to explicitly declare a local variable you would use the syntax (see below for more):
local foop;Or you can also assign a value to that variable at that time:
local foop = "Hi There";The next case are Global variables, which exist across all scripts, but cannot be used until you declare them as globals. So, if you have a global variable called 'foo', you must include a line in your script to declare the variable:
global z;You can also assign something to it at that time, using the deviant syntax
global z = 3;The local keyword is used to explicity declare a local variable - i.e. to tell the compiler that a particular variable is part of the current scope. This is where it may get a little confusing. Take this example:
<< // this is the main scope bar = 3; function foo(x) local bar; bar = x + 1; appendLog(bar); end function; foo(bar); appendLog(bar); >>Now, what is going to happen? In theory, the script sets up a variable called 'bar' in the main scope, and sets it to 3. Then it calls foo() with the variable bar. Inside foo, another variable, also called 'bar', is created in the current scope (which is now the subroutine's scope, not the main scope) and sets it to whatever was handed in (x) plus one. Now, foo::bar (to steal C++'s scope syntax) is set to 4, but main::bar is still 3. These are two separate variables. The appendLog will dutifully print '4' and return. Now, back in the main scope, bar is still set to 3 (remember, the two 'bar's are different) and the second appendLog should print '3' to the log.
But, you may ask, what happens if we take out the local bar; declaration. What happens?
Indeed, since there's nothing to tell the compiler that the foo::bar variable is different than the main::bar variable, they become the same thing. (Sub-scopes inherit variables from their superscopes) and thus, the two bar's are one and the same. The two appendLog()s will both print '4', since foo() now has the side effect of incrementing the One, True bar.
This is what 'local' is good for. Making sure that your subroutines are always safe from stomping on variables of the same name in their enclosing scopes.'Automatic' variables really aren't a particular type of variable. Variables get created when referenced (or by CGI params), and follow the basic scope-inclusing rules. They belong to a particular scope, but you can force a variable to belong to a particular scope (or create a new variable in a subscope to use instead of the same-named variable from the enclosing scope) by using the 'local' keyword.
In general use, you'll find very few times when you'll actually have to use the 'local' keyword, or even explicitly declare variables before you're ready to use them. However, good coders will protect their subroutines from having 'side effects', particularly if those subroutines will be used in other code, by using 'local' to ensure that the subroutine only uses values passed in, operates on local variables, and returns data via the 'return' keyword.
- Variables are available in the scope in which they are defined, and in any deeper scopes (subscopes inherit variables from their superscopes). However they must be explicitly defined to be visible in deeper scopes.
- Functions defined within functions are only available to the scope where is is defined, not the outer scope
// eg: function b() function c() appendLog(foo); end function; c(); end function; c(); // This won't work, because c() is not defined in the outer scope, // only within b()'s scope.- Variables explicitly declared in an enclosing scope are visible (and modifyable) in the sub-scope. But variables implicitly declared (by being url arguments, for example) are not, since the compiler doesn't know about them (unless you declare them as local in the basic scope).
local x=1; a(); print "The value of x is now "&x; function a() b(); function b() x = x + 1; end function; end function; // this prints -- "The value of x is now 2"- Parameter values can be changed, but changes remain local to the scope of the subroutine -except- if the parameter value is a list. Lists can be modified within functions and will retain those changes when the subroutine returns.
function foo(list) list'2 = list'2 + 1; appendList(list, 4); end function; tList = [1 2 3]; foo(tList); appendLog($printList(tList)); // returns [ 1, 3, 3, 4]Note: Typically when using a function, any variables are copied into the function. The list datatype is an exception and the original variable is used. This is done so lists will be as fast as possible when executing. If you want to have a copy of a list passed to a function to work around this behavior, use deepCopy(). See below for more information on the list datatype.Scope Tricks
Remember that parameters passed to your script are automatic variables; they aren't visible inside functions because the compiler does not know they exist. If you need to use them inside functions, you can either explicitly pass them to the function, or you can declare them as local in the main scope of the template (at which point they would be seen by the compiler and thus inherited by any subscopes).If you do this, then when WebSiphon executes your script, it'll get the parameters from the GET or POST arguments, see that there is a local variable of the same name, and set the local variable (the same goes for Cookies). Be careful though - declare the variable without setting it, like this:
local derf; // the variable derf is now defined, but not set to a valueAlso, be aware that because you are defining the variable, the defined() function, which is often used to determine if a parameter was passed to the script, will always return true. At this point you would need to check the value of the variable to see if it is null or not.
If you need to share a variable between two functions, you can do this by declaring a local variable in an enclosing scope. But that variable will only persist as long as the enclosing scope, so if you want it available for the full execution of the script, declare it in the main scope.
Rule of Thumb
If you want a variable to be visible in sub-scopes, explicitly declare it as a local or global.If you want a script parameter (from url or POST arguments) to be visible in sub-scopes, explicitly declare it at the top of your script.