http://www.iamcal.com/publish/articles/php/search/

PHP : Searching with PHP and MySQL
So you have a PHP/MySQL website, and you want to let users search for things. Easy!

$search_term_esc = AddSlashes($search_term);

$sql = “SELECT * FROM Content WHERE content_body LIKE ’%$search_term_esc%’”;
Great. Only not really. If I enter ’%’ or ’_’ as my search term, it’ll match every piece of content you have. And if someone enters ‘e’ then it’ll match every article with an ‘e’ in it. And if someone enters ‘foo bar’ it’ll match the phrase, not the two words. It looks like we have a lot to learn.

Step one is breaking the terms apart. We could do this with a simple explode, but wouldn’t it be cool if we could also allow phrases by accepting quotes? One easy way to achieve this is with a simple three step routine:

Convert whitespace between brackets into something non-whitespacey.
Split on whitespace.
Convert the ‘something’ back to the whitespace it was, for each token.
For the first stage, we can use my favourite feature of regular expressions – the ‘e’ (evaluation) flag.

$terms = preg_replace(“/”(.*?)”/e”, “search_transform_term(’$1’)”, $terms);

function search_transform_term($term){
$term = preg_replace(“/(s)/e”, “’{WHITESPACE-’.ord(’$1’).’}’”, $term);
$term = preg_replace(“/,/”, “{COMMA}”, $term);
return $term;
}
So what happens here exactly? The first preg_replace() call find all pair of brackets and passes their contents to the search_transform_term() function for processing. We then use a further two preg_replace() calls to replace any whitespace with a holder token, and the same with commas. We process commas incase users enter comma seperated terms, instead of space seperated ones.

With this done, we’re ready to split on whitespace and commas. We’ll use preg_split() for this:

$terms = preg_split(“/s+|,/”, $terms);
For each term, we then want to replace the holding tokens back with their original contents.

$term = preg_replace(“/{WHITESPACE-([0-9]+)}/e”, “chr($1)”, $term);
$term = preg_replace(“/{COMMA}/”, “,”, $term);
So now we have a full function for splitting up search terms:

function search_split_terms($terms){

$terms = preg_replace(“/”(.*?)”/e”, “search_transform_term(’$1’)”, $terms);
$terms = preg_split(“/s+|,/”, $terms);

$out = array();

foreach($terms as $term){

$term = preg_replace(“/{WHITESPACE-([0-9]+)}/e”, “chr($1)”, $term);
$term = preg_replace(“/{COMMA}/”, “,”, $term);

$out[] = $term;
}

return $out;
}

function search_transform_term($term){
$term = preg_replace(“/(s)/e”, “’{WHITESPACE-’.ord(’$1’).’}’”, $term);
$term = preg_replace(“/,/”, “{COMMA}”, $term);
return $term;
}
On to stage two – doing a useful search with each term. Instead of using MySQL’s LIKE operator, we’ll use it’s much more powerful friend, RLIKE, which lets us use regular expressions.

MySQL’s regular expressions are quite perl-like, but not entirely. The first thing we need is an escaping function, since some of the characters in the search term might contain a MySQL regular expression meta-character.

function search_escape_rlike($string){
return preg_replace(“/([.[]*^$])/”, ’\$1’, $string);
}
This function inserts a slash before each meta-character that MySQL uses. Next we need to make sure each term matches on word boundaries so that ‘foo’ matches ‘a foo a’ but not ‘a food a’. In MySQL syntax, these are ’[[:<:]]’ and ’[[:>:]]’. We’ll write a function to turn our list of terms into a list of regular expressions.

function search_db_escape_terms($terms){
$out = array();
foreach($terms as $term){
$out[] = ’[[:<:]]’.AddSlashes(search_escape_rlike($term)).’[[:>:]]’;
}
return $out;
}
So now we’re ready to build some SQL. I’m assuming a single content table with a single field to match. You can figure out how to expand it to something more complicated yourself.

$terms = search_split_terms($terms);
$terms_db = search_db_escape_terms($terms);

$parts = array();
foreach($terms_db as $term_db){
$parts[] = “content_body RLIKE ’$term_db’”;
}
$parts = implode(’ AND ’, $parts);

$sql = “SELECT * FROM Content WHERE $parts”;
With this function, the search string ‘foo bar’ generates this SQL:

SELECT * FROM Content WHERE content_body RLIKE ’[[:<:]]foo[[:>:]]’
AND content_body RLIKE ’[[:<:]]bar[[:>:]]’;
You can replace the ‘AND’ with an ‘OR’ if you want to match pages which match any of the terms, rather than all.

