Thursday, March 24, 2011

Outputs a human readable number

<?php
    
#    Output easy-to-read numbers
    #    by james at bandit.co.nz
    
function bd_nice_number($n) {
        
// first strip any formatting;
        
$n = (0+str_replace(",","",$n));
       
        
// is this a number?
        
if(!is_numeric($n)) return false;
       
        
// now filter it;
        
if($n>1000000000000) return round(($n/1000000000000),1).' trillion';
        else if(
$n>1000000000) return round(($n/1000000000),1).' billion';
        else if(
$n>1000000) return round(($n/1000000),1).' million';
        else if(
$n>1000) return round(($n/1000),1).' thousand';
       
        return 
number_format($n);
    }
?>
Outputs:

247,704,360 -> 247.7 million
866,965,260,000 -> 867 billion

Saturday, March 19, 2011

PHP, JSON and JavaScript


Today i want to introduce you to jSON (JavaScript Object Notation), in short, it is a simple format designed to exchange data between different programming languages. I will show you how to create JavaScript object, convert it to JSON string, and send to PHP script, which will decode jSON string to readable format (for PHP). But that’s not all, PHP script will create it’s own data object encode it to jSON string and send it back. All communication between JavaScript and PHP will be done thru AJAX.
If you haven’t heared about jSON yet, then you can visit Wikipedia for more information. The other technology you need to be familiar with before reading this article is AJAX. If you need to, you can read my Introduction to AJAX post.

JSON objects

Usually when it comes to JSON we have an encoded string in mind, however JSON is a subset of JavaScript and in this programming language it can be used as is, to create objects. Simple JavaScript object created using JSON notation can look like this:
var JSONstring = 
{
    "firstname": "Greg", 
    "email": "greg@fake_email.com",
    "hobby": 
    [
 {
     "hobbyName": "sport", 
            "isHobby": "true"
 },
        {
  "hobbyName": "reading", 
         "isHobby": "true"
 },
        {
  "hobbyName": "music", 
         "isHobby": "false"
 }
    ]
};
Accessing fields is done like in any other JS object (mainly because it is “normal” JavaScript object), if we want to know if hobby “reading” was checked then we would have to write:
JSONstring.hobby[1].isHobby; // true

Creating JavaScript Objects

Before we start, we will need something to work with. We will create HTML form with “validate” button, when someone clicks this button the whole proccess i described in the first paragraph will start. Also, despite JSON is a subset of JavaScript there are no built in function for converting JavaScript object into JSON string, so we will use already created class available at JSON homepage, the file is located here json2.js.
Here is the code for the HTML form, i think there is no need to explain it:
</script>
<html>
<head><TITLE>ditio.net jSon Tutorial</TITLE>
<script src="http://www.json.org/json2.js"></script>
<script>
// JavaScript source code will be here
</head>
<body>
<form name="personal" action="" method="POST">
Name <input type="text" name="firstname"><br>
Email <input type="text" name="email"><br>
Hobby 
 <input type="checkbox" name="hobby" value="sport"> Sport
 <input type="checkbox" name="hobby" value="reading"> Reading
 <input type="checkbox" name="hobby" value="music"> Music
<input type="button" name="valid" value="Validate" onclick="validate()">
</form>
</body>
</html>
After clicking “validate” button validate() function will be called, we need to add it, in head section for example:
function validate()
{
    var p = document.forms['personal'];
 
    var JSONObject = new Object;
    JSONObject.firstname = p['firstname'].value;
    JSONObject.email = p['email'].value;
    JSONObject.hobby = new Array;
 
    for(var i=0; i<3; i++)
    {
        JSONObject.hobby[i] = new Object;
 JSONObject.hobby[i].hobbyName = p['hobby'][i].value;
 JSONObject.hobby[i].isHobby = p['hobby'][i].checked;
    }
 
    JSONstring = JSON.stringify(JSONObject);
    runAjax(JSONstring);
 
}
Code is quite easy to understand. First i assign whole form to variable p just to make further access to this form data easier. In next lines JavaScript object is created, as you can see it consists only from Object and Array data objects.
Note that this is completely the same object as in first listing of this tutorial, however different method was used to create it.

Sending JSON object to PHP with AJAX

I do not want ot get into AJAX here because it is not the topic of this tutorial, i will use code from myIntroduction to AJAX post, if you do not want ot go there then remember that at the end of this article there is whole source code waiting to be downloaded.
var request;
function runAjax(JSONstring)
{
    // function returns "AJAX" object, depending on web browser
    // this is not native JS function!
    request = getHTTPObject();
    request.onreadystatechange = sendData;
    request.open("GET", "parser.php?json="+JSONstring, true);
    request.send(null);
}
 
