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