Maybe you want to rank the results by how many times the search terms appear in them – that’s what users often expect.

$terms_rx = search_rx_escape_terms($terms);

$rows = array();

$result = mysql_query($sql);
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){

$row[score] = 0;

foreach($terms_rx as $term_rx){
$row[score] += preg_match_all(“/$term_rx/i”, $row[content_body], $null);
}

$rows[] = $row;
}

uasort($rows, ‘search_sort_results’);

function search_rx_escape_terms($terms){
$out = array();
foreach($terms as $term){
$out[] = ’b’.preg_quote($term, ’/’).’b’;
}
return $out;
}

function search_sort_results($a, $b){

$ax = $a[score];
$bx = $b[score];

if ($ax == $bx){ return 0; }
return ($ax > $bx) ? -1 : 1;
}
Here we use preg_match_all() to find the number of matches for each term in the content. Our search_rx_escape_terms() turns each of the terms into a preg style regular expression. After stashing each of the matching results in $rows, along with its’ score, we use usort() to sort the results using a custom function.

We have a final touch left – displaying the list of search terms back to the user with the results. For this, we want to show quoted terms and display them in a “pretty” list:

$terms_html = search_html_escape_terms($terms);

function search_html_escape_terms($terms){
$out = array();

foreach($terms as $term){
if (preg_match(“/s|,/”, $term)){
$out[] = ’“’.HtmlSpecialChars($term).’”’;
}else{
$out[] = HtmlSpecialChars($term);
}
}

return $out;
}

function search_pretty_terms($terms_html){

if (count($terms_html) == 1){
return array_pop($terms_html);
}

$last = array_pop($terms_html);

return implode(’, ’, $terms_html).“ and $last”;
}
Understanding the above function is left an an exercise for the reader 😉

So now we have our full suite of search code – let’s bring it all together:

function search_split_terms($terms){

$terms = preg_replace(“/”(.*?)”/e”, “search_transform_term(’$1’)”, $terms);
$terms = preg_split(“/s+|,/”, $terms);

$out = array();

foreach($terms as $term){

$term = preg_replace(“/{WHITESPACE-([0-9]+)}/e”, “chr($1)”, $term);
$term = preg_replace(“/{COMMA}/”, “,”, $term);

$out[] = $term;
}

return $out;
}

function search_transform_term($term){
$term = preg_replace(“/(s)/e”, “’{WHITESPACE-’.ord(’$1’).’}’”, $term);
$term = preg_replace(“/,/”, “{COMMA}”, $term);
return $term;
}

function search_escape_rlike($string){
return preg_replace(“/([.[]*^$])/”, ’\$1’, $string);
}

function search_db_escape_terms($terms){
$out = array();
foreach($terms as $term){
$out[] = ’[[:<:]]’.AddSlashes(search_escape_rlike($term)).’[[:>:]]’;
}
return $out;
}

function search_perform($terms){

$terms = search_split_terms($terms);
$terms_db = search_db_escape_terms($terms);
$terms_rx = search_rx_escape_terms($terms);

$parts = array();
foreach($terms_db as $term_db){
$parts[] = “content_body RLIKE ’$term_db’”;
}
$parts = implode(’ AND ’, $parts);

$sql = “SELECT * FROM Content WHERE $parts”;

$rows = array();

$result = mysql_query($sql);
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){

$row[score] = 0;

foreach($terms_rx as $term_rx){
$row[score] += preg_match_all(“/$term_rx/i”, $row[content_body], $null);
}

$rows[] = $row;
}

uasort($rows, ‘search_sort_results’);

return $rows;
}

function search_rx_escape_terms($terms){
$out = array();
foreach($terms as $term){
$out[] = ’b’.preg_quote($term, ’/’).’b’;
}
return $out;
}

function search_sort_results($a, $b){

$ax = $a[score];
$bx = $b[score];

if ($ax == $bx){ return 0; }
return ($ax > $bx) ? -1 : 1;
}

function search_html_escape_terms($terms){
$out = array();

foreach($terms as $term){
if (preg_match(“/s|,/”, $term)){
$out[] = ’“’.HtmlSpecialChars($term).’”’;
}else{
$out[] = HtmlSpecialChars($term);
}
}

return $out;
}

function search_pretty_terms($terms_html){

if (count($terms_html) == 1){
return array_pop($terms_html);
}

$last = array_pop($terms_html);

return implode(’, ’, $terms_html).“ and $last”;
}

#
# do the search here…
#

