Wednesday 6 February 2013

Advanced php

Apache Module mod_cache

The following content will help you guys on how to enable caching on specific folders on your server.

There is module called cache_module in apache modules, first we need to enable it.

mod_cache implements an RFC 2616 compliant HTTP content cache that can be used to cache either local or proxied content. mod_cache requires the services of one or more storage management modules. Two storage management modules are included in the base Apache distribution:

mod_disk_cache
implements a disk based storage manager.
mod_mem_cache
implements a memory based storage manager. mod_mem_cache can be configured to operate in two modes: caching open file descriptors or caching objects in heap storage. mod_mem_cache can be used to cache locally generated content or to cache backend server content for mod_proxy when configured using ProxyPass (aka reverse proxy) 

Sample httpd.conf

#
# Sample Cache Configuration
#
LoadModule cache_module modules/mod_cache.so


#LoadModule disk_cache_module modules/mod_disk_cache.so
# If you want to use mod_disk_cache instead of mod_mem_cache,
# uncomment the line above and comment out the LoadModule line below.

CacheRoot c:/cacheroot
CacheEnable disk /
CacheDirLevels 5
CacheDirLength 3


LoadModule mem_cache_module modules/mod_mem_cache.so

CacheEnable mem /
MCacheSize 4096
MCacheMaxObjectCount 100
MCacheMinObjectSize 1
MCacheMaxObjectSize 2048


# When acting as a proxy, don't cache the list of security updates
CacheDisable http://security.update.server/update-list/



How to write Robots.txt file

In a text editor, open a file named robots.txt. Note that the name must be all lower case, even if your Web pages are hosted on a Windows Web server. You'll need to save this file to the root of your Web server. For example:

http://www.mydomain.com/robots.txt

The format of the robots.txt file is

User-agent: robot

Disallow: directories/folders or files #Google will not crawl the named files / folders here

You can use wildcards to indicate all robots, or all robots of a certain type. For example:
To specify all robots:

User-agent: * 

To specify all robots that start with the letter A:

User-agent: A*

The disallow lines can specify files or directories:
Don't allow robots to view any files on the site:

Disallow: /

Don't allow robots to view the index.html file

Disallow: /index.html

If you leave the Disallow blank, that means that all files can be retrieved, for example, you might want the Googlebot to see everything on your site:

User-agent: Googlebot

Disallow:

If you disallow a directory, then all files below it will be disallowed as well.

Disallow: /norobots/

You can also use multiple Disallows for one User-agent, to deny access to multiple areas:

User-agent: *

Disallow: /cgi-bin/

Disallow: /images/

You can include comments in your robots.txt file, by putting a pound-sign (#) at the front of the line to be commented:

# Allow Googlebot anywhere

User-agent: Googlebot

Disallow:

Robots follow the rules in order. For example, if you set googlebot specifically in one of your first directives, it will then ignore a directive lower down that is set to a wildcard.

# Allow Googlebot anywhere

User-agent: Googlebot

Disallow: 

# Allow no other bots on the site

User-agent: *

Disallow: /


Create virtual hosts in PHP with apache

Virtual hosts are mainly used to run different applications with in a single Apache server by creating different port numbers in Apache configuration.

The default port number that Apache will run is 80.

Now we will see the steps to create virtual hosts in both Windows and Linux.
Both the cases assume that we have installed wamp on D:/wamp
Creating virtual hosts Windows box 

1)Go to the directory: D:\wamp\bin\apache\Apache2.2.17\conf
2) There you will find apache.conf file, open it in any text editor.
3) In that file you will be having 2 lines
# Virtual hosts
#Include conf/extra/httpd-vhosts.conf

Now you need to include the virtual hosts file as like below.
# Virtual hosts
Include conf/extra/httpd-vhosts.conf

4) Now Open file conf/extra/httpd-vhosts.conf in any text editor.
In that file you will be having lines like below
#
# Use name-based virtual hosting.
#
NameVirtualHost *:80

So name your virtual host below these line like below
#
# Use name-based virtual hosting.
#
NameVirtualHost *:80
NameVirtualHost *:8001

5) Then you need to set up directory of your new site. By default for virtual host 80 the following code will work, the code block follows below.

ServerAdmin webmaster@dummy-host.example.com
DocumentRoot "/httpd-2.2-x64/docs/dummy-host.example.com"
ServerName dummy-host.example.com
ServerAlias www.dummy-host.example.com
ErrorLog "logs/dummy-host.example.com-error.log"
CustomLog "logs/dummy-host.example.com-access.log" common


Then we need to copy and paste the same block and make changes like below.

