Sie sind auf Seite 1von 66

Developers Documentation

Welcome to the phpFox2 developers manual. This manual will help you get started with how to develop new modules and plug-ins for phpFox2. phpFox2 is based on a modular system, which runs all of the common features we as users see when we visit a phpFox2 website. It also contains libraries or libs as we will call it, which is the engine that runs the skeleton of phpFox2 and its modules. The products core is designed to be a CMS (Content Management System). If you were only to include the core engine and the few required Core modules to run the product it would simply by a CMS. We bring in modules to introduce other features, which once packed with a heap of them turn the CMS into a Social Networking Solution. With this in mind we leave it up to the client to decide what sort of a site they would like to run and what sort of features they really need to get their crowd interested in their site and what it has to offer. Since the core can be stripped away from modules this leaves a lot of freedom for the script to evolve into other products such as a Software Licensing System or a Bug Tracker. The modules are based on a MVC (Model View Controller) architectural pattern. phpFox2 is developed under a OOP (Object-oriented programming) environment, which will give future developers an easier time to enhance the product to their benefits. Requirements for the product have become more flexible when dealing with PHP related requirements such as safe_mode or open_base_dir. Using those as an example the product can work with whatever those are set to allowing clients not to worry about if their server has a specific setting enabled or not. Our goal is to have a product that is as flexible as possible to make sure it works on all of the popular hosts today. There is one strict requirement and that in order to run the product you must have PHP5. Past requirements have been PHP 4.3.3, however since PHP5 introduced a wave of improvements when it comes down to OOP we decided it was time we move forward. As for MySQL the same 4.1 is required, although phpFox2 will support other database drivers and not just MySQL. When reading this manual it is wise to read everything in order and not skip ahead as items that we cover later on will require knowledge of something you have learned earlier in the manual.

Server Requirements

PHP Version 5+ MySQL Version 4.1+ Web Server Apache Operating System Linux GD complied with your PHP Build Yes PHP memory_limit 32M

Optional Server Requirements


Notice: In order to run the music module:

PHP file_uploads 1 PHP post_max_size 40M PHP max_input_time "1" PHP upload_max_filesize 38M

Notice: In order to run the video module:


FFmpeg FFmpegPHP (optional) Mplayer + Mencoder flv2tool Libogg + Libvorbis LAME MP3 Encoder PHP file_uploads "1" PHP post_max_size "75M" PHP max_input_time "1" PHP upload_max_filesize "75M"

Web Browser Requirements


The requirements for a web browser depends on what theme you are using for your site. The default theme we release has been tested to work on the latest versions of all the most common web browsers (eg. Firefox, IE8, Opera, Chrome etc...). For those that use Internet Explorer must have version 7 or higher installed in order to properly use all the features provided with v2.
/file/ Stores files that are either uploaded or written to the server by the script, which requires folders within this folder to be writable. /cache/ Stores all files cached by the script. /pic/ Stores images uploaded by users using the script. /attachment/ Stores all attachments uploaded by users. /emoticon/ Stores all emoticons added by admins via the AdminCP. /user/ Stores all of the users profile images. /static/ Stores static JavaScript and CSS files cached by the script. /include/ Stores all PHP files that are included in the script. /cron/ Stores PHP files used for cron jobs. /source/ Stores PHP files used when a specific cron job is executed, in other words it holds all the actual cron jobs. /exec.php PHP file that is executed when running or calling a cron job. /library/ Stores the phpFox 2 engine library and any other 3rd party libraries that do not belong to a specific module. /phpfox/ Stores all the classes that belong to the phpFox 2 engine. /plugin/ Stores temporary plugin files being developed before they are exported for deployment. /setting/ Stores all the configuration files. /common.sett.php Includes all the common variables needed for the script to run but should not be modified. /constant.sett.php Includes all constants. /server.sett.php Includes the database configuration and is created via the Installer. This file may be modified at a later time by the Admin if they were moving to another server for example. /validator.sett.php Includes all regex used when validating forms. /xml/ Stores XML data which is used when installing or upgrading the product. /install/ Is the core Installer and handles the install and upgrade of the product. /module/ Stores all the core and 3rd party modules. /static/ Stores static data such as JavaScript and CSS files which do not need to be modified and do not belong to a specific theme or style. /theme/ Stores HTML files which control the themes provided with the product. /adminpanel/ Stores themes & styles for the AdminCP /frontend/ Stores themes & styles for the Front End. /install/ Stores themes & styles for the Installer. favicon.ico Default favicon image. htaccess.txt Sample .htaccess file which has information on how to enable the Short URL feature. index.php First file that is called when a user visits a phpFox 2 site and controls the action of the entire script from this file. robots.txt Used to allow/deny bots to visit certain folders on a site.

Preparation & Uploading of phpFox


At this point you should have the zip archive "unzipped", which should have contents similar to those shown in this structure guide:
tools/ upload/ readme.html

Go into the folder "upload/", which should have contents similar to those shown in this structure guide:
file/ include/ install/ module/ static/ theme/ favicon.ico htaccess.txt index.php robots.txt

Rename the file include/setting/server.sett.php.new to include/setting/server.sett.php Next, upload the files and folders within the "upload/" folder to your site using an FTP client. Be sure to overwrite any old files. Once that is completed we can move on to the next step. CHMOD & File Permissions With the phpFox package there are certain folders and files that need write permission in order to execute certain tasks. Below you will find the files and folders that must have full write permission and set to 0777 in order for the script to be installed:
file/attachment/ file/cache/ file/css/ file/log/ file/music/ file/pic/ad/ file/pic/emoticon/ file/pic/emoticon/default/ file/pic/event/ file/pic/group/ file/pic/icon/ file/pic/marketplace/ file/pic/music/ file/pic/photo/ file/pic/poll/ file/pic/quiz/ file/pic/subscribe/ file/pic/user/ file/pic/video/ file/pic/watermark/ file/static/ file/video/ include/setting/server.sett.php

Once you have completed CHMODING all the needed files and folders continue to the next step.

Using the Web-Installer


An installer is provided to assist you in entering the information phpFox2 needs to connect to your SQL database. Run the installer file through your web browser by entering the URL into your browser address bar. If you have followed our example, type in http://www.domain.com/install/, naturally substituting 'domain.com' for your web address. Then follow these steps:
Step 1: Verification

We first check if the domain you are attempting to install the product on currently has a valid product license. There are several ways you can run a licensed product. You can run it on a licensed domain, which you provide the full path to where the software will be installed. You can also run a test site only for development purposes in a folder, sub-domain or localhost. The folder or sub- domain name must be called "phpfoxdev" or "development" and must be access protected. Here are some examples:
http://www.yoursite.com/phpfoxdev/ or http://www.yoursite.com/development/ (Live Development Server) http://phpfoxdev.yoursite.com/ or http://development.yoursite.com/ (Live Development Server) http://localhost/ or http://127.0.0.1 (Local Server) http://209.85.135.104/ (IP to Your Development Server)

Step 2: License

Abide to the License Agreement to continue to the next step.


Step 3: Requirement Check

Step 3 will consist of checking your server if it meets the software requirements and the correct file permission layout.
Step 4: Configuration

This step is where you will need to know your SQL username and password. If you do not know it, please ask your web host - in most cases it is different from the FTP log in information. The form is very straightforward, with only the following points needing closer examination. If you do not know what your "SQL Host" is, try "localhost" (without the quotes). If the SQL server is on the same server as your website (and in most cases it is) this will suffice. If you have created a new database for phpFox2, or wish to use an existing database, enter the name of the database in the appropriate field.
Video Module Configuration

These settings are optional and only should be filled if you plan to use the video module. The script will attempt to find the correct paths for both FFMPEG and MENCODER, however you should verify if they are correct with your host. If at any time these settings are incorrect you can change them directly from the AdminCP at a later time.
Step 5: Installer

The installer will now be automated until the final step. It will install all the required database tables and insert any data into each of the tables depending on what modules are being installed.
Step 6: Administrators Account

Fill out the form to create your administrators account.


Step 7: Complete

Congratulations phpFox2 is installed!

Installing Short URLs with mod_rewrite


In order to enable short URL's with phpFox2 you will need mod_rewrite, most servers today have support for this. To enable this feature open the file on your server:
htaccess.txt

Look for:
RewriteBase /

If you have installed phpFox2 in a sub-folder you will need to change the trailing slash with the sub-folder path. For example if you installed your site on:
http://www.yoursite.com/folder/

You will need to change the RewriteBase to:


RewriteBase /folder/