// function is executed when var request state changes
function sendData()
{
    // if request object received response
    if(request.readyState == 4)
    {
 // parser.php response
 var JSONtext = request.responseText;
 // convert received string to JavaScript object
 var JSONobject = JSON.parse(JSONtext);
 
 // notice how variables are used
 var msg = "Number of errors: "+JSONobject.errorsNum+
  "\n- "+JSONobject.error[0]+
  "\n- "+JSONobject.error[1];
 
 alert(msg);
    }
}
That’s it we are half way there, now we need to create PHP script to handle AJAX request.

JSON and PHP

Decoding JSON string is very simple with PHP only one line of code is needed to parse string into object. Similary only one function is needed to encode PHP object or array into JSON string, look at the code:
<?php
 
// decode JSON string to PHP object
$decoded = json_decode($_GET['json']);
 
// do something with data here
 
// create response object
$json = array();
$json['errorsNum'] = 2;
$json['error'] = array();
$json['error'][] = 'Wrong email!';
$json['error'][] = 'Wrong hobby!';
 
// encode array $json to JSON string
$encoded = json_encode($json);
 
// send response back to index.html
// and end script execution
die($encoded);
 
?>
It is also interesting what is inside $decode variable
stdClass Object
(
    [firstname] => fgfg
    [email] => 
    [hobby] => Array
        (
            [0] => stdClass Object
                (
                    [hobbyName] => sport
                    [isHobby] => 1
                )
 
            [1] => stdClass Object
                (
                    [hobbyName] => reading
                    [isHobby] => 
                )
 
            [2] => stdClass Object
                (
                    [hobbyName] => music
                    [isHobby] => 
                )
 
        )
)
PHP finished execution, then “request” object status in JavaScript is now equal to 4. Response text (in sendData() function) will be parsed with JSON class to object and used to display message on the screen. Note that instead of using JSON.parse() we could use JavaScriipt eval() function.

Conclusion

This tutorial was intended to introduce you to JSON, and i wanted to make this tutorial as clear as possible so i intentionally used the simplest methods to achieve my goal. However this tutorial wouldn’t be complete if i wouldn’t give you further resources from which you can learn more.
First you should check out is Zend_Json class (a part of Zend Framework), it has the same functionality as json_decode() and json_decode(), but can handle more complicated JSON strings then those two functions.
Second is json.org home of JSON, check especially this tutorial, it has got great examples of more advanced JSON class usage.

Friday, February 25, 2011

Using MySQL Full-text Searching


Intended Audience