ServerAdmin webmaster@dummy-host.example.com
DocumentRoot "/httpd-2.2-x64/docs/dummy-host.example.com"
ServerName dummy-host.example.com
ServerAlias www.dummy-host.example.com
ErrorLog "logs/dummy-host.example.com-error.log"
CustomLog "logs/dummy-host.example.com-access.log" common


ServerAdmin webmaster@dummy-host.example.com
DocumentRoot "D:/wamp/www/"
ServerName dummy-host.example.com
ServerAlias www.dummy-host.example.com
ErrorLog "logs/mydomain.com-error.log"
CustomLog "logs/mydomain.com-access.log" common


This process will finishes the creating of your first virtual host.

6) Restart your Apache server and try it in your browser like http://localhost:8001/

Creating virtual hosts Linux box


Writing a .htaccess file

In this post i am going to give examples of commonly used examples of .htaccess basics.

1) HTTP Authentication for Password protected directories 
AuthType Basic
AuthName "Password Required"
AuthUserFile /www/passwords/password.file
AuthGroupFile /www/passwords/group.file
Require Group admins

2) Change default directory page ( index page ) using htaccess
This directory indexing is useful when ever you wish to change the order of parsing file. example if you want to change the default parsing orders to index.php not index.html,, you can keep the following code in your .htaccess file.

DirectoryIndex index.php index.html index.shtml index.txt

If tou browser your site http://www.yourdomain.com/, then it will search for index.php, if index.php not exists then it will searches for index.html. 

Note: If no files(index.php index.html index.shtml index.txt) are existed then it will display the directory and its contents. In order to prevent this we can write a code as below.

DirectoryIndex index.html index.txt /cgi-bin/index.pl 

Would cause the CGI script /cgi-bin/index.pl to be executed if neither index.html or index.txt existed in a directory.

3) Allow/Deny Directory Browsing

When directory browsing is on, people accessing a URL from your site with no index page or no pages at all, will see a list of files and folders. To prevent such directory access, just place the following line in your .htaccess file.