If you installed your phpFox2 in your sites root directory (eg. http://www.yoursite.com) you can leave this setting as it is. Next lets rename the file htaccess.txt to .htaccess

Once you have done that open the file /include/setting/server.sett.php and look for
$_CONF['core.url_rewrite'] = '2';

and replace that with $_CONF['core.url_rewrite'] = '1';

Save and close the file. You should now have short url's enabled on your site.

Enabling the Video Module


Since our video module relies on 3rd party scripts the ability to upload and convert videos is disabled by default. This feature needs to be enabled. First, we need to verify if FFMPEG and MENCODER is installed and we can find the correct path to them. During the install process we attempt to find these paths for you, however if these paths were wrong you can change them direct from the AdminCP. Navigate to...
AdminCP >> Settings >> Manage Settings >> Video

Look for Path to FFMPEG and Path to MENCODER Make sure the full path to these scripts are correct. If you are unsure you can ask your web host or connect to your server via SSH. If you have connected to your server via SSH execute this command whereis ffmpeg It would output something like this:
ffmpeg: /usr/bin/ffmpeg /usr/X11R6/bin/ffmpeg /usr/bin/X11/ffmpeg /usr/local/bin/ffmpeg /usr/share/man/man1/ffmpeg.1.gz

The full path would then be /usr/bin/ffmpeg This same method needs to be done for mencoder. Once you have entered the correct path enable this option
Enable Uploading of Videos

Once enabled submit the form and the script will attempt to connect to both scripts and if successful it will enable this feature. If unable to connect to the scripts a set of error message(s) will appear to guide you on what is wrong.

Error Log Reporting


During both the install & upgrade procedures we log all the activity within the folder "/file/log/". All the files themselves are secure so no 3rd party can view them unless accessed via the server using an FTP client for example. Currently the product is not supported and the only form of help can be reporting any problems to our developers by submitting a bug report. Submitting bug reports at this stage of the product is crucial so feel free to report any problems here. When submitting a bug report we ask that you ZIP the folder: /file/log/ Note we ask only to ZIP the folder "log" and not the entire "file" folder. If you do not have SSH access to your server you can access your server via an FTP client and browse to the folder: /file/ From there download the folder: /log/ Once the folder is on your computer you can right click and on most operating systems there should be an option to create a ZIP archive. Once a ZIP archive has been created feel free to submit the bug report and attach the ZIP and we can have all the details to help solve the problem.

Enabling Debug Mode


If you encounter problems on your site, enabling Debug Mode in many cases helps answer the question on what is causing a problem. By default all errors are suppressed so end-users do not see such errors. To enable Debug Mode this can be done either by enabling it from your AdminCP or accessing your web server. If you encounter a problem that does not cause a blank white screen you can enable debug mode from your AdminCP, however if your problem is due to a blank white screen you must enable debug mode by accessing your server and creating a PHP file.
Enabling from AdminCP

To enable debug mode from AdminCP, navigate to your AdminCP and go to...
Settings >> System Settings >> Manage Settings >> Debug

For the setting...


Debug Level

change the value from "Level 0" to any of the other levels.
Enabling from Your Server

To enable debug mode from your server you need to access your site via an FTP client or a hosting file manager. Once you have reached your sites root directory navigate to the folder...
include/setting/ dev.sett.php

Create a new file:

Open the file and add the following:


<?php define('PHPFOX_DEBUG', true); ?>

Save and close the file.

The "Musician" User Group Only Allowed Within Custom Fields


When you are adding custom fields you will notice you can only select the user group "Musician" from the "User Group" selection drop down. This is because the "Musician" user group is allowed to have custom fields, while other user groups by default do not have this option enabled. This feature can be enabled by editing the settings for each user group and enabling the option:
Can have special custom fields?

Video remains "in process"


If when you upload certain videos these remain in process it is possible that the database connection is timing out. To confirm this case you need to make sure that the video is being converted properly (as that is the first step in the uploading routine). This problem should also happen with some longer videos. The actual setting on mysql is called wait_timeout, it is measured in seconds and it tells how long to wait before it kills a connection. The connection is open since the user opens up "this" page, for example if they log in, check their private messages and then they go to the video section a connection is established since they got to the video section, not since they logged in. The video upload routine uploads and converts the video while the database session is still active, so if the conversion takes 40 seconds and your wait_timeout is set to 30 you will have this problem. In phpmyadmin you can check the value for wait_timeout by going to Variables and scrolling all the way down. If you have enough permissions you can change this value by executing: set@@global.wait_timeout=3000 In a linux default installation this value is set to 28800, but you should judge if this value is appropriate for your site as it may affect other features (it uses memory for longer time so accepting new connections may run your database server out of memory). Please note that using persistent connections will not help in this case. You should also change this value in the mysql.in

Developers Documentation
Welcome to the phpFox2 developers manual. This manual will help you get started with how to develop new modules and plug-ins for PhpFox2. PhpFox2 is based on a modular system, which runs all of the common features we as users see when we visit a phpFox2 website. It also contains libraries or libs as we will call it, which is the engine that runs the skeleton of phpFox2 and its modules. The products core is designed to be a CMS (Content Management System). If you were only to include the core engine and the few required Core modules to run the product it would simply by a CMS. We bring in modules to introduce other features, which once packed with a heap of them turn the CMS into a Social Networking Solution. With this in mind we leave it up to the client to decide what sort of a site they would like to run and what sort of features they really need to get their crowd interested in their site and what it has to offer. Since the core can be stripped away from modules this leaves a lot of freedom for the script to evolve into other products such as a Software Licensing System or a Bug Tracker. The modules are based on a MVC (Model View Controller) architectural pattern. phpFox2 is developed under a OOP (Object-oriented programming) environment, which will give future developers an easier time to enhance the product to their benefits. Requirements for the product have become more flexible when dealing with PHP related requirements such as safe_mode or open_base_dir. Using those as an example the product can work with whatever those are set to allowing clients not to worry about if their server has a specific setting enabled or not. Our goal is to have a product that is as flexible as possible to make sure it works on all of the popular hosts today. There is one strict requirement and that in order to run the product you must have PHP5. Past requirements have been PHP 4.3.3, however since PHP5 introduced a wave of improvements when it comes down to OOP we decided it was time we move forward. As for MySQL the same 4.1 is required, although phpFox2 will support other database drivers and not just MySQL.

When reading this manual it is wise to read everything in order and not skip ahead as items that we cover later on will require knowledge of something you have learned earlier in the manual. While you are developing there are certain constants to make things a lot easier for all of us. Many aspects of the script is cached into flat files. This can be from data we pull out from the database, templates files that are converted into PHP code or JavaScript files that are gzipped. While we are developing we always enable debug mode to make sure we don't have any errors in our code. The constants we are about to show you is a list of constants that do not all have to be set. You can choose which constants you want. If you do not want a certain constant simply remove it from the file. Note then when certain constants are enabled your site will run much slower. You have to consider that the site is not caching any data and this is very risky on a live community. Remember never have any of these settings on a live community. To enable this settings first create a file
include/setting/dev.sett.php

You can then add the following:


<?php // Enable debug define('PHPFOX_DEBUG', true); // Debug level define('PHPFOX_DEBUG_LEVEL', 1); // Force templates re-cache on each page refresh define('PHPFOX_NO_TEMPLATE_CACHE', true); // Force browsers to re-cache static files on each page refresh define('PHPFOX_NO_CSS_CACHE', true); // Override default email define('PHPFOX_DFAULT_OUT_EMAIL', 'your_email@email.com'); // Skip sending out of emails define('PHPFOX_SKIP_MAIL', true); // Use live templates and not those from the database define('PHPFOX_LIVE_TEMPLATES', true); // Add user_name in the title of each page. Great for when working with many browsers open define('PHPFOX_ADD_USER_TITLE', true); // Cache emails to flat files define('PHPFOX_CACHE_MAIL', true); // Log error messages to XML flat file within the cache folder define('PHPFOX_LOG_ERROR', true); // Skip the storing of cache files in the DB define('PHPFOX_CACHE_SKIP_DB_STORE', true); ?>

Naming Variables

Variable names should define what type of data it holds by adding a lower case letter after the dollar sign. Example:
$iFoo = 1;

In the above example you will notice the letter i after the dollar sign ($). The signifies that this specific variable holds an integer. Use the following letters to define the data the variable is holding:
// i = integer $iFoo = 1; // a = array $aFoo = array(1, 2, 3); // b = boolean $bFoo = true; // h = resource $hFoo = opendir('/var/www/phpfox/'); // s = string $sFoo = 'bar'; // o = object $oFoo = new Bar();

By doing this will give other developers an idea of what a certain variable holds without having to back trace the variable. Remember after you define what the variable holds you must start with an uppercase letter. Variables cannot contain underscores and must be separated by an uppercase character to signify that a new word is starting. Here are some wrong and right examples. Wrong
$variable_name = 'bar'; $aBar_name = array(1, 2, 3); $_new_name = 1;

Right
$sVariableName = 'bar'; $aBarName = array(1, 2, 3); $iNewName = 1;

When naming a variable try not to make the variable name too long or too short. Also don't simply make up words, try to name the variable by making a connection to what the variable holds. If for an example the variable you are creating is an array and it holds information for a user you could name it:
$aUser = array();

This way another developer has an idea that the variable $aUser is holding an array with user information. If a variable is holding an array that has numerous values you should create a plural variable name. Example:
$aUsers = array( 'user1', 'user2', 'user3' );

If an array holds only one value or is a string for example you can create a singular name. Example:
$aUser = array('user1'); $sUser = 'user name';

Classes

To understand how to name classes you will have to know more about how modules and libraries work with phpFox2. Since naming the class has very specific rules with how phpFox2 is designed its best to wait till we get to those sections to fully understand how these classes are named.
Functions/Methods

Naming a function/method is similar to naming a variable except we don't need to define what sort of data it will return. All functions/methods must start with a lower case letter and cannot contain any underscores unless its a private method, then and only then can it start with an underscore. Much like the variable naming you must separate words with an uppercase letter. If a method is not private and is either public or protected it still cannot start with an underscore. Here are some wrong and right examples. Wrong
function name_of_function($sFoo) { } private function name($sBar) { }

Right
function nameOfFunction($sFoo) { } private function _name($sBar) { }

If you are creating a method you must declare if its public, protected or private. When creating a function/method try to create a name that has something to do with what will actually be done in the function/method. If for example we are calling a method that will get an array for a specific menu you could name it:
public function getMenu()

Function Arguments

Arguments are subject to the same guidelines as variable names. We don't want a bunch of functions like:
doStuff($a, $b, $c)

Instead it should be:


doStuff($aUser, $bUpgrade, $iFriendId)

Properties

Properties are subject to the same guidelines as variable names. You must declare if a property is public, protected or private. If a property is private it must being with an underscore like so:
private $_bFoo = true;

Constants

All constants must be uppercase and must have a PHPFOX_ prefix. When naming a constant make sure its related to the data its holding. Here are some wrong and right examples:
define('my_constant', true); define('NEW_CONSTANT_NAME', true);

Right
define('PHPFOX_MY_CONSTANT', true); define('PHPFOX_NEW_CONSTANT_NAME', true);

phpFox Parameters

Certain Settings which can be found in the AdminCP are used throughout the script. The reason these variables are located in the AdminCP instead of being defined in the actual PHP script is Admins may need to edit some of these variables at a later time and its a lot easier for non- developers to edit variables from the AdminCP then it is from the source code. We won't go into much detail on how to add a parameter at the moment as we will go into that later once we reach that section. The variable name for such settings have all lowercase characters and spaces are separated by an underscore (_) and must contain the module ID. If for example we had a parameter that held a setting for the sites title it could be named core.site_title and getting the value via a PHP script can be done by calling the following static method:
echo Phpfox::getParam('core.site_title');

Notice where we have core, this is the module ID so the variable site_title belongs to the core module. If this setting belonged to the user module the call would be:
echo Phpfox::getParam('user.site_title');

Note that phpFox parameters are stored in the database table setting and is automatically cached once the script is executed.
phpFox Phrases

Phrase variables is what we use to get a phrase from the language package being used. This gives us the ability to provide a Multilingual product. We use all lowercase letters and replace spaces with underscores. This is actually automatically done when you create a new variable from the AdminCP so not much thought has to go into creating a variable for phrases. The only thing you can consider is making sure the name of the variable is somewhat related to what sort of information it holds. For example if a phrase holds information that a user has just logged out it could look something like this when trying to call the phpFox static method:
echo Phpfox::getPhrase('user.you_have_logged_out');

Notice that we have user as this specific phrase belongs to the user module. Similar to how settings work and much of the actual script, phrases belong to a module and if its a global phrase then it belongs to the core module.

Folders

All folders must be named with lowercase characters. It cannot contain any other characters other then those from A-Z. All folders must be named singular. Here are some wrong and right examples. Wrong
/my_folder/ /newFolder/ /Folder/

Right
/my/folder/ /new/folder/ /folder/

Filenames

All filenames must be lowercase and cannot contain any other characters other then those from A-Z. All filenames must be singular. If a PHP file contains a class it must have the following .class.php suffix. If a PHP file contains PHP data it must end with a .php. Here are some wrong and right examples. Wrong
Foo.php Foo.class.inc foo_bar.php

Right
foo.php foo.class.php /foo/bar.php

Coding Layout Include Braces

You must always include braces. There are a few ways on how to include your braces and this is usually up to how your personal coding style is, however to make sure we all understand the product we must all use the same style of coding. Below are some wrong and right examples of how to add braces. Wrong
if (condition) doStuff(); if (condition) doStuff(); if (condition) { doStuff(); } while (condition) ddoStuff(); for ($i = 0; $i < size; $i++) doStuff($i);

Right
if (condition) { doStuff(); } while (condition) { doStuff(); } for ($i = 0; $i < size; $i++) { doStuff(); }

Brace Positions

Braces always go on their own line. The closing brace should also always be at the same column as the corresponding opening brace, examples:
if (condition) { while (condition2) { ... } } else { } for ($i = 0; $i < $iSize; $i++) { ... } while (condition) { ... } function doStuff() { ... }

Use Spaces Between Tokens

This is another simple, easy step that helps keep code readable without much effort. Whenever you write an assignment, expression, etc.. Always leave one space between the tokens. Basically, write code as if it was English. Put spaces between variable names and operators. Don't put spaces just after an opening bracket or before a closing bracket. Don't put spaces just before a comma or a semicolon. This is best shown with a few examples, examples: Notice: Each pair shows the wrong way followed by the right way.
$i=0; $i = 0; if($i<7) ... if ($i < 7) ... if ( ($i < 7)&&($j > 8) ) ... if ($i < 7 && $j > 8) ... doStuff( $i, 'foo', $b ); doStuff($i, 'foo', $b); for($i=0; $i<$size; $i++) ... for ($i = 0; $i < $size; $i++) ... $i=($j < $size)?0:1; $i = (($j < $size) ? 0 : 1);

Operator Precedence

Always make it obvious by using brackets to force the precedence of an equation so you know what it does. Remember to not overuse this, as it may harden the readability. Basically, do not enclose single expressions. Examples: What's the result? Who knows.
$bBool = ($i < 7 && $j > 8 || $k == 4);

Now you can be certain what I'm doing here.


$bBool = (($i < 7) && (($j < 8) || ($k == 4)));

But this one is even better, because it is easier on the eye but the intention is preserved
$bBool = ($i < 7 && ($j < 8 || $k == 4));

Quoting Strings

There are two different ways to quote strings in PHP - either with single quotes or with double quotes. The main difference is that the parser does variable interpolation in double-quoted strings, but not in single quoted strings. Because of this, you should always use single quotes unless you specifically need variable interpolation to be done on that string. This way, we can save the parser the trouble of parsing a bunch of strings where no interpolation needs to be done. Also, if you are using a string variable as part of a function call, you do not need to enclose that variable in quotes. Again, this will just make unnecessary work for the parser. Note, however, that nearly all of the escape sequences that exist for double-quoted strings will not work with single- quoted strings. Be careful, and feel free to break this guideline if it's making your code easier to read, examples: Wrong
$sStr = "This is a really long string with no variables for the parser to find.";

Right
$sStr = 'This is a really long string with no variables for the parser to find.';

Notice: In SQL Statements mixing single and double quotes is partly allowed (following the guidelines listed here about SQL Formatting), else it should be tried to only use one method - mostly single quotes.
Associative Array Keys

In PHP, it's legal to use a literal string as a key to an associative array without quoting that string. We don't want to do this -- the string should always be quoted to avoid confusion. Note that this is only when we're using a literal, not when we're using a variable, examples: Wrong
$sFoo = $assocArray[blah];

Right
$sFoo = $assocArray['blah'];

Wrong
$sFoo = $assocArray["$sVar"];

Right
$sFoo = $assocArray[$sVar];

Magic Numbers

Don't use them. Use named constants for any literal value other than obvious special cases. Basically, it's ok to check if an array has 0 elements by using the literal 0. It's not ok to assign some special meaning to a number and then use it everywhere as a literal. This hurts readability AND maintainability. The constants true and false should be used in place of the literals 1 and 0 -- even though they have the same values (but not type!), it's more obvious what the actual logic is when you use the named constants. Typecast variables where it is needed, do not rely on the correct variable type (PHP is currently very loose on typecasting which can lead to security problems if a developer does not have a very close eye to it).
Don't Use Uninitialized Variables

For phpFox 2, we intend to use a higher level of run-time error reporting. This will mean that the use of an uninitialized variable will be reported as a warning. These warnings can be avoided by using the built-in isset() function to check whether a variable has been set - but preferably the variable is always existing. For checking if an array has a key set this can come in handy though, examples: Wrong
if ($iBlog) ...

Right
if (isset($iBlog)) ...

Right
if (isset($iBlog) && $iBlog == 1)...

The empty() function is useful if you want to check if a variable is not set or being empty (an empty string, 0 as an integer or string, NULL, false, an empty array or a variable declared, but without a value in a class). Therefore empty should be used in favor of isset($aArray) && sizeof($aArray) > 0 - this can be written in a shorter way as !empty($aArray).
Switch Statements

Switch/case code blocks can get a bit long sometimes. To have some level of notice and being in- line with the opening/closing brace requirement (where they are on the same line for better readability), this also applies to switch/case code blocks and the breaks. An example: Wrong
switch ($sMode) { case 'mode1': // I am doing something here break; case 'mode2': // I am doing something completely different here break; default: // Always assume that the case got not catched }

Right
break; switch ($sMode) { case 'mode1': // I am doing something here break; case 'mode2': // I am doing something completely different here break; default: break; }

Even if the break for the default case is not needed, it is sometimes better to include it just for readability and completeness.

elseif or else if

This depends on how you code but to make sure we are all on the same page we will just use elseif. Here is a wrong and right example. Wrong
if (condition) { ... } else if (condition) { ... }

Right
if (condition) { ... } elseif (condition) { ... }

Control Structures

All control structures must be lower case. Here is a wrong and right example: Wrong
IF (condition) { ... } ELSEIF (condition) { ... }

Right
if (condition) { ... } elseif (condition) { ... }

SQL/SQL Layout Common SQL Guidelines

All SQL should be cross-DB compatible, if DB specific SQL is used alternatives must be provided which work on all supported DB's (MySQL4/5, MSSQL (7.0 and 2000), PostgreSQL (7.0+), SQLite, Oracle8. All SQL commands should utilize the Database Abstraction Layer and not use the conventional methods of executing SQL queries.

Prefix

All of your queries must use the method we provide to allow sites that use a different prefix. In order to use a prefixed table you can use the following method:
Phpfox::getT('user');

If their prefix is phpfox_ then the above method will output


phpfox_user

Database Abstraction Layer

phpFox2 uses a database abstraction layer to make sure we can work with all the database drivers we support. In order to connect to this object there are several ways to accomplish this. At first sight these methods may look rather odd but once you get a clearer understanding of how we connect to phpFox2 libraries you will fully understand. This is something we will cover shortly after we look over the guidelines. If you would like to connect to the database from anywhere in the script you can do the following:
Phpfox::getLib('phpfox.database')->query('SELECT user FROM ' . Phpfox::getT('user'));

If you plan to use the database object more then once you can do the following:
$oDb = Phpfox::getLib('phpfox.database'); $oDb->query('SELECT user FROM ' . Phpfox::getT('user'));

If you are within a module service class you can do the following:
$this->database()->query('SELECT user FROM ' . Phpfox::getT('user'));

Notice: In many of the examples that are coming up we will be using the first method we displayed, however any of the above methods will work depending on where you are calling it from. You can use the conventional method of using mysql_query() anywhere in the script, however this will cause problems if for example another user is using an Mssql driver.
SQL Code Layout

Since we use a database abstraction layer for all our SQL queries this will take some getting used to especially if you are used to writing the conventional SQL query. However, once you learn the methods used it will be a lot easier and faster to get things down. Below we have listed examples of the common SQL queries and the abstraction layer method we provide.
SELECT

An example of how we perform a simple SELECT query that will pull out a users user name. Wrong
$hRes = mysql_query("SELECT user_name, email FROM " . Phpfox::getT('user') . " WHERE user_id = 1"); $aRow = mysql_fetch_array($hRes);

Right
$aRow = Phpfox::getLib('phpfox.database')->select('user_name, email')->from(Phpfox::getT('user')) ->where('user_id = 1')->execute('getRow');

As you will notice the methods used are similar to that of a query so there won't be too much of a confusion for those that are new to this. You will also notice the execute method. This is the method we use to specify what sort of a query we plan to execute. In this case we used getRow, which simply put gets one row from the database.

With phpFox2 we also provide an alternative to getRow, which is getSlaveRow. What this does is if you are for example using MySQL as your database driver and since it supports the ability to setup multiple slave servers the query will attempt to connect to one of the slave servers thus lowering the load off the main server. If your database driver does not have slave support then getSlaveRow will automatically default back to getRow. Note that you should use getSlaveRow wisely as if a users server as some sort of a delay before one of the slave servers is populated with the new data it could cause some server problems. So only use this if you feel the information that we are getting does not need to be "live". An example of when not to use this is during a login routine. This sort of information needs to be fully up-to-date at all times and if not it could cause the user problems when trying to login. If you wanted to select multiple rows you can execute the getRows method. An example of the wrong and right way are: Wrong
$hRes = mysql_query("SELECT user_name, email FROM " . Phpfox::getT('user') . " WHERE user_id = 1"); while ($aRow = mysql_fetch_array($hRes) { ... }

Right
$aRows = Phpfox::getLib('phpfox.database')->select('user_name, email')->from(Phpfox::getT('user')) ->where('user_id = 1')->execute('getRows'); foreach ($aRows as $aRow) { .... }

Much like how the method getRow has getSlaveRow you can instead use getSlaveRows instead of getRows. The same rules of caution apply to this method as it does withgetSlaveRow. Another useful method is getField and getSlaveField. This method will return the specific field and is very useful when trying to count how many total rows there are for a specific query. Here is an example:
$iCnt = Phpfox::getLib('phpfox.database')->select('COUNT(*)')->from(Phpfox::getT('user'))->execute('getField');

$iCnt will return the total number of members found in the user table.
INSERT

We also provide an easy way to insert data into the database. Here is an example of the wrong and right way. Wrong
mysql_query("INSERT INTO " . Phpfox::getT('user') . " SET user_name = 'natio', email = 'natio@phpfox.com'");

Right
Phpfox::getLib('phpfox.database')->insert(Phpfox::getT('user'), array('user_name' => 'natio', 'email' => 'natio@phpfox.com')

Another method we provide is if you would like to process the data before its added into the database. This is useful as sometimes we allow all the data being posted from a form to pass directly to the insert method. This can be dangerous as sometimes you do not want to insert injected data, however this method will check to make sure the data being posted matches what you want to store in the database. For example if the data for a users birthday needed to be a numeric value you can specify that this field is an int thus it makes sure its numeric before entering it into the database. If you do not specify a field in the database then this field will not have any data inserted into it thus protecting the field from any sort of SQL injection that might be attempted. Here is an example of how to utilize this feature:
Phpfox::getLib('phpfox.database')->process(array('user_id' => 'int', 'email', 'user_name'), $_POST)-> insert(Phpfox::getT('user'));

With the query above it will only allow us to insert the user_id, email and user_name into the database; it also makes sure that the user_id is a numeric value.

One final method we provide is the multi-insert as we call it. This will allow you to insert a lot more information with just one query. One downside with this method is not all database driver support this, however since you will be using the database abstraction layer there is nothing to worry about as for those drivers that don't support this method we simply do the conventional method within the layer. This sort of query won't be used as often but it can be rather handy in some occasions. Take the routine we use to check if any new phrases are found and if they are it lets us insert them into the database so the site has all the latest phrases for a language package. This is something we use during an upgrade of a site. Since we are upgrading and new phrases might have been added we need to find out which ones are new as users can be running very different versions.
$aInsert = array(); $aPhrases = Phpfox::getLib('phpfox.locale')->getPhrases(); $aRows = Phpfox::getLib('phpfox.database')->select('phrase')->from(Phpfox::getT('language_phrase')) ->execute('getSlaveRows'); foreach ($aRows as $aRow) { if (isset($aPhrases[$aRow['phrase']])) { continue; } $aInsert[] = array($aRow['language_id'], $aRow['phrase']); } if (count($aInsert)) { $this->database()->multiInsert(Phpfox::getT('language_phrase'), array('language_id','phrase'), $aInsert); }

By using this method it allows you to insert a lot more data with just one SQL insert

UPDATE

Updating is similar to that of inserting data. Here is an example of the wrong and right way. Wrong
mysql_query("UPDATE " . Phpfox::getT('user') . " SET user_name = 'natio', email = 'natio@test.com' WHERE user_id = 1");

Right
Phpfox::getLib('phpfox.database')->update(Phpfox::getT('user'), array('user_name' => 'natio', 'email' => 'natio@test.com'), 'user_id = 1');

Just like inserting data you can use a process method to update information which would be:
Phpfox::getLib('phpfox.database')->process(array('user_id' => 'int', 'email', 'user_name'), $_POST)->update(Phpfox::getT('user'), 'user_id = 1'); DELETE

For deleting a query we provide a very short method. Here is an example of the wrong and right way. Wrong
mysql_query("DELETE FROM " . Phpfox::getT('user') . " WHERE user_id = 1");

Right
Phpfox::getLib('phpfox.database')->delete(Phpfox::getT('user'), 'user_id = 1'); SELECT with JOINS

Most of the queries you will come across in the script include a joined query. To accomplish this you can do the following:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email')->from(Phpfox::getT('blog'), 'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->where('b.blog_id = 1') ->execute('getSlaveRows');

In the query above you will notice we added the following


->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')

with this we can JOIN another table. The first parameter is the table name. The 2nd parameter is the table alias for that join. The 3rd parameter is the index connection for that table. Its important to remember that these field(s) must be indexed or it could cause severe problems later on. If we were to translate this query into the conventional method it would look like the following:
Phpfox::getLib('phpfox.database')->query('SELECT b.blog_id, u.user, u.email FROM ' . Phpfox::getT('blog') . ' AS b JOIN ' . Phpfox::getT('user') . ' AS b ON(u.user_id = b.user_id) WHERE b.blog_id = 1');

If you were to use a LEFT JOIN instead of a JOIN the query would then look like this:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email')->from(Phpfox::getT('blog'), 'b')->leftJoin(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->where('b.blog_id = '1')->execute('getSlaveRows');

You can also use an innerJoin() which would be:


$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email') b.user_id') ->from(Phpfox::getT('blog'), 'b')->innerJoin(Phpfox::getT('user'), 'u', 'u.user_id = ->where('b.blog_id = 1')->execute('getSlaveRows'); ORDER BY

Ordering a query we use the order() method and the command we use is standard SQL. Here is an example:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email')->from(Phpfox::getT('blog'), 'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->where('b.blog_id = 1') ->order('b.time_stamp DESC')->execute('getSlaveRows');

If you were to translate this query to its conventional form it would look like the following:
Phpfox::getLib('phpfox.database')->query(' SELECT b.blog_id, u.user, u.email FROM ' . Phpfox::getT('blog') . ' AS b JOIN ' . Phpfox::getT('user') . ' AS b ON(u.user_id = b.user_id) WHERE b.blog_id = 1 ORDER BY b.time_stamp DESC'); LIMIT

Limiting a query we use the limit() method. Here is an example:


$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email')->from(Phpfox::getT('blog'), 'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->where('b.blog_id = 1') ->order('b.time_stamp DESC')->limit(0, 5)->execute('getSlaveRows');

If you were to translate this query to its conventional form it would look like the following:
Phpfox::getLib('phpfox.database')->query(' SELECT b.blog_id, u.user, u.email FROM ' . Phpfox::getT('blog') . ' AS b JOIN ' . Phpfox::getT('user') . ' AS b ON(u.user_id = b.user_id) WHERE b.blog_id = 1 ORDER BY b.time_stamp DESC LIMIT 0,5');

With the limit() method we also provide a feature that will get the correct limit by calculating the offset, mainly used on page that display numerous items and only a certain amount can be viewed on that page. Here is an example of how this query could look like:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email') ->from(Phpfox::getT('blog'), 'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->where('b.blog_id = 1')->order('b.time_stamp DESC')->limit($iPage, $sLimit, $iCnt)->execute('getSlaveRows');

You will notice the following part:


->limit($iPage, $sLimit, $iCnt)

The 1st parameter is the current page we are on (eg 1, 2, 3 etc...). The 2nd parameter is how many can be displayed on one page. The 3rd parameter is how many total items or in this case how many blogs we have. Using this method the script will automatically calculate the offset and return the correct LIMIT for you. This will be explained in more detail once we reach how pagination works, however this should get you a head start in that area.
GROUP

Notice: Mssql has a different way of processing grouped fields as it requires everything selected to be defined in the group clause and it also does not allow TEXT fields so use this with caution. In many cases we felt to deal with this problem with Mssql we used PHP logic to group queries instead. It unfortunately will cause a slight overhead but it is a route we must take. You can group a query by using the group() method. Here is an example:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email, COUNT(blog_id) AS total_blogs')->from(Phpfox::getT('blog'), 'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->group('b.blog_id, u.user, u.email')->order('b.time_stamp DESC')->execute('getSlaveRows');

If you were to translate this query to its conventional form it would look like the following:
Phpfox::getLib('phpfox.database')->query('SELECT b.blog_id, u.user, u.email, COUNT(blog_id) AS total_blogs FROM ' . Phpfox::getT('blog') . ' AS b JOIN ' . Phpfox::getT('user') . ' AS b ON(u.user_id = b.user_id) GROUP BY b.blog_id, u.user, u.email ORDER BY b.time_stamp DESC');

HAVING

If you would like to use HAVING you can use it with the having() method. Here is an example:
$aRows = Phpfox::getLib('phpfox.database')->select('b.blog_id, u.user, u.email, u.view')-> from(Phpfox::getT('blog'),'b')->join(Phpfox::getT('user'), 'u', 'u.user_id = b.user_id')->having('u.view > 1')->order('b.time_stamp DESC')->execute('getSlaveRows');

If you were to translate this query to its conventional form it would look like the following:
Phpfox::getLib('phpfox.database')->query('SELECT b.blog_id, u.user, u.email, u.view FROM ' . Phpfox::getT('blog') . ' AS b JOIN ' . Phpfox::getT('user') . ' AS b ON(u.user_id = b.user_id) HAVING u.view > 1 ORDER BY b.time_stamp DESC');

Escape

Escaping queries is very important. This is to prevent any sort of SQL injections at a later time. To make things a little easier for us we automatically escape data when inserting or updating data that is passed within the array. For example in this update:
Phpfox::getLib('phpfox.database')->update(Phpfox::getT('user'), array('email' => $aVals['email']), "user_name = '" . $aVals['user_name'] . "'");

In the example above $aVals['email'] will automatically be escaped since its within the update array. However, $aVals['user_name'] will not be automatically escaped so this is up to you to protect the script from any sort of SQL injections by using the method escape() like so:
Phpfox::getLib('phpfox.database')->update(Phpfox::getT('user'), array('email' => $aVals['email']), "user_name = '" . Phpfox::getLib('phpfox.database')->escape($aVals['user_name']) . "'");

SQL Quotes

Double quotes where applicable. Since SQL standard is to use single quotes within queries you must sometimes use double quotes especially when dealing with strings that need to be escape. Here is a wrong and right example: Wrong
$aRows = Phpfox::getLib('phpfox.database')->select('phrase')->from(Phpfox::getT('language_phrase')) ->where('phrase = "' . $aRow['var'] . '"')->execute('getSlaveRows');

Right
$aRows = Phpfox::getLib('phpfox.database')->select('phrase')->from(Phpfox::getT('language_phrase')) ->where('phrase = \'' . $aRow['var'] . '\'')->execute('getSlaveRows');

Classes
We will now go over the most used classes. We have listed the classes in order of its importance and by the frequency it is used.
Phpfox

Our main class is phpfox.class.php and can be found in the lib folder:
/include/library/phpfox/phpfox/

This class holds only static methods and is the most used class as it is what we use to connect to all libraries and modules. Below you will find the most common methods we use.
run()

We execute the script using the static method run(). If you check the /index.php file you will find the following:
Phpfox::run();

This calls the run() method and this is where all the magic happens.
getLib()

We discussed this method a little earlier and it is what we use to call other libraries.
getService()

We use this method to call a module service. More information regarding what a module service is and does will be mentioned later on. For the time being the method to call them is as follows:
Phpfox::getService('blog');

This would then require the following class and initiate the object
/module/blog/include/service/blog.class.php

To call a method from that

class you could do: Phpfox::getService('blog')->getBlogs(); This will then call the getBlogs() method. However if you are in a for loop or you plan to use the object more then once it might be a good idea to do the following:
$oServiceBlog = Phpfox::getService('blog'); $oServiceBlog->getBlogs(); $oServiceBlog->displayBlogs();

You may noticed we created the variable $oServiceBlog. It is important to prefix this variable with $oService so other developers know that this variable is an object and it is part of a service class. The variable ended with Blog and this will be easy to understand that the service class was blog. If you were to get another class within the blog service folder you could do the following
Phpfox::getService('blog.process');

This would then require the following class and initiate the object
/module/blog/include/service/process.class.php

or

/module/blog/include/service/process/process.class.php

Similar to how libs get included the script will try to find the correct service file for you. Remember to name the variable connected with the service with the same name of the service being called. So with the above example the variable should be $oServiceBlogProcess.
getT()

It is the method used to get a database table name with the prefix specifically used for a site. It is very important to use this method with all database interactions which require the table name. You must use this method as sites have different prefixes and if you use the default prefix or none at all when you develop, this will cause severe problems for those that will use your module in the future. This method only has one parameter and that is the name of the database table. Here is an example of how it is used:
Phpfox::getLib('phpfox.database')->select('user_name') ->from(Phpfox::getT('user')) ->execute('getRows');

As you can see from the query above we used this method to get the table name user.
getUserId()

This method will retrieve the user_id for the current user that is logged in. This is useful when inserting or updating data for a specific user as many tables require the users user_id, which is what connects many tables to the user table. An example is:
Phpfox::getLib('phpfox.database')->update(Phpfox::getT('user'), array('email' => $aVals['email']), 'user_id = ' . Phpfox::getUserId());

This database query will update the users email and it does a check on the user_id, which you can see at the end. This method does not have any parameters and it will return 0 if a user is not logged in.
getUserBy()

This method retrieve specific data for the user that is logged in. Much like the getUserId() method, this method uses the first and only parameter to return the field we need. The supported fields that are returned are session_hash, id_hash, captcha_hash, user_id, server_id, user_id, user_name, user_image, password, password_salt, user_group_id and hide_tip. There are many more fields that could be returned from the user table, however these are the only ones we need on every page. So if we wanted to get the user name for the user that is logged in we could do the following:
Phpfox::getUserBy('user_name');

This will then return their user name.


isUser()

This is useful if you are creating a page that only allows members access to it. If for example you have created a new page and you only want members to view this page you could add the following:
if (!Phpfox::isUser()) { exit('Need to be logged in!'); }

If a user is not logged in the script will exit and output the following on a blank white page:
Need to be logged in!

If you have properly created a phpFox controller, which you will learn shortly:
if (!Phpfox::isUser()) { return Phpfox_Error::display('Need to be logged in!'); }

This will then use the phpFox template and display the message on the site instead of a blank white page. These 2 options are good but the best way to exit a controller that does not allow access to guests is to simply send them to the login page, which will also automatically redirect them back to the last page they visited thus making it a lot more user friendly. In order to do this you can add this instead:
Phpfox::isUser(true);

You may have noticed that this method did not require an if statement and was only one line of code. We simply used the first parameter, which when set to true (bool) it will redirect the user if they are not logged in.
getUserParam()

We have two types of settings that control how many of the features function. Some of these settings are global and anything changed from those settings will effect all users, however there is another setting group which controls settings for a specific user group. Since phpFox2 supports numerous user groups you can specify exactly how a user group should behave. In order to get the value for a specific setting that controls how a user group behalves we use the method getUserParam. From the name you can understand that we will be getting a users parameter. The return value could be Strings, Arrays, Integers or Boolean. It really depends on the setting itself and what sort of information it holds. The most common return value is boolean and it returns either true or false. Lets take for example the Guest user group. Lets say we do not want them to view blogs and the variable name for this setting is blog.can_wiew_blogs (Note that the variable name can be found in the AdminCP and is defined by the one that created the setting). You can then do the following:
if (!Phpfox::getUserParam('blog.can_wiew_blogs')) { return Phpfox_Error::display('Not allowed to view blogs!'); }

From the example above you can see we used the first parameter for this method to identify which setting we would like returned. If it returned false we returned the script with an error message which will output the message:
Not allowed to view blogs!

If you do not want to display an error message and simply redirect them to the generic page that informs them that the feature they are trying to view/use is only allowed for certain user groups you can do the following:
Phpfox::getUserParam('blog.can_wiew_blogs', true);

As you can see we did not need an if statement any longer and we did everything in one line by adding a true (bool) to the 2nd parameter. Note that if the site allows subscriptions it will display a page guiding the user to upgrade their membership and if they are not logged in it will auto redirect to the login page. Just like how other params work we always identify a param with what module it belongs to and in this case it would be the blog module.
isAdminPanel()

It may come up that you need to identify if a page you are viewing is being viewed from the AdminCP. This is very rare as this is usually required in the core source code, but in case you need such a function this will return a boolean true or false with the latter confirming that its not currently being viewed from the AdminCP.

getPhrase()

phpFox2 has multilingual support and because of this it is very important to use the Language Package. More detailed information regarding the language package will be looked over later however getPhrase() is the method we use to return a phrase from the current language package that is being used. If for example we have a phrase with the variable name user.user_not_logged_in and the phrase value was
You are not logged in!

In order to display the phrases value we can call this method from anywhere in the script by doing the following:
Phpfox::getPhrase('user.user_not_logged_in')

There are some phrases that have values that need to be replaced by new values. For example if we had the following phrase with the variable name user.welcome_back_user:
Hello, {user_name}. Welcome back!

Since each user has a different user name we need PHP to identify what the user name is and since phrases are stored in a language package and mass distributed we would need to create a system to allow certain words to be replaced with PHP variables. Using the above example you can replace {user_name} with the user name for the current user logged in by doing the following:
Phpfox::getPhrase('user.welcome_back_user', array('user_name' => Phpfox::getUserBy('user_name')))

Notice we added the following:


array('user_name' => Phpfox::getUserBy('user_name'))

Since we use an array you can replace as many strings as you need as long as they are surrounded by { and }.
getParam()

Similar to the method getUserParam() this method returns a global setting. These settings are found in
AdminCP >> Settings >> Manage Settings.

If for example we had a variable name core.site_title which holds the value for the sites name we could get this value by doing the following:
Phpfox::getParam('core.site_title')

addMessage()

You may find that you need to notify a user with a quick message at times. It requires extra work if you do this via the template so adding a message with the method addMessage is rather easy as it requires only one parameter and that is the actual message you want to pass. Note that this message will be displayed after you have called the method and its mostly used when sending the user to another page or refreshing the page after a posted form. To display this message on top of the main content under the main menu (depending on the template being used) you can do the following:
Phpfox::addMessage('Your blog has been added.');

This will then display the message:


Your blog has been added.

Note when redirecting users which you will learn how to do shortly has a parameter which you can pass a messages with thus leaving this method obsolete in many instances.
setCookie()

Cookies are really yummy and you can set them using this method. We allow 3 parameters with the first being the name of the cookie. The 2nd being the value of the cookie and the 3rd being how long the cookie should last. Note that 0 will expire after the user closes their browser and -1 will destroy the cookie. Here is an example of how to set a cookie:
Phpfox::setCookie('cookie_name', 'cookie_value');

From the example you can see we did not add a 3rd parameter as it defaults to 0.
getCookie()

In order to return the value of the cookie we us the method getCookie(). There is only one parameter and that is the first one which is used to identify the cookies name. Here is a working exmaple:
Phpfox::getCookie('cookie_name');

Cache

Class file: /include/library/phpfox/cache/cache.class.php With phpFox2 we have focused greatly on caching a lot of the data that is being viewed but not constantly updated or the need to display live information not required. If we come across data that has the possibility of being cached we make sure we cache it and follow up on updating the cached data once it needs to be updated. We provide support for 3 different methods of caching. File, Memcache and APC. We recommend using both APC or Memcache, however flat file caching is the commonly used method as the other 2 requires configuring your server. Most of the data we cache is what we pull out from the database. Since pulling loads of information from the database can be very costly with memory consumption and overall performance of the script there are methods to help with this and caching is one of the most effective methods. The supported storage engines can be found in the folder:
/include/library/phpfox/cache/storage/

By default we automatically use the file storage engine. If using the file storage engine cached files are stored in flat files in the folder:
/file/cache/

The cache folder needs to be writable, however the file folder does not. All cached files have a check at the beginning of the file to make sure no direct access can be done. During development you can see the file names of the cached files, however during a live environment all cached files have an MD5() hashed name with a unique salt. You can cache Strings, Integers, Boolean values or Arrays. You cannot store objects, sessions or sensitive information. Here is a working example of how to cache data being pulled from the database:
$oCache = Phpfox::getLib('phpfox.cache'); $sCachedId = $oCache->set('module'); if (!($aModules = $oCache->get($sCachedId))) { $aModules = Phpfox::getLib('phpfox.database')->select('m.module_id, m.name') ->from(Phpfox::getT('module'), 'm')->join(Phpfox::getT('product'), 'p', 'm.product_id = p.product_id AND p.is_active = 1')->where('m.is_active = 1') ->order('m.name')->execute('getRows'); $oCache->save($sCachedId, $aModules); }

Now lets dissect the above example line by line. First we call the cache object.
$oCache = Phpfox::getLib('phpfox.cache');

Next we set the unique name of the cache file to module. Make sure to pick a unique name that is related to what is being stored. In most cases we try to use the database table name. This method will return the unique cache id which we created the variable $sCachedId.
$sCachedId = $oCache->set('module');

Next we do an if statement to make sure not to run the query within the statement as that would be a waste of resources since the data is already cached.
if (!($aModules = $oCache->get($sCachedId)))

If the data is not cached then inside the if statement we first run an SQL query.
$aModules = Phpfox::getLib('phpfox.database')->select('m.module_id, m.name')->from(Phpfox::getT('module'), 'm') ->join(Phpfox::getT('product'), 'p', 'm.product_id = p.product_id AND p.is_active = 1')->where('m.is_active = 1')->order('m.name')->execute('getRows');

Once we ran the query we save the data using the save method. Remember for the first parameter you must add the unique cache id which you created a variable for with the set method. The 2nd parameter is what is used to store the data which in this case was an array.
$oCache->save($sCachedId, $aModules);

After the if statement, if you wanted to get all the modules which is what we pulled from the database you can use the variable $aModules which is what we created with the get method and if not cached it was then created by the SQL query. So the entire script would look like this:
$oCache = Phpfox::getLib('phpfox.cache'); $sCachedId = $oCache->set('module'); if (!($aModules = $oCache->get($sCachedId))) { $aModules = Phpfox::getLib('phpfox.database')->select('m.module_id, m.name') ->from(Phpfox::getT('module'), 'm')->join(Phpfox::getT('product'), 'p', 'm.product_id = p.product_id AND p.is_active = 1')->where('m.is_active = 1') ->order('m.name')->execute('getRows'); $oCache->save($sCachedId, $aModules); } foreach ($aModules as $aModule) { }

set()
echo $aModule['name'];

This method is used to create a unique name of the cache file. This must be a name that only uses characters from a-z and must be lowercase and cannot contain any other characters other then the underscore _. Here are some wrong and right examples: Wrong
cacheName another name new-name

Right
cache_name another_name new_name

There is only one parameter and that is the name of file we plan to cache. Here is a working example:
$sCachedId = Phpfox::getLib('phpfox.cache')->set('module');

This method will return a unique string id.


get()

Use the get method to return the cached value. If the data has not yet been cached it will return a Boolean false. This method has 2 parameters with the first being the unique cache id which you created with the method set. The 2nd is an optional parameter and that is used if you want to limit how long the cached data can be cached before it has to be re-cached. Here is a working example without a time limit:
$aModules = Phpfox::getLib('phpfox.cache')->get($sCachedId);

Make sure to create a return variable otherwise you will not be able to retrieve your cached data which we created with the variable $aModules. Here is an example using this method with a time limit:
$aModules = Phpfox::getLib('phpfox.cache')->get($sCachedId, 15);

Notice we added a 15 for the 2nd parameter which stands for 15 minutes before the data has to be re-cached.
save()

You can save your data with the save method. This method has 2 parameters with the first being the unique cache id you got from the set method and the other the data we are going to cache. You can cache Strings, Integers, Boolean and Arrays. Here is a working example:
Phpfox::getLib('phpfox.cache')->save($sCachedId, $aModules);

Your cache connection is automatically closed once you have saved the data so there is no need to use the close method.
close()

You can close the cache connection you have open which was created with the set method. This method has one parameter which needs to pass the unique cache id.
remove()

Once you have cached data without a time limit then it will not get updated until you clear the cache which can be done with the remove method. This method provides the ability to remove cached data based on the name, sub string of a name or all of the cached data.

name

Pass the name of the unique ID you created earlier with the set method. So far we have used the name module as our unique cache name so using this as an example you can remove this cache file by doing the following:
Phpfox::getLib('phpfox.cache')->remove('module'); sub string

Pass the sub string of a name you created earlier with the set method. For example we cache several types of language arrays which use the prefix language_. So instead of removing these one by one we can do a sub string remove like so:
Phpfox::getLib('phpfox.cache')->remove('language', 'substr');

From the above example you can see we now used the 2nd parameter to pass the command we need to run which in this case was substr.
all

If you want to clear all the cached data you can do the following:
Phpfox::getLib('phpfox.cache')->remove();

This will remove every cached item.


isCached()

The isCached method is used to check if a file is cached. We don't use this method in public calls mainly because the method get has this method built in to it so you would end up running it twice and thus wasting resources. However, there are times when we must use this when trying to simply check if something is cached without the need to get it and if this is the case you can do the following:
if (!Phpfox::getLib('phpfox.cache')->isCached($sCacheId)) { // Add stuff we need to cache. }

With the above example you can see we used an if statement to check if a cache was set for the ID $sCacheId. It will return a Boolean true or false with the latter identifying that the data was not cached. You can also check if a file has passed a certain time limit you have set which is handy to automatically update data without having to manually remove it. You can do this with the following method:
if (!Phpfox::getLib('phpfox.cache')->isCached($sCacheId, 15)) { // Add stuff we need to cache. }

We now added a 15 to the 2nd parameter which checks if the data is older then 15 minutes and it will return a Boolean false otherwise true if under or on the 15 minute mark.
Request

Class path: /include/library/phpfox/request/request.class.php In order to get information from users we must understand the requests they are sending and to do this we use the Request class. Requests can be sent via a URL string or a form that is being posted.

get()

To get a request either from a URL string ($_GET) or passed via a form ($_POST and/or $_FILES) you can use the get method.
URL String ($_GET)

Getting from a URL string is a little different from the conventional method that would look something like this:
index.php?action=blog&id=1

Since we have a special way of creating short URLs that are easy to read and both user friendly the above would end up as:
index.php?do=/action_blog/id_1/

This example does not use the short URL method but you can get the idea of how to pass requests via a URL string. If you have short URLs setup it would look like this:
/action_blog/id_1/

Now to get the value of action the conventional method would be:
$_GET['action']

With phpFox2 you can get the value for action by using the following method:
Phpfox::getLib('phpfox.request')->get('action')

Usually its better to create a variable for the object since you will probably use the request object more then once. Here is an example:
$oRequest = Phpfox::getLib('phpfox.request'); $oRequest->get('action'); $oRequest->get('id');

So why use our method instead of the conventional method? The reason for this is since we provide a special way to create short URLs this is the best way to pass values via a URL string and all data being passed is parsed before you get the value so its safe before you even use it. Another problem we face with the conventional method is PHPs Magic Quotes, which will be removed with PHP6. Using this method will fix this problem and if a server has this feature enabled or disabled it will function the same way. Another benefit is since we code in strict conditions you won't have to check if a request has been set thus adding more work for you to check each request with isset(). If a request has not been set because it was not passed via the URL the method will return a Boolean false and if you were to pass a 2nd parameter it will return that value as the default value. For example if we did not pass the action request via the URL and we wanted the variable that will be set for this request to have a default value you can do the following:
$sAction = Phpfox::getLib('phpfox.request')->get('action', 'blog');

You will notice in the 2nd parameter we added blog and this method will return the string value blog if the action request was not set.
Form ($_POST)

Everything that applies to the URL String method of getting requests applies to this method other then the way it is passed. Since the URL String method uses the URL to pass requests the form method passes it via a form. For example if we had the following form:
<form method="post" action="index.php"> <input type="text" name="user_name" /> <input type="text" name="email" /> </form>

If you were to post the above form the requests would then be user_name and email. This works much like using the conventional method:
echo $_POST['user_name'];

Instead we use:
echo Phpfox::getLib('phpfox.request')->get('user_name');

getInt()

The method getInt works identical to the get method other then the fact that it only allows integers to be passed. Meaning if you were to pass a string or array it will default the value to 0. If an integer was passed it will then allow it and pass it to you.
getArray()

The getArray method works somewhat identical to that of the get method other then the fact that it only allows arrays to be passed. This is something we never use with the URL String method of passing requests. This is generally used with forms. Notice that most of our forms are built around the array val[] and as a rule if building a module or add-on for phpFox2 and if you are going to be passing more the one request via a form it must use the val[] array. Here is a wrong and right example of how a form should look when passing requests: Wrong
<form method="post" action="index.php"> <input type="text" name="user_name" /> <input type="text" name="email" /> </form>

Right
<form method="post" action="index.php"> <input type="text" name="val[user_name]" /> <input type="text" name="val[email]" /> </form>

In order to get this request when being passed is to use the getArray method like so:
$aVals = Phpfox::getLib('phpfox.request')->get('val');

The variable we created $aVals will hold the array value for val[] so to echo the user_name and email you could do the following:
echo $aVals['user_name']; echo $aVals['email'];

Note that it is important to name the variable $aVals for requests you are retrieving via a posted form and if the form uses the val[] method. This way we all know that $aVals is a variable that is holding data that is being passed by a form.
getServer()

Instead of using $_SERVER[] we use our own method getServer(). The reason for this is some server environments might not have a variable you are looking for which will lead to a ugly PHP notice which we don't want to have. Plus we have added some of our own server variables. Note that you can call anything via this method if its supported by $_SERVER[] and we will return it if it exists.

Below we have added some of our own variables that might be useful.
SERVER_NAME

Instead of using the default SERVER_NAME' we check if HTTP_X_FORWARDED_HOST is set because of our Server Load Balancing feature this needs to return the correct server. If HTTP_X_FORWARDED_HOST is not set then it will return SERVER_NAME as normal.
HTTP_HOST

Instead of using the default HTTP_HOST' we check if HTTP_X_FORWARDED_HOST is set because of our Server Load Balancing feature this needs to return the correct server. If HTTP_X_FORWARDED_HOST is not set then it will return HTTP_HOST as normal.
REMOTE_ADDR

Instead of using the default REMOTE_ADDR we try to find and return the users IP, which we try to find if they are hiding behind a proxy.
PHPFOX_SERVER_ID

If you have the Server Load Balancing feature enabled this is a variable we use to find out what server the user is currently on so we can use that information for benefits of the Server Load Balancing feature.
getBrowser()

It will return the browser that the user is using but will return it in a very short format such as
Firefox 3

or IE 7

send()

At times we may need to send information to another server and at the same time retrieve the information from that server depending on the parameters we have sent. To do this you can use the send method. This method supports curl (POST & GET), file_get_contents (GET) and fsockopen (POST & GET). We use curl by default however some servers do not have support for this so we then try file_get_contents and for our last resport we use fsockopen. This method has 3 parameters with only the first being required and is the full path to where you will be sending the request. The 2nd parameter is optional and is setup to allow an array of requests you plan to pass. These will end up as POST or GET requests depending on the 3rd parameter. For the 3rd parameter which is also optional is used to identify how you want to send the request and this can be POST or GET and defaults to POST if this parameter is left empty. Here is a working example:
Phpfox::getLib('phpfox.request')->send('http://api.phpfox.com/', array('foo' => 'bar', 'var' => 'value'));

Once the other server gets this request it can for example echo the following requests that were sent:
echo $_POST['foo']; echo "<br />"; echo $_POST['var'];

This output will be:


bar value

Since we did not create a variable or echo the call from our side we won't see anything, however the other server will get the request and can handle the information without the user even knowing. If you want to return the value simply create a variable for the call like so:
$sRequest = Phpfox::getLib('phpfox.request')->send('http://api.phpfox.com/', array('foo' => 'bar', 'var' => 'value'));

You could then use the variable $sRequest and if you were to echo it you would see the following output:
bar value

Products
When you develop a module or plug-in it must belong to a product. Since you would be releasing your module or plug-in publicly it needs to be part of your unique product. Everything you create within the AdminCP requires that it be part of a product. All the menus, phrases, settings etc... that is provided with the script when you first install it is part of our product, which has the unique ID phpfox. If you add menus or settings for example to your product and once you export your product it will be included in an XML file. From there you can release this as your product for others to install on their site.

Creating a New Product


Follow the guide below to create your first product. When creating your product add the values we added as we plan on using our sample product throughout this manual. Log into your AdminCP and navigate to:
Extensions >> Product >> Create New Product

Next we need to fill out the form.

Product ID
This is the unique product ID you must create. It can have alphanumeric characters and the underscore (_). It must be lowercase. If you plan to release your product publicly it is vital you create a unique ID for your product. If you were to create a product ID that another developer has created this will cause problems for the person installing both products. In order to create a unique ID we ask that you use your company or developer alias as a prefix. For example our product name is phpfox. We also include another product phpfox_installer, which is used during the install or upgrade of the script. As you can see we used phpfox as the prefix. For this field add the following product ID:
phpfox_sample

Title
Here you can add the title of your product, which will be displayed when listing all the products. Add the following:
phpFox Sample Product

Description
Short description about your product. Add the following:
This is a sample product

Version
Current version of your product. Add the following:
1.0

Product URL
URL of your website. Usually the company URL or developers home page can be added here. Add the following:
http://www.phpfox.com/

Version Check URL


URL of where the phpFox2 should check if a new version of your product has been released. Add the following:
http://www.phpfox.com/version.php

Active
Whether the product is active or inactive. Select:
Yes

Submit the form. Now you have created your first product!

Modules
All of the features you see and interact with on phpFox2 is controlled by a module or in many cases several modules. When viewing a blog this is controlled by the blog module. There are certain features that are part of the blog module which are a part of other modules such as tagging. Tagging a blog is part of the tag module. The reason we split up features into modules is because it allows us to easily remove or build onto the core engine. Since phpFox2 is built as an engine that can support other types of products and not just a Social Networking Solution this will allow users to simply remove or uninstall features they don't like. They can also install a whole different set of modules which will allow them to start a site that is just focused on being a personal blog or web page. Since all our features are split up you will only need to install the required phrases, settings, blocks, menus etc... for that module thus saving the space in the database to not install unnecessary data. There are over 2,000 phrases at the time of this writing and if you were to load the entire language package at once this would cause a major overhead. With the usage of modules it only loads the required phrases for that module and caches it so we don't have to pull such information from the database all the time. Another advantage is it allows other developers the ability to easily build onto the package without having to modify any of the other existing modules. Especially since the product is equipped with an advanced plug-in system which allows you to virtually access all the methods provided by other modules, developers will no need to modify other modules to accomplish what they need for their specific product.

File & Folder Structure


Our modular system is based on a MVC (module-view-controller) architecture. The following is a basic module file & folder structure that is required to create a new module.
/phpfoxsample/ The name of the module. In this case its phpfoxsample. /include/ Holds components and services. /component/ Holds ajax, blocks and controllers. /ajax/ Holds ajax classes. /block/ Holds block classes. /controller/ Holds controller classes. /plguin/ Holds plugins. /service/ Holds service classes. /phpfox.xml Exported XML file that holds all the data that belongs to this module. /static/ Holds static JavaScript, images and CSS files. /image/ Holds static images. /jscript/ Holds static JavaScript. /css/ Holds static CSS. /template/ Holds templates. /default/ Default template files. /block/ Holds block HTML files. /controller/ Holds controller HTML files.

Creating a Module
To create a new module log into your AdminCP and navigate to:
Extension >> Module >> Create New Module

Fill out the form to create your first module. Follow the detailed instructions below to properly fill out the form.
Product

Select the product:


phpFox Sample Product

Module ID

Unique module ID. Similar to how you create a product ID your module ID must be unique, however your module ID can only contain lowercase characters from a-z. Adding your alias or company name is a good idea. For this example lets add:
phpfoxsample

Add to Menu

In the AdminCP your module can be added to the Modules menu in case your module has controllers that need access only for Admins. Lets select Yes as we plan on adding some sample menus.
Sub Menu

Since we selected Yes for Add to Menu we must now add these menus. As you may have noticed we provide by default 4 links for your AdminCP menu. The phrase for a link is found on the left side and the URL connection is on the right. For the first Phrase we add
Menu 1

and for the first Link we add


phpfoxsample

For the 2nd Phrase we add


Menu 2

and for the 2nd Link we add


phpfoxsample.new

Info

Short description about your module. Here we can add:


This is a sample module.

Submit the form and you should now have created your very first module. Before we go on we need to create the module file structure. Navigate to the folder /module/ on your server where you have your phpFox2 source files. Within that folder since we used the module name phpfoxsample we must create a new folder with the same name. Within the /module/ create the folder:
phpfoxsample

Within the new folder you created create the following file structure:
/include/ /include/component/ /include/component/ajax/ /include/component/block/ /include/component/controller/ /include/plugin/ /include/service/ /static/ /static/css/ /static/image/ /static/jscript/ /template/ /template/default/ /template/default/block/ /template/default/controller/

Note that your module may not require all the folders we have just created, however in order for us to show you how to use everything a module can provide its best to create all the directories. Now we can move on to creating our first controller.

Creating a Controller
Controllers are PHP classes that control all the pages on phpFox2. It is the first PHP class we connect to in order to control the page we are viewing. The controller then decides how the page should behave and what sort of information we should display.

Registering the Controller


To create your first controller log into the AdminCP and navigate to:
Extension >> Module >> Add Component

Now we need to complete the form...


Product

Select the product this block will belong to. In this case we select the sample product we created earlier:
phpFox Sample Product

Module

Select the module this block will belong to. In this case we select the sample module we created earlier:
phpfoxsample

Component

The name of the block class file without the PHP class suffix. We add:
index

This would then actually be index.class.php.


Type

The type of component this is, which in this case it is a controller. We select:
controller

URL Connection

The URL connection in order to connect to this controller. This is used when creating menus or blocks that will eventually connect with this controller. For this we add:
phpfoxsample.index

The period (.) represents a slash (/). The URL would then be: http://localhost/index.php?do=/phpfoxsample/ If we were to add: phpfoxsample.test The URL would be:
http://localhost/index.php?do=/phpfoxsample/test/

Notice in the first example and the one we are using does not include the index at the end of the URL. The reason for this is since it is the index file it defaults to it and there is no purpose in adding it.
Active

Define if the block is active or inactive. We select Yes. The form should then look Submit the form to register your controller.

Connect your Registered Controller with a PHP class


Now that we have registered the controller in the database we can create the PHP file:
/module/phpfoxsample/include/component/controller/index.class.php

Since the Component we provided in the form earlier was index the PHP class name is index.class.php. In that file, add:
<?php class Phpfoxsample_Component_Controller_Index extends Phpfox_Component { public function process() { } } ?>

If you notice the name of the class


Phpfoxsample_Component_Controller_Index

The underscore (_) signifies a folders slash (/). The class name above means it would connect to the module file:
/module/phpfoxsample/component/controller/index.class.php

Each controller must extend the class


Phpfox_Component

which is the lib we provide to parent other components. In order for us to run a controller we need to provide the public method process which is this part:
public function process()

Connect your Registered Controller with a HTML Template


Now you have your PHP controller setup, in order to display content we need an HTML file and since we separate HTML from PHP we create the template file:
/module/phpfoxsample/template/default/controller/index.html.php

In that file add:


Hello World!

Hello World!
Now lets see the file in action. Visit your site via your favorite web browser and navigate to the page:
http://localhost/index.php?do=/phpfoxsample/

Note in all our examples we use localhost as our domain name. Simply replace this with the full path to where your phpFox2 site is installed.

In the example we covered how to create an index controller, however what if we wanted to create a new page for our module. In our next example we are going to try to create the following page:
http://localhost/index.php?do=/phpfoxsample/add/

In order for us to accomplish this you need to register your controller in the AdminCP. Follow the same steps to register your new controller we went over earlier but for the Component you will need to add:
add

and for the URL Connection you need to add


phpfoxsample.add

Next we need to create the PHP file:


/module/phpfoxsample/include/component/controller/add.class.php

Within that file we add:


<?php class Phpfoxsample_Component_Controller_Add extends Phpfox_Component { public function process() { } } ?>

Next we need to create a new template:


/module/phpfoxsample/template/default/controller/add.html.php

In that file add:


We will be adding a form here...

Now lets visit the page:


http://localhost/index.php?do=/phpfoxsample/add/

Congratulations you have created your first controllers

Creating a Block
Blocks are what we use to connect modules with each other as well as add blocks on our side panel. Here you will notice the block Folder. We added this block to the mail module and added a connection to the inbox. If you wanted from your AdminCP you could move this block to any other page on your site as well as 9 other positions on the site. These are identified as the 9 blocks. In this specific case the Folder block is positioned in Block 1. Please remember that only controllers can modify the template headers since its executed before the main template. The script is designed to load the blocks within the main template and that is done after the template headers are created. By creating a plug-in hook you can connect to the template headers in several places. There are 2 ways to connect to a block. Depending on the purpose of your block and the access you have to the other modules provided you need to decide which method is best.

Connecting to a Block via HTML (controller) file


If the block you plan adding needs to be placed in a specific spot and the 9 positions we provide does not suffice then connecting your block via an HTML controller is your best solution. In order to do this lets first create the file:
/module/phpfoxsample/include/component/block/display.class.php

In that file add:


<?php class Phpfoxsample_Component_Block_Display extends Phpfox_Component { public function process() { } } ?>

Similar to our controller class the block class has the same layout. The main difference is that since this class is within the block folder the class name uses _Block_ instead of _Controller_. We extend the same phpFox2 component. Now we create the HTML file:
/module/phpfoxsample/template/default/block/display.html.php

In that file add:


<br /> <br /> Hello I am a block...

Next we need to connect this block with one of our controllers. Let us open the controller template we created earlier:
/module/phpfoxsample/template/default/controller/index.html.php

Replace the files content with the following:


Hello World! {module name='phpfoxsample.display'}

You will notice we added the following to the file:


{module name='phpfoxsample.display'}

The value phpfoxsample.display is the connection to the block. This value is translated to:
/module/phpfoxsample/include/component/block/display.class.php

Save the file and let us visit the page:


http://localhost/index.php?do=/phpfoxsample/

Now the above example might be pointless as we could have simply added:
Hello I am a block...

in the controller file instead of creating a new block. Which is very true and in most cases its always best to add everything you need in the controller, however if you are working with other modules and with the ability to disable modules from the AdminCP its important to use blocks in case the module you are creating is disabled. For example if the new block we created was actually part of the blog module and if a site Admin disables the blog module the hard coded PHP and HTML data will be processed, opposed to block the data not being processed if the module was disabled.

Connection to a Block via AdminCP


The block you are creating can be positioned using one of the 9 positions we provide this solution. In order to accomplish this we log into the AdminCP and navigate to:
Extension >> Module >> Add Component

Now we need to complete the form...


Registering the Block Product

The product this block will belong to. In this case we select our sample product:
phpFox Sample Product

Module

The module this block will be long to. In this case we select our sample module:
phpfoxsample

Component

The name of the block class file without the PHP class suffix. We add:
Panel

This would then actually be panel.class.php.


Type

The type of component this is, which in this case it is a block. We select:
block

URL Connection

This is used for controllers. However, since this is a block, we leave this blank.
Active

Defines if the block is active or inactive. We select Yes.

Submit the form and your block will then be inserted into the database. Now we need to create the actual PHP & HTML files.
Connect your Registered Block with a PHP class

Let's create the PHP file:


/module/phpfoxsample/include/component/block/panel.class.php

In that file add:


<?php class Phpfoxsample_Component_Block_Panel extends Phpfox_Component { public function process() { } } ?>

Connect your Registered Block with a HTML Template

Next lets create the HTML file:


/module/phpfoxsample/template/default/block/panel.html.php

In that file add:


I am a panel block...

Connect your Registered Block with a Controller

Log back into your AdminCP and navigate to:


CMS >> Blocks >> Add New Block

Complete the form to connect your new block with a controller...


Product

The product this block/controller connection will belong to. We select:


phpFox Sample Product

Type

The Type controls what type of a block this will be. You can connect a block to a PHP file or add HTML/PHP code on the spot. The method we are going to be working with is connecting to a PHP file so select the option:
PHP Block File

Controller

The controller we plan to connect our block with. We select:


-- phpfoxsample.index

Component

The name of the block we will be connecting with is the recently created controller. We select:
-- panel

Make sure the parent is phpfoxsample.


Placement

Controls the placement of a block. Click on View Sample Layout for all the available positions. For this example we select:
Block 1

Can Drag/Drop

Currently there are two areas that support dragging and dropping of blocks. The sites index page (when a user is logged in) and a users profile. If this option is enabled it will allow users the ability to drag and drop the block to a position they specify. For now lets select No.
Active

Controls whether this connection is active or inactive. We select Yes.


Allow Access

Used to control which user groups you would like to be able to see or interact with a block. Keep the default settings which is to allow all user groups access to this block. Submit the form to create the block/controller connection.

Since we selected that this block will be displayed on the controller phpfoxsample.index this would mean this block should show up on the page:
http://localhost/index.php?do=/phpfoxsample/

If you notice on the right hand side of the site we now have:
I am a panel block...

This is what we just recently added using this method of connecting this block with this specific controller.

Working with a Controller


Currently our new page:
http://localhost/index.php?do=/phpfoxsample/

is fairly basic and contains little information. Lets work on adding some PHP logic to the controller so it can control what is displayed on the page.

Controlling the Template (Title, Breadcrumbs and Assigning Variables)


To control aspects of the template or to assign certain variables to the template we use the method template(), which is a parent method that belongs to the extended class Phpfox_Component. Lets open the file:
/module/phpfoxsample/include/component/controller/index.class.php

Everything we will be working with now must be placed within the process method. Example:
<?php class Phpfoxsample_Component_Controller_Index extends Phpfox_Component { public function process() { // Place all your code here... } } ?>

Within the process method lets add:


$this->template()->setTitle('My Sample Title');

Visit the page:


http://localhost/index.php?do=/phpfoxsample/

you should now see My Sample Title as the title for your page.

Most templates will be designed to have breadcrumb support, in order to add a breadcrumb add the following:
$this->template()->setBreadcrumb('My Sample Breadcrumb');

Now that we have a title and a breadcrumb for the page we may need to assign certain variables to it. Lets assign our first template variable by adding the following:
$this->template()->assign(array('sSampleVariable' => 'Hello, I am an assigned variable.'));

As you can see the variable name we created for the template is sSampleVariable and it follows the same rules as if we were creating a PHP variable. In order to output the value we need to add the assigned variable to the HTML template file. Let us now open the file:
/module/phpfoxsample/template/default/controller/index.html.php

Replace the content within that file with:


Hello World! <br /> <br /> {$sSampleVariable} {module name='phpfoxsample.display'}

Notice where we added:


{$sSampleVariable}

this is how we output a variable within an HTML template. Once you saved that file visit the page:
http://localhost/index.php?do=/phpfoxsample/

To add more variables simply add them to the array within the PHP controller class. Example:
$this->template()->assign(array('sVar1' => 'Value 1', 'sVar2' => 'Value 2', 'sVar3' => 'Value 3'));

Instead of assigning the title, breadcrumb and variables on a new line and calling the template method each time we could add all the methods with one call. For example:
$this->template()->setTitle('My Sample Title')->setBreadcrumb('My Sample Breadcrumb')->assign(array( 'sSampleVariable' => 'Hello, I am an assigned variable.'));

So your entire controller should look like this now:


<?php class Phpfoxsample_Component_Controller_Index extends Phpfox_Component { public function process() { $this->template()->setTitle('My Sample Title')->setBreadcrumb('My Sample Breadcrumb') ->assign(array('sSampleVariable' => 'Hello, I am an assigned variable.')); } } ?>

Connect to a CSS File


The goal with modules is to keep as much of what we use for that module within our root module folder. Each module can connect to CSS or JavaScript files that are part of the core product or part of a specific module. Earlier we were looking into how to assign a title and variable to our controller using:
$this->template()->setTitle('My Sample Title')->setBreadcrumb('My Sample Breadcrumb')->assign(array( 'sSampleVariable' => 'Hello, I am an assigned variable.'));

To connect to a CSS file we would add setHeader. It would look like:


$this->template()->setTitle('My Sample Title')->setBreadcrumb('My Sample Breadcrumb')->setHeader(array( 'sample.css' => 'module_phpfoxsample'))->assign(array('sSampleVariable' => 'Hello, I am an assigned variable.'));

In the block of code above we added the following:


->setHeader(array()) 'sample.css' => 'module_phpfoxsample'

The array can hold multiple CSS or JavaScript calls. The name of the CSS file in this case is sample.css and it connects to module_phpfoxsample. The part module_ tells the script that this CSS file is located in a module folder and the phpfoxsample part identifies what module the file belongs too.

Once you have added that you need to create the CSS file. With our example the file should be located here:
module/phpfoxsample/static/css/phpfox/default/sample.css

The reason it is placed in the folder phpfox is because this is the default theme being used. It also uses the default folder which is the default style being used. To use images in your CSS the images must be placed in the folder:
module/phpfoxsample/static/css/phpfox/image/

Note that the folder phpfox/default is the parent location for all themes, if you are working on a custom theme you can create a new directory within the static/css folder. The setHeader() method not only connects to CSS or JavaScript it can actually add anything within the HTML headers:
<head></head>

If you were to use the following:


->setHeader(array('<!-- Add me in the header -->'))

It would end up like:


<head><!-- Add me in the header --></head>

Adding a Menu for your Controller


Lets make things easier by adding a menu for our controller so we won't have to manually write down the full path to the page each time we want to visit it. To add a menu for your controller log into your AdminCP and navigate to:
CMS >> Menus >> Add New Menu

Fill out the form to add the menu...


Menu Form Product

We will connect this menu to the product we created earlier:


phpFox Sample Product

Module

We will connect this menu to the module we created earlier:


phpfoxsample

Connection

The connection is where you would like to add your menu. There are 2 options, which is 'Menus or Modules. If you add a menu to one of the Menus it will be displayed on that specific menu on all the pages, however if you were to add a menu to a module it will only be displayed on the specific module you selected and will be a sub-menu of that module. Since this is our first menu for this module we need to add it as the parent menu so we need to select one of the Menus. Lets select:
main

Which is the main menu for the site.


URL

The URL is the link we will use in the anchor. If its an internal link you must use our method of creating links which in this case we add:
phpfoxsample

This will translate to:


http:://localhost/index.php?do=/phpfoxsample/

Menu

Here we can add the phrase for the menu and if you have more then one language package installed you can create a phrase for each for them. Here lets add:
Sample

Allow Access

Here you can select which user group can view the menu. For now lets allow all user groups so keep the default settings. Click on the link Sample and it should send you to the page:
http:://localhost/index.php?do=/phpfoxsample/

Handling Requests
Handling requests is a little different when comparing to the conventional PHP method:
$_GET['var']

Within a controller you can use the request method we provide with the extended class Phpfox_Component.

If we continue working on the current controller we have been working on and the URL string would be:
http://localhost/index.php?do=/phpfoxsample/link1/link2/link3/

and you were to add the following in your controller:


echo $this->request()->get('req1') . "<br />"; echo $this->request()->get('req2') . "<br />"; echo $this->request()->get('req3') . "<br />"; echo $this->request()->get('req4') . "<br />";

It would output:
phpfoxsample link1 link2 link3

As you can see a req is defined by the position it is in. link2 is in 3rd place so in order to get its value we would use:
$this->request()->get('req3')

In some cases you may want to use this method to pass a request so you could also use the underscore (_). If the URL string was:
http://localhost/index.php?do=/phpfoxsample/foo_bar/

In this case if we wanted to get the value for the request foo we would use:
echo $this->request()->get('foo');

this would then output:


bar

Working with a Block


Now that we have added some PHP logic to our controller we can do the same for our blocks. Unlike a controller a block cannot control the pages title or breadcrumb since it is a block and the job of a block is to simply enhance a controller. The job of the controller is to control the main aspects of a template and include any blocks.

Assigning a Variable to a Block


To start working on our block lets visit the page:
http://localhost/index.php?do=/phpfoxsample/

The part we will be working on is where we have the following phrase:


Hello I am a block...

In order to update this block lets open the PHP block file:
/module/phpfoxsample/include/component/block/display.class.php

The file should look like the following:


<?php class Phpfoxsample_Component_Block_Display extends Phpfox_Component { public function process() { } } ?>

Similar to how a controller works we add all our code within the process method. Here we can assign a variable for our block. Lets do that now by adding the following:
$this->template()->assign(array('sDisplayBlockVariable' => 'I am a variable that belongs to the Display block.'));

In order to output the variable open the file:


/module/phpfoxsample/template/default/block/display.html.php

Replace the content within that file with the following:


<br /> <br /> Hello I am a block... <br /> <br /> {$sDisplayBlockVariable}

If you visit the page:


http://localhost/index.php?do=/phpfoxsample/

Using a Template Layout


Blocks can use other template layouts, which can be found in the folder:
/theme/frontend/default/template/

Let us enhance the block we were currently working on. Open the file:
/module/phpfoxsample/include/component/block/display.class.php

At this time your file should look like this:


<?php class Phpfoxsample_Component_Block_Display extends Phpfox_Component { public function process() { $this->template()->assign(array('sDisplayBlockVariable' => 'I am a variable that belongs to the Display block.')); } } ?>

Replace the content of that file with the following:


<?php class Phpfoxsample_Component_Block_Display extends Phpfox_Component { public function process() { $this->template()->assign(array('sDisplayBlockVariable' => 'I am a variable that belongs to the Display block.', 'sHeader' => 'Display Block','aFooter' => array('Add New Sample' => $this->url()->makeUrl('phpfoxsample.add')),)); return 'content'; } } ?>

Save the file and let us first view the page:


http://localhost/index.php?do=/phpfoxsample/

As you can see the simple block we created earlier now has a block header and footer menu. The idea of blocks is to output the actual data that will be displayed within the outer block template, this way all blocks in the future will look the same depending on the theme the user is browsing the site with. In this case in the PHP block file we added the following to the file:
return 'content';

All blocks can return a string identifying what outer template they should use. In this case we used content, which when we add the template suffix would be content.html.php. When you return a string the script will try to find the template and use that as the outer shell for your block. So the full path to this template would be:
/theme/frontend/default/template/content.html.php

This specific template requires certain variables to be defined in order to work. You must define the sHeader variable with your PHP block class otherwise it simply will not display the block template. We define this variable by assigning it via the template assign method, which is the same method we used earlier to define methods for our templates. In your PHP block class you will find:
'sHeader' => 'Display Block',

We used this to define the block title. Your blocks can also have a footer menu, this does not need to be defined however if you would like a menu you will need to assign the variable aFooter and the value must be an array. Checking back in our PHP block class you will find:
'aFooter' => array('Add New Sample' => $this->url()->makeUrl('phpfoxsample.add')),

The key within the array is the phrase to display and the value is the URL path for the link. To add more links simply add to the array. Example:
'aFooter' => array('Link 1' => $this->url()->makeUrl('phpfoxsample.link1'), 'Link 2' => $this->url() ->makeUrl('phpfoxsample.link2'), 'Link 3' => $this->url()->makeUrl('phpfoxsample.link3')),

Now that we have updated this specific block to use a template, lets update our other block we created earlier. If you visit the page we have been working on you will find to the right we have:
I am a panel block...

To edit this block open the file:


/module/phpfoxsample/include/component/block/panel.class.php

The file should look like:


<?php class Phpfoxsample_Component_Block_Panel extends Phpfox_Component { public function process() { } } ?>

Lets use the layout block.html.php so in order to accomplish this it works similar to how our earlier content.html.php layout worked. We replace the entire block file with:
<?php class Phpfoxsample_Component_Block_Panel extends Phpfox_Component { public function process() { $this->template()->assign(array('sHeader' => 'Panel Block', 'aFooter' => array('Panel Link' => $this->url()->makeUrl('phpfoxsample.add')),)); return 'block'; } } ?>

Save the file and visit the page:


http://localhost/index.php?do=/phpfoxsample/

Similar to the block we updated earlier, here we need to define the header variable which is sHeader. We can also define footer links which is aFooter, however this is not necessary.
Moving Blocks

To get a greater understanding how blocks work and how easy they can be to connect with other modules lets try to move the block we just created to another page and then move it back. In order to do this log into the AdminCP and navigate to:
CMS >> Blocks >> Manage Blocks

Look for where we have: phpfoxsample::panel From the Action drop down for that specific block select Edit. Now lets update the form. For the Controller change it from:
-- phpfoxsample.index

to
-- mail.compose

Save the form and visit the page: http://localhost/index.php?do=/mail/compose/ As you can see it is very easy to move blocks around to any other page on your site, you can even change the position of the block to the 9 available positions. Let us move the block back to the correct section. So let us go back to the form and for the Controller set it to:
-- phpfoxsample.index

Save the form and you should find your block back on the page:
http://localhost/index.php?do=/phpfoxsample/

Module Services

Introduction
A phpFox Service is what we use as a model for our module. We use services to interact with database tables, cache modular data or extend the PHP logic for a module. It is important not to use a component (controllers or blocks) for such services as this should all be handled by your module services. To use a module service you need to call it from a module controller, which we went over earlier by creating our first module. Using that same module as an example you should have the following page setup already: http://localhost/index.php?do=/phpfoxsample/

Creating a Module Service


Lets create our first service class by creating the PHP file:
/module/phpfoxsample/include/service/phpfoxsample.class.php

In that new file paste the following:


<?php class Phpfoxsample_Service_Phpfoxsample extends Phpfox_Service { public function getUsers($iTotal) { return $this->database()->select('u.full_name')->from(Phpfox::getT('user'), 'u')->limit($iTotal)->execute('getRows'); } } ?>

You will notice the name of our class is Phpfoxsample_Service_Phpfoxsample, similar to how other classes work we replace the underscore with a folder slash. So the file location would be:
/module/phpfoxsample/include/service/phpfoxsample.class.php

All service classes must extend our Phpfox_Service class. With this class we create a method getUsers. This method simply gets a specific number of members from the user database table. The number is specified by the variable $iTotal which is defined when calling this specific method. In order for us to use this class and call this method we need to do this from a module controller. Which we already have and created when we were looking into creating controllers. Lets open the file:
/module/phpfoxsample/include/component/controller/index.class.php

Replace the files content with the following:


<?php class Phpfoxsample_Component_Controller_Index extends Phpfox_Component { public function process() { $this->template()->setTitle('My Sample Title')->setBreadcrumb('My Sample Breadcrumb') ->assign(array('sSampleVariable' => 'Hello, I am an assigned variable.', 'aUsers' => Phpfox::getService('phpfoxsample')>getUsers(10))); } } ?>

We added the following to that file:


'aUsers' => Phpfox::getService('phpfoxsample')->getUsers(10)

We defined the template variable aUsers since we want to display the users in the actual HTML template. We initiated the service using the static method getService:
Phpfox::getService('phpfoxsample')

This will return the classes object and the above call would be similar to the conventional method:
new Phpfoxsample_Component_Controller_Index();

Since we only plan to use this class to call one method we do the following:
Phpfox::getService('phpfoxsample')->getUsers(10)

however, if you plan to use the object more then once or within a loop its best to do the following:
$oServicePhpfoxsample = Phpfox::getService('phpfoxsample'); $oServicePhpfoxsample ->getUsers(10);

Now that we have assigned the variable lets open the file:
/module/phpfoxsample/template/default/controller/index.html.php

Replace the entire files content with:


Hello World! <br /> <br /> {$sSampleVariable} {module name='phpfoxsample.display'} <br /> <br /> Members: <ul class="p_4"> {foreach from=$aUsers item=aUser} <li>{$aUser.full_name}</li> {/foreach} </ul>

Save the file and visit the page:


http://localhost/index.php?do=/phpfoxsample/

Near the bottom of that page you should find:


Members:

Plugins
We use plugins to interact with the core product, modules or other 3rd party plugins without the need to modify the source code as it simply plugs into 3rd party code. We have gone over how to create a Product and a Module. In order to work with a module you need to create new PHP and HTML source files, however with plugins you can do this from the comfort of the AdminCP. We also provide the method to create plug-ins via a PHP source file as it is easier to develop when using an IDE compared to working with code in the AdminCP. In the examples we will be using the product we created earlier as well as the module we worked on.

Creating a Plugin via the AdminCP Log into your AdminCP and
navigate to: Extensions >> Plugin >> Create New Plugin
Complete the Form Product

We will use the product we created earlier:


phpFox Sample Product

Module

We will use the module we worked on earlier:


phpfoxsample

Title

We need to identify the plugin and here is where you can best describe what your plugin does. Here we can add:
User Space Usage

Hook

The drop down provides a long list hooks provided by the script and is where you can plugin your code. In order to find where you want to plug your code in you will need to look further into the source code and find the right spot to place for your plugin depending what it is you would like to accomplish. In this example we will be adding a plugin to display the total space usage for a user on their profile in the Basic Information block. This block can be found on a users profile and is located on the side panel. For the hook find and select:
profile.template_block_info

We selected this hook as it can be found in the file:


/module/profile/template/default/block/info.html.php

look for:
{plugin call='profile.template_block_info'}

This is where we plan on plugging in our code.


Active

This identifies if a plugin is active or inactive. Lets set this to Yes.


PHP Code

Here is where you can add your PHP code. You do not need to add the PHP open and closing tags. Let us add the following:
$aUser = $this->getVar('aUser'); echo '<div class="p_2">'; echo 'Space Usage: ' . Phpfox::getLib('phpfox.file')->filesize($aUser['space_total']); echo '</div>';

Submit the form and visit a members profile. Look for the Basic Information block and you should find at the bottom:
Space Usage

As you can see modifying the core product and other 3rd party products won't be has hard as it has been in the past since it does not require overwriting or breaking source code.

Exporting your Product


Congratulations! You now have developed a module and a plug-in for phpFox2. Now its time to export your product so it can be released to others. In order to do this you will need to log into your AdminCP and navigate to:
Extension >> Product >> Import/Export Products

From the Product drop down select:


phpFox Sample Product

and click on the Download button.

You should then be prompted to download an XML file. The contents of that file should be similar to this:
<product> <data> </data> <product_id>superpoke</product_id> <is_core>0</is_core> <title>Superpoke</title> <description>Superpoke plug-in for phpFox v2</description> <version>0.3</version> <is_active>1</is_active> <url>http://www.phpfox.com/</url> <url_version_check></url_version_check> <installs> <install> <version>0.2</version> <install_code> <![CDATA[$this->database()->query("CREATE TABLE IF NOT EXISTS `" . Phpfox::getT('superpoke') . "` ( `poke_id` `type_id` `user_id` `profile_id` `time_stamp` int(10) int(10) int(10) int(10) int(10) UNSIGNED UNSIGNED UNSIGNED UNSIGNED UNSIGNED NOT NOT NOT NOT NOT NULL AUTO_INCREMENT, NULL DEFAULT '0', NULL, NULL, NULL, PRIMARY KEY (`poke_id`),

KEY `user_id` (`user_id`,`profile_id`), KEY `user_id_2` (`user_id`)) ENGINE=MyISAM AUTO_INCREMENT=1"); $this->database()->query("CREATE TABLE IF NOT EXISTS `" . Phpfox::getT('superpoke_type') . "` ( `type_id` int(10) `name` varchar(50) `name_post` varchar(50) `total_usage` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, NOT NULL, NOT NULL, UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`type_id`)

) ENGINE=MyISAM AUTO_INCREMENT=1;");]]> </install_code> <uninstall_code> </uninstall_code> </install> </installs> <modules> <module_id>superpoke</module_id> </modules> </product>

This XML file holds all the information phpFox2 needs to install your product on another site. Since we developed a module we will need to include the module folder in the package and this can't be done via the AdminCP for security reasons so you will need to simply navigate to the folder:
/module/

Look for your module folder, which in this case is phpfoxsample. ZIP the folder and the recently downloaded XML file into one ZIP package and your product is ready to be released to the world. The goal when creating products for phpFox2 is to be able to create robust features without the need to modify core or 3rd party source files. This will allow clients that use our core product and an easier time to upgrade to newer phpFox versions in the future.

Keeping your Clients UpToDate


If you plan on releasing your product(s) to others and want them to know when a new version of your product is completed you can connect it to a public server and check what is the latest version available. When you first created your product you were able to define where we should check for your products version number. In our example here we used
http://www.phpfox.com/version.php

In order for us to correctly parse and identify your current version of your live product you must using the following XML code:
<phpfox> <product_version>1.0.1</product_version> </phpfox>

Das könnte Ihnen auch gefallen