Type-safety is a way to filter function arguments in PHP, which is a type-juggling-supporting programming language. Type juggling is useful when it comes to internal processing, but it adds an additional level of complexity and insecurity when attempting to validate user input. The type-safety mechanism is a system designed to assure that user-specified arguments are of the desired types and assist in validating that.
Exposition
Type-safe functions, in Rabbit, are the functions which are directly exposed to the user. This includes:Unlike normal functions, type-safe functions cannot (and should not) be directly called from within your PHP code. They are invoked by the user, either by navigating to a URL that is routed to a Master Element, by invoking an AJAX call to a Unit, or by submitting a POST request to an Action.
Definition
Type-safe functions are defined as normal PHP functions. They can take zero or more arguments. The naming scheme for your function names should follow the relevant rules defined by Rabbit depending on the context (see the sections for Elements, Units, and Actions).Order and Naming
Unlike normal functions, in type-safe functions, the order of the arguments does not matter. No matter the order in which the user specifies the parameters, Rabbit will reorder the parameters accordingly to match your function definition. However, the names of your arguments are significant. These are used for the reordering. The user specifies the names/values association during calling; for example, by specifying GET variables for Master Elements, POST variables for Actions, or Coala arguments for Units. If the user passes a variable which does not match an argument in your function, it is simply ignored.Optional arguments
Although not clearly visible by the function definitions, all arguments in a type-safe function are optional. This is not specified by the traditional way of specifying a default value, however; any default values specified in your function definition will simply be ignored. Since the user can choose whether to specify an argument or not, based on exposition, you have to explicitly check if an argument was specified.When an argument is not specified, perhaps contradicting the expected behavior, the default value of its type is used (instead of null or some other value).
Type hinting
All function arguments in a type-safe function must be type-hinted. This is done by specifying a type before the argument name. The types used can be one of the Rabbit basic or extended types, or a custom type. For example, if we want to create a function with two arguments of the type "tString" (explained below), named a and b respectively, we would write the following:<?php function MyFunction( tString $a, tString $b ) { // function body } ?>
Basic Types
There are four basic Rabbit types. These can be used for argument type-hinting in type-safe functions.tInteger
This type is used for integer numbers. It casts to a PHP integer. Its default value is 0.tBoolean
This type is used for a true/false condition. It casts to a PHP boolean. Its default value is false.tFloat
This type is used for float numbers. It casts to a PHP float. Its default value is 0.tString
This type is used for strings. It casts to a PHP string. Its default value is the empty string "".Extended Types
Extended types allow additional functionality and can be used for type-hinting.tCoalaPointer
This type is used for references to external Javascript objects. It cannot be cast. Whether it was specified or not can be checked using the ->Exists() method on the variable. More details about this type and how it can be used to exchange references between the client and the server can be found at Coala.Casting
Before the value of a type-safe function argument can be used, it must be cast to a PHP-compatible type. This is done by taking the result value of the method ->Get(), applied on the type variable. Take the following example:<?php function MyFunction( tString $name, tInteger $age ) { $name = $name->Get(); // cast to PHP string $age = $age->Get(); // cast to PHP integer // all subsequent operations can now be performed normally if ( $age < 2 ) { return false; } $age -= 2; if ( strpos( $name, ' ' ) ) { $names = explode( ' ', $name ); $name = $names[ 0 ]; // take first name only } Store( $name, $age ); } ?>
Optimization Concerns
One could argue that type-hinting and especially type casting to native PHP variable types is a slow operation. This is very true, as Rabbit is using PHP objects to emulate scalar types. However, this is only used at the step of data validation, and not for normal function calls. This means that a type-safe call will only occur once per request, as only one function needs to be called that way each time (with the exception of grouped Coala calls, in which case there is one type-safe call per grouped call). Keeping that in mind, type-safe functions should not be used as normal functions, and this is one of the reasons they, in fact, are not. In conclusion, the overhead for only one function call at the moment of request parsing is tiny compared to the benefits of type casting.Custom Types
Custom types should be defined in libs/project or in libraries imported in Project_Construct. To define a custom type, create a new class extending tBaseType and name it after the type you want to build. You must specify a constructor taking one and only parameter named $value. When the class is instantiated, the value passed to your constructor will be the PHP string passed by the user, or false if the user did not specify a value (you can use the identitical operator for distinguishing false from the empty string). Your constructor should set the member variable $mValue to the value you want returned by the ->Get() statement. If you wish, you can specify a __toString method so that your variable can be echo'ed directly without casting. If you want to disable or customize casting, you can overload the default Get() method which simply returns the value of the $mValue member variable. If convenient, you can extend one of the existing basic, extended, or custom types. Let's, for example, extend the tInteger type to eliminate our types furthermore to positive integers and create tPositiveInteger:<?php class tPositiveInteger extends tInteger { public function tPositiveInteger( $value ) { global $water; // unshadow debugger // allow the basic type integer to perform basic filtering // and assignment to $mValue $this->tInteger( $value ); // set a default value of "1" instead of "0" if ( $value == false ) { // if nothing was passed $this->mValue = 1; } // filter the value additionally excluding non-positive numbers if ( $this->mValue <= 0 ) { // issue a notice $water->Notice( 'tPositiveInteger instantiated with non-positive parameter' ); // change to 1 for non-positive input $this->mValue = 1; } } } ?>