IndexIgnore */*
OR
Options -Indexes

Many hosting companies, by default deny directory browsing and having said that, just in case you need to enable directory browsing, place the following line in your .htaccess file.

Options +Indexes

4) Redirect visitors from one page or directory to another

It’s quite simple. Look at the example lines below and place similar lines in your .htaccess file of the root folder and it will do the rest. [Remember to use permanent keyword in the line to tell the search engines that the old link has moved to the new link]. You can also setup multiple redirects using htaccess.

Syntax: Redirect permanent [old directory/file name][space][new directory/file name]

Redirect permanent /olddirectory /newdirectory
Redirect permanent /olddirectory /somedirectory/newdirectory
Redirect permanent /oldhtmlfile.htm /newhtmlfile.htm
Redirect permanent /oldhtmlfile.htm http://your-domain.com/newhtmlfile.htm

All the above lines are valid. Just remember to replace the file/directory names with actual ones.

301 Redirection
This example will redirects you from non-www to www
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\.mydomain\.com$
RewriteRule (.*) http://www.mydomain/$1 [R=301,L]

5) Change the default index page of a directory or site

Almost every hosting company will have index.htm, index.html, index.php, index.asp, default.asp, default.html as the default index page names in their web server settings. So, in case your site or directory does not has a file name which matches a name from the list above, chances are that your visitors will either see a list of all the files and folders [through directory browsing] or will not see anything at all. To change the default index page’s name for a directory or the site, place the following line in the .htaccess file of the root folder or the particular directory for which you want to change the index page’s name.

DirectoryIndex homepage.htm
DirectoryIndex somepage.htm

To have more names, put a space between file names and it will take into considerations all those file names as possible index page names. Which means, if it finds a filename matching a list of names you supplied [in the given order] in .htaccess, then it will open that page as the index page for the directory. The below line, with multiple names, is also a valid usage:

DirectoryIndex homapage.html somepage.html myindexpage.html anything.html

6) Preventing hot linking of images from your website

If your website contains images which people from other websites are linking to and you get charged for the extra bandwidth, then placing the following lines will prevent any such image hot linking. Most of the hosting companies provide this feature in their control panel itself, such as CPanel. This trick requires mod_rewrite engine to be on in Apache on your web server.

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www\.)?your-domain.com/.*$ [NC]
RewriteRule .(gif|jpg)$ – [F]

In the above code, replace [your-domain] with your actual domain name [without www], and instead of (www.\), use your actual subdomain name (sub-domain.\)

7) Prevent access to your .htaccess file (.htaccess security)

To prevent visitors from viewing your .htaccess file, place the following lines in your file. Of course, by default most Apache installations will not show .htaccess file but just in case.

<files .htaccess>
order allow,deny
deny from all
</Files>

8) Custom Error Pages

The .htaccess file will cover custom error pages. These will allow you to have your own, personal error pages (for example when a file is not found) instead of using your host's error pages or having no page. This will make your site seem much more professional in the unlikely event of an error. It will also allow you to create scripts to notify you if there is an error (for example I use a PHP script on Free Webmaster Help to automatically e-mail me when a page is not found).

You can use custom error pages for any error as long as you know its number (like 404 for page not found) by adding the following to your .htaccess file:

ErrorDocument errornumber /file.html

For example if I had the file notfound.html in the root direct
ory of my site and I wanted to use it for a 404 error I would use:

ErrorDocument 404 /notfound.html

If the file is not in the root directory of your site, you just need to put the path to it:

ErrorDocument 500 /errorpages/500.html

These are some of the most common errors:

401 - Authorization Required
400 - Bad request
403 - Forbidden
500 - Internal Server Error
404 - Wrong page

Then, all you need to do is to create a file to display when the error happens and upload it and the .htaccess file.

9) Deny/Allow Certian IP Addresses

In some situations, you may want to only allow people with specific IP addresses to access your site (for example, only allowing people using a particular ISP to get into a certian directory) or you may want to ban certian IP addresses (for example, keeping disruptive memembers out of your message boards). Of course, this will only work if you know the IP addresses you want to ban and, as most people on the internet now have a dynamic IP address, so this is not always the best way to limit usage.

You can block an IP address by using:

deny from 000.000.000.000

where 000.000.000.000 is the IP address. If you only specify 1 or 2 of the groups of numbers, you will block a whole range.

You can allow an IP address by using:

allow from 000.000.000.000

where 000.000.000.000 is the IP address. If you only specify 1 or 2 of the groups of numbers, you will allow a whole range.

If you want to deny everyone from accessing a directory, you can use:

deny from all

but this will still allow scripts to use the files in the directory.





------ Contineous............


Creating and Editing crontab Files

cron is a unix, solaris utility that allows tasks to be automatically run in the background at regular intervals by the cron daemon. These tasks are often termed as cron jobs in unix , solaris. Crontab (CRON TABle) is a file which contains the schedule of cron entries to be run and at specified times.

The following example shows how to determine if an editor has been defined, and how to set up vi as the default.

$ which $EDITOR
$ EDITOR=vi
$ export EDITOR 


When you create a crontab file, it is automatically placed in the/var/spool/cron/crontabs directory and is given your user name. You can create or edit a crontab file for another user, or root, if you have superuser privileges. 

This document covers following aspects of Unix cron jobs
1. Crontab Restrictions
2. Crontab Commands
3. Crontab file – syntax
4. Crontab Example
5. Crontab Environment
6. Disable Email
7. Generate log file for crontab activity

1. Crontab Restrictions
You can execute crontab if your name appears in the file /usr/lib/cron/cron.allow. If that file does not exist, you can use
crontab if your name does not appear in the file /usr/lib/cron/cron.deny.
If only cron.deny exists and is empty, all users can use crontab. If neither file exists, only the root user can use crontab. The allow/deny files consist of one user name per line.

2. Crontab Commands

export EDITOR=vi ;to specify a editor to open crontab file.

crontab -e Edit your crontab file, or create one if it doesn’t already exist.
crontab -l Display your crontab file.
crontab -r Remove your crontab file.
crontab -v Display the last time you edited your crontab file. (This option is only available on a few systems.)

3. Crontab file
Crontab syntax :
A crontab file has five fields for specifying day , date and time followed by the command to be run at that interval.

* * * * * command to be executed
- - - - -
| | | | |
| | | | +----- day of week (0 - 6) (Sunday=0)
| | | +------- month (1 - 12)
| | +--------- day of month (1 - 31)
| +----------- hour (0 - 23)
+------------- min (0 - 59)

* in the value field above means all legal values as in braces for that column.
The value column can have a * or a list of elements separated by commas. An element is either a number in the ranges shown above or two numbers in the range separated by a hyphen (meaning an inclusive range).
Notes
A. ) Repeat pattern like /2 for every 2 minutes or /10 for every 10 minutes is not supported by all operating systems. If you try to use it and crontab complains it is probably not supported.

B.) The specification of days can be made in two fields: month day and weekday. If both are specified in an entry, they are cumulative meaning both of the entries will get executed .

4. Crontab Example
A line in crontab file like below removes the tmp files from /home/someuser/tmp each day at 6:30 PM.

30 18 * * * rm /home/someuser/tmp/*

Changing the parameter values as below will cause this command to run at different time schedule below :
min hour day/month month day/week Execution time
30 0 1 1,6,12 * – 00:30 Hrs on 1st of Jan, June & Dec.
0 20 * 10 1-5 –8.00 PM every weekday (Mon-Fri) only in Oct.
0 0 1,10,15 * * – midnight on 1st ,10th & 15th of month
5,10 0 10 * 1 – At 12.05,12.10 every Monday & on 10th of every month
:

Note : If you inadvertently enter the crontab command with no argument(s), do not attempt to get out with Control-d. This removes all entries in your crontab file. Instead, exit with Control-c.

5. Crontab Environment
cron invokes the command from the user’s HOME directory with the shell, (/usr/bin/sh).
cron supplies a default environment for every shell, defining:
HOME=user’s-home-directory
LOGNAME=user’s-login-id
PATH=/usr/bin:/usr/sbin:.
SHELL=/usr/bin/sh

Users who desire to have their .profile executed must explicitly do so in the crontab entry or in a script called by the entry.

6. Disable Email
By default cron jobs sends a email to the user account executing the cronjob. If this is not needed put the following command At the end of the cron job line .

>/dev/null 2>&1

7. Generate log file
To collect the cron execution execution log in a file :

30 18 * * * rm /home/someuser/tmp/* > /home/someuser/cronlogs/clean_tmp_dir.log


How to enable mod_rewrite in apache web server

How to enable mod_rewrite in apache web server

In Order to enable the clean urls and .htaccess, you need to enable the mod_rewrite in Apache server.

Before enabling clean urls, the Druapl Admin will look like below.
http://www.sriniraj.com/?q=admin

After Enabling, the url looks like below
http://www.sriniraj.com/admin
In Linux
If you want to enable mod_rewrite, then open your terminal and type the following command

sudo a2enmod rewrite


After execution of above command, then restart your apache server.

Use the following command to restart your apache web server.

sudo /etc/init.d/apache2 restart


You can see what modules are loaded by using the following command
sudo apache2ctl -l / sudo apache2ctl -M

In Windows
1) Click on WAMP server which is in system tray.
2) In that drag your mouse to Apache-> Apache module
3) It will give you list of modules of Apache, click on rewrite_module and restart your Apache server.


How to take MySQL database dumps in Windows

If we want to take mysqldump in windows, then use the following syntax.

path to mysqldump execute file -h hostname -u username -ppassword > path to .sql file
For example if you installed WAMP/XAAMP server in E: drive then the mysqldump syntax will follows like below.

E:\wamp\bin\mysql\mysql5.1.36\bin>mysqldump -u root news_admin > E:/anji.sql

To restore mysql dump in the windows then use the following syntax.
Example:
E:\wamp\bin\mysql\mysql5.1.36\bin>mysql -u root newsd < E:/anji.sql


How to add Indexing to a Table in MySQL

Indexing a MySQL table is needed for fast search.
The below syntax is used to add index to a mysql table. Here i am assuming the table name is `product` and the `product_id` is a primary key in that mysql table.

alter table `product` add index `product_id`

How to change the autoincrement value in a mysql table

Initially when we create a new table with auto increment filed, the values start from 1. After some time if we want to set auto increment value to a different value, then we will use the following command in mysql.

alter table members  Engine=MyISAM checksum=1 auto_increment=1000 comment='' delay_key_write=1 row_format=dynamic charset=latin1 collate=latin1_swedish_ci


How to take MySQL database dumps in Terminal.

Use the following command to do MySQL dump of your database:
 mysqldump -h <hostname> -u <database user name> -p<Database Password> <database name> > <path><filename.sql>

How to restore MySQL dump into our current Database: 
mysql -h <hostname> -u<database  user name> -p<Database password> <Database Name> < <path/filename.sql>

How to take backup of only the table structure of MySQL Database:
 mysqldump -h <hostname> -u <database user name> -p<Database Password> <database name> --no-data > <path><filename.sql>

How to take backup of selected MySQL tables: mysqldump -h <hostname> -u <database user name> -p<Database Password> <database name> <tablename1> <tablename2> ..... <tablenameN>  > <path><filename.sql>

Sunday 27 January 2013

mysql joins


I thought Ligaya Turmelle's post on SQL joins was a great primer for novice developers. Since SQL joins appear to be set-based, the use ofVenn diagrams to explain them seems, at first blush, to be a natural fit. However, like the commenters to her post, I found that the Venn diagrams didn't quite match the SQL join syntax reality in my testing.
I love the concept, though, so let's see if we can make it work. Assume we have the following two tables. Table A is on the left, and Table B is on the right. We'll populate them with four records each.
id name       id  name
-- ----       --  ----
1  Pirate     1   Rutabaga
2  Monkey     2   Pirate
3  Ninja      3   Darth Vader
4  Spaghetti  4   Ninja
Let's join these tables by the name field in a few different ways and see if we can get a conceptual match to those nifty Venn diagrams.
SELECT * FROM TableA
INNER JOIN TableB
ON TableA.name = TableB.name

id  name       id   name
--  ----       --   ----
1   Pirate     2    Pirate
3   Ninja      4    Ninja
Inner join produces only the set of records that match in both Table A and Table B.

Venn diagram of SQL inner join
SELECT * FROM TableA
FULL OUTER JOIN TableB
ON TableA.name = TableB.name

id    name       id    name
--    ----       --    ----
1     Pirate     2     Pirate
2     Monkey     null  null
3     Ninja      4     Ninja
4     Spaghetti  null  null
null  null       1     Rutabaga       
null  null       3     Darth Vader
Full outer join produces the set of all records in Table A and Table B, with matching records from both sides where available. If there is no match, the missing side will contain null.
Venn diagram of SQL cartesian join

SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name

id  name       id    name
--  ----       --    ----
1   Pirate     2     Pirate
2   Monkey     null  null
3   Ninja      4     Ninja
4   Spaghetti  null  null
Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null.
Venn diagram of SQL left join
SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name
WHERE TableB.id IS null

id  name       id     name
--  ----       --     ----
2   Monkey     null   null
4   Spaghetti  null   null
To produce the set of records only in Table A, but not in Table B, we perform the same left outer join, thenexclude the records we don't want from the right side via a where clause.
join-left-outer.png
SELECT * FROM TableA
FULL OUTER JOIN TableB
ON TableA.name = TableB.name
WHERE TableA.id IS null 
OR TableB.id IS null

id    name       id    name
--    ----       --    ----
2     Monkey     null  null
4     Spaghetti  null  null
null  null       1     Rutabaga
null  null       3     Darth Vader
To produce the set of records unique to Table A and Table B, we perform the same full outer join, thenexclude the records we don't want from both sides via a where clause.
join-outer.png
There's also a cartesian product or cross join, which as far as I can tell, can't be expressed as a Venn diagram:
SELECT * FROM TableA
CROSS JOIN TableB

Blank Screen with CodeIgniter


Things to check:
  • Inside config.php, make sure your $config['base_url'] is set properly
  • Were you able to copy your .htaccess as well?
  • Do you have the same PHP versions in both machines? If your answer is yes, i'll ask you again: Are you sure?
  • What is the value of your $db['default']['hostname']?
  • Do you have the same database setup in your local and production server? There could be differences with the hostname, username, password and database name
Other things you can do:
  • Set $db['default']['db_debug'] to TRUE
  • Deploy a fresh CodeIgniter installation in your production server and check if you can see something
  • If you still see a blank page, deploy a single PHP file with text in it and tell us what you see

Codeigniter displays a blank page instead of error messages


Since none of the solutions seem to be working for you so far, try this one:
ini_set('display_errors', 1);
This explicitly tells PHP to display the errors. Some environments can have this disabled by default.
This is what my environment settings look like in index.php:
/*
 *---------------------------------------------------------------
 * APPLICATION ENVIRONMENT
 *---------------------------------------------------------------
 */