$results = search_perform($HTTP_GET_VARS[q]);
$term_list = search_pretty_terms(search_html_escape_terms(search_split_terms($HTTP_GET_VARS[q])));

#
# of course, we’re using smarty 😉
#

$smarty->assign(‘term_list’, $term_list);

if (count($results)){

$smarty->assign(‘results’, $results);
$smarty->display(‘search_results.txt’);
}else{

$smarty->display(‘search_noresults.txt’);
}

And there we go. Not quite as easy as our very first example, but alot more useful for your users.

If you’re not totally sick of searching yet, then you might like to add some search term highlighting.

Note: The code in this article is designed for small sized tables. A medium to large dataset will make for very heavy processing – regular expressions are not CPU cheap and can’t be indexed. For large scale websites, you need a proper enterprise-level solution. Xapian is a good place to start. For medium sized tables, MySQL’s own FULLTEXT indexes can do some fancy things. The downsides are that you’ll need to use MySQL 4.1 if you have UTF-8 data and your tables will have to be MyISAM, which has it’s own set of issues.

Ogg Vorbis는 특허권, 지적재산권이 걸려있는 MP3등의 모든 독점적, 폐쇄적인 오디오 코덱들을 대체하기 위해 태어난 손실압축 오디오 포맷입니다. 1990년대 초중반에 구상되기 시작하여, MP3 라이센스 관련 문제가 터진 1998년에 본격 개발되기 시작하였습니다.
Ogg Vorbis 파일은 다양한 샘플링레이트, 비트레이트, 채널 수에도 유연하게 작동하며, 비슷한 비트레이트의 MP3파일들보다 뛰어난 음질을 자랑하고 있습니다. 또, 유연한 파일 포맷의 채택으로, Ogg Vorbis의 음질은 앞으로 발전을 거듭할 것입니다.

Ogg Vorbis의 파일 포맷의 이용에는 아무런 제한이 없어서, 어떠한 용도로도 자유롭게 무료로 사용하실 수 있습니다. 소프트웨어 관련으로는 레퍼런스 라이브러리가 BSD라이센스로 배포되고 있어서, 상용 소프트웨어 제작에도 문제가 없습니다. 인터넷 방송에도 역시 자유롭게 사용하실 수 있습니다.

현재 Ogg는 “Winamp, XMMS, Sonique, Windows Media Player, ZINF 등의 거의 대부분의 음악 재생 플레이어에서 재생할수 있으며, CDex, Easy CD-DA Extractor, GRIP등의 CD추출 프로그램을 통해서 손쉽게 CD에서 추출이 가능합니다. 또한 거원 제트오디오 5.0, SIREN Jukebox, Media Jukebox 등의 프로그램을 통해서 Audio CD로 바로 만들수 있으며, GoldWave, Cool Edit, Audacity 등의 소리 편집 프로그램에서 편집을 할수 있는 등 무척 다양한 종류의 소프트웨어에서 지원되고 있고, 그 수도 점점 늘어나고 있는 추세입니다.

현재 Ogg Vorbis지원에 관심을 갖고 있는 하드웨어 업체로는 우리나라의 iRiver 싱가폴의 Frontier Labs, 독일의 Pontis 등 여러 곳이 있습니다.

영국의 BBC 방송에서도 Ogg Vorbis에 큰 관심을 가져, Ogg Vorbis로 여러 차례에 걸친 인터넷 라디오 시험방송을 마치고 현재 본 방송을 준비중입니다.

사족: Ogg의 이름은 Netrek이라는 온라인 우주전쟁 게임의 어느 전법의 이름에서 유래되었는데, Ogg Vorbis 파일 포맷의 기본 틀을 의미합니다. Vorbis라는 이름은 Terry Pratchett의 SF소설인 Small Gods의 무자비한 종교인의 이름에서 따온 것인데, Ogg Vorbis 파일에서 사용되는 오디오압축 방식을 의미한다고 합니다.

Ogg Vorbis는 Ogg 멀티미디어 프로젝트의 일환으로 Ogg라는 기본 포맷안에 모든 종류의 멀티미디어 정보를 담기 위한 포괄적 프로젝트입니다. 현재 무손실 압축의 Ogg FLAC, 목소리 압축의 Ogg Speex, 동영상 압축의 Ogg Theora등의 코덱들도 개발되고 있으며, Ogg Vorbis의 음질도 계속 향상될 것으로 보입니다

