This document contains all of the class and API documentation, plus selected general documentation for Flourish r899. The Class Documentation and General Documentation links in the header provide for quick access to the table of contents. Any links preceeded by a small graphical arrow will leave this document.
The fActiveRecord class is an abstract class that follows the active record pattern. It provides an object-oriented interface for creating, retrieving, storing and deleting a single row (or record) in a database. All interaction with the database is done automatically without the need to write any SQL.
In addition to providing an interface to the columns in a single table, data from other database tables related via FOREIGN KEY constraints can be easily and efficiently retrieved. To query for and return multiple fActiveRecord objects, please see the class fRecordSet.
The following discussion is built on top of the content of ORM Conventions. Topics include database schema structure, various notation standards and information about MySQL and SQLite databases.
In order to use the fActiveRecord class, a database table must exist to be modeled. Below is an example users table—please note that the table has been designed to demonstrate the features of the class, not as an example of a well-designed schema.
CREATE TABLE users ( user_id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, first_name VARCHAR(100) NOT NULL, middle_initial VARCHAR(5) NOT NULL DEFAULT '', last_name VARCHAR(100) NOT NULL, date_created TIMESTAMP NOT NULL, email_validated BOOLEAN NOT NULL DEFAULT FALSE, membership_fee DECIMAL(10,2) NOT NULL, profile TEXT NOT NULL DEFAULT '', status VARCHAR(20) NOT NULL CHECK(status IN ('Active', 'Inactive')) );
Once a table has been created, a PHP class will need to be made. The name of the class should be in UpperCamelCase notation and should be a singular form of the table name. Thus for the users table a class User would be created that extends fActiveRecord.
class User extends fActiveRecord { protected function configure() { } }
A blank extension of the configure() method has been included since that is where all class functionality configuration is placed. This method is called exactly once per script execution and is the preferred location to call code to extend or set up the class, but not the individual object.
In addition to the fActiveRecord class and database table, a method to connect to the database needs to be set up. To do this, an instance of the fDatabase class needs to be passed to fORMDatabase::attach() — this is commonly done in the site’s initilization script.
// Set up a SQLite database for use by fActiveRecord and fRecordSet fORMDatabase::attach( new fDatabase('sqlite', '/path/to/database') );
The fDatabase instance attached to fORMDatabase will also be used by fRecordSet.
It is possible to change the class to table association for modeling an existing incompatible database. The static method fORM::mapClassToTable() will accept the $class and the $table to map to the class to.
This method should be called in the site-wide configuration and should not be called in the configure() method.
// Class to table mapping should occur before any classes are used // such as when the database is attached via fORMDatabase::attach() fORMDatabase::attach($db); fORM::mapClassToTable('User', 'user');
When a class models a table in a non-default schema (public for PostgreSQL, dbo for MSSQL and the username for Oracle and DB2), the static method fORM::mapClassToTable() should be called with first parameter, the $class to map and the second parameter, $table, should be in the format schema.table.
This method should be called in the site-wide configuration and should not be called in the configure() method.
// This maps the User class to the users table in the authorization schema fORM::mapClassToTable('User', 'authorization.users');
When multiple databases are configured via fORMDatabase, classes can model tables on the non-default database by calling the method fORM::mapClassToDatabase(). The first parameter is the $class to map, and the second is the $name of the database set in fORMDatabase::attach().
// Attach a second database as "commerce_db" and have the User class model the users table in it fORMDatabase::attach($db, 'commerce_db'); fORM::mapClassToDatabase('User', 'commerce_db');
Like fORM::mapClassToTable(), this method should be called in the site-wide configuration and should not be call in the configure() method. This method is not required for classes modeling tables in the default database — if no $name was provided to fORMDatabase::attach(), then the database is the default.
A new record can be created by simply creating an object without any parameters.
$new_user_1 = new User(); $new_user_2 = new User();
Existing records can be loaded from the database by passing the primary key value to the constructor. If a primary key has multiple columns, use an associative array with the keys being the columns.
// Loading a record with a single column primary key $user = new User(2); // Loading a record with a multiple column primary key $permission = new Permission(array('user_id' => 2, 'resource_id' => 3));
It is also possible to load a record based on the values from columns in a UNIQUE constraint. When loading via a UNIQUE constraint, an associative array must be used, even if there is only a single column in the constraint.
// This loads a user by their unique email address $user = new User(array('email' => 'will@flourishlib.com'));
Please note that an fActiveRecord object is a reference object. All objects of the same class will share the same data and any operations will affect all instances. Thus if the User object for user 3 has the first name changed, all other objects representing user 3 will also have the first name changed.
For every column in a record there are at least five different operations that can be performed. ORM plugins can change these default behaviors or add even more.
| Action | Description |
| get | Retrieves the column’s value |
| set | Sets a new value for the column - empty string '' are converted to NULL |
| encode | Encodes all special HTML characters — should be used when content is not trusted or for displaying in HTML tag attributes |
| prepare | Encodes all special HTML characters, but leaves HTML tags and entities unencoded — should only be used for trusted content |
| inspect | Returns information about the column, including information such as the data type and valid values |
These five operations are combined with the column name into a camelCase method name. Below are some examples:
$first_name = $user->getFirstName(); $user->setFirstName($first_name); echo $user->encodeFirstName(); echo $user->prepareFirstName(); $max_length = $user->inspectFirstName('max_length');
As mentioned on the ORM Conventions page, all columns in the database should be created using underscore_notation. This assumes that numbers are separated from words by an underscore, such as address_2. If a number is not separated by an underscore, or you are having other notation conversion issues, you man need to customize the notation conversion using fGrammar.
When dealing with date, time or timestamp columns, the prepare and encode methods require a single parameter, $date_formatting_string. The formatting string can be any valid date() formatting string, or a format name that was created with fTimestamp::defineFormat().
echo $user->prepareDateCreated('n/j/y'); echo $user->encodeDateCreated('my_date_format');
Floating point columns without an explicit precision require a single parameter, $decimal_places, when calling the prepare and encode methods. Floating point columns with an explicit precision can optionally pass an integer for $decimal_places.
echo $user->prepareMembershipFee(2);
Columns that are string columns (VARCHAR, CHAR and TEXT) can optionally pass TRUE to their prepare and encode methods to cause all email addresses and website addresses to be converted to HTML links and to cause content without block-level HTML to have newline characters converted to <br /> tags.
echo $user->prepareEmail(TRUE); echo $user->prepareProfile(TRUE);
Every column has an inspect method that will return an associative array of data about the column. It is also possible to retrieve a single value by passing the optional parameter, $element.
$column_info = $user->inspectFirstName(); $max_length = $user->inspectFirstName('max_length');
The fActiveRecord class supports storing both scalar values (strings, integers, booleans, etc) and objects in columns. The only special consideration with storing objects in columns is that they should have a __toString() method so that they can be converted to a scalar to be saved in the database.
All of the Flourish value objects include such a __toString() method, making them work perfectly with fActiveRecord. In fact, fActiveRecord even loads date, time and timestamp columns out of the database into fDate, fTime and fTimestamp objects respectively. Nothing needs to be called or configured to enable this functionality. Thus, when getting date, time or timestamp values from a record, be sure to treat them as fDate, fTime and fTimestamp objects.
// Chaining the fDate::format() method off of the get method echo $user->getDateCreated()->format('n/j/y'); // Objects can be stored in a column as long as they have a __toString() method $user->setLastLoginTimestamp(new fTimestamp());
The following methods allow manipulation of an active record object:
| Method | Description |
| store() | Performs validate() and then executes an INSERT or UDPATE query |
| validate() | Ensures a record can be successfully saved to the database without actually doing it |
| delete() | Deletes a record from the database |
| load() | Reloads a record’s values from the database |
| populate() | Values from the HTTP request will automatically be set to the various columns of the record |
| replicate() / clone | Creates a copy of the record, cloning all contained objects and removing any auto incrementing primary key, can also replicate related records |
| exists() | Indicates if a record has already been stored in the database |
| reflect() | Returns a string containing the method signature for every method of the record |
The store() method will ensure that the record can be properly saved in the database and will store it there. If any errors are found, an fValidationException will be thrown with a message that is suitable for display to end users. The validation is performed by store() calling validate().
store() will also automatically begin a database and filesystem transaction if they are not already in progress. This allows database and filesystem actions to be performed in extending code and child objects without the need to keep track of changes manually and revert them.
try { $user = new User(); $user->setFirstName('Will'); $user->setLastName('Bond'); $user->store(); } catch (fExpectedException $e) { echo $e->printMessage(); }
There is a single optional parameter, $force_cascade, that affects how related records in one-to-many and one-to-one relationships are stored. See the Related Records Operations section for more details about how related records can be accessed and manipulated.
If related records (which we will call child records) have been set via an associate or populate method, and one or more of the original child records is no longer associated, and that child records has child records (grandchildren of the original) with an ON DELETE RESTRICT or ON DELETE NO ACTION clause in the FOREIGN KEY constraint, that child and the associated grandchildren records will all be deleted anyway. Normally an exception would be thrown indicating the child to be deleted had a grandchild record referencing it.
By default this parameter is set to FALSE to obey the restrictions in the database schema.
$user->populateFakeRelatedRecords(); $user->store(TRUE);
This forced cascade effect is accomplished by first finding the related records and explicitly deleting them before the originally associated record.
Validation of a record is performed based on the database schema and any additional validation rules set via the fORMValidation class, ORM plugins and fORM hooks.
The following rules are used to determine if a record is valid to store in the database:
If any of these validation checks do not work out, an fValidationException will be thrown contains an error message suitable for end users.
There are many additional validation rules that can be added to a record via fORMValidation. If the desired functionality is not available via fORMValidation, an ORM hook can be used. Please see the Adding Functionality to fActiveRecord and Custom Validation Using a Hook section of the fORM page for details and example code.
Many of the various ORM plugins that come with Flourish (fORMColumn, fORMDate, fORMFile, fORMMoney and fORMOrdering) add additional validation rules.
Since this method is automatically called when executing store(), it will typically only be called in situations where storing is not possible, such as multi-page forms.
try { // ... $user->validate(); } catch (fValidationException $e) { echo $e->printMessage(); }
It is also possible to return an array of errors by passing TRUE to the first parameter of validate(), $return_messages. This prevents an exception from being thrown.
$errors = $user->validate(TRUE);
The returned array will have keys in the following format with the value being the error message.
Below is an example of a returned array:
array( // Regular columns in the users table 'first_name' => 'First Name: Please enter a value', 'last_name' => 'Last Name: Please enter a value', // A message involving multiple columns, from fORMValidate::addOneOrMoreRule() 'email,phone' => 'Email, Phone: Please enter at least one', // A message from fORMValidate::addManyToManyRule() 'groups' => 'Groups: Please select at least one', // A message from the bio column in the one-to-one related user_details table 'user_details::bio' => 'User Details Bio: Please enter a value', // Messages from one-to-many related favorites table 'favorites[0]::name' => 'Favorite #1 Name: Please enter a value', 'favorites[2]::name' => 'Favorite #3 Name: Please enter a value' );
When passing TRUE to $return_messages, it is also possible to pass TRUE to the second parameter, $remove_column_names. This will remove the names of the columns from the error messages themselves, leaving the array with the regular keys, but anonymous messages useful for inclusion next to inputs.
$errors = $user->validate(TRUE, TRUE);
The returned array would look like:
array( // Regular columns in the users table 'first_name' => 'Please enter a value', 'last_name' => 'Please enter a value', // A message involving multiple columns, from fORMValidate::addOneOrMoreRule() 'email,phone' => 'Please enter at least one', // A message from fORMValidate::addManyToManyRule() 'groups' => 'Please select at least one', // A message from the bio column in the one-to-one related user_details table 'user_details::bio' => 'Please enter a value', // Messages from one-to-many related favorites table 'favorites[0]::name' => 'Please enter a value', 'favorites[2]::name' => 'Please enter a value' );
The delete() method will remove the record from the database. Obviously, any cascading FOREIGN KEY constraints could cause other records to be deleted as well. This method will throw an fValidationException if the record is referenced by another record via a FOREIGN KEY constraint with an ON DELETE RESTRICT or ON DELETE NO ACTION clause.
try { $user->delete(); } catch (fValidationException $e) { echo $e->printMessage(); }
There is an optional parameter, $force_cascade, that when set to TRUE will delete all records that reference the record being deleted, even if they have an ON DELETE RESTRICT or ON DELETE NO ACTION clause in the FOREIGN KEY constraint. By default this is set to FALSE to obey the restrictions in the database schema.
$user->delete(TRUE);
This is accomplished by first finding the related records and explicitly deleting them before the original record.
The load() method causes the values for the record to be reloaded from the database, overwriting any values that have been changed since the object was first loaded.
$user = new User(3); $user->setFirstName('Joe'); // Reset the first name $user->load();
The populate() method sets the values for a record from the fields and value contained in an HTTP request. For values to be pulled from the request, the HTML field names should be exactly the same as the database column names. Values from the form that are a blank strings are automatically converted to NULL.
The following HTML form when combined with the populate() method would cause the first_name, last_name and email column values to be set:
<form action="" method="post"> <fieldset> <p> <label for="first_name">First Name</label> <input type="text" id="first_name" name="first_name" /> </p> <p> <label for="last_name">Last Name</label> <input type="text" id="last_name" name="last_name" /> </p> <p> <label for="email">Email</label> <input type="text" id="email" name="email" /> </p> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>
try { $user = new User(); // This will set first_name, last_name and email $user->populate(); $user->store(); } catch (fExpectedException $e) { echo $e->printMessage(); }
By default, populate() calls the individual set methods for each column in a record. This ensures that overridden methods are correctly called. The set methods in fActiveRecord will by default convert an empty string value '' to NULL. This treats empty HTML form input as if the user entered nothing.
Blank strings can be stored in a database columninstead of NULL by setting the column to NOT NULL and DEFAULT ''. When fActiveRecord finds a NOT NULL column with a NULL value and a non-NULL default, the default is substituted in place of the NULL.
When using checkboxes in forms to be populated, it is best to preceed the checkbox input with a hidden input using the same name but a FALSE value. This way, if the checkbox is unchecked, PHP gets the value from the hidden input, changing the column to a FALSE value. When the checkbox is checked, PHP gets the value from the checkbox input, changing the column to a TRUE value.
<form action="" method="post"> <fieldset> <p> <input type="hidden" name="enroll_me" value="0" /> <label for="enroll_me">Enroll Me</label> <input type="checkbox" id="enroll_me" name="enroll_me" value="1" /> </p> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>
Please note that there IS a difference between setting the hidden input to a blank string value and a FALSE value such as 0. As mentioned in the previous section, empty strings are converted to NULL, whereas a value such as 0 or false would not be.
Certain fORMValidation methods check to see if a column has a non-null value. If an empty string is used for the hidden input, the validation method would see that no value has been selected. If a non-null false value is used, the validation method would see that a non-null value has been selected. Depending on the requirements of the application, this distinction may be very important.
The replicate() method, passed no parameters is equivalent to a record being cloned. When an fActiveRecord object is cloned, all values and cache entries are copied (or cloned if the value is an object), all related records and old values are purged, any auto incrementing primary keys are erased and the record is changed to look as though it does not yet exist in the database.
$user = new User(1); // Both $user2 and $user3 will have no $user_id and will return FALSE from ->exists() $user2 = clone $user; $user3 = $user->replicate();
It is also possible to replicate related records along with a record by passing plural class names into replicate(). There may be any number of class names and the classes must be for related records that are in a many-to-many or one-to-many relationship with the record. Below is an example of replicating a user and including all of their group memberships and favorites (see Related Records Operations for the database schema).
$user = new User(1); $new_user = $user->replicate('Groups', 'Favorites');
If a record is has more than one relationship route to the related record class, the route should be specified between curly braces ({ and }), like below:
$user = new User(1); $new_user = $user->replicate('Resources{read_permissions}');
The exists() method simply returns a boolean indicating if the record was loaded from the database.
if (!$user->exists()) { $user->sendWelcomeEmail(); }
This function is mostly useful when dealing with an object in a different scope than that which it was created. For instance, checking a record that has been passed into a function or an fActiveRecord hook callback.
Please note that this only checks to see if the object was constructed by passing a primary or unique key into the constructor. It can not be used with set methods to check and see if a record exists in the database.
The following code will NOT check to see if a user with the id 3 exists in the database. This call to exists will always return FALSE since the object was constructed without any parameters.
$user = new User(); $user->setUserId(3); if ($user->exists()) { // … }
The proper way to check if a record exists in the database is to try and create an instance, catching an fNotFoundException:
try { $user = new User(3); } catch (fNotFoundException $e) { // … }
From a technical perspective, for the first example to work, fActiveRecord object would have to have a mutable identity, meaning the object would change what row it represented over its lifetime. This would likely cause many other unintended side effect in common code.
Since fActiveRecord classes have dynamic functionality based on the structure of a database table, sometimes it can be useful to check and see what exactly is available for a specific class. The reflect() method returns a preformatted text block containing the method signature for every method, concrete and dynamic, in the class.
echo '<pre>' . $user->reflect() . '</pre>';
would output something like
public function getFirstName(); public function setFirstName($first_name); public function prepareFirstName(); public function encodeFirstName(); public function inspectFirstName($element=NULL);
It is also possible to pass a TRUE parameter to reflect() to also return the PHPDoc doc block for a method.
// Request the PHPDoc doc block too echo $user->reflect(TRUE);
One of the useful features of fActiveRecord is that it automatically finds all related tables in a database schema by looking at FOREIGN KEY constraints. Absolutely no configuration is needed in the class.
The following tables definitions will be used for the examples below and are purposefully simple to help focus on the features, not the SQL.
CREATE TABLE groups ( name VARCHAR(100) PRIMARY KEY ); CREATE TABLE users ( user_id SERIAL PRIMARY KEY, email VARCHAR(100) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL ); CREATE TABLE user_details ( user_id INTEGER PRIMARY KEY REFERENCES users(user_id) ON DELETE CASCADE, photo VARCHAR(255) NOT NULL ); CREATE TABLE users_groups ( group VARCHAR(100) NOT NULL REFERENCES groups(name) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, PRIMARY KEY (group, user_id) ); CREATE TABLE favorites ( favorite_id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, url VARCHAR(255) NOT NULL ); CREATE TABLE resources ( resource_id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, owner INTEGER NOT NULL REFERENCES users(user_id) ON DELETE RESTRICT ); CREATE TABLE read_permissions ( resource_id INTEGER NOT NULL REFERENCES resources(resource_id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, PRIMARY KEY (resource_id, user_id) );
For the sake of the examples below, assume that the following classes have been defined to extend fActiveRecord:
Favorite Group Resource User UserDetail
Where there are multiple relationships between two table, such as from users to resources, fActiveRecord contains optional parameters for all related record methods that allows the proper relationship route to be specified. For details, please see the Relationship Routes section.
The detection of related tables allows for the following functionality to be provided:
When a column in a table contains a FOREIGN KEY constraint to another table, the two tables are in either a many-to-one or one-to-one relationship. The relationship would only be one-to-one if there was a UNIQUE constraint on the column with the FOREIGN KEY.
For instance, the resources table references the users table via the owner column. Because this FOREIGN KEY exists, it is possible to call a create action to instantiate the proper User object.
$resource = new Resource(1); $user = $resource->createUser();
Since the user_details table references the user_id as a PRIMARY KEY, there can only be a single user_details record for each entry in users. This makes it a one-to-one relationship. For one-to-one relationships, it is also possible to call the populate and has actions just like *-to-many relationships. When working with one-to-one relationships, the related record name is singular rather than plural.
$user = new User(1); $user->populateUserDetail();
if ($user->hasUserDetail()) { // … }
When a column is referenced by a FOREIGN KEY constraint in another table, the two tables involved will end up being in either a one-to-many or many-to-many relationship. Many-to-many relationships happen when a joining table is used and the FOREIGN KEY constraints live in the joining table.
For the examples below the one-to-many relationship between the users table and the favorites table will be used along with the many-to-many relationship between the users and groups tables.
Each user on the system can have multiple favorites simply by creating new favorites and having each one reference the same user. The build action will create an fRecordSet of all records in such a relationship:
$favorites = $user->buildFavorites();
The $favorites record set may be empty, or it may contain quite a number of records.
In situations where the related records don’t need to be created, but a primary key will suffice, it is also possible to list related records. This will return an array of related primary keys.
$favorite_ids = $user->listFavorites();
The count action can be called for any related record and it will return the number of related records.
$number_of_favorites = $user->countFavorites();
If a number of records is not required, but just that related records exist, the has action can be used.
if ($user->hasFavorites()) { // … }
Both one-to-many and many-to-many relationships support the build, count, list and has actions.
In many-to-many relationships, both types of records can exist without directly referencing each other. Thus often it is necessary to take one set of records and associate it with a specific record. The associate action will do this, such as below where a user is being associated with every group.
$groups = fRecordSet::build('Group'); $user->associateGroups($groups); $user->store();
associate methods will accept an fRecordSet, an array of fActiveRecord objects or an array of primary keys. It is also possible to call associate methods to associate records in a one-to-many relationship, however the link action discussed next only works with many-to-many relationships.
It is also possible to parse associations from the fields in an HTTP request. In that situation the HTML form fields must be named in the format {plural_underscore_related_class}::{foreign_column}[]. The link action will grab values from the HTTP request and use them for associating records in a many-to-many relationship.
<form action="" method="post"> <fieldset> <p> <label for="first_name">First Name</label> <input type="text" id="first_name" name="first_name" /> </p> <p> <label for="last_name">Last Name</label> <input type="text" id="last_name" name="last_name" /> </p> <p> <label for="email">Email</label> <input type="text" id="email" name="email" /> </p> <p> <label for="group_a">Group A</label> <input type="checkbox" id="group_a" name="groups::name[]" value="Group A" /> </p> <p> <label for="group_b">Group B</label> <input type="checkbox" id="group_b" name="groups::name[]" value="Group B" /> </p> <p> <label for="group_c">Group C</label> <input type="checkbox" id="group_c" name="groups::name[]" value="Group C" /> </p> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>
$user->linkGroups(); $user->store();
Please note that store() must be called to actually save the new relationships between the two tables.
For records that have "child" object in one-to-many relationships, the populate action will call the populate() method for each of a list of related records. In addition, when store() is called on the master record, all of the child records will be saved too.
Below is an example of the kind of HTML form that is needed for creating and populating child objects. Normally the HTML for the child object would be added and removed from the page on the fly using javascript, otherwise the validate() method from a child object could stop the records from being saved when a child object’s values are not complete. The initial printing of the HTML form elements is normally handled server side by iterating trough the fRecordSet of related records.
Each set of inputs for child object should always contain the PRIMARY KEY column and the column with the FOREIGN KEY constraint that references this table. Any other columns should only be included if new data is desired.
Each input for a related record needs to be prefixed with the plural underscore_notation version of the class name plus ::. In addition, each input should use array syntax at the end with a key shared for all inputs of the same record. The key can be any number or string, but must be the same for each input of the record. The example below uses 0, 1 and 2 as a simple example.
<form action="" method="post"> <fieldset> <p> <label for="first_name">First Name</label> <input type="text" id="first_name" name="first_name" /> </p> <p> <label for="last_name">Last Name</label> <input type="text" id="last_name" name="last_name" /> </p> <p> <label for="email">Email</label> <input type="text" id="email" name="email" /> </p> <p> <label for="fav_1">Favorite #1</label> <input type="name" id="fav_1" name="favorites::url[0]" value="" /> <input type="hidden" name="favorites::user_id[0]" value="2" /> <input type="hidden" name="favorites::favorite_id[0]" value="" /> </p> <p> <label for="fav_2">Favorite #2</label> <input type="checkbox" id="fav_2" name="favorites::url[1]" value="" /> <input type="hidden" name="favorites::user_id[1]" value="2" /> <input type="hidden" name="favorites::favorite_id[1]" value="" /> </p> <p> <label for="fav_3">Favorite #3</label> <input type="checkbox" id="fav_3" name="favorites::url[2]" value="C" /> <input type="hidden" name="favorites::user_id[2]" value="2" /> <input type="hidden" name="favorites::favorite_id[2]" value="" /> </p> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>
The following PHP would actually create 3 Favorite objects and would set them to be saved when store() is called on the User object.
try { $user->populate(); $user->populateFavorites(); $user->store(); } catch (fExpectedException $e) { echo $e->printMessage(); }
When there are multiple one-to-many, many-to-many or *-to-one relationships for two tables, the proper route must be specified when calling the various related record methods. The appropriate route name can be determined by viewing the Relationship Routes section of the ORM Conventions page.
Routes can be specified in any of the following methods even if only one route exists, however that route will be automatically detected if not specified.
The build, count, create, link, list and populate action methods all optionally accept the route as the first parameter.
$record->createRelatedRecord('route'); $record->buildRelatedRecords('route'); $record->countRelatedRecords('route'); $record->linkRelatedRecords('route'); $record->listRelatedRecords('route'); $record->populateRelatedRecords('route');
associate action methods optionally accept the route as the second parameter.
$record->associateRelatedRecords($related_records, 'route');
When working with relationship routes in HTML forms, the relationship route name should be enclosed in {} directly after the foreign table name. This applies to forms being used with both the link and populate actions. Below is an example of a form for selecting the resources related to a user:
<p> <input type="checkbox" id="resource_1" name="resources{read_permissions}::resource_id[]" value="1" /> <label for="resource_1">Resource 1</label> </p> <p> <input type="checkbox" id="resource_2" name="resources{read_permissions}::resource_id[]" value="2" /> <label for="resource_2">Resource 2</label> </p>
As mentioned in the Setup section, the instance method configure() is called once per script execution for each classes that extends fActiveRecord. This method is designed to be the preferred place to execute any configuration code for an active record class.
class User extends fActiveRecord { protected function configure() { // Configure the extra feature and overrides using the supporting ORM classes } }
The following classes provide methods that extends and change the way that fActiveRecord classes work. Each class’s documentation page will include the necessary details on how to configure each bit of functionality and how it affects the standard use of active record classes.
| fORM | Provides functionality to override database table to class mapping, change the names used for records and column and extends fActiveRecord and fRecordSet through registering callbacks for various hooks |
| fORMColumn | Allows changing the validation of columns to require email addresses or links, can configure columns to always create fNumber objects when loaded or have a column filled with a random string the first time a record is saved |
| fORMDate | Can set columns to automatically save the date a record was created or last updated and can tie a timestamp column to another column to allow for saving timezones |
| fORMFile | Provides functionality to automatically handle file or image uploads, including options to automatically duplicate and manipulate images |
| fORMJSON | Extends both fActiveRecord and fRecordSet to have toJSON() methods |
| fORMMoney | Can set columns to be loaded out of the database as fMoney objects and can tie fMoney columns to a second column to store currencies |
| fORMOrdering | Allows configuring a column (either individually or in a group of columns) as the arbitrary ordering column for a class |
| fORMRelated | Provides functionality to set the order in which related records are returned or modify what they are called (in context) |
| fORMValidation | Allows setting additional validation rules (including conditional, one-or-more, many-to-many, etc), set the order for validation messages and configure UNIQUE constraints as case-insensitive |
An active record pattern base class
This class uses fORMSchema to inspect your database and provides an OO interface to a single database table. The class dynamically handles method calls for getting, setting and other operations on columns. It also dynamically handles retrieving and storing related records.
| 1.0.0b67 | Updated code to work with the new fORM API 8/6/10 |
|---|---|
| 1.0.0b66 | Fixed a bug with store() and non-primary key auto-incrementing columns 7/5/10 |
| 1.0.0b65 | Fixed bugs with inspect() making some min_value and max_value elements available for non-numeric types, fixed reflect() to list the min_value and max_value elements 6/8/10 |
| 1.0.0b64 | BackwardsCompatibilityBreak - changed validate()'s returned messages array to have field name keys - added the option to validate() to remove field names from messages 5/26/10 |
| 1.0.0b63 | Changed how is_subclass_of() is used to work around a bug in 5.2.x 4/6/10 |
| 1.0.0b62 | Fixed a bug that could cause infinite recursion starting in v1.0.0b60 4/2/10 |
| 1.0.0b61 | Fixed issues with handling populate actions when working with mapped classes 3/31/10 |
| 1.0.0b60 | Fixed issues with handling associate and has actions when working with mapped classes, added validateClass() 3/30/10 |
| 1.0.0b59 | Changed an extended UTF-8 arrow character into the correct -> 3/29/10 |
| 1.0.0b58 | Fixed reflect() to specify the value returned from set methods 3/15/10 |
| 1.0.0b57 | Added the post::loadFromIdentityMap() hook and fixed __construct() to always call the post::__construct() hook 3/14/10 |
| 1.0.0b56 | Fixed $force_cascade in delete() to work even when the restricted relationship is once-removed through an unrestricted relationship 3/9/10 |
| 1.0.0b55 | Fixed load() to that related records are cleared, requiring them to be loaded from the database 3/4/10 |
| 1.0.0b54 | Fixed detection of route name for one-to-one relationships in delete() 3/3/10 |
| 1.0.0b53 | Fixed a bug where related records with a primary key that contained a foreign key with an on update cascade clause would be deleted when changing the value of the column referenced by the foreign key 12/17/09 |
| 1.0.0b52 | Backwards Compatibility Break - Added the $force_cascade parameter to delete() and store() - enabled calling prepare() and encode() for non-column get methods, added ::has{RelatedRecords}() methods 12/16/09 |
| 1.0.0b51 | Made changed() properly recognize that a blank string and NULL are equivalent due to the way that set() casts values 11/14/09 |
| 1.0.0b50 | Fixed a bug with trying to load by a multi-column primary key where one of the columns was not specified 11/13/09 |
| 1.0.0b49 | Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09 |
| 1.0.0b48 | Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09 |
| 1.0.0b47 | Changed ::associate{RelatedRecords}(), ::link{RelatedRecords}() and ::populate{RelatedRecords}() to allow for method chaining 10/22/09 |
| 1.0.0b46 | Changed SQL statements to use value placeholders and identifier escaping 10/22/09 |
| 1.0.0b45 | Added support for !~, &~, >< and OR comparisons to checkConditions(), made object handling in checkConditions() more robust 9/21/09 |
| 1.0.0b44 | Updated code for new fValidationException API 9/18/09 |
| 1.0.0b43 | Updated code for new fRecordSet API 9/16/09 |
| 1.0.0b42 | Corrected a grammar bug in hash() 9/9/09 |
| 1.0.0b41 | Fixed a bug in the last version that would cause issues with classes containing a custom class to table mapping 9/1/09 |
| 1.0.0b40 | Added a check to the configuration part of __construct() to ensure modelled tables have primary keys 8/26/09 |
| 1.0.0b39 | Changed set{ColumnName}() methods to return the record for method chaining, fixed a bug with loading by multi-column unique constraints, fixed a bug with load() 8/26/09 |
| 1.0.0b38 | Updated changed() to do a strict comparison when at least one value is NULL 8/17/09 |
| 1.0.0b37 | Changed __construct() to allow any Iterator object instead of just fResult 8/12/09 |
| 1.0.0b36 | Fixed a bug with setting NULL values from v1.0.0b33 8/10/09 |
| 1.0.0b35 | Fixed a bug with unescaping data in loadFromResult() from v1.0.0b33 8/10/09 |
| 1.0.0b34 | Added the ability to compare fActiveRecord objects in checkConditions() 8/7/09 |
| 1.0.0b33 | Performance enhancements to __call() and __construct() 8/7/09 |
| 1.0.0b32 | Changed delete() to remove auto-incrementing primary keys after the post::delete() hook 7/29/09 |
| 1.0.0b31 | Fixed a bug with loading a record by a multi-column primary key, fixed one-to-one relationship API 7/21/09 |
| 1.0.0b30 | Updated reflect() for new fORM::callReflectCallbacks() API 7/13/09 |
| 1.0.0b29 | Updated to use new fORM::callInspectCallbacks() method 7/13/09 |
| 1.0.0b28 | Fixed a bug where records would break the identity map at the end of store() 7/9/09 |
| 1.0.0b27 | Changed hash() from a protected method to a static public/internal method that requires the class name for non-fActiveRecord values 7/9/09 |
| 1.0.0b26 | Added checkConditions() from fRecordSet 7/8/09 |
| 1.0.0b25 | Updated validate() to use new fORMValidation API, including new message search/replace functionality 7/1/09 |
| 1.0.0b24 | Changed validate() to remove duplicate validation messages 6/30/09 |
| 1.0.0b23 | Updated code for new fORMValidation::validateRelated() API 6/26/09 |
| 1.0.0b22 | Added support for the $formatting parameter to encode methods on char, text and varchar columns 6/19/09 |
| 1.0.0b21 | Performance tweaks and updates for fORM and fORMRelated API changes 6/15/09 |
| 1.0.0b20 | Changed replacement values in preg_replace() calls to be properly escaped 6/11/09 |
| 1.0.0b19 | Added list{RelatedRecords}() methods, updated code for new fORMRelated API 6/2/09 |
| 1.0.0b18 | Changed store() to use new fORMRelated::store() method 6/2/09 |
| 1.0.0b17 | Added some missing parameter information to reflect() 6/1/09 |
| 1.0.0b16 | Fixed bugs in __clone() and replicate() related to recursive relationships 5/20/09 |
| 1.0.0b15 | Fixed an incorrect variable reference in store() 5/6/09 |
| 1.0.0b14 | store() no longer tries to get an auto-incrementing ID from the database if a value was set 5/2/09 |
| 1.0.0b13 | delete(), load(), populate() and store() now return the record to allow for method chaining 3/23/09 |
| 1.0.0b12 | set() now removes commas from integers and floats to prevent validation issues 3/22/09 |
| 1.0.0b11 | encode() no longer adds commas to floats 3/22/09 |
| 1.0.0b10 | __wakeup() no longer registers the record as the definitive copy in the identity map 3/22/09 |
| 1.0.0b9 | Changed __construct() to populate database default values when a non-existing record is instantiated 1/12/09 |
| 1.0.0b8 | Fixed exists() to properly detect cases when an existing record has one or more NULL values in the primary key 1/11/09 |
| 1.0.0b7 | Fixed __construct() to not trigger the post::__construct() hook when force-configured 12/30/08 |
| 1.0.0b6 | __construct() now accepts an associative array matching any unique key or primary key, fixed the post::__construct() hook to be called once for each record 12/26/08 |
| 1.0.0b5 | Fixed replicate() to use plural record names for related records 12/12/08 |
| 1.0.0b4 | Added replicate() to allow cloning along with related records 12/12/08 |
| 1.0.0b3 | Changed __clone() to clone objects contains in the values and cache arrays 12/11/08 |
| 1.0.0b2 | Added the __clone() method to properly duplicate a record 12/4/08 |
| 1.0.0b | The initial implementation 8/4/07 |
Caches callbacks for methods
array
An array of flags indicating a class has been configured
array
Maps objects via their primary key
array
Caches method name parsings
array
Keeps track of the recursive call level of replication so we can clear the map
integer
Keeps a list of records that have been replicated
array
Contains a list of what columns in each class need to be unescaped and what data type they are
array
A data store for caching data related to a record, the structure of this is completely up to the developer using it
array
The old values for this record
Column names are the keys, but a column key will only be present if a value has changed. The value associated with each key is an array of old values with the first entry being the oldest value. The static methods assign(), changed(), hasOld() and retrieveOld() are the best way to interact with this array.
array
Records that are related to the current record via some relationship
This array is used to cache related records so that a database query is not required each time related records are accessed. The fORMRelated class handles most of the interaction with this array.
array
The values for this record
This array always contains every column in the database table as a key with the value being the current value.
array
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Sets a value to the $values array, preserving the old value in $old_values
void assign( array &$values, array &$old_values, string $column, mixed $value )
| array | &$values | The current values |
| array | &$old_values | The old values |
| string | $column | The column to set |
| mixed | $value | The value to set |
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Checks to see if a value has changed
boolean changed( array &$values, array &$old_values, string $column )
| array | &$values | The current values |
| array | &$old_values | The old values |
| string | $column | The column to check |
If the value for the column specified has changed
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Ensures a class extends fActiveRecord
boolean checkClass( string $class )
| string | $class | The class to check |
If the class is an fActiveRecord descendant
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Checks to see if a record matches all of the conditions
boolean checkConditions( fActiveRecord $record, array $conditions )
| fActiveRecord | $record | The record to check |
| array | $conditions | The conditions to check - see fRecordSet::filter() for format details |
If the record meets all conditions
Composes text using fText if loaded
string compose( string $message, mixed $component )
string compose( mixed .. )
| string | $message | The message to compose |
| mixed | $component | A string or number to insert into the message |
| mixed | .. | ... |
The composed and possible translated message
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Ensures that configure() has been called for the class
void forceConfigure( string $class )
| string | $class | The class to configure |
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Takes a row of data or a primary key and makes a hash from the primary key
string|NULL hash( fActiveRecord|array|string|int $record, string $class=NULL )
| fActiveRecord|array|string|int | $record | An fActiveRecord object, an array of the records data, an array of primary key data or a scalar primary key value |
| string | $class | The class name, if $record isn't an fActiveRecord |
A hash of the record's primary key value or NULL if the record doesn't exist yet
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Checks to see if an old value exists for a column
boolean hasOld( array &$old_values, string $column )
| array | &$old_values | The old values |
| string | $column | The column to set |
If an old value for that column exists
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Resets the configuration of the class
void reset( )
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Retrieves the oldest value for a column or all old values
mixed retrieveOld( array &$old_values, string $column, mixed $default=NULL, boolean $return_all=FALSE )
| array | &$old_values | The old values |
| string | $column | The column to get |
| mixed | $default | The default value to return if no value exists |
| boolean | $return_all | Return the array of all old values for this column instead of just the oldest |
The old value for the column
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Ensures a class extends fActiveRecord
void validateClass( string $class )
| string | $class | The class to verify |
Creates a new record or loads one from the database - if a primary key or unique key is provided the record will be loaded
fActiveRecord __construct( mixed $key=NULL )
| mixed | $key | The primary key or unique key value(s) - single column primary keys will accept a scalar value, all others must be an associative array of (string) {column} => (mixed) {value} |
Handles all method calls for columns, related records and hook callbacks
Dynamically handles get, set, prepare, encode and inspect methods for each column in this record. Method names are in the form verbColumName().
This method also handles associate, build, count, has, and link verbs for records in many-to-many relationships; build, count, has and populate verbs for all related records in one-to-many relationships and create, has and populate verbs for all related records in one-to-one relationships, and the create verb for all related records in many-to-one relationships.
Method callbacks registered through fORM::registerActiveRecordMethod() will be delegated via this method.
mixed __call( string $method_name, array $parameters )
| string | $method_name | The name of the method called |
| array | $parameters | The parameters passed |
The value returned by the method called
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Creates a clone of a record
If the record has an auto incrementing primary key, the primary key will be erased in the clone. If the primary key is not auto incrementing, the primary key will be left as-is in the clone. In either situation the clone will return FALSE from the exists() method until store() is called.
fActiveRecord __clone( )
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
All requests that hit this method should be requests for callbacks
callback __get( string $method )
| string | $method | The method to create a callback for |
The callback for the method requested
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Configure itself when coming out of the session. Records from the session are NOT hooked into the identity map.
void __wakeup( )
Allows the programmer to set features for the class
This method is only called once per page load for each class.
void configure( )
Creates the fDatabase::translatedQuery() insert statement params
array constructInsertParams( )
The parameters for an fDatabase::translatedQuery() SQL insert statement
Creates the fDatabase::translatedQuery() update statement params
array constructUpdateParams( )
The parameters for an fDatabase::translatedQuery() SQL update statement
Deletes a record from the database, but does not destroy the object
This method will start a database transaction if one is not already active.
fActiveRecord delete( boolean $force_cascade=FALSE )
| boolean | $force_cascade | When TRUE, this will cause all child objects to be deleted, even if the ON DELETE clause is RESTRICT or NO ACTION |
The record object, to allow for method chaining
Retrieves a value from the record and prepares it for output into an HTML form element.
Below are the transformations performed:
string encode( string $column, string $formatting=NULL )
| string | $column | The name of the column to retrieve |
| string | $formatting | The formatting string |
The encoded value for the column specified
Checks to see if the record exists in the database
boolean exists( )
If the record exists in the database
Loads a record from the database based on a UNIQUE key
void fetchResultFromUniqueKey( array $values )
| array | $values | The UNIQUE key values to try and load with |
Retrieves a value from the record
mixed get( string $column )
| string | $column | The name of the column to retrieve |
The value for the column specified
Retrieves information about a column
mixed inspect( string $column, string $element=NULL )
| string | $column | The name of the column to inspect |
| string | $element | The metadata element to retrieve |
The metadata array for the column, or the metadata element specified
Loads a record from the database
fActiveRecord load( )
The record object, to allow for method chaining
Tries to load the object (via references to class vars) from the fORM identity map
boolean loadFromIdentityMap( array $row, string $hash )
| array | $row | The data source for the primary key values |
| string | $hash | The unique hash for this record |
If the load was successful
Loads a record from the database directly from a result object
boolean loadFromResult( Iterator $result, boolean $ignore_identity_map=FALSE )
| Iterator | $result | The result object to use for loading the current object |
| boolean | $ignore_identity_map | If the identity map should be ignored and the values loaded no matter what |
If the record was loaded from the identity map
Sets the values for this record by getting values from the request through the fRequest class
fActiveRecord populate( )
The record object, to allow for method chaining
Retrieves a value from the record and prepares it for output into html.
Below are the transformations performed:
string prepare( string $column, mixed $formatting=NULL )
| string | $column | The name of the column to retrieve |
| mixed | $formatting | The formatting parameter, if applicable |
The formatted value for the column specified
Generates a pre-formatted block of text containing the method signatures for all methods (including dynamic ones)
string reflect( boolean $include_doc_comments=FALSE )
| boolean | $include_doc_comments | If the doc block comments for each method should be included |
A preformatted block of text with the method signatures and optionally the doc comment
Generates a clone of the current record, removing any auto incremented primary key value and allowing for replicating related records
This method will accept three different sets of parameters:
The class names specified can be a simple class name if there is only a single route between the two corresponding database tables. If there is more than one route between the two tables, the class name should be substituted with a string in the format 'RelatedClass{route}'.
fActiveRecord replicate( string $related_class=NULL )
fActiveRecord replicate( string .. )
| string | $related_class | The plural related class to replicate - see method description for details |
| string | .. | ... |
The cloned record
Sets a value to the record
fActiveRecord set( string $column, mixed $value )
| string | $column | The column to set the value to |
| mixed | $value | The value to set |
This record, to allow for method chaining
Stores a record in the database, whether existing or new
This method will start database and filesystem transactions if they have not already been started.
fActiveRecord store( boolean $force_cascade=FALSE )
| boolean | $force_cascade | When storing related records, this will force deleting child records even if they have their own children in a relationship with an RESTRICT or NO ACTION for the ON DELETE clause |
The record object, to allow for method chaining
Validates the values of the record against the database and any additional validation rules
void|array validate( boolean $return_messages=FALSE, boolean $remove_column_names=FALSE )
| boolean | $return_messages | If an array of validation messages should be returned instead of an exception being thrown |
| boolean | $remove_column_names | If column names should be removed from the returned messages, leaving just the message itself |
If $return_messages is TRUE, an array of validation messages will be returned
The fAuthorization class is a static class provides functionality to restrict access to pages based on either simple a authorization level or more complex access control lists (ACLs).
Since the class is static, no instantiation is required, however to use the features some setup will need to be performed. The only setup common to using either authorization levels or ACLs is to set up a login page. For maintainability, I recommend you perform your setup in a common configuration like described on the Getting Started page:
// Set up a login page in your local.config.php fAuthorization::setLoginPage('/login/');
The login page URL should be an absolute URL, relative to the domain name.
If need be, the login page URL can be retrieved with the static method getLoginPage().
fURL::redirect(fAuthorization::getLoginPage());
The simplest way to control access to pages is to use authorization levels. Each user is assigned a single authorization level and can view any page that requires that level or a level below.
After you have setup your login page, you are going to need to define the different authorization levels. Just like with the login page, you will probably want to place these in a common configuration file. Here is a simple example:
fAuthorization::setAuthLevels( array( 'admin' => 100, 'user' => 50, 'guest' => 25 ) );
You’ll notice that each authorization level is assigned a number. If a user has a number that is the same or above the required level, they can view a page. If not, they will be redirected to the login page.
The setUserAuthLevel() method provides the functionality to assign an authorization level to a user when they log in:
// This would be executed after the username and password were verified fAuthorization::setUserAuthLevel('admin');
To actually require an authorization level you will need to call requireAuthLevel() at the top of your page:
fAuthorization::requireAuthLevel('user');
If you wish to use a user’s authorization level to control other aspects of your site, you can use the checkAuthLevel() method:
if (fAuthorization::checkAuthLevel('admin')) { // Execute admin specific code }
Last, but not least, if you need to get the user’s authorization level, that can be accomplished by calling getUserAuthLevel():
$auth_level = fAuthorization::getUserAuthLevel();
Access control lists (ACLs) allow for more fine-grained permissions than authorization levels. With ACLs you associate resource names with specific permissions. For a user to be able to access a page they need to have the required permission for the resource specified.
ACLs do not require any setup beyond assigning the user’s ACLs when they log in. Also note that the string '*' acts as a wildcard when doing resource and permission comparisons.
// This would be executed after the username and password were verified fAuthorization::setUserACLs( array( 'posts' => array('*'), 'users' => array('add', 'edit', 'delete'), 'groups' => array('add'), '*' => array('list') ) );
The above user ACLs would imply the user has permissions to: do anything with posts; add, edit and delete users; add groups and list anything.
To require a user have a specific ACL to view a page, use the method requireACL() at the top of a page:
fAuthorization::requireACL('users', 'list');
The above code would require a user to have the list permission for the users resource in order to view the page.
In addition to control page views, ACLs can be useful for controlling access on a smaller level. If you wish to perform a conditional branch based on a user’s ACLs you can use the checkACL() method:
if (fAuthorization::checkACL('users', 'edit')) { // Code to be executed for users who can edit users }
If you need to get a list of all ACLs assigned to the current user (in the same array format they are set), you can use the getUserACLs() method:
$user_acls = fAuthorization::getUserACLs();
Once you have decided if you wish to go with authorization levels or ACLs you can move on to actually logging the user in.
Sometimes when a user visits the login page, they will have entered the URL manually, or will have followed a link. In this sort of situation you will need a default page to redirect them to. The rest of the time users will usually get directed to the login page because they tried to access a restricted page. You can get this information with the getRequestedURL() method.
getRequestedURL() requires a single parameter, $clear, which controls if the requested URL is erased when returned, or if it is to be left for later access. A second, optional, parameter is the default URL to use if the user was not redirected to the login page.
Here is an example of logging a user in and redirecting them to the requested page (or the home page if no page was requested):
if ('login' == $action && fRequest::isPost()) { if ($email == 'john@example.com' && sha1($password) == '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8') { fAuthorization::setUserAuthLevel('admin'); fURL::redirect(fAuthorization::getRequestedURL(TRUE, '/')); } }
Please note that the code above is simplified to demonstration, please check out the fCryptography Class for information on hashing passwords.
If for some reason you need to manually set the requested URL, that can be accomplished with the setRequestedURL() method.
In addition, it is usually necessary to remember some sort of information about the user that is logging in so you can retrieve their information on other pages. The setUserToken() and getUserToken() methods allow storing some sort of user identifier and getting it back later. Here is the above example with the user token code added:
if ('login' == $action && fRequest::isPost()) { if ($email == 'john@example.com' && sha1($password) == '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8') { fAuthorization::setUserAuthLevel('admin'); fAuthorization::setUserToken('john@example.com'); fURL::redirect(fAuthorization::getRequestedURL(TRUE, '/')); } }
If you are using either authorization levels or access control lists, there are two simple methods that can be used to tell if a user is logged in. To require a user is logged in to view a page you will want to use the requireLoggedIn(), whereas if you want to create conditions based on whether or not the user is logged in you can use the checkLoggedIn() method:
// Require a user is logged in fAuthorization::requireLoggedIn(); // Branch based on the user’s login status if (fAuthorization::checkLoggedIn()) { // Code to execute if the user is logged in }
Sometimes it may be a requirement to only execute code if the remote connection is coming from a specific IP address or range. The method checkIP() provides this functionality. A single parameter $ip_ranges is required, which should like the IP address, CIDR range or IP/subnet-mask combination to check for. It is also possible to pass an array of any valid IP/range descriptors.
// Check for the IP 192.168.1.1 if (fAuthorization::checkIP('192.168.1.1')) { // ... } // Check for the IPs 192.168.1.1-255 if (fAuthorization::checkIP('192.168.1.0/24')) { // ... } // Check for the IPs 192.168.1.1-255 if (fAuthorization::checkIP('192.168.1.0/255.255.255.0')) { // ... } // Check for the IPs 192.168.1.1-255 or 192.168.2.1 if (fAuthorization::checkIP(array('192.168.1.0/24', '192.168.2.1'))) { // ... }
It is also possible to create named IPs/ranges using the method addNamedIPRange(). Simply define the $name and $ip_ranges and then you can use that name with checkIP():
// Create a named IP/range fAuthorization::addNamedIPRange('office', '192.168.1.0/24'); // Check the named IP/range if (fAuthorization::checkIP('office')) { // ... }
When a user logs out, it is important that their authorization level or ACLs are erased. This is quite simple to do using the destroyUserInfo() method:
fAuthorization::destroyUserInfo();
One of the simplest ways for an attacker to gain unauthorized privileges is via session fixation. An attack would be executed by sending a link with a known session ID to a user and then waiting for them to log in. Once the user has logged in, the attacker can send the same session ID and receive the privileges of the user. Below is an example:
http://example.com/login.php?PHPSESSID=abcdef1234567890
By default fSession prevents such an attack by requiring that session IDs be passed in cookies, however it is theoretically possible runtime configuration of the session.use_only_cookies INI setting could be disabled.
To prevent attacks based on knowledge of the user’s session ID, fAuthorization automatically regenerates the session ID via session_regenerate_id() whenever setUserACLs(), setUserAuthLevel() or setUserToken() is called. The attacker won’t know the newly regenerated ID, and won’t be able to access the user’s account.
Allows defining and checking user authentication via ACLs, authorization levels or a simple logged in/not logged in scheme
| 1.0.0b5 | Added getLoginPage() 3/9/10 |
|---|---|
| 1.0.0b4 | Updated class to use new fSession API 10/23/09 |
| 1.0.0b3 | Updated class to use new fSession API 5/8/09 |
| 1.0.0b2 | Fixed a bug with using named IP ranges in checkIP() 1/10/09 |
| 1.0.0b | The initial implementation 6/14/07 |
Adds a named IP address or range, or array of addresses and/or ranges
This method allows checkIP() to be called with a name instead of the actual IPs.
void addNamedIPRange( string $name, mixed $ip_ranges )
| string | $name | The name to give the IP addresses/ranges |
| mixed | $ip_ranges | This can be string (or array of strings) of the IPs or IP ranges to restrict to - please see checkIP() for format details |
Checks to see if the logged in user meets the requirements of the ACL specified
boolean checkACL( string $resource, string $permission )
| string | $resource | The resource we are checking permissions for |
| string | $permission | The permission to require from the user |
If the user has the required permissions
Checks to see if the logged in user has the specified auth level
boolean checkAuthLevel( string $level )
| string | $level | The level to check against the logged in user's level |
If the user has the required auth level
Checks to see if the user is from the IPs or IP ranges specified
The $ip_ranges parameter can be either a single string, or an array of strings, each of which should be in one of the following formats:
boolean checkIP( mixed $ip_ranges )
| mixed | $ip_ranges | A string (or array of strings) of the IPs or IP ranges to restrict to - see method description for details |
If the user is coming from (one of) the IPs or ranges specified
Checks to see if the user has an auth level or ACLs defined
boolean checkLoggedIn( )
If the user is logged in
Destroys the user's auth level and/or ACLs
void destroyUserInfo( )
Returns the login page set via setLoginPage()
string getLoginPage( )
The login page users are redirected to if they don't have the required authorization
Returns the URL requested before the user was redirected to the login page
string getRequestedURL( boolean $clear, string $default_url=NULL )
| boolean | $clear | If the requested url should be cleared from the session after it is retrieved |
| string | $default_url | The default URL to return if the user was not redirected |
The URL that was requested before they were redirected to the login page
Gets the ACLs for the logged in user
array getUserACLs( )
The logged in user's ACLs
Gets the authorization level for the logged in user
string getUserAuthLevel( )
The logged in user's auth level
Gets the value that was set as the user token, NULL if no token has been set
mixed getUserToken( )
The user token that had been set, NULL if none
Redirect the user to the login page if they do not have the permissions required
void requireACL( string $resource, string $permission )
| string | $resource | The resource we are checking permissions for |
| string | $permission | The permission to require from the user |
Redirect the user to the login page if they do not have the auth level required
void requireAuthLevel( string $level )
| string | $level | The level to check against the logged in user's level |
Redirect the user to the login page if they do not have an auth level or ACLs
void requireLoggedIn( )
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Resets the configuration of the class
void reset( )
Sets the authorization levels to use for level checking
void setAuthLevels( array $levels )
| array | $levels | An associative array of (string) {level} => (integer) {value}, for each level |
Sets the login page to redirect users to
void setLoginPage( string $url )
| string | $url | The URL of the login page |
Sets the restricted URL requested by the user
void setRequestedURL( string $url )
| string | $url | The URL to save as the requested URL |
Sets the ACLs for the logged in user.
Array should be formatted like:
array (
(string) {resource name} => array(
(mixed) {permission}, ...
), ...
)
The resource name or the permission may be the single character '*' which acts as a wildcard.
void setUserACLs( array $acls )
| array | $acls | The logged in user's ACLs - see method description for format |
Sets the authorization level for the logged in user
void setUserAuthLevel( string $level )
| string | $level | The logged in user's auth level |
Sets some piece of information to use to identify the current user
void setUserToken( mixed $token )
| mixed | $token | The user's token. This could be a user id, an email address, a user object, etc. |
The fBuffer class is a fairly straight-forward static class designed to make the output buffer functions in PHP a little more user-friendly. Only a single level of buffering is supported, however it has been supplemented with buffer capture support and replace functionality.
Output buffering is essential if you wish to change the headers a page sends after some of the output has been sent, and is also utilized by the fTemplating class for fully buffered output that allows changing values until right before the buffer is sent to the user.
Normally when using the output buffer function you call ob_start() to start output buffering and one of the ob_end_*() methods to stop buffering. The fBuffer class instead uses the start() and stop() methods. Here is an example:
fBuffer::start(); // Execute code that produces output // Send a header // Execute more code that creates output fBuffer::stop();
Gzip compression of the output buffer can be turned (if the zlib extension is installed) by passing TRUE to start().
// Start the output buffer with gzip compression fBuffer::start(TRUE);
You can check to see if buffering has been started by calling isStarted():
if (!fBuffer::isStarted()) { fBuffer::start(); }
To get the current contents of the output buffer, simply call get():
$current_buffer = fBuffer::get();
If you wish to get rid of the buffered contents, call erase():
// Get rid of the current buffer contents fBuffer::erase();
Also sometimes useful is the ability to replace a given string in the buffer with another. In this situation you can use the replace() method:
// Output stuff // This would replace every instance of 'foo' with 'bar' fBuffer::replace('foo', 'bar');
Some of the built-in PHP functions (and other third party code) will only output content, as opposed to returning it for further processing. The fBuffer class provides two methods, startCapture() and stopCapture(), that make it easy to intercept such output.
// Begin capturing, everything passed to print or echo after here will be captured fBuffer::startCapture(); // Execute code to output content // ... // Grab the captured output $captured_content = fBuffer::stopCapture();
Provides a single, simplified interface for output buffering to prevent nested buffering issues and provide a more logical API
| 1.0.0b3 | Added a check to ensure the zlib extension is installd when doing gzipped buffering 5/20/10 |
|---|---|
| 1.0.0b2 | Added the $gzip parameter to start() 5/19/10 |
| 1.0.0b | The initial implementation 3/16/08 |
Erases the output buffer
void erase( )
Returns the contents of output buffer
string get( )
The contents of the output buffer
Checks if buffering has been started
boolean isStarted( )
If buffering has been started
Replaces a value in the output buffer
void replace( string $find, string $replace )
| string | $find | The string to find |
| string | $replace | The string to replace |
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Resets the configuration and buffer of the class
void reset( )
Starts output buffering
void start( boolean $gzip=FALSE )
| boolean | $gzip | If the buffered output should be gzipped using ob_gzhandler() |
Starts capturing output, should be used with stopCapture() to grab output from code that does not offer an option of returning a value instead of outputting it
void startCapture( )
Stops output buffering, flushing everything to the browser
void stop( )
Stops capturing output, returning what was captured
string stopCapture( )
The captured output
The fCRUD class is a static class that provides functionality for the CRUD (create, read, update, delete) pages that power most dynamic websites and online applications. The various methods have been implemented to help reduce the amount of code needed to a standard page, letting the developer focus on the unique aspects of sites.
When filtering the objects to display when executing the list action for a CRUD page, it will often times be required to remember the filtered values even after the user had edited or deleted an object. While it is possible to pass the filter values around in the URL, it leads to lots of extra code and more complex URLs.
As a solution, the getSearchValue() method will always return the last value selected by a user for a given GET or POST parameter. When a value is pulled out of GET or POST data, the value is saved in the user’s session. If the user then leaves the page and comes back without a value, the value will be looked up in the session. This method should be used in place of fRequest::get().
Here is an example of the usage:
// Get the parameter search_terms from GET, POST or the session $search_terms = fCRUD::getSearchValue('search_terms'); // Here $search_terms would be used to filter the objects being displayed // The SetCreator class is not a real class, but used as an example $results = SetCreator::findUsers($search_terms);
In order to provide for accessibility and usability, it is recommended to redirect the user whenever values are pulled from a location other than GET data. This way the user can bookmark results, send a link via email, etc. The redirectWithLoadedValues() method will take the currently requested URL and will redirect the user to a new URL that includes all of the search values (and sorting values) that were pulled from the session. If no values are pulled from the session, no redirection will happen.
Here is the example above with the redirection code added:
// Get the parameter search_terms from GET, POST or the session $search_terms = fCRUD::getSearchValue('search_terms'); // Redirect the user if any values were loaded from the session fCRUD::redirectWithLoadedValues(); // Here $search_terms would be used to filter the objects being displayed // The SetCreator class is not a real class, but used as an example $results = SetCreator::findUsers($search_terms);
Having sortable columns on a CRUD page is often times be a huge usability boost. Unfortunately sortable columns can also be a pain to implement. The fCRUD class provides a few methods to help make it a little easier: getSortColumn(), getSortDirection(), printSortableColumn() and getColumnClass().
The getSortColumn() takes a single parameter, a $possible_columns array, and returns the one specified by the GET value for the parameter sort. The method also will save the last sort column and will reload it from the session if none is specified in the GET data. If neither of these methods can determine the sort column it will default to the first value in the $possible_columns array.
getSortDirection() takes a single parameter, $default_direction, and will return the sorting direction specified in the GET data for the parameter dir. The possible values for $default_direction (and the return value) are 'asc' and 'desc'. If no value is specified in the GET data, it will try to load the last sort direction from the session. If this can not be done the sort direction will default to the values specified in $default_direction.
These two methods work with the redirectWithLoadedValues() method the same way that getSearchValue() does. Here is an example of the three methods being used:
// Set the users to be sortable by name or email, defaulting to name $sort = fCRUD::getSortColumn(array('name', 'email')); // Set the sorting to default to ascending $dir = fCRUD::getSortDirection('asc'); // Redirect the user if one of the values was loaded from the session fCURD::redirectWithLoadedValues(); // Use the sort column and direction in your code to load the objects in the proper order $users = SetCreator::findUsers($sort, $dir);
The second step to getting sortable columns on CRUD pages is to create the links to allow sorting. The printSortableColumn() method accomplishes this task.
printSortableColumn() takes two parameters, with the second one being optional. The first is the $column to make sortable. This value will be returned when calling getSortColumn(). The second, optional, parameter $column_name allows you to specify a display name to be used for $column. If $column_name is not specified, the fGrammar::humanize()ed version of $column will be used instead.
printSortableColumn() prints out an a tag containing a link to the current URL with the current query string, except the sort and dir parameters will be changed to the correct values for the link. If a user clicks a sortable column link that is already sorting the object, the direction will be reversed. In addition, the a tag will have the CSS class sortable_column applied to it. If the link being created is for the column currently being sorted, a second CSS class asc or desc will be added to the a tag as appropriate. Here is an example of the PHP and corresponding HTML:
<table> <tr> <th> fCRUD::printSortableColumn('name') </th> <th> fCRUD::printSortableColumn('email', 'E-Mail') </th> </tr> ... </table>
<!-- The following HTML is presented as if the name column is currently being sorted ascending --> <table> <tr> <th><a href="{current_url}?sort=name&dir=desc" class="sortable_column asc">Name</a></th> <th><a href="{current_url}?sort=email&dir=asc" class="sortable_column">E-Mail</a></th> </tr> ... </table>
Finally, when displaying the object information it might be nice to include a visual indication of which column is sorted on each row. This way if the user has scrolled so the column headers are out of view, they can still remember which column is being sorted. This can be accomplished by using the getColumnClass() method:
<tr> <td class=" fCRUD::getColumnClass('name') "> echo $object->getName() </td> <td class=" fCRUD::getColumnClass('email') "> echo $object->getEmail() </td> </tr>
<!-- The following HTML is presented as if the name column is currently being sorted ascending --> <tr> <td class="sorted">Will</th> <td class="">will@example.com</td> </tr>
If you are using columns from related tables, simple include the table name and a . when passing the column name to printSortableColumn() and getColumnClass(). To sort by the name column in the groups table, just use groups.name.
Since search values and sortable columns are saved in the sessions, whenever a user returns to the page the saved values will be loaded. If you want to allow the user to reset their stored values, simply add a reset parameter (without an = or a value) to the end of the query string. This will cause all stored values to be erased, and the user to be redirected to the same URL without the reset parameter. Here are a couple of examples:
<!-- All sticky search values and sorting information would be erased the the user would be redirected to /users/ --> <a href="/users/?reset">Users</a> <!-- In this case the user would be redirected to /galleries/?gallery_id=3 --> <a href="/galleries/?gallery_id=3&reset">Gallery</a> <!-- This would do nothing since reset is not at the end of the query string --> <a href="/galleries/?reset&gallery_id=3">Gallery</a> <!-- This would do nothing since reset is followed by =. By not allowing an =, we prevent the possibility of conflicting with a real query string parameter called reset. --> <a href="/galleries/?gallery_id=3&reset=">Gallery</a>
A common feature for list tables is to include alternating row colors (via CSS) to allow users to track rows across a table. The getRowClass() method provides functionality to accomplish this in a dynamic way that will also highlight a row that has just been added or updated.
The method takes two parameters, $row_value and $affected_value. When these two values are not equal the method will return 'odd' and 'even' on an alternating basis. When the two parameters are equal the method will return highlighted.
Here is an example of usage:
$user_id = fRequest::get('user_id'); $users = SetCreator::findUsers(); foreach ($users as $user) { <tr class=" echo fCRUD::getRowClass($user->getUserId(), $user_id) "> <td> echo $user->getName() </td> <td> echo $user->getEmail() </td> </tr> }
The output from the above PHP would be:
<!-- The following HTML is presented as if $user_id = 1 and the user id for Will was 1 --> <tr class="odd"> <td>Joe</td> <td>joe@example.com</td> </tr> <tr class="highlighted"> <td>Will</td> <td>will@example.com</td> </tr> <tr class="even"> <td>Zach</td> <td>zach@example.com</td> </tr>
Provides miscellaneous functionality for CRUD-like pages
| 1.0.0b5 | Updated class to use new fSession API 10/23/09 |
|---|---|
| 1.0.0b4 | Updated class to use new fSession API 5/8/09 |
| 1.0.0b3 | Backwards Compatiblity Break - moved printOption() to fHTML::printOption(), showChecked() to fHTMLshowChecked(), removeListItems() and reorderListItems() to fException::splitMessage(), generateRequestToken() to fRequestgenerateCSRFToken(), and validateRequestToken() to fRequest::validateCSRFToken() 5/8/09 |
| 1.0.0b2 | Fixed a bug preventing loaded search values from being included in redirects 3/18/09 |
| 1.0.0b | The initial implementation 6/14/07 |
Return the string 'sorted' if $column is the column that is currently being sorted by, otherwise returns ''
This method will only be useful if used with the other sort methods printSortableColumn(), getSortColumn() and getSortDirection().
string getColumnClass( string $column )
| string | $column | The column to check |
The CSS class for the column, either '' or 'sorted'
Returns a CSS class name for a row
Will return 'even', 'odd', or 'highlighted' if the two parameters are equal and not NULL. The first call to this method will return the appropriate class concatenated with ' first'.
string getRowClass( mixed $row_value=NULL, mixed $affected_value=NULL )
| mixed | $row_value | The value from the row |
| mixed | $affected_value | The value that was just added or updated |
The css class
Gets the current value of a search field
If a value is an empty string and no cast to is specified, the value will become NULL.
If a query string of ?reset is passed, all previous search values will be erased.
mixed getSearchValue( string $column, string $cast_to=NULL, string $default=NULL )
| string | $column | The column that is being pulled back |
| string | $cast_to | The data type to cast to |
| string | $default | The default value |
The current value
Gets the current column to sort by, defaults to first one specified
string getSortColumn( string $possible_column )
string getSortColumn( string .. )
| string | $possible_column | The columns that can be sorted by, defaults to first |
| string | .. | ... |
The column to sort by
Gets the current sort direction
string getSortDirection( string $default_direction )
| string | $default_direction | The default direction, 'asc' or 'desc' |
The direction, 'asc' or 'desc'
Prints a sortable column header a tag
The a tag will include the CSS class 'sortable_column' and the direction being sorted, 'asc' or 'desc'.
fCRUD::printSortableColumn('name', 'Name');
would create the following HTML based on the page context
<!-- If name is the current sort column in the asc direction, the output would be --> <a href="?sort=name&dir=desc" class="sorted_column asc">Name</a> <!-- If name is not the current sort column, the output would be --> <a href="?sort-name&dir=asc" class="sorted_column">Name</a>
void printSortableColumn( string $column, string $column_name=NULL )
| string | $column | The column to create the sortable header for |
| string | $column_name | This will override the humanized version of the column |
Checks to see if any values (search or sort) were loaded from the session, and if so redirects the user to the current URL with those values added
void redirectWithLoadedValues( )
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Resets the configuration and data of the class
void reset( )
The fCache class provides a consistent caching interface that can use file, APC, memcached and XCache back-ends. It can be used cache any end-developer data, but is also integrated with the fDatabase, fSQLTranslation and fSchema classes.
APC and XCache are PHP op-code caches that also provide server-wide shared memory caches for user data. File caches use a single file and store serialized PHP data, while memcached is a standalone caching server that can be scaled extensively across machines.
Creating an instance of fCache just requires a $type and $data_store. The $type can be 'apc', 'file', 'memcache' or 'xcache'. Only 'file' and 'memcache' caches require a $data_store.
For 'file' caches, the $data_store should be a file path. For 'memcache' caches, the $data_store should be a PHP Memcache object. Please note there is also a PHP Memcached (note the d at the end) extension, however it is not currently supported as it is PHP 5.2+ only and is current in beta.
$apc_cache = new fCache('apc'); $file_cache = new fCache('file', '/path/to/cache'); $memcache = new Memcache(); $memcache->connect('localhost', 11211); $memcache_cache = new fCache('memcache', $memcache); $xcache_cache = new fCache('xcache');
The method set() accepts a $key, $value and optional $ttl (time-to-live). The $key should be a string of 250 characters or less, and the $value should be any PHP data type that can be serialized. The main PHP data type that can not be serialized in a resource.
The $key and $value combination will be stored in the cache permanently, unless a $ttl is provided. The $ttl is the number of seconds the cached $value will be accessible. Values with a $ttl will be cleaned up by whatever back-end is providing the cache. Warning: the APC back-end currently functions in such a way that the $ttl will be ignored when getting a value in the same script execution that it is set.
Please note that all PHP values are serialized before being stored in the cache. This ensures that the exact same value that goes into the cache will come back out, even if the back-end only supports basic types such as strings and integers.
// This value will last until explicitly deleted or the cache is cleared $file_cache->set('computed_value', $computed_value); // This value will last for 60 seconds $file_cache->set('other_computed_value', $other_computed_value, 60);
It is also possible to only set a value if the $key does not already exist in the cache. This is performed using the add() method. add() takes the exact same parameters as set(), however it returns a boolean indicating if the value was added.
if ($file_cache->add('master_value', $master_value)) { // Compute a related value and store it also }
The method get() takes the $key to retrieve the value for, and an optional $default to return if the $key is not set. If $default is not specified, NULL will be returned for any $key that is not currently set.
// This will return NULL if there is no value for 'computed_value' $cached_value = $file_cache->get('computed_value'); // This will return 10 if there is no value for 'computed_value' $cached_value = $file_cache->get('computed_value', 10);
Values can be deleted individually from the cache by calling the method delete() and passing the $key to delete.
$file_cache->delete('computed_value');
In addition to deleting specific cache entries, it is also possible to clear all of the entries in the cache. This will delete all key/value pairs in your cache, and depending on your cache type, may affect all other websites on the same server.
Please note that the XCache back-end may require an administrator login and password to clear the cache. This setting, and the login/password settings are controlled by ini settings.
$file_cache->clear();
A simple interface to cache data using different backends
| 1.0.0b | The initial implementation 4/28/09 |
|---|
The data cache, only used for file caches
The array structure is:
array(
(string) {key} => array(
'value' => (mixed) {the key's value},
'expire' => (integer) {the timestamp to expire at, 0 for none}
)
)
array
The data store to use - the file path for a file cache, Memcache object for memcache
mixed
The data state, only used for file caches
The valid values are:
string
The type of cache
The valid values are:
string
Set the type and master key for the cache
A file cache uses a single file to store values in an associative array and is probably not suitable for a large number of keys.
Using an apc or xcache cache will have far better performance than a file or directory, however please remember that keys are shared server-wide.
fCache __construct( string $type, mixed $data_store=NULL )
| string | $type | The type of caching to use: 'apc', 'file', 'memcache', 'xcache' |
| mixed | $data_store | The path for a file cache, or an Memcache object for a memcache cache - not used for apc or xcache |
Please note: this method is public, however it is primarily intended for internal use by Flourish and will normally not be useful in site/application code
Cleans up after the cache object
void __destruct( )
Tries to set a value to the cache, but stops if a value already exists
boolean add( string $key, mixed $value, integer $ttl=0 )
| string | $key | The key to store as, this should not exceed 250 characters |
| mixed | $value | The value to store, this will be serialized |
| integer | $ttl | The number of seconds to keep the cache valid for, 0 for no limit |
If the key/value pair were added successfully
Clears the WHOLE cache of every key, use with caution!
xcache may require a login or password depending on your ini settings.
void clear( )
Deletes a value from the cache
void delete( string $key )
| string | $key | The key to delete |
Returns a value from the cache
mixed get( string $key, mixed $default=NULL )
| string | $key | The key to return the value for |
| mixed | $default | The value to return if the key did not exist |
The cached value or the default value if no cached value was found
Only valid for file caches, saves the file to disk and will randomly clean up expired values
void save( )
Sets a value to the cache, overriding any previous value
void set( string $key, mixed $value, integer $ttl=0 )
| string | $key | The key to store as, this should not exceed 250 characters |
| mixed | $value | The value to store, this will be serialized |
| integer | $ttl | The number of seconds to keep the cache valid for, 0 for no limit |
fConnectivityException is a sub-class of fUnexpectedException that indicates some sort of unrecoverable connection error has occurred. This could be used in situations where a database server is not reachable, a remote API could not be contacted, or an FTP connection timed out.
This space intentionally left blank
An exception caused by a connectivity error
| 1.0.0b | The initial implementation 6/14/07 |
|---|
Exception | --fException | --fUnexpectedException | --fConnectivityException