define('ENVIRONMENT', 'development');
/*
 *---------------------------------------------------------------
 * ERROR REPORTING
 *---------------------------------------------------------------
 */
if (defined('ENVIRONMENT'))
{
    switch (ENVIRONMENT)
    {
        case 'development':
            // Report all errors
            error_reporting(E_ALL);

            // Display errors in output
            ini_set('display_errors', 1);
        break;

        case 'testing':
        case 'production':
            // Report all errors except E_NOTICE
            // This is the default value set in php.ini
            error_reporting(E_ALL ^ E_NOTICE);

            // Don't display errors (they can still be logged)
            ini_set('display_errors', 0);
        break;

        default:
            exit('The application environment is not set correctly.');
    }
}

FULLTEXT Search with MySQL and CodeIgniter



FULLTEXT Search with MySQL and CodeIgniter

I was required to use CodeIgniter for a project recently. I’m generally not a big fan of the framework, but I have grown to like aspects of it. A lot of people seem to praise the documentation, the mass of examples and the active community. Maybe my hopes were set too high, but I was disappointed with all of these.
The thing I like about using a framework is being told how to do things, but for many the big attraction to CodeIgniter seems to be that it’s lightweight and you can do things how you like. I think this is the reason for the inconsistencies and low quality of example code. So I thought that I’d put together an article and example project showing a few of the tricks I’ve learnt, and my preferred CodeIgniter style.
I’m going to put together a simple application which shows off the search capabilities of MySQL, whilst exploring some of CodeIgniter’s features.