Ogg Vorbis는 특허권, 지적재산권이 걸려있는 MP3등의 모든 독점적, 폐쇄적인 오디오 코덱들을 대체하기 위해 태어난 손실압축 오디오 포맷입니다. 1990년대 초중반에 구상되기 시작하여, MP3 라이센스 관련 문제가 터진 1998년에 본격 개발되기 시작하였습니다.
Ogg Vorbis 파일은 다양한 샘플링레이트, 비트레이트, 채널 수에도 유연하게 작동하며, 비슷한 비트레이트의 MP3파일들보다 뛰어난 음질을 자랑하고 있습니다. 또, 유연한 파일 포맷의 채택으로, Ogg Vorbis의 음질은 앞으로 발전을 거듭할 것입니다.

Ogg Vorbis의 파일 포맷의 이용에는 아무런 제한이 없어서, 어떠한 용도로도 자유롭게 무료로 사용하실 수 있습니다. 소프트웨어 관련으로는 레퍼런스 라이브러리가 BSD라이센스로 배포되고 있어서, 상용 소프트웨어 제작에도 문제가 없습니다. 인터넷 방송에도 역시 자유롭게 사용하실 수 있습니다.

현재 Ogg는 “Winamp, XMMS, Sonique, Windows Media Player, ZINF 등의 거의 대부분의 음악 재생 플레이어에서 재생할수 있으며, CDex, Easy CD-DA Extractor, GRIP등의 CD추출 프로그램을 통해서 손쉽게 CD에서 추출이 가능합니다. 또한 거원 제트오디오 5.0, SIREN Jukebox, Media Jukebox 등의 프로그램을 통해서 Audio CD로 바로 만들수 있으며, GoldWave, Cool Edit, Audacity 등의 소리 편집 프로그램에서 편집을 할수 있는 등 무척 다양한 종류의 소프트웨어에서 지원되고 있고, 그 수도 점점 늘어나고 있는 추세입니다.

현재 Ogg Vorbis지원에 관심을 갖고 있는 하드웨어 업체로는 우리나라의 iRiver 싱가폴의 Frontier Labs, 독일의 Pontis 등 여러 곳이 있습니다.

영국의 BBC 방송에서도 Ogg Vorbis에 큰 관심을 가져, Ogg Vorbis로 여러 차례에 걸친 인터넷 라디오 시험방송을 마치고 현재 본 방송을 준비중입니다.

사족: Ogg의 이름은 Netrek이라는 온라인 우주전쟁 게임의 어느 전법의 이름에서 유래되었는데, Ogg Vorbis 파일 포맷의 기본 틀을 의미합니다. Vorbis라는 이름은 Terry Pratchett의 SF소설인 Small Gods의 무자비한 종교인의 이름에서 따온 것인데, Ogg Vorbis 파일에서 사용되는 오디오압축 방식을 의미한다고 합니다.

Ogg Vorbis는 Ogg 멀티미디어 프로젝트의 일환으로 Ogg라는 기본 포맷안에 모든 종류의 멀티미디어 정보를 담기 위한 포괄적 프로젝트입니다. 현재 무손실 압축의 Ogg FLAC, 목소리 압축의 Ogg Speex, 동영상 압축의 Ogg Theora등의 코덱들도 개발되고 있으며, Ogg Vorbis의 음질도 계속 향상될 것으로 보입니다

Use chown to change ownership and chmod to change rights.

As Paweł Karpiński said, use the -R option to apply the rights for all files inside of a directory too.

Note that both these commands just work for directories too. The -R option makes them also change the permissions for all files and directories inside of the directory.

For example

sudo chown -R username:group directory

will change ownership (both user and group) of all files and directories inside of directory and directory itself.

sudo chown username:group directory

will only change the permission of the folder directory but will leave the files and folders inside the directory alone.

As enzotib mentioned, you need to use sudo to change the ownership from root to yourself.

Dont load image_lib multiple times. Add image_lib in autoload libs and change

$this->load->library(‘image_lib’, $config);
to

$this->image_lib->initialize($config);

How to validate multiple forms on one page in CodeIgniter?               

In this tutorial I will show how to create a controller and a view in CodeIgniter to render, validate and output forms, validation error messages and results for multiple forms on one page.

We will start from creating the controller.

The controller

On top of every CodeIgniter controller script file we need to put the following line

1
if (!defined('BASEPATH')) exit('No direct script access allowed');

which prevents from accessing the controller from the outside.

Now we’ll create a CodeIgniter controller script for rendering forms, validation and getting the validation results. As you probably know all controller scripts are placed in application/controller directory in you app’s root folder and each script has the same filename as the class inside.

More about this you can find [here; link for external CI site or other article].

Let’s create a class called “Forms” which will extends from the main CodeIgniter controller class (CI_Controller)