This tutorial is intended for developers using MySQL (http://www.MySQL.com/) and PHP (http://www.php.net) who want to create a searchable database of some sort of textual data. It will focus on the Full-text capabilities presented by MySQL, moving into the Boolean opportunities that are presented in the latest alpha version, 4.1, of MySQL.

Overview

Using directories to group articles by category is a great way to help people to navigate through many articles. At some point, however, someone will want to find all the articles that pertain to a certain topic that may not have a directory of it’s own, or may span many directories . This is what the search engine is for.

Learning Objectives

In this tutorial, you will learn:
  • How to modify your current database to accommodate Full-text searching
  • How to use a simple Full-text search to quickly gather relevant responses
  • How to execute a more complex Full-text search with Boolean capabilities
  • Tips on what to do, and what not to do, as well as the security implications of some of the example scripts presented.

Definitions

  • MySQL – An Open Source database that is used by many PHP developers for it’s support and speed, as well as because it’s free.
  • Full-text – Built in functionality in MySQL that allows users to search through certain tables for matches to a string.
  • Boolean Search – A search which allows users to narrow their results through the use of Boolean operators.
  • Boolean Operators – A deductive logical system by which a user can narrow results through the use of AND, OR, XOR, and other operators.

Background Information

Before the advent of the search engine, users had to search manually through dozens – or hundreds – of articles and tidbits to find the ones that were right for them. Nowadays, in our more user-centered world, we expect the results to come to the user, not the other way around. The search engine gets the computer to do the work for the user.

Prerequisites

  • MySQL version 3.23.23 or better for the simple Full-text searching
  • MySQL version 4.1 alpha or better for the complex Boolean searching
  • PHP & A knowledge thereof.

Synopsis

Let’s start with a quick review of our situation:

We have a database that contains articles. We might create a table of database contents using a statement like this:

CREATE TABLE articles (body TEXT, title VARCHAR(250), id INT NOT NULL auto_increment, PRIMARY KEY(id);


Let’s say we have about 100 of these articles, covering various topics: MySQL, PHP, and various other topics of that sort. How do the users find the tutorials they want? Remember, we need to bring the results to the user. This is going to be a search engine operation.

Initial Ideas

When I started to work with my first database which was only a tenth of the size, my MySQL query went something like this:

SELECT FROM articles WHERE body LIKE '%$keyword%';

This was slow and inefficient.Every time someone searched for an article, they got far too many results, and as the database grew the system became downright shameful.

So what is the solution? It’s right here: Full-text Searching.

The Solution: Setup

Full-text Search is a feature introduced to MySQL in version 3.23.23. This is how I used it to fix my problem:

I started out with an update to my table:

ALTER TABLE articles ADD FULLTEXT(bodytitle);

This set ups our Full-text index. The (body, title) part tells us that we can search the body and title for keywords later on. We’ll find out how to use this later, once we’ve overcome a potential problem.

In my original database BLOB was my datatype for the body of the article. What’s the problem, you ask? BLOBs are meant primarily for binary data. What use is searching binary data? MySQL has been programmed not to index BLOB datatypes for Full-text searching. If you try to index BLOB datatypes, you get an Error 140.

The fix for this is simple:

ALTER TABLE articles MODIFY body TEXT;

That switches datatype from BLOB to TEXT, thus making a useful column for searching.

The Solution: Actually Doing Something

How do we get results? Let’s jump right in and try it out:

<?php
    MySQL_connect
("hostname""username""password");
    
MySQL_select_db("our_db");
    
$query "
        SELECT * FROM articles
        WHERE MATCH(title, body) AGAINST ('PHP')
    "
;
    
$sql MySQL_query($query);
    
/* output results */ ?>


What will this give us? Well, let’s go over Full-Text first.

According to the MySQL manual, Full-text is a “natural language search”; it indexes words that appear to represent the row, using the columns you specified. As an example, if all your rows contain “MySQL” then “MySQL” won’t match much. It’s not terribly unique, and it would return too many results. However, if “MySQL” were present in only 5% of the rows, it would return those rows because it doesn’t appear too often to be known as a keyword that’s very common. (If you have “MySQL” in none of your rows, it’ll return nothing; duh.)

MySQL also does something pretty useful. It creates a score. This score is usually something like .9823475 or .124874, but always larger than zero. It can range up above 1, and I have seen it at 4 sometimes. (Don’t try to multiply it by 100 and portray it as a % value; people will wonder why their keyword matches an article 431%!)

MySQL will also order a row by its score, descending.

Another useful tidbit: If you use MATCH() AGAINST() Change the document style for this to “Inline Code” twice in a query, as we will, there is no additional speed penalty. You might expect that because you are executing the same search twice the query would take twice as long, but in fact MySQL remembers the results from the first search as it runs the second.

So, let's talk about the actual query: We are taking every column from articles, and searching "title" and "body" for $keyword This is also Inline Code. Pretty simple.

And if we want to display the score too:

<?php
    
/* connect to MySQL (same as always) */
    
$query "
        SELECT *,
            MATCH(title, body) AGAINST ('PHP') AS score
        FROM articles
        WHERE MATCH(title, body) AGAINST('PHP')
    "
;
    
$sql MySQL_query($query);
    
/* display the results... */ ?>


More about Basic Searching

What more is there to say? Well, there's another feature I can introduce to you.

When most people meet up with a search box they don't type in only one word. Not knowing the backend, they just type in as many words as they feel like!

MySQL realizes this and deals with it. If I were you, the only thing I would do is remove the commas that might be there, using str_replace. MySQL will take all the words, split them up, and then match using a natural language search.

As a secondary note, you should never send input directly from the user to the MySQL prompt because any number of characters could terminate your MySQL query and begin another dastardly statement. (This is presuming you replace PHP with a $keyword in the above script.)

Example: Basic Searching Application

Now that we know all about basic searching, you ask: What more is there to learn? Well, not much except that I’ve created a quick sample application for you.

Let's launch straight into the code. This bare bones application will search for a phrase or a keyword that the user inputs:

<?php
    
/* call this script "this.php" */
    
if ($c != 1) { ?> <form action="this.php?c=1">
<input type="text" name="keyword">
<input type="submit" value="Search!">
</form>
<?php
    
} else if ($c==1) {
        
MySQL_connect("hostname""username""password");
        
MySQL_select_db("database");
        
$sql "
            SELECT *,
                MATCH(title, body) AGAINST('$keyword') AS score
                FROM articles
            WHERE MATCH(title, body) AGAINST('$keyword')
            ORDER BY score DESC
        "
;
        
$res MySQL_query($sql); ?> <table>
<tr><td>SCORE</td><td>TITLE</td><td>ID#</td></tr>
<?php
        
while($row MySQL_fetch_array($rest)) {
            echo 
"<tr><td>{$sql2['score']}</td>";
            echo 
"<td>{$sql2['title']}</td>";
            echo 
"<td>{$sql2['id']}</td></tr>";
        }
        echo 
"</table>";
    } 
?>



What does this script do? First, it checks $cto see if user input has been sent. If it has not, the form is displayed. If it has, the script moves onwards.

The same query that we've been using is used here: we match against what the user inputs. We then draw a table and display it in [semi-]pretty form. The ORDER BY score DESC
Code Inline makes sure that the best scores (the most accurate matches) are shown first.

Important note: Never use this simple script in any production form because I have done absolutely no error checking. The $queryvariable provides an easy opening for an intruder to input something nasty into your query that might destroy your data.

Advanced Boolean Searching

If you need more options in your MySQL searching, or you haven't finished your coffee yet, keep reading. The advanced search engine tutorial begins here.

Before we get started into the magic of bool, I recommend you do a quick SELECT version();
Code Inlineon your MySQL server. I spent several hours battling my computer until I read this line in the MySQL manual:

As of Version 4.0.1, MySQL can also perform Boolean full-text searches using the IN BOOLEAN MODE modifier.

Whoops; 4.0.1 is the newest, alpha release of MySQL
Still check for this... it should by MySQL. If you're looking to use this on a production server, I'd strongly recommend against that decision. I found I was using a 3.23.23, and I had to set up an experimental MySQL server to use the bool functions that it offers now.

Overall, I was very pleased with the performance of the new bool searching; the scoring system is changed, but one can still manage. Within 15 minutes of upgrading, I had a simple bool search page up and running on my articles database.

Boolean: The Basic Technical Aspect

The only thing you change to use Boolean mode is the AGAINST() part of your query. You add IN BOOLEAN MODE to the very end of it, and place the arguments right before it. E.g. to search for all the articles that contain the word PHP, you could write:

SELECT * FROM articles WHERE MATCH(title, body) AGAINST('PHP' IN BOOLEAN MODE)DR[10]

That will find all the articles that contain the word "PHP" somewhere in them. It's a fairly simple search. If you were to get more complex, and wanted everything that has to do with PHP, but not with MySQL, then you could execute this statement:

SELECT FROM articles WHERE MATCH(titlebodyAGAINST('+PHP -MySQL' IN BOOLEAN MODE);

There are more modifiers that one can use to search with, and I will quote from the MySQL manual since I see no point in typing out a synopsis of the manual:

OperatorMeaning

By default (when neither plus nor minus is specified) the word is optional, but the rows that contain it will be rated higher. This mimics the behavior of MATCH() ... AGAINST()DR

+A leading plus sign indicates that this word must be present in every row returned.
-A leading minus sign indicates that this word must not be present in any row returned. 
< >These two operators are used to change a word's contribution to the relevance value that is assigned to a row. The < operator decreases the contribution and the > operator increases it. See the example below. 
( ). Parentheses are put round sub-expressions to give them higher precedence in the search. 
~A leading tilde acts as a negation operator, causing the word's contribution to the row relevance to be negative. It's useful for marking noise words. A row that contains such a word will be rated lower than others, but will not be excluded altogether, as it would be with the minus operator.
*An asterisk is the truncation operator. Unlike the other operators, it is appended to the word, or fragment, not prepended. 
Double quotes at the beginning and end of a phrase, matches only rows that contain the complete phrase, as it was typed. 

A Basic Boolean Searching Application

Again, we’ll start with the code straight off:

<?php 
   
/* call this script "advs.php" */
   
if(!$c) {  ?> <form action="advs.php?c=1" method=POST>
<b>Find Results with: </b><br>
Any of these words: <input type="text" length=40 name="any"> <br>
All of these words: <input type="text" length=40 name="all"> <br>
None of these words: <input type="text" length=40 name="none"> <br>
<input type="submit" value="Search">
</form>
<?
   
} else if($c) {
   
MySQL_connect("hostname""username""password");
       
MySQL_select_db("database");
   if((!
$all) || ($all == "")) { $all ""; } else { $all "+(".$all.")"; }
   if((!
$any) || ($any == "")) { $any ""; } 
   if((!
$none) || ($none == "")) { $none ""; } else { $none "-(".$none.")"; }
   
$query "
       SELECT *,
          MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE) AS score 
          FROM compsite 
       WHERE MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE)"
;
      
$artm1 MySQL_query($query);
      if(!
$artm1) { 
         echo 
MySQL_error()."<br>$query<br>"
      }
      echo 
"<b>Article Matches</b><br>";
      if(
MySQL_num_rows($artm1) > 0) {
         echo 
"<table>";
          echo 
"<tr><td>Score </td><td>Title </td><td>Body</td></tr>";
             while(
$artm2 MySQL_fetch_array($artm1)) {
            
$val round($artm2['score'], 3);
            
$val $val*100;
            echo 
"<tr><td>$val</td>";
            echo 
"<td>{$artm2['title']}</td>";
            echo 
"<td>{$artm2['body']}</td></tr>";
         }
      echo 
"</table>";
   }
   else { 
      echo 
"No Results were found in this category.<br>"
   }
   echo 
"<br>"
   } 


After we get the input from the form, $c Code Inlineis set to 1 and we start the real work.

First we check our input. If it's empty, we leave it empty, if it's not, we append the proper + or - to it. The parentheses are to allow for the user typing more than 1 word in a given field.

$query "
    SELECT *
        MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE) AS score
    FROM compsite
    WHERE
        MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE)"