Getting CodeIgniter Ready

This is pretty basic stuff. Download, unzip, and dump the files somewhere into your web server’s directory. Setup a MySQL database and associated user and fill in the connection details in application/config/database.php.
Open up application/config/config.php and edit the base_url setting. This needs to be the web-visible URL to the directory that you put the CodeIgniter files into. If you did put them into your local web server’s root directory, this may be as simple as ‘http://localhost/’.

Setting Up the Database

You should already have a database setup, but we need a table to store the data. I’ve created a simple ‘pages’ table. To make use of MySQL’s FULLTEXT search capabilities, you must setup an index on any fields that you want to be searchable. See the the SQL below:
CREATE TABLE pages (
  id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  url text NOT NULL,
  title text NOT NULL,
  content text NOT NULL,
  updated datetime NOT NULL,
  PRIMARY KEY  (id),
  FULLTEXT KEY content (content)
) ENGINE=MyISAM
I’ve setup an index on the ‘content’ field so that it will be searchable. Note also that FULLTEXT search is only available when using the MyISAM engine.

Some Test Data

In order to perform a decent evaluation of the searching mechanism, I realised that I would need some substantial data. I decided Wikipedia would make a good source of this data. Rather than export all three million articles, I limited myself to about two thousand of the featured articles. I setup a script to scrape the list of featured articles and then used the Wikipedia export feature to automatically copy across all the data into my database.
After over-coming an issue with the character encoding it soon because obvious that the Wikipedia markup wasn’t quite the format that I wanted. Hacking together a few regular expressions meant I could strip most of this out, leaving plain and readable content.