1
class Forms extends CI_Controller {

Then we have to create a class constructor to initialize parent class constructor first to we will have access to all CI_Controller methods then we load form helper which contains functions that assist in working with forms.

1
2
3
4
5
6
7
// Controller constructor
public function __construct()
{
    parent::__construct();
    // Load form helper required to validate forms
    $this->load->helper('form');     
}

We will write a method (function) for defining form elements and passing that data to the view so it can be rendered. Index function is executed by default when accessing controller. In index method we’ll define three forms. First one will go with username and key text input fields and the rest will go with just a username text input field. And the end of this function we take all the arguments and pass it on to our view which will render the forms.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Prepare data for the view to output the forms
public function index()
{
    // Defining form1 elements
    $data['form_1'] = array('class' => 'main_form', 'id' => 'form_1');
    $data['form_1']['username'] = array('name' => 'username', 'value' => 'Default username');
    $data['form_1']['key'] = array('name' => 'key', 'value' => 'Key');
    $data['form_1']['submit'] = array('value' => 'Submit!', 'type' => 'submit');
 
    // Defining form2 elements
    $data['form_2'] = array('class' => 'main_form', 'id' => 'form_2');
    $data['form_2']['username'] = array('name' => 'username', 'value' => 'Default username');
    $data['form_2']['submit'] = array('value' => 'Submit!', 'type' => 'submit');
 
    // Defining form3 elements
    $data['form_3'] = array('class' => 'main_form', 'id' => 'form_3');
    $data['form_3']['username'] = array('name' => 'username', 'value' => 'Default username');
    $data['form_3']['submit'] = array('value' => 'Submit!', 'type' => 'submit');     
    // Load the forms view and pass data to the view
    $this->load->view('forms', $data);       
}

New we’ll create a function that will assign validation rules to each of form elements and return the validation status.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Form validation function
private function validate_form($validation_rules, $callback=NULL)
{
    $array_index = 0;  
    // Load the form validation CodeIgniter library
    $this->load->library('form_validation');                     
    // Loop through validation rules and set it up for each element
    foreach($validation_rules as $form_rules)
    {
        list($name, $title, $rules) = $form_rules;
        $this->form_validation->set_rules($name, $title, $rules);
    }                          
    // Run the validation and resturn the result
    return $this->form_validation->run();
}

Now is the time to focus on main validation function. It will be picking up the form name to be validated from the current URL than it will run it against validation rules that we specify for each form element.

So for example for form_1 we are setting up validation rules for username and key input fields. In that case username and key fields will be required. We can use more advanced validation but now we will stick up with basic validation so you could get a better overview of it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Validate forms with set rules
    public function validate()
    {      
        // Get the form name from URI
        $form_name = $this->uri->segment(3);
 
        // Setting validation rules for form1
        $rules['form_1'] = array(
            array('username', 'Username', 'required'),
            array('key', 'Key', 'required'),
        ); 
        // Setting validation rules for form2
        $rules['form_2'] = array(
            array('username', 'Username', 'required'),
        );
        // Setting validation rules for form3
        $rules['form_3'] = array(
            array('username', 'Username', 'required'),
        );
        // Validate specific form according to form name given in URL
        $this->validate_form($rules[$form_name]);   
        // Return to forms and display the result
        $this->index();     
    }  
}

So now we have finished writing our forms controller. New we are going to create a view which will hold our forms.

The view

In our view we will make sure that we output any validation errors, generate three forms and get the validation results. So first of all we’ll get (if there are any) validation errors.

1
<?php echo validation_errors(); ?>

Then we will generate three forms with elements specified in controller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<h2>Form 1</h2>
<?php   
print form_open(base_url('forms/validate/form_1'), $form_1) .
form_input($form_1['username']) .
form_input($form_1['key']) .
form_input($form_1['submit']) .
form_close();
?>
 
<h2>Form 2</h2>
<?php   
print form_open(base_url('forms/validate/form_2'), $form_2) .
form_input($form_2['username']) .
form_input($form_2['submit']) .
form_close();
?>
 
<h2>Form 3</h2>
<?php   
print form_open(base_url('forms/validate/form_3'), $form_3) .
form_input($form_2['username']) .
form_input($form_2['submit']) .
form_close();
?>

At the end of our view we will output validation results.

1
2
3
4
5
6
7
<?php
if (isset($result) && $result)
{
    echo '<h1>Result</h1>';
    echo $result;
}
?>

If you followed this tutorial now you should be able to validate all three forms according to submission.

You can download the source code for this tutorial at the bottom.

Good luck and have fun with validation.