That's the final query that we use. $all,$none Code Inline, and $any have already been prepared for the query, and they are inserted. Score is returned as a column to order them by (if we wanted to do that), and from there on, we just have to output the results.

   if(MySQL_num_rows($artm1) > 0) {
      echo 
"<table>";
    echo 
"<tr><td>Score </td><td>Title </td><td>Body</td></tr>";
          while(
$artm2 MySQL_fetch_array($artm1)) {
         
$val round($artm2['score'], 3);
         
$val $val*100;
         echo 
"<tr><td>$val</td>";
         echo 
"<td>{$artm2['title']}</td>";
         echo 
"<td>{$artm2['body']}</td></tr>";
      }
   echo 
"</table>"


That's the output code. If there's less than 1 row to output, we send a "no records found" message out.

That's about it for Full-Text searching.

Resources

MySQL Man Page for Full-Text: http://www.MySQL.com/doc/F/u/Fulltext_Search.html

I found the following note on the Man pages for Full-text. It might be useful for anyone trying to work out why their database returns no matches.

          Extra [important] Note from the Man Page: Tim Gustafson: Note: you should add at least 3 rows to the table before you try to match anything, and what you're searching for should only be contained in one of the three rows. This is because of the 50% threshold. If you insert only one row, then now matter what you search for, it is in 50% or more of the rows in the table, and therefore disregarded.

Saturday, February 19, 2011

PHP: Hypertext Preprocessor


php - what is it?

Taken directly from PHP's home, PHP.net, "PHP is an HTML-embedded scripting language. Much of its syntax is borrowed from C, Java and Perl with a couple of unique PHP-specific features thrown in. The goal of the language is to allow web developers to write dynamically generated pages quickly."
This is generally a good definition of PHP. However, it does contain a lot of terms you may not be used to. Another way to think of PHP is a powerful, behind the scenes scripting language that your visitors won't see!
When someone visits your PHP webpage, your web server processes the PHP code. It then sees which parts it needs to show to visitors(content and pictures) and hides the other stuff(file operations, math calculations, etc.) then translates your PHP into HTML. After the translation into HTML, it sends the webpage to your visitor's web browser.

php - what's it do?

It is also helpful to think of PHP in terms of what it can do for you. PHP will allow you to:
  • Reduce the time to create large websites.
  • Create a customized user experience for visitors based on information that you have gathered from them.
  • Open up thousands of possibilities for online tools. Check out PHP - HotScriptsfor examples of the great things that are possible with PHP.
  • Allow creation of shopping carts for e-commerce websites.

what you should know

Before starting this tutorial it is important that you have a basic understanding and experience in the following:
  • HTML - Know the syntax and especially HTML Forms.
  • Basic programming knowledge - This isn't required, but if you have any traditional programming experience it will make learning PHP a great deal easier.