Searching the Data

At this point, the magic SQL to use takes the form: MATCH (columns) AGAINST ('terms'). Note that when MATCH ... AGAINST is used in the WHERE clause, the results are automatically sorted, so there’s no need to add an ORDER BY clause. An example:
SELECT *
FROM pages
WHERE MATCH (content) AGAINST ('test') > 0
There are a few FULLTEXT gotchas you should be aware of:
  • Something that I was stung by: if you have fewer than three rows of data, you won’t get any search results back.
  • Search strings have a minimum length. This is generally three or four characters, but depends on configuration. See below.
  • There is a collection of ‘stopwords‘ that MySQL will ignore (words like ‘the’, ‘however’, ‘hello’). If you try a search for any of these, you won’t get any results back.
You can find out some of the configuration details by executing SHOW VARIABLES LIKE 'ft%'. You should get something like this:
mysql-variables
A few different ‘modes’ are available for searching. By default, a natural language search is performed. A boolean search is also possible, and allows a number of powerful operators to be used – refer to the documentation for details.
It is also possible to use query expansion. This roughly means that the search may return items where the search terms aren’t actually in the content, but instead there are words that are deemed similar. This is achieved by performing two searches. Where the second search uses content from high-ranking rows in the first search to perform the second search. This only tends to work well for short queries, otherwise less-relevant results will be returned.
More details on the full-text functionality can be found in the MySQL documentation.

CodeIgniter and the MVC Pattern

Let us return our attention to CodeIgniter…
The framework takes advantage of the ever-popular model-view-controller (MVC) pattern. Everyone knows you’re supposed to separate your domain-logic from presentation code, but the nice thing about the MVC pattern is that it’s constantly there to remind you.
I stick to a few basic rules:
  • you can only touch the database from within the model
  • nothing should be echo-ed from the controller (or the model)
  • only one statement can appear inside each set of <?php-tags and it should generally be either en echo or a control structure (use the alternative syntax)
If you find yourself breaking any of these rules, it’s probably a sign that it’s time to step back and re-think what you’re trying to achieve.
Let’s start off with the model. I only have one method here, which performs the searching. You may note that I’m using raw SQL here rather than using CodeIgniter’s Active Record implementation. This is just a personal preference – I think the CodeIgniter implementation is a bit messy compared to something more RoR-like.
The following code needs to go into application/models/page_model.php:
class Page_model extends Model
{
    function Page_model()
    {
        parent::Model();
 
        // Make the database available to all the methods
        $this->load->database();
    }
 
    function search($terms)
    {
        // Execute our SQL statement and return the result
        $sql = "SELECT url, title
                    FROM pages
                    WHERE MATCH (content) AGAINST (?) > 0";
        $query = $this->db->query($sql, array($terms, $terms));
        return $query->result();
    }
}
Next up is the view. Note that I’m religiously using the form helpers. This needs to go intoapplication/views/search_results.php:
<?php $this->load->helper('form'); ?>
 
<?php echo form_open($this->uri->uri_string); ?>
<?php echo form_label('Search:', 'search-box'); ?>
<?php echo form_input(array('name' => 'q', 'id' => 'search-box', 'value' => $search_terms)); ?>
<?php echo form_submit('search', 'Search'); ?>
<?php echo form_close(); ?>
 
