Flourish PHP Unframework

This document contains all of the class and API documentation, plus selected general documentation for Flourish v0.9.0. 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.

Class Documentation

Database

ORM

Filesystem

Request

Session

Response

Utilities

Email

Date/Time

Numbers/Strings

Errors/Exceptions

General Documentation

fActiveRecord

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. Please be sure to read over it since it include important information, such as the fact that column and table names must use underscore_notation by default.

Setup

In order to use the fActiveRecord class, a database table must exist to be modeled. Below is an example users tableplease 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 sites 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.

Custom Class to Table Mapping

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');

Modeling Tables in Other Schemas

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');

Using Multiple Databases

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.

Creating and Loading Records

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.

Column Operations

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 columns 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.

Date, Time and Timestamp Columns

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');

Float/Decimal Columns

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);

String Columns

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);

All Columns

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');

Column Values and Objects

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());

Record Operations

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 records 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

store()

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.

validate()

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.

What is Validated

The following rules are used to determine if a record is valid to store in the database:

  1. All data must be compatible with the data type of the column it is being stored in
  2. If a column has a NOT NULL constraint and does not have a DEFAULT value, a value other than an empty string must be set
  3. If a value has a FOREIGN KEY constraint, the value must reference a valid value
  4. If a column has a UNIQUE or PRIMARY KEY contraint, the value must be NULL or unique
  5. If a column has a CHECK constraint with an IN (...) expression, the value must be in the list - for MySQL this holds true for ENUM columns
  6. If a column is a VARCHAR or CHAR column, the value string length must be less than or equal to the size of the column

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.

Usage

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]'      => array(
        'name'   => 'Favorite #1',
        'errors' => array(
            'name' => 'Name: Please enter a value'
        )
    ),
    'favorites[2]'      => array(
        'name'   => 'Favorite #3',
        'errors' => array(
            'name' => 'Name: Please enter a value'
        )
    )
);

If you are interested in changing the name of a one-to-many child record, please see the fORMRelated section Overriding Child Record Validation Names.

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]'      => array(
        'name'   => 'Favorite #1',
        'errors' => array(
            'name' => 'Please enter a value'
        )
    ),
    'favorites[2]'      => array(
        'name'   => 'Favorite #3',
        'errors' => array(
            'name' => 'Please enter a value'
        )
    )
);

delete()

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.

load()

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();

populate()

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();
}
Blank Strings

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.

Checkboxes

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.

replicate() / clone

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}');

exists()

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.

Warning

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.

reflect()

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);

Related Records Operations

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:

*-to-one Relationships

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 related User object if one exists, or an empty User object if none exists.

$resource = new Resource(1);
$user = $resource->createUser();

See Method Naming for info about create and other method verbs.

One-to-one Relationships

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()) {
    // 
}

*-to-many Relationships

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.

Building, Listing, Counting and Has

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 dont 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.

Associating

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.

Populating

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 objects 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();
}

Relationship Routes

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>

Configuration

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 classs 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

fActiveRecord API Reference

abstract class, v1.0.0b81

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.

Changes:
1.0.0b81Fixed a bug with updating a record that contains only an auto-incrementing primary key 9/6/11
1.0.0b80Added support to checkCondition() for the ^~ and $~ operators 6/20/11
1.0.0b79Fixed some bugs in handling relationships between PHP 5.3 namespaced classes 5/26/11
1.0.0b78Backwards Compatibility Break - reflect() now returns an associative array instead of a string 5/10/11
1.0.0b77Fixed inspect() to not throw an fProgrammerException when a valid element has a NULL value 5/10/11
1.0.0b76Added clearIdentityMap() 5/9/11
1.0.0b75Fixed a bug where child records of a record with a non-auto-incrementing primary key would not be saved properly for a new record 12/6/10
1.0.0b74Updated populate() to use the binary type for fRequest::get() 11/30/10
1.0.0b73Backwards Compatibility Break - changed column set methods to treat strings of all whitespace the same as empty string and convert them to NULL 11/29/10
1.0.0b72Added the new comment element to the reflection signature for inspect methods 11/28/10
1.0.0b71Updated class to use fORM::getRelatedClass() 11/24/10
1.0.0b70Added support for PHP 5.3 namespaced fActiveRecord classes 11/11/10
1.0.0b69Backwards Compatibility Break - changed validate() to return a nested array of validation messages when there are validation errors on child records 10/3/10
1.0.0b68Added hooks to replicate() 9/7/10
1.0.0b67Updated code to work with the new fORM API 8/6/10
1.0.0b66Fixed a bug with store() and non-primary key auto-incrementing columns 7/5/10
1.0.0b65Fixed 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.0b64BackwardsCompatibilityBreak - 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.0b63Changed how is_subclass_of() is used to work around a bug in 5.2.x 4/6/10
1.0.0b62Fixed a bug that could cause infinite recursion starting in v1.0.0b60 4/2/10
1.0.0b61Fixed issues with handling populate actions when working with mapped classes 3/31/10
1.0.0b60Fixed issues with handling associate and has actions when working with mapped classes, added validateClass() 3/30/10
1.0.0b59Changed an extended UTF-8 arrow character into the correct -> 3/29/10
1.0.0b58Fixed reflect() to specify the value returned from set methods 3/15/10
1.0.0b57Added the post::loadFromIdentityMap() hook and fixed __construct() to always call the post::__construct() hook 3/14/10
1.0.0b56Fixed $force_cascade in delete() to work even when the restricted relationship is once-removed through an unrestricted relationship 3/9/10
1.0.0b55Fixed load() to that related records are cleared, requiring them to be loaded from the database 3/4/10
1.0.0b54Fixed detection of route name for one-to-one relationships in delete() 3/3/10
1.0.0b53Fixed 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.0b52Backwards 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.0b51Made changed() properly recognize that a blank string and NULL are equivalent due to the way that set() casts values 11/14/09
1.0.0b50Fixed 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.0b49Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b48Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b47Changed ::associate{RelatedRecords}(), ::link{RelatedRecords}() and ::populate{RelatedRecords}() to allow for method chaining 10/22/09
1.0.0b46Changed SQL statements to use value placeholders and identifier escaping 10/22/09
1.0.0b45Added support for !~, &~, >< and OR comparisons to checkConditions(), made object handling in checkConditions() more robust 9/21/09
1.0.0b44Updated code for new fValidationException API 9/18/09
1.0.0b43Updated code for new fRecordSet API 9/16/09
1.0.0b42Corrected a grammar bug in hash() 9/9/09
1.0.0b41Fixed a bug in the last version that would cause issues with classes containing a custom class to table mapping 9/1/09
1.0.0b40Added a check to the configuration part of __construct() to ensure modelled tables have primary keys 8/26/09
1.0.0b39Changed 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.0b38Updated changed() to do a strict comparison when at least one value is NULL 8/17/09
1.0.0b37Changed __construct() to allow any Iterator object instead of just fResult 8/12/09
1.0.0b36Fixed a bug with setting NULL values from v1.0.0b33 8/10/09
1.0.0b35Fixed a bug with unescaping data in loadFromResult() from v1.0.0b33 8/10/09
1.0.0b34Added the ability to compare fActiveRecord objects in checkConditions() 8/7/09
1.0.0b33Performance enhancements to __call() and __construct() 8/7/09
1.0.0b32Changed delete() to remove auto-incrementing primary keys after the post::delete() hook 7/29/09
1.0.0b31Fixed a bug with loading a record by a multi-column primary key, fixed one-to-one relationship API 7/21/09
1.0.0b30Updated reflect() for new fORM::callReflectCallbacks() API 7/13/09
1.0.0b29Updated to use new fORM::callInspectCallbacks() method 7/13/09
1.0.0b28Fixed a bug where records would break the identity map at the end of store() 7/9/09
1.0.0b27Changed 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.0b26Added checkConditions() from fRecordSet 7/8/09
1.0.0b25Updated validate() to use new fORMValidation API, including new message search/replace functionality 7/1/09
1.0.0b24Changed validate() to remove duplicate validation messages 6/30/09
1.0.0b23Updated code for new fORMValidation::validateRelated() API 6/26/09
1.0.0b22Added support for the $formatting parameter to encode methods on char, text and varchar columns 6/19/09
1.0.0b21Performance tweaks and updates for fORM and fORMRelated API changes 6/15/09
1.0.0b20Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b19Added list{RelatedRecords}() methods, updated code for new fORMRelated API 6/2/09
1.0.0b18Changed store() to use new fORMRelated::store() method 6/2/09
1.0.0b17Added some missing parameter information to reflect() 6/1/09
1.0.0b16Fixed bugs in __clone() and replicate() related to recursive relationships 5/20/09
1.0.0b15Fixed an incorrect variable reference in store() 5/6/09
1.0.0b14store() no longer tries to get an auto-incrementing ID from the database if a value was set 5/2/09
1.0.0b13delete(), load(), populate() and store() now return the record to allow for method chaining 3/23/09
1.0.0b12set() now removes commas from integers and floats to prevent validation issues 3/22/09
1.0.0b11encode() 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.0b9Changed __construct() to populate database default values when a non-existing record is instantiated 1/12/09
1.0.0b8Fixed exists() to properly detect cases when an existing record has one or more NULL values in the primary key 1/11/09
1.0.0b7Fixed __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.0b5Fixed replicate() to use plural record names for related records 12/12/08
1.0.0b4Added replicate() to allow cloning along with related records 12/12/08
1.0.0b3Changed __clone() to clone objects contains in the values and cache arrays 12/11/08
1.0.0b2Added the __clone() method to properly duplicate a record 12/4/08
1.0.0bThe initial implementation 8/4/07

Static Variables

::$callback_cache protected

Caches callbacks for methods

Type

array

::$configured protected

An array of flags indicating a class has been configured

Type

array

::$identity_map protected

Maps objects via their primary key

Type

array

::$method_name_cache protected

Caches method name parsings

Type

array

::$replicate_level protected

Keeps track of the recursive call level of replication so we can clear the map

Type

integer

::$replicate_map protected

Keeps a list of records that have been replicated

Type

array

::$unescape_map protected

Contains a list of what columns in each class need to be unescaped and what data type they are

Type

array

Variables

->cache protected

A data store for caching data related to a record, the structure of this is completely up to the developer using it

Type

array

->old_values protected

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.

Type

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.

Type

array

->values protected

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.

Type

array

Static Methods

::assign() internal public

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

Signature

void assign( array &$values, array &$old_values, string $column, mixed $value )

Parameters
array &$values The current values
array &$old_values The old values
string $column The column to set
mixed $value The value to set
::changed() internal public

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

Signature

boolean changed( array &$values, array &$old_values, string $column )

Parameters
array &$values The current values
array &$old_values The old values
string $column The column to check
Returns

If the value for the column specified has changed

::checkClass() internal public

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

Signature

boolean checkClass( string $class )

Parameters
string $class The class to check
Returns

If the class is an fActiveRecord descendant

::checkConditions() internal public

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

Signature

boolean checkConditions( fActiveRecord $record, array $conditions )

Parameters
fActiveRecord $record The record to check
array $conditions The conditions to check - see fRecordSet::filter() for format details
Returns

If the record meets all conditions

::clearIdentityMap() public

Clears the identity map

Signature

void clearIdentityMap( )

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::forceConfigure() internal public

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

Signature

void forceConfigure( string $class )

Parameters
string $class The class to configure
::hash() internal public

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

Signature

string|NULL hash( fActiveRecord|array|string|int $record, string $class=NULL )

Parameters
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
Returns

A hash of the record's primary key value or NULL if the record doesn't exist yet

::hasOld() internal public

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

Signature

boolean hasOld( array &$old_values, string $column )

Parameters
array &$old_values The old values
string $column The column to set
Returns

If an old value for that column exists

::reset() internal public

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

Signature

void reset( )

::retrieveOld() internal public

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

Signature

mixed retrieveOld( array &$old_values, string $column, mixed $default=NULL, boolean $return_all=FALSE )

Parameters
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
Returns

The old value for the column

::validateClass() internal public

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

Signature

void validateClass( string $class )

Parameters
string $class The class to verify

Methods

->__construct() public

Creates a new record or loads one from the database - if a primary key or unique key is provided the record will be loaded

Signature

fActiveRecord __construct( mixed $key=NULL )

Parameters
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}
Throws
fNotFoundException
When the record specified by $key can not be found in the database
->__call() public

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.

Signature

mixed __call( string $method_name, array $parameters )

Parameters
string $method_name The name of the method called
array $parameters The parameters passed
Returns

The value returned by the method called

->__clone() internal public

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.

Signature

fActiveRecord __clone( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__wakeup() internal public

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.

Signature

void __wakeup( )

->configure() protected

Allows the programmer to set features for the class

This method is only called once per page load for each class.

Signature

void configure( )

->constructInsertParams() protected

Creates the fDatabase::translatedQuery() insert statement params

Signature

array constructInsertParams( )

Returns

The parameters for an fDatabase::translatedQuery() SQL insert statement

->constructUpdateParams() protected

Creates the fDatabase::translatedQuery() update statement params

Signature

array constructUpdateParams( )

Returns

The parameters for an fDatabase::translatedQuery() SQL update statement

->delete() public

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.

Signature

fActiveRecord delete( boolean $force_cascade=FALSE )

Parameters
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
Returns

The record object, to allow for method chaining

->encode() protected

Retrieves a value from the record and prepares it for output into an HTML form element.

Below are the transformations performed:

  • varchar, char, text: will run through fHTML::encode(), if TRUE is passed the text will be run through fHTMLconvertNewLinks() and fHTML::makeLinks()
  • float: takes 1 parameter to specify the number of decimal places
  • date, time, timestamp: format() will be called on the fDate/fTime/fTimestamp object with the 1 parameter specified
  • objects: the object will be converted to a string by __toString() or a (string) cast and then will be run through fHTML::encode()
  • all other data types: the value will be run through fHTML::encode()
Signature

string encode( string $column, string $formatting=NULL )

Parameters
string $column The name of the column to retrieve
string $formatting The formatting string
Returns

The encoded value for the column specified

->exists() public

Checks to see if the record exists in the database

Signature

boolean exists( )

Returns

If the record exists in the database

->fetchResultFromUniqueKey() protected

Loads a record from the database based on a UNIQUE key

Signature

void fetchResultFromUniqueKey( array $values )

Parameters
array $values The UNIQUE key values to try and load with
Throws
fNotFoundException
->get() protected

Retrieves a value from the record

Signature

mixed get( string $column )

Parameters
string $column The name of the column to retrieve
Returns

The value for the column specified

->inspect() protected

Retrieves information about a column

Signature

mixed inspect( string $column, string $element=NULL )

Parameters
string $column The name of the column to inspect
string $element The metadata element to retrieve
Returns

The metadata array for the column, or the metadata element specified

->load() public

Loads a record from the database

Signature

fActiveRecord load( )

Returns

The record object, to allow for method chaining

Throws
fNotFoundException
When the record could not be found in the database
->loadFromIdentityMap() protected

Tries to load the object (via references to class vars) from the fORM identity map

Signature

boolean loadFromIdentityMap( array $row, string $hash )

Parameters
array $row The data source for the primary key values
string $hash The unique hash for this record
Returns

If the load was successful

->loadFromResult() protected

Loads a record from the database directly from a result object

Signature

boolean loadFromResult( Iterator $result, boolean $ignore_identity_map=FALSE )

Parameters
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
Returns

If the record was loaded from the identity map

->populate() public

Sets the values for this record by getting values from the request through the fRequest class

Signature

fActiveRecord populate( )

Returns

The record object, to allow for method chaining

->prepare() protected

Retrieves a value from the record and prepares it for output into html.

Below are the transformations performed:

  • varchar, char, text: will run through fHTML::prepare(), if TRUE is passed the text will be run through fHTMLconvertNewLinks() and fHTML::makeLinks()
  • boolean: will return 'Yes' or 'No'
  • integer: will add thousands/millions/etc. separators
  • float: will add thousands/millions/etc. separators and takes 1 parameter to specify the number of decimal places
  • date, time, timestamp: format() will be called on the fDate/fTime/fTimestamp object with the 1 parameter specified
  • objects: the object will be converted to a string by __toString() or a (string) cast and then will be run through fHTML::prepare()
Signature

string prepare( string $column, mixed $formatting=NULL )

Parameters
string $column The name of the column to retrieve
mixed $formatting The formatting parameter, if applicable
Returns

The formatted value for the column specified

->reflect() public

Generates the method signatures for all methods (including dynamic ones)

Signature

array reflect( boolean $include_doc_comments=FALSE )

Parameters
boolean $include_doc_comments If the doc block comments for each method should be included
Returns

An associative array of method name => method signature

->replicate() public

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:

  • No parameters: this object will be cloned
  • A single TRUE value: this object plus all many-to-many associations and all child records (recursively) will be cloned
  • Any number of plural related record class names: the many-to-many associations or child records that correspond to the classes specified will be cloned

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}'.

Signature

fActiveRecord replicate( string $related_class=NULL [, ... ] )

Parameters
string $related_class [, ... ] The plural related class to replicate - see method description for details
Returns

The cloned record

->set() protected

Sets a value to the record

Signature

fActiveRecord set( string $column, mixed $value )

Parameters
string $column The column to set the value to
mixed $value The value to set
Returns

This record, to allow for method chaining

->store() public

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.

Signature

fActiveRecord store( boolean $force_cascade=FALSE )

Parameters
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
Returns

The record object, to allow for method chaining

Throws
fValidationException
When validate() throws an exception
->validate() public

Validates the values of the record against the database and any additional validation rules

Signature

void|array validate( boolean $return_messages=FALSE, boolean $remove_column_names=FALSE )

Parameters
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
Returns

If $return_messages is TRUE, an array of validation messages will be returned

Throws
fValidationException
When the record, or one of the associated records, violates one of the validation rules for the class or can not be properly stored in the database

fAuthorization

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).

Setup

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());

Authorization Levels

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
    )
);

Youll 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 users 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 users authorization level, that can be accomplished by calling getUserAuthLevel():

$auth_level = fAuthorization::getUserAuthLevel();

Access Control Lists

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 users 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 users 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();

Logging In

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, '/'));
    }
}

Checking Status

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 users login status
if (fAuthorization::checkLoggedIn()) {
    // Code to execute if the user is logged in
}

Remote IP Checking

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')) {
    // ...
}

Logging Out

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();

Preventing Privilege Escalation (Security)

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 users session ID, fAuthorization automatically regenerates the session ID via session_regenerate_id() whenever setUserACLs(), setUserAuthLevel() or setUserToken() is called. The attacker wont know the newly regenerated ID, and wont be able to access the users account.

fAuthorization API Reference

static class, v1.0.0b6

Allows defining and checking user authentication via ACLs, authorization levels or a simple logged in/not logged in scheme

Changes:
1.0.0b6Fixed checkIP() to not trigger a notice when $_SERVER['REMOTE_ADDR'] is not set 5/10/11
1.0.0b5Added getLoginPage() 3/9/10
1.0.0b4Updated class to use new fSession API 10/23/09
1.0.0b3Updated class to use new fSession API 5/8/09
1.0.0b2Fixed a bug with using named IP ranges in checkIP() 1/10/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::addNamedIPRange() public

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.

Signature

void addNamedIPRange( string $name, mixed $ip_ranges )

Parameters
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
::checkACL() public

Checks to see if the logged in user meets the requirements of the ACL specified

Signature

boolean checkACL( string $resource, string $permission )

Parameters
string $resource The resource we are checking permissions for
string $permission The permission to require from the user
Returns

If the user has the required permissions

::checkAuthLevel() public

Checks to see if the logged in user has the specified auth level

Signature

boolean checkAuthLevel( string $level )

Parameters
string $level The level to check against the logged in user's level
Returns

If the user has the required auth level

::checkIP() public

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:

  • A single IP address:
    • 192.168.1.1
    • 208.77.188.166
  • A CIDR range
    • 192.168.1.0/24
    • 208.77.188.160/28
  • An IP/subnet mask combination
    • 192.168.1.0/255.255.255.0
    • 208.77.188.160/255.255.255.240
Signature

boolean checkIP( mixed $ip_ranges )

Parameters
mixed $ip_ranges A string (or array of strings) of the IPs or IP ranges to restrict to - see method description for details
Returns

If the user is coming from (one of) the IPs or ranges specified

::checkLoggedIn() public

Checks to see if the user has an auth level or ACLs defined

Signature

boolean checkLoggedIn( )

Returns

If the user is logged in

::destroyUserInfo() public

Destroys the user's auth level and/or ACLs

Signature

void destroyUserInfo( )

::getLoginPage() public

Returns the login page set via setLoginPage()

Signature

string getLoginPage( )

Returns

The login page users are redirected to if they don't have the required authorization

::getRequestedURL() public

Returns the URL requested before the user was redirected to the login page

Signature

string getRequestedURL( boolean $clear, string $default_url=NULL )

Parameters
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
Returns

The URL that was requested before they were redirected to the login page

::getUserACLs() public

Gets the ACLs for the logged in user

Signature

array getUserACLs( )

Returns

The logged in user's ACLs

::getUserAuthLevel() public

Gets the authorization level for the logged in user

Signature

string getUserAuthLevel( )

Returns

The logged in user's auth level

::getUserToken() public

Gets the value that was set as the user token, NULL if no token has been set

Signature

mixed getUserToken( )

Returns

The user token that had been set, NULL if none

::requireACL() public

Redirect the user to the login page if they do not have the permissions required

Signature

void requireACL( string $resource, string $permission )

Parameters
string $resource The resource we are checking permissions for
string $permission The permission to require from the user
::requireAuthLevel() public

Redirect the user to the login page if they do not have the auth level required

Signature

void requireAuthLevel( string $level )

Parameters
string $level The level to check against the logged in user's level
::requireLoggedIn() public

Redirect the user to the login page if they do not have an auth level or ACLs

Signature

void requireLoggedIn( )

::reset() internal public

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

Signature

void reset( )

::setAuthLevels() public

Sets the authorization levels to use for level checking

Signature

void setAuthLevels( array $levels )

Parameters
array $levels An associative array of (string) {level} => (integer) {value}, for each level
::setLoginPage() public

Sets the login page to redirect users to

Signature

void setLoginPage( string $url )

Parameters
string $url The URL of the login page
::setRequestedURL() public

Sets the restricted URL requested by the user

Signature

void setRequestedURL( string $url )

Parameters
string $url The URL to save as the requested URL
::setUserACLs() public

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.

Signature

void setUserACLs( array $acls )

Parameters
array $acls The logged in user's ACLs - see method description for format
::setUserAuthLevel() public

Sets the authorization level for the logged in user

Signature

void setUserAuthLevel( string $level )

Parameters
string $level The logged in user's auth level
::setUserToken() public

Sets some piece of information to use to identify the current user

Signature

void setUserToken( mixed $token )

Parameters
mixed $token The user's token. This could be a user id, an email address, a user object, etc.

fAuthorizationException

fAuthorizationException is a sub-class of fExpectedException that indicates some sort of authorization error has occurred.

This space intentionally left blank

fAuthorizationException API Reference

class, v1.0.0b

An exception caused by an authorization error

Changes:
1.0.0bThe initial implementation 5/9/11

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fAuthorizationException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fBuffer

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.

Starting and Stopping

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();
}

Getting, Erasing and Replacing

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');

Capturing

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();

fBuffer API Reference

static class, v1.0.0b3

Provides a single, simplified interface for output buffering to prevent nested buffering issues and provide a more logical API

Changes:
1.0.0b3Added a check to ensure the zlib extension is installd when doing gzipped buffering 5/20/10
1.0.0b2Added the $gzip parameter to start() 5/19/10
1.0.0bThe initial implementation 3/16/08

Static Methods

::erase() public

Erases the output buffer

Signature

void erase( )

::get() public

Returns the contents of output buffer

Signature

string get( )

Returns

The contents of the output buffer

::isStarted() public

Checks if buffering has been started

Signature

boolean isStarted( )

Returns

If buffering has been started

::replace() public

Replaces a value in the output buffer

Signature

void replace( string $find, string $replace )

Parameters
string $find The string to find
string $replace The string to replace
::reset() internal public

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

Signature

void reset( )

::start() public

Starts output buffering

Signature

void start( boolean $gzip=FALSE )

Parameters
boolean $gzip If the buffered output should be gzipped using ob_gzhandler()
::startCapture() public

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

Signature

void startCapture( )

::stop() public

Stops output buffering, flushing everything to the browser

Signature

void stop( )

::stopCapture() public

Stops capturing output, returning what was captured

Signature

string stopCapture( )

Returns

The captured output

fCRUD

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.

Sticky Search Values

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 users 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);

Sortable Columns (Sticky)

Having sortable columns on a CRUD page is often 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
fCRUD::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&amp;dir=desc" class="sortable_column asc">Name</a></th>
        <th><a href="{current_url}?sort=email&amp;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') ?>"><?php echo $object->getName() ?></td>
    <td class="<? fCRUD::getColumnClass('email') ?>"><?php 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.

Resetting Sticky Values

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>

Row Colors

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="<?php echo fCRUD::getRowClass($user->getUserId(), $user_id) ?>">
        <td><?php echo $user->getName() ?></td>
        <td><?php 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>

fCRUD API Reference

static class, v1.0.0b5

Provides miscellaneous functionality for CRUD-like pages

Changes:
1.0.0b5Updated class to use new fSession API 10/23/09
1.0.0b4Updated class to use new fSession API 5/8/09
1.0.0b3Backwards 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.0b2Fixed a bug preventing loaded search values from being included in redirects 3/18/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::getColumnClass() public

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().

Signature

string getColumnClass( string $column )

Parameters
string $column The column to check
Returns

The CSS class for the column, either '' or 'sorted'

::getRowClass() public

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'.

Signature

string getRowClass( mixed $row_value=NULL, mixed $affected_value=NULL )

Parameters
mixed $row_value The value from the row
mixed $affected_value The value that was just added or updated
Returns

The css class

::getSearchValue() public

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.

Signature

mixed getSearchValue( string $column, string $cast_to=NULL, string $default=NULL )

Parameters
string $column The column that is being pulled back
string $cast_to The data type to cast to
string $default The default value
Returns

The current value

::getSortColumn() public

Gets the current column to sort by, defaults to first one specified

Signature

string getSortColumn( string $possible_column [, ... ] )

Parameters
string $possible_column [, ... ] The columns that can be sorted by, defaults to first
Returns

The column to sort by

::getSortDirection() public

Gets the current sort direction

Signature

string getSortDirection( string $default_direction )

Parameters
string $default_direction The default direction, 'asc' or 'desc'
Returns

The direction, 'asc' or 'desc'

::printSortableColumn() public

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


Name

Name
Signature

void printSortableColumn( string $column, string $column_name=NULL )

Parameters
string $column The column to create the sortable header for
string $column_name This will override the humanized version of the column
::redirectWithLoadedValues() public

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

Signature

void redirectWithLoadedValues( )

::reset() internal public

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

Signature

void reset( )

fCache

The fCache class provides a consistent caching interface that can use the APC, database, directory, file, Memcache, Redis or XCache backends. It can be used to cache any end-developer data, but can also be used by the fDatabase, fSQLTranslation and fSchema classes.

Instantiation

Creating an instance of fCache requires a $type, and possibly a $data_store and $options. The following types are supported. The $data_store and $config parameters are explained for each backend separately.

APC Backend

Using APC for the backend only requires the $type parameter be set to 'apc'. The $data_store and $config parameters are unused.

$cache = new fCache('apc');

APC is best suited for smaller values where performance is of utmost concern.

Please note that APC shares values server-wide, so cache keys should be appropriately unique in case there are multiple sites being hosted on the same server.

Database Backend

Using a database for the backend requires the $type parameter be set to 'database' and the $data_store be an fDatabase object. The $config parameter must be an array with the following keys:

$db = new fDatabase('mysql', 'mydb', 'username', 'password');
$config = array(
    'table'           => 'sessions',
    'key_column'      => 'session_id',
    'value_column'    => 'values',
    'value_data_type' => 'string',
    'ttl_column'      => 'expiration'
);
$cache = new fCache('database', $db, $config);

Each value is stored in a separate row in the table. A database backend is useful for caching values that need to be shared across servers.

Directory Backend

Using a directory for the backend requires the $type parameter be set to 'directory' and the $data_store be the path to a directory that is writable. The $config parameter is unused.

$cache = new fCache('directory', '/path/to/dir');

A directory backend is generally useful for caching larger values on a single server.

Each value in a directory cache is stored in a separate file, with the filenames being hashes of the cache key. The first line of the file is the integer unix timestamp of when the value expires. All subsequent lines in the file are part of the value. An expiration timestamp of 0 indicates no expiration.

File Backend

For file caches, the $type should be 'file' and the $data_store should be a file path. The $config parameter is unused.

$cache = new fCache('file', '/path/to/cache');

A file backend is most useful for simple single-server caching that requires no extra hosting infrastructure or uncommon PHP extensions.

All values are stored in a serialized array containing the keys, values and expiration timestamps.

Memcache Backend

For memcache caches, the $type should be set to 'memcache' and the $data_store should be a Memcache or Memcached object. The $config parameter is unused.

// Using the memcache extension
$memcache = new Memcache();
$memcache->connect('localhost', 11211);
$cache = new fCache('memcache', $memcache);

// Using the memcached extension
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$cache = new fCache('memcache', $memcached);

A memcache backend is useful for caching all sorts of different values, often for larger sites or sites with multiple web servers.

Redis Backend

For Redis caches, the $type should be set to 'redis' and the $data_store should be an instance of the Redis class from the phpredis extension. The $config parameter is unused.

$redis = new Redis();
$redis->connect('localhost');
$cache = new fCache('redis', $redis);

A redis backend is useful for caching all sorts of different values, often for larger sites or sites with multiple web servers.

XCache Backend

For XCache caches, the $type should be set to 'xcache'. The $data_store and $config parameters are unused.

$cache = new fCache('xcache');

XCache is best suited for smaller values where performance is of utmost concern.

Please note that XCache shares values server-wide, so cache keys should be appropriately unique in case there are multiple sites being hosted on the same server.

Setting Values

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
$cache->set('computed_value', $computed_value);

// This value will last for 60 seconds
$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 ($cache->add('master_value', $master_value)) {
    // Compute a related value and store it also
}

Getting Values

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 = $cache->get('computed_value');

// This will return 10 if there is no value for 'computed_value'
$cached_value = $cache->get('computed_value', 10);

Deleting Values

Values can be deleted individually from the cache by calling the method delete() and passing the $key to delete.

$cache->delete('computed_value');

Clearing the Cache

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 or all web servers.

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.

$cache->clear();

fCache API Reference

class, v1.0.0b6

A simple interface to cache data using different backends

Changes:
1.0.0b6Fixed a bug with add() setting a value when it shouldn't if no ttl was given for the file backend 1/12/12
1.0.0b5Added missing documentation for using Redis as a backend 8/25/11
1.0.0b4Added the database, directory and redis types, added support for the memcached extention and support for custom serialization callbacks 6/21/11
1.0.0b3Added 0 to the memcache delete method call since otherwise the method triggers notices on some installs 5/10/11
1.0.0b2Fixed API calls to the memcache extension to pass the TTL as the correct parameter 2/1/11
1.0.0bThe initial implementation 4/28/09

Variables

->config protected

The cache configuration, used for database, directory and file caches

The array structure for database caches is:

array(
    'table'        => (string) {the database table to use},
    'key_column'   => (string) {the varchar column to store the key in, should be able to handle at least 250 characters},
    'value_column' => (string) {the text/varchar column to store the value in},
    'ttl_column'   => (string) {the integer column to store the expiration time in}
)

The array structure for directory caches:

array(
    'path' => (string) {the directory path with trailing slash}
)

The array structure for file caches:

array(
    'path'  => (string) {the file path},
    'state' => (string) {clean or dirty, used to appropriately save}
)
Type

array

->data_store protected

The data store to use

Either the:

  • array structure for file cache
  • Memcache or Memcached object for memcache
  • fDatabase object for database
  • Redis object for redis

Not used for apc, directory or xcache

Type

mixed

->type protected

The type of cache

The valid values are:

  • 'apc'
  • 'database'
  • 'directory'
  • 'file'
  • 'memcache'
  • 'redis'
  • 'xcache'
Type

string

Methods

->__construct() public

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.

$config is an associative array of configuration options for the various backends. Some backend require additional configuration, while others provide provide optional settings.

The following $config options must be set for the database backend:

  • table: The database table to use for caching
  • key_column: The column to store the cache key in - must support at least 250 character strings
  • value_column: The column to store the serialized value in - this should probably be a TEXT column to support large values, or BLOB if binary serialization is used
  • value_data_type: If a BLOB column is being used for the value_column, this should be set to 'blob', otherwise string
  • ttl_column: The column to store the expiration timestamp of the cached entry - this should be an integer

The following $config for the following items can be set for all backends:

  • serializer: A callback to serialize data with, defaults to the PHP function serialize()
  • unserializer: A callback to unserialize data with, defaults to the PHP function unserialize()

Common serialization callbacks include:

  • json_encode/json_decode
  • igbinary_serialize/igbinary_unserialize

Please note that using JSON for serialization will exclude all non-public properties of objects from being serialized.

A custom serialize and unserialze option is string, which will cast all values to a string when storing, instead of serializing them. If a __toString() method is provided for objects, it will be called.

Signature

fCache __construct( string $type, mixed $data_store=NULL, array $config=array() )

Parameters
string $type The type of caching to use: 'apc', 'database', 'directory', 'file', 'memcache', 'redis', 'xcache'
mixed $data_store The path for a file or directory cache, an Memcache or Memcached object for a memcache cache, an fDatabase object for a database cache or a Redis object for a redis cache - not used for apc or xcache
array $config Configuration options - see method description for details
->__destruct() internal public

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

Signature

void __destruct( )

->add() public

Tries to set a value to the cache, but stops if a value already exists

Signature

boolean add( string $key, mixed $value, integer $ttl=0 )

Parameters
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
Returns

If the key/value pair were added successfully

->clean() public

Removes all cache entries that have expired

Signature

void clean( )

->clear() public

Clears the WHOLE cache of every key, use with caution!

xcache may require a login or password depending on your ini settings.

Signature

boolean clear( )

Returns

If the cache was successfully cleared

->delete() public

Deletes a value from the cache

Signature

boolean delete( string $key )

Parameters
string $key The key to delete
Returns

If the delete succeeded

->get() public

Returns a value from the cache

Signature

mixed get( string $key, mixed $default=NULL )

Parameters
string $key The key to return the value for
mixed $default The value to return if the key did not exist
Returns

The cached value or the default value if no cached value was found

->save() public

Only valid for file caches, saves the file to disk

Signature

void save( )

->serialize() protected

Serializes a value before storing it in the cache

Signature

string serialize( mixed $value )

Parameters
mixed $value The value to serialize
Returns

The serialized value

->set() public

Sets a value to the cache, overriding any previous value

Signature

boolean set( string $key, mixed $value, integer $ttl=0 )

Parameters
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
Returns

If the value was successfully saved

->unserialize() protected

Unserializes a value before returning it

Signature

mixed unserialize( string $value )

Parameters
string $value The serialized value
Returns

The PHP value

fConnectivityException

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

fConnectivityException API Reference

class, v1.0.0b

An exception caused by a connectivity error

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
         |
         --fConnectivityException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fCookie

The fCookie class helps to make PHPs cookie API more consistent, while adding the ability to set default parameters and create httponly cookies in older versions of PHP 5.

Setting Values

The static method set() is a wrapper around the function setcookie(), with the addition of optional default parameters and backwards compatibility for creating httponly cookies in PHP 5.1 and earlier. In addition, the expires parameter has been extended to allow any valid strtotime() string or a timestamp. Below are some examples of using set():

// Set a cookie for the current path
fCookie::set('my_cookie', 'my_value');

// Set a cookie to expire in 1 week
fCookie::set('my_cookie', 'my_value', '+1 week');

// Set a cookie for the whole site
fCookie::set('my_cookie', 'my_value', '+1 week', '/');

// Set a cookie for the whole site that expires when the browser is closed
fCookie::set('my_cookie', 'my_value', 0, '/');

// Set a cookie for all subdomains of example.com
fCookie::set('my_cookie', 'my_value', '+1 week', '/', '.example.com');

// Ensure that the cookie is only set over a secure connection (https://)
fCookie::set('my_cookie', 'my_value', '+1 week', '/', '.example.com', TRUE);

// Set the cookie to only be accessible to HTTP (not javascript)
fCookie::set('my_cookie', 'my_value', '+1 week', '/', '.example.com', TRUE, TRUE);

Please note that set() follows the same functionality as setcookie() when it comes to deleting cookies and storing boolean values. Any value that is set to a cookie that is equal to FALSE will cause the cookie to be deleted. This means storing boolean values in a cookie is not recommended since a FALSE value will cause the cookie to be erased - instead use '0' and '1'. Also, setting the expiration date to a time in the past will cause the cookie to be deleted.

Getting Values

The static method get() replaces direct access to the $_COOKIE superglobal and adds the ability to specify default values if no cookie is found:

// Get the value 'default_value' if no cookie of that name exists
$value = fCookie::get('my_cookie', 'default_value');

get() will also remove any slashes added by the ini setting magic_quotes_gpc being enabled.

Default Parameters

The fCookie class also allows defining default parameters to use for set(). Since most sites will often use the same $path, $domain, $secure and $httponly parameters for cookies, setting default eliminates unnecessary duplication throughout the code. If a parameter is passed as NULL to set() and a default is defined, the default will be used. Here are the methods to set default parameters:

Parameter Method
$expires setDefaultExpires()
$path setDefaultPath()
$domain setDefaultDomain()
$secure setDefaultSecure()
$httponly setDefaultHTTPOnly()

fCookie API Reference

static class, v1.0.0b3

Provides a consistent cookie API, HTTPOnly compatibility with older PHP versions and default parameters

Changes:
1.0.0b3Added the delete() method 9/30/09
1.0.0b2Updated for new fCore API 2/16/09
1.0.0bThe initial implementation 9/1/08

Static Methods

::delete() public

Deletes a cookie - uses default parameters set by the other set methods of this class

Signature

void delete( string $name, string $path=NULL, string $domain=NULL, boolean $secure=NULL )

Parameters
string $name The cookie name to delete
string $path The path of the cookie to delete
string $domain The domain of the cookie to delete
boolean $secure If the cookie is a secure-only cookie
::get() public

Gets a cookie value from $_COOKIE, while allowing a default value to be provided

Signature

mixed get( string $name, mixed $default_value=NULL )

Parameters
string $name The name of the cookie to retrieve
mixed $default_value If there is no cookie with the name provided, return this value instead
Returns

The value

::reset() internal public

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

Signature

void reset( )

::set() public

Sets a cookie to be sent back to the browser - uses default parameters set by the other set methods of this class

The following methods allow for setting default parameters for this method:

Signature

void set( string $name, mixed $value, string|integer $expires=NULL, string $path=NULL, string $domain=NULL, boolean $secure=NULL, boolean $httponly=NULL )

Parameters
string $name The name of the cookie to set
mixed $value The value of the cookie to set
string|integer $expires A relative string to be interpreted by strtotime() or an integer unix timestamp
string $path The path this cookie applies to
string $domain The domain this cookie applies to
boolean $secure If the cookie should only be transmitted over a secure connection
boolean $httponly If the cookie should only be readable by HTTP connection, not javascript
::setDefaultDomain() public

Sets the default domain to use for cookies

This value will be used when the $domain parameter of the set() method is not specified or is set to NULL.

Signature

void setDefaultDomain( string $domain )

Parameters
string $domain The default domain to use for cookies
::setDefaultExpires() public

Sets the default expiration date to use for cookies

This value will be used when the $expires parameter of the set() method is not specified or is set to NULL.

Signature

void setDefaultExpires( string|integer $expires )

Parameters
string|integer $expires The default expiration date to use for cookies
::setDefaultHTTPOnly() public

Sets the default httponly flag to use for cookies

This value will be used when the $httponly parameter of the set() method is not specified or is set to NULL.

Signature

void setDefaultHTTPOnly( boolean $httponly )

Parameters
boolean $httponly The default httponly flag to use for cookies
::setDefaultPath() public

Sets the default path to use for cookies

This value will be used when the $path parameter of the set() method is not specified or is set to NULL.

Signature

void setDefaultPath( string $path )

Parameters
string $path The default path to use for cookies
::setDefaultSecure() public

Sets the default secure flag to use for cookies

This value will be used when the $secure parameter of the set() method is not specified or is set to NULL.

Signature

void setDefaultSecure( boolean $secure )

Parameters
boolean $secure The default secure flag to use for cookies

fCore

The fCore class centralizes debugging, error and exception handling and more.

Error/Exception Handling

When maintaining production systems, knowing about errors and unhandled exceptions that have occurred is key. The fCore class provides two handy methods to simply this task.

The enableErrorHandling() and enableExceptionHandling() methods each take a first parameter that is the destination for errors and unhandled exceptions respectively. The options are an email address, a file, or the string 'html' for output into the currently rendering page.

Since unhandled exceptions cause the page execution to stop immediately, enableExceptionHandling() takes a callback as a second parameter to allow you to cleanly finish HTML output. The optional third parameter is an array of parameters to send to the callback.

Both the error and exception handling provide full backtraces, allowing for an easy time finding bugs.

// Set the site to send me an email every time an error or unhandled exception gets thrown
fCore::enableErrorHandling('will@flourishlib.com');
fCore::enableExceptionHandling('will@flourishlib.com', array($templating, 'place'), array('footer'));

// Set the site to log errors and exceptions to my logs dir
fCore::enableErrorHandling($_SERVER['DOCUMENT_ROOT'] . '/writable/logs/errors.log');
fCore::enableExceptionHandling($_SERVER['DOCUMENT_ROOT'] . '/writable/logs/exceptions.log', array($templating, 'place'), array('footer'));

// Set the site to display errors and exception in the html
fCore::enableErrorHandling('html');
fCore::enableExceptionHandling('html', array($templating, 'place'), array('footer'));

Multiple email addresses may be specified, separated by commas.

fCore::enableErrorHandling('will@flourishlib.com,john.smith@example.com');

By default, error and exception output includes a dump of the state of a number of PHP superglobals including $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION and $_SERVER to aid in debugging. It is possible to disable this functionality by calling disableContext().

// Disable the context dumps that happen with error and exception handling
fCore::disableContext();

Using an SMTP Connection

By default, error and exception emails are sent using the mail() function. An SMTP server may be used by passing an instance of fSMTP and a From email address to configureSMTP().

$smtp = new fSMTP('server.example.com');
$smtp->authenticate('username', 'password');
fCore::configureSMTP($smtp, 'noreply@example.com');

Capturing Errors

While exceptions are fairly easy to use due to the way they bubble up to the closest matching catch block, errors in PHP are not as simple. Errors don't interrupt program flow and can only be captured by an error handler. startErrorCapture() and stopErrorCapture() provide functionality that allows capturing errors and returning them in an array for futher processing.

// Here we don't need warning messages if this fails, we can just check to see if $result is FALSE
fCore::startErrorCapture();
$result = file_get_contents('http://example.com');
$errors = fCore::stopErrorCapture();

While a similar result can be accomplished by using the error_reporting() function and simplying lowering the error reporting level, this may not be available if it has been set via the php_admin_value Apache configuration directive.

In addition, turning down error_reporting() does not allow for acting upon errors, just silencing them. Since stopErrorCapture() returns an array of information about each error that occurred, the messages can be used to determine the next course of action or combined into an exception.

fCore::startErrorCapture();
$connection = pg_connect('dbname=example');
$errors = fCore::stopErrorCapture();
if (!$connection) {
    $error_strings = array();
    foreach ($errors as $error) {
         $error_strings[] = $error['string'];
    }
    throw new fConnectivityException("Unable to connect to the PostgreSQL database:\n%s", join("\n", $error_strings));
}

It is possible to capture only specific $types of errors by passing them as the first parameter to startErrorCapture(). The values are the same as those passed to error_reporting(), a bitmask of the desired error types. The captured messages can be further restricted by passing a PCRE $regex as the second parameter. Any errors that are not captured are passed on to the normal error handler.

Please note that all errors that match $types will be captured, even if they are excluded by the current error_reporting() setting.

// Capture only warnings about SSL
fCore::startErrorCapture(E_WARNING, '#ssl#i');

It is also possible to capture all errors, but only return some from stopErrorCapture(). This is accomplished by by passing a PCRE $regex as the first parameter.

// Capture all warnings, but only return errors about SSL
fCore::startErrorCapture(E_WARNING);
// 
$errors = fCore::stopErrorCapture('#ssl#i');

Debugging

fCore provides a few useful functions when you are trying to debug code. The simplest way to debug is to use the expose() method to show the contents of a file. expose() creates output similar to print_r(), however it uses symbols to differentiate between '', NULL and FALSE. Here is a usage example.

fCore::expose(array('foo', 1, '', NULL, FALSE, TRUE));

The above call to expose() would create the following HTML:

<pre class="exposed">Array
(
    [0] => foo
    [1] => 1
    [2] => {empty_string}
    [3] => {null}
    [4] => {false}
    [5] => {true}
)</pre>

If you want to set your code up to conditionally display debugging information, youll want to use the debug() method. Content sent to debug() is displayed via expose() only if enableDebugging() has been passed TRUE or if the second parameter, $force, is TRUE.

// Enable debugging
fCore::enableDebugging(TRUE);

// Display a debugging message only when fCore::enableDebugging() has been called
fCore::debug('This is only shown when fCore::enableDebugging(TRUE) is called before this code', FALSE);

// Display a debugging message even if fCore::enableDebugging() has not been called
fCore::debug('This will always be shown', TRUE);

If you wish to pass debug information to another debugging or logging system, a callback can be registered via the static method registerDebugCallback(). This method accepts a single parameter, the $callback to send all debug messages to. The $callback should accept a single parameter, a string debug message.

// Create a function to handle debug messages
function handleDebug($message)
{
    // Code to pass message to another debugging or logging system
}
 
// Register the function as the message handler
fCore::registerDebugCallback('handleDebug');

The backtrace() method provides a compact and nicely formatted version of debug_backtrace(). Below is an example of usage:

class Example {
    static public function backtrace()
    {
        fCore::expose(fCore::backtrace());
    }
}

Example::backtrace();

Which would produce the following HTML:

<pre class="exposed">
{doc_root}/example.php(8): Example::backtrace()
{doc_root}/example.php(4): fCore::backtrace()
</pre>

Environment Handling

In some situations it is necessary to write code based on the version of PHP or the operating system the code is running on.

The static method checkVersion() will return TRUE if the currently running version of PHP is greater or equal to the version string passed in.

if (fCore::checkVersion('5.1')) {
    echo 'You are running PHP version 5.1 or newer';
}

The static method checkOS() will return TRUE if the current operating system is one of the OSes passed in as a parameter. Valid operating system strings include:

if (fCore::checkOS('bsd', 'osx')) {
    echo 'You are running either a BSD or OSX';
}

fCore API Reference

static class, v1.0.0b24

Provides low-level debugging, error and exception functionality

Changes:
1.0.0b24Backwards Compatibility Break - moved detectOpcodeCache() to fLoader::hasOpcodeCache() 8/26/11
1.0.0b23Backwards Compatibility Break - changed the email subject of error/exception emails to include relevant file info, instead of the timestamp, for better email message threading 6/20/11
1.0.0b22Fixed a bug with dumping arrays containing integers 5/26/11
1.0.0b21Changed startErrorCapture() to allow "stacking" it via multiple calls, fixed a couple of bugs with dump() mangling strings in the form int(1), fixed mispelling of occurred 5/9/11
1.0.0b20Backwards Compatibility Break - Updated expose() to not wrap the data in HTML when running via CLI, and instead just append a newline 2/24/11
1.0.0b19Added detection of AIX to checkOS() 1/19/11
1.0.0b18Updated expose() to be able to accept multiple parameters 1/10/11
1.0.0b17Fixed a bug with backtrace() triggering notices when an argument is not UTF-8 8/17/10
1.0.0b16Added the $types and $regex parameters to startErrorCapture() and the $regex parameter to stopErrorCapture() 8/9/10
1.0.0b15Added startErrorCapture() and stopErrorCapture() 7/5/10
1.0.0b14Changed enableExceptionHandling() to only call fException::printMessage() when the destination is not html and no callback has been defined, added configureSMTP() to allow using fSMTP for error and exception emails 6/4/10
1.0.0b13Added the $backtrace parameter to backtrace() 3/5/10
1.0.0b12Added getDebug() to check for the global debugging flag, added more specific BSD checks to checkOS() 3/2/10
1.0.0b11Added detectOpcodeCache() 10/6/09
1.0.0b10Fixed expose() to properly display when output includes non-UTF-8 binary data 6/29/09
1.0.0b9Added disableContext() to remove context info for exception/error handling, tweaked output for exceptions/errors 6/28/09
1.0.0b8enableErrorHandling() and enableExceptionHandling() now accept multiple email addresses, and a much wider range of emails 6/1/09
1.0.0b7backtrace() now properly replaces document root with {doc_root} on Windows 5/2/09
1.0.0b6Fixed a bug with getting the server name for error messages when running on the command line 3/11/09
1.0.0b5Fixed a bug with checking the error/exception destination when a log file is specified 3/7/09
1.0.0b4Backwards compatibility break - getOS() and getPHPVersion() removed, replaced with checkOS() and checkVersion() 2/16/09
1.0.0b3handleError() now displays what kind of error occurred as the heading 2/15/09
1.0.0b2Added registerDebugCallback() 2/7/09
1.0.0bThe initial implementation 9/25/07

Static Methods

::backtrace() public

Creates a nicely formatted backtrace to the the point where this method is called

Signature

string backtrace( integer $remove_lines=0, array $backtrace=NULL )

Parameters
integer $remove_lines The number of trailing lines to remove from the backtrace
array $backtrace A backtrace from debug_backtrace() to format - this is not usually required or desired
Returns

The formatted backtrace

::call() public

Performs a call_user_func(), while translating PHP 5.2 static callback syntax for PHP 5.1 and 5.0

Parameters can be passed either as a single array of parameters or as multiple parameters.

// Passing multiple parameters in a normal fashion
fCore::call('Class::method', TRUE, 0, 'test');
// Passing multiple parameters in a parameters array
fCore::call('Class::method', array(TRUE, 0, 'test'));

To pass parameters by reference they must be assigned to an array by reference and the function/method being called must accept those parameters by reference. If either condition is not met, the parameter will be passed by value.

// Passing parameters by reference
fCore::call('Class::method', array(&$var1, &$var2));
Signature

mixed call( callback $callback, array $parameters=array() )

Parameters
callback $callback The function or method to call
array $parameters The parameters to pass to the function/method
Returns

The return value of the called function/method

::callback() public

Translates a Class::method style static method callback to array style for compatibility with PHP 5.0 and 5.1 and built-in PHP functions

Signature

array callback( callback $callback )

Parameters
callback $callback The callback to translate
Returns

The translated callback

::checkOS() public

Returns is the current OS is one of the OSes passed as a parameter

Valid OS strings are:

  • 'linux'
  • 'aix'
  • 'bsd'
  • 'freebsd'
  • 'netbsd'
  • 'openbsd'
  • 'osx'
  • 'solaris'
  • 'windows'
Signature

boolean checkOS( string $os [, ... ] )

Parameters
string $os [, ... ] The operating system to check - see method description for valid OSes
Returns

If the current OS is included in the list of OSes passed as parameters

::checkVersion() public

Checks to see if the running version of PHP is greater or equal to the version passed

Signature

boolean checkVersion( $version )

Parameters
$version
Returns

If the running version of PHP is greater or equal to the version passed

::configureSMTP() public

Sets an fSMTP object to be used for sending error and exception emails

Signature

void configureSMTP( fSMTP $smtp, string $from_email )

Parameters
fSMTP $smtp The SMTP connection to send emails over
string $from_email The email address to use in the From: header
::debug() public

Prints a debugging message if global or code-specific debugging is enabled

Signature

void debug( string $message, boolean $force=FALSE )

Parameters
string $message The debug message
boolean $force If debugging should be forced even when global debugging is off
::disableContext() public

Disables including the context information with exception and error messages

The context information includes the following superglobals:

  • $_SERVER
  • $_POST
  • $_GET
  • $_SESSION
  • $_FILES
  • $_COOKIE
Signature

void disableContext( )

::dump() public

Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings

The string output format of this method is very similar to the output of print_r() except that the following values are represented as special strings:

  • TRUE: '{true}'
  • FALSE: '{false}'
  • NULL: '{null}'
  • '': '{empty_string}'
Signature

string dump( mixed $data )

Parameters
mixed $data The value to dump
Returns

The string representation of the value

::enableDebugging() public

Enables debug messages globally, i.e. they will be shown for any call to debug()

Signature

void enableDebugging( boolean $flag )

Parameters
boolean $flag If debugging messages should be shown
::enableDynamicConstants() public

Turns on a feature where undefined constants are automatically created with the string value equivalent to the name

This functionality only works if enableErrorHandling() has been called first. This functionality may have a very slight performance impact since a E_STRICT error message must be captured and then a call to define() is made.

Signature

void enableDynamicConstants( )

::enableErrorHandling() public

Turns on developer-friendly error handling that includes context information including a backtrace and superglobal dumps

All errors that match the current error_reporting() level will be redirected to the destination and will include a full backtrace. In addition, dumps of the following superglobals will be made to aid in debugging:

  • $_SERVER
  • $_POST
  • $_GET
  • $_SESSION
  • $_FILES
  • $_COOKIE

The superglobal dumps are only done once per page, however a backtrace in included for each error.

If an email address is specified for the destination, only one email will be sent per script execution. If both error and exception handling are set to the same email address, the email will contain both errors and exceptions.

Signature

void enableErrorHandling( string $destination )

Parameters
string $destination The destination for the errors and context information - an email address, a file path or the string 'html'
::enableExceptionHandling() public

Turns on developer-friendly uncaught exception handling that includes context information including a backtrace and superglobal dumps

Any uncaught exception will be redirected to the destination specified, and the page will execute the $closing_code callback before exiting. The destination will receive a message with the exception messaage, a full backtrace and dumps of the following superglobals to aid in debugging:

  • $_SERVER
  • $_POST
  • $_GET
  • $_SESSION
  • $_FILES
  • $_COOKIE

The superglobal dumps are only done once per page, however a backtrace in included for each error.

If an email address is specified for the destination, only one email will be sent per script execution.

If an email address is specified for the destination, only one email will be sent per script execution. If both exception and error handling are set to the same email address, the email will contain both exceptions and errors.

Signature

void enableExceptionHandling( string $destination, callback $closing_code=NULL, array $parameters=array() )

Parameters
string $destination The destination for the exception and context information - an email address, a file path or the string 'html'
callback $closing_code This callback will happen after the exception is handled and before page execution stops. Good for printing a footer. If no callback is provided and the exception extends fException, fException::printMessage() will be called.
array $parameters The parameters to send to $closing_code
::expose() public

Prints the dump() of a value

The dump will be printed in a <pre> tag with the class exposed if PHP is running anywhere but via the command line (cli). If PHP is running via the cli, the data will be printed, followed by a single line break (\n).

If multiple parameters are passed, they are exposed as an array.

Signature

void expose( mixed $data [, ... ] )

Parameters
mixed $data [, ... ] The value to show
::getDebug() public

If debugging is enabled

Signature

boolean getDebug( boolean $force=FALSE )

Parameters
boolean $force If debugging is forced
Returns

If debugging is enabled

::handleError() internal public

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

Handles an error, creating the necessary context information and sending it to the specified destination

Signature

void handleError( integer $error_number, string $error_string, string $error_file=NULL, integer $error_line=NULL, array $error_context=NULL )

Parameters
integer $error_number The error type
string $error_string The message for the error
string $error_file The file the error occurred in
integer $error_line The line the error occurred on
array $error_context A references to all variables in scope at the occurence of the error
::handleException() internal public

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

Handles an uncaught exception, creating the necessary context information, sending it to the specified destination and finally executing the closing callback

Signature

void handleException( object $exception )

Parameters
object $exception The uncaught exception to handle
::registerDebugCallback() public

Registers a callback to handle debug messages instead of the default action of calling expose() on the message

Signature

void registerDebugCallback( callback $callback )

Parameters
callback $callback A callback that accepts a single parameter, the string debug message to handle
::reset() internal public

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

Signature

void reset( )

::sendMessagesOnShutdown() internal public

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

Sends an email or writes a file with messages generated during the page execution

This method prevents multiple emails from being sent or a log file from being written multiple times for one script execution.

Signature

void sendMessagesOnShutdown( )

::startErrorCapture() public

Temporarily enables capturing error messages

Signature

void startErrorCapture( integer $types=NULL, string $regex=NULL )

Parameters
integer $types The error types to capture - this should be as specific as possible - defaults to all (E_ALL | E_STRICT)
string $regex A PCRE regex to match against the error message
::stopErrorCapture() public

Stops capturing error messages, returning all that have been captured

Signature

array stopErrorCapture( string $regex=NULL )

Parameters
string $regex A PCRE regex to filter messages by
Returns

The captured error messages

fCryptography

The fCryptography class provides a simple interface for password hashing, symmetric key encryption and public key encryption. All of the cryptographic methods have been chosen since they are considered to be best practices and fairly secure (which is obviously a relative term). In each section the general algorithm will be explained to help in understanding what level of security is afforded.

Also note that all of the method return values will contain only ASCII characters &mdash; base64 encoding is used to ensure this.

Please do note that this is not meant to be a definitive resource for cryptographic information, I highly recommend reading more about cryptography from other sources.

Random Numbers and Strings

The built in random number generator in PHP, rand() can suffer from a number of attacks, such as outlined by Steffan Esser in his article mt_srand and not so random numbers. To help combat this issue, the fCryptography class provides a random() method that will ensure that the random number generator has been seeded with a good seed value. Be sure not to call the PHP functions srand() or mt_srand() in your code.

// Get a random number
$rand = fCryptography::random();

// Get a random number between 1 and 100
$rand = fCryptography::random(1, 100);

If you need a random string (such as for an authentication code), you can use the method randomString(). It requires one parameter, $length, which is the desired string length. A second optional parameter, $type, allows specifying what set of characters will be used in the string. Options include 'alphanumeric' (the default), 'alpha', 'numeric', and 'hexadecimal'.

// Generate an 8 character alphanumeric string
$code = fCryptography::randomString(8);

// Generate a 32 character hexadecimal string
$code = fCryptography::randomString(32, 'hexadecimal');

Password Hashing

General Information

Password security is easy to overlook when you dont have the whole picture. Even if your site doesnt store any vital information, most users use the same password for most if not all of their accounts, possibly even the email account they used on your site. Because of this fact password security is a big deal for all sites, and storing passwords in plain text is a very insecure practice.

A common practice is to hash a users password using a method such as MD5. Passwords that have been hashed this way are susceptible to rainbow table attacks. In order to provide a reasonable amount of security, all passwords should be hashed using a salt.

By using a salt when hashing the password you pretty much require that a hacker use brute force to decrypt the password. As processors have been increasing in speed the number of hashes that can be checked per second is constantly increasing, not to mention that hardware exists specially built for hashing. Thus to help prevent brute force attacks it is a common practice to make the hash take a significant enough amount of computation so that brute force attacks take even longer.

The key is to balance the computation time to be short enough that a normal user logging in would not notice a performance issue, while a hacker would be significantly slowed in their task of generating millions of hashes.

The hashPassword() method takes the provided password and runs it through a non-trivial number of rounds of SHA-1 hashing, alternating including and excluding a random salt from the value being hashed.

The output of includes an indicator that the fCryptography class was used for hashing, the salt used (which is unique for each invocation of the method), and the hashed password. The password can be verified by sending this return value and the password to test to the checkPasswordHash().

Hashing and Checking

Here is an example of using the methods:

$hash = fCryptography::hashPassword('Example password');
// ...
if (fCryptography::checkPasswordHash('Example password', $hash)) {
    echo 'The correct password was entered';
}

Here is what the output of hashPassword() looks like:

fCryptography::password_hash#Gu19bpZN94#ac74c4ad9ed7103e051e583af86599b95237e9af

Symmetric-Key Encryption

General Information

Symmetric key encryption is encryption where the same secret key is used for both encryption and decryption. This type of encryption can easily be thwarted if the secret key is stored on the server and that machine is compromised. Because of this it is not recommended as a transparent encryption happening behind the scenes, but rather when a user will provide the secret key each time they access a site.

The symmetricKeyEncrypt() method uses the Rijndael block cipher, which is the basis of the Advanced Encryption Standard (or AES). The Rijndael cipher is used with a 192 bit block size and a 256 bit key in cipher feedback mode with a random IV. The encrypted plain text and IV are then run through HMAC-SHA-1 for data integrity checking during decryption.

When decrypting using symmetricKeyDecrypt(), the HMAC is verified to ensure the encrypted value has not been corrupted or altered. The secret key and IV are then used to decrypt the original plain text.

If you arent familiar with cryptography or the algorithms mentioned, you can rest assured that the technique and algorithms used are approved for use by the U.S. government when storing TOP SECRET documents.

The fCryptography class does enforce the requirement that the secret key is at least 8 characters long. In addition, the return value from symmetricKeyEncrypt() is not simply an encrypted string, but also contains information indicating the encrypted text came from Flourish, the IV, the encrypted plain text and the HMAC.

Encryption and Decryption

Here is an example of using symmetricKeyEncrypt() and symmetricKeyDecrypt():

// Encrypt the data
$ciphertext = fCryptography::symmetricKeyEncrypt('This is a secret message to be encrypted', 'Secret Key');

// Decrypt the data
$plaintext  = fCryptography::symmetricKeyDecrypt($ciphertext, 'Secret Key');

Here is an example of what the ciphertext would look like:

fCryptography::symmetric#1l2rt6kP0kqdIDuSVpGrSoTy08sE33fAMf6Y0M0CtOU=#csRlMH6l6dnks6hCOhI+IxDAA69GAI/d5L3L77G0parEdlc/dDDz1z/ASX/I8suj/uAEXjxShhcrEwo0IzYODuoeSdmJvGKZJtquCWkKPg==#ef5973e32808e01f5be2745a0a9ef61396992ddf

Please note that the symmetric key methods require both the mcrypt and hash extensions for PHP to be installed.

Public-Key Encryption and Signing

General Information

Public-key encryption is encryption where a readily available public key can be used by anyone to encrypt data that only the corresponding private key can decrypt. The private key is usually protected by a password, allowing for it to be stored on a server without fear of compromise by simply reading it.

Public-key cryptography would allow for encryption to happen behind the scenes on the front-end of a web site, while requiring users decrypting the information to provide the private-key password.

To perform public-key encryption you will need an X.509 public-key certificate and private key in PEM format. Please visit the Obtaining a Secure Certificate Key Pair page for more information.

The public certificate is not actually used to do the encryption, but rather a randomly generated 128-bit key is used for the encryption process and then the public certificate (which is 1024 or 2048 bits in length) is used to encrypt that random key. The cipher used for the encryption is the RC4 stream cipher. On the receiving end the private key is used to decrypt the random RC4 key and then that is in turn used to decrypt the actual data.

To ensure that the data is not tampered with between encryption and decryption, the encrypted random key and the ciphertext are run through HMAC-SHA-1 and included in the output. This HMAC is verified when the data is decrypted.

Encryption

To encrypt a string you will need the public certificate and the method publicKeyEncrypt(). Simply pass the message and the path to the public certificate to the method and you will receive an encrypted string:

$ciphertext = fCrytography::publicKeyEncrypt('This is a secret message!', './public.cer');

Here is an example of what the ciphertext would look like:

fCryptography::public#VHpfAoWC2h2sqFtdRy7Ihv7P2C1VPQ0SQduHT5div6+nq8Y0o6+sM5XgLDl+zXMnmY4+xOtohsBaFQ/MDiWA7VI5vXgK0j04vv6bcnkGwFz1M+o3Tuyo8Yu152Gj7iajJz9S1fiLOo4PMiRnafxbtfyExMFKJ6wiyc7AfjiGUUM=#YLehWyfNOvUEPrsFtRFeBHtvKJOy#4258dbd7e6bd144ab1aa98a0f5d2a9f8be9fb231

Decryption

To decrypt the ciphertext the private key and private key password are required to be passed with the ciphertext to the publicKeyDecrypt() method. If the private key in unencrypted (not recommended when placed on the same server as the encrypted data) then an empty string can be passed as the password parameter:

// Using a private key that is encrypted with a password
$plaintext = fCryptography::publicKeyDecrypt($ciphertext, './private.key', 'private key password');

// Using a private key that is not encrypted
$plaintext = fCryptography::publicKeyDecrypt($ciphertext, './private.key', '');

Signing and Verification

Public-key cryptography also provides functionality to verify that a message has originated from a specific author through signing and verification. The private key can be used to create a secure message digest of a message or string and then the public key can be used to verify it.

The method publicKeySign() will create the signature for a plaintext source. It takes three parameters, the $plaintext to create a signature for, the $private_key_file path and the $password for the private key. The signature will be returned by the method in base-64 encoding to allow for transmission over channels that may not properly transmit binary data.

$signature = fCryptography::publicKeySign($plaintext, './private.key', 'private key password');

The signature of any plaintext can be authenticated by using the authors public key and the method publicKeyVerify(). This method will return a boolean indicating if the claimed author actually created the plaintext in question. The three parameters are the $plaintext, the base-64 encoded $signature and the authors $public_key_file.

if (fCryptography::publicKeyVerify($plaintext, $signature, './public.cer')) {
   echo 'This message has been verified authentic!';
}

Hash and Ciphertext Formats

If you are hashing or encrypting data using the fCryptography class but need to reverse the process on another system you will probably want to refer to this section as a starting point, and then look at the source code.

If you simply wish to decrypt data encrypted with or check a hash created by the fCryptography class, please check the appropriate section above the relevant decrypt or check method.

hashPassword()

hashPassword() outputs a string in the following format:

fCryptography::password_hash#{salt}#{hash}

{salt} is a random 10 character alphanumeric string that prepended to the password before being run through sha1(). {hash} is a result of another non-trivial number of iterations of executing sha1() on the result of the last sha1() call appended with the original password or salt in alternating fashion.

symmetricKeyEncrypt()

symmetricKeyEncrypt() outputs a string in the following format:

fCryptography::symmetric#{iv}#{ciphertext}#{hmac}

The {iv} is the randomly generated initialization vector that has been base-64 encoded.

The {ciphertext} is the provided plaintext that has been encrypted using Rijndael-192 in CFB mode using the initialization vector and then base-64 encoded.

The {hmac} is the encrypted initialization vector concatenated with the encrypted plaintext (neither having been base-64 encoded yet). It is then run through a SHA-1 HMAC using the secret key provided.

publicKeyEncrypt()

publicKeyEncrypt() outputs a string in the following format:

fCrytography::public#{secret_key}#{ciphertext}#{hmac}

The {secret_key} is a random 128-bit RC4 secret key that has been encrypted (via RSA) using provided public certificate. It is base64-encoded before added to the output string.

{ciphertext} is the plaintext that has been encrypted via the RC4 stream cipher using the random RC4 key and is encoded using base64.

{hmac} is a SHA-1 HMAC of the concatenated encrypted secret key and the ciphertext generated using the plaintext as the key. Neither the encrypted secret key, nor the ciphertext is base64 encoded before the HMAC is calculated.

publicKeySign()

publicKeySign() returns a base-64 encoded SHA-1 hash of the plaintext that has been encrypted using the private key.

fCryptography API Reference

static class, v1.0.0b14

Provides cryptography functionality, including hashing, symmetric-key encryption and public-key encryption

Changes:
1.0.0b14Added the base36, base56 and custom types to randomString() 8/25/11
1.0.0b13Updated documentation about symmetric-key encryption to explicitly state block and key sizes, added base64 type to randomString() 11/6/10
1.0.0b12Fixed an inline comment that incorrectly references AES-256 11/4/10
1.0.0b11Updated class to use fCore::startErrorCapture() instead of error_reporting() 8/9/10
1.0.0b10Added a missing parameter to an fProgrammerException in randomString() 7/29/10
1.0.0b9Added hashHMAC() 4/20/10
1.0.0b8Fixed seedRandom() to pass a directory instead of a file to disk_free_space() 3/9/10
1.0.0b7SECURITY FIX: fixed issue with random() and randomString() not producing random output on OSX, made seedRandom() more robust 10/6/09
1.0.0b6Changed symmetricKeyEncrypt() to throw an fValidationException when the $secret_key is less than 8 characters 9/30/09
1.0.0b5Fixed a bug where some windows machines would throw an exception when generating random strings or numbers 6/9/09
1.0.0b4Updated for new fCore API 2/16/09
1.0.0b3Changed @ error suppression operator to error_reporting() calls 1/26/09
1.0.0b2Backwards compatibility break - changed symmetricKeyEncrypt() to not encrypt the IV since we are using HMAC on it 1/26/09
1.0.0bThe initial implementation 11/27/07

Static Methods

::checkPasswordHash() public

Checks a password against a hash created with hashPassword()

Signature

boolean checkPasswordHash( string $password, string $hash )

Parameters
string $password The password to check
string $hash The hash to check against
Returns

If the password matches the hash

::hashHMAC() internal public

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

Provides a pure PHP implementation of hash_hmac() for when the hash extension is not installed

Signature

string hashHMAC( string $algorithm, string $data, string $key )

Parameters
string $algorithm The hashing algorithm to use: 'md5' or 'sha1'
string $data The data to create an HMAC for
string $key The key to generate the HMAC with
Returns

The HMAC

::hashPassword() public

Hashes a password using a loop of sha1 hashes and a salt, making rainbow table attacks infeasible

Signature

string hashPassword( string $password )

Parameters
string $password The password to hash
Returns

An 80 character string of the Flourish fingerprint, salt and hashed password

::publicKeyDecrypt() public

Decrypts ciphertext encrypted using public-key encryption via publicKeyEncrypt()

A public key (X.509 certificate) is required for encryption and a private key (PEM) is required for decryption.

Signature

string publicKeyDecrypt( string $ciphertext, string $private_key_file, string $password )

Parameters
string $ciphertext The content to be decrypted
string $private_key_file The path to a PEM-encoded private key
string $password The password for the private key
Returns

The decrypted plaintext

Throws
fValidationException
When the ciphertext appears to be corrupted
::publicKeyEncrypt() public

Encrypts the passed data using public key encryption via OpenSSL

A public key (X.509 certificate) is required for encryption and a private key (PEM) is required for decryption.

Signature

string publicKeyEncrypt( string $plaintext, string $public_key_file )

Parameters
string $plaintext The content to be encrypted
string $public_key_file The path to an X.509 public key certificate
Returns

A base-64 encoded result containing a Flourish fingerprint and suitable for decryption using publicKeyDecrypt()

::publicKeySign() public

Creates a signature for plaintext to allow verification of the creator

A private key (PEM) is required for signing and a public key (X.509 certificate) is required for verification.

Signature

string publicKeySign( string $plaintext, string $private_key_file, string $password )

Parameters
string $plaintext The content to be signed
string $private_key_file The path to a PEM-encoded private key
string $password The password for the private key
Returns

The base64-encoded signature suitable for verification using publicKeyVerify()

Throws
fValidationException
When the private key is invalid
::publicKeyVerify() public

Checks a signature for plaintext to verify the creator - works with publicKeySign()

A private key (PEM) is required for signing and a public key (X.509 certificate) is required for verification.

Signature

boolean publicKeyVerify( string $plaintext, string $signature, string $public_key_file )

Parameters
string $plaintext The content to check
string $signature The base64-encoded signature for the plaintext
string $public_key_file The path to an X.509 public key certificate
Returns

If the public key file is the public key of the user who signed the plaintext

::random() public

Generates a random number using mt_rand() after ensuring a good PRNG seed

Signature

integer random( integer $min=NULL, integer $max=NULL )

Parameters
integer $min The minimum number to return
integer $max The maximum number to return
Returns

The psuedo-random number

::randomString() public

Returns a random string of the type and length specified

Signature

string randomString( integer $length, string $type='alphanumeric' )

Parameters
integer $length The length of string to return
string $type The type of string to return: 'base64', 'base56', 'base36', 'alphanumeric', 'alpha', 'numeric', or 'hexadecimal', if a different string is provided, it will be used for the alphabet
Returns

A random string of the type and length specified

::symmetricKeyDecrypt() public

Decrypts ciphertext encrypted using symmetric-key encryption via symmetricKeyEncrypt()

Since this is symmetric-key cryptography, the same key is used for encryption and decryption.

Signature

string symmetricKeyDecrypt( string $ciphertext, string $secret_key )

Parameters
string $ciphertext The content to be decrypted
string $secret_key The secret key to use for decryption
Returns

The decrypted plaintext

Throws
fValidationException
When the ciphertext appears to be corrupted
::symmetricKeyEncrypt() public

Encrypts the passed data using symmetric-key encryption

Since this is symmetric-key cryptography, the same key is used for encryption and decryption.

Signature

string symmetricKeyEncrypt( string $plaintext, string $secret_key )

Parameters
string $plaintext The content to be encrypted
string $secret_key The secret key to use for encryption - must be at least 8 characters
Returns

An encrypted and base-64 encoded result containing a Flourish fingerprint and suitable for decryption using symmetricKeyDecrypt()

Throws
fValidationException
When the $secret_key is less than 8 characters long

fDatabase

The fDatabase class abstracts interaction with MySQL, PostgreSQL, SQLite, Microsoft SQL Server (MSSQL), Oracle and IBM DB2 databases.

Supported Databases

The fDatabase class allows for interaction with a number of popular relational database management systems. Rather than requiring a specific PHP extension to interact with each of these DBs, Flourish shows its portable nature by automatically detecting and using the installed extension for the database type specified.

Here is a list of the supported DBs and the PHP extensions that are currently supported:

DB PHP Extensions
MSSQL sqlsrv, pdo_dblib, mssql (or sybase)
MySQL mysql, mysqli, pdo_mysql
Oracle oci8, pdo_oci
PostgreSQL pgsql, pdo_pgsql
SQLite pdo_sqlite (for v3.x), sqlite (for v2.x)
DB2 ibm_db2, pdo_ibm

Connecting

As a first step to interacting with a database, a connection needs to be made. This is done by creating a new instance of the fDatabase class. The constructor takes the database $type, $name, $username, $password, $server, $port and $timeout as parameters. MSSQL, MySQL, Oracle and PostgreSQL databases require all parameters except for the $server, $port and $timeout. SQLite databases only require the $type and $name parameters.

// Connecting to a MSSQL database on localhost running on the default port
$mssql_db  = new fDatabase('mssql', 'my_database', 'username', 'password');

// Connecting to a MySQL database on the server example.com
$mysql_db  = new fDatabase('mysql', 'my_database', 'username', 'password', 'example.com');

// Connecting to an Oracle database on localhost
$oracle_db = new fDatabase('oracle', 'my_database', 'username', 'password');

// Connecting to a PostgreSQL database on the current server using a non-standard port
$pgsql_db  = new fDatabase('postgresql', 'my_database', 'username', 'password', 'localhost', 1234);

// Connection to an SQLite database
$sqlite_db = new fDatabase('sqlite', '/path/to/database/file');

// Connecting to a remote DB2 server
$db2_db    = new fDatabase('db2', 'my_database', 'username', 'password', 'remote.host.com', 60000);

// Connect on the default port with a timeout of 1 second
$pgsql_db  = new fDatabase('postgresql', 'my_database', 'username', 'password', 'localhost', NULL, 1);

It is possible to connect to a MySQL database using a socket connection by passing sock:/path/to/the/socket as the $server parameter. For a PostgreSQL socket connection, simply pass sock: in the $server parameter.

The $timeout parameter accepts integers, and represents the number of seconds after which to stop trying to connect to the database.

Lazy Connections

When creating an fDatabase instance, a connection to the server is not automatically established. Instead, once a response is required from the server, then fDatabase will establish the connection. To force a connection at a specific time, usually for the sake of handling connection exceptions, call the method connect().

try {
    $db = new fDatabase('postgresql', 'my_database', 'username', 'password');
    // Please note that calling this method is not required, and simply
    // causes an exception to be thrown if the connection can not be made
    $db->connect();

} catch (fAuthorizationException $e) {
    $e->printMessage();
}

Connection Exceptions

When connecting to a server, fDatabase can throw either an fAuthorizationException when a username or password is incorrect, or an fConnectivityException when a server does not respond, a hostname lookup fails, or the database specified can not be accessed.

Catching both fAuthorizationException and fConnectivityException objects can be useful for handling validation of user-supplied connection parameters.

try {
    $db = new fDatabase($type, $database, $username, $password, $server, $port);
    $db->connect();
} catch (fAuthorizationException $e) {
    fMessaging::create('error', $e->getMessage());
} catch (fConnectivityException $e) {
    fMessaging::create('error', $e->getMessage());
}

Catching fConnectivityException objects can also be used to handle failover in replicated database environments.

// Use APC to cache the server status
$cache = new fCache('apc');

$servers = array('server1', 'server2', 'server3');
foreach ($servers as $server) {
    try {
        // Skip servers that are down
        if ($cache->get($server . '-down')) { continue; }

        // Use a one second timeout for fast failover
        $db = new fDatabase('postgresql', 'my_database', 'username', 'password', $server, NULL, 1);
        $db->connect();
        break;
    
    // If the connection failed, mark the server as down for 5 minutes
    } catch (fConnectivityException $e) {
        $cache->set($server . '-down', TRUE, 300);
    }
}

Login Information (Security)

Please note that the database password is stored in the object, and may be exposed via print_r(), fCore::expose(), or similar methods. fResult, fUnbufferedResult, fSchema, fStatement and fSQLTranslation objects also contain a reference to an fDatabase object and thus could expose password data in a similar fashion.

Queries

Once you have established a databases connection you can start executing queries using the query() method. This method executes a query and returns an instance of the fResult class to access returned rows and get information about the query that was executed.

Please note this method executes queries in a buffered manner. This means that all results are loaded into PHP memory, which can cause performance issues for very large result sets (in the order of 500+ rows). For large result sets, unbuffered queries will generally yield better performance (at the cost of certain other restrictions).

// Execute a SQL query and retrieve all returned rows
$result = $mysql_db->query('SELECT * FROM users LIMIT 5');
foreach ($result as $row) {
    // Access the row
}

For more information about what can be done with a query result, please see the fResult page.

Unbuffered Queries

Unbuffered queries will often have better performance for large results sets, however the exact details can vary from database driver to driver. Many database drivers will only allow a single unbuffered query to be active at any point. If another database query is called, it will either cause the previous call to close or will fail itself.

Calling unbufferedQuery() will return an instance of fUnbufferedResult. This is similar to an fResult object, however does not have the ability to retrieve the number of returned rows, or seek to different rows in the set.

Please note that some database/extension combinations do not provide unbuffered query functionality, and thus will not necessarily gain the same performance benefits as others. The following database extensions are known to have unbuffered benefits: pdo_*, mysql, mysqli, sqlite.

$result = $mysql_db->unbufferedQuery('SELECT * FROM users');
foreach ($result as $row) {
    // Don't execute another query in here or the original result will be destroyed
}

Non-Queries

In situations where no result is required to be iterated over, such as an UPDATE statement, the execute() method can be used. This method takes all of the same parameters as query(), however it does not return an fResult object.

$db->execute("UPDATE users SET name = %s WHERE name = %s", 'Will', 'William');

Like the query() and unbufferedQuery() methods, an fSQLException will be thrown if a SQL error occurs.

Cross-Database SQL

One of the features fDatabase provides for portable code is the ability to run SQL queries that work across all supported databases. The Flourish SQL page includes a list of all supported SQL syntax and what it is translated into for each different database engine.

The two methods translatedQuery() and unbufferedTranslatedQuery() work exactly the same as query() and unbufferedQuery() except that the SQL statements are translated from Flourish SQL into the SQL dialect supported by the current database.

For instance, if you are familiar with MSSQL databases, you will know that the LIMIT syntax is not valid, but instead it required you to use the TOP keyword. The following PHP:

$result = $mssql_db->translatedQuery("SELECT * FROM users LIMIT 5");

Would actually be executed as the following SQL:

SELECT TOP 5 * FROM users

Transactions

If you aren't familiar with what database transactions are, please read the Wikipedia page Database transaction first.

The fDatabase class by default executes all SQL queries immediately, in what is referred to as auto-commit mode. To perform one or more queries in a transaction that can be rolled back or commited, basic SQL queries are used instead of method calls.

fDatabase does minimal translation of transaction SQL queries since there isn't a single consistent set of commands for all supported databases. Each of these queries will work with any of the supported databases.

// Start a transaction
$db->query('BEGIN');

// Commit changes made during this transaction
$db->query('COMMIT');

// Rollback changes that have not been committed
$db->query('ROLLBACK');

Please note that MySQL MyISAM tables do not support transactions and will auto-commit even if a transaction has been started. Please see the ORM Conventions: MySQL Storage Engine section for details about this and other drawbacks to using MyISAM.

Escaping Data (Security)

If you have any experience with database interaction you are probably familiar with SQL injection attacks. For this reason, and the fact that the supported databases have varying representations for the various data types, it is recommended that all data going in and coming out of the database be escaped and unescaped respectively.

Arbitrary SQL escaping data can be done at any point by the escape() method, but should normally be done when calling query() (also translatedQuery(), unbufferedQuery(), unbufferedTranslatedQuery()).

The query methods accept $sql as the first parameter, followed by the required number of values to bind/inject into the query. This injection is done via data type placeholders in the $sql, and fully escapes the values based on the data type. These placeholder are similar to some of the formatting strings in sprintf(). Here is a list of the various placeholders and what data type the value will be escaped as:

// Escape a user id and name into the SQL statement and execute it
$result = $db->query('SELECT * FROM users WHERE age = %i AND last_name = %s', 18, "O'Shea");

The query methods by default do not use prepared statements, but instead create fully escaped SQL commands and execute them. For repeat queries or large string/binary values, 32k+ for Oracle/DB2, larger for other databases, a prepared fStatement object should be passed in place of the SQL string.

When using the escape() method, two or more parameters are required. The first, $sql_or_type, allows passing the data type to be escaped, or an SQL statement with data type placeholders. The second (and subsequent) parameter, $value is the PHP value to escape.

The permissible data types to pass into $sql_or_type include:

Escaping not only protects against SQL injection attacks, but also ensures that you are comparing proper data types in your SQL since all values are validated before being escaped. Below are some examples using escape() in various ways:

// Escape a string
$sql_string = $db->escape('string', "This ain't gonna break your SQL");

// Escape a boolean
$sql_boolean = $db->escape('boolean', TRUE);

// Escape a float
$sql_float   = $db->escape('%f', '12.39');

The above statements would produce the following SQL (in a SQLite database):

'This ain''t gonna break your SQL'

'1'

12.39

In addition to escaping single values for SQL, it is also possible to escape an array of values. When passing a data type or placeholder as the first parameter and the array of values as the second, an array of escaped values will be returned.

$escaped_integers = $db->escape('integer', array(1, 5, 'not an int'));

If the first parameter is a SQL string, the array of values will be inserted into the SQL, separated by commas.

// This will return "SELECT * FROM users WHERE user_id IN (1, 3, 7, 10)"
$sql = $db->escape(
    "SELECT * FROM users WHERE user_id IN (%i)",
    array(1, 3, 7, 10)
);

Escaping Identifiers

All databases support quoting table and columns to allow SQL reserved words to be used as identifiers. The SQL standard is to use double quotes. Just like data-type placeholders, there is an identifier placeholder, %r, that can be used with escape() and the various query() methods.

$result = $db->query("SELECT * FROM %r WHERE %r = %i", 'users', 'user_id', 1);

This functionality will only be useful when dynamically creating SQL commands. With static SQL, developers can simply wrap identifiers in double quotes themselves.

$result = $db->query('SELECT * FROM "users" WHERE "user_id" = %i', 1);

Unescaping Data

In addition to databases requiring that data going in be formatted a certain way, many database/driver combinations in PHP dont deliver values back in the appropriate PHP data type. The unescape() method provides a consistent way to ensure that all data coming out of the database is stored correctly in PHP.

The first parameter, $data_type, should be one of the string data type names or a data type placeholder as convered with the escape() method. The second parameter, $value, is the value being returned from the database.

Here are some examples of using the unescape() method:


$is_authorized = $db->unescape('boolean', $row['is_authorized']);

$date_created  = $db->unescape('%d', $row['date_created']);

Please note that if you are using the ORM in Flourish, unescaping will be done automatically.

Prepared Statements

Prepared statements can improve performance when working with a query that will be executed multiple times with different sets of data. The prepare() and translatedPrepare() methods accept a single parameter, $sql, which should contain the SQL statement to prepare. They both return an fStatement object which can in turn be passed to execute(), query() or unbufferedQuery() in place of a SQL string.

$statement = $db->prepare("INSERT INTO users (first_name, last_name) VALUES (%s, %s)");

$db->query($statement, 'Will', 'Bond');
$db->query($statement, 'John', 'Smith');

Just like with query() and translatedQuery(), the values for the placeholders are passed as parameters after the fStatement object.

Performance

Computation is saved since the placeholders are parsed only when the statement is created, instead of upon every execution. If translatedPrepare() is called, the SQL translation is also performed only once.

In addition to Flourish level optimizations, many databases and PHP database extensions support prepared statements and may improve performance by caching query plans. For the databases/extensions that don't support prepared statements, a prepared statement will be executed behind the scenes using the normal query() method.

Please note, due to the way that fDatabase is written, prepared statements are not necessary to prevent SQL injection attacks.

Limitations

The one limitation of using prepared statements instead of a normal query is that multiple values can not be passed for a placeholder. For those situations, execute(), query() or translatedQuery() should be used instead.

// This will NOT work
$statement = $db->prepare("SELECT * FROM users WHERE user_id IN (%i)");
$res       = $db->query($statement, array(1, 2, 3));
foreach (res as $row) {
    // 
}

Debugging

There is some useful debugging functionality built into the class that can help when diagnosing SQL issues. If enableDebugging() or fCore::enableDebugging() (for global debugging) is called with TRUE, the fDatabase class will display each SQL statement and how long it took to perform. In addition, when the class is destructed, a total SQL execution time will be output.

// Enable SQL statement printing
$db->enableDebugging(TRUE);

Caching

Both PostgreSQL and Oracle databases require some schema information about the database to properly fetch the last generated auto-incrementing primary key that is generated from an INSERT SQL statement. The fDatabase class will automatically retrieve that schema information, however in the interest of performance, you may wish to cache the results.

Along a similar vein, MSSQL databases don't support UTF-8 as the character set for non-national data types, so the fDatabase class will determine the databases character set to ensure proper transcoding to UTF-8 is performed.

The enableCaching() method accepts an instance of the fCache class, and will save this schema information so it does not need to be fetched on each page load.

$db->enableCaching(new fCache('file', '/path/to/db.cache'));

The method clearCache() will clear out the cached information, which would be useful when the database schema changes.

When using the Flourish ORM, the fORM class provides some useful caching functionality that will automatically clear the cache when database errors occur.

Hooks

One of the most advanced features of fDatabase is the ability to be able to pass all SQL to a callback at various points in execution to allow for modification or logging. The method registerHookCallback() accepts two parameters, the $hook to register for and the $callback to register.

There are three different hooks avaliable, 'unmodified', 'extracted' and 'run'. The 'unmodified' hook is called with the raw SQL passed to fDatabase, the 'extracted' hooks provides the SQL with all string literals extracted, and the 'run' hook provides the SQL after it has been executed.

The API documentation has details about the required method signatures for each callback. Below are some examples of usage:

// Using the 'extracted' hook to collapsing excess whitespace for easier to read logs
function trim_sql($db, &$sql, &$values) {
    $sql = preg_replace('#\s+#', ' ', $sql);
}
$db->registerHookCallback('extracted', 'trim_sql');


// Using the 'run' hook for logging of slow queries
function log_sql($db, $statement, $query_time, $result) {
    // Don't log queries unless they take half a second or more
    if ($query_time < 0.5) { return; }
    
    // This handles prepared statements since the statement and values are separate
    if (is_array($statement)) {
        $sql = '"' . $statement[0]->getSQL() . '" with the values: ' . join(", ", array_map('fCore::dump', $values));
    } else {
        $sql = '"' . $statement . '"';
    }
    echo 'The following query took ' . $query_time . " seconds: \n" . $sql;
}
$db->registerHookCallback('run', 'log_sql');

fDatabase API Reference

class, v1.0.0b41

Provides a common API for different databases - will automatically use any installed extension

This class is implemented to use the UTF-8 character encoding. Please see UTF-8 for more information.

The following databases are supported:

The class will automatically use the first of the following extensions it finds:

The odbc and pdo_odbc extensions are not supported due to character encoding and stability issues on Windows, and functionality on non-Windows operating systems.

Changes:
1.0.0b41Fixed an array to string conversion notice 9/21/12
1.0.0b40Fixed a bug with notices being triggered when failing to connect to a SQLite database 6/20/11
1.0.0b39Fixed a bug with detecting some MySQL database version numbers 5/24/11
1.0.0b38Backwards Compatibility Break - callbacks registered to the extracted hook via registerHookCallback() no longer receive the $strings parameter, instead all strings are added into the $values parameter - added getVersion(), fixed a bug with SQLite messaging, fixed a bug with __destruct(), improved handling of transactional queries, added close(), enhanced class to throw four different exceptions for different connection errors, silenced PHP warnings upon connection error 5/9/11
1.0.0b37Fixed usage of the mysqli extension to only call mysqli_set_charset() if it exists 3/4/11
1.0.0b36Updated escape() and methods that use escape() to handle float values that don't contain a digit before or after the . 2/1/11
1.0.0b35Updated the class to replace LIMIT and OFFSET value placeholders in the SQL with their values before translating since most databases that translate LIMIT statements need to move or add values together 1/11/11
1.0.0b34Fixed a bug with creating translated prepared statements 1/9/11
1.0.0b33Added code to explicitly set the connection encoding for the mysql and mysqli extensions since some PHP installs don't see to fully respect SET NAMES 12/6/10
1.0.0b32Fixed handling auto-incrementing values for Oracle when the trigger was on INSERT OR UPDATE instead of just INSERT 12/4/10
1.0.0b31Fixed handling auto-incrementing values for MySQL when the INTO keyword is left out of an INSERT statement 11/4/10
1.0.0b30Fixed the pgsql, mssql and mysql extensions to force a new connection instead of reusing an existing one 8/17/10
1.0.0b29Backwards Compatibility Break - removed enableSlowQueryWarnings(), added ability to replicate via registerHookCallback() 8/10/10
1.0.0b28Backwards Compatibility Break - removed ODBC support. Added support for the pdo_ibm extension. 7/31/10
1.0.0b27Fixed a bug with running multiple copies of a SQL statement with string values through a single translatedQuery() call 7/14/10
1.0.0b26Updated the class to use new fCore functionality 7/5/10
1.0.0b25Added IBM DB2 support 4/13/10
1.0.0b24Fixed an auto-incrementing transaction bug with Oracle and debugging issues with all databases 3/17/10
1.0.0b23Resolved another bug with capturing auto-incrementing values for PostgreSQL and Oracle 3/15/10
1.0.0b22Changed clearCache() to also clear the cache on the fSQLTranslation 3/9/10
1.0.0b21Added execute() for result-less SQL queries, prepare() and translatedPrepare() to create fStatement objects for prepared statements, support for prepared statements in query() and unbufferedQuery(), fixed default caching key for enableCaching() 3/2/10
1.0.0b20Added a parameter to enableCaching() to provide a key token that will allow cached values to be shared between multiple databases with the same schema 10/28/09
1.0.0b19Added support for escaping identifiers (column and table names) to escape(), added support for database schemas, rewrote internal SQL string spliting 10/22/09
1.0.0b18Updated the class for the new fResult and fUnbufferedResult APIs, fixed unescape() to not touch NULLs 8/12/09
1.0.0b17Added the ability to pass an array of all values as a single parameter to escape() instead of one value per parameter 8/11/09
1.0.0b16Fixed PostgreSQL and Oracle from trying to get auto-incrementing values on inserts when explicit values were given 8/6/09
1.0.0b15Fixed a bug where auto-incremented values would not be detected when table names were quoted 7/15/09
1.0.0b14Changed determineExtension() and determineCharacterSet() to be protected instead of private 7/8/09
1.0.0b13Updated escape() to accept arrays of values for insertion into full SQL strings 7/6/09
1.0.0b12Updates to unescape() to improve performance 6/15/09
1.0.0b11Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b10Changed date/time/timestamp escaping from strtotime() to fDate/fTime/fTimestamp for better localization support 6/1/09
1.0.0b9Fixed a bug with escape() where floats that start with a . were encoded as NULL 5/9/09
1.0.0b8Added Oracle support, change PostgreSQL code to no longer cause lastval() warnings, added support for arrays of values to escape() 5/3/09
1.0.0b7Updated for new fCore API 2/16/09
1.0.0b6Fixed a bug with executing transaction queries when using the mysqli extension 2/12/09
1.0.0b5Changed @ error suppression operator to error_reporting() calls 1/26/09
1.0.0b4Added a few error suppression operators back in so that developers don't get errors and exceptions 1/14/09
1.0.0b3Removed some unnecessary error suppresion operators 12/11/08
1.0.0b2Fixed a bug with PostgreSQL when using the PDO extension and executing an INSERT statement 12/11/08
1.0.0bThe initial implementation 9/25/07

Variables

->extension protected

The extension to use for the database specified

Options include:

  • 'ibm_db2'
  • 'mssql'
  • 'mysql'
  • 'mysqli'
  • 'oci8'
  • 'pgsql'
  • 'sqlite'
  • 'sqlsrv'
  • 'pdo'
Type

string

->schema_info protected

A cache of database-specific code

Type

array

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

Methods

->__construct() public

Configures the connection to a database - connection is not made until the first query is executed

Passing NULL to any parameter other than $type and $database will cause the default value to be used.

Signature

fDatabase __construct( string $type, string $database, string $username=NULL, string $password=NULL, string $host=NULL, integer $port=NULL, integer $timeout=NULL )

Parameters
string $type The type of the database: 'db2', 'mssql', 'mysql', 'oracle', 'postgresql', 'sqlite'
string $database Name of the database. If SQLite the path to the database file.
string $username Database username - not used for SQLite
string $password The password for the username specified - not used for SQLite
string $host Database server host or IP, defaults to localhost - not used for SQLite. MySQL socket connection can be made by entering 'sock:' followed by the socket path. PostgreSQL socket connection can be made by passing just 'sock:'.
integer $port The port to connect to, defaults to the standard port for the database type specified - not used for SQLite
integer $timeout The number of seconds to timeout after if a connection can not be made - not used for SQLite
->__destruct() internal public

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

Closes the open database connection

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->clearCache() public

Clears all of the schema info out of the object and, if set, the fCache object

Signature

void clearCache( )

->close() public

Closes the database connection

Signature

void close( )

->connect() public

Connects to the database specified, if no connection exists

This method is only intended to force a connection, all operations that require a database connection will automatically call this method.

Signature

void connect( )

Throws
fAuthorizationException
When the username and password are not accepted
->determineCharacterSet() protected

Determines the character set of a SQL Server database

Signature

void determineCharacterSet( )

->determineExtension() protected

Figures out which extension to use for the database type selected

Signature

void determineExtension( )

->enableCaching() public

Sets the schema info to be cached to the fCache object specified

Signature

void enableCaching( fCache $cache, string $key_token=NULL )

Parameters
fCache $cache The cache to cache to
string $key_token Internal use only! (this will be used in the cache key to uniquely identify the cache for this fDatabase object)
->enableDebugging() public

Sets if debug messages should be shown

Signature

void enableDebugging( boolean $flag )

Parameters
boolean $flag If debugging messages should be shown
->escape() public

Escapes a value for insertion into SQL

The valid data types are:

  • 'blob'
  • 'boolean'
  • 'date'
  • 'float'
  • 'identifier'
  • 'integer'
  • 'string' (also varchar, char or text)
  • 'varchar'
  • 'char'
  • 'text'
  • 'time'
  • 'timestamp'

In addition to being able to specify the data type, you can also pass in an SQL statement with data type placeholders in the following form:

  • %l for a blob
  • %b for a boolean
  • %d for a date
  • %f for a float
  • %r for an indentifier (table or column name)
  • %i for an integer
  • %s for a string
  • %t for a time
  • %p for a timestamp

Depending on what $sql_or_type and $value are, the output will be slightly different. If $sql_or_type is a data type or a single placeholder and $value is:

  • a scalar value - an escaped SQL string is returned
  • an array - an array of escaped SQL strings is returned

If $sql_or_type is a SQL string and $value is:

  • a scalar value - the escaped value is inserted into the SQL string
  • an array - the escaped values are inserted into the SQL string separated by commas

If $sql_or_type is a SQL string, it is also possible to pass an array of all values as a single parameter instead of one value per parameter. An example would look like the following:

$db->escape(
    "SELECT * FROM users WHERE status = %s AND authorization_level = %s",
    array('Active', 'Admin')
);
Signature

mixed escape( string $sql_or_type, mixed $value [, ... ] )

Parameters
string $sql_or_type This can either be the data type to escape or an SQL string with a data type placeholder - see method description
mixed $value [, ... ] The value to escape - both single values and arrays of values are supported, see method description for details
Returns

The escaped value/SQL or an array of the escaped values

->execute() public

Executes one or more SQL queries without returning any results

Signature

void execute( string|fStatement $statement, mixed $value [, ... ] )

Parameters
string|fStatement $statement One or more SQL statements in a string or an fStatement prepared statement
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
->getConnection() public

Returns the database connection resource or object

Signature

mixed getConnection( )

Returns

The database connection

->getDatabase() public

Gets the name of the database currently connected to

Signature

string getDatabase( )

Returns

The name of the database currently connected to

->getExtension() internal public

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

Gets the php extension being used

Signature

string getExtension( )

Returns

The php extension used for database interaction

->getHost() public

Gets the host for this database

Signature

string getHost( )

Returns

The host

->getPort() public

Gets the port for this database

Signature

string getPort( )

Returns

The port

->getSQLTranslation() public

Gets the fSQLTranslation object used for translated queries

Signature

fSQLTranslation getSQLTranslation( )

Returns

The SQL translation object

->getType() public

Gets the database type

Signature

string getType( )

Returns

The database type: 'mssql', 'mysql', 'postgresql' or 'sqlite'

->getUsername() public

Gets the username for this database

Signature

string getUsername( )

Returns

The username

->getVersion() public

Gets the version of the database system

Signature

string getVersion( )

Returns

The database system version

->inject() internal public

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

Injects an fSQLTranslation object to handle translation

Signature

void inject( fSQLTranslation $sql_translation )

Parameters
fSQLTranslation $sql_translation The SQL translation object
->isInsideTransaction() public

Will indicate if a transaction is currently in progress

Signature

boolean isInsideTransaction( )

Returns

If a transaction has been started and not yet rolled back or committed

->prepare() public

Prepares a single fStatement object to execute prepared statements

Identifier placeholders (%r) are not supported with prepared statements. In addition, multiple values can not be escaped by a placeholder - only a single value can be provided.

Signature

fStatement prepare( string $sql )

Parameters
string $sql The SQL to prepare
Returns

A prepared statement object that can be passed to query(), unbufferedQuery() or execute()

->preprocess() internal public

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

Preprocesses SQL by escaping values, spliting queries, cleaning escaped semicolons, fixing backslashed single quotes and translating

Signature

array preprocess( string $sql, array $values, boolean $translate, array &$rollback_queries=NULL )

Parameters
string $sql The SQL to process
array $values Literal values to escape into the SQL
boolean $translate If the SQL should be translated
array &$rollback_queries MySQL doesn't allow transactions around ALTER TABLE statements, and some of those require multiple statements, so this is an array of "undo" SQL statements
Returns

The split out SQL queries, queries that have been translated will have a string key of a number, : and the original SQL, non-translated SQL will have a numeric key

->query() public

Executes one or more SQL queries and returns the result(s)

Signature

fResult|array query( string|fStatement $statement, mixed $value [, ... ] )

Parameters
string|fStatement $statement One or more SQL statements in a string or a single fStatement prepared statement
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
Returns

The fResult object(s) for the query

->registerHookCallback() public

Registers a callback for one of the various query hooks - multiple callbacks can be registered for each hook

The following hooks are available:

  • 'unmodified': The original SQL passed to fDatabase, for prepared statements this is called just once before the fStatement object is created
  • 'extracted': The SQL after all non-empty strings have been extracted and replaced with ordered sprintf-style placeholders
  • 'run': After the SQL has been run

Methods for the 'unmodified' hook should have the following signature:

  • $database: The fDatabase instance
  • &$sql: The original, unedited SQL
  • &$values: The values to be escaped into the placeholders in the SQL

Methods for the 'extracted' hook should have the following signature:

  • $database: The fDatabase instance
  • &$sql: The SQL with all strings removed and replaced with %1$s-style placeholders
  • &$values: The values to be escaped into the placeholders in the SQL

The extracted hook is the best place to modify the SQL since there is no risk of breaking string literals. Please note that there may be empty strings ('') present in the SQL since some databases treat those as NULL.

Methods for the 'run' hook should have the following signature:

  • $database: The fDatabase instance
  • $query: The (string) SQL or array(0 => {fStatement object}, 1 => {values array})
  • $query_time: The (float) number of seconds the query took
  • $result The fResult or fUnbufferedResult object, or FALSE if no result
Signature

void registerHookCallback( string $hook, callback $callback )

Parameters
string $hook The hook to register for
callback $callback The callback to register - see the method description for details about the method signature
->translatedExecute() public

Translates one or more SQL statements using fSQLTranslation and executes them without returning any results

Signature

void translatedExecute( string $sql, mixed $value [, ... ] )

Parameters
string $sql One or more SQL statements
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
->translatedPrepare() public

Translates a SQL statement and creates an fStatement object from it

Identifier placeholders (%r) are not supported with prepared statements. In addition, multiple values can not be escaped by a placeholder - only a single value can be provided.

Signature

fStatement translatedPrepare( string $sql )

Parameters
string $sql The SQL to prepare
Returns

A prepared statement object that can be passed to query(), unbufferedQuery() or execute()

->translatedQuery() public

Translates one or more SQL statements using fSQLTranslation and executes them

Signature

fResult|array translatedQuery( string $sql, mixed $value [, ... ] )

Parameters
string $sql One or more SQL statements
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
Returns

The fResult object(s) for the query

->unbufferedQuery() public

Executes a single SQL statement in unbuffered mode. This is optimal for

large results sets since it does not load the whole result set into memory first. The gotcha is that only one unbuffered result can exist at one time. If another unbuffered query is executed, the old result will be deleted.

Signature

fUnbufferedResult unbufferedQuery( string|fStatement $statement, mixed $value [, ... ] )

Parameters
string|fStatement $statement A single SQL statement
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
Returns

The result object for the unbuffered query

->unbufferedTranslatedQuery() public

Translates the SQL statement using fSQLTranslation and then executes it

in unbuffered mode. This is optimal for large results sets since it does not load the whole result set into memory first. The gotcha is that only one unbuffered result can exist at one time. If another unbuffered query is executed, the old result will be deleted.

Signature

fUnbufferedResult unbufferedTranslatedQuery( string $sql, mixed $value [, ... ] )

Parameters
string $sql A single SQL statement
mixed $value [, ... ] The optional value(s) to place into any placeholders in the SQL - see escape() for details
Returns

The result object for the unbuffered query

->unescape() public

Unescapes a value coming out of a database based on its data type

The valid data types are:

  • 'blob' (or '%l')
  • 'boolean' (or '%b')
  • 'date' (or '%d')
  • 'float' (or '%f')
  • 'integer' (or '%i')
  • 'string' (also '%s', 'varchar', 'char' or 'text')
  • 'time' (or '%t')
  • 'timestamp' (or '%p')
Signature

mixed unescape( string $data_type, mixed $value )

Parameters
string $data_type The data type being unescaped - see method description for valid values
mixed $value The value or array of values to unescape
Returns

The unescaped value

fDate

The fDate class is a value object representation of a date. One of the primary attributes of the object is that its value can not be changed, but instead a new object is created.

This class is built on top of the PHP date/time functions and can only handle dates ranging from 1901&ndash;2038.

Instantiation

The fDate constructor takes a single argument, either a string, integer or object (with a __toString() method) representing a date. Any string format accepted by strtotime() will work.

$date1 = new fDate('today');
$date2 = new fDate('sunday');
$date3 = new fDate('3 Feb 2008');
$date4 = new fDate('2005-01-02');

Modification

Rather than allowing an fDate object value to be modified, which can create issues since objects are passed by reference, all changes to a date create a new object.

Usually when modifying a date, only one or two components (such as month or year) of the date will change. The modify() method leverages the formatting codes from the date() function to keep parts of the existing date while replacing others.

Here are some examples of modify():

// The new dates year would be 2007 while the month and day would be the same
$new_date = $date1->modify('2007-m-d');

// The new date would be the 1st day (Monday) of the 9th week of the year
$new_date = $date2->modify('Y-\W9-1');

// The new date would simply change the day of the month to the 1st
$new_date = $date3->modify('Y-m-01');

// The new date would have the same year and day, but the month would be June
$new_date = $date4->modify('Y-06-d');

Adjustments

Occasionally you may have the need to adjust a date. The adjust() method takes a single parameter which can contain any relative time measurement that strtotime() accepts. Since the fDate class is an immutable value object, calls to adjust() return a new fDate object.

$new_date = $date1->adjust('tomorrow');
$new_date = $date2->adjust('+1 day');
$new_date = $date3->adjust('-2 years +1 week');
$new_date = $date4->adjust('next wednesday');

Formatting

To format the date, simply call the format() method with any valid date formatting string from date(). Here are some examples:

// Normal date formatting
echo $date1->format('Y-m-d');
echo $date2->format('n/j/y');

Comparing

There are five different methods available to compare dates, eq(), gt(), gte(), lt() and lte(). Each method optionally accepts a parameter $other_date. If no $other_date is specified, the date is compared to the current date. If $other_date is specified, the two are compared. $other_date accepts any valid date descriptor that works with __construct().

Here are some examples:

$today    = new fDate();
$tomorrow = new fDate('+1 day');

// These return TRUE
$today->eq();
$today->eq('now');
$today->lt($tomorrow);
$today->lte($tomorrow);
$today->lt('+1 year');

// These calls return FALSE
$tomorrow->lt($today);
$today->gt($tomorrow);
$today->gte($tomorrow);

Fuzzy Differences

If you are looking to get a fuzzy difference between two dates for display, youll want to use the getFuzzyDifference() method. The first parameter, $other_date, optionally accepts a valid date descriptor that can be passed to __construct(). If one is passed, the difference will be between the two dates, if nothing is passed, the difference will be between the fDate and the current date.

The value returned by getFuzzyDifference() will be a string representing the most broad time measurement between the two dates. In addition, if the difference is just shy of the next largest time measurement, it will be rounded up. Thus 3.5 weeks would become 1 month.

Here are some examples to clarify. The following examples are comparing two date descriptors:

$date1 = new fDate('2008-01-01');
$date2 = new fDate('2008-01-04');

echo $date1->getFuzzyDifference($date2);
// Output: 3 days before

echo $date2->getFuzzyDifference($date1);
// Output: 3 days after

$date3 = new fDate('2008-01-10');

echo $date3->getFuzzyDifference('2008-01-01');
// Output: 1 week after

$date4 = new fDate('2008-01-28');

echo $date4->getFuzzyDifference('2008-01-01');
// Output: 1 month after

These examples show output when comparing an fDate object with the current date:

// First, lets assume today is January 1st, 2008.

$date1 = new fDate('2008-01-04');
$date2 = new fDate('2008-01-09');
$date2 = new fDate('2007-12-02');

echo $date1->getFuzzyDifference();
// Output: 3 days from now

echo $date2->getFuzzyDifference();
// Output: 1 week from now

echo $date3->getFuzzyDifference();
// Output: 1 month ago

An optional boolean parameter, $simple, can also be passed to getFuzzyDifference(). When TRUE, this parameter causes the method to return the difference in time, but not the direction.

$date1 = new fDate('2008-01-01');
$date2 = new fDate('2008-01-04');

echo $date1->getFuzzyDifference($date2, TRUE);
// Output: 3 days

echo $date2->getFuzzyDifference($date1, TRUE);
// Output: 3 days

$date3 = new fDate('2008-01-10');

echo $date3->getFuzzyDifference('2008-01-01', TRUE);
// Output: 1 week

fDate API Reference

class, v1.0.0b11

Represents a date as a value object

Changes:
1.0.0b11Fixed a method signature 8/24/11
1.0.0b10Fixed a bug with the constructor not properly handling unix timestamps that are negative integers 6/2/11
1.0.0b9Changed the $date attribute to be protected 3/20/11
1.0.0b8Added the $simple parameter to getFuzzyDifference() 3/15/10
1.0.0b7Added a call to fTimestamp::callUnformatCallback() in __construct() for localization support 6/1/09
1.0.0b6Backwards compatibility break - Removed getSecondsDifference(), added eq(), gt(), gte(), lt(), lte() 3/5/09
1.0.0b5Updated for new fCore API 2/16/09
1.0.0b4Fixed __construct() to properly handle the 5.0 to 5.1 change in strtotime() 1/21/09
1.0.0b3Added support for CURRENT_TIMESTAMP and CURRENT_DATE SQL keywords 1/11/09
1.0.0b2Removed the adjustment amount check from adjust() 12/31/08
1.0.0bThe initial implementation 2/10/08

Variables

->date protected

A timestamp of the date

Type

integer

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

Methods

->__construct() public

Creates the date to represent, no timezone is allowed since dates don't have timezones

Signature

fDate __construct( fDate|object|string|integer $date=NULL )

Parameters
fDate|object|string|integer $date The date to represent, NULL is interpreted as today
Throws
fValidationException
When $date is not a valid date
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Returns this date in Y-m-d format

Signature

string __toString( )

Returns

The Y-m-d format of this date

->adjust() public

Changes the date by the adjustment specified, only adjustments of a day or more will be made

Signature

fDate adjust( string $adjustment )

Parameters
string $adjustment The adjustment to make
Returns

The adjusted date

Throws
fValidationException
When $adjustment is not a relative date measurement
->eq() public

If this date is equal to the date passed

Signature

boolean eq( fDate|object|string|integer $other_date=NULL )

Parameters
fDate|object|string|integer $other_date The date to compare with, NULL is interpreted as today
Returns

If this date is equal to the one passed

->format() public

Formats the date

Signature

string format( string $format )

Parameters
string $format The date() function compatible formatting string, or a format name from fTimestamp::defineFormat()
Returns

The formatted date

Throws
fValidationException
When a non-date formatting character is included in $format
->getFuzzyDifference() public

Returns the approximate difference in time, discarding any unit of measure but the least specific.

The output will read like:

  • "This date is {return value} the provided one" when a date it passed
  • "This date is {return value}" when no date is passed and comparing with today

Examples of output for a date passed might be:

  • '2 days after'
  • '1 year before'
  • 'same day'

Examples of output for no date passed might be:

  • '2 days from now'
  • '1 year ago'
  • 'today'

You would never get the following output since it includes more than one unit of time measurement:

  • '3 weeks and 1 day'
  • '1 year and 2 months'

Values that are close to the next largest unit of measure will be rounded up:

  • 6 days would be represented as 1 week, however 5 days would not
  • 29 days would be represented as 1 month, but 21 days would be shown as 3 weeks
Signatures

string getFuzzyDifference( fDate|object|string|integer $other_date=NULL, boolean $simple=FALSE )

string getFuzzyDifference( boolean $simple=FALSE )

Parameters
fDate|object|string|integer $other_date The date to create the difference with, NULL is interpreted as today
boolean $simple When TRUE, the returned value will only include the difference in the two dates, but not from now, ago, after or before
Returns

The fuzzy difference in time between the this date and the one provided

->gt() public

If this date is greater than the date passed

Signature

boolean gt( fDate|object|string|integer $other_date=NULL )

Parameters
fDate|object|string|integer $other_date The date to compare with, NULL is interpreted as today
Returns

If this date is greater than the one passed

->gte() public

If this date is greater than or equal to the date passed

Signature

boolean gte( fDate|object|string|integer $other_date=NULL )

Parameters
fDate|object|string|integer $other_date The date to compare with, NULL is interpreted as today
Returns

If this date is greater than or equal to the one passed

->lt() public

If this date is less than the date passed

Signature

boolean lt( fDate|object|string|integer $other_date=NULL )

Parameters
fDate|object|string|integer $other_date The date to compare with, NULL is interpreted as today
Returns

If this date is less than the one passed

->lte() public

If this date is less than or equal to the date passed

Signature

boolean lte( fDate|object|string|integer $other_date=NULL )

Parameters
fDate|object|string|integer $other_date The date to compare with, NULL is interpreted as today
Returns

If this date is less than or equal to the one passed

->modify() public

Modifies the current date, creating a new fDate object

The purpose of this method is to allow for easy creation of a date based on this date. Below are some examples of formats to modify the current date:

  • 'Y-m-01' to change the date to the first of the month
  • 'Y-m-t' to change the date to the last of the month
  • 'Y-\W5-N' to change the date to the 5th week of the year
Signature

fDate modify( string $format )

Parameters
string $format The current date will be formatted with this string, and the output used to create a new object
Returns

The new date

fDirectory

The fDirectory class is a simple object representation of a directory on the filesystem. It provides an object-based interface to common directory functions and allows actions to be grouped into transactions via the fFilesystem class.

Instantiation

The fDirectory constructor takes a single argument, the filesystem path to a directory. The path can be absolute or relative.

$directory1 = new fDirectory('/var/www/vhosts/examples.com/httpdocs/images/');
$directory2 = new fDirectory('./output/');
$directory3 = new fDirectory('../uploads/documents/');

It is also possible to create a directory on the filesystem and instantiate an fDirectory object for that new directory by calling the static method create(). create() takes up to two parameters, with the first being $directory_path and the second optional parameter being $mode. The $mode parameter allows setting the permissions for the new directory, and usually is set using an octal number (represented by a number with a leading zero such as 0777).

Please note that directory creation is recursive, so any non-existant parent directories will also be created as needed.

$new_directory = fDirectory::create('./logs/');

Getting Information

The fDirectory class includes a few methods to grab some basic information about a directory. These include:

Method Description
getName() Returns the name of the directory as a string - does not include the full path
getSize() Returns the size of the directory and all contents in bytes as an integer, or optionally formatted for easy human readability
getParent() Returns the fDirectory object for the directorys parent directory
getPath() Returns the full path to the directory as a string
isWritable() Indicates if the directory can be written to by the current user

If you want more specific information about a directory, you can pass the output of getPath() into the various PHP filesystem functions.

Manipulation

The following methods provide a straight-forward interface for some standard manipulation of directories. Also note that these manipulations can be wrapped in a filesystem transactions to allow for rolling back changes in the event of a later error.

Method Description
rename() Renames the directory to a new path
move() Moves the directory into a different parent directory, keeping the current name
delete() Removes the directory and all contained files and folders from the filesystem. Please note that if inside of a filesystem transaction, this event will be deferred until commit is called, but instances of fDirectory and fFile for all affected files and directories will act as if the directory/file no longer exists.
clear() Removes all contained files and folders from the filesystem. Please note that if inside of a filesystem transaction, this event will be deferred until commit is called, but instances of fDirectory and fFile for all affected files and directories will act as if the directory/file no longer exists.

Listing Children

The scan() and scanRecursive() method provide functionality for listing all fFile, fImage and fDirectory children of an fDirectory object. Both return an array of objects, however scan() returns only direct children, whereas scanRecursive() returns all descendants.

$children = $dir->scan();

$descendants = $dir->scanRecursive();

Both methods accept a single optional parameter, $filter. The filter can be a valid PCRE regex pattern, or a string pattern containing * and ? wildcards. * will match zero or more characters, while ? will match zero or one characters. When matching the $filter, all file paths will have / directory separators, and all directories will end in /.

// This will list all directory children
$directories = $dir->scan('*/');

// This will find all jpg and gif files under the current directory
$images = $dir->scanRecursive('#\.(jpe?g|gif)$#i');

fDirectory API Reference

class, v1.0.0b14

Represents a directory on the filesystem, also provides static directory-related methods

Changes:
1.0.0b14Fixed a bug in delete() where a non-existent method was being called on fFilesystem, added a permission check to delete() 8/23/11
1.0.0b13Added the clear() method 1/10/11
1.0.0b12Fixed scanRecursive() to not add duplicate entries for certain nested directory structures 8/10/10
1.0.0b11Fixed scan() to properly add trailing /s for directories 3/16/10
1.0.0b10BackwardsCompatibilityBreak - Fixed scan() and scanRecursive() to strip the current directory's path before matching, added support for glob style matching 3/5/10
1.0.0b9Changed the way directories deleted in a filesystem transaction are handled, including improvements to the exception that is thrown 3/5/10
1.0.0b8Backwards Compatibility Break - renamed getFilesize() to getSize(), added move() 12/16/09
1.0.0b7Fixed __construct() to throw an fValidationException when the directory does not exist 8/21/09
1.0.0b6Fixed a bug where deleting a directory would prevent any future operations in the same script execution on a file or directory with the same path 8/20/09
1.0.0b5Added the ability to skip checks in __construct() for better performance in conjunction with fFilesystem::createObject() 8/6/09
1.0.0b4Refactored scan() to use the new fFilesystem::createObject() method 1/21/09
1.0.0b3Added the $regex_filter parameter to scan() and scanRecursive(), fixed bug in scanRecursive() 1/5/09
1.0.0b2Removed some unnecessary error suppresion operators 12/11/08
1.0.0bThe initial implementation 12/21/07

Variables

->deleted protected

A backtrace from when the file was deleted

Type

array

->directory protected

The full path to the directory

Type

string

Static Methods

::create() public

Creates a directory on the filesystem and returns an object representing it

The directory creation is done recursively, so if any of the parent directories do not exist, they will be created.

This operation will be reverted by a filesystem transaction being rolled back.

Signature

fDirectory create( string $directory, numeric $mode=0777 )

Parameters
string $directory The path to the new directory
numeric $mode The mode (permissions) to use when creating the directory. This should be an octal number (requires a leading zero). This has no effect on the Windows platform.
Throws
fValidationException
When no directory was specified, or the directory already exists
::makeCanonical() public

Makes sure a directory has a / or \ at the end

Signature

string makeCanonical( string $directory )

Parameters
string $directory The directory to check
Returns

The directory name in canonical form

Methods

->__construct() public

Creates an object to represent a directory on the filesystem

If multiple fDirectory objects are created for a single directory, they will reflect changes in each other including rename and delete actions.

Signature

fDirectory __construct( string $directory, boolean $skip_checks=FALSE )

Parameters
string $directory The path to the directory
boolean $skip_checks If file checks should be skipped, which improves performance, but may cause undefined behavior - only skip these if they are duplicated elsewhere
Throws
fValidationException
When no directory was specified, when the directory does not exist or when the path specified is not a directory
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Returns the full filesystem path for the directory

Signature

string __toString( )

Returns

The full filesystem path

->clear() public

Removes all files and directories inside of the directory

Signature

void clear( )

->delete() public

Will delete a directory and all files and directories inside of it

This operation will not be performed until the filesystem transaction has been committed, if a transaction is in progress. Any non-Flourish code (PHP or system) will still see this directory and all contents as existing until that point.

Signature

void delete( )

->getName() public

Gets the name of the directory

Signature

string getName( )

Returns

The name of the directory

->getParent() public

Gets the parent directory

Signature

fDirectory getParent( )

Returns

The object representing the parent directory

->getPath() public

Gets the directory's current path

If the web path is requested, uses translations set with fFilesystem::addWebPathTranslation()

Signature

string getPath( boolean $translate_to_web_path=FALSE )

Parameters
boolean $translate_to_web_path If the path should be the web path
Returns

The path for the directory

->getSize() public

Gets the disk usage of the directory and all files and folders contained within

This method may return incorrect results if files over 2GB exist and the server uses a 32 bit operating system

Signature

integer|string getSize( boolean $format=FALSE, integer $decimal_places=1 )

Parameters
boolean $format If the filesize should be formatted for human readability
integer $decimal_places The number of decimal places to format to (if enabled)
Returns

If formatted, a string with filesize in b/kb/mb/gb/tb, otherwise an integer

->isWritable() public

Check to see if the current directory is writable

Signature

boolean isWritable( )

Returns

If the directory is writable

->move() public

Moves the current directory into a different directory

Please note that rename() will rename a directory in its current parent directory or rename it into a different parent directory.

If the current directory's name already exists in the new parent directory and the overwrite flag is set to false, the name will be changed to a unique name.

This operation will be reverted if a filesystem transaction is in progress and is later rolled back.

Signature

fDirectory move( fDirectory|string $new_parent_directory, boolean $overwrite )

Parameters
fDirectory|string $new_parent_directory The directory to move this directory into
boolean $overwrite If the current filename already exists in the new directory, TRUE will cause the file to be overwritten, FALSE will cause the new filename to change
Returns

The directory object, to allow for method chaining

Throws
fValidationException
When the new parent directory passed is not a directory, is not readable or is a sub-directory of this directory
->rename() public

Renames the current directory

This operation will NOT be performed until the filesystem transaction has been committed, if a transaction is in progress. Any non-Flourish code (PHP or system) will still see this directory (and all contained files/dirs) as existing with the old paths until that point.

Signature

void rename( string $new_dirname, boolean $overwrite )

Parameters
string $new_dirname The new full path to the directory or a new name in the current parent directory
boolean $overwrite If the new dirname already exists, TRUE will cause the file to be overwritten, FALSE will cause the new filename to change
->scan() public

Performs a scandir() on a directory, removing the . and .. entries

If the $filter looks like a valid PCRE pattern - matching delimeters (a delimeter can be any non-alphanumeric, non-backslash, non-whitespace character) followed by zero or more of the flags i, m, s, x, e, A, D, S, U, X, J, u - then preg_match() will be used.

Otherwise the $filter will do a case-sensitive match with * matching zero or more characters and ? matching a single character.

On all OSes (even Windows), directories will be separated by /s when comparing with the $filter.

Signature

array scan( string $filter=NULL )

Parameters
string $filter A PCRE or glob pattern to filter files/directories by path - directories can be detected by checking for a trailing / (even on Windows)
Returns

The fFile (or fImage) and fDirectory objects for the files/directories in this directory

->scanRecursive() public

Performs a recursive scandir() on a directory, removing the . and .. entries

Signature

array scanRecursive( string $filter=NULL )

Parameters
string $filter A PCRE or glob pattern to filter files/directories by path - see scan() for details
Returns

The fFile (or fImage) and fDirectory objects for the files/directories (listed recursively) in this directory

->tossIfDeleted() protected

Throws an exception if the directory has been deleted

Signature

void tossIfDeleted( )

fEmail

The fEmail class provides an interface to very easily send well-formed emails with UTF-8 content, HTML, attachments and S/MIME encryption and signing. fEmail uses PHPs built-in mail() function by default, but can be used with fSMTP for mass emailing or mailing via a remote mail server.

Instantiation

Creating a new email is as simple as making a new instance of fEmail:

$email = new fEmail();

Recipients

fEmail supports To:, CC: and BCC: recipients through the methods addRecipient(), addCCRecipient() and addBCCRecipient(). Each method requires a single parameter $email and accepts a second optional parameter $name. The add methods can be called any number of times to add any number of recipients:

// Add our recipient
$email->addRecipient('will@example.com', 'Will');

// Add a few people to the CC and BCC lists
$email->addCCRecipient('john@example.com');
$email->addCCRecipient('bob@example.com', 'Bob Smith, Jr.');

$email->addBCCRecipient('alice@example.com');

If need be, all regular, CC and BCC recipients can be cleared by calling the method clearRecipients().

$email->clearRecipients();

From Headers

When sending a message you will also need to set the From: address via the method setFromEmail(). The first parameter, $email, is required, however the second parameter, $name, is optional. Unfortunately the implementation of mail() on Windows does not allow setting the $name parameter. A $name will work on Windows if you use fSMTP.

// Set who the email is from
$email->setFromEmail('will@example.com');

// Include the users name
$email->setFromEmail('will@example.com', 'Will');

It is also possible to set the Return-Path: header on almost all servers. This email address is listed as the true source of the email and will be the recipient of any bounces or delivery notifications. The method setBounceToEmail() sets this address with just a single parameter $email:

$email->setBounceToEmail('will@example.com');

Subject and Body

All email must have a subject and body set by the methods setSubject() and setBody(). Both of these methods accept a single parameter, a UTF-8 string.

// Set the subject include UTF-8 curly quotes
$email->setSubject('This wont break email programs because it is properly encoded by the class');

// Set the body to include a string containing UTF-8
$email->setBody('This is the body of the email');

Inline Body Templating

The setBody() method can also take a second parameter, $unindent_expand_constants, which is a boolean. When this parameter is TRUE, the body will be unindented as much as possible, and will have all {CONSTANT_NAME} strings replaced with the constants value, if defined. This parameter is useful for inline generation of bodies where you dont want to deal with the issues of having a fully unindented end to a HEREDOC string.

For example, the following PHP:

// Unindenting the body and exapanding curly-braced constant names
if ($email_address) {
    $email = new fEmail();
    $email->addRecipient($email_address);
    // 
    $email->setBody("
        Hello $name,
        
        Thanks for submitting your request, well get back to you as soon as we can!

        {EMAIL_SIGNATURE}
    ", TRUE);
}

would create the email body:

Hello John Smith,

Thanks for submitting your request, well get back to you as soon as we can!

Sincerely,
The ACME Co. Team

Without the parameter, the body would be:

 
        Hello John Smith,

        Thanks for submitting your request, well get back to you as soon as we can!

        {EMAIL_SIGNATURE}
 

The HEREDOC equivalent of our desired output would be:

if ($email_address) {
    $email = new fEmail();
    // 
    $signature = EMAIL_SIGNATURE;
    $email->setBody(<<<EOF
Hello $name,

Thanks for submitting your request, well get back to you as soon as we can!

$signature
EOF
    , TRUE);
}

In short, the $unindent_expand_constants parameter allows for cleaner PHP with logical indentation levels.

Loading a Body Template

When dealing with a more complex body example, or wishing to separate the email templates from your PHP, the method loadBody() can be used. This methods accepts two parameters, the $file to load the body from and an array of $replacements to be performed.

The $file parameter can be either a file path or an fFile object. The $replacements parameter should be an associative array with the terms to search for being the keys and the strings to replace them with being the values.

$email->loadBody(
    './email_templates/welcome.txt',
    array(
        '{NAME}'  => $name,
        '{LOGIN}' => $login
    )
);

Please note that the terms to search for do not have to be in any particular format, a simple str_replace() call is used to do the replacements.

HTML Body

It is also possible to add an HTML version of the body by passing it to setHTMLBody(). This HTML content should be encoded using UTF-8:

$email->setHTMLBody('<p>This it the HTML version of the body</p>');

Loading an HTML Body Template

Just like loadBody() for the plaintext body, the HTML body can be loaded from a file via loadHTMLBody(). This methods accepts two parameters, the $file to load the body from and an array of $replacements to be performed.

The $file parameter can be either a file path or an fFile object. The $replacements parameter should be an associative array with the terms to search for being the keys and the strings to replace them with being the values.

$email->loadHTMLBody(
    './email_templates/welcome.html',
    array(
        '{NAME}'  => $name,
        '{LOGIN}' => $login
    )
);

Please note that the terms to search for do not have to be in any particular format, a simple str_replace() call is used to do the replacements.

Self-Contained HTML Images (Related Files)

When using an HTML body, it is possible to add files to the email that can be referenced in the HTML, but do not appear as an attachment. Such related files are added by the addRelatedFile() method. This method requires either one or two parameters: $contents, or $contents and $filename. If $contents is an fFile object, no $filename is necessary. A URL will be returned for direct embedding into HTML src or href attributes.

$logo_url = $email->addRelatedFile(new fFile('./images/logo.gif'));
$email->setHTMLBody("<p>Thanks for your input!</p><p>ACME Corp.<img src='$logo_url' alt='ACME Corp'></p>");

There is an optional third parameter, $mime_type, which can be used to fix incorrect detection of the mime type of a related file.

$logo_url = $email->addRelatedFile(new fFile('./images/logo.gif'), 'email_logo.gif', 'image/gif');

Attachments

Attachments can be added to an email by calling the method addAttachment(). The method requires either one or two parameters: $contents, or $contents and $filename. If $contents is an fFile object, no $filename is necessary.

foreach ($result as $row) {
    $csv_contents .= join(",", $row) . "\n";
}

// Pass in the contents of the file, plus a filename for it
$email->addAttachment($csv_contents, 'report.csv');

// Passing an fFile object
$email->addAttachment(new fFile('./report.pdf'));

There is an optional third parameter, $mime_type, which can be used to fix incorrect detection of the mime type of an attachment.

$email->addAttachment($file_contents, 'filename.tab', 'text/csv');

There is no limit to the number of attachments.

S/MIME Encryption and Signing

The fEmail class provides support for encrypting and signing messages using S/MIME. To encrypt a message, the PEM-encoded public certificate will be needed. To sign a message, the PEM-encoded private key will be needed. To encrypt and sign you will need both (but not for the user). Please see Obtaining a Secure Certificate/Key Pair for information about how to get the necessary files.

To encrypt a message, call the method encrypt() and pass the path to the recipients PEM-encoded public certificate:

$email->encrypt('/path/to/recipients.cer');

To sign a message, call sign() and pass in the senders public certificate path, private key path and private key password (if applicable):

$email->sign('/path/to/senders.cer', '/path/to/senders.key', 'key_password');

If you call both sign() and encrypt() (in either order), the message will be signed, then encrypted and then signed again. This is the most secure method, however certain older email clients may not open such emails.

Sending

To send an email, simply call the send() method.

$message_id = $email->send();

The return value is the generated Message-ID header for the email, which can be used with the In-Reply-To header for tracking replies. The fMailbox class provides functionality for checking and parsing email.

Custom Headers

If there is a need to add custom headers to an email message, the method addCustomHeader() will accept the header $name and $value. The $value should be a plain string - the method will handle any necessary encoding or line wrapping necessary to keep the headers standards-compliant.

$email->addCustomHeader('X-My-Header', 'This is the value I want to send!');

It is possible to set multiple headers at once by passing an associative array to addCustomHeader().

// Set headers that some programs use to indicate priority
$email->addCustomHeader(array(
    'X-Priority' => '1',
    'Importance' => 'High'
));

Fixing Formatting Issues

On some linux/unix server running qmail as the sendmail replacement, you may experience issues with emails looking corrupted to the recipients. This will often take the form of equal signs (=) appearing throughout the content and lines being wrapped in odd places.

The technical cause of the issue is that the qmail sendmail binary is automatically replacing every line feed (\n) with a CR-LF (\r\n) because the email specifications require emails use CR-LF and linux uses LF as the line ending. Qmail is trying to ensure you are sending emails that meet specifications, however it is not checking to see if the email already has the proper line endings.

The first way to try and solve this issue is to use fSMTP and connect to the SMTP server on localhost. This by-passes the command-line interface to qmail:

$smtp = new fSMTP('localhost');
$email->send($smtp);

If that is not possible, the static method fixQmail() should fix the issue. On servers where open_basedir and safe_mode are not in effect, fEmail will make a commandline call to sendmail and will replace all CR-LF with just LF. If that is not possible, fEmail will simply replace all CR-LFs with LF, however emails with long headers may still have issues.

Normally you would only enable this fix if you experience the issue. Since the fix affects all instances of fEmail, youll normally want to call it in a configuration file.

fEmail::fixQmail();

Validating Email Addresses

In certain situations it may be necessary to validate an email address when not using fValidation or fORMValidation with the ORM. The fEmail class provides two regular expression constants to help with the task.

These regular expressions are designed to fully match the mailbox specification of RFC2822 Section 3.4 except for allowing comments and folding white space. Quoted strings are supported along with the +, - and other special characters.

Below is an example of some of the various valid email address formats that are supported:

# regular address
will@example.com

# with + suffix
will+foo@example.com

# with - suffix
will-foo@example.com

# periods
will.foo@example.com

# quoted strings
"will foo"@example.com

# combinations
"will foo".bar+baz@example.com

The two constants are EMAIL_REGEX and NAME_EMAIL_REGEX. EMAIL_REGEX will match an email address, while NAME_EMAIL_REGEX will match a name <email> string.

EMAIL_REGEX captures 3 subpatterns in the following format:

For example:

preg_match(fEmail::EMAIL_REGEX, 'will@example.com', $matches);

echo $matches[0] . "\n";
echo $matches[1] . "\n";
echo $matches[2];

will output the following:

will@example.com
will
example.com

NAME EMAIL_REGEX captures 5 subpatterns in the following format:

For example:

preg_match(fEmail::NAME_EMAIL_REGEX, 'Will <will@example.com>', $matches);

echo $matches[0] . "\n";
echo $matches[1] . "\n";
echo $matches[2] . "\n";
echo $matches[3] . "\n";
echo $matches[4];

will output the following:

Will <will@example.com>
Will
will@example.com
will
example.com

fEmail API Reference

class, v1.0.0b30

Allows creating and sending a single email containing plaintext, HTML, attachments and S/MIME encryption

Please note that this class uses the mail() function by default. Developers that are sending multiple emails, or need SMTP support, should use fSMTP with this class.

This class is implemented to use the UTF-8 character encoding. Please see UTF-8 for more information.

Changes:
1.0.0b30Changed methods to return instance for method chaining 9/12/11
1.0.0b29Changed combineNameEmail() to be a static method and to be exposed publicly for use by other classes 7/26/11
1.0.0b28Fixed addAttachment() and addRelatedFile() to properly handle duplicate filenames 5/17/11
1.0.0b27Fixed a bug with generating FQDNs on some Windows machines 2/24/11
1.0.0b26Added addCustomerHeader() 2/2/11
1.0.0b25Fixed a bug with finding the FQDN on non-Windows machines 1/19/11
1.0.0b24Backwards Compatibility Break - the $contents parameter of addAttachment() is now first instead of third, addAttachment() will now accept fFile objects for the $contents parameter, added addRelatedFile() 12/1/10
1.0.0b23Fixed a bug on Windows where emails starting with a . would have the . removed 9/11/10
1.0.0b22Revamped the FQDN code and added getFQDN() 9/7/10
1.0.0b21Added a check to prevent permissions warnings when getting the FQDN on Windows machines 9/2/10
1.0.0b20Fixed send() to only remove the name of a recipient when dealing with the mail() function on Windows and to leave it when using fSMTP 6/22/10
1.0.0b19Changed send() to return the message id for the email, fixed the email regexes to require [] around IPs 5/5/10
1.0.0b18Fixed the name of the static method unindentExpand() 4/28/10
1.0.0b17Added the static method unindentExpand() 4/26/10
1.0.0b16Added support for sending emails via fSMTP 4/20/10
1.0.0b15Added the $unindent_expand_constants parameter to setBody(), added loadBody() and loadHTMLBody(), fixed HTML emails with attachments 3/14/10
1.0.0b14Changed send() to not double .s at the beginning of lines on Windows since it seemed to break things rather than fix them 3/5/10
1.0.0b13Fixed the class to work when safe mode is turned on 10/23/09
1.0.0b12Removed duplicate MIME-Version headers that were being included in S/MIME encrypted emails 10/5/09
1.0.0b11Updated to use the new fValidationException API 9/17/09
1.0.0b10Fixed a bug with sending both an HTML and a plaintext body 6/18/09
1.0.0b9Fixed a bug where the MIME headers were not being set for all emails 6/12/09
1.0.0b8Added the method clearRecipients() 5/29/09
1.0.0b7Email names with UTF-8 characters are now properly encoded 5/8/09
1.0.0b6Fixed a bug where <> quoted email addresses in validation messages were not showing 3/27/09
1.0.0b5Updated for new fCore API 2/16/09
1.0.0b4The recipient error message in validate() no longer contains a typo 2/9/09
1.0.0b3Fixed a bug with missing content in the fValidationException thrown by validate() 1/14/09
1.0.0b2Fixed a few bugs with sending S/MIME encrypted/signed emails 1/10/09
1.0.0bThe initial implementation 6/23/08

Constants

::EMAIL_REGEX

A regular expression to match an email address, exluding those with comments and folding whitespace

The matches will be:

  • [0]: The whole email address
  • [1]: The name before the @
  • [2]: The domain/ip after the @
::NAME_EMAIL_REGEX

A regular expression to match a name <email> string, exluding those with comments and folding whitespace

The matches will be:

  • [0]: The whole name and email address
  • [1]: The name
  • [2]: The whole email address
  • [3]: The email username before the @
  • [4]: The email domain/ip after the @

Static Methods

::combineNameEmail() internal public

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

Turns a name and email into a "name" <email> string, or just email if no name is provided

This method will remove newline characters from the name and email, and will remove any backslash (\) and double quote (") characters from the name.

Signature

string combineNameEmail( string $name, string $email )

Parameters
string $name The name associated with the email address
string $email The email address
Returns

The '"name" <email>' or 'email' string

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::fixQmail() public

Sets the class to try and fix broken qmail implementations that add \r to \r\n

Before trying to fix qmail with this method, please try using fSMTP to connect to localhost and pass the fSMTP object to send().

Signature

void fixQmail( )

::getFQDN() internal public

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

Returns the fully-qualified domain name of the server

Signature

string getFQDN( )

Returns

The fully-qualified domain name of the server

::reset() internal public

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

Signature

void reset( )

::stringlike() protected

Returns TRUE for non-empty strings, numbers, objects, empty numbers and string-like numbers (such as 0, 0.0, '0')

Signature

boolean stringlike( mixed $value )

Parameters
mixed $value The value to check
Returns

If the value is string-like

::unindentExpand() public

Takes a block of text, unindents it and replaces {CONSTANT} tokens with the constant's value

Signature

string unindentExpand( string $text )

Parameters
string $text The text to unindent and replace constants in
Returns

The unindented text

Methods

->__construct() public

Initializes fEmail for creating message ids

Signature

fEmail __construct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->addAttachment() public

Adds an attachment to the email

If a duplicate filename is detected, it will be changed to be unique.

Signature

fEmail addAttachment( string|fFile $contents, string $filename=NULL, string $mime_type=NULL )

Parameters
string|fFile $contents The contents of the file
string $filename The name to give the attachement - optional if $contents is an fFile object
string $mime_type The mime type of the file - this allows overriding the mime type of the file if incorrectly detected
Returns

The email object, to allow for method chaining

->addBCCRecipient() public

Adds a blind carbon copy (BCC) email recipient

Signature

fEmail addBCCRecipient( string $email, string $name=NULL )

Parameters
string $email The email address to BCC
string $name The recipient's name
Returns

The email object, to allow for method chaining

->addCCRecipient() public

Adds a carbon copy (CC) email recipient

Signature

fEmail addCCRecipient( string $email, string $name=NULL )

Parameters
string $email The email address to BCC
string $name The recipient's name
Returns

The email object, to allow for method chaining

->addCustomHeader() public

Allows adding a custom header to the email

If the method is called multiple times with the same name, the last value will be used.

Please note that this class will properly format the header, including adding the : between the name and value and wrapping values that are too long for a single line.

Signatures

fEmail addCustomHeader( string $name, string $value )

fEmail addCustomHeader( array $headers )

Parameters
string $name The name of the header
string $value The value of the header
array $headers An associative array of {name} => {value}
Returns

The email object, to allow for method chaining

->addRecipient() public

Adds an email recipient

Signature

fEmail addRecipient( string $email, string $name=NULL )

Parameters
string $email The email address to send to
string $name The recipient's name
Returns

The email object, to allow for method chaining

->addRelatedFile() public

Adds a “related” file to the email, returning the Content-ID for use in HTML

The purpose of a related file is to be able to reference it in part of the HTML body. Image src URLs can reference a related file by starting the URL with cid: and then inserting the Content-ID.

If a duplicate filename is detected, it will be changed to be unique.

Signature

string addRelatedFile( string|fFile $contents, string $filename=NULL, string $mime_type=NULL )

Parameters
string|fFile $contents The contents of the file
string $filename The name to give the attachement - optional if $contents is an fFile object
string $mime_type The mime type of the file - this allows overriding the mime type of the file if incorrectly detected
Returns

The fully-formed cid: URL for use in HTML src attributes

->clearRecipients() public

Removes all To, CC and BCC recipients from the email

Signature

fEmail clearRecipients( )

Returns

The email object, to allow for method chaining

->encrypt() public

Sets the email to be encrypted with S/MIME

Signature

fEmail encrypt( string $recipients_smime_cert_file )

Parameters
string $recipients_smime_cert_file The file path to the PEM-encoded S/MIME certificate for the recipient
Returns

The email object, to allow for method chaining

->loadBody() public

Loads the plaintext version of the email body from a file and applies replacements

The should contain either ASCII or UTF-8 encoded text. Please see UTF-8 for more information.

Signature

fEmail loadBody( string|fFile $file, array $replacements=array() )

Parameters
string|fFile $file The plaintext version of the email body
array $replacements The method will search the contents of the file for each key and replace it with the corresponding value
Returns

The email object, to allow for method chaining

Throws
fValidationException
When no file was specified, the file does not exist or the path specified is not a file
->loadHTMLBody() public

Loads the plaintext version of the email body from a file and applies replacements

The should contain either ASCII or UTF-8 encoded text. Please see UTF-8 for more information.

Signature

fEmail loadHTMLBody( string|fFile $file, array $replacements=array() )

Parameters
string|fFile $file The plaintext version of the email body
array $replacements The method will search the contents of the file for each key and replace it with the corresponding value
Returns

The email object, to allow for method chaining

Throws
fValidationException
When no file was specified, the file does not exist or the path specified is not a file
->send() public

Sends the email

The return value is the message id, which should be included as the Message-ID header of the email. While almost all SMTP servers will not modify this value, testing has indicated at least one (smtp.live.com for Windows Live Mail) does.

Signature

string send( fSMTP $connection=NULL )

Parameters
fSMTP $connection The SMTP connection to send the message over
Returns

The message id for the message - see method description for details

Throws
fValidationException
When validate() throws an exception
->setBody() public

Sets the plaintext version of the email body

This method accepts either ASCII or UTF-8 encoded text. Please see UTF-8 for more information.

Signature

fEmail setBody( string $plaintext, boolean $unindent_expand_constants=FALSE )

Parameters
string $plaintext The plaintext version of the email body
boolean $unindent_expand_constants If this is TRUE, the body will be unindented as much as possible and {CONSTANT_NAME} will be replaced with the value of the constant
Returns

The email object, to allow for method chaining

->setBounceToEmail() public

Adds the email address the email will be bounced to

This email address will be set to the Return-Path header.

Signature

fEmail setBounceToEmail( string $email )

Parameters
string $email The email address to bounce to
Returns

The email object, to allow for method chaining

->setFromEmail() public

Adds the From: email address to the email

Signature

fEmail setFromEmail( string $email, string $name=NULL )

Parameters
string $email The email address being sent from
string $name The from email user's name - unfortunately on windows this is ignored
Returns

The email object, to allow for method chaining

->setHTMLBody() public

Sets the HTML version of the email body

This method accepts either ASCII or UTF-8 encoded text. Please see UTF-8 for more information.

Signature

fEmail setHTMLBody( string $html )

Parameters
string $html The HTML version of the email body
Returns

The email object, to allow for method chaining

->setReplyToEmail() public

Adds the Reply-To: email address to the email

Signature

fEmail setReplyToEmail( string $email, string $name=NULL )

Parameters
string $email The email address to reply to
string $name The reply-to email user's name
Returns

The email object, to allow for method chaining

->setSenderEmail() public

Adds the Sender: email address to the email

The Sender: header is used to indicate someone other than the From: address is actually submitting the message to the network.

Signature

fEmail setSenderEmail( string $email, string $name=NULL )

Parameters
string $email The email address the message is actually being sent from
string $name The sender email user's name
Returns

The email object, to allow for method chaining

->setSubject() public

Sets the subject of the email

This method accepts either ASCII or UTF-8 encoded text. Please see UTF-8 for more information.

Signature

fEmail setSubject( string $subject )

Parameters
string $subject The subject of the email
Returns

The email object, to allow for method chaining

->sign() public

Sets the email to be signed with S/MIME

Signature

fEmail sign( string $senders_smime_cert_file, string $senders_smime_pk_file, string $senders_smime_pk_password )

Parameters
string $senders_smime_cert_file The file path to the sender's PEM-encoded S/MIME certificate
string $senders_smime_pk_file The file path to the sender's S/MIME private key
string $senders_smime_pk_password The password for the sender's S/MIME private key
Returns

The email object, to allow for method chaining

fEmptySetException

fEmptySetException is a sub-class of fExpectedException that indicates an fRecordSet does not contain any records. This type of exception will only be thrown by Flourish code when requested via the tossIfEmpty() method.

This space intentionally left blank

fEmptySetException API Reference

class, v1.0.0b

An exception when an fRecordSet does not contain any elements

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fEmptySetException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fEnvironmentException

fEnvironmentException is a sub-class of fUnexpectedException that indicates some sort of required code, extension or other server environment issue has prevented futher execution of the code. Examples of this type of exception being tossed include a missing PHP extension for cryptography, no image manipulation library present, or an outdated version of PHP being used.

This space intentionally left blank

fEnvironmentException API Reference

class, v1.0.0b

An exception caused by an environment error such a file permissions

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
         |
         --fEnvironmentException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fException

fException is the base Flourish exception class. It supplements the standard Exception class methods most notably with the ability to easily print the exception to the screen, but it also adds the ability to change the exception message after the object is created.

Exceptions are used throughout Flourish in an attempt to fail as noisily and quickly as possible when the code can not repair the situations. This follows the Rule of Repair from the Art of Unix Programming.

The exceptions in Flourish purposefully use inheritance to allow for classification of exceptions when catching them. You probably dont ever really want to toss an fException, and normally wouldnt want to toss an fExpectedException or fUnexpectedException, but instead should use one of the specific sub-classes. However, when creating catch statements it may be beneficial to catch one of the generalized higher-level exception classes.

To the right you will see a list of all of the Flourish exception classes.

Instantiation

fException has a constructor that is compatible with a normal exception class, but also adds the ability to be able to perform sprintf() interpolation and hooks in with the fText class to allow for easy localization.

The constructor requires just a single parameter, the message to be thrown. If that message contains any formatting codes that are compatible with sprintf(), additional parameters will be used for the values. If an extra parameters is passed, it will be set as the exception code.

Please note all of these example use fProgrammerException since fException is abstract.

// Simple usage
throw new fProgrammerException('The method specified does not exist');

// Using sprintf for interpolation
throw new fProgrammerException('The method specified, %s, does not exist', $method);

// Adding an exception code
throw new fProgrammerException('The method specified, %s, does not exist', $method, 1234);

// An exception code without interpolation
throw new fProgrammerException('The method specified does not exist', 1234);

One of the main benefits of using interpolation is that the non-interpolated message will be passed to fText::compose() if fText has been loaded, which allows for localization of exception messages. This technique is used throughout Flourish.

Printing

The method printMessage() allows for easy display and formatting of the message. It will echo a <p> tag containing the message if the message has no block-level HTML tags, or a <div> tag if the message contains block-level HTML. The tag will have a class attribute that is set to exception {exception_class_name}.

The examples below use sub-classes of fException since it is abstract and thus can not be instantiated.

$exception = new fValidationException('This is the message');
$exception->printMessage();

$exception2 = new fValidationException('<p>This is the message</p>');
$exception2->printMessage();

Would output the following HTML:

<p class="exception validation_exception">
    This is the message
</p>
<div class="exception validation_exception">
    <p>This is the message</p>
</div>

Modification

Sometimes when handling an error you need to modify the message in an exception, without losing the backtrace information. If you were to get the message from one exception, modify it, and then create a new exception with that message, the whole backtrace would be lost.

In these situations it is essential to be able to modify the exception message. This can be done by passing the new message to the method setMessage(). Here is an example:

$exception = new fValidationException('This is a test');
$exception->setMessage('This is the test');
$exception->printMessage();

The PHP above would echo the following HTML:

<p class="exception validation_exception">
    This is the test
</p>

Reordering Messages

Most of the validation in Flourish, such as fValidation and fActiveRecord, create exception messages containing a list of all of the encountered errors. Sometimes it is necessary to reorder the list of errors so they coincide with the order of HTML form inputs.

The method reorderMessage() accepts any number of strings to match, and reorders the list items in the message based on the parameter order. Below is an example to show how it works. First, assume the exception contains the following message:

<p>The following problems were found:</p>
<ul>
    <li>Address: Please enter a value</li>
    <li>Email: Please enter a value</li>
    <li>City: Please enter a value</li>
    <li>State: Please enter a value</li>
    <li>Zip Code: Please enter a value</li>
    <li>Last Name: Please enter a value</li>
    <li>First Name: Please enter a value</li>
</ul>

The following PHP would reorder the list items:

$exception->reorderMessage('First Name', 'Last', 'Email', 'Address');

The exception message would then be changed to:

<p>The following problems were found:</p>
<ul>
    <li>First Name: Please enter a value</li>
    <li>Last Name: Please enter a value</li>
    <li>Email: Please enter a value</li>
    <li>Address: Please enter a value</li>
    <li>City: Please enter a value</li>
    <li>State: Please enter a value</li>
    <li>Zip Code: Please enter a value</li>
</ul>

Any list items that do not match a parameter will be included at the end of the list, in the same order they existed in the original message.

The matching of strings in done in a case-sensitive manner and the most-specific strings are matched first. Thus if the parameters to reorderMessage() were Name and Last Name, Last Name:… would be matched first and placed as the second list item. Then the Name string would match against First Name:… and it would be set as the first list item.

Splitting Messages

Most of the validation in Flourish, such as fValidation and fActiveRecord, create exception messages containing a list of all of the encountered errors. These lists are built using HTML unordered lists. Sometimes when creating long forms, it is more usable to split the messages into multiple parts to display in the appropriate places on the form.

The instance method splitMessage() accepts any number of arrays of strings, $list_item_matches, and returns an array of filtered messages. Any list item that contains one of the strings will be included in the corresponding filtered message, with the non-list portions of the message also being included in each filtered message.

The easiest way to understand the functionality is to see an example. First, assume the exception contains the following message:

<p>The following problems were found:</p>
<ul>
    <li>First Name: Please enter a value</li>
    <li>Last Name: Please enter a value</li>
    <li>Email: Please enter a value</li>
    <li>Address: Please enter a value</li>
    <li>City: Please enter a value</li>
    <li>State: Please enter a value</li>
    <li>Zip Code: Please enter a value</li>
</ul>

Passing these two $list_item_matches to splitMessage() would split the exception into two strings:

list ($name_exception, $address_exception) = $exception->splitMessage(
    array('First Name', 'Last Name', 'Email'),
    array('Address', 'City', 'State', 'Zip Code')
);

The resulting strings would be:

<p>The following problems were found:</p>
<ul>
    <li>First Name: Please enter a value</li>
    <li>Last Name: Please enter a value</li>
    <li>Email: Please enter a value</li>
</ul>

and

<p>The following problems were found:</p>
<ul>
    <li>Address: Please enter a value</li>
    <li>City: Please enter a value</li>
    <li>State: Please enter a value</li>
    <li>Zip Code: Please enter a value</li>
</ul>

Notice that the resulting strings will contain the list items in the same order that the strings are set to the $list_item_matches arrays. This allows re-ordering the list items while also filtering them.

It is possible to pass any number of $list_item_matches to splitMessage(), resulting in an equal number of strings in the array result.

If no list items are found in the message, the first value in the returned array will contain the original message and all other array values will be an empty string.

Backtraces

Every exception that is thrown include a full backtrace of all of the function and methods calls that lead up to the point when the exception was thrown. The base Exception class includes two methods to access the backtrace, getTrace() and getTraceAsString(). getTrace() returns an array of the information for each step in the backtrace, while getTraceAsString() formats all of the backtrace information into a string.

fException includes two methods to supplement the built-in backtrace support, formatTrace() and printTrace(). formatTrace() takes the formatted backtrace string and modifies it to be a little more readable. Below is an example of the type of output created.

{doc_root}/example.php(326): fActiveRecord->store()
{doc_root}/inc/classes/trunk/classes/fActiveRecord.php(1386): fActiveRecord->validate()
{doc_root}/inc/classes/trunk/classes/fActiveRecord.php(1564): fCore::toss('fValidationExce...', 'The followin...')

The method printTrace() takes the formatted trace and displays it inside of a pre tag with CSS classes in the format exception {exception_class_name} trace.

<pre class="exception validation_exception trace">
{doc_root}/example.php(326): fActiveRecord->store()
{doc_root}/inc/classes/trunk/classes/fActiveRecord.php(1386): fActiveRecord->validate()
{doc_root}/inc/classes/trunk/classes/fActiveRecord.php(1564): fCore::toss('fValidationExce...', 'The followin...')
</pre>

fException API Reference

abstract class, v1.0.0b8

An exception that allows for easy l10n, printing, tracing and hooking

Changes:
1.0.0b8Added a missing line of backtrace to formatTrace() 6/28/09
1.0.0b7Updated __construct() to no longer require a message, like the Exception class, and allow for non-integer codes 6/26/09
1.0.0b6Fixed splitMessage() so that the original message is returned if no list items are found, added reorderMessage() 6/2/09
1.0.0b5Added splitMessage() to replace fCRUDremoveListItems() and fCRUD::reorderListItems() 5/8/09
1.0.0b4Added a check to __construct() to ensure that the $code parameter is numeric 5/4/09
1.0.0b3Fixed a bug with printMessage() messing up some HTML messages 3/27/09
1.0.0b2compose() more robustly handles $components passed as an array, __construct() now detects stray % characters 2/5/09
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
Child Classes
fExpectedException
An exception that should be handled by the display code
fUnexpectedException
An exception that should probably not be handled by the display code, fCore::enableExceptionHandler() is recommended
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::dump() protected

Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings

The string output format of this method is very similar to the output of print_r() except that the following values are represented as special strings:

  • TRUE: '{true}'
  • FALSE: '{false}'
  • NULL: '{null}'
  • '': '{empty_string}'
Signature

string dump( mixed $data )

Parameters
mixed $data The value to dump
Returns

The string representation of the value

::registerCallback() public

Adds a callback for when certain types of exceptions are created

The callback will be called when any exception of this class, or any child class, specified is tossed. A single parameter will be passed to the callback, which will be the exception object.

Signature

void registerCallback( callback $callback, string $exception_type=NULL )

Parameters
callback $callback The callback
string $exception_type The type of exception to call the callback for

Methods

->__construct() public

Sets the message for the exception, allowing for string interpolation and internationalization

The $message can contain any number of formatting placeholders for string and number interpolation via sprintf(). Any % signs that do not appear to be part of a valid formatting placeholder will be automatically escaped with a second %.

The following aspects of valid sprintf() formatting codes are not accepted since they are redundant and restrict the non-formatting use of the % sign in exception messages:

  • % 2d: Using a literal space as a padding character - a space will be used if no padding character is specified
  • %'.d: Providing a padding character but no width - no padding will be applied without a width
Signature

fException __construct( string $message='', mixed $component [, ... ], mixed $code )

Parameters
string $message The message for the exception. This accepts a subset of sprintf() strings - see method description for more details.
mixed $component [, ... ] A string or number to insert into the message
mixed $code The exception code to set
Overridden By
fValidationException::__construct()
Sets the message for the exception, allowing for custom formatting beyond fException
Overrides
Exception::constructor __construct ( [$message = ], [$code = ], [$previous = ] )
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->formatTrace() public

Gets the backtrace to currently called exception

Signature

string formatTrace( )

Returns

A nicely formatted backtrace to this exception

->getCSSClass() protected

Returns the CSS class name for printing information about the exception

Signature

void getCSSClass( )

->prepare() protected

Prepares content for output into HTML

Signature

string prepare( $content )

Parameters
$content
Returns

The prepared content

->printMessage() public

Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'

Signature

void printMessage( )

Overridden By
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
->printTrace() public

Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'

Signature

void printTrace( )

->reorderMessage() public

Reorders list items in the message based on simple string matching

Signature

fException reorderMessage( string $match [, ... ] )

Parameters
string $match [, ... ] This should be a string to match to one of the list items - whatever the order this is in the parameter list will be the order of the list item in the adjusted message
Returns

The exception object, to allow for method chaining

->setMessage() public

Allows the message to be overwriten

Signature

void setMessage( string $new_message )

Parameters
string $new_message The new message for the exception
->splitMessage() public

Splits an exception with an HTML list into multiple strings each containing part of the original message

This method should be called with two or more parameters of arrays of string to match. If any of the provided strings are matching in a list item in the exception message, a new copy of the message will be created containing just the matching list items.

Here is an exception message to be split:

The following problems were found:

  • First Name: Please enter a value
  • Last Name: Please enter a value
  • Email: Please enter a value
  • Address: Please enter a value
  • City: Please enter a value
  • State: Please enter a value
  • Zip Code: Please enter a value

The following PHP would split the exception into two messages:

list ($name_exception, $address_exception) = $exception->splitMessage(
    array('First Name', 'Last Name', 'Email'),
    array('Address', 'City', 'State', 'Zip Code')
);

The resulting messages would be:

The following problems were found:

  • First Name: Please enter a value
  • Last Name: Please enter a value
  • Email: Please enter a value

and

The following problems were found:

  • Address: Please enter a value
  • City: Please enter a value
  • State: Please enter a value
  • Zip Code: Please enter a value

If no list items match the strings in a parameter, the result will be an empty string, allowing for simple display:

fHTML::show($name_exception, 'error');

An empty string is returned when none of the list items matched the strings in the parameter. If no list items are found, the first value in the returned array will be the existing message and all other array values will be an empty string.

Signature

array splitMessage( array $list_item_matches [, ... ] )

Parameters
array $list_item_matches [, ... ] An array of strings to filter the list items by, list items will be ordered in the same order as this array
Returns

This will contain an array of strings corresponding to the parameters passed - see method description for details

fExpectedException

fExpectedException is a sub-class of fException meant to provide a common parent class for all exceptions that should be expected and handled in the site/application code. There are no special features or functionality for this class.

Child Classes

Below is a list of all child classes that extend fExpectedException. If any of these classes are tossed by a Flourish method, there will be a Throws: entry in the methods API documentation. Note that exceptions that extend fUnexpectedException are not documented in the same way.

Class Description
fEmptySetException This type of exception indicates an fRecordSet is empty, and will only be thrown when requested via tossIfEmpty().
fNoRemainingException This type of exception indicates that a element was requested even though an iterator has iterated over all existing elements.
fNoRowsException This type of exception is for use when no rows are returned by a SQL query.
fNotFoundException This type of exception indicates that something could not be found and should include the item type in the message.
fValidationException This type of exception indicates there was an error when checking data input. The message will indicate what could not be validated.

fExpectedException API Reference

class, v1.0.0b

An exception that should be handled by the display code

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
Child Classes
fNoRemainingException
An exception caused when trying to get a value from an iterator and there is nothing left
fEmptySetException
An exception when an fRecordSet does not contain any elements
fAuthorizationException
An exception caused by an authorization error
fValidationException
An exception caused by a data not matching a rule or set of rules
fNoRowsException
An exception when no rows are returned from a SQL query
fNotFoundException
An exception when an fActiveRecord is not found in the database
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fFile

The fFile class is a simple object representation of a file on the filesystem. It provides an object-based interface to common file functions and allows actions to be grouped into transactions via the fFilesystem class.

Instantiation

The fFile constructor normally takes a single argument, the filesystem path to a file.

$file1 = new fFile('/var/www/vhosts/examples.com/httpdocs/images/example.gif');
$file2 = new fFile('./output.txt');
$file3 = new fFile('../uploads/documents/example.doc');

A second argument can be passed to the constructor allowing for an exception object to be set for the file, however this is primarily for use by the fUpload class.

It is also possible to create an fFile object by calling the static create() method. Not only does it create an instance of fFile, but also allows creates a new file on the filesystem. This method requires two parameters, the $file_path and the $contents to write.

$new_file = fFile::create('./output.log', 'This is the string to be stored in the new file.');

Getting Information

The fFile class includes a few methods to grab some basic information about a file. These include:

Method Description
getName() Returns the filename of the file as a string
getExtension() Returns the extension of the file
getParent() Returns the fDirectory object for the directory containing this file
getPath() Returns a string with the full path to the file
getSize() Returns the size in bytes as an integer, or optionally formatted for easy human readability
getMimeType() Returns the mime type of the file, see the API docs for a list of all supported file types
getMTime() Returns an fTimestamp object representing the date and time the file was last modified
isWritable() Indicates if the file can be written to by the current user

If you want more specific information about a file, you can pass the output of getPath() into the various PHP filesystem functions.

Manipulation

In most situations, manipulation of a file is going to be a requirement. The following methods provide a straight-forward interface for standard manipulation. Also note that these manipulations can be wrapped in a filesystem transactions to allow for rolling back changes in the event of a later error.

Method Description
read() Returns the contents of the file as a string
write() Accepts a string to overwrite the file contents with
append() Appends content to the file
rename() Renames the file to a new filename/path - if no directory / is found in the new path, the file is renamed in the current directory
move() Moves the file to a new directory, keeping the existing filename
duplicate() Will create a copy of the file, optionally in a different directory. If in a different directory, you can specify if you want an existing file with the same name to be overwritten.
delete() Removes the file from the filesystem. Please note that if inside of a filesystem transaction, this event will be deferred until commit is called, but instances of fFile will act as if the file no longer exists.
output() Will echo the contents of the file to the user, optionally including the appropriate HTTP headers

Outputting

When it is necessary to use PHP to control access to files, a file can be sent to a user via the output() method. The first parameters, $headers, is required and controls whether or not mime-type and filesize headers are included with the file contents. If this is set to FALSE, the headers should be manually sent before calling the method.

// Output the file with relevant headers
$file->output(TRUE);

If included, the second parameter, $filename, will send the file as an attachment to the current page instead of using the filename from the current URL.

// If on index.php, this file will be sent to the user as index.php
$file->output(TRUE);

// This file will be sent as document.doc
$file->output(TRUE, 'document.doc');

Performance Issues

When outputting a file, please be certain to turn off any output buffering, whether it be controller by ob_start(), fTemplating or fBuffer. If output buffering is turned on, the whole contents of the file will be stored in web server memory before being sent to the user. This can obviously cause issues with large files.

Due to the way that PHP prevents overwriting session values, only one page can access the session at a time. If the session was opened on the page before calling output() and is not explicitly closed via fSession::close(), the user will not be able to visit any other pages on the site until the download completes. This is obviously more of an issue with large files or slow connections.

Cloning

When an fFile object is cloned, a duplicate of the file is created on the filesystem in the files current directory. Essentially clone is the same as calling duplicate() with no parameters.

$file  = new fFile('/path/to/file.txt');
// $file2 will be a new file with the path /path/to/file_copy1.txt
$file2 = clone $file;

Iteration

fFile implements the Iterator interface, meaning that ever fFile object can be used in a foreach loop, with each iteration returning a line from the line. Each line will include the original line break.

$file = new fFile('report.csv');
foreach ($file as $line) {
    $fields = str_getcsv($line);
    // 
}

fFile API Reference

class, implements Iterator Countable , v1.0.0b39

Represents a file on the filesystem, also provides static file-related methods

Changes:
1.0.0b39Backwards Compatibility Break - output() now automatically ends any open output buffering and discards the contents 8/24/11
1.0.0b38Added the Countable interface to the class 6/3/11
1.0.0b37Fixed mime type detection of BMP images 3/7/11
1.0.0b36Added the $remove_extension parameter to getName() 1/10/11
1.0.0b35Added calls to clearstatcache() in append() and write() to prevent incorrect data from being returned by getMTime() and getSize() 11/27/10
1.0.0b34Added getExtension() 5/10/10
1.0.0b33Fixed another situation where rename() with the same name would cause the file to be deleted 4/13/10
1.0.0b32Fixed rename() to not fail when the new and old filename are the same 3/16/10
1.0.0b31Added append() 3/15/10
1.0.0b30Changed the way files deleted in a filesystem transaction are handled, including improvements to the exception that is thrown 3/5/10
1.0.0b29Fixed a couple of undefined variable errors in determineMimeTypeByContents() 3/3/10
1.0.0b28Added support for some JPEG files created by Photoshop 12/16/09
1.0.0b27Backwards Compatibility Break - renamed getFilename() to getName(), getFilesize() to getSize(), getDirectory() to getParent(), added move() 12/16/09
1.0.0b26getDirectory(), getFilename() and getPath() now all work even if the file has been deleted 10/22/09
1.0.0b25Fixed __construct() to throw an fValidationException when the file does not exist 8/21/09
1.0.0b24Fixed a bug where deleting a file would prevent any future operations in the same script execution on a file or directory with the same path 8/20/09
1.0.0b23Added the ability to skip checks in __construct() for better performance in conjunction with fFilesystem::createObject() 8/6/09
1.0.0b22Fixed __toString() to never throw an exception 8/6/09
1.0.0b21Fixed a bug in determineMimeType() 7/21/09
1.0.0b20Fixed the exception message thrown by output() when output buffering is turned on 6/26/09
1.0.0b19rename() will now rename the file in its current directory if the new filename has no directory separator 5/4/09
1.0.0b18Changed __sleep() to not reset the iterator since it can cause side-effects 5/4/09
1.0.0b17Added __sleep() and __wakeup() for proper serialization with the filesystem map 5/3/09
1.0.0b16output() now accepts TRUE in the second parameter to use the current filename as the attachment filename 3/23/09
1.0.0b15Added support for mime type detection of MP3s based on the MPEG-2 (as opposed to MPEG-1) standard 3/23/09
1.0.0b14Fixed a bug with detecting the mime type of some MP3s 3/22/09
1.0.0b13Fixed a bug with overwriting files via rename() on Windows 3/11/09
1.0.0b12Backwards compatibility break - Changed the second parameter of output() from $ignore_output_buffer to $filename 3/5/09
1.0.0b11Changed __clone() and duplicate() to copy file permissions to the new file 1/5/09
1.0.0b10Fixed duplicate() so an exception is not thrown when no parameters are passed 1/5/09
1.0.0b9Removed the dependency on fBuffer 1/5/09
1.0.0b8Added the Iterator interface, output() and getMTime() 12/17/08
1.0.0b7Removed some unnecessary error suppresion operators 12/11/08
1.0.0b6Added the __clone() method that duplicates the file on the filesystem when cloned 12/11/08
1.0.0b5Fixed detection of mime type for JPEG files with Exif information 12/4/08
1.0.0b4Changed the constructor to ensure the path is to a file and not directory 11/24/08
1.0.0b3Fixed mime type detection of Microsoft Office files 11/23/08
1.0.0b2Made rename() and write() return the object for method chaining 11/22/08
1.0.0bThe initial implementation 6/14/07

Genealogy

Child Classes
fImage
Represents an image on the filesystem, also provides image manipulation functionality

Variables

->deleted protected

A backtrace from when the file was deleted

Type

array

->file protected

The full path to the file

Type

string

Static Methods

::create() public

Creates a file on the filesystem and returns an object representing it.

This operation will be reverted by a filesystem transaction being rolled back.

Signature

fFile create( string $file_path, string $contents )

Parameters
string $file_path The path to the new file
string $contents The contents to write to the file, must be a non-NULL value to be written
Throws
fValidationException
When no file was specified or the file already exists
Overridden By
fImage::create()
Creates an image on the filesystem and returns an object representing it
::determineMimeType() internal public

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

Determines the file's mime type by either looking at the file contents or matching the extension

Please see the getMimeType() description for details about how the mime type is determined and what mime types are detected.

Signature

string determineMimeType( string $file, string $contents=NULL )

Parameters
string $file The file to check the mime type for - must be a valid filesystem path if no $contents are provided, otherwise just a filename
string $contents The first 4096 bytes of the file content - the $file parameter only need be a filename if this is provided
Returns

The mime type of the file

Methods

->__construct() public

Creates an object to represent a file on the filesystem

If multiple fFile objects are created for a single file, they will reflect changes in each other including rename and delete actions.

Signature

fFile __construct( string $file, boolean $skip_checks=FALSE )

Parameters
string $file The path to the file
boolean $skip_checks If file checks should be skipped, which improves performance, but may cause undefined behavior - only skip these if they are duplicated elsewhere
Throws
fValidationException
When no file was specified, the file does not exist or the path specified is not a file
Overridden By
fImage::__construct()
Creates an object to represent an image on the filesystem
->__clone() internal public

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

Duplicates a file in the current directory when the object is cloned

Signature

fFile __clone( )

Returns

The new fFile object

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__sleep() internal public

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

The iterator information doesn't need to be serialized since a resource can't be

Signature

array __sleep( )

Returns

The instance variables to serialize

->__toString() public

Returns the filename of the file

Signature

string __toString( )

Returns

The filename

->__wakeup() internal public

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

Re-inserts the file back into the filesystem map when unserialized

Signature

void __wakeup( )

->append() public

Appends the provided data to the file

If a filesystem transaction is in progress and is rolled back, this data will be removed.

Signature

fFile append( mixed $data )

Parameters
mixed $data The data to append to the file
Returns

The file object, to allow for method chaining

Overridden By
fImage::append()
Prevents a programmer from trying to append an image
->count() public implements Countable

Returns the number of lines in the file

Signature

integer count( )

Returns

The number of lines in the file

->current() internal public implements Iterator

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

Returns the current line of the file (required by iterator interface)

Signature

array current( )

Returns

The current row

Throws
fNoRemainingException
When there are no remaining lines in the file
->delete() public

Deletes the current file

This operation will NOT be performed until the filesystem transaction has been committed, if a transaction is in progress. Any non-Flourish code (PHP or system) will still see this file as existing until that point.

Signature

void delete( )

->duplicate() public

Creates a new file object with a copy of this file

If no directory is specified, the file is created with a new name in the current directory. If a new directory is specified, you must also indicate if you wish to overwrite an existing file with the same name in the new directory or create a unique name.

This operation will be reverted by a filesystem transaction being rolled back.

Signature

fFile duplicate( string|fDirectory $new_directory=NULL, boolean $overwrite=NULL )

Parameters
string|fDirectory $new_directory The directory to duplicate the file into if different than the current directory
boolean $overwrite If a new directory is specified, this indicates if a file with the same name should be overwritten.
Returns

The new fFile object

->getExtension() public

Gets the file extension

Signature

string getExtension( )

Returns

The extension of the file

->getMimeType() public

Gets the file's mime type

This method will attempt to look at the file contents and the file extension to determine the mime type. If the file contains binary information, the contents will be used for mime type verification, however if the contents appear to be plain text, the file extension will be used.

The following mime types are supported. All other binary file types will be returned as application/octet-stream and all other text files will be returned as text/plain.

Archive:

  • application/x-bzip2 BZip2 file
  • application/x-compress Compress (*nix) file
  • application/x-gzip GZip file
  • application/x-rar-compressed Rar file
  • application/x-stuffit StuffIt file
  • application/x-tar Tar file
  • application/zip Zip file

Audio:

  • audio/x-flac FLAC audio
  • audio/mpeg MP3 audio
  • audio/mp4 MP4 (AAC) audio
  • audio/vorbis Ogg Vorbis audio
  • audio/x-wav WAV audio
  • audio/x-ms-wma Windows media audio

Document:

  • application/vnd.ms-excel Excel (2000, 2003 and 2007) file
  • application/pdf PDF file
  • application/vnd.ms-powerpoint Powerpoint (2000, 2003, 2007) file
  • text/rtf RTF file
  • application/msword Word (2000, 2003 and 2007) file

Image:

  • image/x-ms-bmp BMP file
  • application/postscript EPS file
  • image/gif GIF file
  • application/vnd.microsoft.icon ICO file
  • image/jpeg JPEG file
  • image/png PNG file
  • image/tiff TIFF file
  • image/svg+xml SVG file

Text:

  • text/css CSS file
  • text/csv CSV file
  • text/html (X)HTML file
  • text/calendar iCalendar file
  • application/javascript Javascript file
  • application/x-perl Perl file
  • application/x-httpd-php PHP file
  • application/x-python Python file
  • application/rss+xml RSS feed
  • application/x-ruby Ruby file
  • text/tab-separated-values TAB file
  • text/x-vcard VCard file
  • application/xhtml+xml XHTML (Real) file
  • application/xml XML file

Video/Animation:

  • video/x-msvideo AVI video
  • application/x-shockwave-flash Flash movie
  • video/x-flv Flash video
  • video/x-ms-asf Microsoft ASF video
  • video/mp4 MP4 video
  • video/ogg OGM and Ogg Theora video
  • video/quicktime Quicktime video
  • video/x-ms-wmv Windows media video
Signature

string getMimeType( )

Returns

The mime type of the file

->getMTime() public

Returns the last modification time of the file

Signature

fTimestamp getMTime( )

Returns

The timestamp of when the file was last modified

->getName() public

Gets the filename (i.e. does not include the directory)

Signature

string getName( boolean $remove_extension=FALSE )

Parameters
boolean $remove_extension If the extension should be removed from the filename
Returns

The filename of the file

->getParent() public

Gets the directory the file is located in

Signature

fDirectory getParent( )

Returns

The directory containing the file

->getPath() public

Gets the file's current path (directory and filename)

If the web path is requested, uses translations set with fFilesystem::addWebPathTranslation()

Signature

string getPath( boolean $translate_to_web_path=FALSE )

Parameters
boolean $translate_to_web_path If the path should be the web path
Returns

The path (directory and filename) for the file

->getSize() public

Gets the size of the file

The return value may be incorrect for files over 2GB on 32-bit OSes.

Signature

integer|string getSize( boolean $format=FALSE, integer $decimal_places=1 )

Parameters
boolean $format If the filesize should be formatted for human readability
integer $decimal_places The number of decimal places to format to (if enabled)
Returns

If formatted a string with filesize in b/kb/mb/gb/tb, otherwise an integer

->isWritable() public

Check to see if the current file is writable

Signature

boolean isWritable( )

Returns

If the file is writable

->key() internal public implements Iterator

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

Returns the current one-based line number (required by iterator interface)

Signature

integer key( )

Returns

The current line number

Throws
fNoRemainingException
When there are no remaining lines in the file
->move() public

Moves the current file to a different directory

Please note that rename() will rename a file in its directory or rename it into a different directory.

If the current file's filename already exists in the new directory and the overwrite flag is set to false, the filename will be changed to a unique name.

This operation will be reverted if a filesystem transaction is in progress and is later rolled back.

Signature

fFile move( fDirectory|string $new_directory, boolean $overwrite )

Parameters
fDirectory|string $new_directory The directory to move this file into
boolean $overwrite If the current filename already exists in the new directory, TRUE will cause the file to be overwritten, FALSE will cause the new filename to change
Returns

The file object, to allow for method chaining

Throws
fValidationException
When the directory passed is not a directory or is not readable
->next() internal public implements Iterator

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

Advances to the next line in the file (required by iterator interface)

Signature

void next( )

Throws
fNoRemainingException
When there are no remaining lines in the file
->output() public

Prints the contents of the file

This method is primarily intended for when PHP is used to control access to files.

Be sure to close the session, if open, to prevent performance issues. Any open output buffers are automatically closed and discarded.

Signature

fFile output( boolean $headers, mixed $filename=NULL )

Parameters
boolean $headers If HTTP headers for the file should be included
mixed $filename Present the file as an attachment instead of just outputting type headers - if a string is passed, that will be used for the filename, if TRUE is passed, the current filename will be used
Returns

The file object, to allow for method chaining

->read() public

Reads the data from the file

Reads all file data into memory, use with caution on large files!

This operation will read the data that has been written during the current transaction if one is in progress.

Signature

string read( )

Returns

The contents of the file

->rename() public

Renames the current file

If the filename already exists and the overwrite flag is set to false, a new filename will be created.

This operation will be reverted if a filesystem transaction is in progress and is later rolled back.

Signature

fFile rename( string $new_filename, boolean $overwrite )

Parameters
string $new_filename The new full path to the file or a new filename in the current directory
boolean $overwrite If the new filename already exists, TRUE will cause the file to be overwritten, FALSE will cause the new filename to change
Returns

The file object, to allow for method chaining

->rewind() internal public implements Iterator

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

Rewinds the file handle (required by iterator interface)

Signature

void rewind( )

->tossIfDeleted() protected

Throws an fProgrammerException if the file has been deleted

Signature

void tossIfDeleted( )

->valid() internal public implements Iterator

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

Returns if the file has any lines left (required by iterator interface)

Signature

boolean valid( )

Returns

If the iterator is still valid

->write() public

Writes the provided data to the file

Requires all previous data to be stored in memory if inside a transaction, use with caution on large files!

If a filesystem transaction is in progress and is rolled back, the previous data will be restored.

Signature

fFile write( mixed $data )

Parameters
mixed $data The data to write to the file
Returns

The file object, to allow for method chaining

fFilesystem

The fFilesystem class is a static class that provides filesystem level functionality, including the ability to bundle operations into transactions.

Creating Objects

The static method createObject() acts as a factory to create the appropriate filesystem object when dealing with paths that may contain a directory, image or file.

In the case that a directory is detected, an fDirectory object will be returned. In the case that a file that is compatible with the fImage object is detected, an fImage object will be returned. If neither of those conditions hold true, an fFile object will be returned.

// This would return an fImage object
$file = fFilesystem::createObject('./path/to/image.jpg');

// This would return an fFile object
$file = fFilesystem::createObject('./path/to/info.txt');

// This would return an fDirectory object
$dir  = fFilesystem::createObject('./path/to/dir/');

Transactions

When speaking of ACID-compliant relational databases, transactions are one of the key elements. The fFilesystem class provides transaction-like functionality for filesystem operations. Please note that the level of transactional features is nothing like an ACID-compliant RDBMS. If the web server crashes or exits in the middle of a transaction, the changes made to that point will be permanent. Please see below for a list of exactly what is affected by filesystem transactions.

There are four methods used when dealing with filesystem transactions:

Method Description
begin() Begins a transaction, indicating that all following Flourish filesystem operations should be grouped together for the purposes of rolling back or committing them.
rollback() Allows all changes since the transaction was started to be reversed.
commit() Causes all pending operations (such as deletes) to actually be performed.
isInsideTransaction() Indicates if a transaction has been started but not rolled back or committed yet.

Here is a list of how the various fDirectory, fFile and fImage methods are affected by filesystem transactions:

Method Active Transaction Behaviour
fDirectory::delete() Directory will not be deleted from the filesystem until transaction is committed, however trying to manipulate the directory (or an sub-files or directories) via the Flourish filesystem classes will result in an exception being thrown.
fDirectory::getSize() The disk usage returned will include files that have been deleted during the current transaction and also placeholder files that have not yet been deleted.
fDirectory::rename() Directory will be renamed immediately, however a small file will be written to the old directory name to allow for a rollback to revert the directory name. The small placeholder file is deleted when a transaction is committed.
fDirectory::scan() A list of all file and folders contained within the directory will be returned, including files that have been deleted and placeholder files.
fDirectory::scanRecursive() A list of all file and folders contained within the directory (and all sub-directories) will be returned, including files that have been deleted and placeholder files.
fFile::delete() File will not be deleted from the filesystem until transaction is committed, however trying to manipulate the file via the Flourish filesystem classes will result in an exception being thrown.
fFile::duplicate() File will be duplicated immediately, however the copy will be deleted if the transaction is rolled back.
fFile::read() Will always read the most recently written file contents.
fFile::rename() File will be renamed immediately, however a small file will be written to the old filename to allow for a rollback to revert the filename. The small placeholder file is deleted when a transaction is committed.
fFile::write() The new file contents will be written to the file immediately, however the old file contents are saved to allow a rollback to restore the file to its original state. Warning: Large file contents can quickly increase the memory usage of PHP since the original contents are stored in memory.
fImage::saveChanges() The image changes will be written to disk immediately. If the new image name is the same as the old one, the old file contents will be kept in memory in case of a rollback. If the new image name is different than the old one, the old file will be kept until the transaction is committed.

File Paths

Helper Methods

fFilesystem provides three helper functions for dealing with file paths, getPathInfo(), makeUniqueName() and makeURLSafe().

getPathInfo() provides basically the same functionality as the PHP function pathinfo(), however it provides the filename element for PHP 5.1 and also returns the dirname element with a trailing slash.

makeUniqueName() requires a single parameter, $file, to specify the desired filename, and optionally allows a second parameter, $extension, which allows a new file extension to be specified. If a file (or directory) with the path of $file exists, a new filename will be created by appending _copy#. If $extension is provided, the desired filename will use that extension instead of the one included in $file.

makeURLSafe() accepts a $filename and changes it to only include lower case characters, numbers and the _, - and . characters.

Translation

When displaying filesystem paths to access resources on the web, translation must occur. The static method translateToWebPath() accepts a filesystem path and returns the equivalent web server path. By default it simply removes the $_SERVER['DOCUMENT_ROOT']. Additional translations can be set up by passing the $search string and $replace strings to the static method addWebPathTranslation().

Normally it would make sense to define the translations in the site config file, while translateToWebPath() would be used in the page code.

// Config file
fFilesystem::addWebPathTranslation('/var/www/vhosts/example.com/assets', '');
<ul>
    <?
    foreach ($files as $file) {
        ?>
        <li><a href="<?php echo fFilesystem::translateToWebPath($file->getPath()) ?>"><?php echo $file->getName() ?></a></li>
        <?
    }
    ?>
</ul>

File Sizes

In addition to some file path helpers, fFilesystem provides two methods to assist with file size manipulation. The method formatFilesize() accepts a filesize in bytes and will return a human-readable size such as 2.1tb or 5.6mb. convertToBytes() takes a human-readable filesize such as 200k or 1.3GB and converts it into bytes.

formatFilesize() accepts up to two parameters with the first, $bytes, being the filesize of the file/directory in bytes and the second optional parameter, $decimal_places, being the number of decimal places to show for the formatted file size.

convertToBytes() accepts just a single parameters, $size, which can be any human-readable filesize that includes units of measure such as k, kilo, m, mega, g, giga, t, tera, b and bytes. The result of the method is the filesize converted into bytes.

fFilesystem API Reference

static class, v1.0.0b16

Handles filesystem-level tasks including filesystem transactions and the reference map to keep all fFile and fDirectory objects in sync

Changes:
1.0.0b16Added a call to clearstatcache() to rollback() to prevent incorrect data from being returned by fFilegetMTime() and fFile::getSize() 11/27/10
1.0.0b15Fixed translateToWebPath() to handle Windows \s 4/9/10
1.0.0b14Added recordAppend() 3/15/10
1.0.0b13Changed the way files/directories deleted in a filesystem transaction are handled, including improvements to the exception that is thrown 3/5/10
1.0.0b12Updated convertToBytes() to properly handle integers without a suffix and sizes with fractions 11/14/09
1.0.0b11Corrected the API documentation for getPathInfo() 9/9/09
1.0.0b10Updated updateExceptionMap() to not contain the Exception class parameter hint, allowing NULL to be passed 8/20/09
1.0.0b9Added some performance tweaks to createObject() 8/6/09
1.0.0b8Changed formatFilesize() to not use decimal places for bytes, add a space before and drop the B in suffixes 7/12/09
1.0.0b7Fixed formatFilesize() to work when $bytes equals zero 7/8/09
1.0.0b6Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b5Changed formatFilesize() to use proper uppercase letters instead of lowercase 6/2/09
1.0.0b4Added the createObject() method 1/21/09
1.0.0b3Removed some unnecessary error suppresion operators 12/11/08
1.0.0b2Fixed a bug where the filepath and exception maps weren't being updated after a rollback 12/11/08
1.0.0bThe initial implementation 3/24/08

Static Methods

::addWebPathTranslation() public

Adds a directory to the web path translation list

The web path conversion list is a list of directory paths that will be converted (from the beginning of filesystem paths) when preparing a path for output into HTML.

By default the $_SERVER['DOCUMENT_ROOT'] will be converted to a blank string, in essence stripping it from filesystem paths.

Signature

void addWebPathTranslation( string $search_path, string $replace_path )

Parameters
string $search_path The path to look for
string $replace_path The path to replace with
::begin() public

Starts a filesystem pseudo-transaction, should only be called when no transaction is in progress.

Flourish filesystem transactions are NOT full ACID-compliant transactions, but rather more of an filesystem undo buffer which can return the filesystem to the state when begin() was called. If your PHP script dies in the middle of an operation this functionality will do nothing for you and all operations will be retained, except for deletes which only occur once the transaction is committed.

Signature

void begin( )

::commit() public

Commits a filesystem transaction, should only be called when a transaction is in progress

Signature

void commit( )

::convertToBytes() public

Takes a file size including a unit of measure (i.e. kb, GB, M) and converts it to bytes

Sizes are interpreted using base 2, not base 10. Sizes above 2GB may not be accurately represented on 32 bit operating systems.

Signature

integer convertToBytes( string $size )

Parameters
string $size The size to convert to bytes
Returns

The number of bytes represented by the size

::createObject() public

Takes a filesystem path and creates either an fDirectory, fFile or fImage object from it

Signature

fDirectory|fFile|fImage createObject( string $path )

Parameters
string $path The path to the filesystem object
Throws
fValidationException
When no path was specified or the path specified does not exist
::formatFilesize() public

Takes the size of a file in bytes and returns a friendly size in B/K/M/G/T

Signature

string formatFilesize( integer $bytes, integer $decimal_places=1 )

Parameters
integer $bytes The size of the file in bytes
integer $decimal_places The number of decimal places to display
::getPathInfo() public

Returns info about a path including dirname, basename, extension and filename

Signature

array getPathInfo( string $path, string $element=NULL )

Parameters
string $path The file/directory path to retrieve information about
string $element The piece of information to return: 'dirname', 'basename', 'extension', or 'filename'
Returns

The file's dirname, basename, extension and filename

::hookDeletedMap() internal public

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

Hooks a file/directory into the deleted backtrace map entry for that filename

Since the value is returned by reference, all objects that represent this file/directory always see the same backtrace.

Signature

mixed &hookDeletedMap( string $file )

Parameters
string $file The name of the file or directory
Returns

Will return NULL if no match, or the backtrace array if a match occurs

::hookFilenameMap() internal public

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

Hooks a file/directory name to the filename map

Since the value is returned by reference, all objects that represent this file/directory will always be update on a rename.

Signature

mixed &hookFilenameMap( string $file )

Parameters
string $file The name of the file or directory
Returns

Will return NULL if no match, or the exception object if a match occurs

::isInsideTransaction() public

Indicates if a transaction is in progress

Signature

void isInsideTransaction( )

::makeUniqueName() public

Returns a unique name for a file

Signature

string makeUniqueName( string $file, string $new_extension=NULL )

Parameters
string $file The filename to check
string $new_extension The new extension for the filename, should not include .
Returns

The unique file name

::makeURLSafe() public

Changes a filename to be safe for URLs by making it all lower case and changing everything but letters, numers, - and . to _

Signature

string makeURLSafe( string $filename )

Parameters
string $filename The filename to clean up
Returns

The cleaned up filename

::recordAppend() internal public

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

Stores what data has been added to a file so it can be removed if there is a rollback

Signature

void recordAppend( fFile $file, string $data )

Parameters
fFile $file The file that is being written to
string $data The data being appended to the file
::recordCreate() internal public

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

Keeps a record of created files so they can be deleted up in case of a rollback

Signature

void recordCreate( object $object )

Parameters
object $object The new file or directory to get rid of on rollback
::recordDelete() internal public

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

Keeps track of file and directory names to delete when a transaction is committed

Signature

void recordDelete( fFile|fDirectory $object )

Parameters
fFile|fDirectory $object The filesystem object to delete
::recordDuplicate() internal public

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

Keeps a record of duplicated files so they can be cleaned up in case of a rollback

Signature

void recordDuplicate( fFile $file )

Parameters
fFile $file The duplicate file to get rid of on rollback
::recordRename() internal public

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

Keeps a temp file in place of the old filename so the file can be restored during a rollback

Signature

void recordRename( string $old_name, string $new_name )

Parameters
string $old_name The old file or directory name
string $new_name The new file or directory name
::recordWrite() internal public

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

Keeps backup copies of files so they can be restored if there is a rollback

Signature

void recordWrite( fFile $file )

Parameters
fFile $file The file that is being written to
::reset() internal public

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

Signature

void reset( )

::rollback() public

Rolls back a filesystem transaction, it is safe to rollback when no transaction is in progress

Signature

void rollback( )

::translateToWebPath() public

Takes a filesystem path and translates it to a web path using the rules added

Signature

string translateToWebPath( string $path )

Parameters
string $path The path to translate
Returns

The filesystem path translated to a web path

::updateDeletedMap() internal public

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

Updates the deleted backtrace for a file or directory

Signature

void updateDeletedMap( string $file, array $backtrace )

Parameters
string $file A file or directory name, directories should end in / or \
array $backtrace The backtrace for this file/directory
::updateFilenameMap() internal public

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

Updates the filename map, causing all objects representing a file/directory to be updated

Signature

void updateFilenameMap( string $existing_filename, string $new_filename )

Parameters
string $existing_filename The existing filename
string $new_filename The new filename
::updateFilenameMapForDirectory() internal public

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

Updates the filename map recursively, causing all objects representing a directory to be updated

Also updates all files and directories in the specified directory to the new paths.

Signature

void updateFilenameMapForDirectory( string $existing_dirname, string $new_dirname )

Parameters
string $existing_dirname The existing directory name
string $new_dirname The new dirname

fGrammar

The fGrammar class was built to handle various grammar tasks. English is the default language, however there is functionality to allow for some localization.

Quantities and Lists

Due to the way the English language works, most messages and output that references quantities will need to change when there is a single item in question, or a quantity other than one. For instance, you may have a message say:

There are currently 15 users.

Which is great as long as your quantity is zero or greater than one. If there is only a single user, the message should change to:

There is currently one user.

Certainly some conditional code could be written to handle the situation, but why introduce a big if statement (or worse, an unreadable tertiary statement) in the middle of display code? The fGrammar class provides a convenient method to handle such situations, inflectOnQuantity().

inflectOnQuantity() has three required parameters and one optional one we will talk about in a minute. The first parameter is the quantity being measured. This can be an integer, or an array of items. If an array is passed, the size of the array will be used for the quantity. The second parameter is the string to be selected if quantity is one, the third parameter is the string to be selected if the quantity is not one. The third parameter can include %d to be replaced with the actual quantity.

echo 'There ' . fGrammar::inflectOnQuantity(1, 'is one user', 'are %d users') . ".\n";
echo 'There ' . fGrammar::inflectOnQuantity(5, 'is one user', 'are %d users') . ".";

The output from the code above would be:

There is one user.
There are 5 users.

The optional fourth parameter is a flag to indicate if you want single digit quantities to be replaced by the word for that quantity:

echo 'There ' . fGrammar::inflectOnQuantity(5, 'is one user', 'are %d users', TRUE) . ".";

Would result in:

There are five users.

Another common situation is to have a variable number of terms to display, however it is desirable to produce a well-formed sentence. The joinArray() method will take an array of strings and will return the string in a well-formed manner. Here are some examples:

echo fGrammar::joinArray(array()) . "\n";
echo fGrammar::joinArray(array('one')) . "\n";
echo fGrammar::joinArray(array('one', 'two')) . "\n";
echo fGrammar::joinArray(array('one', 'two', 'three')) . "\n";
echo fGrammar::joinArray(array('one', 'two', 'three', 'four'));

Here would be the HTML output:

one
one and two
one, two, and three
one, two, three, and four

Plurals and Singulars

Although it is unlikely that this functionality will be necessary for the majority of web sites and applications, it is good to talk a little about plurals and singulars. Most of the ORM code in Flourish uses the pluralize() and singularize() methods to handle detecting what a developer is trying to do.

Here are some examples of how pluralize() and singularize() can be used:

// Simple plural and singular forms
echo fGrammar::pluralize('user') . "\n";
echo fGrammar::singularize('users') . "\n";

// Most irregular forms are automatically handed
echo fGrammar::pluralize('person') . "\n";
echo fGrammar::singularize('people');

The output would be:

users
user
people
person

Unfortunately the English language has some rather complex rules about pluralization with quite a number of exceptions. To compound the matter further, singularization rules are almost non-existent in a formalized nature, and mostly have to be reverse engineered from the pluralization rules. Finally, many website and applications use acronyms heavily, which often times do not follow the same rules as words with similar structures.

Because of these issues, a day will come when fGrammar will not get a pluralization or singularization of a noun correct. As a fall-back, the static method addSingularPluralRule() has been included. Simply pass the singular form of a noun as the first parameter and the plural form as the second parameter. All subsequent requests to singularize() and pluralize() will check this rule before the default rules.

Here is a rather contrived example of when to use addSingularPluralRule():

// These will produce incorrect results
echo fGrammar::pluralize('phalanx') . "\n";
echo fGrammar::singularize('phalanxes') . "\n";

// Add a custom rule for phalanx
fGrammar::addSingularPluralRule('phalanx', 'phalanxes');

// Now the singularization and pluralization work properly
echo fGrammar::pluralize('phalanx') . "\n";
echo fGrammar::singularize('phalanxes') . "\n";

The output of the PHP above would be:

phalanxs
phalanxe
phalanxes
phalanx

Notation Conversion

Notation conversion is also used heavily by the ORM code in Flourish to translate between the various coding standards that exist.

underscore_notation

Underscore notation is characterized by all lower-case letters with underscores separating words, like underscore_notation.

The method underscorize() will convert camel case notation or space separated words to underscore notation.

echo fGrammar::underscorize('FirstName') . "\n";
echo fGrammar::underscorize('Last Name') . "\n";
echo fGrammar::underscorize('middleInitial');

would produce the following output:

first_name
last_name
middle_initial

CamelCase

Camel case notation uses upper and lower-case letters, with words being separated by a capital letter, like CamelCase. Lower camel case is identical to camel case, except the first letter is always lower-case, like lowerCamelCase.

The method camelize() will convert underscore notation or space separated words to camelCase. The first parameter, $string is the string to be converted and the second parameter $upper indicates if the output should be UpperCamelCase instead of lowerCamelCase.

echo fGrammar::camelize('first_name', FALSE) . "\n";
echo fGrammar::camelize('Last name', FALSE) . "\n";
echo fGrammar::camelize('MIDDLE INITIAL', TRUE);

would produce the following output:

firstName
lastName
MiddleInitial

Human Friendly Strings

In addition to converting between these two formats, it is sometimes required to convert from underscore or camel case notation to a human friendly form. humanize() will take any underscore notation or camelCase string and produce output containing spaces and the first letter of each word being capitalized. For specific words know to be all capitals, it will change those words to all capitals.

echo fGrammar::humanize('first_name') . "\n";
echo fGrammar::humanize('LastName') . "\n";
echo fGrammar::humanize('middleInitial') . "\n";
echo fGrammar::humanize('PdfUpload');

would produce the following output:

First Name
Last Name
Middle Initial
PDF Upload

Fixing Notation Conversion Issues

Occasionally conversion from camelCase to underscore_notation doesnt work properly due to the splitting dynamics. In underscorize(), any number or capital letter following a lower-case letter will cause an _ to be inserted. Thus, if a number exists in the middle of an acronym, the conversion will not occur correctly.

To fix such situation, pass the $camel_case version of the string and the $underscore_notation version of the string to addCamelUnderscoreRule():

// In this situation the underscores are put in the wrong place
echo fGrammar::underscorize('w3cCompliant') . "\n";

fGrammar::addCamelUnderscoreRule('W3cCompliant', 'w3c_compliant');

// In this situation the underscores are put in the right place
echo fGrammar::underscorize('w3cCompliant');

The above PHP would output:

w_3c_compliant
w3c_compliant

In addition, calling humanize() can sometimes create results that are not desired. The method uses a simple system of splitting words and capitalizing the first letter. In some situations more than one letter need to be capitalized. The method addHumanizeRule() allows fixing incorrect results.

echo fGrammar::humanize('w3c_compliant') . "\n";

fGrammar::addHumanizeRule('w3c_compliant', 'W3C Compliant');

echo fGrammar::humanize('w3c_compliant');

would output the following:

W3c Compliant
W3C Compliant

Localization

Localization of the method joinArray() can be accomplished by using registerJoinArrayCallback(). This method accepts a single callback and all calls made to joinArray() are then sent to the registered callback. Obviously the method parameters and return value of the callback should match the real method.

fGrammar API Reference

static class, v1.0.0b15

Provides english word inflection, notation conversion, grammar helpers and internationlization support

Changes:
1.0.0b15Added length checking to ensure blank strings are not being passed to various methods 6/20/11
1.0.0b14Fixed a bug in singularization that would affect words containing the substring mice or lice 2/24/11
1.0.0b13Fixed the pluralization of video 8/10/10
1.0.0b12Updated singularize() and pluralize() to be able to handle underscore_CamelCase 8/6/10
1.0.0b11Fixed custom camelCase to underscore_notation rules 6/23/10
1.0.0b10Removed e flag from preg_replace() calls 6/8/10
1.0.0b9Fixed a bug with camelize() and human-friendly strings 6/8/10
1.0.0b8Added the stem() method 5/27/10
1.0.0b7Added the $return_error parameter to pluralize() and singularize() 3/30/10
1.0.0b6Added missing compose() method 3/3/10
1.0.0b5Fixed reset() to properly reset the singularization and pluralization rules 10/28/09
1.0.0b4Added caching for various methods - provided significant performance boost to ORM 6/15/09
1.0.0b3Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b2Fixed a bug where some words would lose capitalization with pluralize() and singularize() 1/25/09
1.0.0bThe initial implementation 9/25/07

Static Methods

::addCamelUnderscoreRule() public

Adds a custom camelCase to underscore_notation and underscore_notation to camelCase rule

Signature

void addCamelUnderscoreRule( string $camel_case, string $underscore_notation )

Parameters
string $camel_case The lower camelCase version of the string
string $underscore_notation The underscore_notation version of the string
::addHumanizeRule() public

Adds a custom mapping of a non-humanized string to a humanized string for humanize()

Signature

void addHumanizeRule( string $non_humanized_string, string $humanized_string )

Parameters
string $non_humanized_string The non-humanized string
string $humanized_string The humanized string
::addSingularPluralRule() public

Adds a custom singular to plural and plural to singular rule for pluralize() and singularize()

Signature

void addSingularPluralRule( string $singular, string $plural )

Parameters
string $singular The singular version of the noun
string $plural The plural version of the noun
::camelize() public

Converts an underscore_notation, human-friendly or camelCase string to camelCase

Signature

string camelize( string $string, boolean $upper )

Parameters
string $string The string to convert
boolean $upper If the camel case should be UpperCamelCase
Returns

The converted string

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::humanize() public

Makes an underscore_notation, camelCase, or human-friendly string into a human-friendly string

Signature

string humanize( string $string )

Parameters
string $string The string to humanize
Returns

The converted string

::inflectOnQuantity() public

Returns the singular or plural form of the word or based on the quantity specified

Signature

string inflectOnQuantity( mixed $quantity, string $singular_form, string $plural_form=NULL, boolean $use_words_for_single_digits=FALSE )

Parameters
mixed $quantity The quantity (integer) or an array of objects to count
string $singular_form The string to be returned for when $quantity = 1
string $plural_form The string to be returned for when $quantity != 1, use %d to place the quantity in the string
boolean $use_words_for_single_digits If the numbers 0 to 9 should be written out as words
::joinArray() public

Returns the passed terms joined together using rule 2 from Strunk & White's 'The Elements of Style'

Signature

string joinArray( array $strings, string $type )

Parameters
array $strings An array of strings to be joined together
string $type The type of join to perform, 'and' or 'or'
Returns

The terms joined together

::pluralize() public

Returns the plural version of a singular noun

Signature

string pluralize( string $singular_noun, boolean $return_error=FALSE )

Parameters
string $singular_noun The singular noun to pluralize
boolean $return_error If this is TRUE and the noun can't be pluralized, FALSE will be returned instead
Returns

The pluralized noun

::registerJoinArrayCallback() public

Allows replacing the joinArray() function with a user defined function

This would be most useful for changing joinArray() to work with languages other than English.

Signature

void registerJoinArrayCallback( callback $callback )

Parameters
callback $callback The function to replace joinArray() with - should accept the same parameters and return the same type
::reset() internal public

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

Signature

void reset( )

::singularize() public

Returns the singular version of a plural noun

Signature

string singularize( string $plural_noun, boolean $return_error=FALSE )

Parameters
string $plural_noun The plural noun to singularize
boolean $return_error If this is TRUE and the noun can't be pluralized, FALSE will be returned instead
Returns

The singularized noun

::stem() public

Uses the Porter Stemming algorithm to create the stem of a word, which is useful for searching

See http://tartarus.org/~martin/PorterStemmer/ for details about the algorithm.

Signature

string stem( string $word )

Parameters
string $word The word to get the stem of
Returns

The stem of the word

::underscorize() public

Converts a camelCase, human-friendly or underscore_notation string to underscore_notation

Signature

string underscorize( string $string )

Parameters
string $string The string to convert
Returns

The converted string

fHTML

The fHTML class helps to manipulate text so that it can be reliably converted for HTML display.

Escaping Content (Security)

When developing websites that allow for user-generated content, it can be challenging to ensure that all content entered is displayed as it was intended to be displayed. Certain content may allow HTML, while other may not. The fHTML class provides two static methods for escaping data to be displayed, encode() and prepare(). Both methods treat all content as UTF-8.

Disallowing HTML

If there is a need to escape content that does not allow HTML, the static method encode() should be used. This method will encode special characters, including the < and > characters used for HTML tags. Because of this, all HTML tags will be displayed as plain text, and will not function as HTML.

It is very important that all untrusted user input be escaped using this method to prevent cross-site scripting attacks. See the security page for more information.

echo fHTML::encode($form_submitted_content);

encode() also properly escapes characters for display inside of HTML input, textarea and select tags.

Allowing HTML

prepare() will ensure that all special characters in the provided content will be properly displayed, while allowing HTML tags and entities. This method should only be used for content coming from trusted sources otherwise the code will be vulnerable to cross-site scripting attacks.

echo fHTML::prepare($trusted_content);

Formatting

In addition to preparing content for valid display by encoding content, often times content needs to have some basic HTML formatting applied to it. The fHTML class provides two methods, convertNewlines() and makeLinks(), to help with common formatting tasks.

convertNewlines() will look at content as a mixture of plain text and HTML. If neither the <br /> or any block-level HTML tags are found, the content will have all newline characters converted to <br />. If the converse is true, the content will be returned as is.

Below is an example of the conversion happening:

// First example, no block-level HTML
$content = <<<TEXT
Here is content to be formatted.

Newline characters will be converted into HTML break tags.
TEXT;
echo fHTML::convertNewlines($content);

The output from above would be:

Here is content to be formatted.<br />
<br />
Newline characters will be converted into HTML break tags.

Here is an example of newlines being left as-is due to block-level HTML:

// Second example, block-level HTML present
$content2 = <<<TEXT
<p>
    Here is content to be formatted.
</p>
<p>
   Newline characters will be left alone since there are block-level HTML tags present.
</p>
TEXT;
echo fHTML::convertNewlines($content2);

The output from above would be:

<p>
    Here is content to be formatted.
</p>
<p>
   Newline characters will be left alone since there are block-level HTML tags present.
</p>

The makeLinks() method will parse through a string and create HTML links out of anything that resembles a URL or email address, as long as it is not already part of an <a> tag.

Here is an example of it in action:

$content  = "Example 1: www.example.com.\n";
$content .= "Example 2: https://example.com.\n";
$content .= "Example 3: john@example.com.\n";
$content .= "Example 4: ftp://john:password@example.com.\n";
$content .= "Example 5: www.example.co.uk.\n";
$content .= "Example 6: john@example.co.uk.\n";
$content .= 'Example 7: <a href="http://example.com">http://example.com</a>.';

echo fHTML::makeLinks($content);

The output would be:

Example 1: <a href="http://www.example.com">www.example.com</a>.
Example 2: <a href="https://example.com">https://example.com</a>.
Example 3: <a href="mailto:john@example.com">john@example.com</a>.
Example 4: <a href="ftp://john:password@example.com">ftp://john:password@example.com</a>.
Example 5: <a href="http://www.example.co.uk">www.example.co.uk</a>.
Example 6: <a href="mailto:john@example.co.uk">john@example.co.uk</a>.
Example 7: <a href="http://example.com">http://example.com</a>.

Select Options and Checkboxes

When displaying select and checkbox inputs it is necessary to print specific attributes to specify the current state of the inputs. The fHTML class provides two helper methods to simply this process: printOption() and showChecked().

printOption() will display a select input option tag while marking it with selected="selected" if the options value equals the currently selected value. The following PHP:

<select name="status">
    <?
    $statuses = array(
        'active'   => 'Active',
        'inactive' => 'Inactive',
        'pending'  => 'Pending'
    );
    $current_status = 'active';
    
    foreach ($statuses as $value => $text) {
        fHTML::printOption($text, $value, $current_status);
    }
    ?>
</select>

would produce the following HTML:

<select name="status">
    <option value="active" selected="selected">Active</option>
    <option value="inactive">Inactive</option>
    <option value="pending">Pending</option>
</select>

showChecked() provides similar functionality for checkboxes, however since checkboxes can include many different attributes, showChecked() only handles printing the checked="checked" part. Here is an example of using showChecked():

<?php
$is_authenticated = TRUE;
$is_super_admin   = FALSE;
?>
<p>
    <label for="form-is_authenticated">Is Authenticated</label><br />
    <input type="checkbox" id="form-is_authenticated" name="is_authenticated" value="1" <? fHTML::showChecked($is_authenticated, TRUE) ?> />
</p>
<p>
    <label for="form-is_super_admin">Is Super Admin</label><br />
    <input type="checkbox" id="form-is_super_admin" name="is_super_admin" value="1" <? fHTML::showChecked($is_super_admin, TRUE) ?> />
</p>

would produce the following output:

<p>
    <label for="form-is_authenticated">Is Authenticated</label><br />
    <input type="checkbox" id="form-is_authenticated" name="is_authenticated" value="1" checked="checked" />
</p>
<p>
    <label for="form-is_super_admin">Is Super Admin</label><br />
    <input type="checkbox" id="form-is_super_admin" name="is_super_admin" value="1" />
</p>

Sending the Content Type Header

If you default content is HTML, the method sendHeader() should be called ensure that the Content-Type header is set to the correct value of text/html; charset=utf-8. The utf-8 character set encoding is specified since all of Flourish is built to work with UTF-8.

fHTML API Reference

static class, v1.0.0b8

Provides HTML-related methods

This class is implemented to use the UTF-8 character encoding. Please see UTF-8 for more information.

Changes:
1.0.0b8Changed encode() and prepare() to handle arrays of strings 5/19/10
1.0.0b7Fixed a bug where some conditional comments were causing the regex in prepare() to break 11/4/09
1.0.0b6Updated showChecked() to require strict equality if one parameter is NULL 6/2/09
1.0.0b5Fixed prepare() so it does not encode multi-line HTML comments 5/9/09
1.0.0b4Added methods printOption() and showChecked() that were in fCRUD 5/8/09
1.0.0b3Fixed a bug where makeLinks() would double-link some URLs 1/8/09
1.0.0b2Fixed a bug where makeLinks() would create links out of URLs in HTML tags 12/5/08
1.0.0bThe initial implementation 9/25/07

Static Methods

::containsBlockLevelHTML() public

Checks a string of HTML for block level elements

Signature

boolean containsBlockLevelHTML( string $content )

Parameters
string $content The HTML content to check
Returns

If the content contains a block level tag

::convertNewlines() public

Converts newlines into br tags as long as there aren't any block-level HTML tags present

Signature

void convertNewlines( string $content )

Parameters
string $content The content to display
::decode() public

Converts all HTML entities to normal characters, using UTF-8

Signature

string decode( string $content )

Parameters
string $content The content to decode
Returns

The decoded content

::encode() public

Converts all special characters to entites, using UTF-8.

Signature

string encode( string|array $content )

Parameters
string|array $content The content to encode
Returns

The encoded content

Takes a block of text and converts all URLs into HTML links

Signature

string makeLinks( string $content, integer $link_text_length=0 )

Parameters
string $content The content to parse for links
integer $link_text_length If non-zero, all link text will be truncated to this many characters
Returns

The content with all URLs converted to HTML link

::prepare() public

Prepares content for display in UTF-8 encoded HTML - allows HTML tags

Signature

string prepare( string|array $content )

Parameters
string|array $content The content to prepare
Returns

The encoded html

::printOption() public

Prints an option tag with the provided value, using the selected value to determine if the option should be marked as selected

Signature

void printOption( string $text, string $value, string $selected_value=NULL )

Parameters
string $text The text to display in the option tag
string $value The value for the option
string $selected_value If the value is the same as this, the option will be marked as selected
::sendHeader() public

Sets the proper Content-Type header for a UTF-8 HTML (or pseudo-XHTML) page

Signature

void sendHeader( )

::show() public

Prints a p (or div if the content has block-level HTML) tag with the contents and the class specified - will not print if no content

Signature

boolean show( string $content, string $css_class='' )

Parameters
string $content The content to display
string $css_class The CSS class to apply
Returns

If the content was shown

::showChecked() public

Prints a checked="checked" HTML input attribute if $value equals $checked_value, or if $value is in $checked_value

Please note that if either $value or $checked_value is NULL, a strict comparison will be performed, whereas normally a non-strict comparison is made. Thus 0 and FALSE will cause the checked attribute to be printed, but 0 and NULL will not.

Signature

boolean showChecked( string $value, string|array $checked_value )

Parameters
string $value The value for the current HTML input tag
string|array $checked_value The value (or array of values) that has been checked
Returns

If the checked attribute was printed

fImage

The fImage class is a representation of an image file on the filesystem. It provides all of the functionality of the fFile class and adds the ability to perform image manipulation via GD or ImageMagick.

Configuration

As mentioned above, the fImage class provide image manipulation by using either the command line program ImageMagick or the PHP GD extension. fImage prefers ImageMagick since it provides superior performance and does not have the restriction of the PHP memory limit, like the GD extension does.

If the GD extension is not installed and ImageMagick can not be found in a standard location, fImage will need to be configured with the location of ImageMagick. This can be done by passing the ImageMagick binarys directory to the static method setImageMagickDirectory().

// An example of manually configuring ImageMagick
fImage::setImageMagickDirectory('/usr/local/imagemagick/bin');

In addition to configuring the path to the ImageMagick binaries, it is also possible to set a temporary directory for ImageMagick to use when processing files. The setImageMagickTempDir() static method performs this task and takes a single parameter $temp_dir.

// Setting a custom temporary directory for ImageMagick
fImage::setImageMagickTempDir('/tmp/imagemagick');

Image Information

The fImage class includes a few methods to provide basic metadata about the image. getWidth() and getHeight() provide straight-forward access to image dimensions. getDimensions() returns a two-element array of the width, then the height.

$width  = $image->getWidth();
$height = $image->getHeight();

list($witdh, $height) = $image->getDimensions();

The method getType() will return a string with the image type. Types include: 'jpg', 'gif', 'png' and 'tif'.

$type = $image->getType();

Modification

Probably the most significant functionality of the fImage class is the ability to modify an image. There are currently three different modifications that can be performed: crop(), cropToRatio(), resize() and desaturate().

Calling any of these modifications will not actually cause the changes to be written to disk until saveChanges() is called.

Cropping

crop() accepts four parameters, the pixels dimensions for $new_width and $new_height, $crop_from_x, $crop_from_y, and will perform pixel-specific cropping.

// Create a 16x16 icon from the top left of an image
$image1 = new fImage('./example.gif');
$image1->crop(16, 16, 0, 0);
$image1->saveChanges();

It is also possible to crop with $crop_from_x and $crop_from_y being a string position. The possible values for $crop_from_x are left, center and right. The possible values for $crop_from_y are top, center and bottom.

// Create a 16x16 icon from the top right of an image
$image1 = new fImage('./example.gif');
$image1->crop(16, 16, 'right', 'top');
$image1->saveChanges();

cropToRatio() will crop the largest rectangle our of an image that conforms to the ratio passed in via $ratio_width and $ratio_height. By default, the crop is made from the center of the image.

// Create a 32x32 icon from an image
$image3 = new fImage('./example.gif');
$image3->cropToRatio(1, 1);
$image3->resize(32, 32);
$image3->saveChanges();

Similar to crop(), it is possible to crop to a ratio from a specific position in the image. The third parameter, $horizontal_position accepts either left, center or right. The fourth parameter, $vertical_position, accepts top, center or bottom.

// Create a 32x32 icon from the bottom right of an image
$image3 = new fImage('./example.gif');
$image3->cropToRatio(1, 1, 'right', 'bottom');
$image3->resize(32, 32);
$image3->saveChanges();

Desaturating

desaturate() will cause the image to lose all color information and become grayscale.

// Change the image to be grayscale
$image3 = new fImage('./example.gif');
$image3->desaturate();
$image3->saveChanges();

Resizing

resize() will scale the image proportionally to fit the canvas size defined by the parameters $canvas_width and $canvas_height. That is to say, the aspect ratio of the image will be retained, and it will not be stretched in either dimension. If either parameter is empty() or 0, the image will be resized to fit the one specified dimension.

// Ensure an image is never wider than 250 pixels
$image4 = new fImage('./example.gif');
$image4->resize(250, 0);
$image4->saveChanges();

resize() can also accept an optional third parameter, $allow_upsizing. By default resize() will only make the image smaller, but when this is TRUE the image will be made to fit the dimensions even if it must be increased in size.

// Make an image exactly 100 pixels wide
$image5 = new fImage('./example.gif');
$image5->resize(100, 0, TRUE);
$image5->saveChanges();

Saving Changes

saveChanges() can accept from zero to three parameters. The first optional parameter is the $new_image_type which can be 'jpg', 'gif' or 'png'. If no $new_image_type is specified, the image will be resaved in the current format.

// Saving as a PNG
$image2 = new fImage('./example.gif');
$image2->resize(250, 0);
$image2->saveChanges('png');

If the 'jpg' image type is specified, there is a second optional parameter, the $jpeg_quality to use. If no $jpeg_quality is specified, 90 is used as the default.

// Saving as a 60 quality JPEG
$image2 = new fImage('./example.gif');
$image2->resize(250, 0);
$image2->saveChanges('jpeg', 60);

A third optional parameter, $overwrite, accepts a boolean and will cause a file with the same name and type to be overwritten. For example, if the file is currently image.gif and you call saveChanges() with the parameter 'jpg' and another file named image.jpg exists, by default image.gif will be saved with changes as image_copy1.jpg. When $overwrite is TRUE, image.jpg will be overwritten.

// Saving as a PNG and overwrite any existing example.png
$image2 = new fImage('./example.gif');
$image2->resize(250, 0);
$image2->saveChanges('png', TRUE);

fImage API Reference

class, v1.0.0b33

Represents an image on the filesystem, also provides image manipulation functionality

Changes:
1.0.0b33Fixed a method signature 8/24/11
1.0.0b32Added a call to clearstatcache() to saveChanges() to solve a bug when fFile::output() is called in the same script execution 5/23/11
1.0.0b31Fixed a bug in using ImageMagick to convert files with a colon in the filename 3/20/11
1.0.0b30Added a check for systems using the GD extension and no memory limit, plus a check for ImageMagick's convert command failing 3/20/11
1.0.0b29Added checks for AIX 1/19/11
1.0.0b28Added the rotate() method, added code to try and prevent fatal errors due to hitting the memory limit when using GD 11/29/10
1.0.0b27Backwards Compatibility Break - changed the parameter order in crop() from $crop_from_x, $crop_from_y, $new_width, $new_height to $new_width, $new_height, $crop_from_x, $crop_from_y - added $horizontal_position and $vertical_position parameters to cropToRatio() 11/9/10
1.0.0b26Fixed a bug where processing via ImageMagick was not properly setting the default RGB colorspace 10/19/10
1.0.0b25Fixed the class to not generate multiple files when saving a JPG from an animated GIF or a TIF with a thumbnail 9/12/10
1.0.0b24Updated class to use fCore::startErrorCapture() instead of error_reporting() 8/9/10
1.0.0b23Fixed the class to detect when exec() is disabled and the function has a space before or after in the list 7/21/10
1.0.0b22Fixed isImageCompatible() to handle certain JPGs created with Photoshop 4/3/10
1.0.0b21Fixed resize() to allow dimensions to be numeric strings instead of just integers 4/9/10
1.0.0b20Added append() 3/15/10
1.0.0b19Updated for the new fFile API 3/5/10
1.0.0b18Fixed a bug in saveChanges() that would incorrectly cause new filenames to be created, added the $overwrite parameter to saveChanges(), added the $allow_upsizing parameter to resize() 3/3/10
1.0.0b17Fixed a couple of bug with using ImageMagick on Windows and BSD machines 3/2/10
1.0.0b16Fixed some bugs with GD not properly handling transparent backgrounds and desaturation of .gif files 10/27/09
1.0.0b15Added getDimensions() 8/7/09
1.0.0b14Performance updates for checking image type and compatiblity 7/31/09
1.0.0b13Updated class to work even if the file extension is wrong or not present, saveChanges() detects files that aren't writable 7/29/09
1.0.0b12Fixed a bug where calling saveChanges() after unserializing would throw an exception related to the image processor 5/27/09
1.0.0b11Added a crop() method 5/27/09
1.0.0b10Fixed a bug with GD not saving changes to files ending in .jpeg 3/18/09
1.0.0b9Changed processWithGD() to explicitly free the image resource 3/18/09
1.0.0b8Updated for new fCore API 2/16/09
1.0.0b7Changed @ error suppression operator to error_reporting() calls 1/26/09
1.0.0b6Fixed cropToRatio() and resize() to always return the object even if nothing is to be done 1/5/09
1.0.0b5Added check to see if exec() is disabled, which causes ImageMagick to not work 1/3/09
1.0.0b4Fixed saveChanges() to not delete the image if no changes have been made 12/18/08
1.0.0b3Fixed a bug with $jpeg_quality in saveChanges() from 1.0.0b2 12/16/08
1.0.0b2Changed some int casts to round() to fix resize() dimension issues 12/11/08
1.0.0bThe initial implementation 12/19/07

Genealogy

Class Tree
fFile
   |
   --fImage
Inherited Variables
fFile::$deleted
A backtrace from when the file was deleted
fFile::$file
The full path to the file
Inherited Methods
fFile::__construct()
Creates an object to represent a file on the filesystem
fFile::__clone()
Duplicates a file in the current directory when the object is cloned
fFile::__get()
All requests that hit this method should be requests for callbacks
fFile::__sleep()
The iterator information doesn't need to be serialized since a resource can't be
fFile::__toString()
Returns the filename of the file
fFile::__wakeup()
Re-inserts the file back into the filesystem map when unserialized
fFile::append()
Appends the provided data to the file
fFile::count()
Returns the number of lines in the file
fFile::create()
Creates a file on the filesystem and returns an object representing it.
fFile::current()
Returns the current line of the file (required by iterator interface)
fFile::delete()
Deletes the current file
fFile::determineMimeType()
Determines the file's mime type by either looking at the file contents or matching the extension
fFile::duplicate()
Creates a new file object with a copy of this file
fFile::getExtension()
Gets the file extension
fFile::getMimeType()
Gets the file's mime type
fFile::getMTime()
Returns the last modification time of the file
fFile::getName()
Gets the filename (i.e. does not include the directory)
fFile::getParent()
Gets the directory the file is located in
fFile::getPath()
Gets the file's current path (directory and filename)
fFile::getSize()
Gets the size of the file
fFile::isWritable()
Check to see if the current file is writable
fFile::key()
Returns the current one-based line number (required by iterator interface)
fFile::move()
Moves the current file to a different directory
fFile::next()
Advances to the next line in the file (required by iterator interface)
fFile::output()
Prints the contents of the file
fFile::read()
Reads the data from the file
fFile::rename()
Renames the current file
fFile::rewind()
Rewinds the file handle (required by iterator interface)
fFile::tossIfDeleted()
Throws an fProgrammerException if the file has been deleted
fFile::valid()
Returns if the file has any lines left (required by iterator interface)
fFile::write()
Writes the provided data to the file

Static Methods

::create() public

Creates an image on the filesystem and returns an object representing it

This operation will be reverted by a filesystem transaction being rolled back.

Signature

fImage create( string $file_path, string $contents )

Parameters
string $file_path The path to the new image
string $contents The contents to write to the image
Throws
fValidationException
When no image was specified or when the image already exists
Overrides
fFile::create()
Creates a file on the filesystem and returns an object representing it.
::getCompatibleMimetypes() internal public

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

Returns an array of acceptable mime types for the processor that was detected

Signature

array getCompatibleMimetypes( )

Returns

The mime types that the detected image processor can manipulate

::getInfo() protected

Gets the dimensions and type of an image stored on the filesystem

The 'type' key will have one of the following values:

  • {null} (File type is not supported)
  • 'jpg'
  • 'gif'
  • 'png'
  • 'tif'
Signature

mixed getInfo( string $image_path, string $element=NULL )

Parameters
string $image_path The path to the image to get stats for
string $element The element to retrieve: 'type', 'width', 'height'
Returns

An associative array: 'type' => {mixed}, 'width' => {integer}, 'height' => {integer}, or the element specified

Throws
fValidationException
When the file specified is not an image
::isImageCompatible() internal public

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 make sure the class can handle the image file specified

Signature

boolean isImageCompatible( string $image )

Parameters
string $image The image to check for incompatibility
Returns

If the image is compatible with the detected image processor

Throws
fValidationException
When the image specified does not exist
::reset() internal public

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

Signature

void reset( )

::setImageMagickDirectory() public

Sets the directory the ImageMagick binary is installed in and tells the class to use ImageMagick even if GD is installed

Signature

void setImageMagickDirectory( string $directory )

Parameters
string $directory The directory ImageMagick is installed in
::setImageMagickTempDir() public

Sets a custom directory to use for the ImageMagick temporary files

Signature

void setImageMagickTempDir( string $temp_dir )

Parameters
string $temp_dir The directory to use for the ImageMagick temp dir

Methods

->__construct() public

Creates an object to represent an image on the filesystem

Signature

fImage __construct( string $file_path, boolean $skip_checks=FALSE )

Parameters
string $file_path The path to the image
boolean $skip_checks If file checks should be skipped, which improves performance, but may cause undefined behavior - only skip these if they are duplicated elsewhere
Throws
fValidationException
When no image was specified, when the image does not exist or when the path specified is not an image
Overrides
fFile::__construct()
Creates an object to represent a file on the filesystem
->append() public

Prevents a programmer from trying to append an image

Signature

void append( mixed $data )

Parameters
mixed $data The data to append to the image
Overrides
fFile::append()
Appends the provided data to the file
->crop() public

Crops the image by the exact pixel dimensions specified

The crop does not occur until saveChanges() is called.

Signature

fImage crop( numeric $new_width, numeric $new_height, numeric|string $crop_from_x, numeric|string $crop_from_y )

Parameters
numeric $new_width The width in pixels to crop the image to
numeric $new_height The height in pixels to crop the image to
numeric|string $crop_from_x The number of pixels from the left of the image to start the crop from, or a horizontal position of 'left', 'center' or 'right'
numeric|string $crop_from_y The number of pixels from the top of the image to start the crop from, or a vertical position of 'top', 'center' or 'bottom'
Returns

The image object, to allow for method chaining

->cropToRatio() public

Crops the biggest area possible from the center of the image that matches the ratio provided

The crop does not occur until saveChanges() is called.

Signature

fImage cropToRatio( numeric $ratio_width, numeric $ratio_height, string $horizontal_position='center', string $vertical_position='center' )

Parameters
numeric $ratio_width The width ratio to crop the image to
numeric $ratio_height The height ratio to crop the image to
string $horizontal_position A horizontal position of 'left', 'center' or 'right'
string $vertical_position A vertical position of 'top', 'center' or 'bottom'
Returns

The image object, to allow for method chaining

->desaturate() public

Converts the image to grayscale

Desaturation does not occur until saveChanges() is called.

Signature

fImage desaturate( )

Returns

The image object, to allow for method chaining

->getDimensions() public

Returns the width and height of the image as a two element array

Signature

array getDimensions( )

Returns

In the format 0 => (integer) {width}, 1 => (integer) {height}

->getHeight() public

Returns the height of the image

Signature

integer getHeight( )

Returns

The height of the image in pixels

->getType() public

Returns the type of the image

Signature

string getType( )

Returns

The type of the image: 'jpg', 'gif', 'png', 'tif'

->getWidth() public

Returns the width of the image

Signature

integer getWidth( )

Returns

The width of the image in pixels

->resize() public

Sets the image to be resized proportionally to a specific size canvas

Will only size down an image. This method uses resampling to ensure the resized image is smooth in appearance. Resizing does not occur until saveChanges() is called.

Signature

fImage resize( integer $canvas_width, integer $canvas_height, boolean $allow_upsizing=FALSE )

Parameters
integer $canvas_width The width of the canvas to fit the image on, 0 for no constraint
integer $canvas_height The height of the canvas to fit the image on, 0 for no constraint
boolean $allow_upsizing If the image is smaller than the desired canvas, the image will be increased in size
Returns

The image object, to allow for method chaining

->rotate() public

Sets the image to be rotated

Rotation does not occur until saveChanges() is called.

Signature

void rotate( integer $degrees )

Parameters
integer $degrees The number of degrees to rotate - 90, 180, or 270
->saveChanges() public

Saves any changes to the image

If the file type is different than the current one, removes the current file once the new one is created.

This operation will be reverted by a filesystem transaction being rolled back. If a transaction is in progress and the new image type causes a new file to be created, the old file will not be deleted until the transaction is committed.

Signatures

fImage saveChanges( string $new_image_type=NULL, integer $jpeg_quality=90, boolean $overwrite=FALSE )

fImage saveChanges( string $new_image_type=NULL, boolean $overwrite=FALSE )

Parameters
string $new_image_type The new file format for the image: 'NULL (no change), 'jpg', 'gif', 'png'`
integer $jpeg_quality The quality setting to use for JPEG images - this may be ommitted
boolean $overwrite If an existing file with the same name and extension should be overwritten
Returns

The image object, to allow for method chaining

fJSON

The fJSON class is primarily a compatibility class to allow for encoding and decoding JSON strings on server running PHP 5.0 and 5.1PHP 5.2 comes bundled with json_encode() and json_decode(). In addition, the class provides a simple method to send the proper headers with a JSON response.

Encoding and Decoding

The methods encode() and decode() allow for encoding and decoding JSON respectively. For servers that do have the json PHP extension, the json extension functions will be used. A native PHP implementation will automatically be used for server that do not have the json extension installed.

Encoding

encode() takes a single parameter, the PHP value to encode. Associative arrays will be encoded as JSON objects, with the rest of PHP data types being converted into the equivalent JSON data type. Technically all valid JSON needs to be either an array or an object, however the PHP json extension does not follow this restriction.

The following PHP

echo fJSON::encode(array());
echo "\n\n";

echo fJSON::encode(
    array(1, 2, 3, 4, 'Hello!', TRUE, FALSE, NULL)
);
echo "\n\n";

echo fJSON::encode(
    array('foo' => 'bar', 'key' => TRUE
);

would output the following JSON:

[]

[1,2,3,4,"Hello!",true,false,null]

{"foo":"bar","key":true}

Decoding

decode() takes a JSON string and will convert it into the equivalent PHP data types. An optional second parameter $assoc when set to TRUE will cause JSON objects to be converted to associative arrays instead of an instance of the PHP class stdClass.

The following PHP

echo fCore::dump(fJSON::decode('[]'));
echo "\n\n";

echo fCore::dump(fJSON::decode('[1,2,3,4,"Hello!",true,false,null]'));
echo "\n\n";

echo fCore::dump(fJSON::decode('{"foo":"bar","key":true}'));
echo "\n\n";

echo fCore::dump(fJSON::decode('{"foo":"bar","key":true}', TRUE));

would output the following text:

Array
(
)

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => Hello!
    [5] => {true}
    [6] => {false}
    [7] => {null}
)

stdClass Object
(
    [foo] => bar
    [key] => {true}
)

Array
(
    [foo] => bar
    [key] => {true}
)

Sending the Content Type Header

If you are returning JSON in a response, calling the sendHeader() mehod will ensure that the Content-Type header is set to the correct value of application/json; charset=utf-8.

Outputting

Usually when writing a script that creates JSON, the header will need to be sent and a PHP value will need to be encoded and output. The static method output() does all of this.

// These two blocks are equivalent
fJSON::sendHeader();
echo fJSON::encode($value);

fJSON::output($value);

fJSON API Reference

static class, v1.0.0b6

Provides encoding and decoding for JSON

This class is a compatibility class for the json extension on servers with PHP 5.0 or 5.1, or servers with the json extension compiled out.

This class will handle JSON values that are not contained in an array or object - such values are not valid according to the JSON spec, but the functionality is included for compatiblity with the json extension.

Changes:
1.0.0b6Removed e flag from preg_replace() calls 6/8/10
1.0.0b5Added the output() method 3/15/10
1.0.0b4Fixed a bug with decode() where JSON objects could lose all but the first key: value pair 5/6/09
1.0.0b3Updated the class to be consistent with PHP 5.2.9+ for encoding and decoding invalid data 5/4/09
1.0.0b2Changed @ error suppression operator to error_reporting() calls 1/26/09
1.0.0bThe initial implementation 7/12/08

Constants

::J_ARRAY_CLOSE internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of ]

::J_ARRAY_COMMA internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of , in a JSON array

::J_ARRAY_OPEN internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of [

::J_COLON internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of :

::J_FALSE internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of a boolean false

::J_FLOAT internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of a floating value

::J_INTEGER internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of an integer

::J_KEY internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of a JSON object key

::J_NULL internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of null

::J_OBJ_CLOSE internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of }

::J_OBJ_COMMA internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of , in a JSON object

::J_OBJ_OPEN internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of {

::J_STRING internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of a string

::J_TRUE internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

An abstract representation of a boolean true

Static Methods

::decode() public

Decodes a JSON string into native PHP data types

This function is very strict about the format of JSON. If the string is not a valid JSON string, NULL will be returned.

Signature

array|stdClass decode( string $json, boolean $assoc=FALSE )

Parameters
string $json This should be the name of a related class
boolean $assoc If this is TRUE, JSON objects will be represented as an assocative array instead of a stdClass object
Returns

A PHP equivalent of the JSON string

::encode() public

Encodes a PHP value into a JSON string

Signature

string encode( mixed $value )

Parameters
mixed $value The PHP value to encode
Returns

The JSON string that is equivalent to the PHP value

::output() public

Sets the proper Content-Type header and outputs the value, encoded as JSON

Signature

void output( mixed $value )

Parameters
mixed $value The PHP value to output as JSON
::sendHeader() public

Sets the proper Content-Type header for UTF-8 encoded JSON

Signature

void sendHeader( )

fLoader

The fLoader class is designed to load Flourish class. By default it detects the optimal type of loading to perform based on the server environment. In addition to loading classes, it creates constructor functions for the various Flourish value object classes.

Standard Usage

Loading Flourish should normally be performed by calling the static method best().

include './path/to/fLoader.php';
fLoader::best();

If an opcode cache (e.g. APC, XCache, etc) is detected, all class file are immediately included. If an opcode cache is not detected, an autoload function is registered via spl_autoload_register().

If you need explicit control over what type of loading is performed, you should call one of the static methods eager() or lazy() instead.

Constructor Functions

Constructor functions is a term used to refer to functions that simply call the constructor of a class. The functions have the same name as the class, which effectively means that the new keyword is not required, and that method calls may be changed off of the constructor.

// Without constructor functions
$date = new fDate();
$date = $date->modify('Y-m-01');

// With constructor functions
$date = fDate()->modify('Y-m-01');

fLoader automatically creates constructor functions for all value object classes:

Detecting Opcode Caches

The static method hasOpcodeCache() can be used to create intelligent loading functionality for other code.

include './other/project/Loader.php';
if (fLoader::hasOpcodeCache()) {
    Loader::includeAll();
} else {
    Loader::autoload();
}

fLoader API Reference

static class, v1.0.0b3

A class that loads Flourish

Changes:
1.0.0b3Added fEmail() constructor function 9/12/11
1.0.0b2Added fPagination 9/6/11
1.0.0bThe initial implementation 8/26/11

Static Methods

::autoload() internal public

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

Tries to load a Flourish class

Signature

void autoload( string $class )

Parameters
string $class The class to load
::best() public

Performs eager loading if an op-code cache is present, otherwise lazy

Signature

void best( )

::eager() public

Loads all Flourish classes when called

Signature

void eager( )

::hasOpcodeCache() public

Check if a PHP opcode cache is installed

The following opcode caches are currently detected:

Signature

boolean hasOpcodeCache( )

Returns

If a PHP opcode cache is loaded

::lazy() public

Registers an autoloader for Flourish via spl_autoload_register()

Signature

void lazy( )

fMailbox

The fMailbox class provides an interface to list, fetch and delete emails from POP3 and IMAP servers. It fully parses the messages, handles attachments, related content and inline files. All text content is converted to UTF-8. This class does not require the PHP imap extension. Secure connections are supported if the openssl extension is installed.

Instantiation

To create a mailbox object, you must provide the $type (either pop3 or imap), $server, $username and $password.

// Connect to a local pop3 server
$mailbox = new fMailbox('pop3', 'localhost', 'user', 'password');

// Connect to a remote imap server
$mailbox = new fMailbox('imap', 'example.com', 'user', 'password');

The $port is automatically determined by the type, but can be overridden as a fifth parameter.

// Connect to an imap server on port 874
$mailbox = new fMailbox('imap', 'example.com', 'user', 'password', 874);

If the connection needs to be $secure, pass TRUE to the sixth parameter.

// Make a secure connection on the default secure IMAP port
$mailbox = new fMailbox('imap', 'example.com', 'user', 'password', NULL, TRUE);

The default socket $timeout in seconds can be adjusted with the seventh parameter.

// Timeout after 5 seconds
$mailbox = new fMailbox('imap', 'example.com', 'user', 'password', NULL, FALSE, 5);

Listing

A list of messages can be retrieved from a mailbox by calling the listMessages() method.

// Retrieve an overview of all messages
$messages = $mailbox->listMessages();

The result in an array of messages, with the key being the message's unique identifier (UID) and the value being an array with the keys:

Below is an example of a list containing a single example messages:

array(
    7556 => array(
        'uid' => 7556,
        'received' => '28 Jan 2010 19:45:07 -400',
        'size' => 1735,
        'date' => '28 Jan 2010 19:44:48 -400',
        'from' => '"John Smith" <john@example.com>',
        'subject' => 'Thanks for Signing Up!',
        'message_id' => '<788a8c8865e19b0c3243@example.com>',
        'to' => 'will@flourishlib.com, Joe <joe@example.com>',
        'in_reply_to' => '',
    )
)

listMessages() has two optional parameters, the $limit and $page which control how many and which messages should be retrieved.

// Get the first five messages
$messages = $mailbox->listMessages(5);

// Get messages 11 to 15
$messages = $mailbox->listMessage(5, 3);

Fetching

Individual messages can be retrieved in a parsed format by calling the method fetchMessage(). It requires a single parameter, $uid, which can be retrieved by the listMessages() method.

$message = $mailbox->fetchMessage(7556);

The return value is an associative array containing information about the message. The array will always include the following keys:

And one or more of the following keys:

All values in headers, text and body will have been decoded to UTF-8. Files in the attachment, inline and related array will all retain their original encodings.

Here is an example of a parsed message:

array(
    'received' => '28 Apr 2010 22:00:38 -0400',
    'headers'  => array(
		'received' => array(
			0 => '(qmail 25838 invoked from network); 28 Apr 2010 22:00:38 -0400',
			1 => 'from example.com (HELO ?192.168.10.2?) (example) by example.com with (DHE-RSA-AES256-SHA encrypted) SMTP; 28 Apr 2010 22:00:38 -0400'
		),
		'message-id' => '<4BD8E815.1050209@flourishlib.com>',
		'date' => 'Wed, 28 Apr 2010 21:59:49 -0400',
		'from' => array(
			'personal' => 'Will Bond',
			'mailbox'  => 'tests',
			'host'     => 'flourishlib.com'
		),
		'user-agent'   => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.9) Gecko/20100317 Thunderbird/3.0.4',
		'mime-version' => '1.0',
		'to' => array(
			0 => array(
				'mailbox' => 'tests',
				'host'    => 'flourishlib.com'
			)
		),
		'subject' => 'This message is encrypted'
    ),
    'text'      => 'This message is encrypted',
    'decrypted' => TRUE,
    'uid'       => 15
);

The second, optional boolean parameter $convert_newlines will convert all \r\n line-endings to \n inside of the text and html elements when set to TRUE.

// Convert all line-endings to \n for the text and html
$message = $mailbox->fetchMessage(7556, TRUE);

Deleting

One or more messages can be deleted at a time by calling deleteMessages() and passing a single UID, or an array of UIDs.

// Delete a single message
$mailbox->deleteMessages(7556);

// Delete multiple messages
$mailbox->deleteMessages(array(54, 928, 892));

Parsing

The static method parseMessage() can be used to parse a full MIME email message into the same format that fetchMessage() returns, minus the uid key.

$parsed_message = fMailbox::parseMessage(file_get_contents('/path/to/email'));

Like fetchMessage(), it also supports a second, optional boolean parameter $convert_newlines to change \r\n into \n for text and html.

S/MIME Decryption and Verification

One of the more advanced features of fMailbox is the ability to seamlessly handle S/MIME signed and encrypted messages. The static method addSMIMEPair() accepts an $email_address and either a single S/MIME $certificate_file path or that combined with an S/MIME $private_key_file path and $private_key_password.

// Adds the certificate and private key for will@flourishlib.com
fMailbox::addSMIMEPair('will@flourishlib.com', '/path/to/will.crt', '/path/to/will.key', 'password');

If the certificate is provided, messages coming from the $email_address and S/MIME signed will be checked for authenticity. Currently the class is configured to only verify messages with an explicitly added key, and will not use the certificate included in the signature.

If the private key and certificate are provided, any messages coming to the $email_address and S/MIME encrypted will be decrypted. The parsed message will contain the decrypted content in the appropriate text, html, attachment, etc. sections of the parsed message array.

If verification succeeds, the verified key will be set to TRUE in the parsed message array. If decryption is successful, the decrypted key will be set to TRUE. If either fail, or a certificate or key is not available for the specified email address, the S/MIME signature or encrypted message will appear as a file in the attachment element.

fMailbox API Reference

class, v1.0.0b18

Retrieves and deletes messages from a email account via IMAP or POP3

All headers, text and html content returned by this class are encoded in UTF-8. Please see UTF-8 for more information.

Changes:
1.0.0b18Fixed a bug in fetchMessageSource() where IMAP connections would add an extra \r\n to the end of the source 9/16/12
1.0.0b17Updated the class to be more forgiving when parsing the response for STATUS and FETCH IMAP commands 9/15/12
1.0.0b16Added method fetchMessageSource() 9/15/12
1.0.0b15Fixed handling of bounces with no headers 9/15/12
1.0.0b14Added a workaround for iconv having issues in MAMP 1.9.4+ 7/26/11
1.0.0b13Fixed handling of headers in relation to encoded-words being embedded inside of quoted strings 7/26/11
1.0.0b12Enhanced the error checking in write() 6/3/11
1.0.0b11Added code to work around PHP bug #42682 (http://bugs.php.net/bug.php?id=42682) where stream_select() doesn't work on 64bit machines from PHP 5.2.0 to 5.2.5, improved connectivity error handling and timeouts while reading data 1/10/11
1.0.0b10Fixed parseMessage() to properly handle a header format edge case and properly set the text and html keys even when the email has an explicit Content-disposition: inline header 11/25/10
1.0.0b9Fixed a bug in parseMessage() that could cause HTML alternate content to be included in the inline content array instead of the html element 9/20/10
1.0.0b8Fixed parseMessage() to be able to handle non-text/non-html multipart parts that do not have a Content-disposition header 9/18/10
1.0.0b7Fixed a typo in read() 9/7/10
1.0.0b6Fixed a typo from 1.0.0b4 7/21/10
1.0.0b5Fixes for increased compatibility with various IMAP and POP3 servers, hacked around a bug in PHP 5.3 on Windows 6/22/10
1.0.0b4Added code to handle emails without an explicit Content-type header 6/4/10
1.0.0b3Added missing static method callback constants 5/11/10
1.0.0b2Added the missing enableDebugging() 5/5/10
1.0.0bThe initial implementation 5/5/10

Static Methods

::addSMIMEPair() public

Adds an S/MIME certificate, or certificate + private key pair for verification and decryption of S/MIME messages

Signature

void addSMIMEPair( string $email_address, fFile|string $certificate_file, fFile $private_key_file=NULL, string $private_key_password=NULL )

Parameters
string $email_address The email address the certificate or private key is for
fFile|string $certificate_file The file the S/MIME certificate is stored in - required for verification and decryption
fFile $private_key_file The file the S/MIME private key is stored in - required for decryption only
string $private_key_password The password for the private key
::parseMessage() public

Parses a MIME message into an associative array of information

The output includes the following keys:

  • 'received': The date the message was received by the server
  • 'headers': An associative array of mail headers, the keys are the header names, in lowercase

And one or more of the following:

  • 'text': The plaintext body
  • 'html': The HTML body
  • 'attachment': An array of attachments, each containing:
    • 'filename': The name of the file
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'inline': An array of inline files, each containing:
    • 'filename': The name of the file
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'related': An associative array of related files, such as embedded images, with the key 'cid:{content-id}' and an array value containing:
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'verified': If the message contents were verified via an S/MIME certificate - if not verified the smime.p7s will be listed as an attachment
  • 'decrypted': If the message contents were decrypted via an S/MIME private key - if not decrypted the smime.p7m will be listed as an attachment

All values in headers, text and body will have been decoded to UTF-8. Files in the attachment, inline and related array will all retain their original encodings.

Signature

array parseMessage( string $message, boolean $convert_newlines=FALSE )

Parameters
string $message The full source of the email message
boolean $convert_newlines If \r\n should be converted to \n in the text and html parts the message
Returns

The parsed email message - see method description for details

::reset() internal public

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

Signature

void reset( )

Methods

->__construct() public

Configures the connection to the server

Please note that the GMail POP3 server does not act like other POP3 servers and the GMail IMAP server should be used instead. GMail POP3 only allows retrieving a message once - during future connections the email in question will no longer be available.

Signature

fMailbox __construct( string $type, string $host, string $username, string $password, integer $port=NULL, boolean $secure=FALSE, integer $timeout=NULL )

Parameters
string $type The type of mailbox, 'pop3' or 'imap'
string $host The server hostname or IP address
string $username The user to log in as
string $password The user's password
integer $port The port to connect via - only required if non-standard
boolean $secure If SSL should be used for the connection - this requires the openssl extension
integer $timeout The timeout to use when connecting
->__destruct() public

Disconnects from the server

Signature

void __destruct( )

->close() public

Closes the connection to the server

Signature

void close( )

->deleteMessages() public

Deletes one or more messages from the server

Passing more than one UID at a time is more efficient for IMAP mailboxes, whereas POP3 mailboxes will see no difference in performance.

Signature

void deleteMessages( integer|array $uid )

Parameters
integer|array $uid The UID(s) of the message(s) to delete
->enableDebugging() public

Sets if debug messages should be shown

Signature

void enableDebugging( boolean $flag )

Parameters
boolean $flag If debugging messages should be shown
->fetchMessage() public

Retrieves a single message from the server

The output includes the following keys:

  • 'uid': The UID of the message
  • 'received': The date the message was received by the server
  • 'headers': An associative array of mail headers, the keys are the header names, in lowercase

And one or more of the following:

  • 'text': The plaintext body
  • 'html': The HTML body
  • 'attachment': An array of attachments, each containing:
    • 'filename': The name of the file
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'inline': An array of inline files, each containing:
    • 'filename': The name of the file
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'related': An associative array of related files, such as embedded images, with the key 'cid:{content-id}' and an array value containing:
    • 'mimetype': The mimetype of the file
    • 'data': The raw contents of the file
  • 'verified': If the message contents were verified via an S/MIME certificate - if not verified the smime.p7s will be listed as an attachment
  • 'decrypted': If the message contents were decrypted via an S/MIME private key - if not decrypted the smime.p7m will be listed as an attachment

All values in headers, text and body will have been decoded to UTF-8. Files in the attachment, inline and related array will all retain their original encodings.

Signature

array fetchMessage( integer $uid, boolean $convert_newlines=FALSE )

Parameters
integer $uid The UID of the message to retrieve
boolean $convert_newlines If \r\n should be converted to \n in the text and html parts the message
Returns

The parsed email message - see method description for details

->fetchMessageSource() public

Retrieves the raw source of a single message from the server

This method is primarily useful for storing the raw source of a message. Normal use of fMailbox would involved calling fetchMessage(), which calls this method and then parseMessage().

Signature

string fetchMessageSource( integer $uid )

Parameters
integer $uid The UID of the message to retrieve
Returns

The raw message source of the email

->listMessages() public

Gets a list of messages from the server

The structure of the returned array is:

array(
    (integer) {uid} => array(
        'uid'         => (integer) {a unique identifier for this message on this server},
        'received'    => (string) {date message was received},
        'size'        => (integer) {size of message in bytes},
        'date'        => (string) {date message was sent},
        'from'        => (string) {the from header value},
        'subject'     => (string) {the message subject},
        'message_id'  => (string) {optional - the message-id header value, should be globally unique},
        'to'          => (string) {optional - the to header value},
        'in_reply_to' => (string) {optional - the in-reply-to header value}
    ), ...
)

All values will have been decoded to UTF-8.

Signature

array listMessages( integer $limit=NULL, integer $page=NULL )

Parameters
integer $limit The number of messages to retrieve
integer $page The page of messages to retrieve
Returns

A list of messages on the server - see method description for details

fMessaging

The fMessaging class is a simple session-based messaging tool that allows for messaging to be pulled out of your URLs and kept behind the scenes.

Creating Messages

The method create() will construct a message and save it for later retrieval. It accepts three parameters, the $name of the message, the $recipient and the $message itself. Here is an example of creating a message:

fMessaging::create('success', '/admin/users', 'The user was successfully created');

As you can see above, I used a URL as the recipient, however this is not a requirement. Any string can be used as a recipient, you will just need to use the same recipient when retrieving the message, as you will see below.

Retrieving Messages

Once a message has been created, it can then be retrieved. The action of retrieving a message deletes it from the session, thus messages are write-once/read-once. The retrieve() method requires two parameters, the $name of the message and the $recipient. Both parameters need to be the same as when the message was created.

If a message for the $name and $recipient combination has not been created, NULL will be returned instead.

Here is an example of retrieving a message and displaying it:

if ($success = fMessaging::retrieve('success', '/admin/users')) {
    echo $success;
}

Since calling retrieve() removes the message and prevents it from being retrieved on another page, sometimes it is more appropriate to call the method check(). check() will return a boolean, indicating if a message with the $name and $recipient currently exists:

if (fMessaging::check('success', '/admin/users')) {
    // ...
}

Displaying Messages

The show() method will display any message with the $name and $recipient specified. If a message exists, it be displayed in a p or div tag. By default, the $name of the message will be used as the CSS class for the HTML tag, however if a different class is desired, the optional third parameter $css_class can be specified.

show() will only print the output if the string is not empty, and will detect if the provided content contains any block-level HTML tags, automatically switching from printing a p tag to a div tag.

Here are a few examples:

// Example 1: Show the success message for /admin/users
fMessaging::show('success', '/admin/users');

// Example 2: Show the error message for /admin/users with the CSS class 'message'
fMessaging::show('error', '/admin/users', 'message');

And here is the output for the two calls to show() (whitespace added to the HTML for readability):

<!-- Example 1 -->
<p class="success">
    The user was successfully created!
</p>

<!-- Example 2 -->
<div class="message">
    <p>
        The following fields need a value:
    </p>
    <ul>
        <li>Name</li>
        <li>Email</li>
    </ul>
</div>

It is also possible to show multiple messages with a single call by passing an array of message names or '*' for all messages. Please note that using either of these options will disable the functionality of the third parameter, $css_class, and the message names will be used for the CSS classes.

// Show 'success' and 'error'
fMessaging::show(
    array('success', 'error'),
    '/admin/users'
);

// Show all messages for the recipient
fMessaging::show('*', '/admin/users');

Using No Recipient

It is possible to use all of the fMessaging methods without a recipient. To do this, simply leave out the recipient parameter. Please note that this means each method will have one less parameter, it does not mean that NULL should be passed in instead.

Here are the methods without a recipient:

fMessaging::create('success', 'The user was successfully created');
$success = fMessaging::retrieve('success');
if (fMessaging::check('success')) { }
fMessaging::show('success');

The only downside to not specifying a recipient is the possibility of overwriting messages in a multi-tab browser environment.

fMessaging API Reference

static class, v1.0.0b7

Provides session-based messaging for page-to-page communication

Changes:
1.0.0b7Fixed a small PHPDoc error 3/15/10
1.0.0b6Updated class to use new fSession API 10/23/09
1.0.0b5Made the $recipient parameter optional for all methods 7/8/09
1.0.0b4Added support for '*' and arrays of names to check() 6/2/09
1.0.0b3Updated class to use new fSession API 5/8/09
1.0.0b2Changed show() to accept more than one message name, or * for all messages 1/12/09
1.0.0bThe initial implementation 3/5/08

Static Methods

::check() public

Checks to see if a message exists of the name specified for the recipient specified

Signature

boolean check( string $name, string $recipient=NULL )

Parameters
string $name The name or array of names of the message(s) to check for, or '*' to check for any
string $recipient The intended recipient
Returns

If a message of the name and recipient specified exists

::create() public

Creates a message that is stored in the session and retrieved by another page

Signatures

void create( string $name, string $recipient, string $message )

void create( string $name, string $message )

Parameters
string $name A name for the message
string $recipient The intended recipient - this may be ommitted
string $message The message to send
::reset() internal public

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 data of the class

Signature

void reset( )

::retrieve() public

Retrieves and removes a message from the session

Signature

string retrieve( string $name, string $recipient=NULL )

Parameters
string $name The name of the message to retrieve
string $recipient The intended recipient
Returns

The message contents

::show() public

Retrieves a message, removes it from the session and prints it - will not print if no content

The message will be printed in a p tag if it does not contain any block level HTML, otherwise it will be printed in a div tag.

Signature

boolean show( mixed $name, string $recipient=NULL, string $css_class=NULL )

Parameters
mixed $name The name or array of names of the message(s) to show, or '*' to show all
string $recipient The intended recipient
string $css_class Overrides using the $name as the CSS class when displaying the message - only used if a single $name is specified
Returns

If one or more messages was shown

fMoney

The fMoney class is a value object for representing a monetary value. There is support for precise calculation, multiple currencies and formatting.

Currencies

Exchange Rates

By default the fMoney class comes with a single defined currency USD (United States Dollar). fMoney is built in such a way that multiple currencies can be used and converted between, however there is no built-in functionality to fetch exchange rate information. A few sources of free exchange rate information include:

The rest of the documentation will assume that if multiple currencies are being used that the work has been done to fetch the appropriate exchange rate information in a regular fashion.

Defining a Currency

Whenever a system must use a currency other than USD, the new currency must be defined by calling defineCurrency(). This method takes a total of five parameters that define all of the relevant information about a currency. The parameters are $iso_code, $name, $symbol, $precision and $value.

Here is an example of defining the currency Pound Sterling:

fMoney::defineCurrency(
    'GBP',            // The three digit ISO code
    'Pound Sterling', // The name of the currency
    '',              // The currency symbol
    '2',              // The precision after the decimal point
    '1.91865000'      // The current exchange rate with USD
);

The $value of a currency should be defined relative to USD, that is USD has a value of 1.00000000.

Once a currency has been defined, information about it can be retrieved by using the method getCurrencyInfo(). There is one required parameter, $iso_code, which indicates what currency should be returned. A second optional parameter, $element, allows selecting a single piece of information including:

It is also possible to get a list of all currencies by calling getCurrencies().

Default Currency

While not required, setting a default currency is often useful, especially if only a single currency is supported. By setting the default currency, it is no longer necessary to specify the currency code when creating new fMoney objects.

The method setDefaultCurrency() accepts a single parameter, the $iso_code for the desired default currency. Below is an example of setting the default to United States Dollar:

fMoney::setDefaultCurrency('USD');

The default currency can be retrieved by calling getDefaultCurrency().

Instantiation

Create an fMoney object requires either one or two parameters depending on whether or not a default currency has been set. If a default has not been set, or a currency other than the default is desired, an $amount and a $currency must be specified:

$price = new fMoney('12.25', 'USD');

When setting the amount of an fMoney object, a float value should never be used due to the inherit loss of precision when storing floating point values. Instead, always use an integer, or a floating point value in a string.

If a default currency has been set and the default currency is desired, only a single parameter, $amount is required:

fMoney::setDefaultCurrency('USD');

$twelve_dollars = new fMoney('12.00');
$five_dollars   = new fMoney('5.00');

Note that by default the currency symbol and all commas (,) will be removed from any monetary value before parsing it as a number. For details about how to customize this behaviour, please see the localization section.

Accessors

There are two accessors for the fMoney class, getAmount() and getCurrency() which return the amount of the value and the currency respectively.

echo $five_dollars->getAmount() . "\n";
echo $five_dollars->getCurrency();

will output the following:

5.00
USD

Comparison

Comparison of fMoney objects is accomplished by the method eq(), gt(), gte(), lt() and lte(). Each method will convert any non USD values to USD before comparison to ensure that comparisons are done correctly. Below is a table of the comparison methods:

Method Comparison
eq() If the two values are equal
gt() If the object being called is greater than the value or object passed
gte() If the object being called is greater than or equal to the value or object passed
lt() If the object being called is less than the value or object passed
lte() If the object being called is less than or equal to the value being passed

It is possible to pass values other than an fMoney object for comparison. These values will be converted to an fMoney object using the default currency if defined. If no default currency is defined, an exception will be thrown.

Here are a few examples:

if (!$twelve_dollars->eq($five_dollars)) {
    echo 'Twelve is not equal to five';
}

// Passing the string '5.00' only works because a default currency has been defined
if ($five_dollars->eq('5.00')) {
    echo 'Yes, five dollars is equal to five dollars';
}

if ($five_dollars->lte($twelve_dollars)) {
   echo 'Five dollars is less than or equal to twelve';
}

Modification

fMoney objects can be added, subtracted, multiplied and allocated (non-lossy division). All math operations are performed using an extra digit of precision and then the results are rounded using the common method. All math operations also take into account different currencies, with the result being in the currency of the object being called.

Please be sure to avoid floating point numbers in PHP when working with monetary values. Their inherent lack of precision make them a poor choice for precise calculations. Instead, use strings containing floating decimal values.

Addition

Addition is accomplished using the method add(). A single parameter, $addend, is required. The addend may be an fMoney object, or a string or integer if a default currency is defined.

$seventeen_dollars  = $five_dollars->add($twelve_dollars);
$six_dollars        = $five_dollars->add('1.00');

Subtraction

Subtraction is accomplished by the method sub(). A single parameter, $subtrahend, is required. The subtrahend may be an fMoney object, or a string or integer if a default currency is defined.

$seven_dollars = $twelve_dollars->sub($five_dollars);
$four_dollars  = $five_dollars->sub('1.00');

Multiplication

To multiply a monetary value, simply pass a string, integer, or fNumber multiplier to mul().

$fourty_eight_dollars    = $twelve_dollars->mul(4);
$five_dollars_five_cents = $five_dollars->mul('1.1');

$number = new fNumber('+5.5');
$twenty_seven_dollars_fifty_cents = $five_dollars->mul($number);

Allocation (Division)

Instead of providing a division method, which can easily lead to missing pennies, the fMoney class provides the method allocate(). This method splits up a monetary value into chunks that total the original value.

allocate() accepts two or more parameters, each being a string or fNumber fraction that represents the portion of the total each result should hold. The result is an array of fMoney objects with as many elements as parameters specified.

list($four_dollars, $one_dollar) = $five_dollars->allocate('0.8', '0.2');

The resulting monetary values will always add up to exactly the original value. This prevents money from being lost in calculations.

// All three thirds will be equal to four dollars
list($first_third, $second_third, $fourth_third) = $twelve_dollars->allocate('0.333', '0.333', '0.334');

Conversion

If you have defined at least one currency other than USD (such as we did with GBP in the Currencies section) you can convert monetary values between currencies on the fly. The method convert() requires a single parameter, the ISO code of the currency to convert to.

$usd_price = new fMoney('5.00');
$gbp_price = $usd_price->convert('GBP');
$usd_price = $gbp_price->convert('USD');

Formatting

Normally when displaying a monetary value it is desired to display the currency symbol and the value in a standard format with separators at the thousands, millions, etc. The method format() will perform such formatting.

echo $five_dollars->format() . "\n";

$one_thousand_two_hundred_dollars = new fMoney('1200.00');
echo $one_thousand_two_hundred_dollars->format() . "\n";

$five_pounds = new fMoney('5.00', 'GBP');
echo $five_pounds->format();

will output the following:

$5.00
$1,200.00
5.00

If the parameter $remove_zero_fraction is set to TRUE and the value has a fraction that is just zeros, the resulting output will not contain a decimal point or a fraction.

// This will print: $5
echo $five_dollars->format(TRUE);

$two_fifty_three = new fMoney('2.53');
// This will print: $2.53
echo $two_fifty_three->format(TRUE);

The method __toString() will return the value without the currency symbol or the thousands separators.

echo $five_dollars->__toString() . "\n";
echo $one_thousand_two_hundred_dollars->__toString() . "\n";
echo $five_pounds->__toString();

will output the following:

5.00
1200.00
5.00

Localization

When formatting monetary values in different locales, it will often be the case that the thousands separator and decimal point are different than the one in the United States. The methods registerFormatCallback() and registerUnformatCallback() allow for both creating a different formatting and also removing such formatting when creating a new fMoney object.

// Function to format monetary values for Italian
function italian_money_format(fNumber $value, $currency, $remove_zero_fraction=FALSE)
{
    $info = fMoney::getCurrencyInfo($currency);
    if ($remove_zero_fraction && $value->eq($value->trunc())) {
        $info['precision'] = 0;
    }
    return $info['symbol'] . number_format($value->__toString(), $info['precision'], ',', '.');
}

// Function to change a monetary value to a plain number
function italian_money_unformat($value, $currency)
{
    $symbol = fMoney::getCurrencyInfo($currency, 'symbol');
    return str_replace(
        array('.', ',', $symbol),
        array('', '.', ''),
        $value
    );
}

fMoney::registerFormatCallback('italian_money_format');
fMoney::registerUnformatCallback('italian_money_unformat');

fMoney API Reference

class, v1.0.0b3

Represents a monetary value - USD are supported by default and others can be added via defineCurrency()

Changes:
1.0.0b3Added the $remove_zero_fraction parameter to format() 6/9/10
1.0.0b2Fixed a bug with calling format() when a format callback is set, fixed NULL $element handling in getCurrencyInfo() 3/24/09
1.0.0bThe initial implementation 8/10/08

Static Methods

::defineCurrency() public

Allows adding a new currency, or modifying an existing one

Signature

void defineCurrency( string $iso_code, string $name, string $symbol, integer $precision, string $value )

Parameters
string $iso_code The ISO code (three letters, e.g. 'USD') for the currency
string $name The name of the currency
string $symbol The symbol for the currency
integer $precision The number of digits after the decimal separator to store
string $value The value of the currency relative to some common standard between all currencies
::getCurrencies() public

Lists all of the defined currencies

Signature

array getCurrencies( )

Returns

The 3 letter ISO codes for all of the defined currencies

::getCurrencyInfo() public

Allows retrieving information about a currency

Signature

mixed getCurrencyInfo( string $iso_code, string $element=NULL )

Parameters
string $iso_code The ISO code (three letters, e.g. 'USD') for the currency
string $element The element to retrieve: 'name', 'symbol', 'precision', 'value'
Returns

An associative array of the currency info, or the element specified

::getDefaultCurrency() public

Gets the default currency

Signature

string getDefaultCurrency( )

Returns

The ISO code of the default currency

::registerFormatCallback() public

Allows setting a callback to translate or modify any return values from format()

Signature

void registerFormatCallback( callback $callback )

Parameters
callback $callback The callback to pass all fNumber objects to. Should accept an fNumber object, a string currency abbreviation and a boolean indicating if a zero-fraction should be removed - it should return a formatted string.
::registerUnformatCallback() public

Allows setting a callback to clean any formatted values so they can be passed to fNumber

Signature

void registerUnformatCallback( callback $callback )

Parameters
callback $callback The callback to pass formatted strings to. Should accept a formatted string and a currency code and return a string suitable to passing to the fNumber constructor.
::reset() internal public

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

Signature

void reset( )

::setDefaultCurrency() public

Sets the default currency to use when creating fMoney objects

Signature

void setDefaultCurrency( string $iso_code )

Parameters
string $iso_code The ISO code (three letters, e.g. 'USD') for the new default currency

Methods

->__construct() public

Creates the monetary to represent, with an optional currency

Signature

fMoney __construct( fNumber|string $amount, string $currency=NULL )

Parameters
fNumber|string $amount The monetary value to represent, should never be a float since those are imprecise
string $currency The currency ISO code (three letters, e.g. 'USD') for this value
Throws
fValidationException
When $amount is not a valid number/monetary value
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Returns the monetary value without a currency symbol or thousand separator (e.g. 2000.12)

Signature

string __toString( )

Returns

The monetary value without currency symbol or thousands separator

->add() public

Adds the passed monetary value to the current one

Signature

fMoney add( fMoney|string|integer $addend )

Parameters
fMoney|string|integer $addend The money object to add - a string or integer will be converted to the default currency (if defined)
Returns

The sum of the monetary values in this currency

Throws
fValidationException
When $addend is not a valid number/monetary value
->allocate() public

Splits the current value into multiple parts ensuring that the sum of the results is exactly equal to this amount

This method takes two or more parameters. The parameters should each be fractions that when added together equal 1.

Signature

array allocate( fNumber|string $ratio1, fNumber|string $ratio2, fNumber|string ... )

Parameters
fNumber|string $ratio1 The ratio of the first amount to this amount
fNumber|string $ratio2 [, ... ] The ratio of the second amount to this amount
Returns

fMoney objects each with the appropriate ratio of the current amount

Throws
fValidationException
When one of the ratios is not a number
->convert() public

Converts this money amount to another currency

Signature

fMoney convert( string $new_currency )

Parameters
string $new_currency The ISO code (three letters, e.g. 'USD') for the new currency
Returns

A new fMoney object representing this amount in the new currency

->eq() public

Checks to see if two monetary values are equal

Signature

boolean eq( fMoney|string|integer $money )

Parameters
fMoney|string|integer $money The money object to compare to - a string or integer will be converted to the default currency (if defined)
Returns

If the monetary values are equal

Throws
fValidationException
When $money is not a valid number/monetary value
->format() public

Formats the amount by preceeding the amount with the currency symbol and adding thousands separators

Signature

string format( boolean $remove_zero_fraction=FALSE )

Parameters
boolean $remove_zero_fraction If TRUE and all digits after the decimal place are 0, the decimal place and all zeros are removed
Returns

The formatted (and possibly converted) value

->getAmount() public

Returns the fNumber object representing the amount

Signature

fNumber getAmount( )

Returns

The amount of this monetary value

->getCurrency() public

Returns the currency ISO code

Signature

string getCurrency( )

Returns

The currency ISO code (three letters, e.g. 'USD')

->gt() public

Checks to see if this value is greater than the one passed

Signature

boolean gt( fMoney|string|integer $money )

Parameters
fMoney|string|integer $money The money object to compare to - a string or integer will be converted to the default currency (if defined)
Returns

If this value is greater than the one passed

Throws
fValidationException
When $money is not a valid number/monetary value
->gte() public

Checks to see if this value is greater than or equal to the one passed

Signature

boolean gte( fMoney|string|integer $money )

Parameters
fMoney|string|integer $money The money object to compare to - a string or integer will be converted to the default currency (if defined)
Returns

If this value is greater than or equal to the one passed

Throws
fValidationException
When $money is not a valid number/monetary value
->lt() public

Checks to see if this value is less than the one passed

Signature

boolean lt( fMoney|string|integer $money )

Parameters
fMoney|string|integer $money The money object to compare to - a string or integer will be converted to the default currency (if defined)
Returns

If this value is less than the one passed

Throws
fValidationException
When $money is not a valid number/monetary value
->lte() public

Checks to see if this value is less than or equal to the one passed

Signature

boolean lte( fMoney|string|integer $money )

Parameters
fMoney|string|integer $money The money object to compare to - a string or integer will be converted to the default currency (if defined)
Returns

If this value is less than or equal to the one passed

Throws
fValidationException
When $money is not a valid number/monetary value
->mul() public

Mupltiplies this monetary value times the number passed

Signature

fMoney mul( fNumber|string|integer $multiplicand )

Parameters
fNumber|string|integer $multiplicand The number of times to multiply this ammount - don't use a float since they are imprecise
Returns

The product of the monetary value and the multiplicand passed

Throws
fValidationException
When $multiplicand is not a valid number
->sub() public

Subtracts the passed monetary value from the current one

Signature

fMoney sub( fMoney|string|integer $subtrahend )

Parameters
fMoney|string|integer $subtrahend The money object to subtract - a string or integer will be converted to the default currency (if defined)
Returns

The difference of the monetary values in this currency

Throws
fValidationException
When $subtrahend is not a valid number/monetary value

fNoRemainingException

fNoRemainingException is a sub-class of fExpectedException that indicates an iterator has reached the end, yet another element has been requested.

This space intentionally left blank

fNoRemainingException API Reference

class, v1.0.0b2

An exception caused when trying to get a value from an iterator and there is nothing left

Changes:
1.0.0b2Fixed a typo in the documentation 7/14/10
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fNoRemainingException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fNoRowsException

fNoRowsException a sub-class of fExpectedException that indicates a SQL query did not return any rows.

This space intentionally left blank

fNoRowsException API Reference

class, v1.0.0b

An exception when no rows are returned from a SQL query

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fNoRowsException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fNotFoundException

fNotFoundException is a sub-class of fExpectedException that indicates an item could not be found, such as when using object relational mapping code. This class may also be suitable to toss in other situations where code is unable to find a specified element or item.

This space intentionally left blank

fNotFoundException API Reference

class, v1.0.0b

An exception when an fActiveRecord is not found in the database

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fNotFoundException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fNumber

The fNumber class is a value object to represent large integers and arbitrary precision decimal values. The fNumber class is essential when dealing with calculations that must be precise, such as monetary value (consequently the fMoney class is built on top of fNumber). It supplies the same math functionality that is built into PHP, minus the trigonometric operations.

Instantiation

An fNumber object requires just a single parameter for the constructor, the value to represent. It can be a string, an object with a __toString() method, or an integer. If the value being represented is too large to store in an integer, the integer value should be enter via a string to prevent data loss. Also, float values should never be used since they have an inherent loss of precision that this class is designed to overcome.

$five           = new fNumber(5);
$four_point_two = new fNumber('4.2');
$zero_point_one = new fNumber('0.1');

There is a second optional parameter to the constructor that allows specifying the precision of the number. If no precision is specified, the precision of the value is used.

// Represents 1.000
$one = new fNumber(1, 3);

Arithmetic

Basic arithmetic is performed using the method add(), sub(), mul() and div(). Since the fNumber class is a value object, all operations return a new fNumber object instead of modifying the existing one. Also, all methods accept any valid number representation including strings, fNumber objects and integers.

The returned fNumber object will have the same precision as the object being called unless the optional parameter $scale is included.

Addition

Addition is performed by calling the method add() and providing an $addend.

// Addition using different addend number forms
$six              = $five->add(1);
$six              = $five->add($one);
$eight_point_four = $four_point_two->add('4.2');

// Without scale the 0.1 is lost
$five             = $five->add('0.1');

// By specifying a scale of 1 we retain the 0.1
$five_point_one   = $five->add('0.1', 1); 

Subtraction

Subtraction is performed by calling the method sub() and providing a $subtrahend.

// Basic subtraction
$two  = $five->sub(3);

// Since $five has a scale of zero, the result is still five
$five = $five->sub('0.3');

// All arithmetic methods support positive and negative numbers
$five_point_three = $five->sub('-0.3', 1);

Multiplication

Multiplication is performed by calling the method mul() and providing a $multiplicand.

// Simple multiplication
$ten = $five->mul(2);

// A comparison of multiplication with 0 and 1 scale
$twelve            = $five->mul('2.5');
$twelve_point_five = $five->mul('2.5', 1);

Division

Division is performed by calling the method div() and providing a $divisor.

// Basic division
$two = $five->div(2);

// Division with a specific scale
$two_point_five = $five->div(2, 1);

Modulus

The remainder of integer division can be calculated by the method mod() and the remainder of fractional division can be calculated by the method fmod().

The mod() method will convert the current number to an integer and then divide it by the single parameter, $divisor, that is passed to the method. The $divisor is also converted to an integer before the calculation is performed.

// Simple integer division
$one = $three->mod('2');

// The number and divisor are both converted to integers first
$one = $three_point_two->mod('2.1');

The fmod() method allows dividing a fractional number by another fractional number, returning the remainder. The first parameter is the $divisor to use, while a second optional parameter, $scale, allows specifying the scale of the remainder that is being returned.

$zero_point_three    = $four_point_seven->fmod('1.1');
$one_point_four_zero = $four_point_four->fmod('1.5', 2);

Powers and Roots

The fNumber class provides functionality to calculate both square roots and integer powers via the sqrt() and pow() methods.

The sqrt() method accepts a single optional parameter, the $scale of the result.

$three             = $nine->sqrt();
$three_point_eight = $fifteen->sqrt(1);

The pow() method accepts two parameters, the required $exponent to raise the number to, and the optional $scale for the resulting number.

$twenty_five        = $five->pow(2);
$nine_point_two_six = $two_point_one->pow(3, 2);

Under certain situations, especially when dealing with cryptography, it is necessary to raise integers to large powers and then calculate the remainder of the division of that product by another integer. Normal calculation by raising the original number to a large power and then dividing to find the remainder often takes far too much computation. There are, however, some mathematical shortcuts to make such calculations significantly faster.

The powmod() method allows calculating the remainder of the original number raised to an $exponent and then divided by the $modulus.

$four_fourty_five = $four->powmod('13', '497');

Comparison

The fNumber class include comparison methods for testing equality - eq(), less than - lt(), less than or equal - lte(), greater than - gt() and greater than or equal - gte(). Each method accepts two parameters, the $number to compare to and an optional $scale to use for comparison.

The $number to compare to may be any valid fNumber object, string or integer. The $scale parameter sets how many digits after the decimal point to use during comparison. If no scale is specified, the highest scale of the two numbers will be used.

$true  = $five->eq(5);
$false = $five->eq('5.1');
$true  = $five->eq('5.1', 0);

$true  = $five->lt($six);
$true  = $five->lte('5.00', 4);

$false = $five->gt($six, 0);
$false = $five->gte('5.1', 1);

Formatting

There are two options to format fNumber objects, either __toString() or format(). The format() method will include thousands separators in the returned value, while __toString() will not. The inherent scale of the number will be used when displaying the value. To change the scale, use the trunc() or round() method first.

echo $one_thousand_two_hundred->format() . "\n";
echo $one_thousand_two_hundred->__toString() . "\n";

echo $negative_five_point_two->format() . "\n";
echo $negative_five_point_two->__toString() . "\n";
echo $negative_five_point_two->trunc()->__toString();

would output the following

1,200
1200
-5.2
-5.2
-5

If the parameter $remove_zero_fraction is set to TRUE and the value has a fraction that is just zeros, the resulting output will not contain a decimal point or a fraction.

// This will print: 5
$five = new fNumber('5.0');
echo $five->format(TRUE);

$two_fifty_three = new fNumber('2.53');
// This will print: 2.53
echo $two_fifty_three->format(TRUE);

Precision Operations

The precision of an fNumber can be modified by using the ceil(), floor(), round() and trunc() methods.

The ceil() method performs a ceiling operation, rounding up to the next highest integer.

$six = $five_point_two->ceil();

The floor() method preforms a floor operation, rounding down to the next lowest integer.

$negative_six = $negative_five_point_two->floor();

The trunc() method changes the scale of the number to 0 without performing any rounding.

$five = $five_point_two->trunc();

The round() method allows rounding a number to a specified number of decimal places, using the $scale parameter. It is even possible to round left of the decimal point using negative scales. Rounding is done using the common method, that is when the digit one place beyond the $scale is 5 or greater the $scale digit is increased vy 1, otherwise the digit is left the same.

// Rounding positive numbers
$five = $five_point_two->round();
$six  = $five_point_five->round();

// Rounding negative numbers
$negative_two = $negative_one_point_six->round();
$negative_one = $negative_one_point_one->round();

// Rounding to a specific scale
$one_point_three = $one_point_three_three->round(1);
$ten             = $thirteen->round(-1);

Sign Operations

The fNumber class can calculate the absolute value of a number via the abs() method, the negated value via the neg() method and can return the sign of a number via the sign() method.

Here are a few basic example of using the abs() and neg() methods:

// Calculating the absolute value
$five = $five->abs();
$five = $negative_five->abs();

// Negating numbers
$negative_five = $five->neg();
$five          = $negative_five->neg();

The sign() method will return -1 if the number is negative, 0 if the number is zero and 1 if the number is positive.

$true  = $five->sign() == 1;
$true  = $zero->sign() == 0;
$false = $zero->sign() == -1;
$true  = $negative_one->sign() == -1;

Pi

The value of pi can be obtained up to a scale of 500 by calling the static method pi() and providing the desired $scale.

// 3.14
$pi = fNumber::pi(2);

// 3.1415926535897932384626433
$pi = fNumber::pi(25);

Base Conversion

For some calculations, representing numbers in a base other than base 10 is necessary. The static method baseConvert() allow converting integers between any two bases ranging from base 2 (binary) to base 16 (hexadecimal). Three parameters are required, the integer to convert, the base being converted from and the base being converted to.

echo fNumber::baseConvert($five, 10, 2) . "\n";
echo fNumber::baseConvert('10110100110', 2, 16) . "\n";
echo fNumber::baseConvert('10110100110', 2, 8);

would output the following

101
5A6
2646

Localization

When formatting numbers in different locales, it will often be the case that the thousands separator and decimal point are different than the one in the United States. The methods registerFormatCallback() and registerUnformatCallback() allow for both creating a different formatting and also removing such formatting when creating a new fNumber object.

// Function to format numbers for Italian
function italian_number_format($number, $remove_zero_fraction=FALSE)
{
    $parts = explode('.', $number);
    
    $integer  = $parts[0];
    $fraction = (isset($parts[1])) ? ',' . $parts[1] : '';
    
    if ($remove_zero_fraction && rtrim($fraction, ',0') === '') {
        $fraction = '';
    }
    
    return number_format($integer, 0, ',', '.') . $fraction;
}

// Function to change a formatted to a plain number
function italian_number_unformat($value)
{
    return str_replace(array('.', ','), array('', '.'), $value);
}

fNumber::registerFormatCallback('italian_number_format');
fNumber::registerUnformatCallback('italian_number_unformat');

fNumber API Reference

class, v1.0.0b3

Provides large/precise number support

Changes:
1.0.0b3Added the $remove_zero_fraction parameter to format() 2/2/11
1.0.0b2Fixed a bug with parsing decimal numbers in scientific notation 4/13/10
1.0.0bThe initial implementation 7/21/08

Static Methods

::baseConvert() public

Converts any positive integer between any two bases ranging from 2 to 16

Signature

string baseConvert( fNumber|string $number, integer $from_base, integer $to_base )

Parameters
fNumber|string $number The positive integer to convert
integer $from_base The base to convert from - must be between 2 and 16
integer $to_base The base to convert to - must be between 2 and 16
Returns

The number converted to the new base

::pi() public

Returns the value for pi with a scale of up to 500

Signature

fNumber pi( integer $scale )

Parameters
integer $scale The number of places after the decimal to return
Returns

Pi

::registerFormatCallback() public

Allows setting a callback to translate or modify any return values from format()

The callback should accept two parameters:

  • $value: the string value of the number
  • $remove_zero_fraction: a boolean indicating if a zero fraction should be removed

The callback should return a string, the formatted $value.

Signature

void registerFormatCallback( callback $callback )

Parameters
callback $callback The callback to pass the fNumber value to - see method description for parameters
::registerUnformatCallback() public

Allows setting a callback to clean any formatted values so they can be properly parsed - useful for languages where , is used as the decimal point

Signature

void registerUnformatCallback( callback $callback )

Parameters
callback $callback The callback to pass formatted strings to. Should accept a formatted string and return a string the is a valid number using . as the decimal point.
::reset() internal public

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

Signature

void reset( )

Methods

->__construct() public

Creates a large/precise number

Signature

fNumber __construct( string $value, integer $scale=NULL )

Parameters
string $value The value for the number - any valid PHP integer or float format including values with e exponents
integer $scale The number of digits after the decimal place, defaults to number of digits in $value
Throws
fValidationException
When $value is not a valid number
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Converts the object to an string

Signature

string __toString( )

->abs() public

Returns the absolute value of this number

Signature

fNumber abs( integer $scale=NULL )

Parameters
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The absolute number

->add() public

Adds two numbers together

Signature

fNumber add( fNumber|string $addend, integer $scale=NULL )

Parameters
fNumber|string $addend The addend
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The sum

Throws
fValidationException
When $addend is not a valid number
->ceil() public

Rounds the number to the next highest integer

Signature

fNumber ceil( )

Returns

The next highest integer

->div() public

Divides this number by the one passed

Signature

fNumber div( fNumber|string $divisor, integer $scale=NULL )

Parameters
fNumber|string $divisor The divisor
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The quotient

Throws
fValidationException
When $divisor is not a valid number
->eq() public

Indicates if this value is equal to the one passed

Signature

boolean eq( fNumber|string $number, integer $scale=NULL )

Parameters
fNumber|string $number The number to compare to
integer $scale The number of decimal places to compare - will use all available if not specified
Returns

If this number is equal to the one passed

Throws
fValidationException
When $number is not a valid number
->floor() public

Rounds the number to the next lowest integer

Signature

fNumber floor( )

Returns

The next lowest integer

->fmod() public

Returns the float remainder of dividing this number by the divisor provided

Signature

fNumber fmod( fNumber|string $divisor, integer $scale=NULL )

Parameters
fNumber|string $divisor The divisor
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The remainder

Throws
fValidationException
When $divisor is not a valid number
->format() public

Formats the number to include thousands separators

Signature

string format( boolean $remove_zero_fraction=FALSE )

Parameters
boolean $remove_zero_fraction If TRUE and all digits after the decimal place are 0, the decimal place and all zeros are removed
Returns

The formatted value

->getScale() public

Returns the scale of this number

Signature

integer getScale( )

Returns

The scale of this number

->gt() public

Indicates if this value is greater than the one passed

Signature

boolean gt( fNumber|string $number, integer $scale=NULL )

Parameters
fNumber|string $number The number to compare to
integer $scale The number of decimal places to compare - will use all available if not specified
Returns

If this number is less than the one passed

Throws
fValidationException
When $number is not a valid number
->gte() public

Indicates if this value is greater than or equal to the one passed

Signature

boolean gte( fNumber|string $number, integer $scale=NULL )

Parameters
fNumber|string $number The number to compare to
integer $scale The number of decimal places to compare - will use all available if not specified
Returns

If this number is greater than or equal to the one passed

Throws
fValidationException
When $number is not a valid number
->lt() public

Indicates if this value is less than the one passed

Signature

boolean lt( fNumber|string $number, integer $scale=NULL )

Parameters
fNumber|string $number The number to compare to
integer $scale The number of decimal places to compare - will use all available if not specified
Returns

If this number is less than the one passed

Throws
fValidationException
When $number is not a valid number
->lte() public

Indicates if this value is less than or equal to the one passed

Signature

boolean lte( fNumber|string $number, integer $scale=NULL )

Parameters
fNumber|string $number The number to compare to
integer $scale The number of decimal places to compare - will use all available if not specified
Returns

If this number is less than or equal to the one passed

Throws
fValidationException
When $number is not a valid number
->mod() public

Returns the remainder of dividing this number by the divisor provided. All floats are converted to integers.

Signature

fNumber mod( fNumber|string $divisor )

Parameters
fNumber|string $divisor The divisor - will be converted to an integer if it is a float
Returns

The remainder

Throws
fValidationException
When $divisor is not a valid number
->mul() public

Multiplies two numbers

Signature

fNumber mul( fNumber|string $multiplier, integer $scale=NULL )

Parameters
fNumber|string $multiplier The multiplier
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The product

Throws
fValidationException
When $multiplier is not a valid number
->neg() public

Negates this number

Signature

fNumber neg( integer $scale=NULL )

Parameters
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The negated number

->pow() public

Raise this number to the power specified

Signature

fNumber pow( integer $exponent, integer $scale=NULL )

Parameters
integer $exponent The power to raise to - all non integer values will be truncated to integers
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The product

Throws
fValidationException
When $exponent is not a valid number
->powmod() public

Gets the remainder of this integer number raised to the integer $exponent, divided by the integer $modulus

This method is faster than doing $num->pow($exponent)->mod($modulus) and is primarily useful for cryptographic functionality.

Signature

fNumber powmod( fNumber|string $exponent, fNumber|string $modulus )

Parameters
fNumber|string $exponent The power to raise to - all non integer values will be truncated to integers
fNumber|string $modulus The value to divide by - all non integer values will be truncated to integers
Returns

The remainder

Throws
fValidationException
When $exponent or $modulus is not a valid number
->round() public

Rounds this number to the specified number of digits after the decimal - negative scales round the number by places to the left of the decimal

Signature

fNumber round( integer $scale )

Parameters
integer $scale The number of places after (or before if negative) the decimal to round to
Returns

The rounded result

->sign() public

Returns the sign of the number

Signature

integer sign( )

Returns

-1 if negative, 0 if zero, 1 if positive

->sqrt() public

Returns the square root of this number

Signature

fNumber sqrt( integer $scale=NULL )

Parameters
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The square root

->sub() public

Subtracts two numbers

Signature

fNumber sub( fNumber|string $subtrahend, integer $scale=NULL )

Parameters
fNumber|string $subtrahend The subtrahend
integer $scale The number of places after the decimal - overrides the scale for this number
Returns

The difference

Throws
fValidationException
When $subtrahend is not a valid number
->trunc() public

Scales (truncates or expands) the number to the specified number of digits after the decimal - negative scales round the number by places to the left of the decimal

Signature

fNumber trunc( integer $scale )

Parameters
integer $scale The number of places after (or before if negative) the decimal
Returns

The square root

fORM

The fORM class is a static class that implements core ORM functionality for much of the Flourish ORM. It provides means to configure and extend fActiveRecord classes.

Mapping Classes to Tables

By default, when mapping fActiveRecord classes to database tables, an UpperCamelCase singular class name will be mapped the the underscore_notation plural database table of the same noun. For example, the User class is mapped to the users table. The static method mapClassToTable() allows overriding this default by passing the $class and $table in.

The following example would set the User class to map to the user table instead of users. This code should be executed during site-wide configuration and should not be placed inside of the configure() method for a class that extends fActiveRecord.

// 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 writing custom ORM code, the class that is associated with a table can be determined by calling the static method classize() with the parameter $table.

$class = fORM::classize($table);
return new $class();

To translate from the class to the database table simply pass the class to the static method tablize(). The class can be either a class name or an instance of the class.

$object = new User();
$table  = fORM::tablize($object);

Tables in Other Schemas

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 mapClassToTable() should be called with first parameter, the $class to map and the second parameter, $table, should be in the format schema.table.

This code should be executed during site-wide configuration and should not be placed inside of the configure() method for a class that extends fActiveRecord.

// This maps the User class to the users table in the authorization schema
fORM::mapClassToTable('User', 'authorization.users');

Multiple Databases

When multiple databases are configured via fORMDatabase, classes can model tables on the non-default database by calling the method 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 mapClassToTable(), this code should be executed during site-wide configuration and should not be placed inside of the configure() method for a class that extends fActiveRecord. 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.

Column and Record Names

Whenever class and column names are used in messaging, such as in fValidationException, the class or column name is run through fGrammar::humanize() to create a human-friendly version. Obviously in some situations this technique will not get capitalization or punctuation correct. The static methods overrideRecordName() and overrideColumnName() allow setting custom names for classes and columns respectively. It is also possible to set a class name in the context of being a related classsee the fORMRelated class documentation for more details.

The example below shows changing the column email to display as E-Mail instead of Email and the FaqEntry class to display as FAQ Entry instead of Faq Entry.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORM::overrideColumnName($this, 'email', 'E-Mail');
    }
}

class FaqEntry extends fActiveRecord
{
    protected function configure()
    {
        fORM::overrideRecordName($this, 'FAQ Entry');
    }
}

If you are having issues with your column names not being properly converted from CamelCase (for methods) to underscore_notation (for your database and HTML), please see the Fixing Notation Conversion Issues section on the fGrammar page.

Schema Caching

Since the schema information is dynamically pulled out of the database, this can add at least a few database calls to each request that is processed. If the database schema is not changing on a regular basis and better performance is required, the schema can be cached by calling the static method enableSchemaCaching().

enableSchemaCaching() accepts a single parameter, an fCache object to cache the schema information to. This enables caching on the fDatabase, fSQLTranslation and fSchema objects that are used for the ORM.

An additional feature is that the cached schema information will be cleared if an fUnexpectedException is thrown. This would normally happen if a programmer tried to perform an action that was invalid based on the cached schema.

fORM::enableSchemaCaching(new fCache('file', '/file/path/to/cache/file'));

Extending the ORM

The Flourish ORM is built in such a way that it can be easily extended without having to actually extends individual classes. Both the fActiveRecord and fRecordSet classes allow registering callbacks to handle methods that dont exist (and thus fall through to the __call magic method) while in addition, the fActiveRecord class includes a number of predefined "hooks" that allow for injecting functionality using callbacks. There is further functionality that allows defining callbacks to handle the tasks of translating objects to scalar values, scalar values to objects and method reflection.

A large part of the ORM classes built into Flourish use these features to implement their functionality:

The two static methods registerActiveRecordMethod() and registerRecordSetMethod() allow for setting callbacks to handle method calls for methods that dont exist in the fActiveRecord and fRecordSet classes respectively. The static method registerHookCallback() allows setting a hook to be executed at one of the pre-defined hooks in fActiveRecord.

Once a callback has been registered to handle a method call or hook, it will be automatically called at the appropriate time and will be passed the pre-defined parameters listed below. The actual work of calling the callback and passing the parameters is handled by the fActiveRecord and fRecordSet classes so all that the end-developer needs to worry about is the callback parameter signature and the functionality in the callback.

Adding Methods to fActiveRecord

If you wish to add a method to a single fActiveRecord class, simply create the method inside of that class. The following functionality is for the purpose of dynamically adding methods to fActiveRecord at run time. This technique is used to create ORM plugins, such as fORMFile, fORMOrdering, etc.

registerActiveRecordMethod() accepts the $class and $method to register for and the $callback to register. The $class can also be '*' to register the callback for all fActiveRecord classes. The $callback should be a callback for a method or function that accepts the following parameters:

The following example registers the method toXML():

class User extends fActiveRecord
{
    protected function configure()
    {
        fORM::registerActiveRecordMethod($this, 'toXML', 'User::convertToXML');
    }

    public function convertToXML($object, &$value, &$old_values, &$related_records, &$cache, $method_name, $parameters)
    {
        // 
    }
}

$user = new User();
echo $user->toXML();

Adding Methods to fRecordSet

registerRecordSetMethod() accepts the $method to register for and the $callback to register. The $callback should be a callback for a method or function that accepts the following parameters:

The following example adds a method named toXML() to all fRecordSet objects:

class ORMXML
{
    public function extend()
    {
        fORM::registerRecordSetMethod('toXML', 'ORMXML::convertToXML');
    }

    public function convertToXML($object, $class, &$records, $method_name, $parameters)
    {
        // 
    }
}

ORMXML::extend();

$users = fRecordSet::build('User');
echo $users->toXML();

Adding Functionality to fActiveRecord

Rather than requiring all additional functionality for fActiveRecord classes to be defined in each class or requiring that methods be overridden in order to add functionality, the static method registerHookCallback() allows callbacks to be registered that will be executed a predefined places. These hooks make it possible to write plugins for the ORM that can be easily reused.

registerHookCallback() accepts three parameters, the $class and $hook to register for and the $callback to register. The $class can be either a class name or '*' to register for all fActiveRecord classes. The $hook should be one of the hooks listed below:

Hook Location
'post::__construct()' At the very end of __construct()
'pre::delete()' At the very beginning delete()
'post-begin::delete()' After the database and filesystem transactions have been started
'pre-commit::delete()' Just before the database and filesystem transactions are committed
'post-commit::delete()' After the database and filesystem transactions have been committed
'post-rollback::delete()' When an error occurs, right after the database and filesystem transactions are rolled back
'post::delete()' At the very end of delete()
'post::loadFromIdentityMap()' Right after a record is attached to the identity map, is not triggered if loaded from a result
'post::loadFromResult()' Right after a record is loaded from the database, is not triggered if loaded from the identity map
'pre::populate()' At the very beginning of populate()
'post::populate()' At the very end of populate()
'pre::replicate()' At the very beginning of replicate()/clone, on the original record
'post::replicate()' At the very end of replicate()/clone, on the original record
'cloned::replicate()' At the very end of replicate(), on the newly cloned record
'pre::store()' At the very beginning of store()
'post-begin::store()' After the database and filesystem transactions have been started
'post-validate::store()' After validation successfully completes
'pre-commit::store()' Just before the database and filesystem transactions are committed
'post-commit::store()' After the database and filesystem transactions have been committed
'post-rollback::store()' When an error occurs, right after the database and filesystem transactions are rolled back
'post::store()' At the end of store(), just before the existence is changed, thus $record->exists() will still return FALSE for a new record
'pre::validate()' Before any of the built-in validation is done, the $validation_messages array will be empty
'post::validate()' After all of the built-in validation is done, the $validation_messages array will contain all of the messages, however the messages ordering is done after this hook

The $callback specified should have the following signature:

The two hooks, 'pre::validate()' and 'post::validate()' accept one extra parameter:

The three hooks, 'pre::replicate()', 'post::replicate()' and 'cloned::replicate()' accept one extra parameter:

Custom Validation Using a Hook

Below is an example of extending a User class to confirm that the password confirmation is identical to the password when using populate:

class User extends fActiveRecord
{
    protected function configure()
    {
        fORM::registerHookCallback($this, 'post::validate()', 'User::validatePassword');
    }

    static public function validatePassword($object, &$values, &$old_values, &$related_records, &$cache, &$validation_messages)
    {
        // If a new password was set, it came through the request and does not match the field password confirmation, add an error message
        if (fActiveRecord::hasOld($old_values, 'password') && fRequest::get('password') && fRequest::get('password') != fRequest::get('password_confirmation')) {
            $validation_messages['password'] = 'Password: The value entered does not match Password Confirmation';
        }     
    }
}

fActiveRecord Array Structures

When writing callbacks for adding methods or functionality to fActiveRecord, most often there will be a need to work with the $values, $old_values, $related_records and $cache arrays.

Each of these arrays is implemented in such a way that all instances of an fActiveRecord class that represent the same record will share the arrays. If a change is made to the values for one instance of User with the ID 1, all other instance of User 1 will also see those changes.

It is also important to note that all callbacks registered for fActiveRecord method calls and hooks should accept these arrays by reference, otherwise any changes to the arrays will be lost.

$values

The $values array is an associative array of the current values for a record. Each column in the database is a key in the array and points to the current value for that column. Below is an example of what the $values array would look like for a simple User record with a hashed password:

Array
(
    [user_id] => 1
    [first_name] => Will
    [last_name] => Bond
    [email] => will@flourishlib.com
    [password] => fCryptography::password_hash#Gu19bpZN94#ac74c4ad9ed7103e051e583af86599b95237e9af
)

The best practice for assigning new values to the $values array is to use the static method assign() since it will automatically move the old value into the $old_values array.

$old_values

The $old_values array is an associative array of every previous value contained by each of the columns in the record since it was last loaded from the database. The original value will be at key 0, and further revisions will be appended to the array.

The keys in the array are the database column names, however a column will only be present as a key if a value in the record has changed. The value associated with each key is an array of all of the old values. Below is an example of the $old_values array for a User object that has had the first name change twice and the email changed once.

Array
(
    [first_name] => Array
        (
            [0] => William
            [1] => will
        )
  
    [email] => Array
        (
            [0] => will@flourishlib.com
        )
  
)

Records that are new and have not been stored in the database will have all values set to NULL, thus the $old_values array for a new User record that has had each field set once will look like the following:

Array
(
    [user_id] => Array
        (
            [0] => {null}
        )
  
    [first_name] => Array
        (
            [0] => {null}
        )
  
    [last_name] => Array
        (
            [0] => {null}
        )
  
    [email] => Array
        (
            [0] => {null}
        )
  
    [password] => Array
        (
            [0] => {null}
        )
  
)

There are a few fActiveRecord static methods that make working with the $old_values array a little easier. changed() will return a boolean indicating if the value of a column has actually changedFALSE will be returned if there is an old value and the old and current values match. hasOld() returns a boolean indicating if there is an old value for a column and will return TRUE even if the old and current values are the same. retrieveOld() will return either the oldest value for a column, or an array of all old values depending on what parameters are passed.

The $related_records associative array contains a cache of all related records that have been pulled out of the database. This array helps prevent lots of duplicate database queries from being executed.

The structure of the array is as follows:

Array
(
    [$related_table] => Array
        (
            [$route] => Array
                (
                    [record_set] => fRecordSet,
                    [primary_keys] => array(),
                    [associate] => boolean, 
                    [count] => integer
                )
        
        )
  
)

The array is only populated as the related records are requested. The $related_table is the database table corresponding to the related record class. The $route is name of the relationship route between the table for the current class and the $related_table.

The 'record_set' key (which will not be present if the record has only been counted, or if only the primary keys have been accessed) will point to an fRecordSet object. The 'primary_keys' key will point to an array of the primary keys for the related records, but will only be present if a link method has been called. The 'count' key (which will always be present) will point to an integer containing the number of related records. The 'associate' key points to a boolean indicating if the related records should be stored when the parent records store() method is executed.

In general, the $related_records array should not be manipulated directly, and may cause custom code to be more fragile in the face of future Flourish internal code updates. Instead, try to use the various static methods on the fORMRelated class. For normal end-developer use, almost all of the fORMRelated functionality is exposed through the fActiveRecord related records operations.

$cache

The $cache array is an array implemented for use by end-developers or ORM plugins. The structure is completely up to the discretion of the programmer. This array can be useful for temporarily storing data, such as an unhashed password for the purposes of mailing to user, or for caching an expensive calculation.

$validation_messages

The $validation_messages array keys are generated via the following rules. Whenever the array is modified, special care should be taken to add new entries properly. The fActiveRecord::validate() documentation has examples of each type of entry in the array.

Dynamic fActiveRecord Classes

While not a feature that should normally be used in a production environment, the static method defineActiveRecordClass() will automatically create an fActiveRecord class for a class that properly maps to a database table. By placing this method call in an __autoload function, it is possible to start working with the ORM without having to create a class for each database table.

function __autoload($class)
{
    $file = '/path/to/class/files/' . $class . '.php';
    
    if (file_exists($file)) {
        include($file);
        return;
    }

    try {
        fORM::defineActiveRecordClass($class);
    } catch (fProgrammerException $e) {
        fCore::toss('fProgrammerException', sprintf('The class %s could not be found', $class));
    }
}

fORM API Reference

static class, v1.0.0b28

Dynamically handles many centralized object-relational mapping tasks

Changes:
1.0.0b28Updated getColumnName() and getRecordName() to use fText if loaded 2/2/11
1.0.0b27Added links to the detailed documentation for the parameters passed to hooks 11/27/10
1.0.0b26Added getRelatedClass() for handling related classes in PHP 5.3 namespaces 11/17/10
1.0.0b25Added support for PHP 5.3 namespaced fActiveRecord classes 11/11/10
1.0.0b24Backwards Compatibility Break - Callbacks registered via registerRecordSetMethod() should now accept the $method_name in the position where the $pointer parameter used to be passed 9/28/10
1.0.0b23Added the 'pre::replicate()', 'post::replicate()' and 'cloned::replicate()' hooks 9/7/10
1.0.0b22Internal Backwards Compatibility Break - changed parseMethod() to not underscorize the subject of the method 8/6/10
1.0.0b21Fixed some documentation to reflect the API changes from v1.0.0b9 7/14/10
1.0.0b20Added the ability to register a wildcard active record method for all classes 4/22/10
1.0.0b19Added the method isClassMappedToTable() 3/30/10
1.0.0b18Added the post::loadFromIdentityMap() hook 3/14/10
1.0.0b17Changed enableSchemaCaching() to rely on fDatabaseclearCache() instead of explicitly calling fSQLTranslation::clearCache() 3/9/10
1.0.0b16Backwards Compatibility Break - renamed addCustomClassToTableMapping() to mapClassToTable(). Added getDatabaseName() and mapClassToDatabase(). Updated code for new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b15Added support for fActiveRecord to getRecordName() 10/6/09
1.0.0b14Updated documentation for registerActiveRecordMethod() to include info about prefix method matches 8/7/09
1.0.0b13Updated documentation for registerRecordSetMethod() 7/14/09
1.0.0b12Updated callReflectCallbacks() to accept a class name instead of an object 7/13/09
1.0.0b11Added registerInspectCallback() and callInspectCallbacks() 7/13/09
1.0.0b10Fixed a bug with objectify() caching during NULL date/time/timestamp values and breaking further objectification 6/18/09
1.0.0b9Added caching for performance and changed some method APIs to only allow class names instead of instances 6/15/09
1.0.0b8Updated documentation to reflect removal of $associate parameter for callbacks passed to registerRecordSetMethod() 6/2/09
1.0.0b7Added enableSchemaCaching() to replace fORMSchema::enableSmartCaching() 5/4/09
1.0.0b6Added the ability to pass a class instance to addCustomClassTableMapping() 2/23/09
1.0.0b5Backwards compatibility break - renamed addCustomTableClassMapping() to addCustomClassTableMapping() and swapped the parameters 1/26/09
1.0.0b4Fixed a bug with retrieving fActiveRecord methods registered for all classes 1/14/09
1.0.0b3Fixed a static method callback constant 12/17/08
1.0.0b2Added replicate() and registerReplicateCallback() for fActiveRecord::replicate() 12/12/08
1.0.0bThe initial implementation 8/4/07

Static Methods

::callHookCallbacks() internal public

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

Calls the hook callbacks for the class and hook specified

Signature

void callHookCallbacks( fActiveRecord $object, string $hook, array &$values, array &$old_values, array &$related_records, array &$cache, mixed &$parameter=NULL )

Parameters
fActiveRecord $object The instance of the class to call the hook for
string $hook The hook to call
array &$values The current values of the record
array &$old_values The old values of the record
array &$related_records Records related to the current record
array &$cache The cache array of the record
mixed &$parameter The parameter to send the callback
::callInspectCallbacks() internal public

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

Calls all inspect callbacks for the class and column specified

Signature

void callInspectCallbacks( string $class, string $column, array &$metadata )

Parameters
string $class The class to inspect the column of
string $column The column to inspect
array &$metadata The associative array of data about the column
::callReflectCallbacks() internal public

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

Calls all reflect callbacks for the class passed

Signature

void callReflectCallbacks( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to call the callbacks for
array &$signatures The associative array of {method_name} => {signature}
boolean $include_doc_comments If the doc comments should be included in the signature
::checkHookCallback() internal public

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 any (or a specific) callback has been registered for a specific hook

Signature

boolean checkHookCallback( string $class, string $hook, array $callback=NULL )

Parameters
string $class The name of the class
string $hook The hook to check
array $callback The specific callback to check for
Returns

If the specified callback exists

::classize() public

Takes a table and turns it into a class name - uses custom mapping if set

Signature

string classize( string $table )

Parameters
string $table The table name
Returns

The class name

::defineActiveRecordClass() public

Will dynamically create an fActiveRecord-based class for a database table

Normally this would be called from an __autoload() function.

This method will only create classes for tables in the default ORM database.

Signature

void defineActiveRecordClass( string $class )

Parameters
string $class The name of the class to create
::enableSchemaCaching() public

Enables caching on the fDatabase, fSQLTranslation and fSchema objects used for the ORM

This method will cache database schema information to the three objects that use it during normal ORM operation: fDatabase, fSQLTranslation and fSchema. To allow for schema changes without having to manually clear the cache, all cached information will be cleared if any fUnexpectedException objects are thrown.

This method should be called right after fORMDatabase::attach().

Signature

void enableSchemaCaching( fCache $cache, string $database_name='default', string $key_token=NULL )

Parameters
fCache $cache The object to cache schema information to
string $database_name The database to enable caching for
string $key_token This is a token that is used in cache keys to prevent conflicts for server-wide caches - when non-NULL the document root is used
::getActiveRecordMethod() internal public

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

Returns a matching callback for the class and method specified

The callback returned will be determined by the following logic:

1. If an exact callback has been defined for the method, it will be returned 2. If a callback in the form {prefix}* has been defined that matches the method, it will be returned 3. NULL will be returned

Signature

string|null getActiveRecordMethod( string $class, string $method )

Parameters
string $class The name of the class
string $method The method to get the callback for
Returns

The callback for the method or NULL if none exists - see method description for details

::getClass() internal public

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 class name or class and returns the class name

Signature

string getClass( mixed $class )

Parameters
mixed $class The object to get the name of, or possibly a string already containing the class
Returns

The class name

::getColumnName() internal public

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

Returns the column name

The default column name is the result of calling fGrammar::humanize() on the column.

Signature

string getColumnName( string $class, string $column )

Parameters
string $class The class name the column is part of
string $column The database column
Returns

The column name for the column specified

::getDatabaseName() internal public

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

Returns the name for the database used by the class specified

Signature

string getDatabaseName( string $class )

Parameters
string $class The class name to get the database name for
Returns

The name of the database to use

::getRecordName() internal public

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

Returns the record name for a class

The default record name is the result of calling fGrammar::humanize() on the class.

Signature

string getRecordName( string $class )

Parameters
string $class The class name to get the record name of
Returns

The record name for the class specified

::getRecordSetMethod() internal public

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

Returns a matching callback for the method specified

The callback returned will be determined by the following logic:

1. If an exact callback has been defined for the method, it will be returned 2. If a callback in the form {action}* has been defined that matches the method, it will be returned 3. NULL will be returned

Signature

string|null getRecordSetMethod( string $method )

Parameters
string $method The method to get the callback for
Returns

The callback for the method or NULL if none exists - see method description for details

::getRelatedClass() internal public

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 class name and related class name and ensures the related class has the appropriate namespace prefix

Signature

string getRelatedClass( string $class, string $related_class )

Parameters
string $class The primary class
string $related_class The related class name
Returns

The related class name, with the appropriate namespace prefix

::isClassMappedToTable() internal public

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 if a class has been mapped to a table

Signature

boolean isClassMappedToTable( mixed $class )

Parameters
mixed $class The name of the class
Returns

If the class has been mapped to a table

::mapClassToDatabase() public

Sets a class to use a database other than the "default"

Multiple database objects can be attached for the ORM by passing a unique $name to the attach() method.

Signature

void mapClassToDatabase( mixed $class, string $database_name )

Parameters
mixed $class The name of the class, or an instance of it
string $database_name The name given to the database when passed to attach()
::mapClassToTable() public

Allows non-standard class to table mapping

By default, all database tables are assumed to be plural nouns in underscore_notation and all class names are assumed to be singular nouns in UpperCamelCase. This method allows arbitrary class to table mapping.

Signature

void mapClassToTable( mixed $class, string $table )

Parameters
mixed $class The name of the class, or an instance of it
string $table The name of the database table
::objectify() internal public

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 scalar value and turns it into an object if applicable

Signature

mixed objectify( string $class, string $column, mixed $value )

Parameters
string $class The class name of the class the column is part of
string $column The database column
mixed $value The value to possibly objectify
Returns

The scalar or object version of the value, depending on the column type and column options

::overrideColumnName() public

Allows overriding of default column names

By default a column name is the result of fGrammar::humanize() called on the column.

Signature

void overrideColumnName( mixed $class, string $column, string $column_name )

Parameters
mixed $class The class name or instance of the class the column is located in
string $column The database column
string $column_name The name for the column
::overrideRecordName() public

Allows overriding of default record names

By default a record name is the result of fGrammar::humanize() called on the class.

Signature

void overrideRecordName( mixed $class, string $record_name )

Parameters
mixed $class The class name or instance of the class to override the name of
string $record_name The human version of the record
::parseMethod() internal public

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

Parses a camelCase method name for an action and subject in the form actionSubject()

Signature

array parseMethod( string $method )

Parameters
string $method The method name to parse
Returns

An array of 0 => {action}, 1 => {subject}

::registerActiveRecordMethod() public

Registers a callback for an fActiveRecord method that falls through to fActiveRecord::__call() or hits a predefined method hook

The callback should accept the following parameters:

  • $object: The fActiveRecord instance
  • &$values: The values array for the record
  • &$old_values: The old values array for the record
  • &$related_records: The related records array for the record
  • &$cache: The cache array for the record
  • $method_name: The method that was called
  • &$parameters: The parameters passed to the method
Signature

void registerActiveRecordMethod( mixed $class, string $method, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for, '*' will register for all classes
string $method The method to hook for - this can be a complete method name or {prefix}* where * will match any column name
callback $callback The callback to execute - see method description for parameter list
::registerHookCallback() public

Registers a callback for one of the various fActiveRecord hooks - multiple callbacks can be registered for each hook

The method signature should include the follow parameters:

  • $object: The fActiveRecord instance
  • &$values: The values array for the record - see the $values documentation for details
  • &$old_values: The old values array for the record - see the $old_values documentation for details
  • &$related_records: The related records array for the record - see the $related_records documentation for details
  • &$cache: The cache array for the record - see the $cache documentation for details

The 'pre::validate()' and 'post::validate()' hooks have an extra parameter:

The 'pre::replicate()', 'post::replicate()' and 'cloned::replicate()' hooks have an extra parameter:

  • $replication_level: An integer representing the level of recursion - the object being replicated will be 0, children will be 1, grandchildren 2 and so on.

Below is a list of all valid hooks:

  • 'post::__construct()'
  • 'pre::delete()'
  • 'post-begin::delete()'
  • 'pre-commit::delete()'
  • 'post-commit::delete()'
  • 'post-rollback::delete()'
  • 'post::delete()'
  • 'post::loadFromIdentityMap()'
  • 'post::loadFromResult()'
  • 'pre::populate()'
  • 'post::populate()'
  • 'pre::replicate()'
  • 'post::replicate()'
  • 'cloned::replicate()'
  • 'pre::store()'
  • 'post-begin::store()'
  • 'post-validate::store()'
  • 'pre-commit::store()'
  • 'post-commit::store()'
  • 'post-rollback::store()'
  • 'post::store()'
  • 'pre::validate()'
  • 'post::validate()'
Signature

void registerHookCallback( mixed $class, string $hook, callback $callback )

Parameters
mixed $class The class name or instance of the class to hook, '*' will hook all classes
string $hook The hook to register for
callback $callback The callback to register - see the method description for details about the method signature
::registerInspectCallback() public

Registers a callback to modify the results of fActiveRecord::inspect() methods

Signature

void registerInspectCallback( mixed $class, string $column, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for
string $column The column to register for
callback $callback The callback to register. Callback should accept a single parameter by reference, an associative array of the various metadata about a column.
::registerObjectifyCallback() public

Registers a callback for when objectify() is called on a specific column

Signature

void registerObjectifyCallback( mixed $class, string $column, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for
string $column The column to register for
callback $callback The callback to register. Callback should accept a single parameter, the value to objectify and should return the objectified value.
::registerRecordSetMethod() public

Registers a callback for an fRecordSet method that fall through to fRecordSet::__call()

The callback should accept the following parameters:

  • $object: The actual record set
  • $class: The class of each record
  • &$records: The ordered array of fActiveRecord objects
  • $method_name: The method name that was called
  • $parameters: Any parameters passed to the method
Signature

void registerRecordSetMethod( string $method, callback $callback )

Parameters
string $method The method to hook for
callback $callback The callback to execute - see method description for parameter list
::registerReflectCallback() public

Registers a callback to modify the results of fActiveRecord::reflect()

Callbacks registered here can override default method signatures and add method signatures, however any methods that are defined in the actual class will override these signatures.

The callback should accept three parameters:

  • $class: the class name
  • &$signatures: an associative array of {method_name} => {signature}
  • $include_doc_comments: a boolean indicating if the signature should include the doc comment for the method, or just the signature
Signature

void registerReflectCallback( mixed $class, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for, '*' will register for all classes
callback $callback The callback to register. Callback should accept a three parameters - see method description for details.
::registerReplicateCallback() public

Registers a callback for when a value is replicated for a specific column

Signature

void registerReplicateCallback( mixed $class, string $column, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for
string $column The column to register for
callback $callback The callback to register. Callback should accept a single parameter, the value to replicate and should return the replicated value.
::registerScalarizeCallback() public

Registers a callback for when scalarize() is called on a specific column

Signature

void registerScalarizeCallback( mixed $class, string $column, callback $callback )

Parameters
mixed $class The class name or instance of the class to register for
string $column The column to register for
callback $callback The callback to register. Callback should accept a single parameter, the value to scalarize and should return the scalarized value.
::replicate() internal public

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 and value and returns a copy is scalar or a clone if an object

The registerReplicateCallback() allows for custom replication code

Signature

mixed replicate( string $class, string $column, mixed $value )

Parameters
string $class The class the column is part of
string $column The database column
mixed $value The value to copy/clone
Returns

The copied/cloned value

::reset() internal public

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

Signature

void reset( )

::scalarize() internal public

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

If the value passed is an object, calls __toString() on it

Signature

mixed scalarize( mixed $class, string $column, mixed $value )

Parameters
mixed $class The class name or instance of the class the column is part of
string $column The database column
mixed $value The value to get the scalar value of
Returns

The scalar value of the value

::tablize() public

Takes a class name (or class) and turns it into a table name - Uses custom mapping if set

Signature

string tablize( string $class )

Parameters
string $class The class name
Returns

The table name

fORMColumn

The fORMColumn class is an ORM plugin to provide miscellaneous additional functionality to individual columns.

Email Columns

The static method configureEmailColumn() sets a column to be validated as an email address. The first parameter is the $class the column is located in and the second parameter is the $column. As with most active record plugins, this method should normally be called from the configure() method.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMColumn::configureEmailColumn($this, 'email');
    }
}

Link Columns

Columns can be configured as a link column by passing the $class and $column to the static method fORMColumn::configureLinkColumn(). When the record is validated, the link will be checked to make sure it contains a valid domain name, however http:// is not required. When calling the prepare method for the column, it will ensure that the link begings with http:// for output into a HTML <a> tag.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMColumn::configureLinkColumn($this, 'link');
    }
}

$news_article = new NewsArticle();
$news_article->setLink('www.example.com');

echo $news_article->getLink();
echo $news_article->prepareLink();

The above PHP would output the following:

www.example.com
http://www.example.com

Number Columns

While fActiveRecord automatically handles both integer and floating point numbers, sometimes it is necessary to have a columns value be represented as an fNumber object. The static method configureNumberColumn() ensures that values loaded out of the database are automatically converted to an fNumber object. The first parameter is the $class and the second parameter is the $column.

class Product extends fActiveRecord
{
    protected function configure()
    {
        fORMColumn::configureNumberColumn($this, 'weight');
    }
}

Random Columns

In some situations a random string is useful for providing security when resetting passwords, validating email accounts or establishing access authentication. The static method configureRandomColumn() will cause a column to be set to a random string when the record is first saved and will allow generating a new random string at any time by calling generateColumnName().

configureRandomColumn() accepts four parameters, the $class to be configured, the $column, the $type of random string (see fCryptography::randomString() for options) and the $length of the string.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMColumn::configureRandomColumn($this, 'access_code', 'alphanumeric', 16);
    }
}

// Create a new user and get their access code
$user = new User();
$user->store();
$access_code = $user->getAccessCode();

// Generate a new random string for the access code
$user->generateAccessCode();
$access_code = $user->getAccessCode();

fORMColumn API Reference

static class, v1.0.0b15

Provides special column functionality for fActiveRecord classes

Changes:
1.0.0b15Fixed a bug with empty string email values passing through required validation 7/29/11
1.0.0b14Updated code to work with the new fORM API 8/6/10
1.0.0b13Fixed reflect() to include some missing parameters 6/8/10
1.0.0b12Changed validation messages array to use column name keys 5/26/10
1.0.0b11Fixed a bug with prepareLinkColumn() returning http:// for empty link columns and not adding http:// to links that contained a /, but did not start with it 3/16/10
1.0.0b10Fixed reflect() to specify the value returned from set and generate methods, changed generate() methods to return the newly generated string 3/15/10
1.0.0b9Changed email columns to be automatically trimmed if they are a value email address surrounded by whitespace 3/14/10
1.0.0b8Made the validation on link columns a bit more strict 3/9/10
1.0.0b7Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b6Changed SQL statements to use value placeholders, identifier escaping and schema support 10/22/09
1.0.0b5Updated to use new fORM::registerInspectCallback() method 7/13/09
1.0.0b4Updated code for new fORM API 6/15/09
1.0.0b3Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b2Fixed a bug with objectifying number columns 11/24/08
1.0.0bThe initial implementation 5/27/08

Static Methods

::configureEmailColumn() public

Sets a column to be formatted as an email address

Signature

void configureEmailColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class to set the column format
string $column The column to format as an email address
::configureLinkColumn() public

Sets a column to be formatted as a link

Signature

void configureLinkColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class to set the column format
string $column The column to format as a link
::configureNumberColumn() public

Sets a column to be returned as an fNumber object from calls to get{ColumnName}()

Signature

void configureNumberColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class to set the column format
string $column The column to return as an fNumber object
::configureRandomColumn() public

Sets a column to be a random string column - a random string will be generated when the record is saved

Signature

void configureRandomColumn( mixed $class, string $column, string $type, integer $length )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as a random column
string $type The type of random string, must be one of: 'alphanumeric', 'alpha', 'numeric', 'hexadecimal'
integer $length The length of the random string
::encodeNumberColumn() internal public

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

Encodes a number column by calling fNumber::__toString()

Signature

string encodeNumberColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The encoded number

::generate() internal public

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

Generates a new random value for the column

Signature

string generate( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The newly generated random value

::inspect() internal public

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

Adds metadata about features added by this class

Signature

void inspect( string $class, string $column, array &$metadata )

Parameters
string $class The class being inspected
string $column The column being inspected
array &$metadata The array of metadata about a column
::objectifyNumber() internal public

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

Turns a numeric value into an fNumber object

Signature

mixed objectifyNumber( string $class, string $column, mixed $value )

Parameters
string $class The class this value is for
string $column The column the value is in
mixed $value The value
Returns

The fNumber object or raw value

::prepareLinkColumn() internal public

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

Prepares a link column so that the link will work properly in an a tag

Signature

string prepareLinkColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The formatted link

::prepareNumberColumn() internal public

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

Prepares a number column by calling fNumber::format()

Signature

string prepareNumberColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The formatted link

::reflect() internal public

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

Adjusts the fActiveRecord::reflect() signatures of columns that have been configured in this class

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect
array &$signatures The associative array of {method name} => {signature}
boolean $include_doc_comments If doc comments should be included with the signature
::reset() internal public

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

Signature

void reset( )

::setEmailColumn() internal public

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 the value for an email column, trimming the value if it is a valid email

Signature

fActiveRecord setEmailColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::setRandomStrings() internal public

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 the appropriate column values to a random string if the object is new

Signature

string setRandomStrings( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
Returns

The formatted link

::validateEmailColumns() internal public

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

Validates all email columns

Signature

void validateEmailColumns( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages An array of ordered validation messages
::validateLinkColumns() internal public

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

Validates all link columns

Signature

void validateLinkColumns( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages An array of ordered validation messages

fORMDatabase

The fORMDatabase class provides database-related functionality to the Flourish ORM. End-developers will use the class to attach fDatabase objects for the rest of ORM.

fDatabase Registry

Since an ORM by definition maps objects to a database, it is obvious that an instance of the fDatabase class will be needed for all database operations. To make an instance of fDatabase available to all of the ORM classes, pass it to the static method attach().

fORMDatabase::attach(new fDatabase('postgresql', 'database', 'username', 'password'));

When writing code for the ORM that requires use of the database class, the static method retrieve() will return the instance set with attach().

$result = fORMDatabase::retrieve()->translatedQuery("SELECT * FROM users");

Multiple Database Support

Multiple databases are support by the Flourish ORM, both in master-slave and vertical partitioning setups.

Vertical Partitioning

Vertical partitioning is when different tables are split up over multiple databases or servers. For instance, perhaps all users and permissions are stored on one database server and all orders and products are stored on another.

In this type of a setup, it is possible to attach more than one databases for the ORM by passing a second parameter, $name, to the static method attach().

// Attach the users db as the default
fORMDatabase::attach($users_db);
// Attach the ecommerce db with the name "ecommerce_db"
fORMDatabase::attach($ecommerce_db, 'ecommerce_db');

Now individual classes can be mapped to the database by calling fORM::mapClassToDatabase().

fORM::mapClassToDatabase('User', 'ecommerce_db');

Master-Slave Setups

When a database cluster is set up to use a master-slave setup, one database will be designated the master and one or more as slaves. Flourish allows for such a setup with attach() by specifying the third parameter, $role. The $role parameter defaults to both, which means the server will be used for reading and writing. To attach the master server, set the $role to write. For the slave, set the $role to read.

// Attach the master for writing and a slave for reading
fORMDatabase::attach($master_db, 'default', 'write');
fORMDatabase::attach($random_slave_db, 'default', 'read');

It is also possible to combine vertical partitioning with master-slave setups by specifying a $name for the database other than default.

As implied by the slave database variable $random_slave_db in the code example, attach() will not accept multiple databases for the read role and random pick one. For setups where there is more than one slave, code must be manually written to select a slave and provide it to the attach() method.

fORMDatabase API Reference

static class, v1.0.0b32

Holds a single instance of the fDatabase class and provides database manipulation functionality for ORM code

Changes:
1.0.0b32Added support to addWhereClause() for the ^~ and $~ operators 6/20/11
1.0.0b31Fixed a bug with addWhereClause() generating invalid SQL 5/10/11
1.0.0b30Fixed insertFromAndGroupByClauses() to insert MAX() around columns in related tables in the ORDER BY clause when a GROUP BY is used 2/3/11
1.0.0b29Added code to handle old PCRE engines that don't support unicode character properties 12/6/10
1.0.0b28Fixed a bug in the fProgrammerException that is thrown when an improperly formatted OR condition is provided 11/24/10
1.0.0b27Fixed addWhereClause() to ignore fuzzy search clauses with no values to match 10/19/10
1.0.0b26Fixed insertFromAndGroupByClauses() to handle SQL where a table is references in more than one capitalization 7/26/10
1.0.0b25Fixed insertFromAndGroupByClauses() to properly handle recursive relationships 7/22/10
1.0.0b24Fixed parseSearchTerms() to work with non-ascii terms 6/30/10
1.0.0b23Fixed error messages in retrieve() 4/23/10
1.0.0b22Added support for IBM DB2, fixed an issue with building record sets or records that have recursive relationships 4/13/10
1.0.0b21Changed injectFromAndGroupByClauses() to be able to handle table aliases that contain other aliases inside of them 3/3/10
1.0.0b20Fixed a bug where joining to a table two separate ways could cause table alias issues and incorrect SQL to be generated 12/16/09
1.0.0b19Added the ability to compare columns with the =:, !:, <:, <=:, >: and >=: operators 12/8/09
1.0.0b18Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b17Added support for multiple databases 10/28/09
1.0.0b16Internal Backwards Compatibility Break - Renamed methods and significantly changed parameters and functionality for SQL statements to use value placeholders, identifier escaping and to handle schemas 10/22/09
1.0.0b15Streamlined intersection operator SQL and added support for the second value being NULL 9/21/09
1.0.0b14Added support for the intersection operator >< to createWhereClause() 7/13/09
1.0.0b13Added support for the AND LIKE operator &~ to createWhereClause() 7/9/09
1.0.0b12Added support for the NOT LIKE operator !~ to createWhereClause() 7/8/09
1.0.0b11Added support for concatenated columns to escapeBySchema() 6/19/09
1.0.0b10Updated createWhereClause() to properly handle NULLs for arrays of values when doing = and != comparisons 6/17/09
1.0.0b9Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b8Fixed a bug with creatingWhereClause() where a null value would not be escaped property 5/12/09
1.0.0b7Fixed a bug where an OR condition in createWhereClause() could not have one of the values be an array 4/22/09
1.0.0b6insertFromAndGroupByClauses() will no longer wrap ungrouped columns if in a CAST or CASE statement for ORDER BY clauses of queries with a GROUP BY clause 3/23/09
1.0.0b5Fixed parseSearchTerms() to include stop words when they are the only thing in the search string 12/31/08
1.0.0b4Fixed a bug where loading a related record in the same table through a one-to-many relationship caused recursion 12/24/08
1.0.0b3Fixed a bug from 1.0.0b2 12/5/08
1.0.0b2Added support for != and <> to createWhereClause() and createHavingClause() 12/4/08
1.0.0bThe initial implementation 8/4/07

Static Methods

::addHavingClause() internal public

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 HAVING clause from an array of conditions

Signature

array addHavingClause( fDatabase $db, fSchema $schema, array $params, string $table, array $conditions )

Parameters
fDatabase $db The database the query will be executed on
fSchema $schema The schema for the database
array $params The params for the fDatabase::query() call
string $table The table the query is being executed on
array $conditions The array of conditions - see fRecordSet::build() for format
Returns

The params with the HAVING clause added

::addOrderByClause() internal public

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

Adds an ORDER BY clause to an array of params for an fDatabase::query() call

Signature

array addOrderByClause( fDatabase $db, fSchema $schema, array $params, string $table, array $order_bys )

Parameters
fDatabase $db The database the query will be executed on
fSchema $schema The schema object for the database the query will be executed on
array $params The parameters for the fDatabase::query() call
string $table The table any ambigious column references will refer to
array $order_bys The array of order bys to use - see fRecordSet::build() for format
Returns

The params with a SQL ORDER BY clause added

::addPrimaryKeyWhereParams() internal public

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

Add the appropriate SQL and params for a WHERE clause condition for primary keys of the table specified

Signature

array addPrimaryKeyWhereParams( fSchema $schema, array $params, string $table, string $table_alias, array &$values, array &$old_values )

Parameters
fSchema $schema The schema for the database the query will be run on
array $params The currently constructed params for fDatabase::query() - the first param should be a SQL statement
string $table The table to build the where clause for
string $table_alias The alias for the table
array &$values The values array for the fActiveRecord object
array &$old_values The old values array for the fActiveRecord object
Returns

The params to pass to fDatabase::query(), including the new primary key where condition

::addWhereClause() internal public

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

Adds a WHERE clause, from an array of conditions, to the parameters for an fDatabase::query() call

Signature

array addWhereClause( fDatabase $db, fSchema $schema, array $params, string $table, array $conditions )

Parameters
fDatabase $db The database the query will be executed on
fSchema $schema The schema for the database
array $params The parameters for the fDatabase::query() call
string $table The table any ambigious column references will refer to
array $conditions The array of conditions - see fRecordSet::build() for format
Returns

The params with the SQL WHERE clause added

::attach() public

Allows attaching an fDatabase-compatible objects for by ORM code

If a $name other than default is used, any fActiveRecord classes that should use it will need to be configured by passing the class name and $name to mapClassToDatabase(). The $name parameter should be unique per database or database master/slave setup.

The $role is used by code to allow for master/slave database setups. There can only be one database object attached for either of the roles, 'read' or 'write'. If the role 'both' is specified, it will be applied to both the 'read' and 'write' roles. Any sort of logic for picking one out of multiple databases should be done before this method is called.

Signature

void attach( fDatabase $database, string $name='default', string $role='both' )

Parameters
fDatabase $database An object that is compatible with fDatabase
string $name The name for the database instance
string $role If the database should be used for 'read', 'write' or 'both' operations
::injectFromAndGroupByClauses() internal public

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

Finds all of the table names in the SQL and creates the appropriate FROM and GROUP BY clauses with all necessary joins

The SQL string should contain two placeholders, :from_clause and :group_by_clause, although the later may be omitted if necessary. All columns should be qualified with their full table name.

Here is an example SQL string to pass in presumming that the tables users and groups are in a relationship:

SELECT users.* FROM :from_clause WHERE groups.group_id = 5 :group_by_clause ORDER BY lower(users.first_name) ASC
Signature

array injectFromAndGroupByClauses( fDatabase $db, fSchema $schema, array $params, string $table )

Parameters
fDatabase $db The database the query is to be executed on
fSchema $schema The schema for the database
array $params The parameters for the fDatabase::query() call
string $table The main table to be queried
Returns

The params with the SQL FROM and GROUP BY clauses injected

::makeCondition() internal public

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

Makes a condition for a SQL statement out of fDatabase::escape() placeholders

Signature

string makeCondition( fSchema $schema, string $table, string $column, string $comparison_operator, mixed $value )

Parameters
fSchema $schema The schema object for the database the query will be executed on
string $table The table to create the condition for
string $column The column to make the condition for
string $comparison_operator The comparison operator for the condition
mixed $value The value for the condition, which allows the $comparison_operator to be tweaked for NULL values
Returns

A SQL condition using fDatabase::escape() placeholders

::parseSearchTerms() internal public

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

Parses a search string into search terms, supports quoted phrases and removes extra punctuation

Signature

void parseSearchTerms( string $terms, boolean $ignore_stop_words=FALSE )

Parameters
string $terms A text string from a form input to parse into search terms
boolean $ignore_stop_words If stop words should be ignored, this setting will be ignored if all words are stop words
::reset() internal public

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

Signature

void reset( )

::retrieve() public

Return the instance of the fDatabase class

Signature

fDatabase retrieve( string $class='fActiveRecord', string $role='either' )

Parameters
string $class The class to retrieve the database for - if not specified, the default database will be returned
string $role If the database will be used for 'write', 'read' or 'either' operations
Returns

The database instance

::splitHavingConditions() internal public

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

Removed aggregate function calls from where conditions array and puts them in a having conditions array

Signature

array splitHavingConditions( array &$where_conditions )

Parameters
array &$where_conditions The where conditions to look through for aggregate functions
Returns

The conditions to be put in a HAVING clause

fORMDate

The fORMDate class is an ORM plugin to provide additional functionality for date and time columns.

Date Created Columns

The static method configureDateCreatedColumn() sets an active record class to automatically set a date, time or timestamp column to the date/time when the record is first saved in the database.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMDate::configureDateCreatedColumn($this, 'date_created');
    }
}

Date Updated Columns

The static method configureDateUpdatedColumn() sets an active record class to automatically set a date, time or timestamp column to the current date/time each time the record is saved in the database.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMDate::configureDateUpdatedColumn($this, 'last_edited');
    }
}

Timezone Columns

Since not all supported databases support timezone information in timestamp columns, the fORMDate class allows associating a timestamp column with another column to store the timezone name. The static method configureTimezoneColumn() accepts three parameters, the $class, the $timestamp_column and the $timezone_column.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMDate::configureTimezoneColumn($this, 'date_posted', 'timezone_posted');
    }
}

The timezone column will be used to store the timezone stored in any fTimestamp objects that are set to the timestamp column. If a new timestamp is set, it will be combined with the existing timezone into a new fTimestamp object. The object will be stored in the timestamp column. If a new timezone is set, it will be combined with the existing timestamp and the new fTimestamp object will be stored in the timestamp column.

fORMDate API Reference

static class, v1.0.0b9

Provides additional date/time functionality for fActiveRecord classes

Changes:
1.0.0b9Updated code to work with the new fORM API 8/6/10
1.0.0b8Changed validation messages array to use column name keys 5/26/10
1.0.0b7Fixed the set methods to return the record object in order to be consistent with all other set methods 3/15/10
1.0.0b6Fixed an issue with calling a non-existent method on fTimestamp instances 11/3/09
1.0.0b5Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b4Fixed setting up the inspect callback in configureTimezoneColumn() 10/11/09
1.0.0b3Updated to use new fORM::registerInspectCallback() method 7/13/09
1.0.0b2Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0bThe initial implementation 9/5/08

Static Methods

::configureDateCreatedColumn() public

Sets a column to be a date created column

When a new record is stored in the database, date created columns will be filled with the timestamp of the store operation.

Signature

void configureDateCreatedColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as a date created column
::configureDateUpdatedColumn() public

Sets a column to be a date updated column

Whenever a record is stored in the database, a date updated column will be set to the timestamp of the operation.

Signature

void configureDateUpdatedColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as a date updated column
::configureTimezoneColumn() public

Sets a timestamp column to store the timezone in another column

Since not all databases support timezone information in timestamp columns, this method allows storing the timezone in another columns. When the timestamp and timezone are retrieved from the database, they will be automatically combined together into an fTimestamp object.

Signature

void configureTimezoneColumn( mixed $class, string $timestamp_column, string $timezone_column )

Parameters
mixed $class The class name or instance of the class to set the column format
string $timestamp_column The timestamp column to store the timezone for
string $timezone_column The column to store the timezone in
::inspect() internal public

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

Adds metadata about features added by this class

Signature

void inspect( string $class, string $column, array &$metadata )

Parameters
string $class The class being inspected
string $column The column being inspected
array &$metadata The array of metadata about a column
::makeTimestampObjects() internal public

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 fTimestamp objects for every timestamp/timezone combination in the object

Signature

void makeTimestampObjects( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::objectifyTimestampWithTimezone() internal public

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

Turns a timestamp value into an fTimestamp object with a timezone specified by another column

Signature

void objectifyTimestampWithTimezone( array &$values, array &$old_values, string $timestamp_column, string $timezone_column )

Parameters
array &$values The current values
array &$old_values The old values
string $timestamp_column The column holding the timestamp
string $timezone_column The column holding the timezone
::reset() internal public

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

Signature

void reset( )

::setDateCreated() internal public

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 the appropriate column values to the date the object was created (for new records)

Signature

void setDateCreated( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::setDateUpdated() internal public

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 the appropriate column values to the date the object was updated

Signature

void setDateUpdated( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::setTimestampColumn() internal public

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 the timestamp column and then tries to objectify it with an related timezone column

Signature

fActiveRecord setTimestampColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::setTimezoneColumn() internal public

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 the timezone column and then tries to objectify the related timestamp column

Signature

fActiveRecord setTimezoneColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::validateTimezoneColumns() internal public

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

Validates all timestamp/timezone columns

Signature

void validateTimezoneColumns( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages An array of ordered validation messages

fORMFile

The fORMFile class is an ORM plugin to provide file and file upload handling to fActiveRecord classes.

File Upload Columns

Any varchar, char or text field can be configured to act as a file upload column by calling the static method configureFileUploadColumn(). The method takes three parameters, the $class, the $column and the $directory to store file in. The $directory can be a string file path or an fDirectory object.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureFileUploadColumn(
            $this,
            'attachment',
            '/path/to/attachment/upload/dir'
        );
    }
}

Once a file has been configured as a file upload column, the fActiveRecord::populate() method will automatically move any files uploaded in a field with the same name as the column. It is also possible to upload a file for the column separately by calling the upload method for a column.

$user = new User();
if (fRequest::isPost()) {
    // Populate column values from the request and
    // upload files into file upload columns
    $user->populate();

    // Upload just the attachment
    $user->uploadAttachment();
}

The set method for the column will also accept a file path or fFile object and make a copy of the file in the directory specified for the column. When a record is deleted, the file associated with it will be deleted from the directory.

// This file will be copied into the directory defined
// in the fORMFile::configureFileUploadColumn() call
$user->setAttachment('/path/to/a/file.txt');

The get method for a file upload column will return the fFile object of the file, while the prepare and encode methods will return just the filename. If the parameter TRUE is passed to the prepare method, the web server path to the file will be returnedsee fFilesystem::addWebPathTranslation() for details about how filesystem paths are mapped to web server paths.

// The get method will return an fFile or fImage object
switch ($user->getAttachment()->getMimeType()) {
    case 'image/png':
    // 
}

// This will output the filename safe for HTML
echo $user->encodeAttachment();

// This will return the web path to the file
echo $user->prepareAttachment(TRUE);

HTML Form Setup

In order to upload a file through an HTML form the enctype attribute must be set to multipart/form-data. In order for an fActiveRecord class to properly detect a file and move it to the columns directory, the file upload input must have the same name as the column.

The fORMFile plugin provides functionality where an uploaded file can be remembered even if a validation error happens and a file can be deleted through the use of a checkbox. The existing file hidden input field should be named existing-column_name and the delete checkbox should be named delete-column_name. Since the delete field is a checkbox, it is recommended to put a hidden field right before it with a value of 0 to ensure the field is always submitted.

<form action="" method="post" enctype="multipart/form-data">
    <fieldset>
        <legend>Add a News Article</legend>
        ...
        <p>
            <label for="news_article-attachment">Attachment</label>
            <input id="news_article-attachment" type="file" name="attachment" />
            <?
            if ($news_article->getAttachment()) {
                ?>
                <input type="hidden" name="existing-attachment" value="<?php echo $news_article->encodeAttachment() ?>" />
                <input type="hidden" name="delete-attachment" value="0" />
                Existing file: <a href="<?php echo $news_article->prepareAttachment(TRUE) ?>"><?php echo $news_article->prepareAttachment() ?></a>
                <label for="news_article-delete-attachment">Delete existing attachment</label>
                <input type="checkbox" id="news_article-delete-attachment" name="delete-attachment" value="1" />
                <?
            }
            ?>
        </p>
        ...
    </fieldset>
</form>

Adding fUpload Options

Since the fUpload class is used to move and validate files that are uploaded, it is possible to configure the fORMFile plugin to call methods on the fUpload object used to move a file. The static method addFUploadMethodCall() requires four parameters, the $class being configured, the $column to apply the option to, the $method to call and an array of the $parameters to pass to the method.

Below is an example of restricting the file upload size using both client-side restrictions and server-side restrictions. The client-side MAX_FILE_SIZE input field prevents the browser from wasting time sending a file that is too large, while the server-side setMaxSize() will stop field that are too large and are sent by clients that dont send the MAX_FILE_SIZE field. Please note that the MAX_FILE_SIZE must be less than both the upload_max_filesize and post_max_size ini settings.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureFileUploadColumn($this, 'attachment', '/path/to/attachment/upload/dir');
        fORMFile::addFUploadMethodCall($this, 'attachment', 'setMaxSize', array('2mb'));
    }
}
<form action="" method="post" enctype="multipart/form-data">
    <fieldset>
        <legend>Add a News Article</legend>
        ...
        <p>
            <input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
            <label for="news_article-attachment">Attachment</label>
            <input id="news_article-attachment" type="file" name="attachment" />
            ...
        </p>
        ...
    </fieldset>
</form>

The only restriction for setting fUpload method calls is that the method fUpload::enableOverwrite() is not available since it could lead to two records referencing the same file, which creates issues when one record is deleted.

Image Upload Columns

It is also possible to configure an image upload column, which allows for automatic image processing and automatic rejection of non-image files. The static method configureImageUploadColumn() accepts four parameters, the $class to configure, the $column to store the image name in, the $directory to store the files in.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureImageUploadColumn($this, 'photo', '/path/to/photo/upload/dir');
    }
}

Configuring an image upload column will restrict the uploaded files to the following mime types:

In addition to checking the mime type supplied by the browser, a server-side check is done to ensure the image type reported by the browser is correct.

Adding fImage Operations

When working with an image upload column, it is possible to add any number of fImage operations to be executed on the image before it is saved in the upload directory. The static method addFImageMethodCall() accepts four parameters, the $class being configured, the $column to add the call to, the fImage $method to call and the $parameters to pass to it. The operations will be executed in the order they are added.

The following configuration will cause the photo to be cropped and resized to 200x200 pixels.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureImageUploadColumn($this, 'photo', '/path/to/photo/upload/dir');
        fORMFile::addFImageMethodCall($this, 'photo', 'cropToRatio', array(1, 1));
        fORMFile::addFImageMethodCall($this, 'photo', 'resize', array(200, 200));
    }
}

When creating cropped versions of an image, placing the crop operation before the resize operation will almost always create the desired size, while placing them in the opposite order will often lead to images that are too small.

addFImageMethodCall() can also be used to specify the file type, or specific parameters to using when saving the image. If no call to fImage::saveChanges() is added, it will be called implicitly with default parameters.

The following configuration will always save the image as a JPEG with a quality of 50.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureImageUploadColumn($this, 'photo', '/path/to/photo/upload/dir');
        fORMFile::addFImageMethodCall($this, 'photo', 'saveChanges', array('jpg', 50));
    }
}

Column Inheritance

When uploading images or photos for a site, it is often a requirement to create multiple sizes for display in different layouts. The static method configureColumnInheritance() will allow a file to be uploaded to a single column, and will then duplicate the uploaded file into another column. All file duplication is done before any image operations are executed. If the column being duplicated into is an image upload column with fImage operations, those will be executed on the duplicated file.

configureColumnInheritance() accepts three parameters, the $class being configured, the $column to inherit the uploaded file, and the $inherit_from_column which will act as the source column. It is possible to set up multiple columns to inherit from a single master column.

The following example shows small and medium thumbnail columns both inheriting from the main photo column. The upload form for this record would only require that the user upload a single master photo.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMFile::configureImageUploadColumn($this, 'photo', '/path/to/photo/upload/dir', 'jpg');
        fORMFile::addFImageMethodCall($this, 'photo', 'resize', array(600, NULL));

        fORMFile::configureImageUploadColumn($this, 'photo_medium', '/path/to/photo_medium/upload/dir', 'jpg');
        fORMFile::addFImageMethodCall($this, 'photo_medium', 'resize', array(200, NULL));
        fORMFile::configureColumnInheritance($this, 'photo_medium', 'photo');
        
        fORMFile::configureImageUploadColumn($this, 'photo_small', '/path/to/photo_small/upload/dir', 'jpg');
        fORMFile::addFImageMethodCall($this, 'photo_small', 'resize', array(100, NULL));
        fORMFile::configureColumnInheritance($this, 'photo_small', 'photo');
    }
}

fORMFile API Reference

static class, v1.0.0b30

Provides file manipulation functionality for fActiveRecord classes

Changes:
1.0.0b30Updated code for the new fUpload API 8/24/11
1.0.0b29Fixed a bug when uploading a new file to a column with an existing file that was not found on the filesystem 5/10/11
1.0.0b28Backwards Compatibility Break - configureImageUploadColumn() no longer accepts the optional $image_type as the fourth parameter, instead addFImageMethodCall() must be called with saveChanges as the $method and the image type as the first parameter 11/30/10
1.0.0b27Fixed column inheritance to properly handle non-images and inheriting into image upload columns 9/18/10
1.0.0b26Enhanced configureColumnInheritance() to ensure both columns specified have been set up as file upload columns 8/18/10
1.0.0b25Updated code to work with the new fORM API 8/6/10
1.0.0b24Changed validation messages array to use column name keys 5/26/10
1.0.0b23Fixed a bug with upload() that could cause a method called on a non-object error in relation to the upload directory not being defined 5/10/10
1.0.0b22Updated the TEMP_DIRECTORY constant to not include the trailing slash, code now uses DIRECTORY_SEPARATOR to fix issues on Windows 4/28/10
1.0.0b21Fixed set() to perform column inheritance, just like upload() does 3/15/10
1.0.0b20Fixed the set and process methods to return the record instance, changed upload methods to return the fFile object, updated reflect() with new return values 3/15/10
1.0.0b19Fixed a few missed instances of old fFile method names 12/16/09
1.0.0b18Updated code for the new fFile API 12/16/09
1.0.0b17Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b16fImage method calls for file upload columns will no longer cause notices due to a missing image type 9/9/09
1.0.0b15addFImageMethodCall() no longer requires column be an image upload column, inheritance to an image column now only happens for fImage objects 7/29/09
1.0.0b14Updated to use new fORM::registerInspectCallback() method 7/13/09
1.0.0b13Updated code for new fORM API 6/15/09
1.0.0b12Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b11Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b10Fixed a bug where an inherited file upload column would not be properly re-set with an existing- input 5/26/09
1.0.0b9upload() and set() now set the $values entry to NULL for filenames that are empty 3/2/09
1.0.0b8Changed set() to accept objects and reject directories 1/21/09
1.0.0b7Changed the class to use the new fFilesystem::createObject() method 1/21/09
1.0.0b6Old files are now checked against the current file to prevent removal of an in-use file 12/23/08
1.0.0b5Fixed replicate() to ensure the temp directory exists and set() to use the temp directory 12/23/08
1.0.0b4objectify() no longer throws an exception when a file can't be found 12/18/08
1.0.0b3Added replicate() so that replicated files get pu in the temp directory 12/12/08
1.0.0b2Fixed a bug with objectifying file columns 11/24/08
1.0.0bThe initial implementation 5/28/08

Constants

::TEMP_DIRECTORY internal

Please note: this constant is primarily intended for internal use by Flourish and will normally not be useful in site/application code

The temporary directory to use for various tasks

Static Methods

::addFImageMethodCall() public

Adds an fImage method call to the image manipulation for a column if an image file is uploaded

Any call to fImage::saveChanges() will be called last. If no explicit method call to fImage::saveChanges() is made, it will be called implicitly with default parameters.

Signature

void addFImageMethodCall( mixed $class, string $column, string $method, array $parameters=array() )

Parameters
mixed $class The class name or instance of the class
string $column The column to call the method for
string $method The fImage method to call
array $parameters The parameters to pass to the method
::addFUploadMethodCall() public

Adds an fUpload method call to the fUpload initialization for a column

Signature

void addFUploadMethodCall( mixed $class, string $column, string $method, array $parameters=array() )

Parameters
mixed $class The class name or instance of the class
string $column The column to call the method for
string $method The fUpload method to call
array $parameters The parameters to pass to the method
::begin() internal public

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

Begins a transaction, or increases the level

Signature

void begin( )

::commit() internal public

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

Commits a transaction, or decreases the level

Signature

void commit( )

::configureColumnInheritance() public

Takes one file or image upload columns and sets it to inherit any uploaded/set files from another column

Signature

void configureColumnInheritance( mixed $class, string $column, string $inherit_from_column )

Parameters
mixed $class The class name or instance of the class
string $column The column that will inherit the uploaded file
string $inherit_from_column The column to inherit the uploaded file from
::configureFileUploadColumn() public

Sets a column to be a file upload column

Configuring a column to be a file upload column means that whenever fActiveRecord::populate() is called for an fActiveRecord object, any appropriately named file uploads (via $_FILES) will be moved into the directory for this column.

Setting the column to a file path will cause the specified file to be copied into the directory for this column.

Signature

void configureFileUploadColumn( mixed $class, string $column, fDirectory|string $directory )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as a file upload column
fDirectory|string $directory The directory to upload/move to
::configureImageUploadColumn() public

Sets a column to be an image upload column

This method works exactly the same as configureFileUploadColumn() except that only image files are accepted.

To alter an image, including the file type, use addFImageMethodCall().

Signature

void configureImageUploadColumn( mixed $class, string $column, fDirectory|string $directory )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as a file upload column
fDirectory|string $directory The directory to upload to
::delete() internal public

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

Deletes the files for this record

Signature

void delete( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::deleteOld() internal public

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

Deletes old files for this record that have been replaced by new ones

Signature

void deleteOld( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::encode() internal public

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

Encodes a file for output into an HTML input tag

Signature

void encode( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
::inspect() internal public

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

Adds metadata about features added by this class

Signature

void inspect( string $class, string $column, array &$metadata )

Parameters
string $class The class being inspected
string $column The column being inspected
array &$metadata The array of metadata about a column
::moveFromTemp() internal public

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

Moves uploaded files from the temporary directory to the permanent directory

Signature

void moveFromTemp( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::objectify() internal public

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

Turns a filename into an fFile or fImage object

Signature

mixed objectify( string $class, string $column, mixed $value )

Parameters
string $class The class this value is for
string $column The column the value is in
mixed $value The value
Returns

The fFile, fImage or raw value

::populate() internal public

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

Performs the upload action for file uploads during fActiveRecord::populate()

Signature

void populate( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::prepare() internal public

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

Prepares a file for output into HTML by returning filename or the web server path to the file

Signature

void prepare( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
::process() internal public

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

Handles re-processing an existing image file

Signature

fActiveRecord process( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::processImage() internal public

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

Performs image manipulation on an uploaded/set image

Signature

void processImage( string $class, string $column, fFile $image )

Parameters
string $class The name of the class we are manipulating the image for
string $column The column the image is assigned to
fFile $image The image object to manipulate
::reflect() internal public

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

Adjusts the fActiveRecord::reflect() signatures of columns that have been configured in this class

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect
array &$signatures The associative array of {method name} => {signature}
boolean $include_doc_comments If doc comments should be included with the signature
::replicate() internal public

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 copy of an uploaded file in the temp directory for the newly cloned record

Signature

mixed replicate( string $class, string $column, mixed $value )

Parameters
string $class The class this value is for
string $column The column the value is in
mixed $value The value
Returns

The cloned fFile object

::reset() internal public

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

Signature

void reset( )

::rollback() internal public

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

Rolls back a transaction, or decreases the level

Signature

void rollback( )

::set() internal public

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

Copies a file from the filesystem to the file upload directory and sets it as the file for the specified column

This method will perform the fImage calls defined for the column.

Signature

fActiveRecord set( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::upload() internal public

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

Uploads a file

Signature

fFile upload( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The uploaded file

::validate() internal public

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

Validates uploaded files to ensure they match all of the criteria defined

Signature

void validate( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages The existing validation messages

fORMJSON

The fORMJSON class is an ORM plugin to provide JSON encoding functionality to fActiveRecord and fRecordSet objects.

Enabling

To add the JSON encoding functionality for whole records and record sets, simply call the static method extend().

fORMJSON::extend();

Usage

The fORMJSON class adds a toJSON() method to both fActiveRecord and fRecordSet. Calling toJSON() on a record will create a single JSON object with each column being the property name and the value being the value. Calling toJSON() on a record set will create an array of the record JSON objects.

$user = new User(3);
echo $user->toJSON();

$users = fRecordSet::build('User');
echo $users->toJSON();

When outputting JSON to a browser, it is best practice to include the appropriate Content-Type header by calling fJSON::sendHeader().

fJSON::sendHeader();
$user = new User(3);
echo $user->toJSON();

fORMJSON API Reference

static class, v1.0.0b3

Adds JSON functionality to fActiveRecord and fRecordSet

Changes:
1.0.0b3Removed the $pointer parameter from toJSONRecordSet() since fRecordSet no longer has a pointer 9/28/10
1.0.0b2Updated the code to remove the $associate parameter for the record set method callback 6/2/09
1.0.0bThe initial implementation 6/25/08

Static Methods

::extend() public

Adds the method toJSON() to fActiveRecord and fRecordSet instances

Signature

void extend( )

::reflect() internal public

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

Adjusts the fActiveRecord::reflect() signatures of columns that have been added by this class

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect
array &$signatures The associative array of {method name} => {signature}
boolean $include_doc_comments If doc comments should be included with the signature
::toJSON() internal public

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

Returns a JSON object representation of the record

Signature

string toJSON( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The JSON object that represents the values of this record

::toJSONRecordSet() internal public

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

Returns a JSON object representation of a record set

Signature

string toJSONRecordSet( fRecordSet $record_set, string $class, array &$records )

Parameters
fRecordSet $record_set The fRecordSet instance
string $class The class of the records
array &$records The fActiveRecord objects
Returns

The JSON object that represents an array of all of the fActiveRecord objects

fORMMoney

The fORMMoney class is an ORM plugin to provide functionality to treat columns as monetary values.

Money Columns

When dealing with monetary values, it is usually very important that the values stored and calculated are accurate. The fMoney class provides this sort of accuracy and the fORMMoney class allows a column to be configured so that values coming out of the database are automatically converted to fMoney object. The static method configureMoneyColumn() accepts three parameters, the $class being configured, the $column to set as a money column, and optionally a $currency_column to store the currency of the monetary value in.

When configuring a money column without a corresponding currency column, a default currency must be set via fMoney::setDefaultCurrency(). All fMoney objects created will use this default currency. If an fMoney object is set to the value of the money column, and it contains a different currency than the default and no currency column is set, the currency will be lost when the record is saved in the database.

class Order extends fActiveRecord
{
    protected function configure()
    {
        fORMMoney::configureMoneyColumn($this, 'total');
    }
}

When calling the get method for a money column, an fMoney object should be expected whenever a record has been freshly loaded from the databaseit is possible that if a value has been set that it is not a valid money value, and thus will not be an fMoney object. The prepare method for a money column will call the fMoney::format() method on the fMoney object, while the encode method will return the output of fMoney::__toString().

If a currency column is set for the money column, the currency contained in the fMoney object will be stored in that column when the record is saved to the database. If the set method is called on the money column with a non-fMoney object, a new fMoney object will be created with the currency currently stored in the currency column. If the set method is called on the currency column, a new fMoney object will be created with that currency and the current money amount. Setting an fMoney object to the money column will cause the currency column to be updated to the currency of the object.

fORMMoney API Reference

static class, v1.0.0b11

Provides money functionality for fActiveRecord classes

Changes:
1.0.0b11Fixed the generation of validation messages when a non-monetary value is supplied 5/17/11
1.0.0b10Updated code to work with the new fORM API 8/6/10
1.0.0b9Added the $remove_zero_fraction parameter to prepare methods 6/9/10
1.0.0b8Changed validation messages array to use column name keys 5/26/10
1.0.0b7Fixed the set methods to return the record object in order to be consistent with all other set methods 3/15/10
1.0.0b6Fixed duplicate validation messages and fProgrammerException object being thrown when NULL is set 3/3/10
1.0.0b5Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b4Updated to use new fORM::registerInspectCallback() method 7/13/09
1.0.0b3Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b2Fixed bugs with objectifying money columns 11/24/08
1.0.0bThe initial implementation 9/5/08

Static Methods

::configureMoneyColumn() public

Sets a column to be formatted as an fMoney object

Signature

void configureMoneyColumn( mixed $class, string $column, string $currency_column=NULL )

Parameters
mixed $class The class name or instance of the class to set the column format
string $column The column to format as an fMoney object
string $currency_column If specified, this column will store the currency of the fMoney object
::encodeMoneyColumn() internal public

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

Encodes a money column by calling fMoney::__toString()

Signature

string encodeMoneyColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The encoded monetary value

::inspect() internal public

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

Adds metadata about features added by this class

Signature

void inspect( string $class, string $column, array &$metadata )

Parameters
string $class The class being inspected
string $column The column being inspected
array &$metadata The array of metadata about a column
::makeMoneyObjects() internal public

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

Makes fMoney objects for all money columns in the object that also have a currency column

Signature

void makeMoneyObjects( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::objectifyMoney() internal public

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

Turns a monetary value into an fMoney object

Signature

mixed objectifyMoney( string $class, string $column, mixed $value )

Parameters
string $class The class this value is for
string $column The column the value is in
mixed $value The value
Returns

The fMoney object or raw value

::objectifyMoneyWithCurrency() internal public

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

Turns a monetary value into an fMoney object with a currency specified by another column

Signature

void objectifyMoneyWithCurrency( array &$values, array &$old_values, string $value_column, string $currency_column )

Parameters
array &$values The current values
array &$old_values The old values
string $value_column The column holding the value
string $currency_column The column holding the currency code
::prepareMoneyColumn() internal public

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

Prepares a money column by calling fMoney::format()

Signature

string prepareMoneyColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The formatted monetary value

::reflect() internal public

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

Adjusts the fActiveRecord::reflect() signatures of columns that have been configured in this class

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect
array &$signatures The associative array of {method name} => {signature}
boolean $include_doc_comments If doc comments should be included with the signature
::reset() internal public

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

Signature

void reset( )

::setCurrencyColumn() internal public

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 the currency column and then tries to objectify the related money column

Signature

fActiveRecord setCurrencyColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::setMoneyColumn() internal public

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 the money column and then tries to objectify it with an related currency column

Signature

fActiveRecord setMoneyColumn( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The record object, to allow for method chaining

::validateMoneyColumns() internal public

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

Validates all money columns

Signature

void validateMoneyColumns( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages An array of ordered validation messages

fORMOrdering

The fORMOrdering class is an ORM plugin to provide record ordering functionality for fActiveRecord classes.

An ordering column is used to provide arbitrary ordering for fActiveRecord objects. For a column to be an ordering column, it must be an integer column that allows for negative numbers and is part of a UNIQUE constraint. While an ordering value will never be negative, negative integers are used in the process of re-arranging values.

If the column is the only column in the UNIQUE constraint, all records in the table will be part of one ordered set. If the column is part of a multi-column UNIQUE constraint, all records that have the same values for each column other than the ordering will be part of an ordered set.

Basic Ordering (Single-Column)

To configure a column to be treated as an ordering column, the static method configureOrderingColumn() must be called with the $class to configure and the $column to set as an ordering column.

class Photo extends fActiveRecord
{
    protected function configure()
    {
        fORMOrdering::configureOrderingColumn($this, 'display_order');
    ]
}

Once a column has been set to be an ordering column, any new records inserted into the set will be automatically added at the end, unless a specific value is set for the ordering column.

// Assuming there are no other photos, this photo would become #1
$photo = new Photo();
$photo->store();

// This photo would be come number 2
$photo2 = new Photo();
$photo2->store();

Whenever the value for the ordering column is changed, the records around it will be adjusted so there is always a continuous sequence of order numbers.

// This photo would become number 2 and $photo2 would change to number 3
$photo3 = new Photo();
$photo3->setDisplayOrder(2);
$photo3->store();

Advanced Ordering (Multi-Column)

Below is an example of a multi-column UNIQUE constraint and the dynamics related to it. Each photo is part of a photo gallery, thus it is desired to have a separate ordering for each gallery. Flourish will automatically detect that the display_order column is in a UNIQUE constraint with photo_gallery_id and will order each photo gallery separately.

CREATE TABLE photo_galleries (
    name VARCHAR(200) PRIMARY KEY
);

CREATE TABLE photos (
    photo_id SERIAL PRIMARY KEY,
    photo VARCHAR(255) NOT NULL,
    caption VARCHAR(255) NOT NULL DEFAULT '',
    display_order INTEGER NOT NULL,
    photo_gallery VARCHAR(200) NOT NULL REFERENCES photo_galleries(name) ON DELETE CASCADE ON UPDATE CASCADE,
    UNIQUE(photo_gallery_id, display_order)
);
class Photo extends fActiveRecord
{
    protected function configure()
    {
        fORMOrdering::configureOrderingColumn($this, 'display_order');
    ]
}

The following example shows how photos will be ordered in their respective galleries.

// Create the galleries to use
$photo_gallery = new PhotoGallery();
$photo_gallery->setName('Gallery 1');
$photo_gallery->store();

$photo_gallery2 = new PhotoGallery();
$photo_gallery2->setName('Gallery 2');
$photo_gallery2->store();

// This photo will have its display order set to 1
$photo = new Photo();
$photo->setPhotoGallery('Gallery 1');
$photo->setPhoto($photo_path);
$photo->store();

// This photo will have its display order set to 1 also, but as part of Gallery 2
$photo2 = new Photo();
$photo2->setPhotoGallery('Gallery 2');
$photo2->setPhoto($photo_path);
$photo2->store();

// This photo will have its display order set to 2
$photo3 = new Photo();
$photo3->setPhotoGallery('Gallery 1');
$photo3->setPhoto($photo_path);
$photo3->store();

If the gallery was changed for $photo to Gallery 2, it would be moved out of the display order of Gallery 1 and added to the end of the display order for Gallery 2.

$photo->setPhotoGallery('Gallery 2');
$photo->store();

// This would output 2 since it was added at the end of Gallery 2
echo $photo->getDisplayOrder();

fORMOrdering API Reference

static class, v1.0.0b19

Allows a column in an fActiveRecord class to be a relative sort order column

Changes:
1.0.0b19Updated code to work with the new fORM API 8/6/10
1.0.0b18Changed configureOrderingColumn() to ensure the column specified can store negative values 7/21/10
1.0.0b17Changed validation messages array to use column name keys 5/26/10
1.0.0b16Updated the class to allow for multiple ordering columns per class 5/10/10
1.0.0b15Fixed a bug with ordering columns that are part of a multi-column unique constraint 11/13/09
1.0.0b14Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b13Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b12Changed SQL statements to use value placeholders, identifier escaping and schema support 10/22/09
1.0.0b11Fixed another bug with deleting records in the middle of a set, added support for reordering multiple records at once 7/17/09
1.0.0b10Fixed a bug with deleting multiple in-memory records in the same set 7/15/09
1.0.0b9Fixed a bug with using fORM::registerInspectCallback() 7/15/09
1.0.0b8Updated to use new fORM::registerInspectCallback() method 7/13/09
1.0.0b7Fixed validate() so it properly ignores ordering columns in multi-column unique constraints 6/17/09
1.0.0b6Updated code for new fORM API 6/15/09
1.0.0b5Updated class to automatically correct ordering values that are too high 6/14/09
1.0.0b4Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b3Fixed a bug with setting a new record to anywhere but the end of a set 3/18/09
1.0.0b2Fixed a bug with inspect(), 'max_ordering_value' was being returned as 'max_ordering_index' 3/2/09
1.0.0bThe initial implementation 6/25/08

Static Methods

::configureOrderingColumn() public

Sets a column to be an ordering column

There can only be one ordering column per class/table and it must be part of a single or multi-column UNIQUE constraint.

Signature

void configureOrderingColumn( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class
string $column The column to set as an ordering column
::delete() internal public

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

Re-orders other records in the set when the record specified is deleted

Signature

void delete( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::inspect() internal public

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

Returns the metadata about a column including features added by this class

Signature

mixed inspect( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, string $method_name, array $parameters )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
string $method_name The method that was called
array $parameters The parameters passed to the method
Returns

The metadata array or element specified

::reflect() internal public

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

Adjusts the fActiveRecord::reflect() signatures of columns that have been configured in this class

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect
array &$signatures The associative array of {method name} => {signature}
boolean $include_doc_comments If doc comments should be included with the signature
::reorder() internal public

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

Re-orders the object based on it's current state and new position

Signature

void reorder( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
::reset() internal public

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

Signature

void reset( )

::validate() internal public

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

Makes sure the ordering value is sane, removes error messages about missing values

Signature

void validate( fActiveRecord $object, array &$values, array &$old_values, array &$related_records, array &$cache, array &$validation_messages )

Parameters
fActiveRecord $object The fActiveRecord instance
array &$values The current values
array &$old_values The old values
array &$related_records Any records related to this record
array &$cache The cache array for the record
array &$validation_messages An array of ordered validation messages

fORMRelated

The fORMRelated class is a built-in part of the ORM that provides related-record functionality for fActiveRecord classes.

Ordering Related Records

When calling a build method on an fActiveRecord object, by default no ORDER BY clause is used to order the record set. The static method setOrderBys() allows setting an fRecordSet::build() style order bys array for any records created through a build method. The method requires four parameters, the $class being configured, the $related_class to apply the order bys to, the $order_bys array and the $route to the related class. The $route parameter can be omitted if there is only one route.

The following example will cause related groups to be returned order alphabetically.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMRelated::setOrderBys(
            $this,
            'Group',
            array('groups.name' => 'asc')
        );
    }
}

// The groups record set created here will be ordered by name
$user = new User(1);
$groups = $user->buildGroups();

Overriding Related Record Names

Record names are used in messaging related to records, as the fORM documentation discusses. Sometimes, however, a record will need to have a different name when presented in reference to another record. The static method overrideRelatedRecordName() allows defining a custom name to use in the context of being a related record.

CREATE TABLE groups (
    name VARCHAR(255) PRIMARY KEY
);

CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);

CREATE TABLE members (
    group VARCHAR(255) NOT NULL REFERENCES groups(name) ON DELETE CASCADE ON UPDATE CASCADE,
    user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    PRIMARY KEY(group, user_id)
);

CREATE TABLE administrators(
    group VARCHAR(255) NOT NULL REFERENCES groups(name) ON DELETE CASCADE ON UPDATE CASCADE,
    user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
    PRIMARY KEY(group, user_id)
);

The schema presented above related users to groups in two ways, as members of the group, or as administrators. If a validation rule was set up to require at least one user as a member and one user as an administrator, it wouldnt make sense if the error messages for both just referenced users.

overrideRelatedRecordName() accepts four parameters, the $class being configured, the $related_class to set the name for, the $record_name to set and optionally the $route to the related class. The $route is only required if there is more than one.

class Group extends fActiveRecord
{
    protected function configure()
    {
        fORMRelated::overrideRelatedRecordName($this, 'User', 'Member', 'members');
        fORMRelated::overrideRelatedRecordName($this, 'User', 'Administrator', 'administrators');
    }
}

Overriding Child Record Validation Names

For one-to-many relationships, it is possible to override the name given to the child record when being referenced in the validation message (or array). By default, the name of the record will be in the form Child Record #1. The method registerValidationNameMethod() allows setting a method to be called on a record to retrieve its name.

registerValidationNameMethod() requires three parameters, the $class, the $related_class and the $method to call on the related record. An optional fourth parameter, $route, may be specified in there is more than one relationship route between $class and $related_class.

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMRelated::registerValidationNameMethod('User', 'UserPreference', 'makeValidationName');
    }
}
class UserPreference extends fActiveRecord
{
    public function makeValidationName()
    {
        return $this->getName() . ' Preference';
    }
}

The registered method may accept an optional parameter, which will contain a one-based index of its position in the set of child records.

class UserPreference extends fActiveRecord
{
    public function makeValidationName(number)
    {
        return 'Preference #' . $number;
    }
}

fORMRelated API Reference

static class, v1.0.0b44

Handles related record tasks for fActiveRecord classes

The functionality of this class only works with single-field FOREIGN KEY constraints.

Changes:
1.0.0b44Added missing information for has and list methods to reflect() 9/7/11
1.0.0b43Fixed some bugs in handling relationships between PHP 5.3 namespaced classes 5/26/11
1.0.0b42Fixed a bug with associateRecords() not associating record set via primary key 5/23/11
1.0.0b41Fixed a bug in generating errors messages for many-to-many relationships 3/7/11
1.0.0b40Updated getRelatedRecordName() to use fText if loaded 2/2/11
1.0.0b39Fixed a bug with validate() not properly removing validation messages about a related primary key value not being present yet, if the column and related column names were different 11/24/10
1.0.0b38Updated overrideRelatedRecordName() to prefix any namespace from $class to $related_class if not already present 11/24/10
1.0.0b37Fixed a documentation typo 11/4/10
1.0.0b36Fixed getPrimaryKeys() to not throw SQL exceptions 10/20/10
1.0.0b35Backwards Compatibility Break - changed the validation messages array to use nesting for child records 10/3/10
1.0.0b35Updated getPrimaryKeys() to always return primary keys in a consistent order when no order bys are specified 7/26/10
1.0.0b34Updated the class to work with fixes in fORMRelated 7/22/10
1.0.0b33Fixed the related table populate action to use the plural underscore_notation version of the related class name 7/8/10
1.0.0b32Backwards Compatibility Break - related table populate action now use the underscore_notation version of the class name instead of the related table name, allowing for related tables in non-standard schemas 6/23/10
1.0.0b31Fixed reflect() to properly show parameters for associate methods 6/8/10
1.0.0b30Fixed a bug where related record error messages could be overwritten if there were multiple related records with the same error 5/29/10
1.0.0b29Changed validation messages array to use column name keys 5/26/10
1.0.0b28Updated associateRecords() to accept just a single fActiveRecord 5/6/10
1.0.0b27Updated the class to force configure classes before peforming actions with them 3/30/10
1.0.0b26Fixed reflect() to show the proper return values for associate, link and populate methods 3/15/10
1.0.0b25Fixed a bug when storing a one-to-one related record with different column names on each end of the relationship 3/4/10
1.0.0b24Added the ability to associate a single record via primary key 3/3/10
1.0.0b23Fixed a column aliasing issue with SQLite 1/25/10
1.0.0b22Fixed a bug with associating a non-contiguous array of fActiveRecord objects 12/17/09
1.0.0b21Added support for the $force_cascade parameter of fActiveRecord::store(), added hasRecords() and fixed a bug with creating non-existent one-to-one related records 12/16/09
1.0.0b20Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b19Internal Backwards Compatibility Break - Added the $class parameter to storeManyToMany() - also fixed countRecords() to work across all databases, changed SQL statements to use value placeholders, identifier escaping and support schemas 10/22/09
1.0.0b18Fixed a bug in countRecords() that would occur when multiple routes existed to the table being counted 10/5/09
1.0.0b17Updated code for new fRecordSet API 9/16/09
1.0.0b16Fixed a bug with createRecord() not creating non-existent record when the related value is NULL 8/25/09
1.0.0b15Fixed a bug with createRecord() where foreign keys with a different column and related column name would not load properly 8/17/09
1.0.0b14Fixed a bug with createRecord() when a foreign key constraint is on a column other than the primary key 8/10/09
1.0.0b13setOrderBys() now (properly) only recognizes *-to-many relationships 7/31/09
1.0.0b12Changed how related record values are set and how related validation messages are ignored because of recursive relationships 7/29/09
1.0.0b11Fixed some bugs with one-to-one relationships 7/21/09
1.0.0b10Fixed a couple of bugs with validating related records 6/26/09
1.0.0b9Fixed a bug where store() would not save associations with no related records 6/23/09
1.0.0b8Changed associateRecords() to work for *-to-many instead of just many-to-many relationships 6/17/09
1.0.0b7Updated code for new fORM API, fixed API documentation bugs 6/15/09
1.0.0b6Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b5Added getPrimaryKeys() and setPrimaryKeys(), renamed setRecords() to setRecordSet() and tallyRecords() to setCount() 6/2/09
1.0.0b4Updated code to handle new association method for related records and new $related_records structure, added store() and validate() 6/2/09
1.0.0b3associateRecords() can now accept an array of records or primary keys instead of only an fRecordSet 6/1/09
1.0.0b2populateRecords() now accepts any input field keys instead of sequential ones starting from 0 5/3/09
1.0.0bThe initial implementation 12/30/07

Static Methods

::associateRecord() internal public

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 associations for one-to-one relationships

Signature

void associateRecord( string $class, array &$related_records, string $related_class, fActiveRecord|array|string|integer $record, string $route=NULL )

Parameters
string $class The class to get the related values for
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class we are associating with the current record
fActiveRecord|array|string|integer $record The record (or primary key of the record) to be associated
string $route The route to use between the current class and the related class
::associateRecords() internal public

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 associations for *-to-many relationships

Signature

void associateRecords( string $class, array &$related_records, string $related_class, fRecordSet|array $records_to_associate, string $route=NULL )

Parameters
string $class The class to get the related values for
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class we are associating with the current record
fRecordSet|array $records_to_associate An fRecordSet, an array or records, or an array of primary keys of the records to be associated
string $route The route to use between the current class and the related class
::buildRecords() internal public

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

Builds a set of related records along a one-to-many or many-to-many relationship

Signature

fRecordSet buildRecords( string $class, array &$values, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to get the related values for
array &$values The values for the fActiveRecord class
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class that is related to the current record
string $route The route to follow for the class specified
Returns

A record set of the related records

::countRecords() internal public

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

Counts the number of related one-to-many or many-to-many records

Signature

integer countRecords( string $class, array &$values, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to get the related values for
array &$values The values for the fActiveRecord class
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class that is related to the current record
string $route The route to follow for the class specified
Returns

The number of related records

::createRecord() internal public

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

Builds the object for the related class specified

Signature

fActiveRecord createRecord( string $class, array $values, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to create the related record for
array $values The values existing in the fActiveRecord class
array &$related_records The related records for the record
string $related_class The related class name
string $route The route to the related class
Returns

An instance of the class specified

::determineFirstPKColumn() internal public

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

Figures out the first primary key column for a related class that is not the related column

Signature

string determineFirstPKColumn( string $class, string $related_class, string $route )

Parameters
string $class The class name of the main class
string $related_class The related class being filtered for
string $route The route to the related class
Returns

The first primary key column in the related class

::determineRequestFilter() internal public

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

Figures out what filter to pass to fRequest::filter() for the specified related class

Signature

string determineRequestFilter( string $class, string $related_class, string $route )

Parameters
string $class The class name of the main class
string $related_class The related class being filtered for
string $route The route to the related class
Returns

The prefix to filter the request fields by

::flagForAssociation() internal public

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 the related records for a *-to-many relationship to be associated upon fActiveRecord::store()

Signature

void flagForAssociation( string $class, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to associate the related records to
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class we are associating with the current record
string $route The route to use between the current class and the related class
::getOrderBys() internal public

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

Gets the ordering to use when returning an fRecordSet of related objects

Signature

array getOrderBys( string $class, string $related_class, string $route )

Parameters
string $class The class to get the order bys for
string $related_class The related class the ordering rules apply to
string $route The route to the related table, should be a column name in the current table or a join table name
Returns

An array of the order bys - see fRecordSet::build() for format

::getPrimaryKeys() internal public

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

Gets the primary keys of the related records for *-to-many relationships

Signature

array getPrimaryKeys( string $class, array &$values, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to get the related primary keys for
array &$values The values for the fActiveRecord class
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class that is related to the current record
string $route The route to follow for the class specified
Returns

The primary keys of the related records

::getRelatedRecordName() internal public

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

Returns the record name for a related class

The default record name of a related class is the result of fGrammar::humanize() called on the class.

Signature

string getRelatedRecordName( string $class, string $related_class, $route=NULL )

Parameters
string $class The class to get the related class name for
string $related_class The related class to get the record name of
$route
Returns

The record name for the related class specified

::hasRecords() internal public

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

Indicates if a record has a one-to-one or any *-to-many related records

Signature

void hasRecords( string $class, array &$values, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to check related records for
array &$values The values for the record we are checking
array &$related_records The related records for the record we are checking
string $related_class The related class we are checking for
string $route The route to the related class
::linkRecords() internal public

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

Parses associations for many-to-many relationships from the page request

Signature

void linkRecords( string $class, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to get link the related records to
array &$related_records The related records existing for the fActiveRecord class
string $related_class The related class to populate
string $route The route to the related class
::overrideRelatedRecordName() public

Allows overriding of default record names or related records

The default record name of a related record is the result of fGrammar::humanize() called on the class name.

Signature

void overrideRelatedRecordName( mixed $class, mixed $related_class, string $record_name, string $route=NULL )

Parameters
mixed $class The class name or instance of the class to set the related record name for
mixed $related_class The name of the related class, or an instance of it
string $record_name The human version of the related record
string $route The route to the related class
::populateRecords() internal public

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 the values for records in a one-to-many relationship with this record

Signature

void populateRecords( string $class, array &$related_records, string $related_class, string $route=NULL )

Parameters
string $class The class to populate the related records of
array &$related_records The related records existing for the fActiveRecord class
string $related_class The related class to populate
string $route The route to the related class
::reflect() internal public

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

Adds information about methods provided by this class to fActiveRecord

Signature

void reflect( string $class, array &$signatures, boolean $include_doc_comments )

Parameters
string $class The class to reflect the related record methods for
array &$signatures The associative array of {method_name} => {signature}
boolean $include_doc_comments If the doc block comments for each method should be included
::registerValidationNameMethod() public

Registers a method to use to get a name for a related record when doing validation

Signature

void registerValidationNameMethod( string|fActiveRecord $class, string $related_class, string $method, string $route=NULL )

Parameters
string|fActiveRecord $class The class to register the method for
string $related_class The related class to register the method for
string $method The method to be called on the related class that will return the name
string $route The route to the related class
::reset() internal public

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

Signature

void reset( )

::setCount() internal public

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

Records the number of related one-to-many or many-to-many records

Signature

void setCount( string $class, array &$related_records, string $related_class, integer $count, string $route=NULL, array &$values )

Parameters
string $class The class to set the related records count for
array &$values The values for the fActiveRecord class
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class that is related to the current record
integer $count The number of records
string $route The route to follow for the class specified
::setOrderBys() public

Sets the ordering to use when returning an fRecordSet of related objects

Signature

void setOrderBys( mixed $class, string $related_class, array $order_bys, string $route=NULL )

Parameters
mixed $class The class name or instance of the class this ordering rule applies to
string $related_class The related class we are getting info from
array $order_bys An array of the order bys for this table.column combination - see fRecordSet::build() for format
string $route The route to the related table, this should be a column name in the current table or a join table name
::setPrimaryKeys() internal public

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 the related records for *-to-many relationships, providing only primary keys

Signature

void setPrimaryKeys( string $class, array &$related_records, string $related_class, array $primary_keys, string $route=NULL )

Parameters
string $class The class to set the related primary keys for
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class we are setting the records for
array $primary_keys The records to set
string $route The route to use between the current class and the related class
::setRecordSet() internal public

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 the related records for *-to-many relationships

Signature

void setRecordSet( string $class, array &$related_records, string $related_class, fRecordSet $records, string $route=NULL )

Parameters
string $class The class to set the related records for
array &$related_records The related records existing for the fActiveRecord class
string $related_class The class we are associating with the current record
fRecordSet $records The records are associating
string $route The route to use between the current class and the related class
::store() internal public

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

Stores any many-to-many associations or any one-to-many records that have been flagged for association

Signature

void store( string $class, array &$values, array &$related_records, boolean $force_cascade )

Parameters
string $class The class to store the related records for
array &$values The current values for the main record being stored
array &$related_records The related records array
boolean $force_cascade This flag will be passed to the fActiveRecord::delete() method on related records that are being deleted
::storeManyToMany() internal public

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

Associates a set of many-to-many related records with the current record

Signature

void storeManyToMany( string $class, array &$values, array $relationship, array $related_info )

Parameters
string $class The class the relationship is being stored for
array &$values The current values for the main record being stored
array $relationship The information about the relationship between this object and the records in the record set
array $related_info An array containing the keys 'record_set', 'count', 'primary_keys' and 'associate'
::storeOneToStar() internal public

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

Stores a set of one-to-many related records in the database

Signature

void storeOneToStar( string $class, array &$values, array &$related_records, string $related_class, string $route, boolean $force_cascade )

Parameters
string $class The class to store the related records for
array &$values The current values for the main record being stored
array &$related_records The related records array
string $related_class The related class being stored
string $route The route to the related class
boolean $force_cascade This flag will be passed to the fActiveRecord::delete() method on related records that are being deleted
Throws
fValidationException
When one of the "many" records throws an exception from fActiveRecord::store()
::validate() internal public

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

Validates any many-to-many associations or any one-to-many records that have been flagged for association

Signature

void validate( string $class, array &$values, array &$related_records )

Parameters
string $class The class to validate the related records for
array &$values The values for the object
array &$related_records The related records for the object

fORMSchema

The fORMSchema class provides database schema information to the Flourish ORM. For end-developers the class provides access to a single instance of the fSchema class and allows for simple caching of the schema data.

fSchema Singleton

Since the Flourish ORM bases almost all of its functionality on the schema of the database connected to, an instance of the fSchema class is required for it to properly function. As long as an instance of fDatabase has been properly attached using fORMDatabase, an instance of fSchema will be created and automatically attached to fORMSchema.

If fSchema was extended or for some reason a custom instance needs to be attached, the static method attach() will do that.

fORMSchema::attach(new SomeExtensionOfFSchema(fORMDatabase::retrieve()));

When writing custom code for the ORM, the fSchema singleton can be access by calling the static method retrieve().

$primary_keys = fORMSchema::retrieve()->getKeys('users', 'primary');

fORMSchema API Reference

static class, v1.0.0b9

Provides fSchema class related functions for ORM code

Changes:
1.0.0b9Enhanced various exception messages 9/19/10
1.0.0b8Added 'one-to-one' support to getRouteNameFromRelationship(), '!many-to-one' to getRoute() 3/3/10
1.0.0b7Added support for multiple databases 10/28/09
1.0.0b6Internal Backwards Compatibility Break - Added the $schema parameter to the beginning of getRoute(), getRouteName(), getRoutes() and isOneToOne() - added '!many-to-one' relationship type handling 10/22/09
1.0.0b5Fixed some error messaging to not include {empty_string} in some situations 7/31/09
1.0.0b4Added isOneToOne() 7/21/09
1.0.0b3Added routes caching for performance 6/15/09
1.0.0b2Backwards Compatiblity Break - removed enableSmartCaching(), fORM::enableSchemaCaching() now provides equivalent functionality 5/4/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::attach() public

Allows attaching an fSchema-compatible object as the schema singleton for ORM code

Signature

void attach( fSchema $schema, string $name='default' )

Parameters
fSchema $schema An object that is compatible with fSchema
string $name The name of the database this schema is for
::getRoute() internal public

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

Returns information about the specified route

Signature

void getRoute( fSchema $schema, string $table, string $related_table, string $route, string $relationship_type=NULL )

Parameters
fSchema $schema The schema object to get the route from
string $table The main table we are searching on behalf of
string $related_table The related table we are searching under
string $route The route to get info about
string $relationship_type The relationship type: NULL, '*-to-many', '*-to-one', '!many-to-one', 'one-to-one', 'one-to-meny', 'many-to-one', 'many-to-many'
::getRouteName() internal public

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

Returns the name of the only route from the specified table to one of its related tables

Signature

string getRouteName( fSchema $schema, string $table, string $related_table, string $route=NULL, string $relationship_type=NULL )

Parameters
fSchema $schema The schema object to get the route name from
string $table The main table we are searching on behalf of
string $related_table The related table we are trying to find the routes for
string $route The route that was preselected, will be verified if present
string $relationship_type The relationship type: NULL, '*-to-many', '*-to-one', '!many-to-one', 'one-to-one', 'one-to-many', 'many-to-one', 'many-to-many'
Returns

The only route from the main table to the related table

::getRouteNameFromRelationship() internal public

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

Returns the name of the route specified by the relationship

Signature

string getRouteNameFromRelationship( string $type, array $relationship )

Parameters
string $type The type of relationship: '*-to-one', 'one-to-one', 'one-to-many', 'many-to-one', 'many-to-many'
array $relationship The relationship array from fSchema::getKeys()
Returns

The name of the route

::getRoutes() internal public

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

Returns an array of all routes from a table to one of its related tables

Signature

array getRoutes( fSchema $schema, string $table, string $related_table, string $relationship_type=NULL )

Parameters
fSchema $schema The schema object to get the routes for
string $table The main table we are searching on behalf of
string $related_table The related table we are trying to find the routes for
string $relationship_type The relationship type: NULL, '*-to-many', '*-to-one', '!many-to-one', 'one-to-one', 'one-to-many', 'many-to-one', 'many-to-many'
Returns

All of the routes from the main table to the related table

::isOneToOne() internal public

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

Indicates if the relationship specified is a one-to-one relationship

Signature

boolean isOneToOne( fSchema $schema, string $table, string $related_table, string $route=NULL )

Parameters
fSchema $schema The schema object the tables are from
string $table The main table we are searching on behalf of
string $related_table The related table we are trying to find the routes for
string $route The route between the two tables
Returns

If the table is in a one-to-one relationship with the related table over the route specified

::reset() internal public

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

Signature

void reset( )

::retrieve() public

Return the instance of the fSchema class

Signature

fSchema retrieve( string $class='fActiveRecord' )

Parameters
string $class The class the object will be used with
Returns

The schema instance

fORMValidation

The fORMValidation class is a built-in part of the ORM that provides validation functionality for fActiveRecord classes.

Adding Validation Rules

While most databases support a fairly broad set of restrictions on data format and validity, some validation tasks are too complicated or not possible with the standard databases. The fORMValidation class allows for supplementing the database schema with additional validation rules to help ensure valid data is being stored. These rules will be checked whenever fActiveRecord::validate() or fActiveRecord::store() is called.

Required Rules

By default, any database column that is set to NOT NULL and does not have a DEFAULT value, will be required. The static method addRequiredRule() allow setting columns that do allow NULL or have a default to also be required. It accepts a $class and one or more $column names.

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        // Makes the photo column required
        fORMValidation::addRequiredRule($this, 'photo');

        // Makes the publish_date and removal_date columns required
        fORMValidation::addRequiredRule($this, array('publish_date', 'removal_date'));
    }
}

Conditional Rules

The static method addConditionalRule() allows adding a rule where a column can be required to be filled in based on the presence of a value in another column. addConditionalRule() accepts four parameters, the $class being configured, the (one or more) $main_columns to trigger the rule, the optional $conditional_values to trigger the rule, and the $conditional_columns to require. If $conditional_values is NULL, the $conditional_columns will be required if any value is entered into any of the $main_columns, otherwise one of the values in $conditional_values would need to be entered.

The following example will require a photo if the type is set to photo:

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addConditionalRule($this, 'type', 'Photo', 'photo');
    }
}

This example show how to require all three of a set of columns must be filled in if any of them are filled in:

class Product extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addConditionalRule(
            $this,
            array('price', 'discount_price', 'member_price'),
            NULL,
            array('price', 'discount_price', 'member_price')
        );
    }
}

Many-to-Many Rules

Many-to-many validations rules all requiring that a record have at least one of another type of record related to it in a many-to-many relationship. The static method addManyToManyRule() accepts three parameters, the $class to configure, the $related_class to require and optionally the $route to the $related_class.

The following example will require that a user is associated with at least one group:

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addManyToManyRule($this, 'Group');
    }
}

One-to-Many Rules

One-to-many validations rules all requiring that a record have at least one of another type of record related to it in a one-to-many relationship. The static method addOneToManyRule() accepts three parameters, the $class to configure, the $related_class to require and optionally the $route to the $related_class.

The following example will require that a survey question has at least one option to pick from:

class SurveyQuestion extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addOneToManyRule($this, 'SurveyQuestionOption');
    }
}

One or More Rules

In some situations it is necessary to require that a user fills in at least one of a group of columns. This can be accomplished by calling the static method addOneOrMoreRule(). The method accepts two parameters, the $class to configure and an array of $columns to require one or more of.

The following example would require that at least one of photo, link and body were entered:

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addOneOrMoreRule(
            $this,
            array(
                'photo',
                'link',
                'body'
            )
        );
    }
}

Only One Rules

When presenting multiple options to users, sometimes it is necessary to restrict the user to only be able to select one from a number of choices. The static method addOnlyOneRule() accepts the $class to configure, plus an array of the $columns to restrict.

The following example would only allow the entry of one of photo, link and body:

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addOnlyOneRule(
            $this,
            array(
                'photo',
                'link',
                'body'
            )
        );
    }
}

Regex Rules

The static method addRegexRule() allow validating a value against a perl-compatible regular expression (PCRE). This method accepts the $class and $column to test, the $regex to use and the $message for when the regular expression does not match. The rule will not be checked if the value is NULL.

The following example would allow the length only allow a number, followed by zero or more spaces and in, inches, cm, m, meters, ft or feet:

class Box extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addRegexRule(
            $this,
            'length',
            '#^\d+\s*(in(ches)?|cm|m(eters)?|ft|feet)$#',
            'Please enter a length in cm, in, ft or m'
        );
    }
}

Valid Values Rules

While it is possible to restrict the valid input to a column via a CHECK constraint (or the MySQL ENUM data type), there is also a method in fORMValidation to do the same thing. addValidValuesRule() accepts the $class to configure, the $column to restrict the value of and an array of the $valid_values. Please note that NULL is always allowed as long as the column is not set as NOT NULL.

The following example would only allow 'Active' or 'Inactive' in the status column:

class NewsArticle extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addValidValuesRule(
            $this,
            'status',
            array(
                'Active',
                'Inactive'
            )
        );
    }
}

The list of valid values can be retrieved by requesting the 'valid_values' element during an inspect method call. This is also true if the valid values are defined in the database by a CHECK constraint.

$valid_values = $news_article->inspectStatus('valid_values');
foreach ($valid_values as $valid_status) {
    // ...
}

Custom Validation

In addition to using the built-in validation rules, it is possible to do custom validation by using an ORM hook via fORM. Please see the Adding Functionality to fActiveRecord and Custom Validation Using a Hook sections for details and example code.

Column Case Sensitivity

UNIQUE constraints on databases are normally case sensitive, meaning that the values will@flourishlib.com and Will@flourishlib.com can both exist in a column with a UNIQUE constraint. For most users this distinction is more confusing than useful. The static method setColumnCaseInsensitive() restricts values to be unique in a case-insensitive manner. The method accepts two parameters, the $class to configure and the $column to treat as case-insensitive.

The following example will ensure that category names must have a unique spelling, not just unique case:

class Category extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::setColumnCaseInsensitive($this, 'name');
    }
}

Validation Message Order

When fActiveRecord::validate() finds one of more validation issues (either by being called explicitly, or through fActiveRecord::store()), the error messages are created in the order they are detected. The static method setMessageOrder() accepts two parameters, the $class to configure and an ordered array of $matches to use to set the order of the error messages.

The $matches array should contain strings that will be matched to the error messages, with the first array entry causing any matching error message to be first, the second array match to be next, and so forth. All matches are done in a case-insensitive manner.

The longest matches are made first to help prevent unintended substring matches. Any messages that dont match anything will be placed at the end of the exception message. Please note that there is no special format necessary to the match stringit can include any part of the message, including punctuation.

Below is an example that will ensure that the error messages are displayed with first name first, last name second and email last:

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::setMessageOrder(
            $this,
            array(
                'First Name:',
                'Last Name:',
                'Email:'
            )
        );
    }
}

Message Modification

There are two static methods to modifying validation messages, addStringReplacement() and addRegexReplacement(). Each accepts the $class to apply the replacement to, the string/regex to $search for, and the string to $replace with. The search and replace action is performed on every validation message generated in the class, and is performed right before the messages are reordered.

addRegexReplacement() accepts Perl-compatible regular expressions (PCRE) and thus the $replace parameter needs to escape any literal $ or \ characters.

The following example shows how the text of an error message can be easily changed:

class User extends fActiveRecord
{
    protected function configure()
    {
        fORMValidation::addStringReplacement($this, 'Favorite Genre: Please enter a value', 'Favorite Genre: Please check at least one');
    }
}

fORMValidation API Reference

static class, v1.0.0b32

Handles validation for fActiveRecord classes

Changes:
1.0.0b32Fixed an array to string conversion notice 9/21/12
1.0.0b31Fixed checkConditionalRule() to require columns that default to an empty string and are currently set to that value 6/14/11
1.0.0b30Fixed a bug with setMessageOrder() not accepting a variable number of parameters like fValidation::setMessageOrder() does 3/7/11
1.0.0b29Updated addManyToManyRule() and addOneToManyRule() to prefix any namespace from $class to $related_class if not already present 11/24/10
1.0.0b28Updated the class to work with the new nested array structure for validation messages 10/3/10
1.0.0b27Fixed hasValue() to properly detect zero-value floats, made hasValue() internal public 7/26/10
1.0.0b26Improved the error message for integers to say whole number instead of just number 5/29/10
1.0.0b25Added addRegexRule(), changed validation messages array to use column name keys 5/26/10
1.0.0b24Added addRequiredRule() for required columns that aren't automatically handled via schema detection 4/6/10
1.0.0b23Added support for checking integers and floats to ensure they fit within the range imposed by the database schema 3/17/10
1.0.0b22Made the value checking for one-or-more and only-one rules more robust when detecting the absence of a value 12/17/09
1.0.0b21Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b20Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b19Changed SQL statements to use value placeholders, identifier escaping and schema support 10/22/09
1.0.0b18Fixed checkOnlyOneRule() and checkOneOrMoreRule() to consider blank strings as NULL 8/21/09
1.0.0b17Added @internal methods removeStringReplacement() and removeRegexReplacement() 7/29/09
1.0.0b16Backwards Compatibility Break - renamed addConditionalValidationRule() to addConditionalRule(), addManyToManyValidationRule() to addManyToManyRule(), addOneOrMoreValidationRule() to addOneOrMoreRule(), addOneToManyValidationRule() to addOneToManyRule(), addOnlyOneValidationRule() to addOnlyOneRule(), addValidValuesValidationRule() to addValidValuesRule() 7/13/09
1.0.0b15Added addValidValuesValidationRule() [wb/jt, 2009-07-13]
1.0.0b14Added addStringReplacement() and addRegexReplacement() for simple validation message modification 7/1/09
1.0.0b13Changed reorderMessages() to compare string in a case-insensitive manner 6/30/09
1.0.0b12Updated addConditionalValidationRule() to allow any number of $main_columns, and if any of those have a matching value, the condtional columns will be required 6/30/09
1.0.0b11Fixed a couple of bugs with validating related records 6/26/09
1.0.0b10Fixed UNIQUE constraint checking so it is only done once per constraint, fixed some UTF-8 case sensitivity issues 6/17/09
1.0.0b9Updated code for new fORM API 6/15/09
1.0.0b8Updated code to use new fValidationException::formatField() method 6/4/09
1.0.0b7Updated validateRelated() to use new fORMRelated::validate() method and checkRelatedOneOrMoreRule() to use new $related_records structure 6/2/09
1.0.0b6Changed date/time/timestamp checking from strtotime() to fDate/fTime/fTimestamp for better localization support 6/1/09
1.0.0b5Fixed a bug in checkOnlyOneRule() where no values would not be flagged as an error 4/23/09
1.0.0b4Fixed a bug in checkUniqueConstraints() related to case-insensitive columns 2/15/09
1.0.0b3Implemented proper fix for addManyToManyValidationRule() 12/12/08
1.0.0b2Fixed a bug with addManyToManyValidationRule() 12/8/08
1.0.0bThe initial implementation 8/4/07

Static Methods

::addConditionalRule() public

Adds a conditional rule

If a non-empty value is found in one of the $main_columns, or if specified, a value from the $conditional_values array, all of the $conditional_columns will also be required to have a value.

Signature

void addConditionalRule( mixed $class, string|array $main_columns, mixed $conditional_values, string|array $conditional_columns )

Parameters
mixed $class The class name or instance of the class this rule applies to
string|array $main_columns The column(s) to check for a value
mixed $conditional_values If NULL, any value in the main column will trigger the conditional column(s), otherwise the value must match this scalar value or be present in the array of values
string|array $conditional_columns The column(s) that are to be required
::addManyToManyRule() public

Add a many-to-many rule that requires at least one related record is associated with the current record

Signature

void addManyToManyRule( mixed $class, string $related_class, string $route=NULL )

Parameters
mixed $class The class name or instance of the class to add the rule for
string $related_class The name of the related class
string $route The route to the related class
::addOneOrMoreRule() public

Adds a one-or-more rule that requires at least one of the columns specified has a value

Signature

void addOneOrMoreRule( mixed $class, array $columns )

Parameters
mixed $class The class name or instance of the class the columns exists in
array $columns The columns to check
::addOneToManyRule() public

Add a one-to-many rule that requires at least one related record is associated with the current record

Signature

void addOneToManyRule( mixed $class, string $related_class, string $route=NULL )

Parameters
mixed $class The class name or instance of the class to add the rule for
string $related_class The name of the related class
string $route The route to the related class
::addOnlyOneRule() public

Add an only-one rule that requires exactly one of the columns must have a value

Signature

void addOnlyOneRule( mixed $class, array $columns )

Parameters
mixed $class The class name or instance of the class the columns exists in
array $columns The columns to check
::addRegexReplacement() public

Adds a call to preg_replace() for each message

Regex replacement is done after the post::validate() hook, and right before the messages are reordered.

If a message is an empty string after replacement, it will be removed from the list of messages.

Signature

void addRegexReplacement( mixed $class, string $search, string $replace )

Parameters
mixed $class The class name or instance of the class the columns exists in
string $search The PCRE regex to search for - see http://php.net/pcre for details
string $replace The string to replace with - all $ and \ are used in back references and must be escaped with a \ when meant literally
::addRegexRule() public

Adds a rule to validate a column against a PCRE regular expression - the rule is not run if the value is NULL

Signature

void addRegexRule( mixed $class, string $column, string $regex, string $message )

Parameters
mixed $class The class name or instance of the class the columns exists in
string $column The column to match with the regex
string $regex The PCRE regex to match against - see http://php.net/pcre for details
string $message The message to use if the value does not match the regular expression
::addRequiredRule() public

Requires that a column have a non-NULL value

Before using this method, try setting the database column to NOT NULL and remove any default value. Such a configuration will trigger the same functionality as this method, and will enforce the rule on the database level for any other code that queries it.

Signature

void addRequiredRule( mixed $class, array $columns )

Parameters
mixed $class The class name or instance of the class the column(s) exists in
array $columns The column or columns to check - each column will require a value
::addStringReplacement() public

Adds a call to str_replace() for each message

String replacement is done after the post::validate() hook, and right before the messages are reordered.

If a message is an empty string after replacement, it will be removed from the list of messages.

Signature

void addStringReplacement( mixed $class, string $search, string $replace )

Parameters
mixed $class The class name or instance of the class the columns exists in
string $search The string to search for
string $replace The string to replace with
::addValidValuesRule() public

Restricts a column to having only a value from the list of valid values

Please note that NULL values are always allowed, even if not listed in the $valid_values array, if the column is not set as NOT NULL.

This functionality can also be accomplished by added a CHECK constraint on the column in the database, or using a MySQL ENUM data type.

Signature

void addValidValuesRule( mixed $class, string $column, array $valid_values )

Parameters
mixed $class The class name or instance of the class this rule applies to
string $column The column to validate
array $valid_values The valid values to check - NULL values are always allows if the column is not set to NOT NULL
::hasValue() internal public

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 columns has a value, but based on the schema and if the column allows NULL

If the columns allows NULL values, than anything other than NULL will be returned as TRUE. If the column does not allow NULL and the value is anything other than the "empty" value for that data type, then TRUE will be returned.

The values that are considered "empty" for each data type are as follows. Please note that there is no "empty" value for dates, times or timestamps.

  • Blob: ''
  • Boolean: FALSE
  • Float: 0.0
  • Integer: 0
  • String: ''
Signature

string hasValue( fSchema $schema, string $class, array &$values, $column, array $columns )

Parameters
fSchema $schema The schema object for the table
string $class The class the column is part of
array &$values An associative array of all values for the record
array $columns The column to check
$column
Returns

An error message for the rule

::inspect() internal public

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

Adds metadata about features added by this class

Signature

void inspect( string $class, string $column, array &$metadata )

Parameters
string $class The class being inspected
string $column The column being inspected
array &$metadata The array of metadata about a column
::removeRegexReplacement() internal public

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

Removes a regex replacement

Signature

void removeRegexReplacement( mixed $class, string $search, string $replace )

Parameters
mixed $class The class name or instance of the class the columns exists in
string $search The string to search for
string $replace The string to replace with
::removeStringReplacement() internal public

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

Removes a string replacement

Signature

void removeStringReplacement( mixed $class, string $search, string $replace )

Parameters
mixed $class The class name or instance of the class the columns exists in
string $search The string to search for
string $replace The string to replace with
::reorderMessages() internal public

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

Reorders list items in an html string based on their contents

Signature

array reorderMessages( string $class, array $messages )

Parameters
string $class The class to reorder messages for
array $messages An array of the messages
Returns

The reordered messages

::replaceMessages() internal public

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 list of messages and performs string and regex replacements on them

Signature

array replaceMessages( string $class, array $messages )

Parameters
string $class The class to reorder messages for
array $messages The array of messages
Returns

The new array of messages

::reset() internal public

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

Signature

void reset( )

::setColumnCaseInsensitive() public

Sets a column to be compared in a case-insensitive manner when checking UNIQUE and PRIMARY KEY constraints

Signature

void setColumnCaseInsensitive( mixed $class, string $column )

Parameters
mixed $class The class name or instance of the class the column is located in
string $column The column to set as case-insensitive
::setMessageOrder() public

Allows setting the order that the list items in a message will be displayed

All string comparisons during the reordering process are done in a case-insensitive manner.

Signature

void setMessageOrder( mixed $class, array $matches )

Parameters
mixed $class The class name or an instance of the class to set the message order for
array $matches This should be an ordered array of strings. If a line contains the string it will be displayed in the relative order it occurs in this array.
::validate() internal public

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

Validates values for an fActiveRecord object against the database schema and any additional rules that have been added

Signature

array validate( fActiveRecord $object, array $values, array $old_values )

Parameters
fActiveRecord $object The instance of the class to validate
array $values The values to validate
array $old_values The old values for the record
Returns

An array of messages

::validateRelated() internal public

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

Validates related records for an fActiveRecord object

Signature

array validateRelated( fActiveRecord $object, array &$values, array &$related_records )

Parameters
fActiveRecord $object The object to validate
array &$values The values for the object
array &$related_records The related records for the object
Returns

An array of messages

fProgrammerException

fProgrammerException is a sub-class of fUnexpectedException that indicates the programmer has written invalid code that will not allow for further execution. This exception is one of the most prevalent in the Flourish code base and is used to indicate improper parameter values, incorrect code sequencing and similar errors.

This space intentionally left blank

fProgrammerException API Reference

class, v1.0.0b

An exception caused by programmer error

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
         |
         --fProgrammerException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fRecordSet

The fRecordSet class provides functionality to load sets of fActiveRecord objects from the database and manipulate them.

Since the class contains quite a number of features and handles finding data even with complex database schemas, having an example database schema to reference is important. The following database tables will be used as the basis for the examples on this page:

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,
    address VARCHAR(255) NOT NULL,
    city VARCHAR(100) NOT NULL,
    state VARCHAR(2) NOT NULL,
    zip_code VARCHAR(10) NOT NULL,
    date_created TIMESTAMP NOT NULL,
    last_login TIMESTAMP NOT NULL,
    status VARCHAR(20) NOT NULL CHECK(status IN ('Active', 'Inactive'))
);
 
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)
);

Creation

Record sets can be created from simple condition arrays, SQL statements, or from an fActiveRecord class. Under the majority of circumstances, the shorthand condition array method provides for an efficient and expressive method to build a record set.

From Conditions

The static method build() accepts between one and five parameters to build a record set. The only required parameter is the first, $class, which specified the type of record to build. Passing on this parameter will cause all records of that type to be created in the set.

$users = fRecordSet::build('User');

The second parameter is the array of $where_conditions. This parameter accepts an array that contains columns and operaters as the key and the value(s) to match as the value. A simple example of matching all users with a status of Active is shown below:

$active_users = fRecordSet::build(
    'User',
    array('status=' => 'Active')
);

Each key => value pair in the conditions array represents a single expression that will be joined by AND logic to create there WHERE clause. It is also possible, with slightly different syntax, to create simple OR conditions and use aggregate functions in the HAVING clause.

Column Specifications

The column name can be any column in the record being created, or can be columns in related records. The following example would select users in a group with the name Administrators:

$administrators = fRecordSet::build(
    'User',
    array('groups.name=' => 'Administrators')
);

It is also possible to specify columns in a table that is twice-removed from the record being created. The following example would create the groups that contain users who have a favorite including http://example.com:

$groups = fRecordSet::build(
    'Group',
    array('users=>favorites.url=' => 'http://example.com')
);

When there is more than one relationship between two tables, the relationship route can be specified by putting it in {} after the table name. For example, the following SQL would select all users who are the owner of a resource called money:

$owners = fRecordSet::build(
    'User',
    array('resources{owner}.name=' => 'money')
);
Operators

All of the examples in the column specification section above use a simple equation operator, =. The following is a list of all supported operators:

The =, !, ~ and !~ operators also support comparison with an array of values. The following example will find all users with the first name James or John:

$users = fRecordSet::build(
    'User',
    array('first_name=' => array('James', 'John'))
);

This example will return all users with a name like Joe or Fred:

$users = fRecordSet::build(
    'User',
    array('first_name~' => array('Joe', 'Fred'))
);

It is also possible to do ~ matching over multiple columns. If a single string value is set for such a condition, it will be parsed for individual words and quoted phrases. In addition, stop words (such as "the" and "a") will be removed from the parsed words and punctuation will be stripped from the beginning and end of each word parsed.

The following example would find any users that contained john, smith and west peabody in any combination of the first_name, last_name, address and city columns:

$users = fRecordSet::build(
    'User',
    array('first_name|last_name|address|city~' => 'john "west peabody" smith.')
);

If another method of parsing search terms is required, it is also possible to specify the value of the conditions as an array of string to match. In this case no further parsing will be done.

There are two other operators available that work with multiple values, the &~ (AND LIKE) and >< (intersection) operators.

&~ accepts two or more LIKE patterns and requires that each values match every pattern. The example below would require that the persons email include both example.com and john:

$users = fRecordSet::build(
    'User',
    array('email&~' => array('example.com', 'john'))
);

The intersection operator works with ranges of values, so it is really only applicable to date/time and numeric fields. It requires two columns in the database table and two values to compare with. If the range of values in the two database columns in any way intersects the range between the two values specified, a match will be made.

For example, if there is an events table in the database that has a start and end date, it is possible to find any single or multi-day events that will occur during the next week with the following code:

$events = fRecordSet::build(
    'Event',
    array('start_date|end_date><' => array(new fDate(), new fDate('+7 days')))
);
OR Conditions

While adding key => value pairs to the conditions array always joins the conditions using AND logic, it is possible to create simple OR conditions too. If the array key is two or more column specifications with operators, separated by |, and the value is an array of values equal in size to the number of column specifications, an OR condition will be created.

The following example will return all users with the first name John or that were created after January 1st, 2008:

$users = fRecordSet::build(
    'User',
    array('first_name=|date_created>' => array('John', '2008-01-01'))
);

These OR conditions dont have any technical restrictions, however may become unwieldy to maintain if they are too complex.

Aggregate Functions

Aggregate functions are supported in place of single columns for all operators in the conditions array. Currently the aggregate functions AVG(), COUNT(), MAX(), MIN() and SUM() are supported. The following example will return all users who are part of two or more groups:

$users = fRecordSet::build(
    'User',
    array('count(groups.name)>=' => 2)
);

Conditions using aggregate functions will be automatically placed into the HAVING clause of the query that is executed.

Comparing Columns

In addition to being able to compare columns to values, it is also possible to compare two columns with each other. These comparisons use a slight modified version of the standard operators, where a : is appended.

// Find users who have only ever logged in when they created their account
$users = fRecordSet::build(
    'User',
    array('date_created=:' => 'last_login')
);

It is also possible to use the column comparison operators with aggregate functions.

Ordering

When building a record set from conditions, it is also possible to pass an array of $order_bys to specify the order in which the records are returned. The $order_bys parameter is an associative array of the column name, or expression, to order by as the key, and the direction to sort as the value.

The following example sort the users by their status, in an ascending manner:

$users = fRecordSet::build(
    'User',
    array('status=' => 'Active'),
    array('status' => 'asc')
);

In addition to being able to sort by columns, it is also possible to sort by expressions, such as CASE statements, and by aggregate functions, such as COUNT(), on related tables. The following example sorts by the number of groups the user is part of in a descending manner:

$users = fRecordSet::build(
    'User',
    array('status=' => 'Active'),
    array('count(groups.name)' => 'desc')
);

From Records

In certain situations it may be necessary to create a record set from record objects that have already been loaded from the database. The method buildFromArray() requires two parameters, the $class of record to create the set for and an array of $records to put in the set.

$users = fRecordSet::buildFromArray('User', array($user1, $user2, $user3));
Pagination

It is possible to pass information for use with pagination as the $total_records, $limit and $page parameters. These values power the methods getLimit(), getPage() and getPages(). The actual pagination of the records should either be done before the array is passed, or via the slice() method.

$users = fRecordSet::buildFromArray(
    'User',
    array($user1, $user2, $user3),
    8, // $total_records
    3, // $limit
    1  // $page
);
Different Record Classes

With buildFromArray() it is possible to create a record set from two or more different types of records. To create such a set, the $class parameter should be changed to an array containing each class name and the $records array should contain the different objects.

Please note that creating a record set from more than one kind of record will disable certain manipulation methods, such as retrieving primary keys and preloading related data. The section on manipulation contains details about what methods and features are unavailable in such a circumstance.

$calendar_objects = fRecordSet::buildFromArray(
    array('Event', 'Meeting'),
    array($event1, $meeting1, $event2, $event3)
);

From SQL

There are a number of limitations when using a conditions array to create a record set. Specific types of JOIN operations, GROUP BY clauses and OR conditions are impossible, or can not be tailored to the situation. In such instances, the buildFromSQL() method allows a raw Flourish SQL statement to be passed as the source for the records.

The first parameter is the $class of records to create and the second is the $sql to retrieve the records' data.

$users = fRecordSet::buildFromSQL(
    'User',
    "SELECT users.* FROM users WHERE (first_name = 'John' AND last_name = 'Smith') OR date_created < '2008-01-01'"
);

The SQL statement passed to buildFromSQL() should select every column for the table for the record type specified. It will often also be desired to ensure that statements using JOINs along many-to-many relationships have an appropriate GROUP BY clause to ensure that duplicate records are not returned.

Limit Clauses/Pagination

If a LIMIT clause is used in the SQL passed to buildFromSQL(), it is recommended to pass a third parameter, $non_limited_count_sql, containing a SQL query that will return the number of rows that would be returned if no LIMIT clause was present. This additional SQL statement powers the functionality of ->count(TRUE), which counts the number of records that would be returned with no LIMIT clause.

In addition to the $non_limited_count_sql, the $limit and $page parameters should be passed to power the getLimit(), getPage() and getPages() methods.

$users = fRecordSet::buildFromSQL(
    'User',
    "SELECT * FROM users LIMIT 5",
    "SELECT count(*) FROM users",
    5, // $limit
    1  // $page
);
Escaping Values into SQL

When using buildFromSQL(), you will usually need to include one or more dynamic values. Instead of passing a SQL string to $sql or $non_limited_count_sql, an array may be passed that contains a SQL string plus any values to escape into it.

$users = fRecordSet::buildFromSQL(
    'User',
    array("SELECT * FROM users WHERE date_created > %d LIMIT %i", $start_date, 5),
    array("SELECT count(*) FROM users WHERE date_created > %d", $start_date),
    5, // $limit
    1  // $page
);

From Call and Map

Results from both call and map operations can be turned into record sets by the buildFromCall() and buildFromMap() methods. These methods take the exact same parameters as call() and map(), but take a resulting array of fActiveRecord objects and turn them into an fRecordSet.

// This creates a record set of the owners for a set of
// resources by calling createUser() on each resource
$owners = $resources->buildFromCall('createUser');

// This uses a function to create a record
function make_record($class, $primary_key)
{
    return new $class($primary_key);
}
$owners = $resources->buildFromMap('make_record', 'User', '{record}::getOwner');

From Related Records

When two classes are related to each other through another table, building a record set from related records is often the easiest way to get what you are looking for. While is is possible to use buildFromCall() with a createRelatedRecord() method from the fActiveRecord class, there is a built-in dynamic build method that does all of this for you.

// Creating owners manually
$owners = $resources->buildFromCall('createUser');

// Creating them from the dynamic build method
$owners = $resources->buildUsers();

The dynamic build methods also take advantage of the preloading functionality to improve database performance.

Record Access

There are a number of different ways that records can be retrieved from a record set.

Iteration

One of the most basic operations for a record set is iteration. Just like an array, an fRecordSet can be used with a foreach loop to access each record sequentially.

foreach ($records as $record) {
    // Do something with the record
}

Individual Records

The method getRecord() will return the record at the index specified. This is good for retrieving a single record out of the set, but will throw an fNoRemainingException if there is no record to fetch.

$first_record = fRecordSet::build('User')->getRecord(0);

It is also possible to use array-style referencing to accomplish the same result as getRecord().

$first_record = $record_set[0];

All Records

To retrieve an array of all records in the set, simply call the method getRecords().

$records = $record_set->getRecords();

Metadata Access

In addition to retrieving the record from a record set, other information about the set and records is available.

Size

The size of a record set can often be important since it affect if code can be executed or not. The count() method gives a simple total containing the number of records in the set:

if ($records->count()) {
    echo 'Records were found!';
}

In the situation that a record set is actually a LIMITed number of records from the full query results, a single TRUE value can be passed to count() to retrieve the total number of records that exist.

echo $records->count() . ' records displayed, ' . $records->count(TRUE) . ' records total';

It is also possible to throw an exception if no records are contained in a set. The method tossIfEmpty() will throw an fEmptySetException if called on a record set with zero records. By default the exception will contain a message indicating that no matching records could be found. It is possible to set a custom message by passing it as the first parameter to tossIfEmpty().

// Throw a general exception message if no records were found
try {
    $records->tossIfEmpty()
} catch (fEmptySetException $e) {
    $e->printMessage();
}

// Throw a specific exception message
try {
    $records->tossIfEmpty('No active users could be found')
} catch (fEmptySetException $e) {
    $e->printMessage();
}

Limit

If a limit was specified when calling build(), it will be available from getLimit().

$limit = $record_set->getLimit();

Page/Pages

If a limit was specified when calling build(), it will be available from getPage().

$page = $record_set->getPage();

The number of pages will be available from getPages().

$total_pages = $record_set->getPages();

Primary Keys

If only the primary keys of the records are needed, the method getPrimaryKeys() does just that.

$primary_keys = $record_set->getPrimaryKeys();

Class Names

The method getClass() will return the class (or classes) of the record in the record set.

if ($record_set->getClass() == 'Example') {
    // ...
}

Manipulation

Once a record set has been created, it can be manipulated in a number of different ways to retrieve information. A number of array-like functions are built into the class, and there is support for the map, reduce and filter operations common in functional programming.

Map, Reduce, Filter and Call

The map, reduce and filter operations are declarative constructs that are common in functional programming. Map applies a function or method to a set of records and returns the results. Filter applies a function/method to each record and uses the return value to determine if a record should be removed. Reduce uses a function/method to convert all records into a single value via an iterative process of passing two values to the function at a time. Call is a feature implemented on fRecordSet that is not normally included with map, reduce and filter. Call allows a method to be called on every object in the record set, returning all of the values as an array.

Map

PHP include a built-in array_map() function that allows an array of values to be passed to a callback. This works great for callbacks that require only one parameter, however if two or more parameters are required then all parameters must be arrays of equal length. In practical terms, this requires heavy usage of array_fill() and leads to code that is a pain to write and difficult to read.

The map() method in fRecordSet provides some features to reduce the extra work. By default when calling map(), each record will be passed to the callback as the first parameter.

function convert_records($record)
{
    return new ArrayObject($record);
}

$array_objects = $record_set->map('convert_records');

If the callback takes more than one parameter, extra parameters can be passed to map(). Any parameter that is not an array will automatically be converted, so there is no need to calls to array_fill(). The example below would cause the $upper parameter to be set to TRUE for every record.

function camel_case($record, $upper)
{
    return fGrammar::camelize($record->getName(), $upper);
}

$names = $record_set->map('camel_case', TRUE);

Another important feature is that the output of a method call to each record can be passed to the callback by passing a string such as '{record}::methodName'. If this is the case, the record will not be automatically passed as the first parameter. The example below will cause the output of the getFirstName() method to be passed to fUTF8::lower():

$names = $record_set->map('fUTF8::lower', '{record}::getFirstName');

It is also possible to pass the complete record (as opposed to just the output of a method) in a position other than the first parameter by passing '{record}'.

function camel_case($upper, $record)
{
    return fGrammar::camelize($record->getName(), $upper);
}

$names = $record_set->map('camel_case', TRUE, '{record}');
Reduce

The reduce() method accepts a callback that accepts two values and iteratively performs an operation on the result of the last operation plus the next record. It uses the same dynamics as the function array_reduce(), except that the initial value can be of any data type.

The first call to the callback will pass NULL as the first parameter and the first record as the second parameter. All subsequent calls will pass the result of the last call as the first parameter and the next record as the second parameter.

function add_products($tally, $next_record)
{
    return $tally + $next_record->getPrice();
}
$sum = $record_set->reduce('add_products');

It is also possible to seed the operation with an initial value other than NULL by passing it as the second parameter to reduce().

function concat_names($string, $next_record)
{
    return $string . ' ' . $next_record->getName();
}
$names = $record_set->reduce('concat_names', 'Names:');
Filter

The filter() method allows for creating a new record set by removing records from the existing set by checking with a callback, testing the result of a method call on the record, or by comparing with a conditions array. It functions almost identically to array_filter(), except for supporting other methods of checking beyond a simple callback.

In the most basic form, a callback is passed and each record is passed one at a time to the callback. If the callback returns a value equal to FALSE the record will not be copied to the new record set.

function check_name($record)
{
    return (boolean) $record->getName();
}
$users_with_name = $record_set->filter('check_name');

It is also possible to filter a record set based on the return value of a method. This is accomplished by passing a string in the form '{record}::methodName'. The example below will remove all users with no middle initial:

$users = $record_set->filter('{record}::getMiddleInitial');

As a final option, it is also possible to filter records based on whether or not they match all of the conditions in a conditions array. The conditions array should contain keys that are method names followed by a comparison operator and values to make the comparison with. Below is an example of checking to see if a users status is 'Active':

$active_users = $record_set->filter(array('getStatus=' => 'Active'));

The following operators are supported for values that are not arrays:

If the value is an array, the following operators are supported:

The following example will filter out any users who dont have the first name John, who dont have the last name Smith or Henry or who were created on or before January 1st, 2008.

$users = $record_set->filter(
    array(
        'getFirstName='   => 'John',
        'getLastName='    => array('Smith', 'Henry'),
        'getDateCreated>' => '2008-01-01'
    )
);

It is also possible to do a search through the output of multiple method using the ~ operator. Method names should be separated by the | character.

If the value is a string, it will be parsed as search terms, which allows for quoted phrases, will removed punctuation from words and will ignore stop words (such as "the' and "a"). If the only words found are stop words, they will be included. If the value is an array, the strings in the array will be searched for.

The following example will find all users who have the words and phrases john, smith and west peabody in the output of any combination of getFirstName(), getLastName(), getAddress() and getCity():

$users = $record_set->filter(
    array('getFirstName|getLastName|getAddress|getCity~' => 'John Smith "west peabody"')
);

It is possible to perform an OR comparison using | separated list of method names with operators and the value to be an array of values, with one for each method. For example, the following code will check if getFirstName() returns John or getEmailAddress() contains smith.com:

$users = $record_set->filter(
    array('getFirstName=|getEmailAddress~' => array('John', 'smith.com'))
);

The last supported operator in the intersection operator ><. This operator checks to see if two columns that form a range intersect in any way with two values that form a range. This is most useful for dates, but can be used with numbers too.

The following example checks to see if getStartDate() and getEndDate() form a date range that includes any days between January 1, 2010 and January 3, 2010:

$users = $record_set->filter(
    array('getStartDate|getEndDate><' => array(new fDate('2010-01-01'), new fDate('2010-01-03')))
);

The optional boolean second parameter, $remember_original_count, will save the number of records in the current record set as the non-limited count on the new set. See the section on Size for details about how to retrieve this number.

$users = $record_set->filter('{record}::getMiddleInitial', TRUE);
Call

The call() method of fRecordSet returns an array of the return values from a call to a method on each record in the set. The first parameter is a string with the method to call.

$first_names = $record_set->call('getFirstName');

It is also possible to pass parameters to the method by adding them to the call() method. The following example will pass TRUE to each call of the method prepareLink():

$links = $record_set->call('prepareLink', TRUE);

Preloading Related Records

While the fRecordSet class uses a minimal number of database queries to fetch a set of records, in turn getting records related to each of those will cause at least one database query to be executed per record. The three actions prebuild, precount and precreate all allow for running a single database query to fetch records related to every record in the set.

For example, if a record set of users needs to be displayed included how many comments they have left on a blog, the blog comments for each will need to be counted. Normally a call to countBlogComments() be all that is necessary. However, if 100 users are displayed on a page then an additional 100 database queries would be performed. Calling precountBlogComments() on the record set would cause a single database query to be executed to collect the counts for each user.

// Count related comments for ever user in a single query
$record_set->precountBlogComments();

The prebuild method action would be appropriate to call when it is necessary to build a set of related records that are in a many-to-many or one-to-many.

$record_set->prebuildBlogComments();
foreach ($record_set as $record) {
    $comments = $record_set->buildBlogComments();
}

The precreate method action allows creating objects from a column that is part of a FOREIGN KEY constraint.

$record_set->precreateStates();
foreach ($record_set as $record) {
    echo $record->createState()->prepareName();
}

Slice, Merge, Diff, Intersect, Unique and Contains Operations

In a manner similar to arrays, an fRecord contains quite a number of methods to add, remove and change records in a record set.

slice()

The slice() method takes up to two parameters, the zero-based $offset to start slicing at and the $length of a slice to make. If the $offset is negative, the slice will start that many records from the end of the set. If the $length is negative, the slice will stop that many records from the end of the set. If the $length is omitted or NULL, all records until the end of the set will be returned.

The following will create a new record set from the first three records.

$new_set = $record_set->slice(0, 3);

The optional boolean third parameter, $remember_original_count, will save the number of records in the current record set as the non-limited count on the new set. See the section on Size for details about how to retrieve this number.

$new_set = $record_set->slice(0, 3, TRUE);

If $remember_original_count is TRUE and the slicing is done in such a way that a valid $limit and $page can be determined, they will be appropriately set on the new record set.

merge()

The merge() method accepts a single parameter, the $records, and returns a new record set containing all records from both. All of the records from the second record set will be found after the records from the first. The $records parameter will accept an fRecordSet, an array of fActiveRecord objects or a single fActiveRecord.

Please note that it is possible to merge records sets of different types of records. If a record set contains records of more than one class, however, the prebuild, precount and precreate method actions will be unavailable, along with the methods getPrimaryKeys(), flagAssociate() and isFlaggedForAssociation().

$events_and_meetings = $events->merge($meetings);
diff()

The diff() method accepts a single parameter, the $records to remove from the current record set. The $records parameter will accept an fRecordSet, an array of fActiveRecord objects or a single fActiveRecord.

$active_events = $events->diff($inactive_events);

The optional boolean second parameter, $remember_original_count, will save the number of records in the current record set as the non-limited count on the new set. See the section on Size for details about how to retrieve this number.

$active_events = $events->diff($inactive_events, TRUE);
intersect()

The intersect() method accepts a single parameter, the $records to create an intersection with the current record set. All records not in both will be removed. The $records parameter will accept an fRecordSet, an array of fActiveRecord objects or a single fActiveRecord.

$our_free_days = $my_free_days->intersect($your_free_days);

The optional boolean second parameter, $remember_original_count, will save the number of records in the current record set as the non-limited count on the new set. See the section on Size for details about how to retrieve this number.

$our_free_days = $my_free_days->intersect($your_free_days, TRUE);
unique()

The unique() method takes the current record set and removes all duplicate records, returning a new record set.

$sessions = $sessions->unique();

The optional boolean parameter, $remember_original_count, will save the number of records in the current record set as the non-limited count on the new set. See the section on Size for details about how to retrieve this number.

$sessions = $sessions->unique(TRUE);

Chunk and Split Operations

For display purposes, it can be useful to segment an fRecordSet into multiple smaller fRecordSet objects. The chunk() and split() methods to exactly this.

chunk()

The chunk() method accepts a $number of records to place in each resulting fRecordSet. The returned value will be an array of fRecordSet objects that each contain $number records, although the last one may have fewer if there are not enough to fill it.

// This statement will segment 10 users into 4 records sets containing 3, 3, 3 and 1 users respectively
$sets = $users->chunk(3);
split()

The split() method accepts a $number of fRecordSet objects to return. The returned value will be an array of fRecordSet objects that each contain ceil(total records/$number) records, although the last set may have fewer if there are not enough to fill it.

// This statement will split 10 users into 3 records sets containing 4, 4 and 2 users respectively
$sets = $users->split(3);

Checking for a Record

The contains() method accepts a single fActiveRecord record and checks if is is present in the record set.

if ($users->contains($user)) {
    // ...
}

Sorting

When building a record set from conditions, the records can be sorted by the $order_bys parameter. See the section Ordering for more details.

Two methods are available with fRecordSet to reorder the records in the set after it has been created. The method sort() accepts two parameters, the $method to call to get the value to compare, and the $direction to sort those values in. The $direction can be either 'asc' or 'desc'. The sorting is done using fUTF8::inatcmp() for comparison. A new sorted fRecordSet object is returned.

$sorted_set = $record_set->sort('getName', 'asc');

If a different sorting method is required, the method sortByCallback() will be of interest. This method requires a single parameter, a $callback that accepts two records and returns a negative value if the first record is less than the second, 0 if they are equal, or a positive value if the first record is greater than the second.

function method_sort($record_a, $record_b)
{
    return strnatcasecmp($record_a->getFirstName(), $record_b->getFirstName());
}

$sorted_set = $record_set->sortByCallback('method_sort');

fRecordSet API Reference

class, implements IteratorAggregate ArrayAccess Countable , v1.0.0b47

A lightweight, iterable set of fActiveRecord-based objects

Changes:
1.0.0b47Fixed the new version of precount() to work with tables having an explicit schema 9/21/12
1,0,0b46Fixed a bug with precount() not working for self-joining tables 9/16/12
1.0.0b45Added support for the starts with like, ^~, and ends with like, $~, operators to both build() and filter() 6/20/11
1.0.0b44Backwards Compatibility Break - sort() and sortByCallback() now return a new fRecordSet instead of sorting the record set in place 6/20/11
1.0.0b43Added the ability to pass SQL and values to buildFromSQL(), added the ability to manually pass the $limit and $page to buildFromArray() and buildFromSQL(), changed slice() to remember $limit and $page if possible when $remember_original_count is TRUE 1/11/11
1.0.0b42Updated class to use fORM::getRelatedClass() 11/24/10
1.0.0b41Added support for PHP 5.3 namespaced fActiveRecord classes 11/11/10
1.0.0b40Added the tally() method 9/28/10
1.0.0b39Backwards Compatibility Break - removed the methods fetchRecord(), http://www.php.net/current, http://www.php.net/key, http://www.php.net/next, http://www.php.net/rewind and valid() and the Iterator interface - and the $pointer parameter for callbacks registered via fORM::registerRecordSetMethod() was replaced with the $method_name parameter - added the methods getIterator(), getLimit(), getPage(), getPages(), getRecord(), offsetExists(), offsetGet(), offsetSet() and offsetUnset() and the IteratorAggregate and ArrayAccess interfaces 9/28/10
1.0.0b38Updated code to work with the new fORM API 8/6/10
1.0.0b37Fixed a typo/bug in reduce() 6/30/10
1.0.0b36Replaced create_function() with a private method call 6/8/10
1.0.0b35Added the chunk() and split() methods 5/20/10
1.0.0b34Added an integer cast to count() to fix issues with the dblib MSSQL driver 4/9/10
1.0.0b33Updated the class to force configure classes before peforming actions with them 3/30/10
1.0.0b32Fixed a column aliasing issue with SQLite 1/25/10
1.0.0b31Added the ability to compare columns in build() with the =:, !:, <:, <=:, >: and >=: operators 12/8/09
1.0.0b30Fixed a bug affecting where conditions with columns that are not null but have a default value 11/3/09
1.0.0b29Updated code for the new fORMDatabase and fORMSchema APIs 10/28/09
1.0.0b28Fixed prebuild() and precount() to work across all databases, changed SQL statements to use value placeholders, identifier escaping and schema support 10/22/09
1.0.0b27Changed fRecordSet::build() to fix bad $page numbers instead of throwing an fProgrammerException 10/5/09
1.0.0b26Updated the documentation for build() and filter() to reflect new functionality 9/21/09
1.0.0b25Fixed map() to work with string-style static method callbacks in PHP 5.1 9/18/09
1.0.0b24Backwards Compatibility Break - renamed buildFromRecords() to buildFromArray(). Added buildFromCall(), buildFromMap() and ::build{RelatedRecords}() 9/16/09
1.0.0b23Added an extra parameter to diff(), filter(), intersect(), slice() and unique() to save the number of records in the current set as the non-limited count for the new set 9/15/09
1.0.0b22Changed __construct() to accept any Iterator instead of just an fResult object 8/12/09
1.0.0b21Added performance tweaks to prebuild() and precreate() 7/31/09
1.0.0b20Changed the class to implement Countable, making the count() function work 7/29/09
1.0.0b19Fixed bugs with diff() and intersect() and empty record sets 7/29/09
1.0.0b18Added method chaining support to prebuild, precount and precreate methods 7/15/09
1.0.0b17Changed __call() to pass the parameters to the callback 7/14/09
1.0.0b16Updated documentation for the intersection operator >< 7/13/09
1.0.0b15Added the methods diff() and intersect() 7/13/09
1.0.0b14Added the methods contains() and unique() 7/9/09
1.0.0b13Added documentation to build() about the intersection operator >< 7/9/09
1.0.0b12Added documentation to build() about the AND LIKE operator &~ 7/9/09
1.0.0b11Added documentation to build() about the NOT LIKE operator !~ 7/8/09
1.0.0b10Moved the private method checkConditions() to fActiveRecord::checkConditions() 7/8/09
1.0.0b9Changed build() to only fall back to ordering by primary keys if one exists 6/26/09
1.0.0b8Updated merge() to accept arrays of fActiveRecords or a single fActiveRecord in addition to an fRecordSet 6/2/09
1.0.0b7Backwards Compatibility Break - Removed flagAssociate() and isFlaggedForAssociation(), callbacks registered via fORM::registerRecordSetMethod() no longer receive the $associate parameter 6/2/09
1.0.0b6Changed tossIfEmpty() to return the record set to allow for method chaining 5/18/09
1.0.0b5build() now allows NULL for $where_conditions and $order_bys, added a check to the SQL passed to buildFromSQL() 5/3/09
1.0.0b4__call() was changed to prevent exceptions coming from fGrammar when an unknown method is called 3/27/09
1.0.0b3sort() and sortByCallback() now return the record set to allow for method chaining 3/23/09
1.0.0b2Added support for != and <> to build() and filter() 12/4/08
1.0.0bThe initial implementation 8/4/07

Static Methods

::build() public

Creates an fRecordSet by specifying the class to create plus the where conditions and order by rules

The where conditions array can contain key => value entries in any of the following formats:

'column='                    => VALUE,                       // column = VALUE
'column!'                    => VALUE                        // column <> VALUE
'column!='                   => VALUE                        // column <> VALUE
'column<>'                   => VALUE                        // column <> VALUE
'column~'                    => VALUE                        // column LIKE '%VALUE%'
'column^~'                   => VALUE                        // column LIKE 'VALUE%'
'column$~'                   => VALUE                        // column LIKE '%VALUE'
'column!~'                   => VALUE                        // column NOT LIKE '%VALUE%'
'column<'                    => VALUE                        // column < VALUE
'column<='                   => VALUE                        // column <= VALUE
'column>'                    => VALUE                        // column > VALUE
'column>='                   => VALUE                        // column >= VALUE
'column=:'                   => 'other_column'               // column = other_column
'column!:'                   => 'other_column'               // column <> other_column
'column!=:'                  => 'other_column'               // column <> other_column
'column<>:'                  => 'other_column'               // column <> other_column
'column<:'                   => 'other_column'               // column < other_column
'column<=:'                  => 'other_column'               // column <= other_column
'column>:'                   => 'other_column'               // column > other_column
'column>=:'                  => 'other_column'               // column >= other_column
'column='                    => array(VALUE, VALUE2, ... )   // column IN (VALUE, VALUE2, ... )
'column!'                    => array(VALUE, VALUE2, ... )   // column NOT IN (VALUE, VALUE2, ... )
'column!='                   => array(VALUE, VALUE2, ... )   // column NOT IN (VALUE, VALUE2, ... )
'column<>'                   => array(VALUE, VALUE2, ... )   // column NOT IN (VALUE, VALUE2, ... )
'column~'                    => array(VALUE, VALUE2, ... )   // (column LIKE '%VALUE%' OR column LIKE '%VALUE2%' OR column ... )
'column^~'                   => array(VALUE, VALUE2, ... )   // (column LIKE 'VALUE%' OR column LIKE 'VALUE2%' OR column ... )
'column$~'                   => array(VALUE, VALUE2, ... )   // (column LIKE '%VALUE' OR column LIKE '%VALUE2' OR column ... )
'column&~'                   => array(VALUE, VALUE2, ... )   // (column LIKE '%VALUE%' AND column LIKE '%VALUE2%' AND column ... )
'column!~'                   => array(VALUE, VALUE2, ... )   // (column NOT LIKE '%VALUE%' AND column NOT LIKE '%VALUE2%' AND column ... )
'column!|column2<|column3='  => array(VALUE, VALUE2, VALUE3) // (column <> '%VALUE%' OR column2 < '%VALUE2%' OR column3 = '%VALUE3%')
'column|column2><'           => array(VALUE, VALUE2)         // WHEN VALUE === NULL: ((column2 IS NULL AND column = VALUE) OR (column2 IS NOT NULL AND column <= VALUE AND column2 >= VALUE))
                                                             // WHEN VALUE !== NULL: ((column <= VALUE AND column2 >= VALUE) OR (column >= VALUE AND column <= VALUE2))
'column|column2|column3~'    => VALUE                        // (column LIKE '%VALUE%' OR column2 LIKE '%VALUE%' OR column3 LIKE '%VALUE%')
'column|column2|column3~'    => array(VALUE, VALUE2, ... )   // ((column LIKE '%VALUE%' OR column2 LIKE '%VALUE%' OR column3 LIKE '%VALUE%') AND (column LIKE '%VALUE2%' OR column2 LIKE '%VALUE2%' OR column3 LIKE '%VALUE2%') AND ... )

When creating a condition in the form column|column2|column3~, if the value for the condition is a single string that contains spaces, the string will be parsed for search terms. The search term parsing will handle quoted phrases and normal words and will strip punctuation and stop words (such as "the" and "a").

The order bys array can contain key => value entries in any of the following formats:

'column'     => 'asc'      // 'first_name' => 'asc'
'column'     => 'desc'     // 'last_name'  => 'desc'
'expression' => 'asc'      // "CASE first_name WHEN 'smith' THEN 1 ELSE 2 END" => 'asc'
'expression' => 'desc'     // "CASE first_name WHEN 'smith' THEN 1 ELSE 2 END" => 'desc'

The column in both the where conditions and order bys can be in any of the formats:

'column'                                                         // e.g. 'first_name'
'current_table.column'                                           // e.g. 'users.first_name'
'related_table.column'                                           // e.g. 'user_groups.name'
'related_table{route}.column'                                    // e.g. 'user_groups{user_group_id}.name'
'related_table=>once_removed_related_table.column'               // e.g. 'user_groups=>permissions.level'
'related_table{route}=>once_removed_related_table.column'        // e.g. 'user_groups{user_group_id}=>permissions.level'
'related_table=>once_removed_related_table{route}.column'        // e.g. 'user_groups=>permissions{read}.level'
'related_table{route}=>once_removed_related_table{route}.column' // e.g. 'user_groups{user_group_id}=>permissions{read}.level'
'column||other_column'                                           // e.g. 'first_name||last_name' - this concatenates the column values

In addition to using plain column names for where conditions, it is also possible to pass an aggregate function wrapped around a column in place of a column name, but only for certain comparison types. Note that for column comparisons, the function may be placed on either column or both.

'function(column)='   => VALUE,                       // function(column) = VALUE
'function(column)!'   => VALUE                        // function(column) <> VALUE
'function(column)!=   => VALUE                        // function(column) <> VALUE
'function(column)<>'  => VALUE                        // function(column) <> VALUE
'function(column)~'   => VALUE                        // function(column) LIKE '%VALUE%'
'function(column)^~'  => VALUE                        // function(column) LIKE 'VALUE%'
'function(column)$~'  => VALUE                        // function(column) LIKE '%VALUE'
'function(column)!~'  => VALUE                        // function(column) NOT LIKE '%VALUE%'
'function(column)<'   => VALUE                        // function(column) < VALUE
'function(column)<='  => VALUE                        // function(column) <= VALUE
'function(column)>'   => VALUE                        // function(column) > VALUE
'function(column)>='  => VALUE                        // function(column) >= VALUE
'function(column)=:'  => 'other_column'               // function(column) = other_column
'function(column)!:'  => 'other_column'               // function(column) <> other_column
'function(column)!=:' => 'other_column'               // function(column) <> other_column
'function(column)<>:' => 'other_column'               // function(column) <> other_column
'function(column)<:'  => 'other_column'               // function(column) < other_column
'function(column)<=:' => 'other_column'               // function(column) <= other_column
'function(column)>:'  => 'other_column'               // function(column) > other_column
'function(column)>=:' => 'other_column'               // function(column) >= other_column
'function(column)='   => array(VALUE, VALUE2, ... )   // function(column) IN (VALUE, VALUE2, ... )
'function(column)!'   => array(VALUE, VALUE2, ... )   // function(column) NOT IN (VALUE, VALUE2, ... )
'function(column)!='  => array(VALUE, VALUE2, ... )   // function(column) NOT IN (VALUE, VALUE2, ... )
'function(column)<>'  => array(VALUE, VALUE2, ... )   // function(column) NOT IN (VALUE, VALUE2, ... )

The aggregate functions AVG(), COUNT(), MAX(), MIN() and SUM() are supported across all database types.

Below is an example of using where conditions and order bys. Please note that values should not be escaped for the database, but should just be normal PHP values.

return fRecordSet::build(
    'User',
    array(
        'first_name='      => 'John',
        'status!'          => 'Inactive',
        'groups.group_id=' => 2
    ),
    array(
        'last_name'   => 'asc',
        'date_joined' => 'desc'
    )
);
Signature

fRecordSet build( string $class, array $where_conditions=array(), array $order_bys=array(), integer $limit=NULL, integer $page=NULL )

Parameters
string $class The class to create the fRecordSet of
array $where_conditions The column => value comparisons for the WHERE clause
array $order_bys The column => direction values to use for the ORDER BY clause
integer $limit The number of records to fetch
integer $page The page offset to use when limiting records
Returns

A set of fActiveRecord objects

::buildFromArray() internal public

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 an fRecordSet from an array of records

Signature

fRecordSet buildFromArray( string|array $class, array $records, integer $total_records=NULL, integer $limit=NULL, integer $page=1 )

Parameters
string|array $class The class or classes of the records
array $records The records to create the set from, the order of the record set will be the same as the order of the array.
integer $total_records The total number of records - this should only be provided if the array is a segment of a larger array - this is informational only and does not affect the array
integer $limit The maximum number of records the array was limited to - this is informational only and does not affect the array
integer $page The page of records the array is from - this is informational only and does not affect the array
Returns

A set of fActiveRecord objects

::buildFromSQL() public

Creates an fRecordSet from an SQL statement

The SQL statement should select all columns from a single table with a * pattern since that is what an fActiveRecord models. If any columns are left out or added, strange error may happen when loading or saving records.

Here is an example of an appropriate SQL statement:

SELECT users.* FROM users INNER JOIN groups ON users.group_id = groups.group_id WHERE groups.name = 'Public'

Here is an example of a SQL statement that will cause errors:

SELECT users.*, groups.name FROM users INNER JOIN groups ON users.group_id = groups.group_id WHERE groups.group_id = 2

The $non_limited_count_sql should only be passed when the $sql contains a LIMIT clause and should contain a count of the records when a LIMIT is not imposed.

Here is an example of a $sql statement with a LIMIT clause and a corresponding $non_limited_count_sql:

fRecordSet::buildFromSQL('User', 'SELECT * FROM users LIMIT 5', 'SELECT count(*) FROM users');

The $non_limited_count_sql is used when count() is called with TRUE passed as the parameter.

Both the $sql and $non_limited_count_sql can be passed as a string SQL statement, or an array containing a SQL statement and the values to escape into it:

fRecordSet::buildFromSQL(
    'User',
    array("SELECT * FROM users WHERE date_created > %d LIMIT %i OFFSET %i", $start_date, 10, 10*($page-1)),
    array("SELECT * FROM users WHERE date_created > %d", $start_date),
    10,
    $page
)
Signature

fRecordSet buildFromSQL( string $class, string|array $sql, string|array $non_limited_count_sql=NULL, integer $limit=NULL, integer $page=1 )

Parameters
string $class The class to create the fRecordSet of
string|array $sql The SQL to create the set from, or an array of the SQL statement plus values to escape
string|array $non_limited_count_sql An SQL statement, or an array of the SQL statement plus values to escape, to get the total number of rows that would have been returned if a LIMIT clause had not been used. Should only be passed if a LIMIT clause is used in $sql.
integer $limit The number of records the SQL statement was limited to - this is information only and does not affect the SQL
integer $page The page of records the SQL statement returned - this is information only and does not affect the SQL
Returns

A set of fActiveRecord objects

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::tally() public

Counts the number of records that match the conditions specified

Signature

integer tally( string $class, mixed $where_conditions=array() )

Parameters
string $class The class of records to count
mixed $where_conditions An array of where clause parameters in the same format as build()
Returns

The number of records

Methods

->__construct() protected

Sets the contents of the set

Signature

fRecordSet __construct( string|array $class, Iterator|array $records=NULL, string|integer $non_limited_count=NULL, integer $limit=NULL, integer $page=1 )

Parameters
string|array $class The type(s) of records the object will contain
Iterator|array $records The Iterator object of the records to create or an array of records
string|integer $non_limited_count An SQL statement to get the total number of records sans a LIMIT clause or a integer of the total number of records
integer $limit The number of records the set was limited to
integer $page The page of records that was built
->__call() public

Allows for preloading various data related to the record set in single database queries, as opposed to one query per record

This method will handle methods in the format verbRelatedRecords() for the verbs build, prebuild, precount and precreate.

build calls create{RelatedClass}() on each record in the set and returns the result as a new record set. The relationship route can be passed as an optional parameter.

prebuild builds *-to-many record sets for all records in the record set. precount will count records in *-to-many record sets for every record in the record set. precreate will create a *-to-one record for every record in the record set.

Signature

void __call( string $method_name, string $parameters )

Parameters
string $method_name The name of the method called
string $parameters The parameters passed
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->buildFromCall() public

Calls a specific method on each object, returning an fRecordSet of the results

Signature

fRecordSet buildFromCall( string $method, mixed $parameter [, ... ] )

Parameters
string $method The method to call
mixed $parameter [, ... ] A parameter to pass for each call to the method
Returns

A set of records that resulted from calling the method

->buildFromMap() public

Maps each record in the set to a callback function, returning an fRecordSet of the results

Signature

fRecordSet buildFromMap( callback $callback, mixed $parameter [, ... ] )

Parameters
callback $callback The callback to pass the values to
mixed $parameter [, ... ] The parameter to pass to the callback - see method description for details
Returns

A set of records that resulted from the mapping operation

->call() public

Calls a specific method on each object, returning an array of the results

Signature

array call( string $method, mixed $parameter [, ... ] )

Parameters
string $method The method to call
mixed $parameter [, ... ] A parameter to pass for each call to the method
Returns

An array the size of the record set with one result from each record/method

->chunk() public

Chunks the record set into an array of fRecordSet objects

Each fRecordSet would contain $number records, except for the last, which will contain between 1 and $number records.

Signature

array chunk( integer $number )

Parameters
integer $number The number of fActiveRecord objects to place in each fRecordSet
Returns

An array of fRecordSet objects

->contains() public

Checks if the record set contains the record specified

Signature

boolean contains( fActiveRecord $record )

Parameters
fActiveRecord $record The record to check, must exist in the database
Returns

If the record specified is in this record set

->count() public implements Countable

Returns the number of records in the set

Signature

integer count( boolean $ignore_limit=FALSE )

Parameters
boolean $ignore_limit If set to TRUE, this method will return the number of records that would be in the set if there was no LIMIT clause
Returns

The number of records in the set

->diff() public

Removes all passed records from the current record set

Signature

fRecordSet diff( fRecordSet|array|fActiveRecord $records, boolean $remember_original_count=FALSE )

Parameters
fRecordSet|array|fActiveRecord $records The record set, array of records, or record to remove from the current record set, all instances will be removed
boolean $remember_original_count If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to 1 either way
Returns

The records not present in the passed records

->filter() public

Filters the records in the record set via a callback

The $callback parameter can be one of three different forms to filter the records in the set:

  • A callback that accepts a single record and returns FALSE if it should be removed
  • A psuedo-callback in the form '{record}::methodName' to filter out any records where the output of $record->methodName() is equivalent to FALSE
  • A conditions array that will remove any records that don't meet all of the conditions

The conditions array can use one or more of the following key => value syntaxes to perform various comparisons. The array keys are method names followed by a comparison operator.

// The following forms work for any $value that is not an array
'methodName='                           => $value  // If the output is equal to $value
'methodName!'                           => $value  // If the output is not equal to $value
'methodName!='                          => $value  // If the output is not equal to $value
'methodName<>'                          => $value  // If the output is not equal to $value
'methodName<'                           => $value  // If the output is less than $value
'methodName<='                          => $value  // If the output is less than or equal to $value
'methodName>'                           => $value  // If the output is greater than $value
'methodName>='                          => $value  // If the output is greater than or equal to $value
'methodName~'                           => $value  // If the output contains the $value (case insensitive)
'methodName^~'                          => $value  // If the output starts with the $value (case insensitive)
'methodName$~'                          => $value  // If the output ends with the $value (case insensitive)
'methodName!~'                          => $value  // If the output does not contain the $value (case insensitive)
'methodName|methodName2|methodName3~'   => $value  // Parses $value as a search string and make sure each term is present in at least one output (case insensitive)
// The following forms work for any $array that is an array
'methodName='                           => $array  // If the output is equal to at least one value in $array
'methodName!'                           => $array  // If the output is not equal to any value in $array
'methodName!='                          => $array  // If the output is not equal to any value in $array
'methodName<>'                          => $array  // If the output is not equal to any value in $array
'methodName~'                           => $array  // If the output contains one of the strings in $array (case insensitive)
'methodName^~'                          => $array  // If the output starts with one of the strings in $array (case insensitive)
'methodName$~'                          => $array  // If the output ends with one of the strings in $array (case insensitive)
'methodName!~'                          => $array  // If the output contains none of the strings in $array (case insensitive)
'methodName&~'                          => $array  // If the output contains all of the strings in $array (case insensitive)
'methodName|methodName2|methodName3~'   => $array  // If each value in the array is present in the output of at least one method (case insensitive)
// The following works for an equal number of methods and values in the array
'methodName!|methodName2<|methodName3=' => array($value, $value2, $value3) // An OR statement - one of the method to value comparisons must be TRUE
// The following accepts exactly two methods and two values, although the second value may be NULL
'methodName|methodName2><'              => array($value, $value2) // If the range of values from the methods intersects the range of $value and $value2 - should be dates, times, timestamps or numbers
Signature

fRecordSet filter( callback|string|array $procedure, boolean $remember_original_count=FALSE )

Parameters
callback|string|array $procedure The way in which to filter the records - see method description for possible forms
boolean $remember_original_count If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to 1 either way
Returns

A new fRecordSet with the filtered records

->getClass() public

Returns the class name of the record being stored

Signature

string|array getClass( )

Returns

The class name(s) of the records in the set

->getIterator() internal public implements IteratorAggregate

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

Returns an iterator for the record set

This method is required by the IteratorAggregate interface.

Signature

ArrayIterator getIterator( )

Returns

An iterator for the record set

->getLimit() public

Returns the number of records the set was limited to

Signature

integer getLimit( )

Returns

The number of records the set was limited to

->getPage() public

Returns the page of records this set represents

Signature

integer getPage( )

Returns

The page of records this set represents

->getPages() public

Returns the number of pages of records exist for the limit used when creating this set

Signature

integer getPages( )

Returns

The number of pages of records that exist for the limit specified

->getPrimaryKeys() public

Returns the primary keys for all of the records in the set

Signature

array getPrimaryKeys( )

Returns

The primary keys of all the records in the set

->getRecord() public

Returns the record at the zero-based index specified

Signature

fActiveRecord getRecord( integer $index )

Parameters
integer $index The index of the record to return
Returns

The record requested

->getRecords() public

Returns all of the records in the set

Signature

array getRecords( )

Returns

The records in the set

->intersect() public

Returns all records in the current record set that are also present in the passed records

Signature

fRecordSet intersect( fRecordSet|array|fActiveRecord $records, boolean $remember_original_count=FALSE )

Parameters
fRecordSet|array|fActiveRecord $records The record set, array of records, or record to create an intersection of with the current record set
boolean $remember_original_count If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to 1 either way
Returns

The records present in the current record set that are also present in the passed records

->map() public

Performs an array_map() on the record in the set

The record will be passed to the callback as the first parameter unless it's position is specified by the placeholder string '{record}'.

Additional parameters can be passed to the callback in one of two different ways:

  • Passing a non-array value will cause it to be passed to the callback
  • Passing an array value will cause the array values to be passed to the callback with their corresponding record

If an array parameter is too long (more items than records in the set) it will be truncated. If an array parameter is too short (less items than records in the set) it will be padded with NULL values.

To allow passing the record as a specific parameter to the callback, a placeholder string '{record}' will be replaced with a the record. It is also possible to specify '{record}::methodName' to cause the output of a method from the record to be passed instead of the whole record.

It is also possible to pass the zero-based record index to the callback by passing a parameter that contains '{index}'.

Signature

array map( callback $callback, mixed $parameter [, ... ] )

Parameters
callback $callback The callback to pass the values to
mixed $parameter [, ... ] The parameter to pass to the callback - see method description for details
Returns

An array of the results from the callback

->merge() public

Merges the record set with more records

Signature

fRecordSet merge( fRecordSet|array|fActiveRecord $records )

Parameters
fRecordSet|array|fActiveRecord $records The record set, array of records, or record to merge with the current record set, duplicates will not be removed
Returns

The merged record sets

->offsetExists() internal public implements ArrayAccess

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 offset exists

This method is required by the ArrayAccess interface.

Signature

boolean offsetExists( mixed $offset )

Parameters
mixed $offset The offset to check
Returns

If the offset exists

->offsetGet() internal public implements ArrayAccess

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

Returns a record based on the offset

This method is required by the ArrayAccess interface.

Signature

fActiveRecord offsetGet( mixed $offset )

Parameters
mixed $offset The offset of the record to get
Returns

The requested record

Throws
fNoRemainingException
When the offset specified is beyond the last record
->offsetSet() internal public implements ArrayAccess

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

Prevents setting values to the record set

This method is required by the ArrayAccess interface.

Signature

void offsetSet( mixed $offset, mixed $value )

Parameters
mixed $offset The offset to set
mixed $value The value to set to the offset
->offsetUnset() internal public implements ArrayAccess

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

Prevents unsetting values from the record set

This method is required by the ArrayAccess interface.

Signature

void offsetUnset( mixed $offset )

Parameters
mixed $offset The offset to unset
->reduce() public

Reduces the record set to a single value via a callback

The callback should take two parameters and return a single value:

  • The initial value and the first record for the first call
  • The result of the last call plus the next record for the second and subsequent calls
function my_reduce($sum, $record)
{
    return $sum + $record->getQuantity();
)
// For the first record, 0.0 will be passed as the $sum, then subsequent
// calls end up getting the return value of the last call to my_reduce()
$total_quantity = $record_set->reduce('my_reduce', 0.0);
Signature

mixed reduce( callback $callback, mixed $initial_value=NULL )

Parameters
callback $callback The callback to pass the records to - see method description for details
mixed $initial_value The initial value to seed reduce with
Returns

The result of the reduce operation

->slice() public

Slices a section of records from the set and returns a new set containing those

Signature

fRecordSet slice( integer $offset, integer $length=NULL, boolean $remember_original_count=FALSE )

Parameters
integer $offset The index to start at, negative indexes will slice that many records from the end
integer $length The number of records to return, negative values will stop that many records before the end, NULL will return all records to the end of the set - if there are not enough records, less than $length will be returned
boolean $remember_original_count If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to 1 either way
Returns

The new slice of records

->sort() public

Sorts the set by the return value of a method from the class created and rewind the interator

This methods uses fUTF8::inatcmp() to perform comparisons.

Signature

fRecordSet sort( string $method, string $direction )

Parameters
string $method The method to call on each object to get the value to sort by
string $direction Either 'asc' or 'desc'
Returns

A new record set object, with the records sorted as requested

->sortByCallback() public

Sorts the set by passing the callback to usort() and rewinds the interator

Signature

fRecordSet sortByCallback( mixed $callback )

Parameters
mixed $callback The function/method to pass to usort()
Returns

A new record set object, with the records sorted as requested

->split() public

Splits the record set into an array of fRecordSet objects

Each fRecordSet would contain ceil(number of records/$number) records, except for the last, which will contain between 1 and ceil(…) records.

Signature

array split( integer $number )

Parameters
integer $number The number of fRecordSet objects to create
Returns

An array of fRecordSet objects

->tossIfEmpty() public

Throws an fEmptySetException if the record set is empty

Signature

fRecordSet tossIfEmpty( string $message=NULL )

Parameters
string $message The message to use for the exception if there are no records in this set
Returns

The record set object, to allow for method chaining

Throws
fEmptySetException
When there are no record in the set
->unique() public

Returns a new fRecordSet containing only unique records in the record set

Signature

fRecordSet unique( boolean $remember_original_count=FALSE )

Parameters
boolean $remember_original_count If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to 1 either way
Returns

The new record set with only unique records

fRequest

The fRequest class is a static class that allows access to data sent via GET, POST, PUT and DELETE HTTP requests. It also provides functionality to determine the HTTP method used and retrieve relevant Accept and Accept-Language values sent by the browser.

Getting Values

While fRequest does more than just get values, most developers will be primarily interested in the two methods get() and getValid(). Both methods pull values from the $_GET and $_POST superglobals and the php://input stream (for PUT and DELETE requests).

The get() method will pull a value, but you can also typecast the value and provide a default. The first parameter, $key, specifies the key to get the value for. The second (optional) parameter, $cast_to, will set the data type of the value. It accepts any type string that is valid for the PHP function settype(). NULL can be used if no type casting should happen.

The third (optional) parameter, $default_value, specifies what should be returned if the key is not found in $_POST, $_GET or php://input. The fourth (optional) parameter, $use_default_for_blank, causes the $default value to be returned if a blank string value was provided for the key, or the key is not found.

Here are some examples:

<?php
// The first_name field will be returned in whatever format was submitted - string or array
$first_name = fRequest::get('first_name');

// An array will always be returned, defaulting to an array of 1 if no value is provided
$group_ids  = fRequest::get('group_ids', 'array', array(1));

// Integer return value - 1 will be returned if no value, but a blank value will be cast to 0
$user_id    = fRequest::get('user_id', 'integer', 1);

// Integer return value -  1 will be returned if no value or a blank string is provided
$total_cost = fRequest::get('total_cost', 'integer', 1, TRUE);
?>

Input Filtering by Typecasting (Security)

An important aspect of the get() method is to use the $cast_to parameter whenever possible. This helps to restrict data coming in and is part of creating an effective solution against cross-site scripting.

// IDs that can only ever be integers should be cast to integers
$blog_id = fRequest::get('blog_id', 'integer');

Currently the following data types are supported:

Special Data Type Handling

When accepting information from the user, different data types have to be handled differently. Of special note are arrays, binary, booleans, dates/times/timestamps, integers and strings.

Arrays

For arrays, PHP will automatically create an array when input field names end with []. Thus if you wanted to capture an array of groups ids, you could create a checkbox for each group id and give each one the name group_ids[]. When PHP parses the request data it will join all group_ids[] values into an array and assign it to the key groups_ids in the appropriate superglobal.

The array features mentioned above are built into PHP, however Flourish takes the array processing a step further. If a value is to be cast to an array, and is a string that contains commas, the string will be exploded on the comma character. Additionally, if an array contains a single entry of either a blank string or a NULL value, Flourish will return an empty array. This prevents having to manually filter arrays for meaningless values.

See strict arrays to create a single-dimensional array of a specific data type.

Binary

Binary data does not have any modification made to it when it is returned. There is no guarantee what encoding it will be in, and it may even contain null bytes.

Booleans

Booleans are interpreted from string values in as logical a way as possible. Empty values (according to PHPs empty() function) and the strings 'f', 'false' and 'no' (case-insensitive) will result in a FALSE value. This is obviously also the case if the key is not present in the request. Any other string value such as '1', 't', 'true', 'yes', etc. will be interpreted as a TRUE value.

Dates/Times/Timestamps

When the date, time and timestamp data types are specified, the returned values will be fDate, fTime and fTimestamp objects, respectively.

Integers

When the integer or int type is specified, all values are cast to a real PHP integer, except for large integer values, which are kept as strings. Large integer values are value that can not be represented by a PHP integer. If a real PHP integer is always required, the integer! type may be used instead. This type may cause the modification of some large integer values.

Strings

All strings that are passed through the get() method are expected to be UTF-8 and any invalid UTF-8 characters are removed by passing the data through fUTF::clean(). In addition to invalid UTF-8 byte sequences, all low byte non-printable characters are removed. This is true even if a value is not explicitly cast to a string, or if the string is contained inside of an array. If you need raw data, use the binary type.

Allowing Null

When typecasting values, it is sometimes useful for the absence of a value to return NULL, while still casting all non-NULL values. By adding a ? to the end of the data type, NULL will be returned if the key is not present in the request data, or if the value is an empty string ''.

// Get an integer or NULL
$count = fRequest::get('count', 'integer?');

// Get an fDate object or NULL
$date  = fRequest::get('date', 'date?');

Strict Arrays

The array type ensures that the value returned will be an array containing string values, with an unlimited number of dimensions. It is also possible to add [] to the end of any other data type to return a single-dimensional array containing values of that type.

// Return a single dimensional array of integers
$ids = fRequest::get('ids', 'integer[]');

Restricting Valid Values

The method getValid() simplifies the process a great deal by taking exactly two parameters, $key, the key to request the value for and $valid_values, an array of permissible values. If the value is not one of the valid value, the first valid value will be picked. Here is an example:

<?php
// $action will get the value 'list' if no value was present
$action = fRequest::getValid('action', array('list', 'search'));
?>

Escaping for HTML Output

The static methods encode() and prepare() provide a simple short to calling get() and then wrapping the result in fHTML::encode() or fHTML::prepare() respectively. These two methods have the exact same signature as get() and don't perform any processing other than passing the resulting value to fHTML.

// Output a trusted value from the request
echo fRequest::prepare('name');

// Output an untrusted value from the request
echo fRequest::encode('name');

Just as with fHTML::prepare(), be sure to only ever use prepare() if the user input can be trusted. prepare() allows for HTML to be inserted into the page unescaped. If the input is untrusted, the encode() method should be used instead.

Setting Values

Sometimes when dealing with different input types, it is useful to be able to alter or fix the request data. The method set() allows a new value to be set to any key. It accepts two parameters, the $key to set and the $value to assign.

if ($datetime = fRequest::get('date_time')) {
    fRequest::set('date', date('Y-m-d', strtotime($datetime)));
    fRequest::set('time', date('g:ia',  strtotime($datetime)));
}

Array Dereferencing

When a request value is an array, it is possible to use array dereference syntax in the field name to access a specific array key. This syntax works with get(), getValid(), set(), check(), encode() and prepare().

An input using array notation will automatically be converted to an array when PHP processes the request. This means the following inputs:

<input name="users[first_name]" value="John" />
<input name="users[last_name]" value="Smith" />

will cause the $_POST superglobal to contain:

array(
    'user' => array(
        'first_name' => 'John',
        'last_name'  => 'Smith'
    )
)

fRequest uses array dereference syntax that is identical to the <input> tag syntax.

// This will echo John
echo fRequest::get('user[first_name]');

Array dereferencing can be any number of layers deep.

echo fRequest::get('user[groups][0][name]');

Preventing CSRF (Security)

Cross-site request forgery (CSRF) is a technique where malicious websites will take advantage of the fact that a user has a valid session on a site, and will generate unauthorized requests on their behalf. These unauthorized requests can take the form of either GET or POST requests and can affect any page that does not validate the request properly.

GET request exploits are very easy to implement by taking advantage of the src attribute of various HTML tags. Imagine the HTML below living on http://example2.com:

<img src="http://example.com/my_messages/delete_all.php" />

If the script located at /my_messages/delete_all.php allowed a GET request to cause all messages to delete and the user who visited example2.com was logged into example.com, all of their messages would be deleted.

POST request exploits are slightly more difficult to take advantage of since a user will have to actually submit a form. Imagine the HTML below living on http://example2.com:

<p>
    Sign up for a free MP3 player!
</p>
<form action="http://example.com/my_messages/delete_all.php" method="post">
    <p>
        <label for="email">Your Email</label>
        <input id="email" name="email" value="" />   
    </p>
    <p>
        <input type="submit" value="Sign Up!" />
    </p>
</form>

This form would work even if the script requires a POST request, however it requires convincing the user to submit a form or use javascript to automatically trigger it.

Obviously quite a number of things can help to prevent there CSRF attacks, however the best security is implemented by giving the user a single-use crytographically random token for them to resubmit with the request. This requires that the user request the page first (to get the token) and then resubmit it.

Since the attack relies on the user being logged in to a site in their browser, the attacking site will not be able to use a server-side request to retrieve the page, and thus the token. In addition, browsers prevent javascript from requesting pages across domains, so that precludes an attacking using it to retrieve the page and then resubmit.

The static method generateCSRFToken() will create a single-use token to place into a form to protect against CSRF attacks. When processing a form, the static method validateCSRFToken() should be used to ensure the form submission is valid by passing the $token as the first parameter. If the token is not valid, an fValidationException will be thrown.

The following HTML form and corresponding PHP will ensure that only users who request the form will then be able to delete their messages.

<form action="" method="post">
    <p>
        <input type="submit" value="Yes, delete my messages!" />
        <input type="hidden" name="request_token" value="<?php echo fRequest::generateCSRFToken() ?>" />
    </p>
</form>
if (fRequest::isPost()) {
    try {
        fRequest::validateCSRFToken(fRequest::get('request_token'));

        // Delete all of the users messages
        
    } catch (fExpectedException $e) {
        $e->printMessage();
    }
}

By default both generateCSRFToken() and validateCSRFToken() will use the current page as the identifier to use when checking the token. If one page is submitting the value, but another is checking it, the $url parameter of each method can be used.

<input type="hidden" name="request_token" value="<?php echo fRequest::generateCSRFToken('/processing_page.php') ?>" />
fRequest::validateCSRFToken(fRequest::get('request_token'), '/processing_page.php');

In addition, each URL can have multiple valid tokens at one time. This ensures that tabbed browsing will not cause the user to receive error messages about unauthenticated requests.

Checking The HTTP Method

When creating RESTful applications it is useful to know the HTTP method used to request the current page. There are four methods to check each of the four HTTP methods:

HTTP Method !fRequest Method
GET isGet()
POST isPost()
PUT isPut()
DELETE isDelete()
HEAD isHead()

Since browsers only currently support GET and POST method, isPost() will commonly be the only one used unless creating a RESTful API.

if (fRequest::isPost()) {
    // Perform constructive/destructive action
}

It is also possible to see if a request was made via AJAX by using the static method isAjax(). This checks the HTTP_X_REQUESTED_WITH HTTP header to see if it is set to xmlhttprequest.

if (fRequest::isAjax()) {
    // Perform partial interaction
}

Retrieving HTTP Accept Values

When building applications or sites that deliver different formats or translations of content, the HTTP Accept and Accept-Language headers include important information about what the client expects in response.

The Accept header includes a list of acceptable mime-types that the client expects in return. In addition, the HTTP spec includes a q value to indicate which mime-types are preferred over others. The Accept-Language header include a similar list for the language of the response. Accept-Language value can also include a q value for each language.

The static methods getAcceptTypes() and getAcceptLanguages() will each return a list of the acceptable mime-types and languages, respectively, ordered by the q values for each.

$mime_types = fRequest::getAcceptTypes();
$languages  = fRequest::getAcceptLanguages();

If no accepts headers are sent by the client, an empty array will be returned.

It is also possible to provide a list of valid accept types or languages and pick the clients preferred one by using the methods getBestAcceptType() and getBestAcceptLanguage().

Each method accepts a single parameter, an ordered array of the mime type or languages supported by your application. The type or language with the highest q value for the client will be returned.

$mime_type = fRequest::getBestAcceptType(array(
    'application/json',
    'text/html'
));
$language = fRequest::getBestAcceptLanguage(array(
    'en-us',
    'fr-ca'
));

If no value in the array matches, FALSE will be returned. If any value is acceptable, the first value in the array will be returned.

If no array of valid values is specified, and the client accepts any value, NULL will be returned. Otherwise the value specified by the client with the highest q will be returned.

Multiple Submit Buttons

There are sure to be times when a user is given multiple options when submitting a form and where you dont want to require the user to have javascript enabled in their browser. The overrideAction() method allows for a form to have multiple submit buttons and for them to trigger different functionality.

This solution stems from the problem that the only way to tell which submit button was click is that buttons name and value pair is added to the GET or POST data, while all other submit buttons are ignored. Rather than requiring the developer to manually check for specific values when creating multiple submit buttons, overrideAction() allows the name of a submit button to be set to action::{action_to_trigger}.

If a submit button with a name as above is clicked and overrideAction() is called in the destination page, the action parameter of $_GET or $_POST will all be overridden with this action.

Here is an example:

<form action="<?php echo fURL::get() ?>" method="post">
    ...
    <p>
        <input type="hidden" name="action" value="delete" />
        <input type="submit" value="Yes, delete this user" />
        <input type="submit" name="action::list" value="No, keep this user" />
    </p>
</form>

And the PHP to handle the form submission:

// We will call overrideAction() as the very first thing so everything else see the modified `$_POST` superglobal
fRequest::overrideAction();

// Get the action to execute
$action = fRequest::get('action');

If the user were to click Yes, delete this user then the action delete would be executed, however if the user clicked No, keep this user then the list action would be executed.

It is also possible to pass a redirection URL to overrideAction(). If the string %action% is found in the redirection URL, it will be replaced with the overridden action. Here is an example:

// Redirect the user to /admin/users/{action} if an overridden action is posted
fRequest::overrideAction('/admin/users/%action%');

fRequest API Reference

static class, v1.0.0b20

Provides request-related methods

This class is implemented to use the UTF-8 character encoding. Please see UTF-8 for more information.

Please also note that using this class in a PUT or DELETE request will cause the php://input stream to be consumed, and thus no longer available.

Changes:
1.0.0b20Added isHead(), fixed ability to call set() on HEAD requests 11/23/11
1.0.0b19Added the $use_default_for_blank parameter to get() 6/3/11
1.0.0b18Backwards Compatibility Break - getBestAcceptType() and getBestAcceptLanguage() now return either NULL, FALSE or a string instead of NULL or a string, both methods are more robust in handling edge cases 2/6/11
1.0.0b17Fixed support for 3+ dimensional input arrays, added a fixed for the PHP DoS float bug #53632, added support for type-casted arrays in get() 1/9/11
1.0.0b16Backwards Compatibility Break - changed get() to remove binary characters when casting to a string, changed int and integer to cast to a real integer when possible, added new types of binary and integer! 11/30/10
1.0.0b15Added documentation about [sub-key] syntax, added [sub-key] support to check() 9/12/10
1.0.0b14Rewrote set() to not require recursion for array syntax 9/12/10
1.0.0b13Fixed set() to work with PUT requests 6/30/10
1.0.0b12Fixed a bug with getBestAcceptLanguage() returning the second-best language 5/27/10
1.0.0b11Added isAjax() 3/15/10
1.0.0b10Fixed get() to not truncate integers to the 32bit integer limit 3/5/10
1.0.0b9Updated class to use new fSession API 10/23/09
1.0.0b8Casting to an integer or string in get() now properly casts when the $key isn't present in the request, added support for date, time, timestamp and ? casts 8/25/09
1.0.0b7Fixed a bug with filter() not properly creating new $_FILES entries 7/2/09
1.0.0b6filter() now works with empty prefixes and filtering the $_FILES superglobal has been fixed 7/2/09
1.0.0b5Changed filter() so that it can be called multiple times for multi-level filtering 6/2/09
1.0.0b4Added the HTML escaping functions encode() and prepare() 5/27/09
1.0.0b3Updated class to use new fSession API 5/8/09
1.0.0b2Added generateCSRFToken() from fCRUDgenerateRequestToken() and validateCSRFToken() from fCRUD::validateRequestToken() 5/8/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::check() public

Indicated if the parameter specified is set in the $_GET or $_POST superglobals or in the post data of a PUT or DELETE request

Signature

boolean check( string $key )

Parameters
string $key The key to check - array elements can be checked via [sub-key] syntax
Returns

If the parameter is set

::encode() public

Gets a value from get() and passes it through fHTML::encode()

Signature

string encode( string $key, string $cast_to=NULL, mixed $default_value=NULL )

Parameters
string $key The key to get the value of - array elements can be accessed via [sub-key] syntax
string $cast_to Cast the value to this data type
mixed $default_value If the parameter is not set in the DELETE/PUT post data, $_POST or $_GET, use this value instead
Returns

The encoded value

::filter() internal public

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

Parses through $_FILES, $_GET, $_POST and the PUT/DELETE post data and filters out everything that doesn't match the prefix and key, also removes the prefix from the field name

Signature

void filter( string $prefix, mixed $key )

Parameters
string $prefix The prefix to filter by
mixed $key If the field is an array, get the value corresponding to this key
::generateCSRFToken() public

Returns a request token that should be placed in each HTML form to prevent cross-site request forgery

This method will return a random 15 character string that should be placed in a hidden input element on every HTML form. When the form contents are being processed, the token should be retrieved and passed into validateCSRFToken().

The value returned by this method is stored in the session and then checked by the validate method, which helps prevent cross site request forgeries and (naive) automated form submissions.

Tokens generated by this method are single use, so a user must request the page that generates the token at least once per submission.

Signature

string generateCSRFToken( string $url=NULL )

Parameters
string $url The URL to generate a token for, default to the current page
Returns

The token to be submitted with the form

::get() public

Gets a value from the DELETE/PUT post data, $_POST or $_GET superglobals (in that order)

A value that exactly equals '' and is not cast to a specific type will become NULL.

Valid $cast_to types include:

  • 'string'
  • 'binary'
  • 'int'
  • 'integer'
  • 'bool'
  • 'boolean'
  • 'array'
  • 'date'
  • 'time'
  • 'timestamp'

It is possible to append a ? to a data type to return NULL whenever the $key was not specified in the request, or if the value was a blank string.

The array and unspecified $cast_to types allow for multi-dimensional arrays of string data. It is possible to cast an input value as a single-dimensional array of a specific type by appending [] to the $cast_to.

All string, array or unspecified $cast_to will result in the value(s) being interpreted as UTF-8 string and appropriately cleaned of invalid byte sequences. Also, all low-byte, non-printable characters will be stripped from the value. This includes all bytes less than the value of 32 (Space) other than Tab (\t), Newline (\n) and Cariage Return (\r).

To preserve low-byte, non-printable characters, or get the raw value without cleaning invalid UTF-8 byte sequences, plase use the value of binary for the $cast_to parameter.

Any integers that are beyond the range of 32bit storage will be returned as a string. The returned value can be forced to always be a real integer, which may cause truncation of the value, by passing integer! as the $cast_to.

Signature

mixed get( string $key, string $cast_to=NULL, mixed $default_value=NULL, boolean $use_default_for_blank=FALSE )

Parameters
string $key The key to get the value of - array elements can be accessed via [sub-key] syntax
string $cast_to Cast the value to this data type - see method description for details
mixed $default_value If the parameter is not set in the DELETE/PUT post data, $_POST or $_GET, use this value instead. This value will get cast if a $cast_to is specified.
boolean $use_default_for_blank If the request value is a blank string and $default_value is specified, this flag will cause the $default_value to be returned
Returns

The value

::getAcceptLanguages() public

Returns the HTTP Accept-Languages sorted by their q values (from high to low)

Signature

array getAcceptLanguages( )

Returns

An associative array of {language} => {q value} sorted (in a stable-fashion) from highest to lowest q - if no header was sent, an empty array will be returned

::getAcceptTypes() public

Returns the HTTP Accept types sorted by their q values (from high to low)

Signature

array getAcceptTypes( )

Returns

An associative array of {type} => {q value} sorted (in a stable-fashion) from highest to lowest q - if no header was sent, an empty array will be returned

::getBestAcceptLanguage() public

Returns the best HTTP Accept-Language (based on q value) - can be filtered to only allow certain languages

Special conditions affecting the return value:

  • If no $filter is provided and the client does not send the Accept-Language header, NULL will be returned
  • If no $filter is provided and the client specifies * with the highest q, NULL will be returned
  • If $filter contains one or more values, but the Accept-Language header does not match any, FALSE will be returned
  • If $filter contains one or more values, but the client does not send the Accept-Language header, the first entry from $filter will be returned
  • If $filter contains two or more values, and two of the values have the same q value, the one listed first in $filter will be returned
Signatures

string|NULL|FALSE getBestAcceptLanguage( array $filter=array() )

string|NULL|FALSE getBestAcceptLanguage( string $language [, ... ] )

Parameters
array $filter An array of languages that are valid to return - these should be in the form {language}-{territory}, e.g. en-us
string $language [, ... ] A language that is valid to return
Returns

The best language listed in the Accept-Language header - see method description for edge cases

::getBestAcceptType() public

Returns the best HTTP Accept type (based on q value) - can be filtered to only allow certain types

Special conditions affecting the return value:

  • If no $filter is provided and the client does not send the Accept header, NULL will be returned
  • If no $filter is provided and the client specifies */* with the highest q, NULL will be returned
  • If $filter contains one or more values, but the Accept header does not match any, FALSE will be returned
  • If $filter contains one or more values, but the client does not send the Accept header, the first entry from $filter will be returned
  • If $filter contains two or more values, and two of the values have the same q value, the one listed first in $filter will be returned
Signatures

string|NULL|FALSE getBestAcceptType( array $filter=array() )

string|NULL|FALSE getBestAcceptType( string $type [, ... ] )

Parameters
array $filter An array of types that are valid to return
string $type [, ... ] A type that is valid to return
Returns

The best type listed in the Accept header - see method description for edge cases

::getValid() public

Gets a value from the DELETE/PUT post data, $_POST or $_GET superglobals (in that order), restricting to a specific set of values

Signature

mixed getValid( string $key, array $valid_values )

Parameters
string $key The key to get the value of - array elements can be accessed via [sub-key] syntax
array $valid_values The array of values that are permissible, if one is not selected, picks first
Returns

The value

::isAjax() public

Indicates if the URL was accessed via an XMLHttpRequest

Signature

boolean isAjax( )

Returns

If the URL was accessed via an XMLHttpRequest

::isDelete() public

Indicates if the URL was accessed via the DELETE HTTP method

Signature

boolean isDelete( )

Returns

If the URL was accessed via the DELETE HTTP method

::isGet() public

Indicates if the URL was accessed via the GET HTTP method

Signature

boolean isGet( )

Returns

If the URL was accessed via the GET HTTP method

::isHead() public

Indicates if the URL was accessed via the HEAD HTTP method

Signature

boolean isHead( )

Returns

If the URL was accessed via the HEAD HTTP method

::isPost() public

Indicates if the URL was accessed via the POST HTTP method

Signature

boolean isPost( )

Returns

If the URL was accessed via the POST HTTP method

::isPut() public

Indicates if the URL was accessed via the PUT HTTP method

Signature

boolean isPut( )

Returns

If the URL was accessed via the PUT HTTP method

::overrideAction() public

Overrides the value of 'action' in the DELETE/PUT post data, $_POST or $_GET superglobals based on the 'action::{action_name}' value

This method is primarily intended to be used for hanlding multiple submit buttons.

Signature

void overrideAction( string $redirect=NULL )

Parameters
string $redirect The url to redirect to if the action is overriden. %action% will be replaced with the overridden action.
::prepare() public

Gets a value from get() and passes it through fHTML::prepare()

Signature

string prepare( string $key, string $cast_to=NULL, mixed $default_value=NULL )

Parameters
string $key The key to get the value of - array elements can be accessed via [sub-key] syntax
string $cast_to Cast the value to this data type
mixed $default_value If the parameter is not set in the DELETE/PUT post data, $_POST or $_GET, use this value instead
Returns

The prepared value

::reset() internal public

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

Signature

void reset( )

::set() public

Sets a value into the appropriate $_GET or $_POST superglobal, or the local PUT/DELETE post data based on what HTTP method was used for the request

Signature

void set( string $key, mixed $value )

Parameters
string $key The key to set the value to - array elements can be modified via [sub-key] syntax
mixed $value The value to set
::unfilter() internal public

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

Returns $_GET, $_POST and $_FILES and the PUT/DELTE post data to the state they were at before filter() was called

Signature

void unfilter( )

::validateCSRFToken() public

Validates a request token generated by generateCSRFToken()

This method takes a request token and ensures it is valid, otherwise it will throw an fValidationException.

Signature

void validateCSRFToken( string $token, string $url=NULL )

Parameters
string $token The request token to validate
string $url The URL to validate the token for, default to the current page
Throws
fValidationException
When the CSRF token specified is invalid

fResult

The fResult class is an iterable result object that is returned when an SQL query is executed. It contains all of the information about a query including any rows that may have been returned. This class uses result buffering to provide more features than fUnbufferedResult, albeit at the cost of extra memory usage proportional to amount of data returned.

Creation

The fResult class can be created by executing the methods fDatabase::query() or fDatabase::translatedQuery().

// Creation of fResult from a normal query
$result = $db->query("SELECT * FROM users");

// Creation of an fResult object from a translated query
$result2 = $db->translatedQuery("SELECT * FROM users LIMIT 5");

Row Format

By default when retrieving rows from a result object, they are returned as associative arrays. In the case of Oracle databases, where columns are case-insensitive, the array keys are lowercase.

It is possible to retrieve stdClass objects back for each row instead of associative arrays. This is done by calling the method asObjects(). The method returns the fResult object, allowing for method chaining:

// Set the result to return objects
$res = $db->query("SELECT * FROM users")->asObjects();

If you are looking for objects with more functionality, please see fActiveRecord.

Unescaping Data

While it is certainly possible to manually unescape data from a result set, one row and column at a time, unescaping the whole result is often much easier. The method unescape() accepts an associative array of the column name as the key and the data type as the value. This method should be called before any rows are fetched.

$result = $db->query("SELECT * FROM users");
$result->unescape(array(
    'first_name'       => 'string',
    'last_name'        => 'string',
    'is_authenticated' => 'boolean',
    'last_login'       => 'timestamp'
));

foreach ($result as $row) {
    // $row now contains the data in native PHP data types
}

Iteration

The fResult class implements the Iterator interface, which means that you can use the foreach construct to loop through all resulting rows. Below is an example of iterating over a result:

$result = $db->query("SELECT * FROM users");

foreach ($result as $row) {
    echo $row['name'] . '<br />';
}

If the result did not return any rows, the foreach construct will not be looped at all and the execution of the code will continue. If you wish to execute different code if no rows are returned, see the next section about exceptions.

If you wish to do manual iteration you can use the fetchRow() and valid() methods as shown below:

// Iteration using valid() and fetchRow()
while ($result->valid()) {
    $row = $result->fetchRow();
}

Seeking

Since an fResult object is a buffered result of a database call, it is possible to seek forwards and backwards in the set. The method seek() accepts a single integer parameter $row that will change the zero-based pointer in the result set.

// This would set the current row to be row #3
$result->seek(2);

Data Retrieval

In addition to allowing for iteration over the database query result, there are also two method that allow for simple data retrieval.

The method fetchScalar() will return the first value from the first row of a result set. This is useful if it is known that a query will only return a single value. If no rows are returned, a fNoRowsException will be thrown.

$result = $db->query("SELECT count(*) FROM users");
$count  = $result->fetchScalar();

While fetchScalar() allows retrieving data in amounts less than a row, the method fetchAllRows() does the opposite by allowing access to all rows at once. The return value of the method is an array of all row arrays.

$rows = $result->fetchAllRows();

Exceptions

As mentioned in the last section, if you iterate through the result and no rows are returned, nothing will happen. If you do need to execute different code when no rows are returned, you will want to call the tossIfNoRows() to cause an fNoRowsException to be thrown:

try {
    $result = $db->query("SELECT * FROM users");
    $result->tossIfNoRows();
    ?>
    <h1>Users</h1>
    <?
    foreach ($result as $row) {
        echo $row['name'] . '<br />';
    }
} catch (fNoRowsException $e) {
    ?>
    <p>No users were found</p>
    <?php
}

In addition to calling tossIfNoRows(), an fNoRowsException will be thrown if any Iterator interface methods (such as current(), next(), etc), fetchRow() or fetchAllRows() are called on a result object which returned no rows.

Query Info

Along with providing an iterable interface to a query result, the fResult class stores some information about the query including:

Method Description
countAffectedRows() Returns how many rows were affected by a DELETE, INSERT or UPDATE query
countReturnedRows() Returns how many rows were returned by a SELECT query
getAutoIncrementedValue() Returns the last value generated by an auto-incrementing integer field
getSQL() Returns the SQL statement executed for this result
getUntranslatedSQL() Returns the SQL from before translation happened - only applicable for results from translatedQuery()

fResult API Reference

class, implements Iterator , v1.0.0b13

Representation of a result from a query against the fDatabase class

Changes:
1.0.0b12Added a workaround for iconv having issues in MAMP 1.9.4+ 7/26/11
1.0.0b12Fixed MSSQL to have a properly reset row array, added silenceNotices(), fixed pdo_dblib on Windows when using the Microsoft DBLib driver 5/9/11
1.0.0b11Backwards Compatibility Break - removed ODBC support 7/31/10
1.0.0b10Added IBM DB2 support 4/13/10
1.0.0b9Added support for prepared statements 3/2/10
1.0.0b8Fixed a bug with decoding MSSQL national column when using an ODBC connection 9/18/09
1.0.0b7Added the method unescape(), changed tossIfNoRows() to return the object for chaining 8/12/09
1.0.0b6Fixed a bug where fetchAllRows() would throw a fNoRowsException 6/30/09
1.0.0b5Added the method asObjects() to allow for returning objects instead of associative arrays 6/23/09
1.0.0b4Fixed a bug with not properly converting SQL Server text to UTF-8 6/18/09
1.0.0b3Added support for Oracle, various bug fixes 5/4/09
1.0.0b2Updated for new fCore API 2/16/09
1.0.0bThe initial implementation 9/25/07

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::silenceNotices() public

Turns off notices about broken database extensions much as the MSSQL DBLib driver

Signature

void silenceNotices( )

Methods

->__construct() internal public

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

Configures the result set

Signature

fResult __construct( fDatabase $database, string $character_set=NULL )

Parameters
fDatabase $database The database object this result set was created from
string $character_set MSSQL only: the character set to transcode from since MSSQL doesn't do UTF-8
->__destruct() internal public

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

Frees up the result object to save memory

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->asObjects() public

Sets the object to return rows as objects instead of associative arrays (the default)

Signature

fResult asObjects( )

Returns

The result object, to allow for method chaining

->countAffectedRows() public

Returns the number of rows affected by the query

Signature

integer countAffectedRows( )

Returns

The number of rows affected by the query

->countReturnedRows() public

Returns the number of rows returned by the query

Signature

integer countReturnedRows( )

Returns

The number of rows returned by the query

->current() internal public implements Iterator

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

Returns the current row in the result set (required by iterator interface)

Signature

array|stdClass current( )

Returns

The current row

Throws
fNoRemainingException
When there are no remaining rows in the result
fNoRowsException
When the query did not return any rows
->fetchAllRows() public

Returns all of the rows from the result set

Signature

array fetchAllRows( )

Returns

The array of rows

->fetchRow() public

Returns the row next row in the result set (where the pointer is currently assigned to)

Signature

array|stdClass fetchRow( )

Returns

The next row in the result

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->fetchScalar() public

Wraps around fetchRow() and returns the first field from the row instead of the whole row

Signature

string|number|boolean fetchScalar( )

Returns

The first scalar value from fetchRow()

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->getAutoIncrementedValue() public

Returns the last auto incremented value for this database connection. This may or may not be from the current query.

Signature

integer getAutoIncrementedValue( )

Returns

The auto incremented value

->getResult() internal public

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

Returns the result

Signature

mixed getResult( )

Returns

The result of the query

->getSQL() public

Returns the SQL used in the query

Signature

string getSQL( )

Returns

The SQL used in the query

->getUntranslatedSQL() public

Returns the SQL as it was before translation

Signature

string getUntranslatedSQL( )

Returns

The SQL from before translation

->key() internal public implements Iterator

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

Returns the current row number (required by iterator interface)

Signature

integer key( )

Returns

The current row number

Throws
fNoRemainingException
When there are no remaining rows in the result
fNoRowsException
When the query did not return any rows
->next() internal public implements Iterator

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

Advances to the next row in the result (required by iterator interface)

Signature

void next( )

Throws
fNoRemainingException
When there are no remaining rows in the result
fNoRowsException
When the query did not return any rows
->rewind() internal public implements Iterator

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

Rewinds the query (required by iterator interface)

Signature

void rewind( )

->seek() public

Seeks to the specified zero-based row for the specified SQL query

Signature

void seek( integer $row )

Parameters
integer $row The row number to seek to (zero-based)
Throws
fNoRowsException
When the query did not return any rows
->setAffectedRows() internal public

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 the number of affected rows

Signature

void setAffectedRows( integer $affected_rows )

Parameters
integer $affected_rows The number of affected rows
->setAutoIncrementedValue() internal public

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 the auto incremented value

Signature

void setAutoIncrementedValue( integer $auto_incremented_value )

Parameters
integer $auto_incremented_value The auto incremented value
->setResult() internal public

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 the result from the query

Signature

void setResult( mixed $result )

Parameters
mixed $result The result from the query
->setReturnedRows() internal public

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 the number of rows returned

Signature

void setReturnedRows( integer $returned_rows )

Parameters
integer $returned_rows The number of rows returned
->setSQL() internal public

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 the SQL used in the query

Signature

void setSQL( string $sql )

Parameters
string $sql The SQL used in the query
->setUntranslatedSQL() internal public

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 the SQL from before translation

Signature

void setUntranslatedSQL( string $untranslated_sql )

Parameters
string $untranslated_sql The SQL from before translation
->tossIfNoRows() public

Throws an fNoResultException if the query did not return any rows

Signature

fResult tossIfNoRows( string $message=NULL )

Parameters
string $message The message to use for the exception if there are no rows in this result set
Returns

The result object, to allow for method chaining

Throws
fNoRowsException
When the query did not return any rows
->unescape() public

Sets the result object to unescape all values as they are retrieved from the object

The data types should be from the list of types supported by fDatabase::unescape().

Signature

fResult unescape( array $column_data_type_map )

Parameters
array $column_data_type_map An associative array with column names as the keys and the data types as the values
Returns

The result object, to allow for method chaining

->valid() public implements Iterator

Returns if the query has any rows left

Signature

boolean valid( )

Returns

If the iterator is still valid

fSMTP

The fSMTP class allows for sending emails with fEmail via an SMTP server. It supports multiple authentication methods, secure connections (including via STARTTLS) and increased sending performance via SMTP pipelining.

Instantiation

Creating an SMTP connection can be as simple as passing the IP address or hostname of the $server.

$smtp = new fSMTP('example.com');

In addition to the $server, it is also possible to specify the $port, if the connection is $secure and the $timeout.

// This sets up fSMTP to connect to the gmail SMTP server
// with a 5 second timeout. Gmail requires a secure connection.
$smtp = new fSMTP('smtp.gmail.com', 465, TRUE, 5);

Secure connections require the openssl extension. If a secure connection is not requested, but the SMTP server provides STARTTLS functionality, fSMTP will automatically upgrade the connection to be secure.

Authentication

fSMTP also supports SMTP authentication. Simple pass a $username and $password to authenticate(). This should be called before trying to send any emails.

$smtp->authenticate('username', 'password');

Behind the scenes, there are four authentication methods supported: digest-md5, cram-md5, plain and login. The more secure digest-md5 and cram-md5 methods will be used if possible since they do not send login credentials in the clear.

Sending Messages

Sending messages via fEmail and fSMTP is extremely simple, just pass the fSMTP object to fEmail::send():

$email->send($smtp);

Closing the Connection

The connection to the SMTP server will be automatically closed at the end of the script. If there is a need to close it before then, simply call close().

$smtp->close();

fSMTP API Reference

class, v1.0.0b11

Creates a connection to an SMTP server to be used by fEmail

Changes:
1.0.0b11Enhanced the error checking for write() 6/3/11
1.0.0b10Added code to work around PHP bug #42682 (http://bugs.php.net/bug.php?id=42682) where stream_select() doesn't work on 64bit machines from PHP 5.2.0 to 5.2.5, improved timeouts while reading data 1/10/11
1.0.0b9Fixed a bug where lines starting with . and containing other content would have the . stripped 9/11/10
1.0.0b8Updated the class to use fEmail::getFQDN() 9/7/10
1.0.0b7Updated class to use new fCore::startErrorCapture() functionality 8/9/10
1.0.0b6Updated the class to use new fCore functionality 7/5/10
1.0.0b5Hacked around a bug in PHP 5.3 on Windows 6/22/10
1.0.0b4Updated the class to not connect and authenticate until a message is sent, moved message id generation in fEmail 5/5/10
1.0.0b3Fixed a bug with connecting to servers that send an initial response of 220- and instead of 220 4/26/10
1.0.0b2Fixed a bug where STARTTLS would not be triggered if it was last in the SMTP server's list of supported extensions 4/20/10
1.0.0bThe initial implementation 4/20/10

Methods

->__construct() public

Configures the SMTP connection

The SMTP connection is only made once authentication is attempted or an email is sent.

Please note that this class will upgrade the connection to TLS via the SMTP STARTTLS command if possible, even if a secure connection was not requested. This helps to keep authentication information secure.

Signature

fSMTP __construct( string $host, integer $port=NULL, boolean $secure=FALSE, integer $timeout=NULL )

Parameters
string $host The hostname or IP address to connect to
integer $port The port number to use
boolean $secure If the connection should be secure - if STARTTLS is available, the connection will be upgraded even if this is FALSE
integer $timeout The timeout for the connection - defaults to the default_socket_timeout ini setting
->__destruct() public

Closes the connection to the SMTP server

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->authenticate() public

Authenticates with the SMTP server

This method supports the digest-md5, cram-md5, login and plain SMTP authentication methods. This method will try to use the more secure digest-md5 and cram-md5 methods first since they do not send information in the clear.

Signature

void authenticate( string $username, string $password )

Parameters
string $username The username
string $password The password
Throws
fValidationException
When the $username and $password are not accepted
->close() public

Closes the connection to the SMTP server

Signature

void close( )

->enableDebugging() public

Sets if debug messages should be shown

Signature

void enableDebugging( boolean $flag )

Parameters
boolean $flag If debugging messages should be shown
->send() internal public

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

Sends a message via the SMTP server

Signature

void send( string $from [, ... ], string $headers, string $body )

Parameters
string $from The email address being sent from - this will be used as the Return-Path header
array $to All of the To, Cc and Bcc email addresses to send the message to - this does not affect the message headers in any way
string $headers The message headers - the Bcc header will be removed if present
string $body The mail body
Throws
fValidationException
When the message is too large for the server

fSQLException

fSQLException is a sub-class of fUnexpectedException that indicates an error has occurred while executing an SQL query. This type of exception will not be tossed if a query returns no results, but would be tossed if there is a syntax error in the SQL statement. The fNoRowsException would be the type of exception tossed when an SQL query returns no rows.

This space intentionally left blank

fSQLException API Reference

class, v1.0.0b

An exception occurred while executing a SQL statement

Changes:
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
         |
         --fSQLException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fUnexpectedException::printMessage()
Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

fSQLSchemaTranslation

The fSQLSchemaTranslation class is an internal class used by fSQLTranslation for translating Flourish SQL DDL statements, such as ALTER TABLE and CREATE TABLE, into the dialect of SQL supported by the current database. To take advantage of the features of this class, be sure to call translatedQuery() instead of query().

fSQLSchemaTranslation is separate from fSQLTranslation due to the sheer amount of code necessary to implement ALTER TABLE functionality that works consistently across databases. In addition, this functionality is not used as frequently as the DML statements supported by fSQLTranslation.

fSQLSchemaTranslation API Reference

class, v1.0.0b3

Adds cross-database CREATE TABLE, ALTER TABLE and COMMENT ON COLUMN statements to fSQLTranslation

Changes:
1.0.0b3Fixed associating a sequence with a column in PostgreSQL when setting auto-increment, fixed detection of some Oracle CHECK(IN) constraints, fixed default values for SQLite ON DELETE and ON UPDATE clauses 1/12/12
1.0.0b2Fixed detection of explicitly named SQLite foreign key constraints 8/23/11
1.0.0bThe initial implementation 5/9/11

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

Methods

->__construct() public

Sets up the class

Signature

fSQLSchemaTranslation __construct( fDatabase $database )

Parameters
fDatabase $database The database being translated for
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->translate() internal public

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

Translates a Flourish SQL DDL statement into the dialect for the current database

Signature

array translate( string $sql, array &$rollback_statements=NULL )

Parameters
string $sql The SQL statement to translate
array &$rollback_statements SQL statements to rollback the returned SQL statements if something goes wrong - only applicable for MySQL ALTER TABLE statements
Returns

An array containing the translated $sql statement and an array of extra statements

fSQLTranslation

The fSQLTranslation class is an internal class used by fDatabase for translating Flourish SQL into the dialect of SQL supported by the current database. To take advantage of the features of this class, be sure to call translatedQuery() instead of query().

For more information about translatedQuery() and query(), please see the Queries section of the fDatabase page.

Debugging

The method enableDebugging(), accepts a single boolean parameter to turn debugging on or off. This method is automatically passed the same value as fDatabase::enableDebugging() when that is called.

When debugging is enabled, the class will print the original and translated queries, allowing developers to track down translation issues.

$sql_translation->enableDebugging(TRUE);

Caching

Part of the functionality that fSQLTranslation provides is to provide a way to get national character data (NCHAR, NVARCHAR and NTEXT columns) out of Microsoft SQL Server. Most of the MSSQL extensions for PHP do not properly retrieve national character data since it is encoded in UCS-2, which contains NULL bytes. fSQLTranslation translates the SQL query to request data from such columns as binary data, which allows NULL bytes, and then changes the encoding to UTF-8 before being returned to the developer.

Part of this process is to retrieve a list of all national character columns. The enableCaching() method accepts an instance of the fCache class, and will save the appropriate database schema information so it does not need to be fetched on each page load.

$sql_translation->enableCaching(new fCache('file', '/path/to/cache/file'));

The method clearCache() will clear out the cached information, which would be useful when the database schema changes.

When using the Flourish ORM, the fORM class provides some useful caching functionality that will automatically clear the cache when database errors occur.

fSQLTranslation API Reference

class, v1.0.0b20

Takes a subset of SQL from IBM DB2, MySQL, PostgreSQL, Oracle, SQLite and MSSQL and translates into the various dialects allowing for cross-database code

Changes:
1.0.0b20Added fix for PostgreSQL to handle INSERT statements that don't specify any columns or values 9/6/11
1.0.0b19Removed the stray method removeSQLiteIndexes() that was left over from moving code into fSQLSchemaTranslation 5/17/11
1.0.0b18Fixed LENGTH() and SUBSTR() functions for non-ascii characters being stored in MySQL, SQLite and DB2, moved CREATE TABLE support to fSQLSchemaTranslation 5/9/11
1.0.0b17Internal Backwards Compatiblity Break - changed the array keys for translated queries returned from translate() to include a number plus : before the original SQL, preventing duplicate keys 7/14/10
1.0.0b16Added IBM DB2 support 4/13/10
1.0.0b15Fixed a bug with MSSQL national character conversion when running a SQL statement with a sub-select containing joins 12/18/09
1.0.0b14Changed PostgreSQL to cast columns in LOWER() calls to VARCHAR to allow UUID columns (which are treated as a VARCHAR by fSchema) to work with default primary key ordering in fRecordSet 12/16/09
1.0.0b13Added a parameter to enableCaching() to provide a key token that will allow cached values to be shared between multiple databases with the same schema 10/28/09
1.0.0b12Backwards Compatibility Break - Removed date translation functionality, changed the signature of translate(), updated to support quoted identifiers, added support for PostgreSQL, MSSQL and Oracle schemas 10/22/09
1.0.0b11Fixed a bug with translating MSSQL national columns over an ODBC connection 9/18/09
1.0.0b10Changed last bug fix to support PHP 5.1.6 9/18/09
1.0.0b9Fixed another bug with parsing table aliases for MSSQL national columns 9/18/09
1.0.0b8Fixed a bug with parsing table aliases that occurs when handling MSSQL national columns 9/9/09
1.0.0b7Fixed a bug with translating NOT LIKE operators in PostgreSQL 7/15/09
1.0.0b6Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b5Update code to only translate data types inside of CREATE TABLE queries 5/22/09
1.0.0b4Added the missing __get() method for callback support 5/6/09
1.0.0b3Added Oracle and caching support, various bug fixes 5/4/09
1.0.0b2Fixed a notice with SQLite foreign key constraints having no ON clauses 2/21/09
1.0.0bThe initial implementation 9/25/07

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::sqliteCotangent() internal public

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

Callback for custom SQLite function; calculates the cotangent of a number

Signature

numeric sqliteCotangent( numeric $x )

Parameters
numeric $x The number to calculate the cotangent of
Returns

The contangent of $x

::sqliteDate() internal public

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

Callback for custom SQLite function; returns the current date

Signature

string sqliteDate( )

Returns

The current date

::sqliteLength() internal public

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

Callback for custom SQLite length function; returns the number of UTF-8 characters in a string

Signature

string sqliteLength( string $string )

Parameters
string $string The string to measure
Returns

The current date

::sqliteLogBaseFirst() internal public

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

Callback for custom SQLite function; calculates the log to a specific base of a number

Signature

numeric sqliteLogBaseFirst( integer $base, numeric $num )

Parameters
integer $base The base for the log calculation
numeric $num The number to calculate the logarithm of
Returns

The logarithm of $num to $base

::sqliteSign() internal public

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

Callback for custom SQLite function; returns the sign of the number

Signature

numeric sqliteSign( numeric $x )

Parameters
numeric $x The number to change the sign of
Returns

-1 if a negative sign, 0 if zero, 1 if positive sign

::sqliteSubstr() internal public

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

Callback for custom SQLite function; creates a substring

Signature

string sqliteSubstr( string $string, integer $start, integer $length )

Parameters
string $string The string to take a substring of
integer $start The one-based position to start the substring at
integer $length The length of the substring to take
Returns

The substring

::sqliteTime() internal public

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

Callback for custom SQLite function; returns the current time

Signature

string sqliteTime( )

Returns

The current time

::sqliteTimestamp() internal public

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

Callback for custom SQLite function; returns the current timestamp

Signature

string sqliteTimestamp( )

Returns

The current date

Methods

->__construct() public

Sets up the class and creates functions for SQLite databases

Signature

fSQLTranslation __construct( fDatabase $database )

Parameters
fDatabase $database The database being translated for
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->clearCache() public

Clears all of the schema info out of the object and, if set, the fCache object

Signature

void clearCache( )

->enableCaching() public

Sets the schema info to be cached to the fCache object specified

Signature

void enableCaching( fCache $cache, $key_token=NULL )

Parameters
fCache $cache The cache to cache to
$key_token
->enableDebugging() public

Sets if debug messages should be shown

Signature

void enableDebugging( boolean $flag )

Parameters
boolean $flag If debugging messages should be shown
->translate() internal public

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

Translates Flourish SQL into the dialect for the current database

Signature

array translate( array $statements, array &$rollback_statements=NULL )

Parameters
array $statements The SQL statements to translate
array &$rollback_statements SQL statements to rollback the returned SQL statements if something goes wrong - only applicable for MySQL ALTER TABLE statements
Returns

The translated SQL statements all ready for execution. Statements that have been translated will have string key of the number, : and the original SQL, all other will have a numeric key.

fSchema

The fSchema class provides information about the structure of a database, from table and column info to keys and relationships. The database schema is converted to a standard format that can then be used by other code wishing to interact with the database.

Example Database

The commands throughout this page are used to display information about database structure. Here are the CREATE TABLE statements for the example database:

CREATE TABLE groups (
    group_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(100) NOT NULL UNIQUE
);

CREATE TABLE users (
    user_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(200) NOT NULL UNIQUE,
    posts INTEGER NOT NULL DEFAULT 0,
    birthday DATE,
    status VARCHAR(20) NOT NULL DEFAULT 'Inactive' CHECK(status IN ('Active', 'Inactive')),
    group_id INTEGER REFERENCES groups(group_id) ON DELETE CASCADE ON UPDATE CASCADE
);

Instantiation

The fSchema class simply requires and instance of the fDatabase class in order to be instantiated:

$db     = new fDatabase('sqlite', $_SERVER['DOCUMENT_ROOT'] . '/example.db');
$schema = new fSchema($db);

Table Listing

One of the most basic tasks to perform with the fSchema class is to return a list of all tables in the database by using getTables():

$tables = $schema->getTables();
fCore::expose($tables);

The output of the above PHP would be:

<pre class="exposed">Array
(
    [0] =&gt; groups
    [1] =&gt; users
)</pre>

Column Information

Column information can be returned for any table in the database using the getColumnInfo() method. The returned associative array contains the following information about each column: type, not_null, default, valid_values, max_length, min_value, max_value, decimal_places and auto_increment. For details about the returned array and the data type mapping that occurs, please view the documentation for getColumnInfo().

Here is an example:

$column_info = $schema->getColumnInfo('users');
fCore::expose($column_info);

The HTML output would be:

<pre class="exposed">Array
(
    [user_id] =&gt; Array
        (
            [type] =&gt; integer
            [not_null] =&gt; {true}
            [auto_increment] =&gt; {true}
            [default] =&gt; {null}
            [valid_values] =&gt; {null}
            [min_value] =&gt; -2147483648
            [max_value] =&gt; 2147483647
            [max_length] =&gt; {null}
            [decimal_places] =&gt; {null}
            [comment] =&gt; {empty_string}
        )
  
    [name] =&gt; Array
        (
            [type] =&gt; varchar
            [max_length] =&gt; 200
            [not_null] =&gt; {true}
            [default] =&gt; {null}
            [valid_values] =&gt; {null}
            [min_value] =&gt; {null}
            [max_value] =&gt; 2147483647
            [decimal_places] =&gt; {null}
            [auto_increment] =&gt; {false}
            [comment] =&gt; {empty_string}
        )
  
    [posts] =&gt; Array
        (
            [type] =&gt; integer
            [not_null] =&gt; {true}
            [default] =&gt; 0
            [valid_values] =&gt; {null}
            [max_length] =&gt; {null}
            [min_value] =&gt; -2147483648
            [max_value] =&gt; 2147483647
            [decimal_places] =&gt; {null}
            [auto_increment] =&gt; {false}
            [comment] =&gt; {empty_string}
        )
  
    [birthday] =&gt; Array
        (
            [type] =&gt; date
            [not_null] =&gt; {false}
            [default] =&gt; {null}
            [valid_values] =&gt; {null}
            [max_length] =&gt; {null}
            [min_value] =&gt; {null}
            [max_value] =&gt; {null}
            [decimal_places] =&gt; {null}
            [auto_increment] =&gt; {false}
            [comment] =&gt; {empty_string}
        )
  
    [status] =&gt; Array
        (
            [type] =&gt; varchar
            [max_length] =&gt; 20
            [not_null] =&gt; {true}
            [default] =&gt; Inactive

            [valid_values] =&gt; Array
                (
                    [0] =&gt; Active
                    [1] =&gt; Inactive
                )
            
            [min_value] =&gt; {null}
            [max_value] =&gt; {null}
            [decimal_places] =&gt; {null}
            [auto_increment] =&gt; {false}
            [comment] =&gt; {empty_string}
        )
  
    [group_id] =&gt; Array
        (
            [type] =&gt; integer
            [not_null] =&gt; {false}
            [default] =&gt; {null}
            [valid_values] =&gt; {null}
            [min_value] =&gt; -2147483648
            [max_value] =&gt; 2147483647
            [max_length] =&gt; {null}
            [auto_increment] =&gt; {false}
            [comment] =&gt; {empty_string}
        )
  
)</pre>

Key Information

In addition to determining basic information about table and columns in a database, fSchema can also detect all of the primary, foreign and unique keys in a database using the getKeys() method.

Here is the PHP to get the key information:

$keys = $schema->getKeys('users');
fCore::expose($keys);

And here is the HTML that would be output:

<pre class="exposed">Array
(
    [primary] =&gt; Array
        (
            [0] =&gt; user_id
        )
  
    [foreign] =&gt; Array
        (
            [0] =&gt; Array
                (
                    [column] =&gt; group_id
                    [foreign_table] =&gt; groups
                    [foreign_column] =&gt; group_id
                    [on_delete] =&gt; cascade
                    [on_update] =&gt; cascade
                )
        
        )
  
    [unique] =&gt; Array
        (
            [0] =&gt; Array
                (
                    [0] =&gt; name
                )
        
        )
  
)</pre>

Relationships

Key information for the database is useful, but another very useful bit of information is how the different tables in the database are related. getRelationships() method uses the key information to determine the one-to-one, one-to-many, many-to-one and many-to-many relationships present:

$users_relationships = $schema->getRelationships('users');
fCore::expose($users_relationships);

$groups_relationships = $schema->getRelationships('groups');
fCore::expose($groups_relationships);

The output for the users relationships would be:

<pre class="exposed">Array
(
    [one-to-one] =&gt; Array
        (
        )
  
    [many-to-one] =&gt; Array
        (
            [0] =&gt; Array
                (
                    [column] =&gt; group_id
                    [related_table] =&gt; groups
                    [related_column] =&gt; group_id
                )
        
        )
  
    [one-to-many] =&gt; Array
        (
        )
  
    [many-to-many] =&gt; Array
        (
        )
  
)</pre>

While the output for the groups relationships would be:

<pre class="exposed">Array
(
    [one-to-one] =&gt; Array
        (
        )
  
    [many-to-one] =&gt; Array
        (
        )
  
    [one-to-many] =&gt; Array
        (
            [0] =&gt; Array
                (
                    [column] =&gt; group_id
                    [related_table] =&gt; users
                    [related_column] =&gt; group_id
                    [on_delete] =&gt; cascade
                    [on_update] =&gt; cascade
                )
        
        )
  
    [many-to-many] =&gt; Array
        (
        )
  
)</pre>

Overriding

The fSchema class is used extensively by the object relational mapping code built into Flourish. Occasionally Flourish will support certain features based on database structure that are impossible to accomplish in a certain type of database.

To allow the object relational mapping code to still perform the necessary tasks, even if the database engine doesnt support a feature, the setColumnInfoOverride() and setKeysOverride() methods allow schema information to be overridden.

Please note that faking foreign-key relationships for MyISAM tables in MySQL may cause your data to get into an inconsistent state. This is because MyISAM tables do not support transactions, which Flourish uses for the purpose of atomic changes to the database across multiple tables at a time.

Example Database

Below are some example tables to show how foreign keys can be faked to help provide relational data integrity. These examples are presented using MySQL.

CREATE TABLE users (
	user_id INTEGER PRIMARY KEY AUTO_INCREMENT,
	first_name VARCHAR(100) NOT NULL,
	last_name VARCHAR(100) NOT NULL,
	email_address VARCHAR(200) NOT NULL UNIQUE,
	hashed_password VARCHAR(100) NOT NULL
);

CREATE TABLE groups (
	group_id INTEGER PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(255) NOT NULL UNIQUE,
	group_leader INTEGER,
	group_founder INTEGER
);

CREATE TABLE users_groups (
	user_id INTEGER NOT NULL,
	group_id INTEGER NOT NULL,
	PRIMARY KEY(user_id, group_id)
);

PHP to Override

The following PHP will create relationships between groups and users. There will not be any normal situations where you would want to override the primary or unique keys on a table. If you are using the ORM, this code should be executed before any fActiveRecord objects are used, such as in the bootstrap page, or something called from it.

The exact array structure to use with setKeysOverride() can be found in the getKeys() method documentation.

$schema = new fSchema($database);

// If we are using the ORM well want to attach this instance
fORMSchema::attach($schema);

// Set up the foreign keys from groups to users
$schema->setKeysOverride(
	array(
		array(
			'column'         => 'group_founder',
			'foreign_table'  => 'users',
			'foreign_column' => 'user_id',
			'on_delete'      => 'cascade',
			'on_update'      => 'cascade'
		),
		array(
			'column'         => 'group_leader',
			'foreign_table'  => 'users',
			'foreign_column' => 'user_id',
			'on_delete'      => 'cascade',
			'on_update'      => 'cascade'
		)
	),
	'groups',
	'foreign'
);

// Set up the keys for users_groups to create a join table
// for a many-to-many relationship between users and groups
$schema->setKeysOverride(
	array(
		array(
			'column'         => 'user_id',
			'foreign_table'  => 'users',
			'foreign_column' => 'user_id',
			'on_delete'      => 'cascade',
			'on_update'      => 'cascade'
		),
		array(
			'column'         => 'group_id',
			'foreign_table'  => 'groups',
			'foreign_column' => 'group_id',
			'on_delete'      => 'cascade',
			'on_update'      => 'cascade'
		)
	),
	'users_groups',
	'foreign'
);

Caching

Since the fSchema class executes a number of database calls to determine the structure of the database, it may be desirable to cache the information to reduce database load and script execution time. The method enableCaching() accepts an instance of the fCache object and will use it to save all of the schema information.

$schema->enableCaching(new fCache('file', '/path/to/cache/file'));

The method clearCache() will clear out the cached information, which would be useful when the database schema changes.

When using the Flourish ORM, the fORM class provides some useful caching functionality that will automatically clear the cache when database errors occur.

fSchema API Reference

class, v1.0.0b51

Gets schema information for the selected database

Changes:
1.0.0b51Fixed handling of getting tables in table creation order when a table references itself, fixed default value detection for the last column in a MySQL table 1/12/12
1.0.0b50Fixed detection of explicitly named SQLite foreign key constraints 8/23/11
1.0.0b49Added support for spatial/geometric data types in MySQL and PostgreSQL 5/26/11
1.0.0b48Fixed a bug with getTables() not working on MySQL 4.x, fixed getKeys() to always return a reset array 5/24/11
1.0.0b47Backwards Compatibility Break - getTables(), getColumnInfo(), getDatabases(), getKeys() and getRelationships() now return database, schema, table and column names in lowercase, added the $creation_order parameter to getTables(), fixed bugs with getting column and key information from MSSQL, Oracle and SQLite 5/9/11
1.0.0b46Enhanced SQLite schema detection to cover situations where UNIQUE constraints are defined separately from the table and when comments are used in CREATE TABLE statements 2/6/11
1.0.0b45Fixed Oracle auto incrementing detection to work with INSERT OR UPDATE triggers, fixed detection of dynamic default date/time/timestamp values for DB2 and Oracle 12/4/10
1.0.0b44Fixed the list of valid elements for getColumnInfo() 11/28/10
1.0.0b43Added the comment element to the information returned by getColumnInfo() 11/28/10
1.0.0b42Fixed a bug with MySQL detecting default ON DELETE clauses 10/19/10
1.0.0b41Fixed handling MySQL table names that require quoting 8/24/10
1.0.0b40Fixed bugs in the documentation and error message of getColumnInfo() about what are valid elements 7/21/10
1.0.0b39Fixed a regression where key detection SQL was not compatible with PostgreSQL 8.1 4/13/10
1.0.0b38Added Oracle support to getDatabases() 4/13/10
1.0.0b37Fixed getDatabases() for MSSQL 4/9/10
1.0.0b36Fixed PostgreSQL to properly report explicit NULL default values via getColumnInfo() 3/30/10
1.0.0b35Added max_length values for various text and blob data types across all databases 3/29/10
1.0.0b34Added min_value and max_value attributes to getColumnInfo() to specify the valid range for numeric columns 3/16/10
1.0.0b33Changed it so that PostgreSQL unique indexes containing functions are ignored since they can't be properly detected at this point 3/14/10
1.0.0b32Fixed getTables() to not include views for MySQL 3/14/10
1.0.0b31Fixed the creation of the default caching key for enableCaching() 3/2/10
1.0.0b30Fixed the class to work with lower privilege Oracle accounts and added detection of Oracle number columns 1/25/10
1.0.0b29Added on_delete and on_update elements to one-to-one relationship info retrieved by getRelationships() 12/16/09
1.0.0b28Fixed a bug with detecting some multi-column unique constraints in SQL Server databases 11/13/09
1.0.0b27Added a parameter to enableCaching() to provide a key token that will allow cached values to be shared between multiple databases with the same schema 10/28/09
1.0.0b26Added the placeholder element to the output of getColumnInfo(), added support for PostgreSQL, MSSQL and Oracle "schemas", added support for parsing quoted SQLite identifiers 10/22/09
1.0.0b25One-to-one relationships utilizing the primary key as a foreign key are now properly detected 9/22/09
1.0.0b24Fixed MSSQL support to work with ODBC database connections 9/18/09
1.0.0b23Fixed a bug where one-to-one relationships were being listed as many-to-one 7/21/09
1.0.0b22PostgreSQL UNIQUE constraints that are created as indexes and not table constraints are now properly detected 7/8/09
1.0.0b21Added support for the UUID data type in PostgreSQL 6/18/09
1.0.0b20Add caching of merged info, improved performance of getColumnInfo() 6/15/09
1.0.0b19Fixed a couple of bugs with setKeysOverride() 6/4/09
1.0.0b18Added missing support for MySQL mediumint columns 5/18/09
1.0.0b17Fixed a bug with clearCache() not properly reseting the tables and databases list 5/13/09
1.0.0b16Backwards Compatibility Break - setCacheFile() changed to enableCaching() and now requires an fCache object, flushInfo() renamed to clearCache(), added Oracle support 5/4/09
1.0.0b15Added support for the three different types of identifier quoting in SQLite 3/28/09
1.0.0b14Added support for MySQL column definitions containing the COLLATE keyword 3/28/09
1.0.0b13Fixed a bug with detecting PostgreSQL columns having both a CHECK constraint and a UNIQUE constraint 2/27/09
1.0.0b12Fixed detection of multi-column primary keys in MySQL 2/27/09
1.0.0b11Fixed an issue parsing MySQL tables with comments 2/25/09
1.0.0b10Added the getDatabases() method 2/24/09
1.0.0b9Now detects unsigned and zerofill MySQL data types that do not have a parenthetical part 2/16/09
1.0.0b8Mapped the MySQL data type 'set' to 'varchar', however valid values are not implemented yet 2/1/09
1.0.0b7Fixed a bug with detecting MySQL timestamp columns 1/28/09
1.0.0b6Fixed a bug with detecting MySQL columns that accept NULL 1/19/09
1.0.0b5setColumnInfo(): fixed a bug with not grabbing the real database schema first, made general improvements 1/19/09
1.0.0b4Added support for MySQL binary data types, numeric data type options unsigned and zerofill, and per-column character set definitions 1/17/09
1.0.0b3Fixed detection of the data type of MySQL timestamp columns, added support for dynamic default date/time values 1/11/09
1.0.0b2Fixed a bug with detecting multi-column unique keys in MySQL 1/3/09
1.0.0bThe initial implementation 9/25/07

Methods

->__construct() public

Sets the database

Signature

fSchema __construct( fDatabase $database )

Parameters
fDatabase $database The fDatabase instance
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->clearCache() internal public

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

Clears all of the schema info out of the object and, if set, the fCache object

Signature

void clearCache( )

->enableCaching() public

Sets the schema to be cached to the fCache object specified

Signature

void enableCaching( fCache $cache, string $key_token=NULL )

Parameters
fCache $cache The cache to cache to
string $key_token Internal use only! (this will be used in the cache key to uniquely identify the cache for this fSchema object)
->getColumnInfo() public

Returns column information for the table specified

If only a table is specified, column info is in the following format:

array(
    (string) {column name} => array(
        'type'           => (string)  {data type},
        'placeholder'    => (string)  {fDatabase::escape() placeholder for this data type},
        'not_null'       => (boolean) {if value can't be null},
        'default'        => (mixed)   {the default value},
        'valid_values'   => (array)   {the valid values for a varchar field},
        'max_length'     => (integer) {the maximum length in a varchar field},
        'min_value'      => (numeric) {the minimum value for an integer/float field},
        'max_value'      => (numeric) {the maximum value for an integer/float field},
        'decimal_places' => (integer) {the number of decimal places for a decimal/numeric/money/smallmoney field},
        'auto_increment' => (boolean) {if the integer primary key column is a serial/autoincrement/auto_increment/indentity column},
        'comment'        => (string)  {the SQL comment/description for the column}
    ), ...
)

If a table and column are specified, column info is in the following format:

array(
    'type'           => (string)  {data type},
    'placeholder'    => (string)  {fDatabase::escape() placeholder for this data type},
    'not_null'       => (boolean) {if value can't be null},
    'default'        => (mixed)   {the default value-may contain special strings CURRENT_TIMESTAMP, CURRENT_TIME or CURRENT_DATE},
    'valid_values'   => (array)   {the valid values for a varchar field},
    'max_length'     => (integer) {the maximum length in a char/varchar field},
    'min_value'      => (fNumber) {the minimum value for an integer/float field},
    'max_value'      => (fNumber) {the maximum value for an integer/float field},
    'decimal_places' => (integer) {the number of decimal places for a decimal/numeric/money/smallmoney field},
    'auto_increment' => (boolean) {if the integer primary key column is a serial/autoincrement/auto_increment/indentity column},
    'comment'        => (string)  {the SQL comment/description for the column}
)

If a table, column and element are specified, returned value is the single element specified.

The 'type' element is homogenized to a value from the following list:

  • 'varchar'
  • 'char'
  • 'text'
  • 'integer'
  • 'float'
  • 'timestamp'
  • 'date'
  • 'time'
  • 'boolean'
  • 'blob'

Please note that MySQL reports boolean data types as tinyint(1), so all tinyint(1) columns will be listed as boolean. This can be fixed by calling:

$schema->setColumnInfoOverride(
    array(
        'type'        => 'integer',
        'placeholder' => '%i',
        'default'     => {default integer},
        'min_value'   => new fNumber(-128),
        'max_value'   => new fNumber(127)
    ),
    '{table name}',
    '{column name}'
);

The 'comment' element pulls from the database's column comment facility with the exception of MSSQL and SQLite.

For MSSQL, the comment is pulled from the MS_Description extended property, which can be added via the Description field in SQL Server Management Studio, or via the sp_addextendedproperty stored procedure.

For SQLite, the comment is extracted from any SQL comment that is placed at the end of the line on which the column is defined:

CREATE TABLE users (
    user_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(200) NOT NULL -- This is the full name
);

For the SQLite users table defined above, the name column will have the comment This is the full name.

Signature

mixed getColumnInfo( string $table, string $column=NULL, string $element=NULL )

Parameters
string $table The table to get the column info for
string $column The column to get the info for
string $element The element to return: 'type', 'placeholder', 'not_null', 'default', 'valid_values', 'max_length', 'min_value', 'max_value', 'decimal_places', 'auto_increment', 'comment'
Returns

The column info for the table/column/element specified - see method description for format

->getDatabases() public

Returns the databases on the current server

Signature

array getDatabases( )

Returns

The databases on the current server

->getKeys() public

Returns a list of primary key, foreign key and unique key constraints for the table specified

The structure of the returned array is:

array(
     'primary' => array(
         {column name}, ...
     ),
     'unique'  => array(
         array(
             {column name}, ...
         ), ...
     ),
     'foreign' => array(
         array(
             'column'         => {column name},
             'foreign_table'  => {foreign table name},
             'foreign_column' => {foreign column name},
             'on_delete'      => {the ON DELETE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'},
             'on_update'      => {the ON UPDATE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'}
         ), ...
     )
)
Signature

array getKeys( string $table, string $key_type=NULL )

Parameters
string $table The table to return the keys for
string $key_type The type of key to return: 'primary', 'foreign', 'unique'
Returns

An array of all keys, or just the type specified - see method description for format

->getRelationships() public

Returns a list of one-to-one, many-to-one, one-to-many and many-to-many relationships for the table specified

The structure of the returned array is:

array(
    'one-to-one' => array(
        array(
            'table'          => (string) {the name of the table this relationship is for},
            'column'         => (string) {the column in the specified table},
            'related_table'  => (string) {the related table},
            'related_column' => (string) {the related column},
            'on_delete'      => (string) {the ON DELETE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'},
            'on_update'      => (string) {the ON UPDATE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'}
        ), ...
    ),
    'many-to-one' => array(
        array(
            'table'          => (string) {the name of the table this relationship is for},
            'column'         => (string) {the column in the specified table},
            'related_table'  => (string) {the related table},
            'related_column' => (string) {the related column}
        ), ...
    ),
    'one-to-many' => array(
        array(
            'table'          => (string) {the name of the table this relationship is for},
            'column'         => (string) {the column in the specified table},
            'related_table'  => (string) {the related table},
            'related_column' => (string) {the related column},
            'on_delete'      => (string) {the ON DELETE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'},
            'on_update'      => (string) {the ON UPDATE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'}
        ), ...
    ),
    'many-to-many' => array(
        array(
            'table'               => (string) {the name of the table this relationship is for},
            'column'              => (string) {the column in the specified table},
            'related_table'       => (string) {the related table},
            'related_column'      => (string) {the related column},
            'join_table'          => (string) {the table that joins the specified table to the related table},
            'join_column'         => (string) {the column in the join table that references 'column'},
            'join_related_column' => (string) {the column in the join table that references 'related_column'},
            'on_delete'           => (string) {the ON DELETE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'},
            'on_update'           => (string) {the ON UPDATE action: 'no_action', 'restrict', 'cascade', 'set_null', or 'set_default'}
        ), ...
    )
)
Signature

array getRelationships( string $table, string $relationship_type=NULL )

Parameters
string $table The table to return the relationships for
string $relationship_type The type of relationship to return: 'one-to-one', 'many-to-one', 'one-to-many', 'many-to-many'
Returns

An array of all relationships, or just the type specified - see method description for format

->getTables() public

Returns the tables in the current database

Signature

array getTables( boolean|string $creation_order=NULL )

Parameters
boolean|string $creation_order TRUE to return in a valid table creation order, or a table name to return that table and any tables that depend on it, in table creation order
Returns

The tables in the current database, all converted to lowercase

->setColumnInfoOverride() public

Allows overriding of column info

Performs an array merge with the column info detected from the database.

To erase a whole table, set the $column_info to NULL. To erase a column, set the $column_info for that column to NULL.

If the $column_info parameter is not NULL, it should be an associative array containing one or more of the following keys. Please see getColumnInfo() for a description of each.

  • 'type'
  • 'placeholder'
  • 'not_null'
  • 'default'
  • 'valid_values'
  • 'max_length'
  • 'min_value'
  • 'max_value'
  • 'decimal_places'
  • 'auto_increment'
  • 'comment'

The following keys may be set to NULL:

  • 'not_null'
  • 'default'
  • 'valid_values'
  • 'max_length'
  • 'min_value'
  • 'max_value'
  • 'decimal_places'
  • 'comment'

The key 'auto_increment' should be a boolean.

The 'type' key should be one of:

  • 'blob'
  • 'boolean'
  • 'char'
  • 'date'
  • 'float'
  • 'integer'
  • 'text'
  • 'time'
  • 'timestamp'
  • 'varchar'
Signature

void setColumnInfoOverride( array $column_info, string $table, string $column=NULL )

Parameters
array $column_info The modified column info - see method description for format
string $table The table to override
string $column The column to override
->setKeysOverride() public

Allows overriding of key info. Replaces existing info, so be sure to provide full key info for type selected or all types.

Signature

void setKeysOverride( array $keys, string $table, string $key_type=NULL )

Parameters
array $keys The modified keys - see getKeys() for format
string $table The table to override
string $key_type The key type to override: 'primary', 'foreign', 'unique'

fSession

The fSession class provides an enhanced interface to PHPs native session handling and $_SESSION superglobal features.

Configuration (Security)

There are three options for configuring the session, the setPath(), setLength() and ignoreSubdomain() static methods. All must be called before any other fSession methods.

Session File Path

The most important method to call when setting up a site is setPath(). This static method accepts a single parameter, the $directory to save all session files in. The directory specified must be writable by the web server, and should not contain anything except for session files because the session manager will delete old files after the predetermined session time has expired.

By default, all sites on a server use the same temporary directory to store the session files. This opens the opportunity for cross-site session transfer since a valid session ID can be pasted from from one session cookie to another. By setting the session directory per site, this type of attack is prevented. For additional security, it is wise to set the session directory to a location that is not readable by other users so they can not find it and set their session directory to the same place.

fSession::setPath('/path/to/private/writable/dir');

Duration

The static method setLength() allows you to set the minimum length of the session, using English descriptions of a timespan. Note that the minimum length, not the exact length, is specified since the session garbage collector uses a probabilistic approach to cleaning up session data. If the session length is set, the session directory should also be set via setPath() or else other sites on the server may delete session files that they consider "old", but that have not expired for the current site.

Here are a few example of setting the session length:

fSession::setLength('30 minutes');
fSession::setLength('1 hour');
fSession::setLength('1 day 2 hours');

There is a second, optional, parameter $persistent_timespan which is discussed in the section Keeping Users Logged In.

Spanning Subdomains

By default PHP will only allow access to the $_SESSION superglobal values by pages on the same subdomain, such that www.example.com could access the session, but example.com could not. Calling ignoreSubdomain() removes that restriction and allows access to any subdomain.

Preventing Session Fixation (Security)

Session fixation is an exploit where an attacker provides a user with a known session id and then uses the same session id to access their authentication session once they have logged in. Below is a simple example of a URL that allows the attacker to know the users session id:

http://example.com/login.php?PHPSESSID=abcdef1234567890

After the unsuspecting user has logged into the site, the attacker simply needs to set the same session id in his browser and hell have full access to the users session and information.

The fSession class prevents against such session fixation attacks by automatically setting the session.use_cookies and session.use_only_cookies ini settings so that session ids will not be accepted in a query string or POST data, but only in cookies.

When using the fAuthorization class, an additional layer of protected is added because all operations that add user information to the session will automatically regenerate the session id. This way even if an attacker was able to influence the session id, it will change once any useful information is present.

Controlling the Session

A session can be in one of three states: open, closed, and non-existent. An open session can have data written to the $_SESSION superglobal. A closed session retains all information, however the information can not be read or written. A non-existent session is exactly that, not present at all.

Opening

The session is automatically opened when any session method such as set(), get() or destroy() is called. It can also be opened explicitly by calling the static method open(). In the case that a Cannot send session cache limiter - headers already sent warning is generated, be sure to call open() before any output is sent to the browser.

// If you arent using the session until after content has been output,
// be sure to explicitly open the session before any content is echoed
fSession::open();

Closing

To close the session, simply call close(). The session information can be erased by calling destroy().

During normal usage of (see Storing and Retrieving Values for details) you can read and write throughout the script or page. However, if close() has been called on a page, no data can be read from or written to the session cache after that point.

There is, however, some benefit to closing the session once you are done, rather than waiting for the page to finish execution and the session to be closed automatically. The biggest limitation of PHP is that only a single page can be reading from or writing to a single session. This means a user with multiple browsers or tabs open to a site will only be able to load data from one page at a time. Any other pages being requested that need session data will have to wait until the first page is complete. On most sites with fast-loading pages this may not be an issue, however if any pages take any significant amount of time to the load, users may notice the site will become unresponsive.

// If you are about to execute a time-intensive block of code and no longer need the seesion, close it
fSession::close();

Destroying

Finally, the destroy() method will completely erase all data from a session and destroy the session id, preventing it from being opened again. This method is most useful when a logged-in user logs out.

// If a user is logging out, remove the information you have stored about them
fSession::destroy();

Keeping Users Logged In

On most sites that have a user login system, it will often be desirable to provide an option for a user to stay logged in even after their browser closes. Obviously this can be a security issue, however many large websites control the functionality via a checkbox in the login form that is labelled Keep me logged in. This will usually keep a user logged in for a week or two.

To implement this is Flourish, the static method fSession::setLength() allows for an optional second parameter, $persistent_timespan, which enables persistent logins and sets their length. Whenever using this functionality please be sure to set a custom session file path.

fSession::setLength('30 minutes', '1 week');

This will not cause all users to stay logged in for a week. The session files will only be garbage collected after a week, but fSession uses a timestamp in the session superglobal to log normal sessions out after 30 minutes.

To enable a user to stay logged in for the whole $persistent_timespan and to stay logged in across browser restarts, the static method fSession::enablePersistence() must be called when they log in. Here is an example:

if ($login == 'test' && fCryptography::checkPasswordHash($password, $hash)) {
    fAuthorization::setUserToken('test');
    if (fRequest::get('keep_me_logged_in', 'boolean')) {
        fSession::enablePersistence();
    }
}

Please note that setLength() must be called before enablePersistence().

Storing and Retrieving Values

Now that we have discussed how to control the session, lets get into the heart of the matter, storing and retrieving values. There are two methods available to accomplish this task, set() and get().

The set() method takes two parameters, $key and $value. In a fairly straight-forward manner, $key specifies what identifier to save the $value under.

The default prefix is 'fSession::'. It is recommended that under normal use the prefix is not changed. A logical place to change the prefix would be for values specific to another class. For example, the fAuthorization class changes the prefix to that all authorization-related session data does not conflict with anything a developer may add.

Here are some examples of adding data to the session:

// This is equivalent to $_SESSION['fSession::current_user_id'] = 5;
fSession::set('current_user_id', 5);
fSession::set('last_viewed_article', 42);

// Using the prefix here could allow us to not worry about overwriting values
// This is equivalent to $_SESSION['forum::current_user_id'] = 2;
fSession::set('current_user_id', 2, 'forum::');

Hand-in-hand with the set() method is get(). get() allows retrieval of session values with a twist. The first parameter, $key specifies what value to retrieve. The second (optional) parameter is $default_value. This value will be returned if the requested $key has no value set. Here are some example of getting values out of the session:

$current_user_id = fSession::get('current_user_id');
$user_groups     = fSession::get('user_groups', array(1,2));

Deleting Values

If you wish to unset a session value, simply use the delete() method. It accepts the name of the $key and returns the value:

$name = fSession::delete('name');

An optional second parameter allows providing a $default_value to be returned if the key specified is not set.

$name = fSession::delete('name', 'No name specified');

To delete all keys for a specific prefix, use the clear() method:

// Clear the default fSession:: prefix
fSession::clear();

// Clear all keys that start with MyPrefix_
fSession::clear('MyPrefix_');

Adding and Removing Values

The static methods add() and remove() allow adding and removing values from arrays stored in the session. add() accepts a $key and the $value to add. If the key is not an array, an array will be created and the new value will be added.

// Add John Smith at the end of users
fSession::add('users', 'John Smith');

The new value will be added at the end of the array unless the optional third parameter, $beginning, is set to TRUE.

// Add Jane Smith at the beginning of users
fSession::add('users', 'Jane Smith', TRUE);

remove() accepts one parameter, the $key to remove a value from, and returns the removed value. The value will be removed from the end of the array unless the second optional parameter, $beginning, is set to TRUE.

$last_value  = fSession::remove('users');
$first_value = fSession::remove('users', TRUE);

Array Dereferencing

When a value stored in the session is an array, it is possible to use array dereference syntax in the element name to access a specific array key. This syntax works with set(), get(), delete(), add() and remove().

fSession::set(
    'user',
    array(
        'first_name' => 'John',
        'last_name'  => 'Smith'
    )
);
// This will echo John
echo fSession::get('user[first_name]');

Array dereferencing can be any number of layers deep.

echo fSession::get('user[groups][0][name]');

fSession API Reference

static class, v1.0.0b22

Wraps the session control functions and the $_SESSION superglobal for a more consistent and safer API

A Cannot send session cache limiter warning will be triggered if open(), add(), clear(), delete(), get() or set() is called after output has been sent to the browser. To prevent such a warning, explicitly call open() before generating any output.

Changes:
1.0.0b22Fixed destroy() to no longer call regenerateID() since it fails after a session is destroyed 9/20/12
1.0.0b21Changed regenerateID() to not fail silently if the session has not been opened yet 9/15/12
1.0.0b20Fixed bugs with reset() introduced in 1.0.0b19 8/23/11
1.0.0b19Fixed some session warning messages for PHP 5.1.6 7/29/11
1.0.0b18Added support for storing session data in memcache, redis and databases using fCache and setBackend() 6/21/11
1.0.0b17Updated ignoreSubdomain() to use $_SERVER['HTTP_HOST'] when $_SERVER['SERVER_NAME'] is not set 2/1/11
1.0.0b16Changed delete() to return the value of the key being deleted 9/19/10
1.0.0b15Added documentation about [sub-key] syntax 9/12/10
1.0.0b14Backwards Compatibility Break - add(), delete(), get() and set() now interpret [ and ] as array shorthand and thus they can not be used in keys - added $beginning parameter to add(), added remove() method 9/12/10
1.0.0b13Fixed a bug that prevented working with existing sessions since they did not have the fSession::expires key 8/24/10
1.0.0b12Changed enablePersistence() to always regenerate the session ID, which ensures the function works even if the ID has already been regenerated by fAuthorizaton 8/21/10
1.0.0b11Updated the class to make sure enablePersistence() is called after ignoreSubdomain(), setLength() and setPath() 5/29/10
1.0.0b10Fixed some documentation bugs 3/3/10
1.0.0b9Fixed a bug in destroy() where sessions weren't always being properly destroyed 12/8/09
1.0.0b8Fixed a bug that made the unit tests fail on PHP 5.1 10/27/09
1.0.0b7Backwards Compatibility Break - Removed the $prefix parameter from the methods delete(), get() and set() - added the methods add(), enablePersistence(), regenerateID() 10/23/09
1.0.0b6Backwards Compatibility Break - the first parameter of clear() was removed, use delete() instead 5/8/09
1.0.0b5Added documentation about session cache limiter warnings 5/4/09
1.0.0b4The class now works with existing sessions 5/4/09
1.0.0b3Fixed clear() to properly handle when $key is NULL 2/5/09
1.0.0b2Made open() public, fixed some consistency issues with setting session options through the class 1/6/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::add() public

Adds a value to an already-existing array value, or to a new array value

Signature

void add( string $key, mixed $value, boolean $beginning=FALSE )

Parameters
string $key The name to access the array under - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in key names
mixed $value The value to add to the array
boolean $beginning If the value should be added to the beginning
::clear() public

Removes all session values with the provided prefix

This method will not remove session variables used by this class, which are prefixed with fSession::.

Signature

void clear( string $prefix=NULL )

Parameters
string $prefix The prefix to clear all session values for
::close() public

Closes the session for writing, allowing other pages to open the session

Signature

void close( )

::closeCache() internal public

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

Callback to close the session

Signature

boolean closeCache( )

Returns

If the operation succeeded

::delete() public

Deletes a value from the session

Signature

mixed delete( string $key, mixed $default_value=NULL )

Parameters
string $key The key of the value to delete - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in key names
mixed $default_value The value to return if the $key is not set
Returns

The value of the $key that was deleted

::destroy() public

Destroys the session, removing all values

Signature

void destroy( )

::destroyCache() internal public

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

Callback to destroy a session

Signature

boolean destroyCache( string $id )

Parameters
string $id The session to destroy
Returns

If the operation succeeded

::enablePersistence() public

Changed the session to use a time-based cookie instead of a session-based cookie

The length of the time-based cookie is controlled by setLength(). When this method is called, a time-based cookie is used to store the session ID. This means the session can persist browser restarts. Normally, a session-based cookie is used, which is wiped when a browser restart occurs.

This method should be called during the login process and will normally be controlled by a checkbox or similar where the user can indicate if they want to stay logged in for an extended period of time.

Signature

void enablePersistence( )

::gcCache() internal public

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

Callback to garbage-collect the session cache

Signature

boolean gcCache( )

Returns

If the operation succeeded

::get() public

Gets data from the $_SESSION superglobal

Signature

mixed get( string $key, mixed $default_value=NULL )

Parameters
string $key The name to get the value for - array elements can be accessed via [sub-key] syntax, and thus [ and ] can not be used in key names
mixed $default_value The default value to use if the requested key is not set
Returns

The data element requested

::ignoreSubdomain() public

Sets the session to run on the main domain, not just the specific subdomain currently being accessed

This method should be called after any calls to session_set_cookie_params().

Signature

void ignoreSubdomain( )

::open() public

Opens the session for writing, is automatically called by clear(), get() and set()

A Cannot send session cache limiter warning will be triggered if this, add(), clear(), delete(), get() or set() is called after output has been sent to the browser. To prevent such a warning, explicitly call this method before generating any output.

Signature

void open( boolean $cookie_only_session_id=TRUE )

Parameters
boolean $cookie_only_session_id If the session id should only be allowed via cookie - this is a security issue and should only be set to FALSE when absolutely necessary
::openCache() internal public

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

Callback to open the session

Signature

boolean openCache( )

Returns

If the operation succeeded

::readCache() internal public

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

Callback to read a session's values

Signature

string readCache( string $id )

Parameters
string $id The session to read
Returns

The session's serialized data

::regenerateID() internal public

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

Regenerates the session ID, but only once per script execution

Signature

void regenerateID( )

::remove() public

Removes and returns the value from the end of an array value

Signature

mixed remove( string $key, boolean $beginning=FALSE )

Parameters
string $key The name of the element to remove the value from - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in key names
boolean $beginning If the value should be removed to the beginning
Returns

The value that was removed

::reset() internal public

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

Signature

void reset( )

::set() public

Sets data to the $_SESSION superglobal

Signature

void set( string $key, mixed $value )

Parameters
string $key The name to save the value under - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in key names
mixed $value The value to store
::setBackend() public

Sets an fCache object to store sessions in

While any type of fCache backend should technically work, it would be unwise to use the file and directory types. The file caching backend stores all values in a single file, which would quickly become a performance bottleneck and could cause data loss with many concurrent users. The directory caching backend would not make sense since it is the same general functionality as the default session handler, but it would be slightly slower since it is written in PHP and not C.

It is recommended to set the serializer and unserializer $config settings on the fCache object to string for the best performance and minimal storage space.

For better performance, check out using the built-in session handlers that are bundled with the following extensions:

The igbinary extension can provide even more of a performance boost by storing serialized data in binary format instead of as text.

Signature

void setBackend( fCache $backend, string $key_prefix='' )

Parameters
fCache $backend An fCache object to store session values in
string $key_prefix A prefix to add to all session IDs before storing them in the cache
::setLength() public

Sets the minimum length of a session - PHP might not clean up the session data right away once this timespan has elapsed

Please be sure to set a custom session path via setPath() to ensure another site on the server does not garbage collect the session files from this site!

Both of the timespan can accept either a integer timespan in seconds, or an english description of a timespan (e.g. '30 minutes', '1 hour', '1 day 2 hours').

Signature

void setLength( string|integer $normal_timespan, string|integer $persistent_timespan=NULL )

Parameters
string|integer $normal_timespan The normal, session-based cookie, length for the session
string|integer $persistent_timespan The persistent, timed-based cookie, length for the session - this is enabled by calling enabledPersistence() during login
::setPath() public

Sets the path to store session files in

This method should always be called with a non-standard directory whenever setLength() is called to ensure that another site on the server does not garbage collect the session files for this site.

Standard session directories usually include /tmp and /var/tmp.

Signature

void setPath( string|fDirectory $directory )

Parameters
string|fDirectory $directory The directory to store session files in
::writeCache() internal public

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

Callback to write a session's values

Signature

string writeCache( string $id, string $values )

Parameters
string $id The session to write
string $values The serialized values
Returns

The session's serialized data

fStatement

The fStatement class is an internal class used by fDatabase for representing a database prepared statement. There are no public methods for this class, all functionality is exposed through fDatabase. Please see fDatabase for more information about creating and using prepared statements.

This space intentionally left blank

fStatement API Reference

class, v1.0.0b7

Representation of a prepared statement for use with the fDatabase class

Changes:
1.0.0b7Fixed handling of arrays of values for execute(), executeQuery() and executeUnbufferedQuery(), fixed escaping of values that become NULL 5/9/11
1.0.0b6Added getUntranslatedSQL() 1/9/11
1.0.0b5Fixed an edge case where the mysqli extension would leak memory when fetching a TEXT or BLOB column 8/28/10
1.0.0b4Updated class to use fCore::startErrorCapture() instead of error_reporting() 8/9/10
1.0.0b3Backwards Compatibility Break - removed ODBC support. Fixed UTF-8 support for the pdo_dblib extension. 7/31/10
1.0.0b2Added IBM DB2 support 4/13/10
1.0.0bThe initial implementation 3/2/10

Methods

->__construct() internal public

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 up a prepared statement

Signature

fStatement __construct( fDatabase $database, string $query, array $placeholders, $untranslated_sql, string $untranslated_query )

Parameters
fDatabase $database The database object this result set was created from
string $query The SQL statement to prepare
array $placeholders The data type placeholders
string $untranslated_query The original untranslated SQL, if applicable
$untranslated_sql
->__destruct() internal public

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

Frees up the result object to save memory

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->execute() internal public

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

Executes the statement without returning a result

Signature

mixed execute( array $params, mixed &$extra, boolean $different )

Parameters
array $params The parameters for the statement
mixed &$extra A variable to place extra information needed by some database extensions
boolean $different If this statement is different than the last statement run on the fDatabase instance
Returns

The (usually boolean) result of the extension function/method call

->executeQuery() internal public

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

Executes the statement in buffered mode

Signature

void executeQuery( fResult $result, array $params, mixed &$extra, boolean $different )

Parameters
fResult $result The object to place the result into
array $params The parameters for the statement
mixed &$extra A variable to place extra information needed by some database extensions
boolean $different If this statement is different than the last statement run on the fDatabase instance
->executeUnbufferedQuery() internal public

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

Executes the statement in unbuffered mode (if possible)

Signature

void executeUnbufferedQuery( fUnbufferedResult $result, array $params, mixed &$extra, boolean $different )

Parameters
fUnbufferedResult $result The object to place the result into
array $params The parameters for the statement
mixed &$extra A variable to place extra information needed by some database extensions
boolean $different If this statement is different than the last statement run on the fDatabase instance
->getSQL() public

Returns the SQL for the prepared statement

Signature

string getSQL( )

Returns

The SQL statement

->getUntranslatedSQL() public

Returns the untranslated SQL for the prepared statement

Signature

string getUntranslatedSQL( )

Returns

The untranslated SQL statement

fTemplating

The fTemplating class allows for simple PHP templating with abstraction of HTML into a separate scope. This is a contrast to system like Smarty where a complete language has been developed, and must be learned, to use templating.

Initialization

When creating an instance of the fTemplating class, a single optional parameter $root can be passed. This controls the directory that is used as the basis for relative paths. If no root is passed, the $_SERVER['DOCUMENT_ROOT'] is used as a default.

$template = new fTemplating('/var/www/inc/templates/');

Setting, Getting and Deleting Elements

The fTemplating class is built around the concept of elements. Elements can be any data type, and may represent anything from a chunk of data, to an object, or a file path.

The first operation to be performed is setting an element via the set() method. The first parameter, $element, is an identifier for the element you are setting. The second parameter is the $value.

Here are some example of setting various elements:

// These value would usually be set in the initialization script for a site
$template->set('header', 'header.php');
$template->set('footer', 'footer.php');

$template->set('db',  $database);

Multiple elements can be set in one method call by passing an associative array to set().

$template->set(array(
    'header' => 'header.php',
    'footer' => 'footer.php'
));

Elements can be retrieved by calling the get() method. This method required the first parameter, $element, which is what you want to retrieve. You can also optionally pass a second parameter $default_value to specify what should be returned in the element has not yet been set. If no $default_value is specified and the element has not been set, NULL will be returned.

// This will be the name or NULL
$name = $template->get('name');

// This will be the name or 'No name provided'
$name = $template->get('name', 'No name provided');

Multiple elements can be retrieved in one method call by passing an array of element names to get(). If default values are required, an associative array can be passed with the element name being the key and the default value being the value.

// Get an array with two values - the element names will be the keys and the
// values will be the values, NULL will be returned if an element is not set
$values = $template->get(array('first_name', 'last_name'));

// This returns the same elements, but with the explicit default values N/A
$values = $template->get(array(
    'first_name' => 'N/A',
    'last_name'  => 'N/A'
));

Elements can be removed by passing the $element name to delete(). The value of the element being deleted will be returned.

$value = $template->delete('name');

A $default_value can be passed as the second parameter to delete(), and it will be returned if the element specified was not set.

$value = $template->delete('name', 'None specified');

Multiple elements can be deleted at a time by passing an array of element names to delete(). Default values can be provided by passing an associative array of element names as keys and default values as values.

// Delete the first_name and last_name elements
$values = $template->delete(array('first_name', 'last_name'));

// Delete the first_name and last_name elements, providing defaults
$values = $template->delete(array(
    'first_name' => 'No first name provided',
    'last_name'  => 'No last name provided'
));

Adding, Removing and Filtering Elements

In addition to set(), there is a method called add() that will allow appending a value to an element that is an array. It will also initialize an element to an array if the element does not already exist.

$template->add('js',  '/sup/js/main.js');

$template->add('css', array('path' => '/sup/css/print.css', 'media' => 'print'));
$template->add('css', '/sup/css/base.css');

A value can be added at the beginning by passing TRUE as the third parameter.

$template->add('js', '/sup/js/jquery-min.js', TRUE);

The remove() method can used to remove a value from an array element. The removed value will be returned.

$template->add('numbers', 1);
$template->add('numbers', 2);
$two = $template->remove('numbers');

A value may be removed from the beginning of an array element by passing TRUE as a second parameter to remove().

$one = $template->remove('numbers');

Values may be removed from an array element by calling filter() with the $element as the first parameter, and the $value to remove as the second.

$template->filter('numbers', 1);

The filter() method does a normal equality comparison, so filtering 0 will also removed FALSE and other empty values. All matching values will be removed from the array, not just the first one.

Encoding and Preparing Elements

While simply retrieving elements is useful in certain situations, much of the time there will be a need to process text content for output in the HTML. The encode() and prepare() methods function the same was as get() except that they run the returned value through fHTML::encode() and fHTML::prepare(), respectively.

// Encode turns <, >, & and " into HTML entities to prevent XSS
echo $template->encode('name');

// ::prepare() should only be used with trusted content to make
// sure special characters outside of HTML tags are encoded
echo $template->prepare('html_bio');

Array Dereferencing

When an element is an array, it is possible to use array dereference syntax in the element name to access a specific array key. This syntax works with set(), get(), delete(), add(), remove(), filter(), encode() and prepare().

$template->set(
    'user',
    array(
        'first_name' => 'John',
        'last_name'  => 'Smith'
    )
);
// This will echo John
echo $template->get('user[first_name]');

Array dereferencing can be any number of layers deep.

echo $template->get('user[groups][0][name]');

Placing Elements

Probably the most useful aspect of the fTemplating class is the place() method. This method takes a single parameter, $element, in which you specify what element you want to place in the page.

place() works differently depending of the type of path is contained in the element. If the element contains a path is to a .php (.inc or .php5) file, the file will be included inside the scope of the fTemplating class (i.e. $this will return to the instance of the class). If a .css, .js or .rss (.xml) path is placed, the proper XHTML tags will be echoed.

In order to handle the title attribute for RSS feeds and the media attribute for CSS files it is possible to set the value of an element to be an associative array containing a key path and a key corresponding to the appropriate attributes.

Here are some examples of using place() for different types of files:

$template->set('title', 'Flourish');
$template->add('rss', array('path' => '/sup/rss/blog.rss', 'title' => 'Blog Posts'));
$template->place('header');
?>
<h1>Flourish</h1>
...
<?php
$template->place('footer');

Placing the header would include the file header.php from the root we defined earlier of /var/www/inc/templates/. Placing the footer would do that same thing for footer.php.

Here is an example header.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>  
        <title><?php echo $this->prepare('title') ?></title>
        <?php echo $this->place('css') ?>
        <?php echo $this->place('js') ?>
        <?php echo $this->place('rss') ?>
    </head>
    <body>

The HTML output from the above script would be:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>  
        <title>Flourish</title>
        <link rel="stylesheet" type="text/css" href="/sup/css/base.css" media="all" />
        <link rel="stylesheet" type="text/css" href="/sup/css/print.css" media="print" />
        <script type="text/javascript" src="/sup/js/main.js"></script>
        <link rel="alternate" type="application/rss+xml" href="/sup/rss/blog.rss" title="Blog Posts" />
    </head>
    <body>
        <h1>Flourish</h1>
        ...
    </body>
</html>

If paths are contained in element that do not end in one of the extensions listed below, you can force all paths in an element to be displayed as a certain type of file by passing the type of file as the second parameter to place.

Here are the valid extensions:

Here is how you could force all paths in an element to be displayed as a certain type regaredless of the path extensions:

// JS files
$this->place('element1', 'js');

// CSS files
$this->place('element2', 'css');

// RSS files
$this->place('element3', 'rss');

// PHP files
$this->place('element4', 'php');

Injecting Files

The method inject() works identically to place(), except that is accepts a file name or path instead of an element name. This allows placing a file without having to pass it to set() first.

// Include a PHP file in the template root
$template->inject('sidebar.php');

// Add a JS file
$template->inject('/path/to/example.js');

The second parameter also functions identically to place(), allowing the developer to specify the type of file being injected if it can't be auto-detected.

// Specifying the file type
$template->inject('/path/to/rss', 'rss');

Minification

fTemplating includes functionality that will minify javascript and CSS, combine multiple files into one, and add a query string to allow for far-futures expire headers. All of these features provide increased performance for HTTP clients.

Minification reduces the number of bytes sent to a user by removing whitespace and other unnecessary syntactic sugar. Combining multiple files reduces the number of HTTP requests made for each script execution. Adding a query string of the cache-file creation date allows use of far-futures expires headers without worrying about out-dated browser caches, which further reduces the number of HTTP requests.

In this section of documentation, a **cache file** refers to a file containing minified code of one or more original code files.

Enabling Minification

JS and CSS minification is enabled by calling enableMinification() with a $mode, $cache_directory and optional $path_prefix. This should be called before calling place() on any elements since it works through the place() method.

$template->enableMinification('development', $_SERVER['DOCUMENT_ROOT'] . '/sup/minification_cache/');

The $mode can be either 'development' or 'production'. In 'development' mode, all files are checked for modifications on each script execution. If a file has been modified since the cache was last created, the cache is regenerated. In 'production' mode, cache files are only regenerated if they are missing.

The $cache_directory should be a web-accessible directory for fTemplating to write the cache files to. It needs to be web-accessible since <script> and <link> tags will be referencing cache files stored inside of it.

The $path_prefix is optional, and defaults to $_SERVER['DOCUMENT_ROOT']. The $path_prefix is appended to all JS and CSS paths in order to load them from the filesystem. For example a CSS file /css/main.css would be prefixed with $_SERVER['DOCUMENT_ROOT'], resulting in fTemplating looking for the file in /document/root/css/main.css.

Since the minified files are stored on the filesystem, fTemplating must know how to translate the filesystem path into a URL. This translation is done by using fFilesystem::translateToWebPath(). If fTemplating is generating incorrect URLs for the minified files, please use fFilesystem::addWebPathTranslation() to indicate what translation should be done. fFilesystem: Translation has an example showing how the method is used.

How Minification Affects HTML Output

Once minification is enabled, all calls to place() that end up placing one or more CSS or JS files will automatically be minified.

$template->add('css', '/css/structure.css');
$template->add('css', '/css/typography.css');
$template->place('css');

Would normally result in the following HTML being written to the output:

<link rel="stylesheet" type="text/css" href="/css/structure.css" />
<link rel="stylesheet" type="text/css" href="/css/typography.css" />

With minification enabled, it would be written as:

<link rel="stylesheet" type="text/css" href="/cache/ae9210f4cbd017f4cbes.css?v=10298382912" />

Minification Algorithm

fTemplating uses Douglas Crockfords JSMin algorithm, but has its own implementation that is optimized for PHP.

For CSS, there is not a definitive algorithm like JSMin. Currently fTemplating performs the following steps to minify CSS:

CSS minification does not touch @import statements or url() literals. Depending on the location of the $cache_directory relative to the normal CSS directory, some @import statements may break. However, with enableMinification(), @import statements should not generally be necessary. Similarly, url() literals may break if the $cache_directory is on a different directory level than the original CSS directory. This can be remedied by placing them on the same level, or using website-relative URLs.

PHP Short Tags

PHP short tags, such as <? and <?= often make for easy-to-read and easy-to-write templates in raw PHP. Unfortunately, short tags can be disabled by an ini setting, and cant be relied upon when developing PHP for different environments.

The method enablePHPShortTags() allows any PHP file directly included via place() or inject() to use PHP short tags even if they are turned off. This is accomplished by transforming short tags into long tags and saving the transformed PHP into a cache directory. The method accepts two parameters, $mode and $cache_directory.

$template->enablePHPShortTags('development', '/path/to/php/cache');

The $mode can be either 'development' or 'production'. In 'development' mode, all files are checked for modifications on each script execution. If a file has been modified since the cache was last created, the cache is regenerated. In 'production' mode, cache files are only regenerated if they are missing.

The $cache_directory is where the transformed PHP files are saved. This directory should not be web-accessible, but must be writable by the web server. fTemplating will properly preserve the value of __FILE__ and __DIR__ constants that are contained within templates that are transformed.

Buffered Output

In certain circumstances it is useful to buffer the output of the page to allow for elements to be changed after content has already been output. The buffer() method allows such buffering to be enabled.

When buffering is enabled, all calls to place() actually insert a text token into the output. When the fTemplating class destructor is called (explicitly, or at the end of the page execution) the text tokens are all replaced by the output of the place() call. The place() method is actually executed in the destructor, so it will have the state of all elements at the end of the page, thus allowing elements to be modified after they have been seemingly already output.

There are two caveats to using the fTemplating output buffering.

  1. If any of your code relies on sequential execution of code in a place() call and then in other non-templating code, it will be executed in the wrong order due to the fact that the place() call is not done until the end of the page. If this is the case, you should probably not use buffering, or consider a way to refactor your code so that such sequential code execution is not required.
  2. All objects that are used inside of PHP that is placed by the fTemplating object need to be constructed before the fTemplating object is constructed. This is because destructors are executed in the reverse order of constructors, which means the objects being used by fTemplating will be destructed before fTemplating itself. Since fTemplating's destructor is what causes PHP code to be placed, the object being used may no longer be in a usable state. This happens most often with fDatabase.

Here is an example of using buffered output:

// Create the template and turn on buffering
$temp = new fTemplating();
$temp->buffer();

// Set up out elements
$temp->set('header', 'header.php');
$temp->set('title',  'This is the old title');

// Output the header
$temp->place('header');

// Change the title after the fact
$temp->set('title', 'This is the new title');

Lets assume that header.php looks like this:

echo $this->get('title');

The output from above would be:

This is the new title

since the place() call is not executed until the $temp destructor is called at the end of the code (implicitly) and the title was changed before the end of the code.

This buffering technique can greatly reduce the complexity of the code that needs to be executed before the first output can be sent to the user.

Named Instances

It is possible to attach and retrieve fTemplating objects in any scope using the static methods attach() and retrieve(). attach() accepts an fTemplating instance and an optional $name for it. If no $name is provided, it will be set to default.

fTemplating::attach(new fTemplating('/path/to/root'));

The method retrieve() accepts the $name of a previously creating instance and returns it. If no $name is passed, the default template is returned.

$tmpl = fTemplating::retrieve();

Sub-Templating

It is possible to nest fTemplating objects for more complex situations. The concept is to have a child fTemplating object that will be placed by a parent. Such a child fTemplating object will automatically call place() on the __main__ element when it itself is placed.

The __main__ element will normally be set by passing a PHP file path to the second parameter of the constructor. Like calls to set(), any file path that is not absolute and does not start with ./ will be relative to the template root.

$user_info = new fTemplating('/templating/root/', 'sub_templates/user_info.php');

The fTemplating object may then be set to another fTemplating object and placed.

$template->set('user_info', $user_info);
// At this point sub_templates/user_info.php is placed
$template->place('user_info');

All sub-templates will inherit all minification and PHP short tag settings from their parent if not explicitly set.

fTemplating API Reference

class, v1.0.0b23

Allows for quick and flexible HTML templating

Changes:
1.0.0b23Added a default $name for retrieve() to mirror attach() 8/31/11
1.0.0b22Backwards Compatibility Break - removed the static method create(), added the static method attach() to fill its place 8/31/11
1.0.0b21Fixed a bug in enableMinification() where the minification cache directory was sometimes not properly converted to a web path 8/31/11
1.0.0b20Fixed a bug in CSS minification that would reduce multiple zeros that are part of a hex color code, fixed minification of + ++ and similar constructs in JS 8/31/11
1.0.0b19Corrected a bug in enablePHPShortTags() that would prevent proper translation inside of HTML tag attributes 1/9/11
1.0.0b18Fixed a bug with CSS minification and black hex codes 10/10/10
1.0.0b17Backwards Compatibility Break - delete() now returns the values of the element or elements that were deleted instead of returning the fTemplating instance 9/19/10
1.0.0b16Fixed another bug with minifying JS regex literals 9/13/10
1.0.0b15Fixed a bug with minifying JS regex literals that occur after a reserved word 9/12/10
1.0.0b14Added documentation about [sub-key] syntax 9/12/10
1.0.0b13Backwards Compatibility Break - add(), delete(), get() and set() now interpret [ and ] as array shorthand and thus they can not be used in element names, renamed remove() to filter() - added $beginning parameter to add() and added remove() method 9/12/10
1.0.0b12Added enableMinification(), enablePHPShortTags(), the ability to be able to place child fTemplating objects via a new magic element __main__ and the $main_element parameter for __construct() 8/31/10
1.0.0b11Fixed a bug with the elements not being initialized to a blank array 8/12/10
1.0.0b10Updated place() to ignore URL query strings when detecting an element type 7/26/10
1.0.0b9Added the methods delete() and remove() 7/15/10
1.0.0b8Fixed a bug with placing absolute file paths on Windows 7/9/10
1.0.0b7Removed e flag from preg_replace() calls 6/8/10
1.0.0b6Changed set() and add() to return the object for method chaining, changed set() and get() to accept arrays of elements 6/2/10
1.0.0b5Added encode() 5/20/10
1.0.0b4Added create() and retrieve() for named fTemplating instances 5/11/10
1.0.0b3Fixed an issue with placing relative file path 4/23/10
1.0.0b2Added the inject() method 1/9/09
1.0.0bThe initial implementation 6/14/07

Static Variables

::$instances

Named fTemplating instances

Type

array

Variables

->root protected

The directory to look for files

Type

string

Static Methods

::attach() public

Attaches a named template that can be accessed from any scope via retrieve()

Signature

void attach( fTemplating $templating, string $name='default' )

Parameters
fTemplating $templating The fTemplating object to attach
string $name The name for this templating instance
::reset() internal public

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

Signature

void reset( )

::retrieve() public

Retrieves a named template

Signature

fTemplating retrieve( string $name='default' )

Parameters
string $name The name of the template to retrieve
Returns

The specified fTemplating instance

Methods

->__construct() public

Initializes this templating engine

Signature

fTemplating __construct( string $root=NULL, string $main_element=NULL )

Parameters
string $root The filesystem path to use when accessing relative files, defaults to $_SERVER['DOCUMENT_ROOT']
string $main_element The value for the __main__ element - this is used when calling place() without an element, or when placing fTemplating objects as children
->__destruct() internal public

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

Finishing placing elements if buffering was used

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->add() public

Adds a value to an array element

Signature

fTemplating add( string $element, mixed $value, boolean $beginning=FALSE )

Parameters
string $element The element to add to - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $value The value to add
boolean $beginning If the value should be added to the beginning of the element
Returns

The template object, to allow for method chaining

->buffer() public

Enables buffered output, allowing set() and add() to happen after a place() but act as if they were done before

Please note that using buffered output will affect the order in which code is executed since the elements are not actually place()'ed until the destructor is called.

If the non-template code depends on template code being executed sequentially before it, you may not want to use output buffering.

Signature

void buffer( )

->delete() public

Deletes an element from the template

Signatures

mixed delete( string $element, mixed $default_value=NULL )

mixed delete( array $elements )

Parameters
string $element The element to delete - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $default_value The value to return if the $element is not set
array $elements The elements to delete - an array of element names or an associative array of keys being element names and the values being the default values
Returns

The value of the $element that was deleted - an associative array of deleted elements will be returned if an array of $elements was specified

->destroy() public

Erases all output since the invocation of the template - only works if buffering is on

Signature

void destroy( )

->enableMinification() public

Enables minified output for CSS and JS elements

For CSS and JS, compilation means that the file will be minified and cached. The filename will change whenever the content change, allowing for far-futures expire headers.

Please note that this option requires that all CSS and JS paths be relative to the $_SERVER['DOCUMENT_ROOT'] and start with a /. Also this class will not clean up old cached files out of the cache directory.

This functionality will be inherited by all child fTemplating objects that do not have their own explicit minification settings.

Signature

void enableMinification( string $mode, fDirectory|string $cache_directory, fDirectory|string $path_prefix=NULL )

Parameters
string $mode The compilation mode - 'development' means that file modification times will be checked on each load, 'production' means that the cache files will only be regenerated when missing
fDirectory|string $cache_directory The directory to cache the compiled files into - this needs to be inside the document root or a path added to fFilesystem::addWebPathTranslation()
fDirectory|string $path_prefix The directory to prepend to all CSS and JS paths to load the files from the filesystem - this defaults to $_SERVER['DOCUMENT_ROOT']
->enablePHPShortTags() public

Converts PHP short tags to long tags when short tags are turned off

Please note that this only affects PHP files that are directly evaluated with place() or inject(). It will not affect PHP files that have been evaluated via include or require statements inside of the directly evaluated PHP files.

This functionality will be inherited by all child fTemplating objects that do not have their own explicit short tag settings.

Signature

void enablePHPShortTags( string $mode, fDirectory|string $cache_directory )

Parameters
string $mode The compilation mode - 'development' means that file modification times will be checked on each load, 'production' means that the cache files will only be regenerated when missing
fDirectory|string $cache_directory The directory to cache the compiled files into - this directory should not be accessible from the web
->encode() public

Gets the value of an element and runs it through fHTML::encode()

Signature

mixed encode( string $element, mixed $default_value=NULL )

Parameters
string $element The element to get - array elements can be accessed via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $default_value The value to return if the element has not been set
Returns

The value of the element specified run through fHTML::encode(), or the default value if it has not been set

->filter() public

Removes a value from an array element

Signature

fTemplating filter( string $element, mixed $value )

Parameters
string $element The element to remove from - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $value The value to remove - compared in a non-strict manner, such that removing 0 will remove a blank string and false also
Returns

The template object, to allow for method chaining

->get() public

Gets the value of an element

Signatures

mixed get( string $element, mixed $default_value=NULL )

mixed get( array $elements )

Parameters
string $element The element to get - array elements can be accessed via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $default_value The value to return if the element has not been set
array $elements An array of elements to get, or an associative array where a string key is the element to get and the value is the default value
Returns

The value of the element(s) specified, or the default value(s) if it has not been set

->handleMinified() protected

Combines an array of CSS or JS files and places them as a single file

Signature

void handleMinified( string $type, string $element, array $values )

Parameters
string $type The type of compilation, 'css' or 'js'
string $element The element name
array $values An array of file paths
->inject() public

Includes the file specified - this is identical to place() except a filename is specified instead of an element

Please see the place() method for more details about functionality.

Signature

void inject( string $file_path, string $file_type=NULL )

Parameters
string $file_path The file to place
string $file_type Will force the file to be placed as this type of file instead of auto-detecting the file type. Valid types include: 'css', 'js', 'php' and 'rss'.
->minify() protected

Minifies JS or CSS

For JS, this function is based on the JSMin algorithm (not the code) from http://www.crockford.com/javascript/jsmin with the addition of preserving /*! comment blocks for things like licenses. Some other versions of JSMin change the contents of special comment blocks, but this version does not.

Signature

string minify( string $code, string $type )

Parameters
string $code The code to minify
string $type The type of code, 'css' or 'js'
Returns

The minified code

->minifyCode() protected

Takes a block of CSS or JS and reduces the number of characters

Signature

void minifyCode( string &$part, string &$buffer, &$stack, mixed $type='js', array $stack )

Parameters
string &$part The part of code to minify
string &$buffer A buffer containing the last code or literal encountered
array $stack A stack used to keep track of the nesting level of CSS
mixed $type The type of code, 'css' or 'js'
&$stack
->minifyLiteral() protected

Takes a literal and either discards or keeps it

Signature

void minifyLiteral( mixed &$part, mixed &$buffer, string $type )

Parameters
mixed &$part The literal to process
mixed &$buffer The last literal or code processed
string $type The language the literal is in, 'css' or 'js'
->place() public

Includes the element specified - element must be set through set() first

If the element is a file path ending in .css, .js, .rss or .xml an appropriate HTML tag will be printed (files ending in .xml will be treated as an RSS feed). If the element is a file path ending in .inc, .php or .php5 it will be included.

Paths that start with ./ will be loaded relative to the current script. Paths that start with a file or directory name will be loaded relative to the $root passed in the constructor. Paths that start with / will be loaded from the root of the filesystem.

You can pass the media attribute of a CSS file or the title attribute of an RSS feed by adding an associative array with the following formats:

array(
    'path'  => (string) {css file path},
    'media' => (string) {media type}
);
array(
    'path'  => (string) {rss file path},
    'title' => (string) {feed title}
);
Signature

void place( string $element='__main__', string $file_type=NULL )

Parameters
string $element The element to place
string $file_type Will force the element to be placed as this type of file instead of auto-detecting the file type. Valid types include: 'css', 'js', 'php' and 'rss'.
->placeCSS() protected

Prints a CSS link HTML tag to the output

Signature

void placeCSS( mixed $info )

Parameters
mixed $info The path or array containing the 'path' to the CSS file. Array can also contain a key 'media'.
->placeElement() protected

Performs the action of actually placing an element

Signature

void placeElement( string $element, string $file_type )

Parameters
string $element The element that is being placed
string $file_type The file type to treat all values as
->placeJS() protected

Prints a javascript HTML tag to the output

Signature

void placeJS( mixed $info )

Parameters
mixed $info The path or array containing the 'path' to the javascript file
->placePHP() protected

Includes a PHP file

Signature

void placePHP( string $element, string $path )

Parameters
string $element The element being placed
string $path The path to the PHP file
->placeRSS() protected

Prints an RSS link HTML tag to the output

Signature

void placeRSS( mixed $info )

Parameters
mixed $info The path or array containing the 'path' to the RSS xml file. May also contain a 'title' key for the title of the RSS feed.
->prepare() public

Gets the value of an element and runs it through fHTML::prepare()

Signature

mixed prepare( string $element, mixed $default_value=NULL )

Parameters
string $element The element to get - array elements can be access via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $default_value The value to return if the element has not been set
Returns

The value of the element specified run through fHTML::prepare(), or the default value if it has not been set

->remove() public

Removes and returns the value from the end of an array element

Signature

mixed remove( string $element, boolean $beginning=FALSE )

Parameters
string $element The element to remove from to - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in element names
boolean $beginning If the value should be removed from the beginning of the element
Returns

The value that was removed

->set() public

Sets the value for an element

Signatures

fTemplating set( string $element, mixed $value )

fTemplating set( array $elements )

Parameters
string $element The element to set - the magic element __main__ is used for placing the current fTemplating object as a child of another fTemplating object - array elements can be modified via [sub-key] syntax, and thus [ and ] can not be used in element names
mixed $value The value for the element
array $elements An associative array with the key being the $element to set and the value being the $value for that element
Returns

The template object, to allow for method chaining

->verifyValue() protected

Ensures the value is valid

Signature

string verifyValue( string $element, mixed $value, string $file_type=NULL )

Parameters
string $element The element that is being placed
mixed $value A value to be placed
string $file_type The file type that this element will be displayed as - skips checking file extension
Returns

The file type of the value being placed

fText

The fText class is a static class with the sole purpose of creating a hook to allow for writing internationalized application and localizing Flourish. None of the Flourish classes require it be loaded, but if it is, all message/parameter interpolation is passed through this class.

Composing Messages

The fText class includes a key method to help with the i18n of code. The method compose() allows creating a message in multiple pieces that are later interpolated, allowing for efficient translation efforts.

Typically, a call to compose() will look like:

// Example variable values
$message_type      = 'error';
$message_part_name = 'parts';

echo fText::compose(
    'This is an example of displaying an %1$s message containing variable %2$s',
    $message_type,
    $message_part_name
);

which would produce the result:

This is an example of displaying an error message containing variable parts

At its core, compose() is simply a wrapper around sprintf(). However, being a wrapper allows setting up two hooks for translation. The method registerComposeCallback() allows us to intercept any string sent to compose() and modify it. The first parameter, $timing, allow registering the callback 'pre' or 'post' the sprintf() call. The second parameter, $callback, defines which method to pass the string to.

Any callback registered pre will get the un-interpolated string, while any callback registered post will get the interpolated string. Here is an example of translation using compose:

function translate($string)
{
    static $translations = array(
        'This is an example of displaying an %1$s message containing variable %2$s' => 'This is an %1$s message containing %2$s'
    );

    if (isset($translations[$string])) {
        return $translations[$string];
    }
    return $string;
}

fText::registerComposeCallback('pre', 'translate');

$message_type      = 'error';
$message_part_name = 'parts';

echo fText::compose(
    'This is an example of displaying an %1$s message containing variable %2$s',
    $message_type,
    $message_part_name
);

The above PHP would output the following:

This is an error message containing parts

Localization of Flourish

Every exception and error in Flourish will be passed through compose(), thus allowing for localization of the Flourish code base. Please see the MessagesList page for a list of all messages and their location in the source code.

fText API Reference

static class, v1.0.0b2

Provides internationlization support for strings

Changes:
1.0.0b2Updated compose() to more handle $components passed as an array 2/5/09
1.0.0bThe initial implementation 11/12/08

Static Methods

::compose() public

Performs an sprintf() on a string and provides a hook for modifications such as internationalization

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message A message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed message

::registerComposeCallback() public

Adds a callback for when a message is created using compose()

The primary purpose of these callbacks is for internationalization of error messaging in Flourish. The callback should accept a single parameter, the message being composed and should return the message with any modifications.

The timing parameter controls if the callback happens before or after the actual composition takes place, which is simply a call to sprintf(). Thus the message passed 'pre' will always be exactly the same, while the message 'post' will include the interpolated variables. Because of this, most of the time the 'pre' timing should be chosen.

Signature

void registerComposeCallback( string $timing, callback $callback )

Parameters
string $timing When the callback should be executed - 'pre' or 'post' performing the actual composition
callback $callback The callback
::reset() internal public

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

Signature

void reset( )

fTime

The fTime class is a value object representation of a time. One of the primary attributes of the object is that its value can not be changed, but instead a new object is created.

Instantiation

The fTime constructor takes a single argument, a string, object or integer representing a time of day. For strings and objects (with __toString() methods), any format accepted by strtotime() will work. If the parameter is an integer, it will be interpreted as a unix timestamp and the date portion will be discarded.

$time1 = new fTime('now');
$time2 = new fTime('+1 hour');
$time3 = new fTime('9:14 am');
$time4 = new fTime(1223926420);

Modification

Rather than allowing an fTime object value to be modified, which can create issues since objects are passed by reference, all changes to a time create a new object.

Usually when modifying a time, only one or two components (such as hour or minute) of the time will change. The modify() method leverages the formatting codes from the date() function to keep parts of the existing time while replacing others.

Here are some examples of modify():

// The new time would have the same minutes and seconds, but the hour would be set to 5am
$new_time = $time1->modify('5:i:s');

// The new time would be the same hour, but the very beginning
$new_time = $time2->modify('H:00:00');

// The new time would be the same hour, but the very end
$new_time = $time3->modify('H:59:59');

Adjustments

Occasionally you may have the need to adjust a time. The adjust() method takes a single parameter which can contain any relative time measurement that strtotime() accepts. Since fTime is a value object, a new object is returned with the adjusted time. Here are some examples:

$new_time = $time1->adjust('+1 hour');
$new_time = $time2->adjust('-2 hours +5 minutes');

Formatting

To format the time, simply call the format() method with any valid time formatting string from date(). Here are some examples:

echo $time1->format('g:ia');
echo $time2->format('H:i:s');

Comparing

There are five different methods available to compare times, eq(), gt(), gte(), lt() and lte(). Each method optionally accepts a parameter $other_time. If no $other_time is specified, the time is compared to the current time. If $other_time is specified, the two are compared. $other_time accepts any valid date string that works with __construct().

Here are some examples:

$now = new fTime();
$hour_ago = new fTime('-1 hour');

// These return TRUE
$now->eq();
$now->eq('now');
$now->gt($hour_ago);
$now->gte($hour_ago);
$now->lt('+5 min');

// These calls return FALSE
$hour_ago->lt($now);
$now->gt($hour_ago);
$now->gte($hour_ago);

Fuzzy Differences

If you are looking to get a fuzzy difference between two times for display, youll want to use the getFuzzyDifference() method. The first parameter, $other_time, optionally accepts a valid time descriptor that can be passed to __construct(). If a valid time descriptor is passed, the difference will be between the two times, if nothing is passed, the difference will be between the fTime and the current time.

The value returned by getFuzzyDifference() will be a string representing the most broad time measurement between the two times. In addition, if the difference is just shy of the next largest time measurement, it will be rounded up. Thus 52 minutes would become 1 hour.

Here are some examples to clarify. The following examples are comparing two times:

$time1 = new fTime('12:24 pm');
$time2 = new fTime('3:24 pm');

echo $time1->getFuzzyDifference($time2);
// Output: 3 hours before

echo $time2->getFuzzyDifference($time1);
// Output: 3 hours after

$time3 = new fTime('12:31 pm');

echo $time3->getFuzzyDifference('12:24 pm');
// Output: 7 minutes after

$time4 = new fTime('12:24:57 pm');

echo $time4->getFuzzyDifference('12:24 pm');
// Output: 1 minute after

These examples show output when comparing an fTime object with the current time:

// First, lets assume the time is currently 1:19 am.

$time1 = new fTime('11:59 pm');
$time2 = new fTime('12:53 am');
$time3 = new fTime('7:00 am');

echo $time1->getFuzzyDifference();
// Output: 1 day from now

echo $time2->getFuzzyDifference();
// Output: 26 minutes ago

echo $time3->getFuzzyDifference();
// Output: 6 hours from now

An optional boolean parameter, $simple, can also be passed to getFuzzyDifference(). When TRUE, this parameter causes the method to return the difference in time, but not the direction.

$time1 = new fTime('12:24 pm');
$time2 = new fTime('3:24 pm');

echo $time1->getFuzzyDifference($time2, TRUE);
// Output: 3 hours

echo $time2->getFuzzyDifference($time1, TRUE);
// Output: 3 hours

$time3 = new fTime('12:31 pm');

echo $time3->getFuzzyDifference('12:24 pm', TRUE);
// Output: 7 minutes

fTime API Reference

class, v1.0.0b12

Represents a time of day as a value object

Changes:
1.0.0b12Fixed a method signature 8/24/11
1.0.0b11Fixed a bug with the constructor not properly handling unix timestamps that are negative integers 6/2/11
1.0.0b10Changed the $time attribute to be protected 3/20/11
1.0.0b9Added the $simple parameter to getFuzzyDifference() 3/15/10
1.0.0b8Added a call to fTimestamp::callUnformatCallback() in __construct() for localization support 6/1/09
1.0.0b7Backwards compatibility break - Removed getSecondsDifference(), added eq(), gt(), gte(), lt(), lte() 3/5/09
1.0.0b6Fixed an outdated fCore method call 2/23/09
1.0.0b5Updated for new fCore API 2/16/09
1.0.0b4Fixed __construct() to properly handle the 5.0 to 5.1 change in strtotime() 1/21/09
1.0.0b3Added support for CURRENT_TIMESTAMP and CURRENT_TIME SQL keywords 1/11/09
1.0.0b2Removed the adjustment amount check from adjust() 12/31/08
1.0.0bThe initial implementation 2/12/08

Variables

->time protected

A timestamp of the time

Type

integer

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

Methods

->__construct() public

Creates the time to represent, no timezone is allowed since times don't have timezones

Signature

fTime __construct( fTime|object|string|integer $time=NULL )

Parameters
fTime|object|string|integer $time The time to represent, NULL is interpreted as now
Throws
fValidationException
When $time is not a valid time
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Returns this time in 'H:i:s' format

Signature

string __toString( )

Returns

The 'H:i:s' format of this time

->adjust() public

Changes the time by the adjustment specified, only adjustments of 'hours', 'minutes', and 'seconds' are allowed

Signature

fTime adjust( string $adjustment )

Parameters
string $adjustment The adjustment to make
Returns

The adjusted time

Throws
fValidationException
When $adjustment is not a valid relative time measurement
->eq() public

If this time is equal to the time passed

Signature

boolean eq( fTime|object|string|integer $other_time=NULL )

Parameters
fTime|object|string|integer $other_time The time to compare with, NULL is interpreted as today
Returns

If this time is equal to the one passed

->format() public

Formats the time

Signature

string format( string $format )

Parameters
string $format The date() function compatible formatting string, or a format name from fTimestamp::defineFormat()
Returns

The formatted time

Throws
fValidationException
When a non-time formatting character is included in $format
->getFuzzyDifference() public

Returns the approximate difference in time, discarding any unit of measure but the least specific.

The output will read like:

  • "This time is {return value} the provided one" when a time it passed
  • "This time is {return value}" when no time is passed and comparing with the current time

Examples of output for a time passed might be:

  • '5 minutes after'
  • '2 hours before'
  • 'at the same time'

Examples of output for no time passed might be:

  • '5 minutes ago'
  • '2 hours ago'
  • 'right now'

You would never get the following output since it includes more than one unit of time measurement:

  • '5 minutes and 28 seconds'
  • '1 hour, 15 minutes'

Values that are close to the next largest unit of measure will be rounded up:

  • '55 minutes' would be represented as '1 hour', however '45 minutes' would not
Signatures

string getFuzzyDifference( fTime|object|string|integer $other_time=NULL, boolean $simple=FALSE )

string getFuzzyDifference( boolean $simple=FALSE )

Parameters
fTime|object|string|integer $other_time The time to create the difference with, NULL is interpreted as now
boolean $simple When TRUE, the returned value will only include the difference in the two times, but not from now, ago, after or before
Returns

The fuzzy difference in time between the this time and the one provided

->gt() public

If this time is greater than the time passed

Signature

boolean gt( fTime|object|string|integer $other_time=NULL )

Parameters
fTime|object|string|integer $other_time The time to compare with, NULL is interpreted as now
Returns

If this time is greater than the one passed

->gte() public

If this time is greater than or equal to the time passed

Signature

boolean gte( fTime|object|string|integer $other_time=NULL )

Parameters
fTime|object|string|integer $other_time The time to compare with, NULL is interpreted as now
Returns

If this time is greater than or equal to the one passed

->lt() public

If this time is less than the time passed

Signature

boolean lt( fTime|object|string|integer $other_time=NULL )

Parameters
fTime|object|string|integer $other_time The time to compare with, NULL is interpreted as today
Returns

If this time is less than the one passed

->lte() public

If this time is less than or equal to the time passed

Signature

boolean lte( fTime|object|string|integer $other_time=NULL )

Parameters
fTime|object|string|integer $other_time The time to compare with, NULL is interpreted as today
Returns

If this time is less than or equal to the one passed

->modify() public

Modifies the current time, creating a new fTime object

The purpose of this method is to allow for easy creation of a time based on this time. Below are some examples of formats to modify the current time:

  • '17:i:s' to set the hour of the time to 5 PM
  • 'H:00:00'` to set the time to the beginning of the current hour
Signature

fTime modify( string $format )

Parameters
string $format The current time will be formatted with this string, and the output used to create a new object
Returns

The new time

fTimestamp

The fTimestamp class is a value object representation of a date/time. One of the primary attributes of the object is that its value can not be changed, but instead a new object is created. This object has full support for timezones.

This class is built on top of the PHP date/time functions and can only handle dates ranging from 19012038.

Default Timezone

The fTimestamp class is fully compatible with timezones thanks the the great changes in PHP 5.1. Classically timezones were represented by short abbreviations such as EST, GMT, PST, etc. To help remove abiguity, PHP now recommends using the zoneinfo timezone names.

Due to the revamped timezone support in PHP 5.1, it is now necessary to define the default timezone. This is done to ensure the proper timezone is being used and also prevent strict error reporting messages from appearing. fTimestamp provides two methods for setting and getting the default timezone, setDefaultTimezone() and getDefaultTimezone().

// Set the default timezone to New York time
fTimestamp::setDefaultTimezone('America/New_York');

echo fTimestamp::getDefaultTimezone();
// Output: America/New_York

There will be more timezone discussion throughout the rest of this document in the relevant places.

Instantiation

The fTimestamp constructor takes two parameters, including $datetime and $timezone. $datetime is a string, object (with a __toString() method) or integer representing a date/time. For strings and objects, any format accepted by strtotime() will work. Integers are interpreted as a unix timestamp. $timezone is optional and if passed should be a string with a zoneinfo timezone name. This timezone will be used throughout the life of the timestamp.

// Date/times without a timezone
$timestamp1 = new fTimestamp('today');
$timestamp2 = new fTimestamp('now');
$timestamp3 = new fTimestamp('3 Feb 2008 5:12 pm');
$timestamp4 = new fTimestamp('8 am');
$timestamp5 = new fTimestamp('next wednesday');
$timestamp6 = new fTimestamp(1223926420);

// Date/times with a timezone
$timestamp7 = new fTimestamp('2008-03-01 1 pm', 'America/New_York');
$timestamp8 = new fTimestamp('2008-03-01 1 pm', 'America/Los_Angeles');
$timestamp9 = new fTimestamp('2008-03-01 1 pm', 'Europe/London');

Modification

Rather than allowing an fTimestamp object value to be modified, which can create issues since objects are passed by reference, all changes to a timestamp create a new object.

Usually when modifying a timestamp, only one or two components (such as month, year, hour or minute) of the timestamp will change. The modify() method leverages the formatting codes from the date() function to keep parts of the existing timestamp while replacing others.

Here are some examples of modify():

// The new timestamps year would be 2007 while the rest would be the same
$new_timestamp = $timestamp1->modify('2007-m-d H:i:s');

// The new timestamp would be the 1st day (Monday) of the 9th week of the year
$new_timestamp = $timestamp2->modify('Y-\W9-1 00:00:00');

// The new timestamp would be moved to the beginning of the hour
$new_timestamp = $timestamp3->modify('Y-m-d H:00:00');

// The new timestamp would be moved to the end of the hour
$new_timestamp = $timestamp4->modify('Y-m-d H:59:59');

It is also possible to set the timezone of the new timestamp. The timezone can be passed as the optional second parameter to modify(). If no timezone is specified, the new timestamp will have the same timezone as the original.

$timestamp = new fTimestamp('now', 'America/New_York');

// This new timestamp is different by three hours due to the timezone change
$new_timestamp = $timestamp->modify('c', 'America/Los_Angeles');

Adjustments

Occasionally you may have the need to adjust a timestamp. The adjust() method takes a single parameter which can contain any relative time measurement that strtotime() accepts. Since fTimestamp is a value object, a new object is returned with the adjusted value. Here are some examples:

$new_timestamp = $timestamp1->adjust('tomorrow');
$new_timestamp = $timestamp2->adjust('+1 day');
$new_timestamp = $timestamp3->adjust('-2 years +1 week +5 hours');
$new_timestamp = $timestamp4->adjust('next wednesday');
$new_timestamp = $timestamp5->adjust('+1 hour');
$new_timestamp = $timestamp6->adjust('-2 hours +5 minutes +3 seconds');

Adjustments can also be adjustments of timezone. If a valid timezone is passed, the actual date/time will not be changed, however the date/time will appear different from format().

$new_timestamp = $timestamp1->adjust('America/Los_Angeles');

// Since the unix timestamp is always in UTC, these will be equal
if ($new_timestamp->format('U') == $timestamp1->format('U')) {
    echo 'The date/time has not changed, but the timezone has';
}

Formatting

To format the timestamp, simply call the format() method with any valid formatting string from date(). Here are some examples:

// Normal date/time formatting
echo $timestamp1->format('Y-m-d H:i:s');
echo $timestamp2->format('n/j/y g:ia');

// Using format to retrieve the timezone
echo $timestamp3->format('e');

Defining Formats

When dealing with date across a site or application, it is easy to create inconsistent formatting. In an effort to encourage consistency and at the same time prevent the need to clutter the global namespace with constants, the defineFormat() static method allows for creating named formats for use with the format() method and the method fTime::format() and fDate::format().

Definition of the formats would logically go in a configuration file. defineFormat() takes two parameters, the $name and the $formatting_string.

fTimestamp::defineFormat('list_date', 'n/j/y');
fTimestamp::defineFormat('list_time', 'g:ia');

Once defined, the format names can be passed into the format() method of fDate, fTime and fTimestamp.

echo $timestamp->format('list_date');
echo $time->format('list_time');

Comparing

There are five different methods available to compare timestamps, eq(), gt(), gte(), lt() and lte(). Each method optionally accepts a parameter $other_timestamp. If no $other_timestamp is specified, the timestamp is compared to the current timestamp. If $other_timestamp is specified, the two are compared. $other_timestamp accepts any valid date string that works with __construct().

Here are some examples:

$now = new fTimestamp();
$day_ago = new fTimestamp('-1 day');

// These return TRUE
$now->eq();
$now->eq('now');
$now->gt($day_ago);
$now->gte($day_ago);
$now->lt('+5 min');

// These calls return FALSE
$day_ago->gt($now);
$now->lt($day_ago);
$now->lte($day_ago);

Fuzzy Differences

If you are looking to get a fuzzy difference between two timestamps for display, youll want to use the getFuzzyDifference() method. The first parameter, $other_timestamp, optionally accepts a valid timestamp descriptor that can be passed to __construct(). If a valid timestamp descriptor is passed, the difference will be between the two timestamps, if nothing is passed, the difference will be between the fTimestamp and the current timestamp.

The value returned by getFuzzyDifference() will be a string representing the most broad time measurement between the two timestamps. In addition, if the difference is just shy of the next largest time measurement, it will be rounded up. Thus 3.5 weeks would become 1 month.

Here are some examples to clarify. The following examples are comparing two timestamps:

$timestamp1 = new fTimestamp('2008-01-01 8:00 am');
$timestamp2 = new fTimestamp('2008-01-04 5:00 pm');

echo $timestamp1->getFuzzyDifference($timestamp2);
// Output: 3 days before

echo $timestamp2->getFuzzyDifference('2008-01-01 8:00 am');
// Output: 3 days after

$timestamp3 = new fTimestamp('2008-01-10 1:00 am');

echo $timestamp3->getFuzzyDifference($timestamp1);
// Output: 1 week after

$timestamp4 = new fTimestamp('2008-01-28 12:00pm');

echo $timestamp4->getFuzzyDifference('2008-01-01 8:00 am');
// Output: 1 month after

These examples show output when comparing an fTimestamp object with the current timestamp:

// First, lets assume the current day/time is January 1st, 2008 at 9:00 am.

$timestamp1 = new fTimestamp('2008-01-01 12:00 pm');
$timestamp2 = new fTimestamp('2008-01-09 9:00 am');
$timestamp3 = new fTimestamp('2007-12-02 5:00 pm');

echo $timestamp1->getFuzzyDifference();
// Output: 3 hours from now

echo $timestamp2->getFuzzyDifference();
// Output: 1 week from now

echo $timestamp3->getFuzzyDifference();
// Output: 1 month ago

An optional boolean parameter, $simple, can also be passed to getFuzzyDifference(). When TRUE, this parameter causes the method to return the difference in time, but not the direction.

$timestamp1 = new fTimestamp('2008-01-01 8:00 am');
$timestamp2 = new fTimestamp('2008-01-04 5:00 pm');

echo $timestamp1->getFuzzyDifference($timestamp2, TRUE);
// Output: 3 days

echo $timestamp2->getFuzzyDifference('2008-01-01 8:00 am', TRUE);
// Output: 3 days

$timestamp3 = new fTimestamp('2008-01-10 1:00 am');

echo $timestamp3->getFuzzyDifference($timestamp1, TRUE);
// Output: 1 week

Localization

PHP contains built-in support for formatting date and times in different languages via the setlocale() function. This function, however, has a number of shortcomings including it requiring a non-threaded web server and requiring that locale files be installed for each locale to support.

The fTimestamp class provides a hook for formatting fDate, fTime and fTimestamp objects in whatever fashion is necessary. A callback can be assigned to the hook by passing it to the static method registerFormatCallback(). The callback should accept a single string and return a single string.

It is also possible to parse locale-specific date/time/timestamp strings by passing a callback to registerUnformatCallback(). The callback should accept a string and return a string that will properly be parsed by strtotime(). An example of a valid return string would be 2009-05-01 15:22:01.

Below is an example of how the hooks could be used.

function parse_uk_dates($date_time_string)
{
    if (preg_match('#^(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})$#', $date_time_string, $matches)) {
        if (strlen($matches[3]) == 2) {
            $matches[3] = ($matches[3] <= 37) ? '20' . $matches[3] : '19' . $matches[3];
        }
        return $matches[3] . '-' . $matches[2] . '-' . $matches[1];
    }
    return $date_time_string;
}
function translate_dates_to_spanish($formatted_string)
{
    $replacements = array(
        'Monday' => 'lunes',
        // ...
        'Mon' => 'lun',
        // ...
    );
    return strtr($formatted_string, $replacements);
}
fTimestamp::registerFormatCallback('translate_dates_to_spanish');
fTimestamp::registerUnformatCallback('parse_uk_dates');

fTimestamp API Reference

class, v1.0.0b13

Represents a date and time as a value object

Changes:
1.0.0b13Fixed a method signature 8/24/11
1.0.0b12Fixed a bug with the constructor not properly handling unix timestamps that are negative integers 6/2/11
1.0.0b11Changed the $timestamp and $timezone attributes to be protected 3/20/11
1.0.0b10Fixed a bug in __construct() with specifying a timezone other than the default for a relative time string such as "now" or "+2 hours" 7/5/10
1.0.0b9Added the $simple parameter to getFuzzyDifference() 3/15/10
1.0.0b8Fixed a bug with fixISOWeek() not properly parsing some ISO week dates 10/6/09
1.0.0b7Fixed a translation bug with getFuzzyDifference() 7/11/09
1.0.0b6Added registerUnformatCallback() and callUnformatCallback() to allow for localization of date/time parsing 6/1/09
1.0.0b5Backwards compatibility break - Removed getSecondsDifference() and getSeconds(), added eq(), gt(), gte(), lt(), lte() 3/5/09
1.0.0b4Updated for new fCore API 2/16/09
1.0.0b3Removed a useless double check of the strtotime() return value in __construct() 1/21/09
1.0.0b2Added support for CURRENT_TIMESTAMP, CURRENT_DATE and CURRENT_TIME SQL keywords 1/11/09
1.0.0bThe initial implementation 2/12/08

Variables

->timestamp protected

The date/time

Type

integer

->timezone protected

The timezone for this date/time

Type

string

Static Methods

::callFormatCallback() internal public

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

If a format callback is defined, call it

Signature

string callFormatCallback( string $formatted_string )

Parameters
string $formatted_string The formatted date/time/timestamp string to be (possibly) modified
Returns

The (possibly) modified formatted string

::callUnformatCallback() internal public

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

If an unformat callback is defined, call it

Signature

string callUnformatCallback( string $date_time_string )

Parameters
string $date_time_string A raw date/time/timestamp string to be (possibly) parsed/modified
Returns

The (possibly) parsed or modified date/time/timestamp

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::defineFormat() public

Creates a reusable format for formatting fDate, fTime, and fTimestamp objects

Signature

void defineFormat( string $name, string $formatting_string )

Parameters
string $name The name of the format
string $formatting_string The format string compatible with the date() function
::fixISOWeek() internal public

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

Fixes an ISO week format into 'Y-m-d' so strtotime() will accept it

Signature

string fixISOWeek( string $date )

Parameters
string $date The date to fix
Returns

The fixed date

::getDefaultTimezone() public

Provides a consistent interface to getting the default timezone. Wraps the date_default_timezone_get() function.

Signature

string getDefaultTimezone( )

Returns

The default timezone used for all date/time calculations

::isValidTimezone() internal public

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 timezone is valid

Signature

boolean isValidTimezone( string $timezone )

Parameters
string $timezone The timezone to check
Returns

If the timezone is valid

::registerFormatCallback() public

Allows setting a callback to translate or modify any return values from format(), fDateformat() and fTime::format()

Signature

void registerFormatCallback( callback $callback )

Parameters
callback $callback The callback to pass all formatted dates/times/timestamps through. Should accept a single string and return a single string.
::registerUnformatCallback() public

Allows setting a callback to parse any date strings passed into __construct(), fDate__construct() and fTime::__construct()

Signature

void registerUnformatCallback( callback $callback )

Parameters
callback $callback The callback to pass all date/time/timestamp strings through. Should accept a single string and return a single string that is parsable by strtotime().
::reset() internal public

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

Signature

void reset( )

::setDefaultTimezone() public

Provides a consistent interface to setting the default timezone. Wraps the date_default_timezone_set() function.

Signature

void setDefaultTimezone( string $timezone )

Parameters
string $timezone The default timezone to use for all date/time calculations
::translateFormat() internal public

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 format name set via defineFormat() and returns the date() function formatting string

Signature

string translateFormat( string $format )

Parameters
string $format The format to translate
Returns

The formatting string. If no matching format was found, this will be the same as the $format parameter.

Methods

->__construct() public

Creates the date/time to represent

Signature

fTimestamp __construct( fTimestamp|object|string|integer $datetime=NULL, string $timezone=NULL )

Parameters
fTimestamp|object|string|integer $datetime The date/time to represent, NULL is interpreted as now
string $timezone The timezone for the date/time. This causes the date/time to be interpretted as being in the specified timezone. If not specified, will default to timezone set by setDefaultTimezone().
Throws
fValidationException
When $datetime is not a valid date/time, date or time value
->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->__toString() public

Returns this date/time

Signature

string __toString( )

Returns

The 'Y-m-d H:i:s' format of this date/time

->adjust() public

Changes the date/time by the adjustment specified

Signature

fTimestamp adjust( string $adjustment )

Parameters
string $adjustment The adjustment to make - may be a relative adjustment or a different timezone
Returns

The adjusted date/time

Throws
fValidationException
When $adjustment is not a valid relative date/time measurement or timezone
->eq() public

If this timestamp is equal to the timestamp passed

Signature

boolean eq( fTimestamp|object|string|integer $other_timestamp=NULL )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to compare with, NULL is interpreted as today
Returns

If this timestamp is equal to the one passed

->format() public

Formats the date/time

Signature

string format( string $format )

Parameters
string $format The date() function compatible formatting string, or a format name from defineFormat()
Returns

The formatted date/time

->getFuzzyDifference() public

Returns the approximate difference in time, discarding any unit of measure but the least specific.

The output will read like:

  • "This timestamp is {return value} the provided one" when a timestamp it passed
  • "This timestamp is {return value}" when no timestamp is passed and comparing with the current timestamp

Examples of output for a timestamp passed might be:

  • '5 minutes after'
  • '2 hours before'
  • '2 days after'
  • 'at the same time'

Examples of output for no timestamp passed might be:

  • '5 minutes ago'
  • '2 hours ago'
  • '2 days from now'
  • '1 year ago'
  • 'right now'

You would never get the following output since it includes more than one unit of time measurement:

  • '5 minutes and 28 seconds'
  • '3 weeks, 1 day and 4 hours'

Values that are close to the next largest unit of measure will be rounded up:

  • '55 minutes' would be represented as '1 hour', however '45 minutes' would not
  • '29 days' would be represented as '1 month', but '21 days' would be shown as '3 weeks'
Signatures

string getFuzzyDifference( fTimestamp|object|string|integer $other_timestamp=NULL, boolean $simple=FALSE )

string getFuzzyDifference( boolean $simple=FALSE )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to create the difference with, NULL is interpreted as now
boolean $simple When TRUE, the returned value will only include the difference in the two timestamps, but not from now, ago, after or before
Returns

The fuzzy difference in time between the this timestamp and the one provided

->gt() public

If this timestamp is greater than the timestamp passed

Signature

boolean gt( fTimestamp|object|string|integer $other_timestamp=NULL )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to compare with, NULL is interpreted as now
Returns

If this timestamp is greater than the one passed

->gte() public

If this timestamp is greater than or equal to the timestamp passed

Signature

boolean gte( fTimestamp|object|string|integer $other_timestamp=NULL )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to compare with, NULL is interpreted as now
Returns

If this timestamp is greater than or equal to the one passed

->lt() public

If this timestamp is less than the timestamp passed

Signature

boolean lt( fTimestamp|object|string|integer $other_timestamp=NULL )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to compare with, NULL is interpreted as today
Returns

If this timestamp is less than the one passed

->lte() public

If this timestamp is less than or equal to the timestamp passed

Signature

boolean lte( fTimestamp|object|string|integer $other_timestamp=NULL )

Parameters
fTimestamp|object|string|integer $other_timestamp The timestamp to compare with, NULL is interpreted as today
Returns

If this timestamp is less than or equal to the one passed

->modify() public

Modifies the current timestamp, creating a new fTimestamp object

The purpose of this method is to allow for easy creation of a timestamp based on this timestamp. Below are some examples of formats to modify the current timestamp:

  • 'Y-m-01 H:i:s' to change the date of the timestamp to the first of the month:
  • 'Y-m-t H:i:s' to change the date of the timestamp to the last of the month:
  • 'Y-m-d 17:i:s' to set the hour of the timestamp to 5 PM:
Signature

fTimestamp modify( string $format, string $timezone=NULL )

Parameters
string $format The current timestamp will be formatted with this string, and the output used to create a new object. The format should not include the timezone (character e).
string $timezone The timezone for the new object if different from the current timezone
Returns

The new timestamp

fURL

The fURL class provides a simple and useful set of features for dealing with URLs in your site or application.

Getting URL Information

There are a few static methods that allow retrieval of URL information. All of these static methods return information about the URL as requested by the browser. All server-side rewrites are ignored.

Method Description
get() Returns the current URL minus the query string and domain name
getQueryString() Returns the current query string
getWithQueryString() Returns the current URL minus the domain name
getDomain() Returns the current domain name with protocol prefix

Here are examples of them being used:

// Since all of the methods are static, no instance of the fURL object is required
$url    = fURL::get();
$qs     = fURL::getQueryString();
$url_qs = fURL::getWithQueryString();
$domain = fURL::getDomain();

Modifying the Query String

Beyond simply retrieving information about the URL, the fURL class also provides some functionality to modify the query string.

The replaceInQueryString() and removeFromQueryString() methods allow addition/replacement and deletion of a values in the query string, respectively. Similar to the various get methods, these methods work with the URL before any server-side rewrites were applied.

replaceInQueryString() takes two parameters, $key and $value. Any existing value for the key will be replaced. If no value exists for the key, the key will be added with the new value. Here is the method in use:

// The following code assumes the query string is: example=one&example_2=false

// $new_qs would end up being: example=one&example_2=false&example_3=today
$new_qs  = fURL::replaceInQueryString('example_3', 'today');

// $new_qs would end up being: example=two&example_2=false
$new_qs  = fURL::replaceInQueryString('example', 'two');

removeFromQueryString() takes any number of parameters with each one being a GET key/field. Any matching key in the query string will be removed. Here is an example of the method in action:

// The following code assumes the query string is: example=one&example_2=false

// $new_qs would end up being: example=one
$new_qs  = fURL::removeFromQueryString('example_2');

Redirection

The redirect() method was built to simplify the task of redirecting a user to another web page. Simply pass the name of the site/page to redirect to as the only parameter and the method will determine the type of link it is (relative, web server absolute, etc.).

Since headers can only be sent before content, you will either need to ensure no content has been sent to the user yet, or that output buffering has been enabled. The fBuffer class may be of interest if you are going to use output buffering.

The method will redirect to almost any URL provided, and will do so in standard-compliant way by sending a fully-formed (including protocol and domain name) URL in the HTTP headers. Here are some examples of how to use the redirect() method:

// Redirect to an absolute web server path
fURL::redirect('/supplemental/example.php');

// Redirect to a relative page
fURL::redirect('example.php');

// Redirect to the current page with a different query string
fURL::redirect('?parameter=TRUE');

// Redirect to a fully-formed URL
fURL::redirect('http://example.com');
fURL::redirect('https://example.com/example.php');

URL Creation

When creating websites with friendly URLs it is useful to be able to convert the title of something into a human-readable url without having to worry about punctuation or characters with diacritics creating those nasty hexadecimal codes.

The makeFriendly() method performs this URL creation task. You can pass in any UTF-8 string and you will get back a URL made up of lowercase letters, numbers and the underscore character. All characters with diacritics will be converted to their plain counterparts.

Here are some examples:

// $url will be set to: /users/view/1/john_smith
$url = '/users/view/1/' . fURL::makeFriendly('John Smith');

// $url will be set to: /users/view/2/renee_smith
$url = '/users/view/2/' . fURL::makeFriendly('Rene Smith');

// $url will be set to: /news/view/1/notice_this_title_will_work_fine
$url = '/news/view/1/' . fURL::makeFriendly('Notice: This Title Will Work Fine!');

It is possible to limit the character length of the generated string by passing a number to the optional second parameter $max_length.

// Limit the URL to 40 characters
$limited_url = fURL::makeFriendly('This is a test of limiting the length of a URL', 40);

By default makeFriendly() uses the _ character to separate words. The optional third parameter, $delimiter, allows for changing this. If no length restriction is desired, the $delimiter can be passed as the second parameter.

// Limit the URL to 40 characters and use - for the delimiter
$limited_url = fURL::makeFriendly('This is a test of length limiting and using a different delimiter', 40, '-');

// Use - for the delimiter
$limited_url = fURL::makeFriendly('This is a test of using a separate delimiter', '-');

fURL API Reference

static class, v1.0.0b10

Provides functionality to retrieve and manipulate URL information

This class uses $_SERVER['REQUEST_URI'] for all operations, meaning that the original URL entered by the user will be used, or that any rewrites will not be reflected by this class.

Changes:
1.0.0b10Fixed some method signatures 8/24/11
1.0.0b9Fixed redirect() to handle no parameters properly 6/13/11
1.0.0b8Added the $delimiter parameter to makeFriendly() 6/3/11
1.0.0b7Fixed redirect() to be able to handle unqualified and relative paths 3/2/11
1.0.0b6Added the $max_length parameter to makeFriendly() 9/19/10
1.0.0b5Updated redirect() to not require a URL, using the current URL as the default 7/29/09
1.0.0b4getDomain() now includes the port number if non-standard 5/2/09
1.0.0b3makeFriendly() now changes _-_ to - and multiple _ to a single _ 3/24/09
1.0.0b2Fixed makeFriendly() so that _ doesn't appear at the beginning of URLs 3/22/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::get() public

Returns the requested URL, does no include the domain name or query string

This will return the original URL requested by the user - ignores all rewrites.

Signature

string get( )

Returns

The requested URL without the query string

::getDomain() public

Returns the current domain name, with protcol prefix. Port will be included if not 80 for HTTP or 443 for HTTPS.

Signature

string getDomain( )

Returns

The current domain name, prefixed by http:// or https://

::getQueryString() public

Returns the current query string, does not include parameters added by rewrites

Signature

string getQueryString( )

Returns

The query string

::getWithQueryString() public

Returns the current URL including query string, but without domain name - does not include query string parameters from rewrites

Signature

string getWithQueryString( )

Returns

The URL with query string

::makeFriendly() public

Changes a string into a URL-friendly string

Signatures

string makeFriendly( string $string, integer $max_length=NULL, string $delimiter=NULL )

string makeFriendly( string $string, string $delimiter=NULL )

Parameters
string $string The string to convert
integer $max_length The maximum length of the friendly URL
string $delimiter The delimiter to use between words, defaults to _
Returns

The URL-friendly version of the string

::redirect() public

Redirects to the URL specified, without requiring a full-qualified URL

  • If the URL starts with /, it is treated as an absolute path on the current site
  • If the URL starts with http:// or https://, it is treated as a fully-qualified URL
  • If the URL starts with anything else, including a ?, it is appended to the current URL
  • If the URL is ommitted, it is treated as the current URL
Signature

void redirect( string $url=NULL )

Parameters
string $url The url to redirect to
::removeFromQueryString() public

Removes one or more parameters from the query string

This method uses the query string from the original URL and will not contain any parameters that are from rewrites.

Signature

string removeFromQueryString( string $parameter [, ... ] )

Parameters
string $parameter [, ... ] A parameter to remove from the query string
Returns

The query string with the parameter(s) specified removed, first character is ?

::replaceInQueryString() public

Replaces a value in the query string

This method uses the query string from the original URL and will not contain any parameters that are from rewrites.

Signature

string replaceInQueryString( string|array $parameter, string|array $value )

Parameters
string|array $parameter The query string parameter
string|array $value The value to set the parameter to
Returns

The full query string with the parameter replaced, first char is ?

fUTF8

The class fUTF8 is a static class that provides UTF-8 compatible versions of almost every string function that is provided with PHP. Since UTF-8 uses multiple bytes of data for some characters and the built-in PHP string functions are built to work with single-byte encodings, many of the PHP string functions will perform incorrectly on UTF-8 strings.

There is a PHP extension called mbstring that is designed for dealing with multi-byte string encodings, however it is not installed by default, does not include many commonly used functions, and contains some bugs. The fUTF8 class will use the mbstring extension for performance benefits in appropriate situations if it is installed.

Method to Function Mapping

The table below contains a list of the built-in PHP string functions with the equivalent fUTF8 method beside it. Any additional features or differences will also be listed.

PHP Function !fUTF8 Method Differences
chr() chr() Accepts U+hex or decimal Unicode code point instead of ASCII decimal value
explode() explode() Parameter order is switched to $string, $delimeter - also accepts NULL delimeter to explode into characters
ltrim() ltrim()
ord() ord() Returns U+hex Unicode code point instead of ASCII decimal value
rtrim() rtrim()
str_ireplace() ireplace()
str_pad() pad()
str_replace() replace()
strcasecmp() icmp() Letters that are ASCII letters with diacritics are sorted right after the base ASCII letter
strcmp() cmp() Letters that are ASCII letters with diacritics are sorted right after the base ASCII letter
stripos() ipos()
stristr() istr()
strlen() len()
strnatcasecmp() inatcmp() Letters that are ASCII letters with diacritics are sorted right after the base ASCII letter
strnatcmp() natcmp() Letters that are ASCII letters with diacritics are sorted right after the base ASCII letter
strpos() pos()
strrev() rev()
strripos() irpos()
strrpos() rpos()
strstr() str()
strtolower() lower()
strtoupper() upper()
substr() sub()
trim() trim()
ucfirst() ucfirst()
ucwords() ucwords()
wordwrap() wordwrap()

Cleaning Strings (Security)

Due to the way that UTF-8 is implemented, certain character combinations are not allowed. Allowing such invalid data into a system could easily lead to all sorts of bugs with character parsing. To solve this issue, the clean() method will remove any malformed UTF-8 characters from a string.

This method should be used when importing data into a system from an external data source that may contain invalid data. Please note that fRequest::get() and fCookie::get() automatically call this method, so it is not necessary to clean it again.

$cleaned_string = fUTF8::clean($imported_string);

fUTF8 API Reference

static class, v1.0.0b16

Provides string functions for UTF-8 strings

This class is implemented to provide a UTF-8 version of almost every built-in PHP string function. For more information about UTF-8, please visit UTF-8.

Changes:
1.0.0b16Added code to clean() to use mbstring if available since recent versions of iconv and IGNORE now return FALSE for bad encodings 9/21/12
1.0.0b15Fixed a bug with using IBM's iconv implementation on AIX 7/29/11
1.0.0b14Added a workaround for iconv having issues in MAMP 1.9.4+ 7/26/11
1.0.0b13Fixed notices from being thrown when invalid data is sent to clean() 6/10/11
1.0.0b12Fixed a variable name typo in sub() 5/9/11
1.0.0b11Updated the class to not using phpinfo() to determine the iconv implementation 11/4/10
1.0.0b10Fixed a bug with capitalizing a lowercase i resulting in a dotted upper-case I 11/1/10
1.0.0b9Updated class to use fCore::startErrorCapture() instead of error_reporting() 8/9/10
1.0.0b8Removed e flag from preg_replace() calls 6/8/10
1.0.0b7Added the methods trim(), rtrim() and ltrim() 5/11/10
1.0.0b6Fixed clean() to work with PHP installs that use an iconv library that doesn't support IGNORE 3/2/10
1.0.0b5Changed ucwords() to also uppercase words right after various punctuation 9/18/09
1.0.0b4Changed replacement values in preg_replace() calls to be properly escaped 6/11/09
1.0.0b3Fixed a parameter name in rpos() from $search to $needle 2/6/09
1.0.0b2Fixed a bug in explode() with newlines and zero-length delimiters 2/5/09
1.0.0bThe initial implementation 6/1/08

Static Methods

::ascii() internal public

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

Maps UTF-8 ASCII-based latin characters, puntuation, symbols and number forms to ASCII

Any characters or symbols that can not be translated will be removed.

This function is most useful for situation that only allows ASCII, such as in URLs.

Translates elements form the following unicode blocks:

  • Latin-1 Supplement
  • Latin Extended-A
  • Latin Extended-B
  • IPA Extensions
  • Latin Extended Additional
  • General Punctuation
  • Letterlike symbols
  • Number Forms
Signature

string ascii( string $string )

Parameters
string $string The string to convert
Returns

The input string in pure ASCII

::chr() public

Converts a unicode value into a UTF-8 character

Signature

string chr( mixed $unicode_code_point )

Parameters
mixed $unicode_code_point The character to create, either the U+hex or decimal code point
Returns

The UTF-8 character

::clean() public

Removes any invalid UTF-8 characters from a string or array of strings

Signature

string clean( array|string $value )

Parameters
array|string $value The string or array of strings to clean
Returns

The cleaned string

::cmp() public

Compares strings, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters

Please note that this function sorts based on English language sorting rules only. Locale-sepcific sorting is done by strcoll(), however there are technical limitations.

Signature

integer cmp( string $str1, string $str2 )

Parameters
string $str1 The first string to compare
string $str2 The second string to compare
Returns

< 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2

::explode() public

Explodes a string on a delimiter

If no delimiter is provided, the string will be exploded with each characters being an element in the array.

Signature

array explode( string $string, string $delimiter=NULL )

Parameters
string $string The string to explode
string $delimiter The string to explode on. If NULL or '' this method will return one character per array index.
Returns

The exploded string

::icmp() public

Compares strings in a case-insensitive manner, with the resulting order having characters that are based on ASCII letters placed after the relative ASCII characters

Please note that this function sorts based on English language sorting rules only. Locale-sepcific sorting is done by strcoll(), however there are technical limitations.

Signature

integer icmp( string $str1, string $str2 )

Parameters
string $str1 The first string to compare
string $str2 The second string to compare
Returns

< 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2

::inatcmp() public

Compares strings using a natural order algorithm in a case-insensitive manner, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters

Please note that this function sorts based on English language sorting rules only. Locale-sepcific sorting is done by strcoll(), however there are technical limitations.

Signature

integer inatcmp( string $str1, string $str2 )

Parameters
string $str1 The first string to compare
string $str2 The second string to compare
Returns

< 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2

::ipos() public

Finds the first position (in characters) of the search value in the string - case is ignored when doing performing a match

Signature

mixed ipos( string $haystack, string $needle, integer $offset=0 )

Parameters
string $haystack The string to search in
string $needle The string to search for. This match will be done in a case-insensitive manner.
integer $offset The character position to start searching from
Returns

The integer character position of the first occurence of the needle or FALSE if no match

::ireplace() public

Replaces matching parts of the string, with matches being done in a a case-insensitive manner

If $search and $replace are both arrays and $replace is shorter, the extra $search string will be replaced with an empty string. If $search is an array and $replace is a string, all $search values will be replaced with the string specified.

Signature

string ireplace( string $string, mixed $search, mixed $replace )

Parameters
string $string The string to perform the replacements on
mixed $search The string (or array of strings) to search for - see method description for details
mixed $replace The string (or array of strings) to replace with - see method description for details
Returns

The input string with the specified replacements

::irpos() public

Finds the last position (in characters) of the search value in the string - case is ignored when doing performing a match

Signature

mixed irpos( string $haystack, string $needle, integer $offset=0 )

Parameters
string $haystack The string to search in
string $needle The string to search for. This match will be done in a case-insensitive manner.
integer $offset The character position to start searching from. A negative value will stop looking that many characters from the end of the string
Returns

The integer character position of the last occurence of the needle or FALSE if no match

::istr() public

Matches a string needle in the string haystack, returning a substring from the beginning of the needle to the end of the haystack

Can optionally return the part of the haystack before the needle. Matching is done in a case-insensitive manner.

Signature

mixed istr( string $haystack, string $needle, boolean $before_needle=FALSE )

Parameters
string $haystack The string to search in
string $needle The string to search for. This match will be done in a case-insensitive manner.
boolean $before_needle If a substring of the haystack before the needle should be returned instead of the substring from the needle to the end of the haystack
Returns

The specified part of the haystack, or FALSE if the needle was not found

::len() public

Determines the length (in characters) of a string

Signature

integer len( string $string )

Parameters
string $string The string to measure
Returns

The number of characters in the string

::lower() public

Converts all uppercase characters to lowercase

Signature

string lower( string $string )

Parameters
string $string The string to convert
Returns

The input string with all uppercase characters in lowercase

::ltrim() public

Trims whitespace, or any specified characters, from the beginning of a string

Signature

string ltrim( string $string, string $charlist=NULL )

Parameters
string $string The string to trim
string $charlist The characters to trim
Returns

The trimmed string

::natcmp() public

Compares strings using a natural order algorithm, with the resulting order having latin characters that are based on ASCII letters placed after the relative ASCII characters

Please note that this function sorts based on English language sorting rules only. Locale-sepcific sorting is done by strcoll(), however there are technical limitations.

Signature

integer natcmp( string $str1, string $str2 )

Parameters
string $str1 The first string to compare
string $str2 The second string to compare
Returns

< 0 if $str1 < $str2, 0 if they are equal, > 0 if $str1 > $str2

::ord() public

Converts a UTF-8 character to a unicode code point

Signature

string ord( string $character )

Parameters
string $character The character to decode
Returns

The U+hex unicode code point for the character

::pad() public

Pads a string to the number of characters specified

Signature

string pad( string $string, integer $pad_length, string $pad_string=' ', string $pad_type='right' )

Parameters
string $string The string to pad
integer $pad_length The character length to pad the string to
string $pad_string The string to pad the source string with
string $pad_type The type of padding to do: 'left', 'right', 'both'
Returns

The input string padded to the specified character width

::pos() public

Finds the first position (in characters) of the search value in the string

Signature

mixed pos( string $haystack, string $needle, integer $offset=0 )

Parameters
string $haystack The string to search in
string $needle The string to search for
integer $offset The character position to start searching from
Returns

The integer character position of the first occurence of the needle or FALSE if no match

::replace() public

Replaces matching parts of the string

If $search and $replace are both arrays and $replace is shorter, the extra $search string will be replaced with an empty string. If $search is an array and $replace is a string, all $search values will be replaced with the string specified.

Signature

string replace( string $string, mixed $search, mixed $replace )

Parameters
string $string The string to perform the replacements on
mixed $search The string (or array of strings) to search for - see method description for details
mixed $replace The string (or array of strings) to replace with - see method description for details
Returns

The input string with the specified replacements

::reset() internal public

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

Signature

void reset( )

::rev() public

Reverses a string

Signature

string rev( string $string )

Parameters
string $string The string to reverse
Returns

The reversed string

::rpos() public

Finds the last position (in characters) of the search value in the string

Signature

mixed rpos( string $haystack, string $needle, integer $offset=0 )

Parameters
string $haystack The string to search in
string $needle The string to search for.
integer $offset The character position to start searching from. A negative value will stop looking that many characters from the end of the string
Returns

The integer character position of the last occurence of the needle or FALSE if no match

::rtrim() public

Trims whitespace, or any specified characters, from the end of a string

Signature

string rtrim( string $string, string $charlist=NULL )

Parameters
string $string The string to trim
string $charlist The characters to trim
Returns

The trimmed string

::str() public

Matches a string needle in the string haystack, returning a substring from the beginning of the needle to the end of the haystack

Can optionally return the part of the haystack before the needle.

Signature

mixed str( string $haystack, string $needle, boolean $before_needle=FALSE )

Parameters
string $haystack The string to search in
string $needle The string to search for
boolean $before_needle If a substring of the haystack before the needle should be returned instead of the substring from the needle to the end of the haystack
Returns

The specified part of the haystack, or FALSE if the needle was not found

::sub() public

Extracts part of a string

Signature

mixed sub( string $string, integer $start, integer $length=NULL )

Parameters
string $string The string to extract from
integer $start The zero-based starting index to extract from. Negative values will start the extraction that many characters from the end of the string.
integer $length The length of the string to extract. If an empty value is provided, the remainder of the string will be returned.
Returns

The extracted subtring or FALSE if the start is out of bounds

::trim() public

Trims whitespace, or any specified characters, from the beginning and end of a string

Signature

string trim( string $string, string $charlist=NULL )

Parameters
string $string The string to trim
string $charlist The characters to trim, .. indicates a range
Returns

The trimmed string

::ucfirst() public

Converts the first character of the string to uppercase.

Signature

string ucfirst( string $string )

Parameters
string $string The string to process
Returns

The processed string

::ucwords() public

Converts the first character of every word to uppercase

Words are considered to start at the beginning of the string, or after any whitespace character.

Signature

string ucwords( string $string )

Parameters
string $string The string to process
Returns

The processed string

::upper() public

Converts all lowercase characters to uppercase

Signature

string upper( string $string )

Parameters
string $string The string to convert
Returns

The input string with all lowercase characters in uppercase

::wordwrap() public

Wraps a string to a specific character width

Signature

string wordwrap( string $string, integer $width=75, string $break="\n", boolean $cut=FALSE )

Parameters
string $string The string to wrap
integer $width The character width to wrap to
string $break The string to insert as a break
boolean $cut If words longer than the character width should be split to fit
Returns

The input string with all lowercase characters in uppercase

fUnbufferedResult

The fUnbufferedResult class is an iterable result object that is returned when an SQL query is executed. It contains similar information to fResult, however some features are missing due to the nature of the results not being buffered. The quality of being unbuffered causes this object to use less memory than an fResult object.

Creation

The fUnbufferedResult class can be created by executing the methods fDatabase::unbufferedQuery() or fDatabase::unbufferedTranslatedQuery().

// Creation of fUnbufferedResult from a normal query
$result = $db->unbufferedQuery("SELECT * FROM users");

// Creation of an fUnbufferedResult object from a translated query
$result2 = $db->unbufferedTranslatedQuery("SELECT * FROM users LIMIT 5");

Row Format

By default when retrieving rows from an unbuffered result object, they are returned as associative arrays. In the case of Oracle databases, where columns are case-insensitive, the array keys are lowercase.

It is possible to retrieve stdClass objects back for each row instead of associative arrays. This is done by calling the method asObjects(). The method returns the fUnbufferedResult object, allowing for method chaining:

// Set the result to return objects
$res = $db->unbufferedQuery("SELECT * FROM users")->asObjects();

If you are looking for objects with more functionality, please see fActiveRecord.

Unescaping Data

While it is certainly possible to manually unescape data from a result set, one row and column at a time, unescaping the whole result is often much easier. The method unescape() accepts an associative array of the column name as the key and the data type as the value. This method should be called before any rows are fetched.

$result = $db->unbufferedQuery("SELECT * FROM users");
$result->unescape(array(
    'first_name'       => 'string',
    'last_name'        => 'string',
    'is_authenticated' => 'boolean',
    'last_login'       => 'timestamp'
));

foreach ($result as $row) {
    // $row now contains the data in native PHP data types
}

Iteration

The fUnbufferedResult class implements the Iterator interface, which means that you can use the foreach construct to loop through all resulting rows. Below is an example of iterating over a result:

$result = $db->unbufferedQuery("SELECT * FROM users");

foreach ($result as $row) {
    echo $row['name'] . '<br />';
}

If the result did not return any rows, the foreach construct will not be looped at all and the execution of the code will continue. If you wish to execute different code if no rows are returned, see the next section about exceptions.

If you wish to to manual iteration you can use the fetchRow() and valid() methods as shown below:

// Iteration using valid() and fetchRow()
while ($result->valid()) {
    $row = $result->fetchRow();
}

Exceptions

As mentioned in the last section, if you iterate through the result and no rows are returned, nothing will happen. If you do need to execute different code when no rows are returned, you will want to call the tossIfNoRows() to cause an fNoRowsException to be thrown:

try {
    $result = $db->unbufferedQuery("SELECT * FROM users");
    $result->tossIfNoRows();
    ?>
    <h1>Users</h1>
    <?
    foreach ($result as $row) {
        echo $row['name'] . '<br />';
    }
} catch (fNoRowsException $e) {
    ?>
    <p>No users were found</p>
    <?php
}

In addition to calling tossIfNoRows(), an fNoRowsException will be thrown if any Iterator interface methods (such as current(), next(), etc) or fetchRow() is called on a result object which returned no rows.

Query Info

Since fUnbufferedResult does not use buffering, only a fraction of the information about a query is available compared to the fResult class.

Method Description
getSQL() Returns the SQL statement executed for this result
getUntranslatedSQL() Returns the SQL from before translation happened - only applicable for results from unbufferedTranslatedQuery()

fUnbufferedResult API Reference

class, implements Iterator , v1.0.0b13

Representation of an unbuffered result from a query against the fDatabase class

Changes:
1.0.0b13Added a workaround for iconv having issues in MAMP 1.9.4+ 7/26/11
1.0.0b12Fixed MSSQL to have a properly reset row array, added silenceNotices(), fixed pdo_dblib on Windows when using the Microsoft DBLib driver 5/9/11
1.0.0b11Fixed some bugs with the mysqli extension and prepared statements 8/28/10
1.0.0b10Backwards Compatibility Break - removed ODBC support 7/31/10
1.0.0b9Added IBM DB2 support 4/13/10
1.0.0b8Added support for prepared statements 3/2/10
1.0.0b7Fixed a bug with decoding MSSQL national column when using an ODBC connection 9/18/09
1.0.0b6Added the method unescape(), changed tossIfNoRows() to return the object for chaining 8/12/09
1.0.0b5Added the method asObjects() to allow for returning objects instead of associative arrays 6/23/09
1.0.0b4Fixed a bug with not properly converting SQL Server text to UTF-8 6/18/09
1.0.0b3Added support for Oracle, various bug fixes 5/4/09
1.0.0b2Updated for new fCore API 2/16/09
1.0.0bThe initial implementation 5/7/08

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::silenceNotices() public

Turns off notices about broken database extensions much as the MSSQL DBLib driver

Signature

void silenceNotices( )

Methods

->__construct() internal public

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

Configures the result set

Signature

fUnbufferedResult __construct( fDatabase $database, string $character_set=NULL )

Parameters
fDatabase $database The database object this result was created from
string $character_set MSSQL only: the character set to transcode from since MSSQL doesn't do UTF-8
->__destruct() internal public

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

Frees up the result object

Signature

void __destruct( )

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->asObjects() public

Sets the object to return rows as objects instead of associative arrays (the default)

Signature

fUnbufferedResult asObjects( )

Returns

The result object, to allow for method chaining

->current() internal public implements Iterator

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

Returns the current row in the result set (required by iterator interface)

Signature

array|stdClass current( )

Returns

The current row

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->fetchRow() public

Returns the row next row in the result set (where the pointer is currently assigned to)

Signature

array|stdClass fetchRow( )

Returns

The next row in the result

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->getResult() internal public

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

Returns the result

Signature

mixed getResult( )

Returns

The result of the query

->getSQL() public

Returns the SQL used in the query

Signature

string getSQL( )

Returns

The SQL used in the query

->getUntranslatedSQL() public

Returns the SQL as it was before translation

Signature

string getUntranslatedSQL( )

Returns

The SQL from before translation

->key() internal public implements Iterator

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

Returns the current row number (required by iterator interface)

Signature

integer key( )

Returns

The current row number

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->next() internal public implements Iterator

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

Advances to the next row in the result (required by iterator interface)

Signature

void next( )

Throws
fNoRemainingException
When there are no rows left in the result
fNoRowsException
When the query did not return any rows
->rewind() internal public implements Iterator

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

Rewinds the query (required by iterator interface)

Signature

void rewind( )

->setResult() internal public

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 the result from the query

Signature

void setResult( mixed $result )

Parameters
mixed $result The result from the query
->setSQL() internal public

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 the SQL used in the query

Signature

void setSQL( string $sql )

Parameters
string $sql The SQL used in the query
->setUntranslatedSQL() internal public

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 the SQL from before translation

Signature

void setUntranslatedSQL( string $untranslated_sql )

Parameters
string $untranslated_sql The SQL from before translation
->tossIfNoRows() public

Throws an fNoResultException if the query did not return any rows

Signature

fUnbufferedResult tossIfNoRows( string $message=NULL )

Parameters
string $message The message to use for the exception if there are no rows in this result set
Returns

The result object, to allow for method chaining

Throws
fNoRowsException
When the query did not return any rows
->unescape() public

Sets the result object to unescape all values as they are retrieved from the object

The data types should be from the list of types supported by fDatabase::unescape().

Signature

fUnbufferedResult unescape( array $column_data_type_map )

Parameters
array $column_data_type_map An associative array with column names as the keys and the data types as the values
Returns

The result object, to allow for method chaining

->valid() public implements Iterator

Returns if the query has any rows left

Signature

boolean valid( )

Returns

If the iterator is still valid

fUnexpectedException

fUnexpectedException is a sub-class of fException meant to provide a common parent class for all exceptions that should not be expected or handled in the site/application code.

Special Functionality

All classes that extend fUnexpectedException change the functionality of the printMessage() method. Instead of printing the exception message, a generic error message is printed. These classes are designed to be handled by the fCore::enableExceptionHandling() method, which intercepts all uncaught exceptions and delivers the backtrace via one of three methods (see the fCore class for details).

Child Classes

Below is a list of all child classes that extend fUnexpectedException. These classes are not documented using a Throws: entry in the API documentation since they should not be handled via normal site/application code.

Class Description
fConnectivityException This type of exception indicates some sort of connection error occured, whether it be with a database server, remote API, etc.
fEnvironmentException This type of exception indicates something is wrong with the server environment that is preventing proper execution of code.
fProgrammerException This type of exception indicates that the programming building the site/application wrote invalid code, or somehow otherwise violated an expected format or sequence.
fSQLException This type of exception indicates an error was returned when executing an SQL query. This will not be thrown in the situation where a query returns no results, but rather when SQL syntax is incorrect.

fUnexpectedException API Reference

class, v1.0.0b2

An exception that should probably not be handled by the display code, fCore::enableExceptionHandler() is recommended

Changes:
1.0.0b2Updated printMessage() to use an ASCII dash to prevent encoding issues when an output encoding is not specified 5/9/11
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fUnexpectedException
Child Classes
fConnectivityException
An exception caused by a connectivity error
fSQLException
An exception occurred while executing a SQL statement
fProgrammerException
An exception caused by programmer error
fEnvironmentException
An exception caused by an environment error such a file permissions
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

Methods

->printMessage() public

Prints out a generic error message inside of a div with the class being 'exception {exception_class_name}'

Signature

void printMessage( )

Overrides
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'

fUpload

The fUpload class provides functionality to validate and move files uploaded through HTML forms.

Prerequisites

In order to use the fUpload class, a file needs to uploaded to PHP by the POST HTTP method. If the POST is coming from an HTML form, the form will need to have its enctype set to multipart/form-data.

<form action="" method="POST" enctype="multipart/form-data">
    <fieldset>
        <p>
            <label for="file">File</label>
            <input id="file" type="file" name="file" />
        </p>
    </fieldset>
</form>

Instantiation

The constructor does not accept any parameters.

$uploader = new fUpload();

Setting Options (Security)

Uploaded files can be validated by configuring the fUpload instance using setMIMETypes(), setMaxSize(), setOptional(), enableOverwrite(), allowPHP() and allowDotFiles(). All of these methods must be called before the methods move() or validate() are called.

setMIMETypes()

As with all other data accepted from users, file uploads should be filtered to only allow safe content. The method setMIMETypes() allows defining the mime types to allow with an appropriate error message. The first parameter is an array of valid mime types and the second parameter is the error message to display if the mime types are not matched.

$uploader->setMIMETypes(
    array(
        'image/gif',
        'image/jpeg',
        'image/pjpeg',
        'image/png'
    ),
    'The file uploaded is not an image'
);

While the $_FILES superglobal includes a mime type for the uploaded file, fUpload does not use this value since it is provided by the user and thus not trustworthy. Instead, mime type checking is done on the server side by inspecting the contents of the file. Please see the fFile::getMimeType() method documentation for a list of all supported mime types.

setMaxSize()

setMaxSize() allows validating a file to ensure it does not exceed a specific size. It accepts a single parameter, $size, that can be any filesize with units or an integer to be interpreted as bytes.

This method works in concert with two other settings, the upload_max_filesize ini setting and the MAX_FILE_SIZE hidden input. The upload_max_filesize ini setting controls the maximum size of a file that can be uploaded by a user, and must be larger or equal to the value passed to setMaxSize(). This setting can not be controlled in the PHP script since file uploads are processed before the request executes PHP.

The MAX_FILE_SIZE hidden input is primarily designed for the convenience of end-users since it will reject large files before they are fully uploaded. Since the hidden input element is controlled by the program submitting the request, it can no be trusted and the setMaxSize() method must be used as a server-side failsafe.

// These three method calls will all limit the uploaded file to 2 megabytes
$uploader->setMaxSize('2m');
$uploader->setMaxSize('2MB');
$uploader->setMaxSize('2048k');

setOptional()

By default, fUpload treats every file as required and will throw and exception or return an error if no file is uploaded. The method setOptional() changes this behavior to not treat the absence of a file as an error. This will cause the move() method to return NULL when no file is uploaded.

$uploader->setOptional();

enableOverwrite()

When uploading files, if there is an existing file with the same name in the destination directory the fUpload class will rename the uploaded file to a unique name. If this is not the desired action, the method enableOverwrite() will allow the class to overwrite existing files.

$uploader->enableOverwrite();

allowPHP(), allowDotFiles()

By default the fUpload class does not allow files ending in .php, .php4 or .php5 or beginning with . to be uploaded due to security concerns. If there is a need for PHP files to uploaded the method allowPHP() can be called. Files beginning with . may be allowed by calling allowDotFiles().

$uploader->allowPHP();
$uploader->allowDotFiles();

Validation/Moving

Once options have been set it is time to try moving (or possibly just validating) the uploaded file or files. The method move() will move the file uploading in the specified field to the directory requested, while the validate() method will simply ensure the uploaded file matches the options that were set. Calling move() will also cause validate() to happen.

move()

The move() accepts three parameters, the $directory to move the file to, the $field the file was uploaded through and optionally the $index of the file if the file upload was part of an array of file upload fields. The return value is an fFile object representing the newly moved file. An fValidationException will be thrown if no file was uploaded or one of the options is not met.

// An example of uploading a file passing the destination directory as a string
$file = $uploader->move('/uploaded/file/destination', 'file_input_name');

// An example of passing the destination directory as an fDirectory object
$dir = new fDirectory('/some/directory');
$file = $uploader->move($dir, 'file_input_name');

If setOptional() has been called and no file was uploaded, NULL will be returned instead of an fFile object.

validate()

The validate() method accepts two parameters, the $field to validate and optionally the $index of the file if the file upload was part of an array of file upload fields. As with all other Flourish methods that begin with validate, this method will throw an fValidationException if one of the options is not met or no file is uploaded.

$uploader->validate('file_input_name');

It is also possible to return an error instead of throwing an exception by passing TRUE as the second parameter.

// $error will contain any error that occurs, or NULL if there is none
$error = $uploader->validate('file_input_name', TRUE);

Arrays of File Uploads

When it is necessary to have a user upload multiple file, it is possible to take advantage of PHP array syntax for input fields. Any input field name that ends in [] will cause an array to be created in the $_GET or $_POST superglobals for normal inputs, or in the $_FILES superglobal for file uploads. Please note that on the PHP side, the [] is dropped from the input name, e.g. file_input_name[] would be found in $_FILES['file_input_name'].

<form action="" method="POST" enctype="multipart/form-data">
    <fieldset>
        <p>
            <label for="file_1">File 1</label>
            <input id="file_1" type="file" name="file_input_name[]" />
        </p>
        <p>
            <label for="file_2">File 2</label>
            <input id="file_2" type="file" name="file_input_name[]" />
        </p>
    </fieldset>
</form>

fUpload provides the static method count() to determine how many files were uploaded by a user. This can be used with the optional third parameter, $index, of move() or optional second parameter, $index of validate() to perform the action on the specified file.

$files    = array();
$uploaded = fUpload::count('file_input_name');
for ($i=0; $i < $uploaded; $i++) {
    $files[] = $uploader->move($dir, 'file_input_name', $i);
}

When an index is specified for validate(), the boolean $return_message parameter to return the message instead of throw an exception is passed as the third parameter.

// Return an error for the second file upload named attachment
$error = $uploader->validate('attachment', 1, TRUE);

Filtering

When multiple files are uploaded for a field, it is common for one or more file upload fields to not actually upload a file. The static method filter() accepts a $field name and will removed info about any file upload fields where a file was not uploaded.

fUpload::filter('attachments');
$uploader   = new fUpload();
$attachment = array();
$uploaded   = fUpload::count('attachments');
for ($i=0; $i < $uploaded; $i++) {
    $attachments[] = $uploader->move($dir, 'attachments', $i);
}

fUpload API Reference

class, v1.0.0b15

Provides validation and movement of uploaded files

Changes:
1.0.0b15Fixed an undefined variable error in setMaxSize() 9/16/12
1.0.0b14Fixed some method signatures 8/24/11
1.0.0b13Changed the class to throw fValidationException objects instead of fProgrammerException objects when the form is improperly configured - this is to prevent error logs when bad requests are sent by scanners/hackers 8/24/11
1.0.0b12Fixed the filter() callback constant 11/24/10
1.0.0b11Added setImageDimensions() and setImageRatio() 11/11/10
1.0.0b10BackwardsCompatibilityBreak - renamed setMaxFilesize() to setMaxSize() to be consistent with fFile::getSize() 5/30/10
1.0.0b9BackwardsCompatibilityBreak - the class no longer accepts uploaded files that start with a . unless allowDotFiles() is called - added setOptional() 5/30/10
1.0.0b8BackwardsCompatibilityBreak - validate() no longer returns the $_FILES array for the file being validated - added $return_message parameter to validate(), fixed a bug with detection of mime type for text files 5/26/10
1.0.0b7Added filter() to allow for ignoring array file upload field entries that did not have a file uploaded 10/6/09
1.0.0b6Updated move() to use the new fFilesystem::createObject() method 1/21/09
1.0.0b5Removed some unnecessary error suppression operators from move() 1/5/09
1.0.0b4Updated validate() so it properly handles upload max filesize specified in human-readable notation 1/5/09
1.0.0b3Removed the dependency on fRequest 1/5/09
1.0.0b2Fixed a bug with validating filesizes 11/25/08
1.0.0bThe initial implementation 6/14/07

Static Methods

::check() public

Checks to see if the field specified is a valid file upload field

Signature

boolean check( string $field, boolean $throw_exception=TRUE )

Parameters
string $field The field to check
boolean $throw_exception If an exception should be thrown when there are issues with the form
Returns

If the field is a valid file upload field

Throws
fValidationException
If $throw_exception is TRUE and the request was not a POST or the content type is not multipart/form-data
::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::count() public

Returns the number of files uploaded to a file upload array field

Signature

integer count( string $field )

Parameters
string $field The field to get the number of files for
Returns

The number of uploaded files

Throws
fValidationException
If the form is not properly configured for file uploads
::filter() public

Removes individual file upload entries from an array of file inputs in $_FILES when no file was uploaded

Signature

array filter( string $field )

Parameters
string $field The field to filter
Returns

The indexes of the files that were uploaded

Throws
fValidationException
If the form is not properly configured for file uploads

Methods

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->allowDotFiles() public

Sets the upload class to allow files starting with a .

Files starting with . may change the behaviour of web servers, for instance .htaccess files for Apache.

Signature

void allowDotFiles( )

->allowPHP() public

Sets the upload class to allow PHP files

Signature

void allowPHP( )

->enableOverwrite() public

Set the class to overwrite existing files in the destination directory instead of renaming the file

Signature

void enableOverwrite( )

->move() public

Moves an uploaded file from the temp directory to a permanent location

Signature

fFile|NULL move( string|fDirectory $directory, string $field, mixed $index=NULL )

Parameters
string|fDirectory $directory The directory to upload the file to
string $field The file upload field to get the file from
mixed $index If the field was an array file upload field, upload the file corresponding to this index
Returns

An fFile (or fImage) object, or NULL if no file was uploaded

Throws
fValidationException
When the form is not setup for file uploads, the $directory is somehow invalid or validate() thows an exception
->setImageDimensions() public

Sets the allowable dimensions for an uploaded image

Signature

void setImageDimensions( integer $min_width, integer $min_height, integer $max_width=0, integer $max_height=0 )

Parameters
integer $min_width The minimum width - 0 for no minimum
integer $min_height The minimum height - 0 for no minimum
integer $max_width The maximum width - 0 for no maximum
integer $max_height The maximum height - 0 for no maximum
->setImageRatio() public

Sets the allowable dimensions for an uploaded image

Signature

void setImageRatio( numeric $width, numeric $height, string $allow_excess_dimension )

Parameters
numeric $width The minimum ratio width
numeric $height The minimum ratio height
string $allow_excess_dimension The dimension that should allow for excess pixels
->setMaxSize() public

Sets the maximum size the uploaded file may be

This method should be used with the MAX_FILE_SIZE hidden form input since the hidden form input will reject a file that is too large before the file completely uploads, while this method will wait until the whole file has been uploaded. This method should always be used since it is very easy for the MAX_FILE_SIZE post field to be manipulated on the client side.

This method can only further restrict the upload_max_filesize ini setting, it can not increase that setting. upload_max_filesize must be set in the php.ini (or an Apache configuration) since file uploads are handled before the request is handed off to PHP.

Signature

void setMaxSize( string $size )

Parameters
string $size The maximum file size (e.g. 1MB, 200K, 10.5M) - 0 for no limit
->setMIMETypes() public

Sets the file mime types accepted

Signature

void setMIMETypes( array $mime_types, string $message )

Parameters
array $mime_types The mime types to accept
string $message The message to display if the uploaded file is not one of the mime type specified
->setOptional() public

Sets the file upload to be optional instead of required

Signature

void setOptional( )

->validate() public

Validates the uploaded file, ensuring a file was actually uploaded and that is matched the restrictions put in place

Signatures

NULL|string validate( string $field, mixed $index=NULL, boolean $return_message=NULL )

NULL|string validate( string $field, boolean $return_message=NULL )

Parameters
string $field The field the file was uploaded through
mixed $index If the field was an array of file uploads, this specifies which one to validate
boolean $return_message If any validation error should be returned as a string instead of being thrown as an fValidationException
Returns

If $return_message is not TRUE or if no error occurs, NULL, otherwise a string error message

Throws
fValidationException
When the form is not configured for file uploads, no file is uploaded or the uploaded file violates the options set for this object

fValidation

The fValidation class provides an interface for validating standalone forms, such as a contact form. In these situations there are usually a mix of required fields and fields that require specific formatting. In addition, form contents that are emailing will often need to be checked to prevent email header injection attacks.

Instantiation

The fValidation class must be instantiated before options can be set and the validation can be performed.

$validator = new fValidation();

Setting Fields and Rules

To have the class actually perform any validation it will need to know what fields and rules it is checking.

Simple Field Validation

The simplest validation is done by specifying individual fields against data types or input formats.

Method Description
addRequiredFields() Requires that the field(s) specified have a value.
addBooleanFields() Requires that the field(s) specified can be interpreted as a boolean if a value is entered.
addDateFields() Requires that the field(s) specified can be interpreted as a date if a value is entered.
addFloatFields() Requires that the field(s) specified contain a floating point/decimal number if a value is entered.
addIntegerFields() Requires that the field(s) specified contain an integer if a value is entered.
addEmailFields() Requires that the fields specified are formatted like an email address if a value is entered.
addEmailHeaderFields() Checks field(s) for line breaks to prevent email header injection. Should be used whenever embedding user input into email headers.
addURLFields() Requires that the field(s) specified are formatted like a full URL (http://example.com) if a value is entered.

Each of these methods takes any number of parameters with each parameter being a field name. Subsequent calls to the methods will add to the list of fields.

// Require that name and email be filled in
$validator->addRequiredFields('name', 'email');

// Require that name and email be filled in, and that the email field contains an email address
$validator->addRequiredFields('name');
$vaildator->addRequiredFields('email');
$validator->addEmailFields('email');

// Require that name be filled in, and if birthday is specified that
// it will be interpreted as a date or time by fTimestamp
$validator->addRequiredFields('name');
$validator->addDateFields('birthday');

Rule Validation

More advanced validation rules can be checked by using the following methods.

addCallbackRule()

addCallbackRule() accepts a $field to check, a $callback to execute and retrieve a boolean result from, and a $message to use when the result is FALSE.

function check_length($value) {
    return strlen($value) >= 8;
}
$validator->addCallbackRule('password', 'check_length', 'Please enter at least 8 characters');
addConditionalRule()

The method addConditionalRule() allows requiring one or more fields, if another field (or set of fields) has any value, or a value from a pre-defined set. The first parameter $main_fields accepts a single string field, or an array of fields. The second parameter, $conditional_values, accepts NULL or an array of values. The third parameter, $conditional_fields, accepts a single string field, or an array of fields.

If $conditional_values is NULL, and value present in any of the $main_fields will cause all $conditional_fields to be required. If $conditional_values is an array of values, if any of the $main_fields contains one of those values, all of the $conditional_columns will be required.

// If the address, city, state_province, country or postal_code is filled
// in, require that at least the address and city are filled in
$validator->addConditionalRule(
    array('address', 'city', 'state_province', 'country', 'postal_code'),
    NULL,
    array('address', 'city')
);

// If the country is US or Canada, require the state_province and postal_code
$validator->addConditionalRule('country', array('US', 'CA'), array('state_province', 'postal_code'));
addFileUploadRule()

When creating a form that includes file uploads via fUpload, the addFileUploadRule() method will combine the validation performed by fUpload with the rest of the fValidation fields and rules. The first parameter is the file upload $field, the second parameter is the fUpload instance, $uploader. All validation options for the file, including if it is required, are set via the fUpload object.

$uploader = new fUpload();
// Make the file upload optional
$uploader->setOptional();
$uploader->setMaxSize('1MB');
$validator->addFileUploadRule('attachment', $uploader);

If a file upload field being validated is part of an array of file uploads, the array index can be passed as the second parameter to addFileUploadRule(), and the $uploader parameter should be passed as the third parameter.

$uploader = new fUpload();
// Validate just the first attachment
$validator->addFileUploadRule('attachment', 0, $uploader);
addOneOrMoreRule()

The method addOneOrMoreRule() accepts two or more $field parameters, and requires that at least one of them has a value.

$validator->addOneOrMoreRule('email', 'phone', 'cell_phone');
addOnlyOneRule()

The method addOnlyOneRule() accepts two or more $field parameters, and requires that exactly one of them has a value. It is considered an error if none, or more than one have a value.

$validator->addOnlyOneRule('ssn', 'ein');
addRegexRule()

The addRegexRule() method allows validating non-blank values against a Perl-compatible regular expression (PCRE). The first parameter is the $field to validate, followed by the $regex to test and the $message to use if the value does not match the expression.

// Require the value is a length in in or cm
$validator->addRegexRule('length', '#^\d+\s*(in|cm)$#', 'Please enter a length');
addValidValuesRule()

A value can be tested against a set of valid values by calling the method addValidValuesRule(). This method accepts a $field to validate, followed by an array of $valid_values.

// Require the value be 'yes', 'no' or 'maybe'
$validator->addValidValuesRule('answer', array('yes', 'no', 'maybe'));

Performing Validation

Once all of the fields and rules have been set, the validate() method will check them all against the current request using fRequest. If any errors are found an fValidationException will be thrown containing an explanation of the errors that occurred.

The validate() method will always evaluate all fields and rules so users will not have to submit a form multiple times to figure out everything they are doing wrong. Here is a full example:

try {
    $validator = new fValidation();
    
    $validator->addRequiredFields('name');
    $validator->addOneOrMoreRule('email', 'phone'); 
    $validator->addEmailFields('email');
    $validator->addEmailHeaderFields('name', 'email');
    $validator->addDateFields('birthday');
    $validator->addURLFields('my_blog');

    $validator->validate();

    // Here would be the action of the contact form, such as sending an email

} catch (fValidationException $e) {
    fMessaging::create('error', $e->getMessage());
}

The exception message that is generated will contain HTML including <p>The following problems were found:</p> followed by a <ul> containing one <li> for each error detected. Below are some examples.

<p>The following problems were found:</p>
<ul>
    <li>Name: Please enter a value</li>
    <li>Email, Phone: Please enter at least one</li>
</ul>
<p>The following problems were found:</p>
<ul>
    <li>Email: Please enter an email address in the form name@example.com</li>
</ul>

Returning Messages

If an exception is not desired, an array of individual error messages can be retrieved by passing TRUE to the first parameter of validate(), $return_messages. The returned array will have keys that are the field name for individual fields, or the field names joined by ,s for multiple fields. The values will be the error messages, including formatted field names.

// Return the messages instead of throwing an exception
$messages = $validator->validate(TRUE);

The $messages array will have a structure such as:

array(
    'name'        => 'Name: Please enter a value',
    'email,phone' => 'Email, Phone: Please enter at least one'
)

If the messages are being used for inline validation, it may be useful to remove the field names from the error messages. The field names can be removed by passing TRUE to the second parameter of validate(), $remove_field_names.

$messages = $validator->validate(TRUE, TRUE);

In this case, the $messages array would look like:

array(
    'name'        => 'Please enter a value',
    'email,phone' => 'Please enter at least one'
)

Throwing an Exception and Returning Messages

Often the most usable way to notify users of an error is to produce a full error message listing all errors on a form, but also to individually highlight the fields with errors. The $return_messages parameter and fValidationException can be used together to provide such functionality.

First, the messages should be returned by passing TRUE to the $return_messages parameter. The static method fValidationException::removeFieldNames() can be used to remove the field names from the error messages for inline field highlighting and then the fValidationException constructor can be used to generate a full error message.

try {
    // ...
    
    $errors       = $validator->validate(TRUE);
    $field_errors = fValidationException::removeFieldNames($errors);
    if ($errors) {
        throw new fValidationException(
            'The following problems were found:',
            $errors
        );
    }
    
    // ...
    
} catch (fValidationException $e) {
    // fMessaging allows for creating session-based one-time-use messages
    fMessaging::create('error', $e->getMessage();
    fMessaging::create('field_errors', $field_errors);
}

Message Customization

Three methods exist to customize the messages generated, overrideFieldName(), addRegexReplacement() and addStringReplacement(). Both regular expression and string replacements are suitable for changing error messages to use slightly different wording. More advanced message modification can be done library wide by using the features of fText.

overrideFieldName()

By default, all field names are converted from field_name to Field Name for all error messages by fGrammar::humanize(). This conversion may be incorrect for non-English words or acronyms. overrideFieldName() accepts a $field and the $name to use for it.

$validator->overrideFieldName('zip_code', 'ZIP Code');

It is also possible to pass an associative array with fields as the keys and names as the values.

$validator->overrideFieldName(array(
    'zip_code'       => 'ZIP Code',
    'state_province' => 'State/Province'
));

overrideFieldName() can be called any number of times in either the two parameter, or one parameter signatures. If a field name is overridden more than once, the last override will be the one used.

addRegexReplacement()

addRegexReplacement() accepts two parameters, the Perl-compatible regular expression to $search for and the string to $replace it with. This search and replace action is performed right before messages are reordered.

$validator->addRegexReplacement('#Please enter a value$#', 'Please enter something');

Please note that $replace should be a string with all normal $ and \ characters escaped with a \ since preg_replace() is used under-the-hood. Because preg_replace() is used, it is also possible to use back-references in $replace.

// This will change "Name: Please enter a value" to "Please enter a value for Name"
$validator->addRegexReplacement('#^(.*?): (.*)$#', '\2 for \1');

It is also possible to pass an associative array as a single with they keys being $search expressions and the values being $replace strings.

$validator->addRegexReplacement(array(
    '#Please enter a value$#' => 'Please enter something',
    '#^(.*?): (.*)$#'          => '\2 for \1'
));

addRegexReplacement() can be called any number of times, and the replacements will be added together. If a search value is passed more than once, the $replace parameter from the last method call will be the one used.

addStringReplacement()

The addStringReplacement() method works exactly like addRegexReplacement(), except that the first parameter is a string to $search for and the second is plain string to $replace with. $replace does not require any escaping and obviously does not support back-references.

$validator->addStringReplacement('Please enter a value', 'Please enter something');

Also supported is a single parameter containing an associative array with the key being a string to search for and the value being the string to replace it with.

$validator->addStringReplacement(array(
    'Please enter a value'        => 'Please enter something',
    'Please enter a whole number' => 'Please enter an integer'
));

addStringReplacement() can be called any number of times to add multiple search/replace pairs. If the same search string is passed more than once, the replace string from the last method call will be used.

Ordering Errors

The order in which error messages appear in an exception, or the returned messages array, is based upon what order the class checks the various field and rules. Thus, while sometimes the Email field may appear first in the list, other times it may appear last. The setMessageOrder() method allows for consistent ordering of messages.

setMessageOrder() accepts any number of parameters, which are strings to search for. Any message that contains the first parameter will be listed first, any message that contains the second parameter will be listed second, and so on. Matches are done in a longest match first to ensure accidental matches dont occur. Errors that do not match any parameter are left at the end.

With the following exception message:

The following problems were found:
 - Last Name: Please enter a value
 - First Name: Please enter a value
 - Comments: Please enter a value
 - Company Email: Please enter a value
 - Personal Email: Please enter an email address in the form name@example.com

And calling setMessageOrder() with the following parameters:

$validator->setMessageOrder('First Name', 'Last Name', 'Email', 'Company Email', 'Comments');

Will result in the message being changed to:

The following problems were found:
 - First Name: Please enter a value
 - Last Name: Please enter a value
 - Personal Email: Please enter an email address in the form name@example.com
 - Company Email: Please enter a value
 - Comments: Please enter a value

Notice that Personal Email is listed before Company Email - this is because the fourth parameter 'Company Email' was a longer match and the error was placed into position 4, while Personal Email was matched by the shorter parameter 3 and was placed in position 3.

Messages that match the parameters will always be ordered in the same relative order as the parameters, although they may not have the exact same position. This may occur if there is no error that matches a parameter, or if multiple messages match a parameter.

fValidation API Reference

class, v1.0.0b12

Provides validation routines for standalone forms, such as contact forms

Changes:
1.0.0b12Fixed some method signatures 8/24/11
1.0.0b11Fixed addCallbackRule() to be able to handle multiple rules per field 6/2/11
1.0.0b10Fixed addRegexRule() to be able to handle multiple rules per field 8/30/10
1.0.0b9Enhanced all of the add fields methods to accept one field per parameter, or an array of fields 6/24/10
1.0.0b8Added/fixed support for array-syntax fields names 6/9/10
1.0.0b7Added the ability to pass an array of replacements to addRegexReplacement() and addStringReplacement() 5/31/10
1.0.0b6BackwardsCompatibilityBreak - moved one-or-more required fields from addRequiredFields() to addOneOrMoreRule(), moved conditional required fields from addRequiredFields() to addConditionalRule(), changed returned messages array to have field name keys - added lots of functionality 5/26/10
1.0.0b5Added the $return_messages parameter to validate() and updated code for new fValidationException API 9/17/09
1.0.0b4Changed date checking from strtotime() to fTimestamp for better localization support 6/1/09
1.0.0b3Updated for new fCore API 2/16/09
1.0.0b2Added support for validating date and URL fields 1/23/09
1.0.0bThe initial implementation 6/14/07

Static Methods

::compose() protected

Composes text using fText if loaded

Signature

string compose( string $message, mixed $component [, ... ] )

Parameters
string $message The message to compose
mixed $component [, ... ] A string or number to insert into the message
Returns

The composed and possible translated message

::stringlike() protected

Returns TRUE for non-empty strings, numbers, objects, empty numbers and string-like numbers (such as 0, 0.0, '0')

Signature

boolean stringlike( mixed $value )

Parameters
mixed $value The value to check
Returns

If the value is string-like

Methods

->__get() internal public

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

Signature

callback __get( string $method )

Parameters
string $method The method to create a callback for
Returns

The callback for the method requested

->addBooleanFields() public

Adds fields to be checked for 1/0, t/f, true/false, yes/no

Signatures

fValidation addBooleanFields( string $field [, ... ] )

fValidation addBooleanFields( array $fields )

Parameters
string $field [, ... ] A field that should contain a boolean value
array $fields Any number of fields that should contain a boolean value
Returns

The validation object, to allow for method chaining

->addCallbackRule() public

Adds a callback validation of a field, with a custom error message

Signature

fValidation addCallbackRule( string $field, callback $callback, string $message )

Parameters
string $field The field to test with the callback
callback $callback The callback to test the value with - this callback should accept a single string parameter and return a boolean
string $message The error message to return if the regular expression does not match the value
Returns

The validation object, to allow for method chaining

->addConditionalRule() public

Adds fields to be conditionally required if another field has any value, or specific values

Signature

fValidation addConditionalRule( string|array $main_fields, mixed $conditional_values, string|array $conditional_fields )

Parameters
string|array $main_fields The fields(s) to check for a value
mixed $conditional_values If NULL, any value in the main field(s) will trigger the conditional field(s), otherwise the value must match this scalar value or be present in the array of values
string|array $conditional_fields The field(s) that are to be required
Returns

The validation object, to allow for method chaining

->addDateFields() public

Adds form fields to the list of fields to be blank or a valid date

Use addRequiredFields() disallow blank values.

Signatures

fValidation addDateFields( string $field [, ... ] )

fValidation addDateFields( array $fields )

Parameters
string $field [, ... ] A field that should contain a valid date
array $fields Any number of fields that should contain a valid date
Returns

The validation object, to allow for method chaining

->addEmailFields() public

Adds form fields to the list of fields to be blank or a valid email address

Use addRequiredFields() disallow blank values.

Signatures

fValidation addEmailFields( string $field [, ... ] )

fValidation addEmailFields( array $fields )

Parameters
string $field [, ... ] A field that should contain a valid email address
array $fields Any number of fields that should contain a valid email address
Returns

The validation object, to allow for method chaining

->addEmailHeaderFields() public

Adds form fields to be checked for email injection

Every field that is included in email headers should be passed to this method.

Signatures

fValidation addEmailHeaderFields( string $field [, ... ] )

fValidation addEmailHeaderFields( array $fields )

Parameters
string $field [, ... ] A field to be checked for email injection
array $fields Any number of fields to be checked for email injection
Returns

The validation object, to allow for method chaining

->addFileUploadRule() public

Add a file upload field to be validated using an fUpload object

Signatures

fValidation addFileUploadRule( string $field, mixed $index, fUpload $uploader )

fValidation addFileUploadRule( string $field, fUpload $uploader )

Parameters
string $field The field to validate
mixed $index The index for array file upload fields
fUpload $uploader The uploader to validate the field with
Returns

The validation object, to allow for method chaining

->addFloatFields() public

Adds fields to be checked for float values

Signatures

fValidation addFloatFields( string $field [, ... ] )

fValidation addFloatFields( array $fields )

Parameters
string $field [, ... ] A field that should contain a float value
array $fields Any number of fields that should contain a float value
Returns

The validation object, to allow for method chaining

->addIntegerFields() public

Adds fields to be checked for integer values

Signatures

fValidation addIntegerFields( string $field [, ... ] )

fValidation addIntegerFields( array $fields )

Parameters
string $field [, ... ] A field that should contain an integer value
array $fields Any number of fields that should contain an integer value
Returns

The validation object, to allow for method chaining

->addOneOrMoreRule() public

Adds a rule to make sure at least one field of multiple has a value

Signature

fValidation addOneOrMoreRule( string $field, string $field_2 [, ... ] )

Parameters
string $field One of the fields to check for a value
string $field_2 [, ... ] Another field to check for a value
Returns

The validation object, to allow for method chaining

->addOnlyOneRule() public

Adds a rule to make sure at exactly one field of multiple has a value

Signature

fValidation addOnlyOneRule( string $field, string $field_2 [, ... ] )

Parameters
string $field One of the fields to check for a value
string $field_2 [, ... ] Another field to check for a value
Returns

The validation object, to allow for method chaining

->addRegexReplacement() public

Adds a call to preg_replace() for each message

Replacement is done right before the messages are reordered and returned.

If a message is an empty string after replacement, it will be removed from the list of messages.

Signatures

fValidation addRegexReplacement( string $search, string $replace )

fValidation addRegexReplacement( array $replacements )

Parameters
string $search The PCRE regex to search for - see http://php.net/pcre for details
string $replace The string to replace with - all $ and \ are used in back references and must be escaped with a \ when meant literally
array $replacements An associative array with keys being regular expressions to search for and values being the string to replace with
Returns

The validation object, to allow for method chaining

->addRegexRule() public

Adds regular expression validation of a field, with a custom error message

Signature

fValidation addRegexRule( string $field, string $regex, string $message )

Parameters
string $field The field to test with the regular expression
string $regex The PCRE regex to search for - see http://php.net/pcre for details
string $message The error message to return if the regular expression does not match the value
Returns

The validation object, to allow for method chaining

->addRequiredFields() public

Adds form fields to be required

Signatures

fValidation addRequiredFields( string $field [, ... ] )

fValidation addRequiredFields( array $fields )

Parameters
string $field [, ... ] A field to require a value for
array $fields Any number of fields to require a value for
Returns

The validation object, to allow for method chaining

->addStringReplacement() public

Adds a call to str_replace() for each message

Replacement is done right before the messages are reordered and returned.

If a message is an empty string after replacement, it will be removed from the list of messages.

Signatures

fValidation addStringReplacement( string $search, string $replace )

fValidation addStringReplacement( array $replacements )

Parameters
string $search The string to search for
string $replace The string to replace with
array $replacements An associative array with keys being strings to search for and values being the string to replace with
Returns

The validation object, to allow for method chaining

->addURLFields() public

Adds form fields to the list of fields to be blank or a valid URL

Use addRequiredFields() disallow blank values.

Signatures

fValidation addURLFields( string $field [, ... ] )

fValidation addURLFields( array $fields )

Parameters
string $field [, ... ] A field that should contain a valid URL
array $fields Any number of fields that should contain a valid URL
Returns

The validation object, to allow for method chaining

->addValidValuesRule() public

Adds a rule to make sure a field has one of the specified valid values

A strict comparison will be made from the string request value to the array of valid values.

Signature

fValidation addValidValuesRule( string $field, array $valid_values )

Parameters
string $field The field to check the value of
array $valid_values The valid values
Returns

The validation object, to allow for method chaining

->overrideFieldName() public

Allows overriding the default name used for a field in the error message

By default, all fields are referred to by the field name run through fGrammar::humanize(). This may not be correct for acronyms or complex field names.

Signatures

fValidation overrideFieldName( string $field, string $name )

fValidation overrideFieldName( array $field_names )

Parameters
string $field The field to set the custom name for
string $name The custom name for the field
array $field_names An associative array of custom field names where the keys are the field and the values are the names
Returns

The validation object, to allow for method chaining

->setMessageOrder() public

Allows setting the order that the individual errors in a message will be displayed

All string comparisons during the reordering process are done in a case-insensitive manner.

Signature

fValidation setMessageOrder( string $match, string $match_2=NULL [, ... ] )

Parameters
string $match The string match to order first
string $match_2 [, ... ] The string match to order second
Returns

The validation object, to allow for method chaining

->validate() public

Checks for required fields, email field formatting and email header injection using values previously set

Signature

void|array validate( boolean $return_messages=FALSE, boolean $remove_field_names=FALSE )

Parameters
boolean $return_messages If an array of validation messages should be returned instead of an exception being thrown
boolean $remove_field_names If field names should be removed from the returned messages, leaving just the message itself
Returns

If $return_messages is TRUE, an array of validation messages will be returned

Throws
fValidationException
When one of the options set for the object is violated

fValidationException

fValidationException is a sub-class of fExpectedException that indicates that some sort of input did not meet the requirements of the code. This class is used by Flourish code quite a bit and would be suitable to toss on almost any web site or application at some point.

Message with a List

This functionality is specific to fValidationException, where it is most common and useful. In addition to being able to pass a $message to __construct() (and possibly some values to interpolate), it is possible to pass a string $message followed by an array of $sub_messages. The $message will be placed in a <p> tag and the $sub_messages will each be placed in an <li> tag inside of a <ul> tag.

Thus, the following example code:

throw new fValidationException(
    'The following problems were found:',
    array(
        'First Name: Please enter a value',
        'Last Name: Please enter a value',
        'Email: Please enter your email in the format name@host.com'
    )
);

will create the message:

<p>The following problems were found:</p>
<ul>
    <li>First Name: Please enter a value</li>
    <li>Last Name: Please enter a value</li>
    <li>Email: Please enter your email in the format name@host.com</li>
</ul>

Field Name Formatting

Both fValidation and fORMValidation throw fValidationException objects when data has been detected that does not validate according to the established rules. In the message for these exceptions there are unordered lists of each field/column name followed by the error message for that field/column. By default the field/column name is printed followed by : .

Sometimes it may be desirable to change the formatting of the field name, to include HTML tags such as <strong> or <em>. The static method setFieldFormat() accepts a single parameter that is the format for field/column names. The token %s is replaced with the field/column name. Any literal % symbols should be changed to %% to prevent formatting issues.

Here is an example of adding <strong> tags around field/column names:

fValidationException::setFieldFormat('<strong>%s</strong>: ');

Removing Field Names

When fActiveRecord::validate() or fValidation::validate() is called with the $return_messages parameter set to TRUE, an array of error messages will be returned, with the column or field names included in the error messages. For situations where the messages are going to be displayed next to the field in question, having the field name in the actual error message is not desirable. The static method removeFieldNames() will accept an array of error messages and will return an array with the same error messages, sans field names.

The code below will produce a full exception including the field names, but will also provide an associative array of the key being the field/column and the value being the error message without the field name.

$errors = $user->validate(TRUE);
$field_errors = fValidationException::removeFieldNames($errors);
if ($errors) {
    throw new fValidationException('The following problems were found:', $errors);
}

The exception message would be:

The following problems were found:
 - First Name: Please enter a value
 - Last Name: Please enter a value

And the $field_errors array would contain:

array(
    'first_name' => 'Please enter a value',
    'last_name'  => 'Please enter a value'
);

fValidationException API Reference

class, v1.0.0b4

An exception caused by a data not matching a rule or set of rules

Changes:
1.0.0b4Added support for nested error arrays 10/3/10
1.0.0b3Added removeFieldNames() 5/26/10
1.0.0b2Added a custom __construct() to handle arrays of messages 9/17/09
1.0.0bThe initial implementation 6/14/07

Genealogy

Class Tree
Exception
   |
   --fException
      |
      --fExpectedException
         |
         --fValidationException
Inherited Variables
$code
$file
$line
$message
$previous
$string
$trace
Inherited Methods
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization
fException::__get()
All requests that hit this method should be requests for callbacks
fException::compose()
Composes text using fText if loaded
fException::dump()
Creates a string representation of any variable using predefined strings for booleans, NULL and empty strings
fException::formatTrace()
Gets the backtrace to currently called exception
fException::getCSSClass()
Returns the CSS class name for printing information about the exception
fException::prepare()
Prepares content for output into HTML
fException::printMessage()
Prints the message inside of a div with the class being 'exception %THIS_EXCEPTION_CLASS_NAME%'
fException::printTrace()
Prints the backtrace to currently called exception inside of a pre tag with the class being 'exception %THIS_EXCEPTION_CLASS_NAME% trace'
fException::registerCallback()
Adds a callback for when certain types of exceptions are created
fException::reorderMessage()
Reorders list items in the message based on simple string matching
fException::setMessage()
Allows the message to be overwriten
fException::splitMessage()
Splits an exception with an HTML list into multiple strings each containing part of the original message
constructor __construct ( [$message = ], [$code = ], [$previous = ] )
__clone ( )
__toString ( )
getCode ( )
getFile ( )
getLine ( )
getMessage ( )
getPrevious ( )
getTrace ( )
getTraceAsString ( )

Static Variables

::$field_format protected

The formatting string to use for field names

Type

string

Static Methods

::formatField() public

Accepts a field name and formats it based on the formatting string set via setFieldFormat()

Signature

string formatField( string $field )

Parameters
string $field The name of the field to format
Returns

The formatted field name

::removeFieldNames() public

Removes the field names from normal validation messages, leaving just the message part

Signature

array removeFieldNames( array $messages )

Parameters
array $messages The messages to remove the field names from
Returns

The messages without field names

::setFieldFormat() public

Set the format to be applied to all field names used in fValidationExceptions

The format should contain exactly one %s sprintf() conversion specification, which will be replaced with the field name. Any literal % characters should be written as %%.

The default format is just %s: , which simply inserts a : and space after the field name.

Signature

void setFieldFormat( string $format )

Parameters
string $format A string to format the field name with - %s will be replaced with the field name

Methods

->__construct() public

Sets the message for the exception, allowing for custom formatting beyond fException

If this method receives exactly two parameters, a string and an array, the string will be used as a message in a HTML <p> tag and the array will be turned into an unorder list <ul> tag with each element in the array being an <li> tag. It is possible to pass an optional exception code as a third parameter.

The following PHP:

throw new fValidationException(
    'The following problems were found:',
    array(
        'Please provide your name',
        'Please provide your email address'
    )
);

Would create the message:

The following problems were found:

  • Please provide your name
  • Please provide your email address

If the parameters are anything else, they will be passed to fException::__construct().

Signature

fException __construct( string $message='', array $sub_messages, mixed $code )

Parameters
string $message The beginning message for the exception. This will be placed in a <p> tag.
array $sub_messages An array of strings to place in a <ul> tag
mixed $code The optional exception code
Overrides
fException::__construct()
Sets the message for the exception, allowing for string interpolation and internationalization

fXML

The fXML class provides functionality to read and create XML. An fXML object represents an XML string for reading/traversing, but does not allow modification. The static methods encode() and sendHeader() are useful when creating XML.

Instantiation

The fXML constructor will accept four different types of sources for the XML to represent:

  1. An fFile object
  2. A file path
  3. A URL
  4. An XML string
// From an fFile
$file = new fFile('/path/to/file');
$xml  = new fXML($file);

// From a file path
$xml  = new fXML('/path/to/file');

// From a URL
$xml  = new fXML('http://example.com/rss');

// From an XML string
$xml  = new fXML('<?xml ');

XML files with invalid data will cause an fValidationException to be thrown.

When fetching XML from a URL, an optional second parameter, $timeout, can be specified. If not specified, the default_socket_timeout ini setting is used.

// Fetch the XML with a 5 second timeout
$xml = new fXML('http://examples.com/rss', 5);

Fixing Invalid XML

Two of the most common errors when creating XML are encoding characters as HTML entities for XML and delivering ISO-8859-1 (or Windows-1252) encoded XML as UTF-8. An optional boolean parameter, $fix_entities_encoding can fix both of these problems.

// Used with an HTTP timeout
$xml = new fXML('http://example.com/rss', 5, TRUE);

// Used without a timeout
$xml = new fXML('./foo.xml', TRUE);
Invalid Named Entities

In XML, there are only five pre-defined named entities: &amp;, &gt;, &lt;, &quot; and &apos;. All other named entities from HTML will cause a parse error if included in XML without further encoding. For instance, &rsquo; is invalid, but &amp;rsquo; or is valid.

$fix_entities_encoding will take

<book><title>Isn&rsquo;t Valid XML</title></book>

and convert it to

<book><title>Isnt Valid XML</title></book>

before passing the XML to the parser.

Incorrectly Encoded Content

If no encoding is specified for an XML document, the encoded is assumed to be UTF-8. Many developers not familiar with XML and issues related to encoding will omit the encoding attribute of the <?xml> tag and will insert ISO-8859-1 or Windows-1252 (also called Latin or Latin 1) content.

fXML will throw an exception when such an XML document is parsed, since the parser being used will find invalid UTF-8 characters and give up encoding. The $fix_entities_encoding parameter will detect non-UTF-8 characters for documents defined as UTF-8 (whether explicitly or by omission of the encoding attribute) and convert the content.

Element Information

Information about the root XML element that was passed into the constructor can be accessed by the following methods:

Method Description
getName() Returns the name of the element without any preceding namespace prefix
getNamespace() Returns the full namespace URI for the element, if any
getPrefix() Returns the namespace prefix for the element, if any
getText() Returns the text content of the element

Raw XML

The raw XML being modeled by the fXML object can be retrieved by calling the method toXML().

echo $xml->toXML();

Attribute Values

Attributes of the XML element can be accessed using array notation.

// Normal attribute
echo $xml['my_attribute'];

// Attribute with namespace prefix
echo $xml['ns:attribute'];

Child Element Text Content

The text content of children of the XML element can be accessed by requesting object memebers.

// The content of the child element "firstName"
echo $xml->firstName;

// Child "title" in the "ns" prefix
echo $xml->{'ns:title'};

Traversing

For anything beyond attribute and child element text content of the root XML element, the xpath() method will need to be used. This method returns an array of all matching parts of the XML file.

If you aren't familiar with XPath, the Wikipedia page about XPath 1.0 is a good place to start. XPath allow traversal of the XML file using element names combined with predicates and functions.

xpath() accepts two parameter, the XPath $path, and optionally a boolean flag, $first_only, to return only the first match.

// Select every "item" element in the "channel"
foreach ($xml->xpath("channel/item") as $item) {
    echo $item->title;
}

The items matched by xpath() may be child elements, text content or attributes. Here are the data types for various types of matches:

It is also possible to pull back just the first matched element by passing the second parameter as TRUE.

// Pull the first attribute only, thus returning a string
echo $xml->xpath('@attribute', TRUE);

Custom Prefixes

When using XPath, array-style attribute access or child element text content access, it can be useful to set a custom namespace prefix to allow for compatibility with various XML sources. The method addCustomPrefix() accepts two parameters, the $ns_prefix to register and its corresponding $namespace. One registered, this prefix will be available throughout fXML.

$xml = new fXML('http://example.com/rss');
$xml->addCustomPrefix('pf', 'http://namspace');

echo $xml['pf:attribute'];
echo $xml->{'pf:child'};
foreach ($xml->xpath('pf:*') as $element) {
    echo $element->getText();
}

Encoding Data

When creating XML documents, such as RSS feeds, it is necessary to create properly encoded XML, otherwise strict XML parsers will not be able to parse the document. The encode() method will ensure that all content is properly encoded for including in a UTF-8 XML file.

Here is an example of usage:

$xml  = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
$xml .= "<articles>";

foreach ($articles as $article) {
    $xml .= "<article><title>";
    $xml .= fXML::encode($article->getTitle());
    $xml .= "</title><description>";
    $xml .= fXML::encode($article->getDescription());
    $xml .= "</description></article>";
}

$xml .= "</articles>";

Sending the Content Type Header

When sending XML files over HTTP, the method sendHeader() should be called ensure that the Content-Type header is set to the correct value of text/xml; charset=utf-8. The utf-8 character set encoding is specified since all of Flourish is built to work with UTF-8 and all XML parsers must support that character set.

fXML API Reference

class, implements ArrayAccess , v1.0.0b8

Provides functionality for XML files

This class is implemented to use the UTF-8 character encoding. Please see UTF-8 for more information.

Changes:
1.0.0b8Fixed a method signature 8/24/11
1.0.0b7Added a workaround for iconv having issues in MAMP 1.9.4+ 7/26/11
1.0.0b6Updated class to use fCore::startErrorCapture() instead of error_reporting() 8/9/10
1.0.0b5Added the $fix_entities_encoding parameter to __construct() 8/8/10
1.0.0b4Updated the class to automatically add a __ prefix for the default namespace and to use that for attribute and child element access 4/6/10
1.0.0b3Added the $http_timeout parameter to __construct() 9/16/09
1.0.0b2Added instance functionality for reading of XML files 9/1/09
1.0.0bThe initial implementation 1/13/08

Variables

->__custom_prefixes protected

Custom prefix => namespace URI mappings

Type

array

->__dom protected

The dom element for this XML

Type

DOMElement

->__xml protected

The XML string for serialization

Type

string

->__xpath protected

An XPath object for performing xpath lookups

Type

DOMXPath

Static Methods

::encode() public

Encodes content for display in a UTF-8 encoded XML document

Signature

string encode( string $content )

Parameters
string $content The content to encode
Returns

The encoded content

::sendHeader() public

Sets the proper Content-Type HTTP header for a UTF-8 XML file

Signature

void sendHeader( )

Methods

->__construct() public

Create the XML object from a string, fFile or URL

The $default_namespace will be used for any sort of methods calls, member access or array access when the element or attribute name does not include a :.

Signatures

fXML __construct( fFile|string $source, numeric $http_timeout=NULL, boolean $fix_entities_encoding=NULL )

fXML __construct( fFile|string $source, boolean $fix_entities_encoding=NULL )

Parameters
fFile|string $source The source of the XML, either an fFile object, a string of XML, a file path or a URL
numeric $http_timeout The timeout to use in seconds when requesting an XML file from a URL
boolean $fix_entities_encoding This will fix two common XML authoring errors and should only be used when experiencing decoding issues - HTML entities that haven't been encoded as XML, and XML content published in ISO-8859-1 or Windows-1252 encoding without an explicit encoding attribute
Throws
fValidationException
When the source XML is invalid or does not exist
->__get() internal public

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

Allows access to the text content of a child tag

The child element name ($name) may start with a namespace prefix and a : to indicate what namespace it is part of. A blank namespace prefix (i.e. an element name starting with :) is treated as the XML default namespace.

Signature

fXML|NULL __get( string $name )

Parameters
string $name The child element to retrieve
Returns

The child element requested

->__isset() internal public

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

The child element name ($name) may start with a namespace prefix and a : to indicate what namespace it is part of. A blank namespace prefix (i.e. an element name starting with :) is treated as the XML default namespace.

Signature

boolean __isset( string $name )

Parameters
string $name The child element to check - see method description for details about namespaces
Returns

If the child element is set

->__set() internal public

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

Prevents users from trying to set elements

Signature

void __set( string $name, mixed $value )

Parameters
string $name The element to set
mixed $value The value to set
->__sleep() internal public

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

The XML needs to be made into a string before being serialized

Signature

array __sleep( )

Returns

The members to serialize

->__toString() public

Gets the string inside the root XML element

Signature

string __toString( )

Returns

The text inside the root element

->__unset() internal public

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

Prevents users from trying to unset elements

Signature

void __unset( string $name )

Parameters
string $name The element to unset
->__wakeup() internal public

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

The XML needs to be made into a DOMElement when woken up

Signature

void __wakeup( )

->addCustomPrefix() public

Adds a custom namespace prefix to full namespace mapping

This namespace prefix will be valid for any operation on this object, including calls to xpath().

Signature

void addCustomPrefix( string $ns_prefix, string $namespace )

Parameters
string $ns_prefix The custom namespace prefix
string $namespace The full namespace it maps to
->getName() public

Returns the name of the current element

Signature

string getName( )

Returns

The name of the current element

->getNamespace() public

Returns the namespace of the current element

Signature

string getNamespace( )

Returns

The namespace of the current element

->getPrefix() public

Returns the namespace prefix of the current element

Signature

string getPrefix( )

Returns

The namespace prefix of the current element

->getText() public

Returns the string text of the current element

Signature

string getText( )

Returns

The string text of the current element

->offsetExists() internal public implements ArrayAccess

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

Provides functionality for isset() and empty() (required by arrayaccess interface)

Offsets refers to an attribute name. Attribute may start with a namespace prefix and a : to indicate what namespace the attribute is part of. A blank namespace prefix (i.e. an offset starting with :) is treated as the XML default namespace.

Signature

boolean offsetExists( string $offset )

Parameters
string $offset The offset to check
Returns

If the offset exists

->offsetGet() internal public implements ArrayAccess

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

Provides functionality for get [index] syntax (required by ArrayAccess interface)

Offsets refers to an attribute name. Attribute may start with a namespace prefix and a : to indicate what namespace the attribute is part of. A blank namespace prefix (i.e. an offset starting with :) is treated as the XML default namespace.

Signature

string offsetGet( string $offset )

Parameters
string $offset The attribute to retrieve the value for
Returns

The value of the offset

->offsetSet() internal public implements ArrayAccess

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

Required by ArrayAccess interface

Signature

void offsetSet( integer|string $offset, $value )

Parameters
integer|string $offset The offset to set
$value
->offsetUnset() internal public implements ArrayAccess

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

Required by ArrayAccess interface

Signature

void offsetUnset( integer|string $offset )

Parameters
integer|string $offset The offset to unset
->query() protected

Performs an XPath query on the current element, returning the raw results

Signature

array query( string $path )

Parameters
string $path The XPath path to query
Returns

The matching elements

->toXML() public

Returns a well-formed XML string from the current element

Signature

string toXML( )

Returns

The XML

->xpath() public

Executes an XPath query on the current element, returning an array of matching elements

Signature

array|string|fXML xpath( string $path, boolean $first_only=FALSE )

Parameters
string $path The XPath path to query
boolean $first_only If only the first match should be returned
Returns

An array of matching elements, or a string or fXML object if $first_only is TRUE

Backwards Compatibility Breaks

On this page you will find a list of all breaks in backwards compatibility. Once Flourish becomes stable this will only happen with major version releases. During the alpha phase they are bound to happen on a regular basis, whereas during beta they should be rare.

If you work with any protected methods or @internal public methods, be sure to check out the internal backwards compatiblity breaks page.

Beta Phase

Revision 1035

fTemplating::create() was removed and a new method fTemplating::attach() was created to provide similar functionality.

Revision 1031

fCore::detectOpcodeCache() was renamed to fLoader::hasOpcodeCache()

Revision 1026

fFile::output() now automatically closes any open output buffering and discards the contents.

Revision 1011

The email subject of error/exception emails sent by fCore has changed.

Revision 1010

fRecordSet::sort() and fRecordSet::sortByCallback() now return a new record set instead of sorting the record set in place - this helps prevent side effects.

Revision 990

fActiveRecord::reflect() now returns an associative array instead of a string.

Revision 984

fSchema::getTables(), fSchema::getColumnInfo(), fSchema::getDatabases(), fSchema::getKeys() and fSchema::getRelationships() now return database, schema, table and column names in lowercase. This may change the behavior of, or break ORM classes for SQLite, MySQL, PostgreSQL or MSSQL databases containing mixed-case identifiers, although such mixed-cased identifiers have never been officially supported. Please test ORM functionality when upgrading to this revision.

Callbacks registered to the extracted hook via fDatabase::registerHookCallback() no longer receive the $strings parameter, instead all strings are added into the $values parameter.

Revision 973

Updated fCore::expose() to not wrap the value in an HTML tag when PHP is being run via the CLI.

Revision 969

fRequest::getBestAcceptType() and fRequest::getBestAcceptLanguage() now return either NULL, FALSE or a string instead of NULL or a string. See documentation for details of return values.

Revision 945

The $contents parameter of fEmail::addAttachment() is now first instead of third.

Revision 944

fORMFile::configureImageUploadColumn() no longer accepts the optional $image_type as the fourth parameter, instead fORMFile::addFImageMethodCall() must be called with saveChanges as the $method and the image type as the first parameter.

Revision 943

fRequest::get() now strips out low bytes characters if no $cast_to, or if a string or array $cast_to is specified. Using the new binary $cast_to will leave all bytes intact.

Revision 942

fActiveRecord column set methods now treat strings of all whitespace the same as empty strings and convert them to NULL.

Revision 934

Changed the parameter order in fImage::crop() from $crop_from_x, $crop_from_y, $new_width, $new_height to $new_width, $new_height, $crop_from_x, $crop_from_y.

Revision 917

Changed the array structure for child record in validation messages. Instead of all main record and child record messages being in a single array with keys in the form of child_table[0]::column, there is now a child_table[0] key that points to an array containing a name key and an errors key. The value for name is the name of the child record, e.g. Child Record #1. The value for errors is an array of validation messages with the keys being the column names and the values being the validation messages. Thus:

array(
    'first_name' => 'First Name: Please enter a value',
    'user_permissions[0]::resource' => 'Resource: Please enter a value'
)

would now be represented as:

array(
    'first_name' => 'First Name: Please enter a value',
    'user_permissions[0]' => array(
        'name' => 'User Permission #1',
        'errors' => array(
            'resource' => 'Resource: Please enter a value'
        )
    )
)

Methods registered to the pre::validate() and post::validate() hooks will need to handle this new array structure. Code that calls ->validate(TRUE) on an fActiveRecord object will need to handle this new array structure. fORMValidation::addStringReplacement(), fORMValidation::addRegexReplacement() and fORMValidation::setMessageOrder() will now only affect the specified class specified and will not affect messages from child records.

Revision 915

Restructured individual record access, the iterator interface and callback parameters for fRecordSet.

Removed the method fRecordSet::fetchRecord() - functionality can be replicated via the new method fRecordSet::getRecord().

Manual iteration via fRecordSet::current(), fRecordSet::key(), fRecordSet::next(), fRecordSet::rewind() and fRecordSet::valid() (the Iterator interface) was removed and should be replaced by retrieving an ArrayIterator from the new method fRecordSet::getIterator(). Manual iteration can be performed on the ArrayIterator object.

The $pointer parameter was replaced with $method_name for callbacks registered via fORM::registerRecordSetMethod().

Revision 911

Changed fTemplating::delete() to return the value(s) of the element(s) deleted instead of the fTemplating instance.

Revision 904

fSession::add(), fSession::delete(), fSession::get(), fSession::set(), fTemplating::add(), fTemplating::delete(), fTemplating::get() and fTemplating::set() all now interpret [ and ] as array syntax and thus these can not be used in key or element names.

fTemplating::remove() was renamed to fTemplating::filter().

Revision 885

Removed fDatabase::enableSlowQueryWarnings(), added ability to replicate via fDatabase::registerHookCallback().

Revision 879

Removed support for the odbc and pdo_odbc extensions from fDatabase, fResult, fStatement and fUnbufferedResult. With this change, Flourish no longer supports any kind of ODBC connections.

Revision 854

fActiveRecord::populate{RelatedRecords}() changed from requiring the database table name as the input prefix to the underscore_notation version of the class name. This will only affect related records where the class was mapped using fORM::mapClassToTable().

Revision 840

Renamed fUpload::setMaxFilesize() to fUpload::setMaxSize() to be consistent with fFile::getSize().

Revision 839

fUpload no longer accepts uploaded files that start with a . unless fUpload::allowDotFiles() is called.

Revision 833

fActiveRecord::validate() now uses column names as array keys if messages are returned, the $validation_messages parameter for the pre::validate() and post::validate() hooks now requires array keys be column names and fValidation::validate() now uses field names as array keys if messages are returned.

fUpload::validate() no longer returns a $_FILES array.

fValidation::addRequiredFields() no longer accepts one-or-more rules, instead use fValidation::addOneOrMoreRule(). fValidation::addRequiredFields() no longer accepts conditional rules, instead use fValidation::addConditionalRule().

Revision 760

fDirectory::scan() and fDirectory::scanRecursive() to strip the current directory's path before matching the $filter

Revision 736

Renamed fFile::getFilename() to fFile::getName(), fFile::getFilesize() to fFile::getSize(), fFile::getDirectory() to fFile::getParent() and fDirectory::getFilesize() to fDirectory::getSize()

Revision 735

Added the $force_cascade parameter to fActiveRecord::delete() and fActiveRecord::store(). This change require any classes that override those methods be updated to include at least one parameter.

Revision 726

fORM::addCustomClassTableMapping() was renamed to fORM::mapClassToTable()

Revision 721

Removed the $prefix parameter from the methods fSession::delete(), fSession::get() and fSession::set()

Revision 720

Removed support for date function translation in fDatabase/fSQLTranslation

Revision 700

Renamed fRecordSet::buildFromRecords() to fRecordSet::buildFromArray()

Revision 659

Before this revision one-to-one relationships were not properly detected, so some one-to-many functionality such as fActiveRecord::populate{RelatedRecords}(), fActiveRecord::build{RelatedRecords}() and fActiveRecord::associate{RelatedRecords}() was working on these one-to-one relationships when it should not have.

one-to-one relationships support fActiveRecord::create{RelatedRecord}(), fActiveRecord::populate{RelatedRecord}() and fActiveRecord::associate{RelatedRecord}() instead.

Revision 651

Renamed fORMValidation::addConditionalValidationRule() to fORMValidation::addConditionalRule(), fORMValidation::addManyToManyValidationRule() to fORMValidation::addManyToManyRule(), fORMValidation::addOneOrMoreValidationRule() to fORMValidation::addOneOrMoreRule(), fORMValidation::addOneToManyValidationRule() to fORMValidation::addOneToManyRule(), fORMValidation::addOnlyOneValidationRule() to fORMValidation::addOnlyOneRule(), fORMValidation::addValidValuesValidationRule() to fORMValidation::addValidValuesRule()

Revision 591

Removed fRecordSet::flagAssociate() and fRecordSet::isFlaggedForAssociation(), the $associate parameter is no longer passed to callbacks registered via fORM::registerRecordSetMethod()

Revision 567

The first parameter of fSession::clear() was removed, fSession::delete() should now be used instead

Revision 566

Moved fCRUD::printOption() to fHTML::printOption(), fCRUD::showChecked() to fHTML::showChecked(), fCRUD::removeListItems() and fCRUD::reorderListItems() to fException::splitMessage(), fCRUD::generateRequestToken() to fRequest::generateCSRFToken(), and fCRUD::validateRequestToken() to fRequest::validateCSRFToken()

Revision 562

Removed fORMSchema::enableSmartCaching(), fORM::enableSchemaCaching() now provides equivalent functionality. fSchema::setCacheFile() changed to fSchema::enableCaching() and now requires an fCache object. fSchema::flushInfo() was renamed to fSchema::clearCache().

Revision 525

Changed the second parameter of fFile::output() from $ignore_output_buffer to $filename

Revision 524

Removed fDate::getSecondsDifference(), fTime::getSecondsDifference(), fTimestamp::getSecondsDifference() and fTimestamp::getSeconds()

Revision 514

fCore::getOS() and fCore::getPHPVersion() were removed and replaced with fCore::checkOS() and fCore::checkVersion()

Revision 499

Renamed fORM::addCustomTableClassMapping() to fORM::addCustomClassTableMapping() and swapped the parameters

Revision 498

Changed fCryptography::symmetricKeyEncrypt() to not encrypt the IV since we are using HMAC on it


Alpha Phase

Most users will not have code from the Flourish alpha

Revision 403

fCore::stringlike() was removed

Revision 402

fCore::trigger() was removed

Revision 399

!fPrintableException was renamed to fException

Revision 388

fCore::toss() was removed and all exceptions are now normally thrown with throw new ExceptionName(). fCore::registerTossCallback() was renamed to fPrintableException::registerCallback(), fGrammar::compose() was renamed to fText::compose() and fGrammar::registerComposeCallback() was renamed to fText::registerComposeCallback().

Revision 374

fActiveRecord hooks and method calls now have a &$cache parameter after the &$related_records parameter, but before any other parameters. Any method registered to a hook will need to have the method signature updated.

Revision 364

Swapped the order of the last two parameters of fORMRelated::setOrderBys() so that $route (which is really just NULL most of the time) was last and optional

Revision 361

Changed the existing file upload field name from __flourish_existing_column_name to existing-column_name and changed the delete file upload field name from __flourish_delete_column_name to delete-column_name

Revision 348

fNoResultsException was renamed to fNoRowsException, fResult::tossIfNoResults() was renamed to fRequest::tossIfNoRows(), fUnbufferedResult::tossIfNoResults() was renamed to fUnbufferedResult::tossIfNoRows(), fResult::getAffectedRows() was renamed to fResult::countAffectedRows() and fResult::getReturnedRows() was renamed to fRequest::countReturnedRows()

Revision 332

fActiveRecord::has() was renamed to fActiveRecord::hasOld() and fActiveRecord::retrieve() was renamed to fActiveRecord::retrieveOld()

Revision 331

replace:: hooks are no longer allowed in fORM::registerHookCallback(), instead use fORM::registerActiveRecordMethod(). fRecordSet::registerMethodCallback() has been renamed to fORM::registerRecordSetMethod().

Revision 308

Renamed fORMDatabase::getInstance() to fORMDatabase::retrieve() and renamed fORMSchema::getInstance() to fORMSchema::retrieve()

Revision 305

fUTF8::detect() went from being public to private

Revision 304

fResult::getPointer() was removed, same functionality is available from fResult::key()

Revision 303

fResult::areRemainingRows() was removed, same functionality is available from fResult::valid()

Revision 300

fUpload::upload() was renamed to fUpload::move()

Revision 298

Changed fUpload from a static class to an instance class

Revision 294

Removed fPrintableException::prepareMessage()

Revision 293

Removed fTimestamp::combine()

Revision 289

Changed the replacement token in fRequest::overrideAction() from %%action%% to %action%

Revision 282

Changed the separator between the table name and column name for related form inputs from . back to :: because PHP converts . to _ - this reverts changes in r264

Revision 278

fRecordSet::build{RelatedRecords}() was renamed to fRecordSet::prebuild{RelatedRecords}() and fRecordSet::count{RelatedRecords}() was renamed to fRecordSet::precount{RelatedRecords}()

Revision 277

Removed fRecordSet::countWithoutLimit() and added the $ignore_limit parameter to fRecordSet::count()

Revision 264

Changed the separator between the table name and column name for related form inputs from :: to . - users::user_id[] will now be users.user_id[]

Revision 263

Removed fRecordSet::buildFromPrimaryKeys(), changed fORMRelated::associateRecords() to only accept an fRecordSet instead of an fRecordSet or an array of primary keys

Revision 259

Renamed fORM::createActiveRecordClass() to fORM::defineActiveRecordClass()

Revision 258

Renamed fFilesystem::createUniqueName() to fFilesystem::makeUniqueName()

Revision 257

Renamed fTimestamp::createFormat() to fTimestamp::defineFormat()

Revision 253

Removed fORMDatabase::escapeByType(), added support for shorthand table.column names to fORMDatabase::escapeBySchema() to provide replacement functionality

Revision 251

The fourth parameter of fRecordSet::build() is now the page number instead of the offset. The page number is subtracted by one and multiplied by the limit to get the correct offset.

Revision 250

Removed fGrammar::registerHumanizeCallback()

Revision 249

fGrammar::replaceHumanize() was renamed to fGrammar::registerHumanizeCallback(), fGrammar::replaceJoinArray() was renamed to fGrammar::registerJoinArrayCallback(). fGrammar::addAllCapitalsWord() was removed and fGrammar::addHumanizeRule() was added with similar functionality.

Revision 239

fDate::setDate(), fDate::setISODate(), fTime::setTime(), fTimestamp::setDate(), fTimestamp::setISODate(), fTimestamp::setTime(), fTimestamp::setTimezone(), fTimestamp::getTimezone() were removed. The fDate, fTime and fTimestamp classes all became value objects (immutable) and fDate::adjust(), fTime::adjust() and fTimestamp::adjust() all now return new objects. Each class also gained a method called modify() which returns a new object based on the current object, replacing the various set methods.

Revision 229

Changed fCRUD::removeListItems() so its parameters were consistent with fCRUD::reorderListItems()

Revision 225

fXML::prepare() was renamed to fXML::encode()

Revision 223

fRecordSet::flagForAssociation() was renamed to fRecordSet::flagAssociate()

Revision 222

fRecordSet::registerCallback() was renamed to fRecordSet::registerMethodCallback()

Revision 221

fORM::getClassName() was renamed to fORM::getClass() and fRecordSet::getClassName() was renamed to fRecordSet::getClass()

Revision 218

fHTML::checkForBlockLevelHTML() was renamed to fHTML::containsBlockLevelHTML() and fHTML::createLinks() was renamed to fHTML::makeLinks()

Revision 217

fCryptography::generateRandomString() was renamed to fCryptography::randomString()

Revision 216

fCRUD::createSortableColumn() was renamed to fCRUD::printSortableColumn()

Revision 215

Removed fFinancialTransaction and fShippingRates. Both were unlike the rest of Flourish in that they were tied to the APIs of other services and neither was very comprehensive in terms of the number of services supported.

Revision 206

Removed fORMDatabase::initialize(), you must now construct an fDatabase instance and pass it to fORMDatabase::attach()

Revision 205

fORMDatabase::prepareBySchema() was renamed to fORMDatabase::escapeBySchema() and fORMDatabase::prepareByType() was renamed to fORMDatabase::escapeByType()

Revision 204

fDatabase::escape() replaces fDatabase::escapeBlob(), fDatabase::escapeBoolean(), fDatabase::escapeDate(), fDatabase::escapeFloat(), fDatabase::escapeInteger(), fDatabase::escapeString(), fDatabase::escapeTime() and fDatabase::escapeTimestamp(). fDatabase::unescape() replaces fDatabase::unescapeBlob(), fDatabase::unescapeBoolean(), fDatabase::unescapeDate(), fDatabase::unescapeFloat(), fDatabase::unescapeInteger(), fDatabase::unescapeString(), fDatabase::unescapeTime() and fDatabase::unescapeTimestamp().

Revision 202

The second and third parameters of fMessaging::create() were swapped to be consistent with the other fMessaging methods

Revision 201

fORMColumn::configureDateCreatedColumn() was moved to fORMDate::configureDateCreatedColumn() and fORMColumn::configureDateUpdatedColumn() was moved to fORMDate::configureDateUpdatedColumn()

Revision 195

fORMColumn::configureMoneyColumn() was moved to fORMMoney::configureMoneyColumn()

Revision 184

fMoney::multiply() renamed to fMoney::mul() and fMoney::subtract() renamed to fMoney::sub() to be consistent with fNumber

Revision 174

fORMRelated::constructRecord() renamed to fORMRelated::createRecord(), fORMRelated::constructRecordSet() renamed to fORMRelated::buildRecords()

Revision 173

Renamed fRecordSet::create() to fRecordSet::build(), removed fRecordSet::createEmpty(), renamed fRecordSet::createFromObjects() to fRecordSet::buildFromRecords() and added a new first parameter, renamed fRecordSet::createFromPrimaryKeys() to fRecordSet::buildFromPrimaryKeys(), renamed fRecordSet::createFromSQL() to fRecordSet::buildFromSQL().

Revision 166

fValidation no longer has the methods fValidation::setEmailFields(), fValidation::setEmailHeaderFields() or fValidation::setRequiredFields(). The various fValidation::add*() methods should be used instead.

Revision 144

The method fImage::getInfo() went from being public to protected.

Revision 127

fRecordSet::preload{RelatedRecords}() became fRecordSet::build{RelatedRecords}().

Revision 125

The $debug parameter was removed from all calls to fActiveRecord hook callbacks.

Revision 84

Removed fRecordSet::preload{RelatedRecords}().

Revision 81

fInflection was renamed to fGrammar.

Revision 76

fMessaging::show() now behaves differently than it did before.

Flourish Callback Syntax

In most programming languages where functions are first-class members, the syntax for a function or method callback is simply the function/method name without the parenthesis. The below snippet of javascript defines a function and then uses a callback for setTimeout to cause the function to be executed at a later time.

function foo() {
    // Do stuff
}
setTimeout(foo, 100);

Unfortunately PHP does not treat function at first-class members of the language. It uses strings and arrays for function and method callbacks respectively. In an attempt to make the callback syntax a little more intuitive, Flourish uses class constants and the __get() magic method to make javascript-style callbacks.

Flourish defines class constants with the same name as all static methods:

// Uppercase a UTF-8 string
$string = fUTF8::upper($string);

// Uppercase an array of UTF-8 strings (PHP 5.2 only)
$strings = array_map(fUTF8::upper, $strings);

The only caveat with these class constants is that in PHP 5.1 they need to be passed to fCore::callback() if they are going to be used with built-in PHP functions. This is because the constants are the string-style static method callbacks, which were added in PHP 5.2. The method fCore::callback() translates them into the array-style syntax that works in 5.1.

// PHP 5.1 compatible callbacks for PHP functions
$string = array_map(fCore::callback(fUTF8::upper), $strings);

For instance method callbacks, Flourish uses the __get() magic method to create the appropriate array-syntax callback for the object being called.

$ten_dollars = new fMoney(10);
echo $ten_dollars->format();

// Callback syntax for format
fCore::call($ten_dollars->format);

Both the instance and static method callback syntaxes work on every method of every Flourish class.

Flourish SQL

One of the aims of Flourish is to make a portable PHP library so that applications can be deployed on different servers with little to no changes. With the multitude of database servers available and different platforms, one of the main hurdles for portability is the different dialects of SQL.

Flourish SQL has grown a common subset of the SQL dialects supported by IBM DB2, Microsoft SQL Server, MySQL, PostgreSQL and SQLite, with a few additions for ease-of-use. This document describes what features and syntax are supported across all databases.

In the Flourish SQL column you will find the supported syntax for a function, data type, etc. The other columns contain a if the syntax is the same, or the database-specific syntax highlighted in red.

Supported Database Versions

The information on this page is targeted at the following database versions, or newer editions:

Limitations

If targeting MSSQL or DB2, please be aware that UNIQUE constraints only allow a single NULL value to be present in all rows. To work around this, split the column in to a separate table and use a foreign key to associate it to the original table.

Oracle does not support the ON UPDATE clause for foreign keys. MSSQL does not allow ON UPDATE CASCADE or ON DELETE CASCADE clauses for foreign keys that could cause cycles during handling. This is often caused by multiple columns in a table referencing the same foreign table.

MySQL, DB2, Oracle and MSSQL all have various constraints on TEXT and BLOB data types. To achieve cross-database support, be sure not to do the following with such columns:

Since fRecordSet uses GROUP BY clauses to allow for selection of records by values in related tables, it is highly recommended that TEXT and BLOB columns always be split out into a separate table and to use a foreign key to associate it with the original table. The Flourish ORM has strong support to easily retrieving such related records.

Usage

The following Flourish SQL is supported when calling the fDatabase::translatedQuery() and fDatabase::unbufferedTranslatedQuery() methods. The Cross-Database SQL section of the fDatabase page has more information.

Data Types

Most dialects of SQL support a large number of common data types, however a things like booleans, binary data and dates and times are often different. Here is an outline of the data types supported by Flourish SQL:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
smallint
integer
bigint integer
integer autoincrement primary key integer generated by default as identity primary key integer identity(1) primary key integer auto_increment primary key integer + sequence + trigger serial primary key integer primary key autoincrement
float
real
decimal
char nchar
varchar nvarchar varchar2
text clob ntext mediumtext clob
blob image longblob bytea
timestamp datetime datetime
date date for 2008, datetime for 2005
time time for 2008, datetime for 2005 timestamp
boolean char(1) bit number(1)

In all cases except for the date and time are the data types consistent across the databases. MSSQL does not support date or time, so the more precise datetime is used instead. Oracle does not support time and uses timestamp instead. In both cases, the unnecessary portions are removed by the unescape() method of fDatabase.

One other thing to note is the apparent perfect compatibility for SQLite data types. This is not because the SQLite data types were chosen as the basis for Flourish SQL, but rather that SQLite is loosely-typed and allows anything to be entered for a data type.

Boolean Values

Since boolean fields are implemented quite differently across databases, it comes as no surprise that there are different values to use for boolean fields:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
TRUE '1' '1' 1 '1'
FALSE '0' '0' 0 '0'

Transactions

Transaction control differs slightly among the supported databases:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
BEGIN auto-commit disabled BEGIN TRANSACTION auto-commit disabled
COMMIT
ROLLBACK
SAVEPOINT savepoint_name SAVE TRANSACTION savepoint_name
ROLLBACK TO SAVEPOINT savepoint_name ROLLBACK TRANSACTION savepoint_name

Data Manipulation Statements

The following constructs are are used with the data manipulation language (DML) statements in SQL. Such statements include SELECT, INSERT, UPDATE and DELETE.

Operators

Operators are fairly consistent across the different databases, with the exception of the concatenation operator. Please note that || works for concatenation in MySQL only when MySQL is in ANSI mode (Flourish automatically switches into ANSI mode when a connection to a MySQL database is initiated).

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
Mathematical Operators
+
-
/
*
% mod() mod()
String Operators
|| (concatenation) +
Comparison Operators
<
>
<=
>=
<> (inequality)
!=
=
IN
NOT IN
IS (equality with NULL)
IS NOT (inequality with NULL)
LIKE (case insensitive) lower(value) LIKE lower(pattern) lower(value) LIKE lower(pattern) ILIKE
Boolean Operators
AND
OR

Functions

There a quite a few functions that are consistent across the different databases:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
Mathematical Functions
abs(x)
acos(x) PHP Callback
asin(x) PHP Callback
atan(x) PHP Callback
atan2(x, y) atn2(x, y) PHP Callback
ceil(x) ceiling(x) ceiling(x) PHP Callback
ceiling(x) ceil(x) PHP Callback
cos(x) PHP Callback
cot(x) (1/tan(x)) (1/tan(x)) PHP Callback
degrees(x) (x * 57.295779513083) PHP Callback
exp(x) PHP Callback
floor(x) PHP Callback
ln(x) log(x) PHP Callback
log(b, x) (ln(x)/ln(b)) (log(x)/log(b)) PHP Callback
pi() 3.14159265358979 (pi()+0.0000000000000) 3.14159265358979 PHP Callback
power(x, y) PHP Callback
radians(x) (x * 0.017453292519943) PHP Callback
random() rand() rand() rand() (abs( dbms_random.random ) / 2147483647) (abs( random()) / 9223372036854775807)
round(x)
sign(x) PHP Callback
sqrt(x) PHP Callback
sin(x) PHP Callback
tan(x) PHP Callback
Aggregate Functions
avg(l)
count(l)
max(l)
min(l)
sum(l)
String Functions
trim(s) rtrim(ltrim(s))
rtrim(s)
ltrim(s)
upper(s)
lower(s)
substr(s, start, length) substring(s, start, length, CODEUNITS32) substring(s, start, length)
replace(s, find, replace)
length(s) character_length(s, CODEUNITS32) len(s) char_length(s)
coalesce(a, b,...)
Date/Time Functions
CURRENT_TIMESTAMP datetime( CURRENT_TIMESTAMP, 'localtime')

A quick scan of the SQLite column indicates that the trigonometric functions are all implemented as PHP callbacks. SQLite made the decision to include only specific functions in the supported SQL dialect, however they added support for hooks into other programming languages. Flourish uses this ability to provide support for trigonometric functions.

Expressions

There are a few expressions that are consistent across databases:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
BETWEEN
CASE (simple)
CASE (complex)

Joins

There are quite a few different types of join supported by the four different databases, however the common join functionality is a small subset. Note that all joins except CROSS and , require use of an ON clause, while CROSS and , can not use an ON clause.

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
, (cross join)
CROSS JOIN
[INNER] JOIN
LEFT [OUTER] JOIN

The brackets indicate the words OUTER and INNER are optional.

Other Join Types

You may notice the lack of a FULL [OUTER] JOIN, RIGHT [OUTER] JOIN, the keyword NATURAL and USING clauses. Unfortunately DB2, MySQL and SQLite do not support FULL joins, SQLite does not support RIGHT joins and DB2 and MSSQL do not support NATURAL joins. USING (col, ...) clauses are not supported by MSSQL.

FULL joins can be achieved by a somewhat complex combination of a SELECT from the first table with extra columns UNIONed with an INNER JOIN, UNIONed with a SELECT from the second table with extra columns.

RIGHT joins can be performed by switching the order of the tables in the FROM clause.

NATURAL joins are just shorthand for the common columns between tables, so all that needs to be done is an INNER join with manually specified columns in the ON condition.

USING clauses can be approximated with a ON condition, however the USING operator ensures that only one copy of each column is included in the returned rows, whereas ON does not.

Clauses

There are a few clauses that are supported across all databases:

Flourish SQL DB2 MSSQL MySQL Oracle PostgreSQL SQLite
DISTINCT (follows SELECT)
ALL (follows SELECT)
LIMIT (without an offset) FETCH FIRST n ROWS ONLY TOP subquery with rownum clause
LIMIT/OFFSET subquery with row_number() clause and removal of extra column via PHP subquery with row_number() clause and removal of extra column via PHP nested subqueries with rownum clauses and removal of extra column via PHP
UNION
UNION ALL

Data Definition Statements

The following are the supported data definition language (DDL) statements in Flourish SQL. Some examples of DDL statements include CREATE TABLE, ALTER TABLE and CREATE INDEX.

While DML statements between the supported databases tend to be fairly consistent, DDL statements tend to be much more varied. For instance, all of the supported databases use different constructs to achieve auto-incrementing primary keys.

The following grammars will show the supported Flourish SQL syntax, but do not cover exactly what is run for each type of database. To see what processing takes place, please see the fSQLSchemaTranslation source code, or call fDatabase::enableDebugging() before executing DDL statements.

Create Table

The following syntax is supported for CREATE TABLE statements. By strictly following this syntax, and using the data types previously defined, CREATE TABLE statements should work identically across all six supported database systems.

CREATE TABLE table_name ( 
    {
        column_name data_type [ DEFAULT default_value ] [ column_constraint [ ... ] ] |
        table_constraint
    }, ...
)

where column_constraint is:

{
    NOT NULL | 
    NULL | 
    UNIQUE |
    [ AUTOINCREMENT ] PRIMARY KEY |
    CHECK ( column_name IN ( value1 [, value2 [, ... ] ] ) ) |
    REFERENCES referenced_table ( referenced_column ) [ ON DELETE action ]
}

table_constraint is:

{
    UNIQUE ( column_name [, ... ] ) |
    PRIMARY KEY ( column_name [, ... ] ) |
    CHECK ( column_name IN ( value1 [, value2 [, ... ] ] ) ) |
    FOREIGN KEY ( column_name ) REFERENCES referenced_table ( referenced_column )
        [ ON DELETE action ]
}

and action is:

{ RESTRICT | CASCADE | SET NULL | NO ACTION }

Alter Table

The ALTER TABLE statements are some of the most powerful aspects of Flourish SQL since the native support for ALTER TABLE varies wildly between databases. In addition to varying syntax, many databases require explicit constraint names for dropping UNIQUE, CHECK, FOREIGN KEY and PRIMARY KEY constraints. These constraint names are usually generated by the system and are not consistent across different databases.

To solve these problems, fSQLSchemaTranslation introspects the database and alters the SQL statements to work with the different database engines. In some cases multiple SQL statements must be executed. fSQLSchemaTranslation is written in such a way that each of the statements listen below should be individually atomic. Thus, if the statement fails, the database should remain at the state before the statement was executed.

PostgreSQL, SQLite and MSSQL all fully support transactions for DDL statements, which makes this easy. For DB2, most DDL statements run within a transaction, and the one that doesn't (REORG TABLE) is only executed once the primary statement succeeds. For Oracle, all of these statement correspond to a single SQL statement, so the statement will either succeed or fail. MySQL requires storing rollback statements for all database operations, and such statements are run if a statement fails. The tests for Flourish include a bunch of tests to ensure that statements are run atomically.

The syntax is strongly based on PostgreSQL's ALTER TABLE statements, since they are powerful and succinct. Users who are more familiar with other database systems, especially SQLite and MySQL, will hopefully find these statement significantly easier to use than native commands.

Of special note among the supported database systems is SQLite, since it only natively supports renaming tables and adding columns for version 3.x and nothing for version 2.x. Flourish does a fairly significant amount of work for SQLite, which includes:

  1. parsing the existing table structure
  2. modifying it into a new temporary table
  3. transferring table contents to the temporary table
  4. dropping the original table
  5. renaming the temporary table to the original table name

Some of these steps require significant sub-steps, such as in SQLite 2, where table renaming does not exists. Luckily SQLite allows DDL statements within transactions, so everything is safe, but ALTER TABLE statements may run more slowly because of the extensive amount of work required.

Rename Table
ALTER TABLE table_name RENAME TO new_table_name

An fSQLException will be thrown if a table with the new name already exists.

Add Column
ALTER TABLE table_name ADD COLUMN column_name data_type [ DEFAULT default_value ] [ column_constraint [ ... ] ]

The data_type, default_value and column_constraint values are all exactly the same as those for Create Table statements. An fSQLException will be thrown if a column with the same name already exists or there is an error in the column definition.

Rename Column
ALTER TABLE table_name RENAME COLUMN column_name TO new_column_name

An fSQLException will be thrown if a column with the new name already exists.

Drop Column
ALTER TABLE table_name DROP COLUMN column_name

Any UNIQUE, FOREIGN KEY or PRIMARY KEY constraints that involve this column will also be dropped. An fSQLException will be thrown if the column does not exist.

Set Column Type
ALTER TABLE table_name ALTER COLUMN column_name TYPE data_type

For many of the supported databases, there are restrictions about what data types can be automatically converted. In general it is safe to change the data type to increase the size, such as making a VARCHAR longer, or changing from an INTEGER to a BIGINT.

For more drastic data type changes, it is normally necessary to:

  1. Create a new column
  2. Assign values to the new column from the old column, using a CAST() statement
  3. Drop the old column
  4. Rename the new column to the old column
  5. Re-create UNIQUE, FOREIGN KEY and PRIMARY KEY constraints that existed on the old column
Set Default
ALTER TABLE table_name ALTER COLUMN column_name SET DEFAULT default_value

Sets the default value for a column, overriding any previous value. Some databases only support constant values for default_value. For example, not all databases allow setting CURRENT_TIMESTAMP() to be a default value since it is a function.

Drop Default
ALTER TABLE table_name ALTER COLUMN column_name DROP DEFAULT

Drops the default value for a column. This statement will succeed even if no default value exists.

Set Not Null
ALTER TABLE table_name ALTER COLUMN column_name SET NOT NULL [ DEFAULT default_value ]

Sets a column to not allow NULL values, and optionally sets the default value for a column. This statement will succeed even if the column already restricts NULL values.

This combined form of SET NOT NULL and SET DEFAULT is useful to support NOT NULL DEFAULT '' configurations on Oracle databases, where blank strings are automatically converted to NULL. NOT NULL DEFAULT '' is useful for writing simpler SQL statements since it is no longer necessary to specially test for NULL values via (column_name IS NULL OR column_name = '').

Drop Not Null
ALTER TABLE table_name ALTER COLUMN column_name DROP NOT NULL

Changes a column to allow NULL values. For most databases this will not succeed on columns that are part of a PRIMARY KEY constraint. This statement will succeed even if the column already allows NULLs.

Set Check Constraint
ALTER TABLE table_name ALTER COLUMN column_name SET CHECK IN (string_value [, ... ] )

Creates a CHECK(column_name IN ('string value', 'string value')) check constraint for the column. This check constraint will override any existing check constraint.

MySQL doesn't support CHECK constraints, but the ENUM() data type is basically a check constraint for a set of string values. Thus for MySQL, this statement will turn a VARCHAR column into an ENUM() column with the strings provided.

Drop Check Constraint
ALTER TABLE table_name ALTER COLUMN column_name DROP CHECK

Drop the CHECK constraint for a column. An fSQLException will be thrown if no check constraint exists.

MySQL doesn't support CHECK constraints, but the ENUM() data type is basically a check constraint for a set of string values. Thus the only cross- database compatible CHECK constraint is CHECK(column_name IN ('string value', 'string value 2')), which will be automatically converted to an ENUM() for MySQL. Consequently, when DROP CHECK is called for MySQL, an ENUM() column will be coverted to a VARCHAR.

Add Primary Key
ALTER TABLE table_name ADD PRIMARY KEY (column_name [, ... ] ) [ AUTOINCREMENT ]

Creates a primary key for table_name with the columns specified. An fSQLException will be thrown if a primary key already exists.

If only one column is specified and the AUTOINCREMENT keyword is provided, the column will be configured to automatically created auto-incrementing integer values when no value is provided. The actual implementation of AUTOINCREMENT varies widely between databases. Please see Data Types for details.

Drop Primary Key
ALTER TABLE table_name DROP PRIMARY KEY

Drops the primary key for table_name. Any foreign keys that reference this primary key will also be dropped. An fSQLException will be thrown if a primary key does not exist.

Add Foreign Key
ALTER TABLE table_name ADD FOREIGN KEY (column_name) REFERENCES foreign_table(foreign_column) [ ON DELETE action ]

Adds a foreign key to the column specified. Currently, Flourish as a whole, only supports single-column foreign key constraints. This restriction is also present for this ALTER TABLE statement. The valid action values are the same as those supported by Create Table. An fSQLException will be thrown if a foreign key already exist.

Since DB2 and Oracle do not fully support ON UPDATE clauses, such a clause will be automatically removed for those databases.

Drop Foreign Key
ALTER TABLE table_name DROP FOREIGN KEY (column_name)

Drops the foreign key for the column specified. Currently, Flourish as a whole, only supports single-column foreign key constraints. An fSQLException will be thrown if a foreign key does not exist.

Add Unique
ALTER TABLE table_name ADD UNIQUE (column_name [, ... ] )

Adds a unique constraint to the column(s) specified. Some databases, such as MSSQL and DB2, treat a NULL value as a distinct value, only allowing one per column. PostgreSQL, SQLite, Oracle and MySQL treat NULL specially and allow any number of NULLs in a unique column.

Drop Unique
ALTER TABLE table_name DROP UNIQUE (column_name [, ... ] )

Drop the unique constraint that exists for the column(s) specified. Any foreign keys that reference this unique constraint will also be dropped. An fSQLException will be thrown if a UNIQUE constraint does not exist for the column(s) specified.

Comment on Column

COMMENT ON COLUMN table_name.column_name IS 'Comment value'

For SQLite, this statement adds an inline SQL comment at the end of the line that defines the column. All other databases have a native system for storing comments.

Drop Table

DROP TABLE table_name

Create Index

The following syntax is supported for CREATE INDEX statements.

CREATE [ UNIQUE ] INDEX index_name ON table_name ( column_name [, ... ] ) 

Drop Index

DROP INDEX index_name

Foreign Key Support

All of the databases supported by Flourish support foreign key constraints through some method. By using translatedQuery() to execute CREATE TABLE statements, you can be sure that foreign keys will be enforced.

PostgreSQL, MSSQL, Oracle and DB2 support foreign keys completely natively. MySQL supports them natively as long as the InnoDB engine type is specified in the CREATE TABLE statement. This is automatically added when using translatedQuery().

SQLite supports the syntax, however enforcement before version 3.6.19 has to be done through triggers. translatedQuery() will create the necessary triggers to enforce foreign key constraints on an SQLite database.

Please note that Oracle does not support ON UPDATE clauses, and DB2 only supports the NO ACTION and RESTRICT actions for ON UPDATE clauses. Because of these limitations, it is best to avoid ON UPDATE clauses for cross-database applications.

In addition, Flourish is designed to support only single-column foreign keys. While most databases support multi-column foreign keys, Flourish's SQLite foreign key trigger generation only supports a single column. In addition, the ALTER TABLE through Flourish SQL is only designed and tested against single- column foreign keys.

Getting Started

Since Flourish isnt a framework, getting started might seem a little daunting. Lets start with getting the Flourish code and setting up our pages to include it.

Downloading Flourish

As a first step, download flourish and extract it into a directory on your server. To help ensure that a server misconfiguration wont expose your PHP (possibly including database credentials), it is best to try and place your includes outside of the document root.

{doc_root}/../inc/flourish/

Set Up an Init Script

At the beginning of every page we arent going to want to include every class file or configuration file, so a logical first step is to create an initialization script to handle that. I prefer to create the script at:

{doc_root}/../inc/init.php

Inside of init.php, put:

<?php
include($_SERVER['DOCUMENT_ROOT'] . '/../inc/config.php');

Create a Configuration Script

For better separation, I prefer to put all configuration code into a separate file from the initialization code. I put all of my configuration code in:

{doc_root}/../inc/config.php

Inside of config.php we will put our auto-loading function:

<?php
/**
 * Automatically includes classes
 * 
 * @throws Exception
 * 
 * @param  string $class_name  Name of the class to load
 * @return void
 */
function __autoload($class_name)
{
	// Customize this to your root Flourish directory
	$flourish_root = $_SERVER['DOCUMENT_ROOT'] . '/../inc/flourish/';
	
	$file = $flourish_root . $class_name . '.php';

	if (file_exists($file)) {
		include $file;
		return;
	}
	
	throw new Exception('The class ' . $class_name . ' could not be loaded');
}

Performance tip: If you are running Flourish on a server that has APC, eAccelerator, Turck MMCache or XCache check out the section about class loading.

Start Using Flourish

Now that we have our init script setup, we can start creating pages and using Flourish. Just add this snippet to the top of each of your pages:

<?php
include_once($_SERVER['DOCUMENT_ROOT'] . '/../inc/init.php');

Alternatively if you run Apache and have access to the Apache conf file, you can use PHPs auto_prepend_file setting with the Apache PHP settings directives.

Next Steps

If you havent read it already, the How Do I ? page is a good resource for exploring some of the functionality of Flourish. The documentation includes links to detailed information about each class, class APIs and general documentation.

The Flourish Demo Site is a good way to dive into some code to see how Flourish can be used.

For a simpler more guided introduction to Flourish, try checking out the following classes:

The documentation below includes some topics that most PHP developers should know or learn about:

How Do I?

This page is meant to be sort of a compass to new users of Flourish to help them figure out where to look for solutions to their problems.

Task at Hand Where to Look
Handle exceptions and errors gracefully fCore
Ensure content is properly encoded (HTML entities) fHTML
Handle UTF-8 content fUTF8
Perform simple form validation (contact forms, etc) fValidation
Simple (non-ORM) interaction with a database fDatabase, fResult, fUnbufferedResult
Retrieve the structure of the database I am connected to fSchema
Interact with the database simply by using object-relational mapping (ORM) fActiveRecord, fRecordSet
Get and sanitize information from the users request, including GET, POST, PUT and DELETE data fRequest
Pass transient messages around without polluting URLs fMessaging
Store and retrieve session information fSession
Handle user permissions and access control fAuthorization
Ensure that data is stored securely (passwords, confidential information, etc) fCryptography
Handle dates and times, including timezones fDate, fTime, fTimestamp
Manipulate images fImage
Manipulate and query files and directories fFile, fDirectory
Handle file uploads fUpload
Use templating to reduce code duplication and keep logic and presentation separate fTemplating
Conditionally build English language constructs and convert between syntax notations fGrammar
Handle translation or modification of messages fText
Send emails and validate email addresses fEmail
Encode and decode JSON data fJSON
Deal with precise numbers and calculations fNumber
Represent, manipulate and translate monetary values fMoney
Handle sticky search values and sortable columns for list pages fCRUD
Get and set cookies, including HTTPOnly cookies and default values fCookie
Encode content for XML files fXML

Topic Pages

The following pages include some more in-depth information about topics, including links to the relevant Flourish classes.

Internal Backwards Compatibility Breaks

See also non-internal backwards compatibility breaks

Public methods flagged with the PHPDoc tag @internal and all protected methods and attributes are considered internal to Flourish and are subject to change even in minor revisions or during the beta. Below is a list of changes that affect internal code.

Normally this list will only be applicable if you write ORM plugins or mess around with the ORM internals.

Revision 881

Changed fORM::parseMethod() to not underscorize the subject of the method.

Revision 867

Changed the structure of the array returned from fSQLTranslation::translate() to include a number plus : before the original SQL in the array keys.

Revision 720

Removed:

Rewrote:

Added the $schema parameter to the beginning of:

Added the $class parameter to the beginning of fORMRelated::storeManyToMany()

Revision 659

fORMRelated::createRecord() now has an extra parameter in the middle, $related_records. fORMRelated::storeOneToMany() was renamed to fORMRelated::storeOneToStar().

Revision 639

Changed fActiveRecord::hash() from a protected method to a static public/internal method that requires the class name for non-fActiveRecord values

Revision 616

The method signatures for fORMRelated::validate() and fORMValidation::validateRelated() changed

Revision 603

The following methods no longer accept an object instance, just a class name:

Revision 593

Renamed fORMRelated::setRecords() to fORMRelated::setRecordSet() and fORMRelated::tallyRecords() to fORMRelated::setCount()

Internationalization and Localization

When creating code for audiences in more than one language, it is necessary to write it with internationalization (i18n) and localization (L10n) in mind.

A locale is a combination of a language and standards for formatting of numbers, date, currencies and more. Normally an locale is identified by a combination of a country and language code. Different languages require different sort ordering (or collation) for words and numbers often have different thousands and decimal separators. Other differences include date formatting and the words used for days and months.

Internationalization is the act of writing code than can be customized to work in multiple locales. Localization is the act of customizing code to work in a specific locale. Flourish is written with internationalization in mind, however there is no localization done, that is left for the teams working on individual projects.

Internationalized Classes

The following classes in Flourish are in some way internationalized, thus capable of being localized.

Please note that every class in Flourish that creates messages hooks into fText (once loaded) and thus all messages can be translated through the features available in fText. The message list page includes a list of every message created by a Flourish class, along with the location in the source code.

License

The source code and documentation for Flourish are licensed under two separate licenses due to their nature of being two vastly different things.

The Flourish logo and http://flourishlib.com site design are (c) 2007-2012 Will Bond and are not licensed under either of these licenses.

Source Code License

Info about the MIT License:

Copyright (c) 2007-2010 Will Bond <will@flourishlib.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Documentation License

The license for Flourish documentation is based on the FreeBSD Documentation License with modifications to address the format in which the Flourish documentation is presented.

Info about the FreeBSD Documentation License:

Copyright (c) 2007-2010 Will Bond <will@flourishlib.com>. All rights reserved.

Redistribution and use in any form (html, pdf, etc) with or without modification, are permitted provided that the following condition is met:

Redistributions must reproduce the above copyright notice, this condition and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS DOCUMENTATION IS PROVIDED BY WILLIAM BOND "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WILLIAM BOND BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Messages List

This page contains resources for use when translating the messages used internally by Flourish.

Downloadable Template

A ready-made PHP array is available that includes every string as an array key and blank string values to use for the translations. This array can then be used with fText to perform translation of internal Flourish messaging. See the fText documentation for an example.

If you fill one of these out for a language, please send a copy to will@wbond.net so I may post it on the site for others to use also.

Translations

The following translations have been generously created by the community. It is possible that the messages may be slightly out-of-date due to changes in Flourish since the revision listed.

Linked Reference

The following table contains a list of every message used in the current revision of Flourish, and their locations in the source code.

[Include(http://docs.flourishlib.com/messages.php)

Method Naming

Flourish uses some standards in method names to help developers easily identify functionality without having to review the documentation. Below is a list of verbs and their connotations in Flourish code:

The Flourish ORM also has a few conventions for method names:

ORM (Object-Relational Mapping)

When dealing with database-driven web applications (and sites) often times interacting with the DB is one of the most repetitive and messy tasks. Having to manually build SELECT, INSERT, UPDATE, and DELETE queries for a multitude of tables is time consuming and mostly deterministic.

Object-Relational Mapping (ORM) helps to reduce the tedium, creates cleaner code, make relationships easy to access. Rows from a relational database management system (RDBMS) such as MySQL, PostgreSQL, SQL Server, Oracle, DB2, etc. are translated into objects in the programming language.

ORM Classes in Flourish

The ORM features of Flourish are primarily accomplished by the following two classes:

There are a number of other classes in Flourish that provide functionality to fActiveRecord and fRecordSet:

Why Another ORM?

Many of the big PHP frameworks (CakePHP, Code Igniter, Symfony, Zend Framework) perform ORM to some degree. There are also a few PHP ORM suites (Propel and Doctrine) focused on strictly ORM tasks.

You may be wondering, why build another PHP "framework" with ORM functionality? Flourish is intended to be more of a library of PHP code as opposed to a framework (defined as strict set of conventions, rules and methods that must be followed to build a site). In addition Flourish sits somewhere between the frameworks and ORM suites by providing some of the more advanced ORM features that most of the frameworks are missing, while being lighter weight and easier to use than the ORM suites.

You may also be interested in checking out some of the motivations for creating Flourish instead of using an existing solution.

Obtaining a Secure Certificate/Key Pair

When dealing with public- key encryption using the fCryptography class or sending secure emails via the fEmail class, you will need an x509 public-key certificate and a PEM-encoded private key.

Certificate authorities offer certificates that will validate your identity by the public, however, self-generated keys can be used for any purposes where identity verification is not important, just encryption.

InstantSSL and StartSSL offer certificates for free, while VeriSign and many others offer them for a fee. Most of these services tend to install the certificate in your browser, and thus only work with some browsers. As of January 2010, Google Chrome seems to be the only major browser not providing this functionality. Also, since the private key/certificate is installed in your browser, you'll need to figure out how to use your browser to export the .p12 file.

Self-signed certificates are not recommended for a secure connection (https://) on a public web server since browsers will not recognize you as an authorized certificate authority (CA) and warning messages will be displayed.

Generating a Certificate with OpenSSL

There are various methods to generate a self-signed SSL certificate, however one of the simplest to explain in using the OpenSSL executable. Most Linux/BSD distributions and OSX all have it installed by default while Windows users can download an installer or use it through Cygwin. If you run the Windows installer, be sure to open a command prompt and cd to the {install_dir}\bin before executing these commands.

First, lets generate the private key file (it will be output in PEM format) by executing the following command:

openssl genrsa -des3 -out private.key 1024

The output will look something like the text below and will prompt for a passphrase (and a repeat). This is essential for restricting access to the key if it is stored on the same server as the encrypted data.

Generating RSA private key, 1024 bit long modulus
.....................................++++++
........................++++++
e is 65537 (0x10001)
Enter pass phrase for private.key:
Verifying - Enter pass phrase for private.key:

Next we need to create a certificate signing request (CSR) which is used to generate the certificate:

openssl req -new -x509 -key private.key -out public.crt

You will be asked a series of questions used for identifying the owner of the certificate (in this case, you). Below is an example with my answers in bold:

Enter pass phrase for private.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Massachusetts
Locality Name (eg, city) []:Newburyport
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Flourish
Organizational Unit Name (eg, section) []:Web Development
Common Name (eg, YOUR name) []:William Bond
Email Address []:will@flourishlib.com

When performing public-key encryption with the fCryptography you will need the public.crt and private.key files.

Creating a PKCS! File from a Private Key and Public Certificate

If you are going to use the certificate you generated with the fEmail class you will need public.crt for the web server and a PKCS#12 formatted file for your email program. You can generate the PKCS!#12 file by executing the following:

openssl pkcs12 -export -inkey private.key -in public.crt -out private_public.p12 -name "Flourish Certificate"

Notice that the PKCS!#12 file contains both the public and the private keys. You will asked for a password for the PKCS!#12 file, which you will also have to provide to the email program you use it with:

Enter pass phrase for private.key:
Enter Export Password:
Verifying - Enter Export Password:

Creating a Private Key and Public Certificate from a PKCS! File

Depending on where you obtain a secure certificate from, you may only receive a .p12 file since it contains both the private key and public certificate. If you are going to use this certificate with fCryptography or fEmail you are going to need the private key and public certificate separated into individual files in specific formats.

The following command will export a public.crt file from an PKCS!#12 named private_public.p12:

openssl pkcs12 -in private_public.p12 -clcerts -nokeys | openssl x509 -out public.crt

Youll be asked for the .p12 files password:

Enter Import Password:
MAC verified OK

Next, well export a private.key file from the same .p12 file:

openssl pkcs12 -in private_public.p12 -nocerts -nodes | openssl rsa -des3 -out private.key

First you will be prompted for the .p12 password, after which you will be prompted for the private key password (with repeat).

Enter Import Password:
MAC verified OK
writing RSA key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

You will now have public.crt and private.key files for use with fCryptography and fEmail.

ORM Conventions

The Flourish ORM uses a few different conventions to prevent needless configuration and to reduce typing. Most of the conventions have to do with the database schema and various notations.

Primary Keys

The whole ORM is built in such a way that all tables you are using with it should have primary keys. Without primary keys, things may start acting weird or breaking. If you don't have primary keys for your tables, consider adding them, they're generally considered a best practice.

Database Table & Column Notation

The Flourish ORM assumes that all database table and column names are written in underscore_notation. Below is an example of a correctly implemented database table:

CREATE TABLE users (
    first_name VARCHAR(255) NOT NULL,
    last_name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL
);

This database table would not be properly detected:

CREATE TABLE Users (
    FirstName VARCHAR(255) NOT NULL,
    LastName VARCHAR(255) NOT NULL,
    Email VARCHAR(255) NOT NULL UNIQUE,
    Password VARCHAR(100) NOT NULL
);

With correct underscore_notation, numbers should be 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.

Method Notation

Methods for fActiveRecord objects use lowerCamelCase, like the rest of the methods in Flourish. When dealing with database columns, every method will be in the form verbColumnName(). Below are some example of working with the users table defined in Schema Notation:

$user->getFirstName();
$user->setLastName($last_name);

HTML Form Notation

HTML forms use underscore_notation just as the database schema should. The method fActiveRecord::populate() will looks for input names in underscore_notation when populating an object. Below is an example of a valid HTML form that will work with the users table defined in Schema Notation:

<form action="" method="post" charset="utf-8">
    <p>
        <label for="users-first_name">First Name:</label>
        <input id="users-first_name" type="text" name="first_name" />
    </p>
    <p>
        <label for="users-last_name">Last Name:</label>
        <input id="users-last_name" type="text" name="last_name" />
    </p>
    <p>
        <label for="users-email">Email:</label>
        <input id="users-email" type="text" name="email" />
    </p>
    <p>
        <label for="users-password">Password:</label>
        <input id="users-password" type="password" name="password" />
    </p>
</form>

Table Names

Database table names should always be plural nouns. A proper database table name would be users, not user.

For existing databases, it is possible to configure a class to model a non- plural table name, or a name that is different than the class. The static method fORM::mapClassToTable() accepts a $class and $table and will override the default mapping. The fActiveRecord page includes an example of custom mapping.

Joining Tables

When two tables are related in a many-to-many relationship the proper way to model the relationship is to use a table consisting of the primary keys from each of the two tables. Flourish uses the term joining table to refer to these.

Currently Flourish only works with single column FOREIGN KEY constraints, thus these simple tables consist of exactly two columns, each of which have a FOREIGN KEY constraint. The PRIMARY KEY of the joining table a multi-column key containing both FOREIGN KEY columns.

Relationship Routes

When two database tables are in more than one relationship via FOREIGN KEY constraints, the Flourish ORM uses the term route to refer to the different ways in which the two tables are related. If two tables only have a single relationship, routes will never need to be specified. Otherwise routes will, and the following rules are used to determine the route name.

Schemas (Oracle/DB2 Users)

PostgreSQL, MSSQL, Oracle and DB2 all have the concept of schemas, although in Oracle and DB2 a schema is simply a specific user's set of database objects. Schemas are used for grouping tables, views, functions and other database objects.

With the Flourish ORM (and in raw SQL) a table in the non-default schema (public for PostgreSQL, dbo for MSSQL and the username for Oracle) is represented by schema.table. Anywhere that a table name can be used in the ORM, a schema.table string can also be used. This includes methods such as fORM::mapClassToTable(), for mapping a class to a table with a different name or in a different schema, and fRecordSet::build(), when specifying a related table in the $where_conditions.

Multiple Databases

The Flourish ORM supports using multiple databases, both for vertical partition and master-slave setups. Please see the fORMDatabase documentation for more information.

MySQL Storage Engine

The Flourish ORM is built on top of the principles of relational database systems including transactions and foreign key constraints. MySQL is built in such a way that multiple storage engines are supported to do the work of actually storing data. Choosing the right storage engine for MySQL is essential for getting the Flourish ORM to work to the best of its abilities.

Unfortunately not all of the MySQL storage engines support the necessary features such as transactions and foreign key constraints. In fact the default storage engine, MyISAM, does not support either of these features as of MySQL

  1. 0. The InnoDB storage engine, which is shipped with MySQL by default, does

however support the necessary features. Because of these feature limitations, developers should be sure to specify the InnoDB storage engine when creating tables to be used with the Flourish ORM.

Below is an example of creating an InnoDB database table in MySQL, note specifically the ENGINE parameter after the closing ) of the table definition.

CREATE TABLE users (
    customer_id INTEGER AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(255) NOT NULL,
    last_name VARCHAR(255) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL
) ENGINE=InnoDB;

If the InnoDB engine is not used for tables, foreign key constraints will not be created and Flourish will be unable to automatically detect the relationships between tables. In addition, any operations on multiple records will not be atomic since transactions are not supported. Thus the first record could be successfully changed, but if the second one fails, the first will not be rolled back.

SQLite Foreign Key Constraints

SQLite databases supports the syntax for foreign key constraints, however does not enforce them as of version 3.6.4. In order to enforce the foreign key constraints, triggers must be used instead.

A slightly old, but still relevant, wiki page on the SQLite site explains how triggers can be used to enforce foreign keys. There are a couple of tools mentioned that can automatically generate the appropriate triggers.

In addition, the fDatabase::translatedQuery() method will automatically create appropriate triggers for CREATE TABLE statements executed through an instance of the fDatabase class. If a CREATE TABLE statement is executed that includes a FOREIGN KEY constraint, the clauses will be automatically parsed for the relevant restrictions and the triggers will be created. Both ON UPDATE and ON DELETE clauses plus the actions RESTRICT, NO ACTION, CASCADE and SET NULL are supported:

// The following will create a users table that actually
// enforces the group_id foreign key constraint
$db->translatedQuery("
CREATE TABLE users (
    customer_id INTEGER AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(255) NOT NULL,
    last_name VARCHAR(255) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    group_id INTEGER REFERENCES groups(group_id) ON DELETE RESTRICT
)
");

Performance Tips

This page is a small collection of some tips to help increase performance of you PHP code. Most of them require that you have enough control of the server to be able to change settings in Apache and load different PHP extensions.

Opcode Caching

Under normal operation, PHP will load each source code file upon each request. The source code files are first compiled into PHP opcodes and then the opcodes are executed. A number of PHP opcode caching extensions exist that keep the compiled opcodes stored in memory and only regenerate the opcode if the source code changes.

Mike Willbanks has a comparison of APC, eAccelerator and XCache from late 2007.

fLoader will utilize the most performant loading technique based on the current execution environment. If a opcode cache is present, all classes will be included. If not, autoloading will be done via spl_autoload_register().

Extensions

A few of the Flourish classes provide pure PHP implementations of functionality that is also available from extensions that are not installed by default. Below is a list of the classes and the extension that will provide better performance.

Class Extension
fJSON json (included in 5.2 by default)
fNumber/fMoney bcmath
fUTF8 mbstring

PHP Version Differences

Flourish is designer to be fully functional on PHP 5.1+. There are, however a number of end-programmer differences between PHP 5.1 and 5.2. This page contains an overview of the major differences.

Callback Formats

In both 5.1 and 5.2, callbacks to instance methods of object are formulated as an array with the first element being the object and the second being a string of the method name.

$date = new fDate();
$callback = array($date, '__toString');

Similarly, with both 5.1 and 5.2 static method callbacks can be created with an array containing a string of the class and a string of the static method to call.

$callback = array('fCore', 'backtrace');

In 5.2, however, a second static method callback format was added. The new format consists of a single string with the class name concatenated with :: and then the method name.

$callback = 'fCore::backtrace';

If you are using callbacks with Flourish, be sure to check out how Flourish provides an intuitive callback syntax.

Converting Objects to Strings

Many different kinds of objects, especially value objects, will intrinsically have a string representation of their value. In both 5.1 and 5.2 a __toString() method can be defined for any object. The difference between 5.1 and 5.2 is the automatic conversion of an object to a string.

In 5.1, the __toString() method is only automatically called when an object is echoed or printed. However, if the object is concatenated with another string during the echo or print statement, the __toString() method is not called. In PHP 5.2 this behavior was fixed, and the __toString() method is called whenever a string is expected.

In the context of Flourish, all classes that represent some sort of simple value have a predefined __toString() methods. Also, any methods that accept strings or objects will automatically check for a __toString() method on a object and call that. The only thing to keep in mind is that when using objects where strings are expected you will need to explicitly call __toString() if the code will be run on an environment with 5.1.

Reference Objects

A reference object refers to a class that is implemented in such a way that all instances with the same value share properties. Thus changes to one instance of an object representing a user should also show up in another object representing the same user. Reference objects are mutable, in that their state can be changed without creating a new object.

Flourish includes a few different classes that are reference objects:

Here is another page with some info about reference objects:

The opposite of a reference object is a value object.

Security

Security is an important topic when creating web sites. Quite a few techniques exist to compromise web servers, web browsers, or peoples information. This page serves as an overview of the various attacks that Flourish helps to prevent. While technology can help to keep sites secure, the most important part of the equation is that developers know what they are up against.

Cross-Site Request Forgery

CSRF attacks consist of malicious websites taking advantage of users being logged in to multiple web sites in a single browser, thereby allowing them to direct users to perform password protected tasks in an unauthorized manner. The fRequest class provides functionality to prevent CSRF attacks.

More info:

Cross-Site Scripting

Cross-site scripting (or XSS) is an attack that injects malicious code into a normally "safe" page. This usually takes the form of a link containing malicious content which is embedded in a page or by allowing posting of permanent data (such as blog comments) that gets embedded in the page. To prevent this type of attack, all input should be filtered and all output should be escaped. The fRequest::get() method allows for filtering input data with typecasting, while fHTML::escape() ensures that all output is properly escaped for output in HTML.

More info:

Session Fixation

Session fixation is when an attacker tricks a user into using a known session id to log into a website and then uses the same session id to access their account. The fSession class helps prevent this by setting the appropriate ini settings so that session ids can only be specified in cookies and not in the query string. The fAuthorization class takes it a step further and regenerates the session identifier whenever any new user information (such as ACLs or authorization level) is set.

More info:

Cross-Site Session Transfer

When multiple sites on a single server share the same directory to store their session files, it is possible that a user could log into a legitimate account on one site and then copy and paste the session id into the other sites session id cookie. This type of attack would allow an attacker to gain access to information of the second site as long as the $_SESSION data is structured the same as the first site.

The easiest way to prevent such an attack is to have each site use a custom directory to store session files by calling the static method fSession::setPath().

More info:

Pseudo-Random Number Generator Attacks

The pseudo-random number generator (PRNG) built into PHP includes a fairly secure seeding system to make sure that the number generated are hard to guess or replicate. Unfortunately the functions srand() and mt_srand() allow seeding the rand() and mt_rand() functions, and the seeds used are usually very insecure and can be calculated by attackers. It is also possible via a number of different techniques for an attacker to manage to reset the seed to a known value.

The fCryptography class provides functionality where the OS random number generator (/dev/urandom on BSD/Linux and the CAPICOM object on Windows) is used to seed the PRNG. If neither of those sources is available, a combination of a number of other attributes about the current server and install are used to create a seed value. Because of this functionality, the static method fCrytography::random() should always be used for random numbers and the static method fCryptography::randomString() should always be used for random strings.

More info:

SQL Injection

SQL injection is a technique where an attacker passes in a value into a site that includes SQL commands where the application is normally expecting a value to use in a query. Suppose there is a site that accepts a blog id in the query string of the blog page:

http://example.com/blogs.php?blog_id=25

This value would then be used in the SQL query to look up the appropriate blog:

$sql = "SELECT * FROM blogs WHERE blog_id = '" . $_GET['blog_id'] . "'";

which would create the following SQL statement:

SELECT * FROM blogs WHERE blog_id = '2'

If an attacker were to use a different value for the blog id, such as below, they could execute arbitrary SQL statements.

http://example.com/blogs.php?blog_id=2'%3B%20DELETE%20FROM%20blogs%3B

which would end up creating the following SQL statement:

SELECT * FROM blogs WHERE blog_id = '2'; DELETE FROM blogs;

The way to prevent such attacks is to always escape data that is being placed into a SQL query. The fDatabase class is built with this in mind, providing the static method fDatabase::escape() which allows escaping of data based on its type. In addition, each of the four different query methods allow using the same escape syntax.

More info:

Email Injection

Email injection is an attack similar to SQL injection, except that the malicious content must be placed in the $additional_headers parameter of the mail() function. Since the From: header is added to an email using this additional headers parameter, it is possible to an attacker to set additional headers or even inject full messages if user submitted data is included in the From: header.

Below is an example of code that would be vulnerable to an email injection attack:

$headers = "From: " . $_POST['name'] . " <" . $_POST['email'] . ">";
mail($to, $subject, $body, $headers);

If an attacker were to include line breaks in the name or email fields, they could add additional headers including To:, Cc:, Bcc:, Subject:, and even more like setting up a mime type. PHP automatically prevents line break characters in the $subject parameter of mail(), while the $to parameter requires a valid email address and the $message parameter would just display the headers as normal text content.

Both the fValidation and fEmail classes contain features to help prevent email injection attacks. The fValidation class includes methods to check fields for being valid email addresses and to make sure they dont include line breaks. The fEmail class provides full prevention against attacks by validating each email address and name (to, from, reply to, etc) plus the subject to ensure they dont contain vulnerabilities.

More info:

Path Disclosure

Error messages and unhandled exception messages often include full file or directory paths to documents on your server. During development it is useful to display this information so that debugging is easier. Once a site has moved to its production environment, however, these file and directory paths can be a security risk. While not a risk in an of themselves, the information is useful when combined with other attacks.

Say that a site is on a shared server with a custom session path to prevent cross-site session transfer attacks. Obviously the directory has to be readable and writable by the web server, otherwise the session files would not be saved. If the attacker was able to find the session path via an error or exception message, they could then set their application to use the same session path and execute an attack.

To prevent this information from leaking, error and exception messages should be routed to a log file or an email address once a site is in production. The static methods fCore::enableErrorHandling() and fCore::enableExceptionHandling() provide such functionality, plus more.

More info:

Path Traversal

Path traversal is an attack in which a script that uses user input to access a file is fed a path outside of the intended directory. Suppose the following URL allows downloading files from a directory:

http://example.com/download.php?file=image.gif

And that download.php contained code such as:

$path   = '/path/to/download/dir/' . $_GET['file'];
$handle = fopen($path, 'rb');
fpassthru($handle);
fclose($handle);
exit;

If a user specified the following URL, they could access the file /etc/passwd since they are using the ../ notation for the parent directory:

http://example.com/download.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd

The best way to prevent such an attack is to not accept filenames from users, but instead accept an identifer which can be mapped to a filename.

$valid_files = array(
    '1' => 'image.gif',
    '2' => 'image.jpg',
    '3' => 'image.png'
);

$file = $_GET['file'];

if (isset($valid_files[$file])) {
    // Handle the error
}

$path = '/path/to/download/dir/' . $file;

Similarly, another option is to create some sort of whitelist of valid files and make sure the file requested is in the whitelist.

If it is not possible to create a whitelist, the function realpath() will return the canonical form of a path, resolving any ../ paths and following any symlinks. For the example above, realpath() would have returned /etc/passwd. Thus, checking the output of realpath() against the valid file directory can prevent unauthorized filesystem access:

$dir  = '/path/to/download/dir/';
$path = realpath($dir . $_GET['file']);

if (strpos($path, $dir) !== 0 || $path == $dir) {
    // Handle the error
}

The fFile class can be useful in validating files since it will throw an fValidation exception if the file is not found and will return the output of realpath() from the method fFile::getPath().

More info:

Request Value Fixation

Request value fixation is an attack that is only effective against script that use the $_REQUEST superglobal. The $_REQUEST superglobal pulls in values from $_GET, $_POST, and $_COOKIE, in that order. This means that any value specified in a cookie with the same key as a value passed in by a query string will always resolve to the cookie value.

This type of attack would require that a users cookie be compromised, but could cause serious issues. Because of this vulnerability, the $_REQUEST superglobal should not be used. The fRequest class provides functionality to retrieve values from both the $_GET and $_POST superglobals (in that order) and provides additional useful features.

More info:

Invalid Character Encoding

Some multibyte character encodings can cause issues when escaping data, especially when creating SQL statements. The issues is that some character encodings use the \ character as a second, third or fourth byte in a multibyte sequence, however that can be combined with other characters to cause escaping to be done incorrectly.

UTF-8, the character encoding that Flourish uses for everything, does not suffer from this type of vulnerability. As a precaution, however, the static method fRequest::get() filters all incoming content through fUTF8::clean() to ensure that no malformed UTF-8 characters are present. In a similar sense, all data imported into the system should be converted to UTF-8, and cleaned to make sure no invalid characters exist.

More info:

File Uploads

File uploads can clearly be a security vulnerability if no filtering is done of user input. Malicious users could upload executables containing viruses, trojans, or any other sort of harmful content. Because of this, it is best practice to limit the accepted file types to those that are suitable for the task at hand.

The $_FILES superglobal that PHP provides to access file uploads includes a type key which contains the mime type of the file. This mime type, however, is specified by the user and should not be trusted for filtering nefarious files. The fUpload class, however, includes functionality that filters uploaded files by their mime type, with the mime type checking being done on the server side.

More info:

Password Hashing

Users' passwords should never be stored in plain text. As much as there is a desire to provide functionality such as "email me my password," the benefit is not worth the risk of an attacker obtaining a list of users and their passwords. Instead, all user passwords should be hashed and then functionality such as "reset my password" should be implemented.

It is also important to understand that simply hashing a password using md5() or sha1() is not enough to provide reasonable security. Before hashing the password, it should be combined with a salt to provide extra security and protect against certain forms of attack. The fCryptography class provides secure password hashing functionality via the static methods fCryptography::hashPassword() and fCryptography::checkPasswordHash().

More info:

Magic Quotes and Register Globals

Magic quotes and register globals are two features built into PHP that can cause security issues and affect how user input is treated in PHP.

Magic quotes (specifically the ini setting magic_quotes_gpc) tries to prevent SQL injection attacks by automatically escaping all ', ", \ and NULL characters with a \. This affects all data in the $_GET, $_POST and $_COOKIE superglobals and is enabled by default in new PHP installations. The fRequest::get() and fCookie::get() static methods will automatically detect if magic_quotes_gpc is turned on and will remove the extra escape \ characters.

Register globals is a feature where all data from the $_GET, $_POST and $_COOKIE superglobals are exported into the global scope as variables. This can very quickly lead to injection vulnerabilities and this feature should never be enabled. Register globals has been disabled by default since PHP version

  1. 2.0.

More info:

Additional Security Resources

The following web sites and blogs have a wealth of information about web/PHP security and best practices.

UTF-8 in PHP

After some experience with PHP, developers will often start to notice issues related to character encoding including "weird" characters and multiple characters where there should only be one. Handling character encoding on the web usually means support the UTF-8 character encoding to allow for more than the standard ASCII characters present on US keyboard layouts. This page will try to give a brief overview of the issues and solutions to using UTF-8 with PHP.

What is UTF-8?

UTF-8 is a character encoding, or a way to represent characters in a digital manner. It is an encoding of the of the Unicode standard which is closely related to the Universal Character Set (UCS). There are many different character encodings in the Unicode standard, however UTF-8 has a few properties that make it one of the best and most popular choices for work on the web.

Unicode contains around 100,000 characters, allowing it to represent a majority of the written languages in the world. Other common characters sets in languages with Latin characters include ISO-8859-1 and Windows-1252. Each of these character encoding suffers from issues that they only support 256 different characters, with some common characters not being present (such as curly quotes and the Euro symbol in ISO-8859-1).

Since UTF-8 is an encoding that represents the Unicode standard, character availability is not an issue. However, to be able to represent so many different characters, UTF-8 uses multiple bytes of space for all non-ASCII characters. One of the nice properties of UTF-8 is that it is backwards compatible with ASCII and the first 128 characters in ISO-8859-1 and Windows-1252. In addition, UTF-8 is constructed in such a way that it is possible to tell where character boundaries are even if a parser is started in the middle of a character. Related to that, it is simple for a UTF-8 string to be verified as correctly encoded.

PHP and UTF-8

Unfortunately, PHP does not include native support for UTF-8. All of the built- in string functions are designed to work with single-byte encodings only. The mbstring extension exists to provide string functions that are compatible with UTF-8, however it only covers a fraction of the standard string functions, and is not installed by default.

In addition to manipulating strings, the character encoding also affects the HTML that is returned to browsers and text in databases. The HTML standard defines ISO-8859-1 as the default character set to use for HTML when not specified, which will cause "weird" characters when UTF-8 content is returned. In a similar fashion, both MySQL and PostgreSQL default to using ISO-8859-1 as the character encoding unless specified.

MSSQL is much more complicated to work with since the whole server uses a single character encoding. In order to store unicode information, the column data types must be specified as one of the national character types, NVARCHAR, NCHAR or NTEXT. These national columns store data in USC-2 encoding, which contains null bytes for the characters in the ASCII range. Unfortunately none of the PHP extensions support binary data to be returned in string columns, so the national columns require extra work to cast them as binary data and then translated the data once in PHP.

Flourishs UTF-8 Support

Even though PHP has poor UTF-8 support by default, there are ways to work around it. Since using UTF-8 is the most universal way to handle characters from other languages, Flourish includes code that automatically works the shortcomings of PHP and provides UTF-8 support in all situations. While Flourish can solve many of the PHP issues with UTF-8, it is also important to understand how UTF-8 affects the other aspects of building a web site.

Database Encoding

If a database is being used to store text information, it should be created using UTF-8 as the character encoding. This allows the widest range of characters to be properly stored, sorted and manipulated by the database.

PostgreSQL

For PostgreSQL, the database encoding must be specified when the database is created. If it is not, the database will have to be dumped and re-imported into a new database.

CREATE DATABASE database_name ENCODING = 'UTF-8';

The fDatabase class will also automatically switch the connection encoding of any PostgreSQL database to UTF-8 even if it is not set up with UTF-8 encoding. This ensures that all data coming from PostgreSQL is always UTF-8, however there can be issues with trying to store UTF-8 characters in a database that does not support all of the same characters. Commonly this will be manifested by characters being stored as ?s.

MySQL

MySQL allows the character encoding for content to be defined when the database is created, or when a table is created.

-- Setting the default encoding for a database
CREATE DATABASE example CHARACTER SET 'utf8';

-- Setting the encoding on a table
CREATE TABLE examples (
    name VARCHAR(255) PRIMARY KEY
) CHARACTER SET utf8;

However, fDatabase will also automatically switch the connection encoding for any MySQL database to UTF-8. This means that all data coming back to PHP will be encoded as UTF-8 regardless of the table encoding. There can, however, be issues if text containing UTF-8 data is stored in a table that can not handle as many characters as UTF-8. Commonly this will be manifested by characters being stored as ?s.

MSSQL

MSSQL does not support UTF-8 natively like the other database engines, and requires that all databases on a server use the same character encoding. Luckily it also provides the national (or unicode) character data types NVARCHAR, NCHAR and NTEXT that allow storing unicode text.

CREATE TABLE examples (
    name NVARCHAR(255) PRIMARY KEY,
    description NTEXT NOT NULL
);

If national columns are not used and the data you insert into the database can not be represented by the default database encoding, the characters will be stored as ?s.

There can be difficulties in dealing with such data types in PHP, however the fDatabase::translatedQuery() method has been developed in such a way that all data from national columns will be returned as UTF-8.

// This will ensure all data coming out of MSSQL is properly converted to UTF-8
$result = $db->translatedQuery("SELECT * FROM examples WHERE name = %s", $name);

When inserting strings containing UTF-8 into a MSSQL database, the method fDatabase::escape() should be used since it will detect extended characters and escape them properly.

// This will ensure all data going into MSSQL is properly stored in NCHAR, NVARCHAR, NTEXT columns
$result = $db->translatedQuery(
    "INSERT INTO examples (name, description) VALUES (%s, %s)",
    $name
    $description
);

If you are using a linux or BSD server, you are probably access SQL Server through FreeTDS. The following configuration options should be set in your freetds.conf to ensure that everything works properly.

The encoding (CP1252) may need to be changed if the Windows machine the server is running on uses a primary language other than English.

# This is the version of the protocol to use for SQL Server 2000
# and newer. Versions less than this may cause weird database bugs.
tds version = 8.0

# By default FreeTDS uses ISO-8859-1 which will turn some characters
# into ?s. CP1252 (or Windows-1252) is usually the character encoding
# used by SQL Server for English installs
client charset = CP1252

Depending on what linux distribution or flavor of BSD you are using, the freetds.conf may be in a few different directories, including:

  • /etc/
  • /etc/freetds/
  • /usr/local/etc/
  • /usr/local/etc/freetds/
It is also possible your operating system may not install a default freetds.conf, but it may provide a freetds.conf.dist or something similar.

SQLite
SQLite uses UTF-8 for all text storage by default, so no special configuration is needed.

Oracle
Like with the other databases, Oracle has both a database encoding and a client encoding, however, unlike the other databases, the client encoding can not be set at connection time.

In order to full support UTF-8, the Oracle database should be installed with the AL32UTF8 encoding for version 9i+ and UTF8 for version 8. If you are installing Oracle 10g XE, be sure to get the universal version that support multi-byte encodings.

On the client side, the character encoding is controlled by the NLS_LANG environmental variable. For US-English installs, this should probably be set to AMERICAN_AMERICA.AL32UTF8. In addition to the NLS_LANG environmental variable, the Oracle PHP drivers will also need the ORACLE_HOME environmental variable set. /connecting.htm#sthref81 Examples for Linux.

DB2
DB2 can be instructed to store UTF-8 as the database default, or on individual tables. UTF-8 is used whenever unicode is specified. To specified unicode as the default for a database, add the USING CODESET option to the CREATE DATABASE statement:

CREATE DATABASE example USING CODESET UTF-8 TERRITORY US;

The TERRITORY should be set to an appropriate locale for your data.

It is also possible to set the CCSID UNICODE on individual tables with CREATE TABLE:

CREATE TABLE example (
    -- ....
) CCSID UNICODE;

In addition to setting up your server for UTF-8 database, it is required to set the client to use UTF-8 also. Unfortunately this can not be controlled via SQL over the database connection like other database, but must be set as an environmental variable. In a method appropriate for your OS, set the DB2CODEPAGE environmental variable to 1208, which represents UTF-8.

For linux with a sh/bash shell, this can be accomplished via:

export DB2CODEPAGE=1208

For Windows, this can be set in the Environmental Variables panel under

My Computer > Properties > Advanced > Environmental Variables.

HTML Encoding

When delivering HTML to browsers, it is important to include the proper character encoding information so that the browser can properly display the characters. The method fHTML::sendHeader() sets the proper Content-type header for UTF-8 HTML. This method should be called for every page that returns HTML, and should be called before any output is created. Consequently, it will often be called in an init or bootstrapping script:

fHTML::sendHeader();

It is also good practice to include an HTML meta tag specifying the character set for when HTML is displayed from the filesystem instead of a web server.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        

Request Data

As long as the HTML encoding is set to UTF-8, all browsers should obey this setting and submit request data encoded as UTF-8. When retrieving information from GET and POST requests, the class fRequest ensures that all values coming in are valid UTF-8 strings, and cleans out those that are not.

// This value will be proper
$name = fRequest::get('name');

String Manipulation

The class fUTF8 is a static class that provides all of the same functionality as the normal PHP string functions, but in a way that is compatible with the multi- byte nature of UTF-8. The class documentation includes a list of all of the PHP string functions and their equivalent methods in fUTF8.

Importing Data

While Flourish is built to ensure that all data coming in through normal web interactions is clean and valid UTF-8, it is necessary to manually convert data coming from an old database or text files. The iconv PHP extension provides functionality to convert text stored in another encoding to UTF-8 using a simple function call.

When importing data from an old database, as long as the fDatabase class is used to connect to the old DB, all data should be automatically converted from the old encoding to UTF-8 on-the-fly. This is accomplished by the fact that fDatabase sets the connection encoding to UTF-8 whenever connecting to a database. This, does not, however mean that fDatabase will work seamlessly with a non UTF-8 database. Most of the default character sets for databases only support a subset of the UTF-8 characters, making conversion to UTF-8 fine, but conversion back nearly impossible.

Value Objects

The term value object refers to a small object that contains a simple value such as a dates, numbers, string or money. They are implemented to be immutable, meaning a new object is created whenever modifications are requested. This property helps to prevent the side affects that come from objects being passed by reference.

There are five classes in Flourish that are implemented as value objects:

Here are some other pages about value objects:

The opposite of a value object is a reference object.

Versioning

Versioning happens on two different levels with Flourish. On the lowest level, each class that is part of Flourish is versioned using a three-part number. On the library level, Flourish is versioned using the current revision number of the SVN repository.

Class Versioning

All classes are versioned using a three-part number in the following format:

{major_version}.{minor_version}.{edit_version}

Below are some examples of valid version numbers:

1.0.0
1.0.25
1.5.1
1.0.0b

The last version, 1.0.0b signifies that the class is currently in beta. This type of version will only exist before version 1.0.0, and will not be present once Flourish hits stable.

The version number have the following significance:

  • Major version: this will only change when a major rearchitecting of a class happens, or when backwards compatibility breaks occur
  • Minor version: an increase to the minor version indicates that a new feature was added or an existing feature was enhanced
  • Edit version: this is the most common change to the version and indicates a bug was fixed or changes were made that did not affect functionality in any way

Library Versioning

Flourish, as a library of related classes, is versioned simply by the current revision of the master SVN repository.