<?php if ( ! is_null($results)): ?>
    <?php if (count($results)): ?>
        <ul>
        <?php foreach ($results as $result): ?>
            <li><a href="<?php echo $result->url; ?>"><?php echo $result->title; ?></a></li>
        <?php endforeach ?>
        </ul>
    <?php else: ?>
        <p><em>There are no results for your query.</em></p>
    <?php endif ?>
<?php endif ?>
Finally, pulling both the model and view together with the controller. The following code should go into application/controllers/pages.php:
class Pages extends Controller {
 
    function search($search_terms = '')
    {
        // If the form has been submitted, rewrite the URL so that the search
        // terms can be passed as a parameter to the action. Note that there
        // are some issues with certain characters here.
        if ($this->input->post('q'))
        {
            redirect('/pages/search/' . $this->input->post('q'));
        }
 
        if ($search_terms)
        {
            // Load the model and perform the search
            $this->load->model('page_model');
            $results = $this->page_model->search($search_terms);
        }
 
        // Render the view, passing it the necessary data
        $this->load->view('search_results', array(
            'search_terms' => $search_terms,
            'results' => @$results
        ));
    }
}
At the beginning of the method, there is a reference to a redirect function. This is a helper function, and it’s part of the ‘url’ group of helper functions. To use it, we could load the helper manually using: $this->load->helper('url');. However, because this group of helper functions is quite commonly used, we can load them automatically. To do this, we need to edit the application/config/autoload.php file:
$autoload['helper'] = array('url');
We should now have a working prototype. Make sure you have some data in your database (remember, at least three rows). Then steer your browser to the relevant URL, which should be something like: ‘http://localhost/index.php/pages/search’, enter some text to search (something that you know is in the database) and hit the ‘Search’ button.
You should get something like this:
screenshot-1

Adding More Features

To show off a bit more of CodeIgniter, I’ll add a few more features, including pagination, custom configuration files and custom helpers.
The CodeIgniter user guide boasts that the pagination class is “100% customizable”. I wouldn’t go that far, but you can certainly change a few things.
First of all, we need to modify our model so that it only returns a portion of the results, and also so that we can find out how many results in total our query would return.
The first problem is easily solved by adding an argument to the method, and a LIMIT to our SQL query. For the second problem, there are a few solutions, none of which seem very elegant:
  • We can add a separate method to the model, which will return the total number of results. This means that generally we will always have to call both routines. Certainly not a disaster, but it just doesn’t seem very refined.
  • Alternatively, we can calculate this count from within our ‘search’ method, and return both a collection of results and the total count together in some form of array structure. This is a bit abusive of the only-return-one thing methodology.
  • Or, we can pass a reference parameter to the method, set the value inside the method, and then access this variable from the controller. I think this works quite nicely because it’s discrete, but possibly a bit too subtle to be obvious to other developers.
To keep things simple, I’m going to go with the first option. Let’s refactor the model:
class Page_model extends Model {
 
    // Constructor as before
 
    function search($terms, $start = 0, $results_per_page = 0)
    {
        // Determine whether we need to limit the results
        if ($results_per_page > 0)
        {
            $limit = "LIMIT $start, $results_per_page";
        }
        else
        {
            $limit = '';
        }
 
        // Execute our SQL statement and return the result
        $sql = "SELECT url, title, content
                    FROM pages
                    WHERE MATCH (content) AGAINST (?) > 0
                    $limit";
        $query = $this->db->query($sql, array($terms, $terms));
        return $query->result();
    }
 
    function count_search_results($terms)
    {
        // Run SQL to count the total number of search results
        $sql = "SELECT COUNT(*) AS count
                    FROM pages
                    WHERE MATCH (content) AGAINST (?)";
        $query = $this->db->query($sql, array($terms));
        return $query->row()->count;
    }
}
Note that I’m also returning the ‘content’ field in the SQL – this will be useful later. The next thing to do is to modify our controller. We need to load and initialise the pagination library. Here’s the modified ‘search’ method:
class Pages extends Controller {
 
    function search($search_terms = '', $start = 0)
    {
        // If the form has been submitted, rewrite the URL so that the search
        // terms can be passed as a parameter to the action. Note that there
        // are some issues with certain characters here.
        if ($this->input->post('q'))
        {
            redirect('/pages/search/' . $this->input->post('q'));
        }
 
        if ($search_terms)
        {
            // Determine the number of results to display per page
            $results_per_page = $this->config->item('results_per_page');
 
            // Load the model, perform the search and establish the total
            // number of results
            $this->load->model('page_model');
            $results = $this->page_model->search($search_terms, $start, $results_per_page);
            $total_results = $this->page_model->count_search_results($search_terms);
 
            // Call a method to setup pagination
            $this->_setup_pagination('/pages/search/' . $search_terms . '/', $total_results, $results_per_page);
 
            // Work out which results are being displayed
            $first_result = $start + 1;
            $last_result = min($start + $results_per_page, $total_results);
        }
 
        // Render the view, passing it the necessary data
        $this->load->view('search_results', array(
            'search_terms' => $search_terms,
            'first_result' => @$first_result,
            'last_result' => @$last_result,
            'total_results' => @$total_results,
            'results' => @$results
        ));
    }
 
    function _setup_pagination($url, $total_results, $results_per_page)
    {
        // Ensure the pagination library is loaded
        $this->load->library('pagination');
 
        // This is messy. I'm not sure why the pagination class can't work
        // this out itself...
        $uri_segment = count(explode('/', $url));
 
        // Initialise the pagination class, passing in some minimum parameters
        $this->pagination->initialize(array(
            'base_url' => site_url($url),
            'uri_segment' => $uri_segment,
            'total_rows' => $total_results,
            'per_page' => $results_per_page
        ));
    }
}
Let’s look at the changes we’ve made…
We have added an extra argument to the method. This means the URL will have another (optional) segment to it. It will be a number representing the record that we are starting on, and will get set automatically by the pagination code.
In order to determine the number of results to show on a page at a time, I have used theconfig library. In order to setup custom configuration for your application, it’s best to add a new file to the application/config directory, then set it to be autoloaded. Edit theapplication/config/autoload.php file again, and add ‘application’ to the $autoload['config']array. Then create the application/config/application.php file, and put the following line in it:
$config['results_per_page'] = 10;
The next change we have made is to setup the pagination. I have put this code into a separate method. Note that the method name is prefixed with an underscore. This prevents the private method being called as an action on the controller. The code here is fairly self-explanatory.
We also pass some extra information over to our view. Which leads us to our next task of updating the view. But first, we will write our own custom helper functions for use within the view. These go into application/helpers/search_helper.php. The first function –search_highlight($text, $search_terms) – provides a way to highlight search terms in a string (there is a similar CodeIgniter helper, but it doesn’t quite do what we want). The second function – search_extract($content, $search_terms, $number_of_snippets = 3, $snippet_length = 60) – is a flaky attempt to generate an ‘excerpt’ to accompany each result. An excerpt is made up of a number of ‘snippets’ which should each contain at least one of the search terms. I won’t paste the code here since it’s far from elegant, but it’s available in the download.
Finally, the updated view. Note that we have to manually load the helper before using the helper functions. The pagination links are created using the create_links method.
<?php $this->load->helper(array('form', 'search')); ?>
 
<?php echo form_open($this->uri->uri_string); ?>
<?php echo form_label('Search:', 'search-box'); ?>
<?php echo form_input(array('name' => 'q', 'id' => 'search-box', 'value' => $search_terms)); ?>
<?php echo form_submit('search', 'Search'); ?>
<?php echo form_close(); ?>
 
<?php if ( ! is_null($results)): ?>
    <?php if (count($results)): ?>
 
        <p>Showing search results for '<?php echo $search_terms; ?>' (<?php echo $first_result; ?>&ndash;<?php echo $last_result; ?> of <?php echo $total_results; ?>):</p>
 
        <ul>
        <?php foreach ($results as $result): ?>
            <li><a href="<?php echo $result->url; ?>"><?php echo search_highlight($result->title, $search_terms); ?></a><br /><?php echo search_extract($result->content, $search_terms); ?></li>
        <?php endforeach ?>
        </ul>
 
        <?php echo $this->pagination->create_links(); ?>
 
    <?php else: ?>
        <p><em>There are no results for your query.</em></p>
    <?php endif ?>
<?php endif ?>
Here’s the result of trying it out in the browser again:
screenshot-2

Evaluation

CodeIgniter comes with benchmarking and profiling features. We can mark points in the execution, enable profiling, and have them have them output to the browser. For example, I might add marks around the calls to the model:
// Mark the start of search
$this->benchmark->mark('search_start');
 
// Load the model, perform the search and establish the total
// number of results
$this->load->model('page_model');
$results = $this->page_model->search($search_terms, $start, $results_per_page);
$total_results = $this->page_model->count_search_results($search_terms);
 
// Mark the end of search
$this->benchmark->mark('search_end');
The output at the bottom of the page would look something like this:
profiling
We can see that the search on 1800 rows took 0.0138 seconds. This is 49% of the total execution time.
It is also possible to measure the time it takes to render the view by putting marks in the controller on either side of the $this->load->view(...) call. The view takes about 0.007 seconds to load. Without the generation of a page extract, it takes significantly less time: 0.002 seconds.