Posted by admin on December-11-2009 Add Comments

XML processing with PHP

XML processing with PHP

Introduction

XML is Extensible Markup Language. It is a markup language – much like HTML – and was designed to describe data. XML uses tags but these tags are not predefined as in HTML. In XML you define your own tags. All XML documents have 3 types of components: Elements, attributes and data.

Let’s see a basic XML file:

Code:
<root>
  <child attribute='test'>
    <subchild attribute='sub'>testdata</subchild>
  </child>
</root>

The first line defines the root element of the XML. It is the <root>. The root element can contain any number of child elements. All of them can be found between the opening <root> and closing </root> tag. In our example the file contains a child element. The child element has and that in turn can have its own and so on. There is no restriction as to how many child element is defined. All is up to you.
In the second line there is a new element which is a child of the root element. In this case it has an attribute which you can interpret as a property of the given element. In this example the property name is attribute and the property values is ‘test’. This element has again a child (line 3) – which is a subchild of the root – element with an attribute. Besides this it has a data with the value of ‘testdata’.

As you can see from this small example:

  1. 1. Each XML element has an opening and a closing tag.
  1. 2. The attributes is defined inside the opening tag.
  1. 3. The data is surrounded by opening and closing tags.

PHP and XML

PHP has its own built in functions for XML handling. To process an XML file first of all you need an XML parser. You can create it with the following code:

PHP Code:
$parser = xml_parser_create();

This code creates an empty XML parser object which will process our XML file later. The parser requires the XML document as a string so we read the file as follows:

PHP Code:
$document = file_get_contents("test.xml");

Now we have an XML document in string format and an XML parser. Let’s process the file.

PHP Code:
xml_parse($parser, $document);

After processing we free up the resources:

PHP Code:
xml_parser_free($parser);

The complete code looks like this at the moment:

PHP Code:
<?php
$parser
= xml_parser_create();
$document = file_get_contents("test.xml");
xml_parse($parser, $document);
xml_parser_free($parser);
?>

And our test XML file (test.xml), which is located in the same directory as our PHP script:

Code:
<carlist>
  <car type="Mercedes">S 600</car>
  <car type="Mercedes">E 270 CDI</car>
  <car type="BMW">535 D</car>
  <car type="Lexus">IS 220</car>
</carlist>



Try to execute it!
What happened?
Nothing.
It is normal as we didn’t define what to do with the results and how the result should look like. Then for example how can I list all Mercedeses?

XML processing refinement

To get a list of Mercedes we need to write some additional code. First make some refinement how the parser should process the XML you can set some property with the xml_parser_set_option() function
In our example I set the case folding property to false, which generally means that no tag name will convert to uppercase.

PHP Code:
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);

XML element handlers

Besides this we have to tell the parser what to do if an opening or closing tag is found. To do this I wrote two functions to handle these events. Both have a well defined attribute list.

PHP Code:
function openElement($parser, $element, $attributes)
function
closeElement($parser, $element)


The first parameter in both cases is the parser object. The second is the element name. The openElement has a third parameter the attributes which is an array representation of the actual element attribute names and values.

You can assign these function to the parser as follows:

PHP Code:
xml_set_element_handler($parser, "openElement", "closeElement");


Take care that the functions defined above must have the same names as the parameters of the xml_set_element_handler. As result if the parser finds an open element than it will call the openElement function. Of course it calls closeElement if a closing tag was found.

XML data handler

One more thing is missing. A handler function to process the XML data values. For that I implemented a new function:

PHP Code:
function characterData($parser, $data);


The first parameter is already known. The second contains the actual data value. To make the assignment you should use the following statement:

PHP Code:
xml_set_character_data_handler($parser, "characterData");




Now the full code looks like this:

PHP Code:
<?php

function openElement($parser, $element, $attributes) {
}

function closeElement($parser, $element) {
}

function characterData($parser, $data) {
}

$parser = xml_parser_create();

xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($parser, "openElement", "closeElement");
xml_set_character_data_handler($parser, "characterData");

$document = file_get_contents("test.xml");
xml_parse($parser, $document);

xml_parser_free($parser);

?>

Of course this code will result again is an empty page. Just to have some output and demonstrate how the functions are called I added some code in the handler functions.

The new code is:

PHP Code:
<?php

function openElement($parser, $element, $attributes) {
foreach (
$attributes as $key => $value) $attr .= $key." : ".$value." - ";
echo
"-> openElement element: $element, attribute: $attributes ($attr) <br/>";
}

function closeElement($parser, $element) {
echo
"-> closeElement element is: $element<br/>";
}

function characterData($parser, $data) {
echo
"-> characterData data is: [$data]<br/>";
}

$parser = xml_parser_create();

xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($parser, "openElement", "closeElement");
xml_set_character_data_handler($parser, "characterData");

$document = file_get_contents("test.xml");
xml_parse($parser, $document);

xml_parser_free($parser);

?>

And the output is:

-> openElement element: carlist, attribute: Array ()
-> characterData data is: [ ]
-> characterData data is: [ ]
-> openElement element: car, attribute: Array (type : Mercedes – )
-> characterData data is: [S 600]
-> closeElement element is: car
-> characterData data is: [ ]
-> characterData data is: [ ]
-> openElement element: car, attribute: Array (type : Mercedes – )
-> characterData data is: [E 270 CDI]
-> closeElement element is: car
-> characterData data is: [ ]
-> characterData data is: [ ]
-> openElement element: car, attribute: Array (type : BMW – )
-> characterData data is: [535 D]
-> closeElement element is: car
-> characterData data is: [ ]
-> characterData data is: [ ]
-> openElement element: car, attribute: Array (type : Lexus – )
-> characterData data is: [IS 220]
-> closeElement element is: car
-> characterData data is: [ ]
-> closeElement element is: carlist

In the output you can find the root element <carlist>, the four child elements <car> the attribute list and the data values.
The only interesting thing is why we have so many time characterData with an empty string?
To understand this you should open the XML file and displays the hidden characters. After it you can see that there is a new line character after a <carlist> element and it is handled as a data value. After it in the second line there are some spaces before the <car> element and it results again a characterData call.
Try to remove all hidden character from the test.xml.

The result will be the following:

-> openElement element: carlist, attribute: Array ()
-> openElement element: car, attribute: Array (type : Mercedes – )
-> characterData data is: [S 600]
-> closeElement element is: car
-> openElement element: car, attribute: Array (type : Mercedes – )
-> characterData data is: [E 270 CDI]
-> closeElement element is: car
-> openElement element: car, attribute: Array (type : BMW – )
-> characterData data is: [535 D]
-> closeElement element is: car
-> openElement element: car, attribute: Array (type : Lexus – )
-> characterData data is: [IS 220]
-> closeElement element is: car
-> closeElement element is: carlist

Now it is much more clear. Of course you don’t have to remove these characters from the XML. I did it just to demonstrate how the processing works.

Last steps

We are almost ready. Some further modification and we have the list of Mercedeses. To do that we have to change the handler functions code as follows:

function openElement($parser, $element, $attributes) {
global
$flag;
if ((
$element == ‘car’) && ($attributes['type'] == ‘Mercedes’)) $flag = true;
}

function closeElement($parser, $element) {
global
$flag;
$flag = false;
}

function characterData($parser, $data) {
global
$flag,$mblist;
if (
$flag) $mblist[] = $data;
}


The $flag and $type are defined outside of the functions and used inside as global variables.

  • - The openElement function sets the flag to true if a car with the type of Mercedes was found.
  • - The closeElement function resets the flag, to represent we are using not more the relevant element.
  • - The characterData function checks the flag and if it is set – means that the actual element is a car and the attribute type is Mercedes – than add the data value to the type array.

At the end we have an array with the requested list. Just display it. The complete code looks like this:

PHP Code:
<?php

$mblist = '';
$flag = false;

function openElement($parser, $element, $attributes) {
global
$flag;
if ((
$element == 'car') && ($attributes['type'] == 'Mercedes')) $flag = true;
}

function closeElement($parser, $element) {
global
$flag;
$flag = false;
}

function characterData($parser, $data) {
global
$flag,$mblist;
if (
$flag) $mblist[] = $data;
}

$parser = xml_parser_create();

xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($parser, "openElement", "closeElement");
xml_set_character_data_handler($parser, "characterData");

$document = file_get_contents("test.xml");
xml_parse($parser, $document);

xml_parser_free($parser);

foreach ($mblist as $value) {
echo
$value.'<br/>';
}

?>



And the output is:

S 600
E 270 CDI

Final words

I hope that this small tutorial helps you to understand the basics of PHP XML processing. If everything is clear then it will not cause any further problem to extend the script and process your own XML file.

Thanks for your time and attention!

Posted by admin on December-11-2009 Add Comments

Simple PHP Hit Counter Script

Important

It is worth noting that this tutorial is paginated, and that the entire tutorial contains 4 pages. Just remember that the tutorial is not finished just after the first page!

PHP 4+ is required for this tutorial. PHP 5 is optional

This tutorial is for beginners with very limited knowledge of PHP. You will learn to make a very simple hit counter. The counter isn’t important — it’s the techniques used that you should try to remember. Something similar will pop up later on, so try to remember what was said in this tutorial.

If you are rather experienced with PHP, you still can follow along. It never hurts to brush up on some old easy stuff. I will show you how to create a hit counter (not uniques, just simple hits) using a flatfile. Alright, let’s get started.

Step 1 A

Open your text editor and create a new document. I will use NotePad as an example here. Before you even start typing a single thing, press File -> Save As… and save your document first. We want to create all our files first before starting the code.

I saved my file as “counter.php“. The name doesn’t matter, but make sure you don’t forget the .php extension.

Step 1 B

Now, we need to create a new file to store the number of hits. This is called a flat file. A flat file is similar to a database, but it is a file instead. Press File -> New. This new file will consist of nothing but a number. This number will be the hit count. Because we’ve just started, type “0” in the document and press File->Save As…. Save this file as a TXT document. I named mine “hits.txt”, but you can name it differently.

Step 2

Alright, now that we’ve got our files set up, lets start codin’. Open up counter.php (or whatever you named it) and type the PHP tags.

PHP Code:
<?php

?>

Step 3

Now, we open up the flat file (hits.txt) and tell PHP to read the number inside and set it as a variable (we’ve already put “0″ into hits.txt)

PHP Code:
<?php
$hits
= file_get_contents("hits.txt");

?>

The function file_get_contents reads all the text in the file that is quoted in the first argument (the first argument being hits.txt in this case) and puts it into a string. A string is simply data. It could be text, numbers, anything. In this case, it is only one thing — a number.

When we put “$hits =”, this means we are defining a variable. In this case, we are saying that the variable $hits (all variables must have a dollar sign before it) is equal to everything inside hits.txt (which in this case, is only “0″).

Step 4

Because we are viewing the file, we have to add a new hit to the counter. Take a look at the following code and try to figure out yourself what it means:

PHP Code:
<?php
$hits
= file_get_contents("hits.txt");
$hits = $hits + 1;

?>

The new line that I’ve added — “$hits = $hits + 1;” should be easy to decipher. I am redefining the variable $hits. This time, $hits is “$hits + 1″. The line basically says in plain english — $hits should now be whatever’s inside the file “hits.txt” plus one. What’s inside the file? Well, it was only 0… which means $hits should equal 0 + 1.. = 1.

Step 5

Now that we’ve updated the hits count to add one, lets update hits.txt to the NEW hits count.

Take a look at this code:

PHP Code:
<?php
$hits
= file_get_contents("hits.txt");
$hits = $hits + 1;

$handle = fopen("hits.txt", "w");
fwrite($handle, $hits);
fclose($handle);

?>

The function fopen opens the file “hits.txt” in the first argument. The “w” tells PHP that we are only opening the file to write, and nothing else. It also tells PHP to erase everything in the file and set the pointer (something like little flashing black line you see when you type in a text box) to the beginning.

Why do we need a variable? Because we need a handle from fopen to execute other commands like — in this case — fwrite(). To write, you have to tell PHP that you’ve previously opened the file — and give PHP the handle. The line “fwrite($handle, $hits);” tells PHP to write to the file specified in the $handle (”hits.txt”) and it also tells PHP to write $hits into the file. $hits — as we’ve already discovered — is the old hit number plus one. Now you can see somewhat of a hit counter resemblance. Adding one hit to the old number.

fclose() tells PHP that we’re done with the file, so close it.

Side Note — in PHP 5, it is possible to fopen, fwrite and fclose all at once with the function file_put_contents

Step 6

This part’s the easiest — output the number.

PHP Code:
<?php
$hits
= file_get_contents("hits.txt");
$hits = $hits + 1;

$handle = fopen("hits.txt", "w");
fwrite($handle, $hits);
fclose($handle);

print $hits;

?>

It’s a no-brainer: “print $hits”. It prints out whatever’s in the variable $hits — in this case, it’s “1″. But as time goes on, when we execute this script over and over again — it will always add 1 and so — this is a hit counter.

Completed

Now, assuming you saved this file as counter.php, in any other file you want to count hits — put

PHP Code:
<?php include "counter.php"; ?>

This will include the counter file — which will only output a number, and nothing else. You can put something like
There are <?php include “counter.php”; ?> hits.

or something like that.

Posted by admin on December-11-2009 Add Comments

Basic Variables

Basic Variables

PHP Code:
<?php
$hello
="Hello people,";
$php="this is my first PHP script.";
$php2="FTW!";
echo
$hello." ".$php." ".$php2;
?>

Outputs….

Quote:
Hello people, this is my first PHP script. FTW!
I could of used just one variable, for example….
PHP Code:
<?php
$text
="Hello people, his is my first PHP script. FTW!";
echo
$text;
?>

But I wanted to use multi variables ftw. Learn how to join them and put spaces between then and shiz.

The . is used to join two comands/variable outputs, like this.

PHP Code:
<?php
$hello
="Hello people,";
$php="this is my first PHP script.";
$php2="FTW!";
echo
$hello.$php.$php2;
?>

That would output…

Quote:
Hello people,this is my first PHP script.FTW!
See, there is no spaces in between the variables. So we want to add spaces yea? Yea ftw. So we use ” ” to add spaces between the variables. But we also want to join the variables after the space so we use . ” ” . between the variables like this.
PHP Code:
<?php
$hello
="Hello people,";
$php="this is my first PHP script.";
$php2="FTW!";
echo
$hello. " " .$php. " " .$php2;
?>
Posted by admin on December-11-2009 Add Comments

Dynamic PHP Google Sitemap

Dynamic PHP Google Sitemap

This tutorial will show You how to generate google sitemaps based on Your site MySQL structure.

What is Google Sitemap?

About Google sitemap You can read on my Google Sitemap basics tutorial.

Now some theory

First – we need a site which we want to be “mapped”. I’ll show a basic one:

index.php – main page which is showing news.
tutorials.php – this page is showing a list of tutorials.
vievtutorial.php – and this one is showing selected tutorial.
contact.php – just a static page.

Schema:

vievtutorial.php file shows tutorial which ID is typed in ?id=NUMBER.
All news and tutorials are stored in MySQL database. Here are sample tables:

Tutorials:
——————————————————————-|
tutorials_id | tutorials_name | tutorials_text | tutorials_date |
——————————————————————-|
1 | tutorial 1 | some txt | 2007-09-03 08:36:27 |
——————————————————————-|
2 | tutorial 2 | and more… | 2007-09-15 17:06:16 |
——————————————————————-|

News:
—————————————————————-|
news_id | news_title | news_text | news_date |
—————————————————————-|
1 |
news1 | some text | 2007-09-03 08:36:27|
—————————————————————-|
2 |
news2 | some text | 2007-09-15 17:06:16|
—————————————————————-|

Database details:
Username: username
Password: password
Database: database

Time to write some code.

First we have to make our map skeleton with some needed stuff (for example database connection)

PHP Code:
<?php

$user = 'username';
$pwd = 'password';
$conn = mysql_connect('localhost', $user, $pwd) or die ('Cannot connect to server');
mysql_select_db('database') or die ('Cannot open database');

header('Content-Type: application/xml');
echo
'<?xml version="1.0" encoding="UTF-8"?>'."\n";
?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">

URLs goes here

</urlset>

Explanation:

First 4 lines are for MySQL connection (You may connect in diffrent way – It is Your choise)

header(’Content-Type: application/xml’); - this line “tells” to browser that this document should be readed as XML.

echo ‘<?xml version=”1.0″ encoding=”UTF-8″?>’.”\n”; - it is a simple trick. Because on this line we have <? and ?> signs, it may cause errors in PHP file – that’s why we write this line in single quotes as echo.

Rest is like in normal sitemap.

Adding pages

First the index.php file:


PHP Code:
<url>
<loc>http://www.yoursite.com/</loc>
<lastmod>
<?php
$sql
= "SELECT MAX( news_date ) as date FROM news";
$result = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_assoc($result);
echo
str_replace(' ', 'T', $row['date']).substr(date("O"), 0, -2).':00';
?>
</lastmod>
</url>

Explanation:
Here we have simple MySQL query (we take newest date from news table). One interesting command is the echo one.

echo str_replace(’ ‘, ‘T’, $row['date']).substr(date(”O”), 0, -2).’:00′; – str_replace and substr was used to change date from 2007-09-03 08:36:27 to 2007-09-15T09:31:11-05:00 format.

This same method we can use for tutorials.php file. We can take the date of newest tutorial.

Next thing is viewtutorial.php file.

PHP Code:
<?php
$sql
= "SELECT * FROM `tutorials`";
$result = mysql_query($sql) or die(mysql_error());
while(
$row = mysql_fetch_assoc($result)) {
?>
<url>
<loc>http://www.yoursite.com/viewtutorial.php?id=<?php echo $row['tutorials_id']; ?></loc>
<lastmod><?php echo str_replace(' ', 'T', $row['tutorials_date']).substr(date("O"), 0, -2).':00'; ?></lastmod>
</url>
<?php } ?>

We used a while loop here to take every single ID from tutorials table and make URL from it. In this example it will be two URLs: vievtutorial.php?id=1 and viewtutorial.php?id=2

Last thing is contact.php but this is only static file – there will be no problem with it.

Now it’s time for full code

PHP Code:
<?php

$user = 'username';
$pwd = 'password';
$conn = mysql_connect('localhost', $user, $pwd) or die ('Cannot connect to server');
mysql_select_db('database') or die ('Cannot open database');

header('Content-Type: application/xml');
echo
'<?xml version="1.0" encoding="UTF-8"?>'."\n";
?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
<url>
<loc>http://www.yoursite.com/</loc>
<lastmod>
<?php
$sql
= "SELECT MAX( news_date ) as date FROM news";
$result = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_assoc($result);
echo
str_replace(' ', 'T', $row['date']).substr(date("O"), 0, -2).':00';
?>
</lastmod>
</url>

<url>
<loc>http://www.yoursite.com/tutorials.php</loc>
<lastmod>
<?php
$sql
= "SELECT MAX( tutorials_date ) as date FROM tutorials";
$result = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_assoc($result);
echo
str_replace(' ', 'T', $row['date']).substr(date("O"), 0, -2).':00';
?>
</lastmod>
</url>

<?php
$sql
= "SELECT * FROM `tutorials`";
$result = mysql_query($sql) or die(mysql_error());
while(
$row = mysql_fetch_assoc($result)) {
?>
<url>
<loc>http://www.yoursite.com/viewtutorial.php?id=<?php echo $row['tutorials_id']; ?></loc>
<lastmod><?php echo str_replace(' ', 'T', $row['tutorials_date']).substr(date("O"), 0, -2).':00'; ?></lastmod>
</url>
<?php } ?>

<url>
<loc>http://bwebi.com/contact.php</loc>
<lastmod>2007-09-14T21:56:53<?php echo substr(date("O"), 0, -2).':00'; ?></lastmod>
</url>
</urlset>

With this knowlage You can make dynamic sitemaps for any page You want.

Posted by admin on December-11-2009 Add Comments

How to create a basic rating system


Creating a basic rating system

In this tutorial I will show you how to create a basic and simple to use rating system. To make the script more simple and database independent we will use simple files to store rating information.

The basics

We divide the task into the following steps:

  • Display a HTML form where a visitor can select a rating.
  • Read the existing results
  • Check if the actual IP exists among the old results
  • Add valid rating to the result list
  • Display the actual rating

Displaying the HTML form

This is an easy part. As usually we create a normal HTML form which is displayed during the first call of the script. As the script itself contains the processing part as well we first check if we really need to display the form or process the submitted data. So the first part of the code looks like this:

PHP Code:
echo $_SERVER['PHP_SELF']; ?>" method="post">

Your rating: 1 2 3 4 5
Read the existing results

As next step we need to process the user input. In this step we initialize some variables to store total ratings, total points, actual rating. This is quite simple:

PHP Code:
$rate = isset ($_POST['rate']) ? $_POST['rate'] : 0;
$filename = "ratings";
$alreadyRated = false;
$totalRates = 0;
$totalPoints = 0;

$ip = getenv('REMOTE_ADDR');
?>

Afterwards we retrieve the actual user IP. Later we will check if this IP is already present in the result list. If we already have a result from the actual IP then we will ignore the new one. With this solution we can avoid to manipulate our ratings. So we need to open the result file and read it into an array using the file function. Next we iterate over this array and summarize the ratings until now. Of course we also check the IP:

PHP Code:
// Read the result file
$oldResults = file('results/'.$filename.'.txt');

// Summarize total points and rates
foreach ($oldResults as $value) {
$oneRate = explode(':',$value);
// If our IP is in the list then set the falg
if ($ip == $oneRate[0]) $alreadyRated = true;
$totalRates++;
$totalPoints += $oneRate[1];
}
?>

Now if the rating seems to be valid the we open the result file again and append a new result entry at the end of the file with the actual user IP. Besides this we update the total rating points we have collected in the previous step.

PHP Code:
// If our rating is valid then append it to the result file
if ((!$alreadyRated) && ($rate > 0)){
$f = fopen('results/'.$filename.".txt","a+");
fwrite($f,$ip.':'.$rate."\n");
fclose($f);
$totalRates++;
$totalPoints+=$rate;
}
?>

As last step we only need to display the actual rating. We can do this with only displaying a the value or we can displaying some graphics like stars to make it a bit nicer.

PHP Code:
echo "Actual rating from $totalRates rates is: "
.substr(($totalPoints/$totalRates),0,3)."
"
;

// Display the actual rating
for ($i=0;$i<round(($totalPoints/$totalRates),0);$i++){
echo
"";
}
?>

That’s all!
You can download a complete rating system as well.

Posted by admin on December-11-2009 Add Comments

Generating a Range of Numbers the Easy Way

Range is native PHP function that takes 2 compulsory arguments and 1 optional argument (As of PHP 5.0.0 which defaults to 1) and returns an array. Seem ever so simple? Well it is! The 3 arguments the function can accept are as follows:

  • $low: The number or character to begin at.
  • $high: The number or character to end at.
  • $step: The amount to count up or down in.

Take the example below which will begin at 0, increment in steps of 10 until we get to 50.

PHP Code:
foreach(range(0, 50, 10) as $iNumber)
{
echo
$iNumber . ' ';
}

This would result in 0 10 20 30 40 50 being returned. If you were to assign the result of the range() into an array then all you would see is a nicely constructed array:

PHP Code:
Array
(
[
0] => 0
[1] => 10
[2] => 20
[3] => 30
[4] => 40
[5] => 50
)

The great thing about this function is that it can also count up or down in characters. For instance, if we wanted to list the 6 characters from A to F, then we could do that quite easily with range():

PHP Code:
$aArray = range('A', 'F');
echo
implode(', ', $aArray);

Which would output A, B, C, D, E, F. Range can also go backwards, which can be a whole lot of fun. Not quite as exciting as a rollercoaster going backwards but I hope you appreciate me trying to liven the article up!

PHP Code:
$aArray = range(100, 0, 25);
echo
implode(', ', $aArray);

As I’m sure you can surmise, the above example would give us 100, 75, 50, 25, 0!

There is not a lot more to it. Such an easy function but one that is perhaps not as well heard of as it should be. I can’t say that I’ve really needed this function before, however, perhaps there might be an occasion where you would require its presence! I’d be interested to know if anyone out there has any really good uses for it? I’ve seen some good uses on php.net for such tasks as checking if an array is associative – but any more would be great to hear!

Posted by admin on December-11-2009 Add Comments

Lowdown on Passing Things as Reference

As a boy of 18 learning PHP, many tutorials over-complicated the PHP passing by reference side of things and just became all too puzzling to say the least. Let’s start us off with a little quiz.

PHP Code:
$szVar1 = 'expbuilder.com';
$szVar2 = 'google.com';

$szVar1 =& $szVar2;
$szVar1 = 'expbuilder.com';

In the above code, if I were to echo out $szVar2, what would its value be? If you’d have said to me google.com then you’d have been dead wrong, sadly. Surprisingly, expbuilder.com is the correct answer. Even though we’re assigning the value of expbuilder.com to $szVar1, because we’ve made $szVar1 as a reference to $szVar2, we can effectively now control $szVar2 as well.

Passing by reference is really quite simple on the face of it. You’re not assigning $szVar1 the value of $szVar2, which would have been WiredFlame.com. You are assigning it $szVar2 as a reference.

Essentially now, when $szVar1 changes, $szVar2 follows suit.

You may ask why this is useful. Well, the chances are you’ve blissfully been using PHP functions out-of-the-box and they’ve been accepting variables or arrays as reference. These are the functions that just work, they do not return anything, usually – which means you do not have to catch the function’s return value like so:

PHP Code:
$aArray = array_pop($aArray);

Albeit some will still return true and false. What you normally do is:

PHP Code:
array_pop($aArray);

Your $aArray will then be changed for you. This is a good example of an array being passed as a reference, and not by its value.

A good example where you could use passing by reference, is in the foreach loop. Most people would do the following:

PHP Code:
$aArray = array('Raspberries', 'Oranges', 'Apples', 'Kiwis');

foreach($aArray as $szKey => $szValue)
{
$aArray[$szKey] .= '.';
}

This would place full-stops on the end of all of our fruits. However, what we could do is pass by reference and use it that way. Like so:

PHP Code:
$aArray = array('Raspberries', 'Oranges', 'Apples', 'Kiwis');

foreach($aArray as &$szItem)
{
$szItem .= '.';
}

As we are passing the elements in by reference, from the array, we can set them just by assigning $szItem to the action we wish to perform. PHP will handle the rest and change all the items in our array for us! How so very clever is that?

Of course, you may also pass by reference in functions. This is done like so:

PHP Code:
function array_add_fullstop(&$aArray)
{
if(
count($aArray) == 0)
{
return
false;
}

foreach($aArray as &$szItem)
{
$szItem .= '.';
}

return true;
}

You see, we’ve even gone the extra mile and added in a return as true or false. This can be checked like so:

PHP Code:
if(array_add_fullstop($aArray) === true)
{
// Do something fancy!
}
else
{
// Do some disastrous!
}

You don’t need to worry about the array because if the function has returned true then your array has been modified to include the full-stops at the end of each array element.

Passing by reference, as you can see, need not be a complex topic. It’s pretty straightforward as aforementioned. The next time you see the ampersands (&) prepended to variables and arrays, as well as in function arguments and loops, just remember, whatever the topic, complex or not, expbuilder.com is the next best thing to having Albert Einstein sat next to you when you’re doing your Maths homework. That’ll show those condescending teachers!

Posted by admin on December-11-2009 Add Comments

Creating RSS documents with the DOM API

This article illustrates how you can use the PHP5 DOM API to create your own RSS files. When used in conjunction with RSS Parsing with SimpleXML, these articles will help you create and read RSS files by utilizing two of the new PHP5 extensions, SimpleXML and DOM.

If you haven’t read my article on reading RSS files with SimpleXML, you can do so here.

Like my previous article, the code is heavily commented to help explain what it does.

PHP Code:
<?php

// Create a new DOM Document object, this will create a new XML declaration for us.
// We can enter the version number and encoding (in that order), by passing
// two arguments to the object's constructor.  By default, the XML
// declaration's version attribute will be set to "1.0"
$pDom = new DOMDocument();

// Here we create a new root elelement named rss.
$pRSS = $pDom->createElement('rss');

// We now add a new attribute to the rss element.  We name this new
// attribute version and give it a value of 0.91.
$pRSS->setAttribute('version', 0.91);

// Finally we append the attribute to the XML tree using appendChild
$pDom->appendChild($pRSS);

// We repeat the same process again here, but this time we're creating
// the channel element.
$pChannel = $pDom->createElement('channel');

$pRSS->appendChild($pChannel);

// Create the main child nodes of channel, these contain the information
// related to this RSS file.  I'm not going to comment each one of these
// as they should be easy enough to understand.  Basically we're creating
// a new element for each node, the first argument specifies the name of
// the element we're creating, and the second specifies the text value
// of the node, for example, title would render as:  <title>TalkPHP</title>
$pTitle = $pDom->createElement('title', 'TalkPHP');
$pLink = $pDom->createElement('link', 'http://www.talkphp.com');
$pDesc = $pDom->createElement('description', 'Discuss PHP and other various web related topics in a knowledgeable and friendly community.');
$pLang = $pDom->createElement('language', 'en');
$pImage = $pDom->createElement('image');

// Here we simply append all the nodes we just created to the channel node
$pChannel->appendChild($pTitle);
$pChannel->appendChild($pLink);
$pChannel->appendChild($pDesc);
$pChannel->appendChild($pLang);
$pChannel->appendChild($pImage);

// Create three new elements that are needed to "describe" our image
$pURL = $pDom->createElement('url', 'http://www.talkphp.com/images/misc/rss.jpg');
$pTitle = $pDom->createElement('title', 'TalkPHP');
$pLink = $pDom->createElement('link', 'http://www.talkphp.com');

// Append these new elements to the image element
$pImage->appendChild($pURL);
$pImage->appendChild($pTitle);
$pImage->appendChild($pLink);

// This array represents the data contained within the imaginary database.
$aLatestThreads = array
(
array
(
'title' => "G'day",
'link' => 'http://www.talkphp.com/showthread.php?t=1107&amp;goto=newpost',
'description' => "Forum: Member Introductions
Posted By: Shaun
Post Time: 09-14-2007 at 01:15 PM"
),

array
(
'title' => "AJAX File Uploader",
'link' => 'http://www.talkphp.com/showthread.php?t=1106&amp;goto=newpost',
'description' => "Forum: Javascript, AJAX, E4X
Posted By: CreativeLogic
Post Time: 09-13-2007 at 07:32 PM"
,
),

array
(
'title' => "Some sites of mine...",
'link' => 'http://www.talkphp.com/showthread.php?t=1104&amp;goto=newpost',
'description' => "Forum: Show Off
Posted By: Craddock
Post Time: 09-13-2007 at 04:54 PM"
)

);

// Loop trough each result from our imaginary database, these are the
// RSS items that the viewer of the RSS will see
foreach ($aLatestThreads as $aThread)
{
// Nothing new here, we're creating an item element as our parent
// and then creating and adding three child nodes to it.
$pItem = $pDom->createElement('item');
$pTitle = $pDom->createElement('title', $aThread['title']);
$pLink = $pDom->createElement('link', $aThread['link']);
$pDesc = $pDom->createElement('description', $aThread['description']);

// Append the nodes to the item, then append the item to the channel

$pItem->appendChild($pTitle);
$pItem->appendChild($pLink);
$pItem->appendChild($pDesc);

$pChannel->appendChild($pItem);
}

// Set content type to XML, thus forcing the browser to render is as XML
header('Content-type: text/xml');

// Here we simply dump the XML tree to a string and output it to the browser
// We could use one of the other save methods to save the tree as a HTML string
// XML file or HTML file.
echo $pDom->saveXML();
?>

You can see the output from this script here.

In conclusion, the DOM extension gives you the power you need to manipulate the DOM. Used in conjunction with SimpleXML, you have all the functionality you should need, these two extensions allow you to to easily read, write and modify XML documents using PHP 5.

Posted by admin on December-11-2009 Add Comments

Log User Data with PHP & MySQL

Hello, if any of you guys want to know who is visiting your website and when, then this is for you.

This will consist of 3 files for adding and displaying the logs (plus any other page you wish to log user data from).

The first thing we have to do is create a new table in your database:

Code:
CREATE TABLE logs (
      id INT(11) NOT NULL AUTO_INCREMENT,
      page VARCHAR(50) NOT NULL,
      DATE DATETIME NOT NULL,
      ip VARCHAR(70) NOT NULL,
      PRIMARY KEY (id)
);

This creates a new table called logs with an auto_increment id (id is automaticly added when ever data is inserted into the table), page, date and ip.

Now we create a config file to connect to your database:
Call it config.php

PHP Code:
<?php
DEFINE
('DB_USER', 'insert username here');// database username
DEFINE ('DB_PASSWORD', 'insert password here');//database password
DEFINE ('DB_HOST', 'localhost');//database host, usually localhost
DEFINE ('DB_NAME', 'insert database name here');//and finally the database name
$dbc = @mysql_connect (DB_HOST, DB_USER, DB_PASSWORD) OR die ('Could not connect to MySQL: ' . mysql_error());
@
mysql_select_db (DB_NAME) OR die('Could not select the database: ' . mysql_error() );
?>

Firstly this defines the database info: username, password, host and name. Then it connects to the database using the data provided by you using the mysql_connect function but if it does not connect it will echo the mysql error and stop the script. Finally it selects the database using the DB name you gave, if i can’t find the DB it will echo the error and stop page generation with the die function.

Now we will create a functions file:
functions.php

PHP Code:
<?php
$page
= $_SERVER['PHP_SELF'];
$ip = $_SERVER['REMOTE_ADDR'];
$logq = "INSERT INTO logs (page, DATE, ip) VALUES ('$page', NOW(), '$ip')";
$logr = @mysql_query($logq);
?>

This creates a bunch of variables:
$page – This uses the $_SERVER['PHP_SELF'] php variable to get the location of the page being logged.
$ip – This uses the $_SERVER['REMOTE_ADDR'] php variable to get the users ip.
$logq – This is just a simple string that is in the format of a MySQL query, it gets the page, DATE and ip columns from the database and then gets the values to insert: $page, $ip and NOW() (NOW() is a MySQL function that gets the current date in local time) and inserts those values into the database.
$logr – This runs the $logq as a proper MySQL query.

Now we need a file to display the logs:

PHP Code:
<?php
$lq
= "SELECT id, ip, page, DATE_FORMAT(date, '%d %M, %Y') as sd FROM logs ORDER BY id DESC LIMIT 50";
$lr = @mysql_query($lq);

if($lr){
echo
"<table><th>IP</th><th>Page</th><th>Date</th>";
while(
$lf = mysql_fetch_array($lr, MYSQL_ASSOC)){
echo
"<tr><td>" . $lf['ip'] . "</td><td>" . $lf['page'] . "</td><td>" . $lf['sd'] . "</td></tr>";
}
echo
"</table>";
}
else
{
echo
"No results!";
}
?>

All this does is selects data from the logs table in your database and displays the data in a table.
We run a while loop for every time it can get new data from the query. We create a new variable, $lf, to create an associative array of the mysql data using mysql_fetch_array.

Now finally we just need the code to add logs, insert this code into any of your .php or .php3 pages where you want the user data to be logged, Remember to place this code at the top of your page BEFORE ANY html output.

PHP Code:
<?php
include_once("config.php");
include_once(
"functions.php");
?>

This code includes the config.php file (the one with the database info and connections) and functions.php (the one that actually logs the data).

I hope this helps, if there is any problems or I have missed anything out, just tell me.

Posted by admin on December-11-2009 Add Comments

Sanitisation and Validation in PHP

Sanitisation and Validation in PHP

Step 1

What Do Sanitisation and Validation Mean? Sanitisation and Validation are important terms to understand when writing PHP applications. Both in the context of this tutorial are about processes performed on user input. Sanitisation is cleaning user input to make it safe to process, and Validation is checking the data to see if it is: in the correct format; of the correct type etc. It is important to sanitise and validate data coming in from users of your PHP applications, because if it is left unchecked, the input may be used to facilitate an exploit. Some of the most common exploits involving user input are: code injection, sql injection and header injection. And we will have a look at some of these during the tutorial.

Step 2

Validation is a vital topic when handling user input. It helps to improve security, improve usability and reduce the amount of bugs in your program. To validate something, we first work out a criteria which our user input has to conform to. For example, we might want the user input to be a number between 10 and 99, we then test the user input against these rules, and if the input fails the check(s) we will not use the data and inform the user that they have input something incorrect. Ok, but what does that mean in terms of code? Well here’s an example of the code you might use to test a number to see if it is between 10 and 99.

PHP Code:
<?php
// check the input
if($_POST['number'] >= 10 && $_POST['number'] <= 99)
{
// the number is fine, continue
echo $_POST['number'];
}
else
{
// the number provided is not within range
die('The number provided is not valid.  Please provide a number between 10 and 99.');
}
?>

Step 3

Validation is especially useful because once we are certain of what format the user input is in we might not have to sanitise it. For example, in the previous code snippet we no longer need to sanitise $_POST['number'] because to have passed the validation it would have to be a number, and is therefore harmless. A practical example of this might be in an email form, where we are taking user input and then placing it in an email header. For example, this script is vulnerable to header injection:

PHP Code:
<?php
// the email to send to
$myemail = 'ted@platypus.org.uk';

// from header
$from = 'From: ' . $_POST['name'] . ' <' . $_POST['email'] . '>';

// send the email
mail($myemail,$_POST['subject'],$_POST['message'],$from);
?>

This script is not validating any of the input it is given, so a user could send an email with a line break within it. This would then allow them to add extra headers to the email, which is not desired. More fundamentally, it just makes no sense not to validate the input. If someone has sent an email like “not_a_valid_email” the email should just not be sent. To combat this we can validate the input provided by the user, to see if it makes sense to allow it. This could be done with string functions, but it is much easier to introduce Regular Expressions (see link to tutorial about regexps). We can use RegExps in the previous example to make the script much more sensible:

PHP Code:
<?php
// the email to send to
$myemail = 'ted@platypus.org.uk';

if(!preg_match('/^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/',$_POST['email']))
die(
'Invalid email proved, the email must be in valid email format (such as name@domain.tld).');
if(!
preg_match('/^[-_ 0-9a-z]$/i',$_POST['name']))
die(
'Invalid name proved, the name may only contain a-z, A-Z, 0-9, "-", "_" and spaces.');

// from header
$from = 'From: ' . $_POST['name'] . ' <' . $_POST['email'] . '>';

// send the email
mail($myemail,$_POST['subject'],$_POST['message'],$from);
?>

The script is now both safer and more suitable.
Step 4

Sanitisation is as we said above, cleaning user input to make it safe to process further, but what does that actually mean? Well, below we have a vulnerable PHP/MySQL login form. First, we’ll show it in its vulnerable state, then improve on it and show why it is now safer than it was previously.

PHP Code:
<?php
// connection to MySQL server
mysql_connect('localhost','username','password');
mysql_select_db('database');

// User input
$username = $_POST['username'];
$password = md5($_POST['password']);

// Construct and run query.
$sql = 'SELECT id FROM users WHERE username="'.$username.'" AND password="'.$password.'"';
$result = mysql_query($sql);

// If there is a user, log them in.
if(mysql_num_rows($result) > 0)
{
$_SESSION['login'] = true;
// Redirect to admincp
header('Location: http://somesite.com/admincp/');
}
else
die(
'Incorrect username or password.');
?>

Now, on the face of things that may look safe, it’s checking the username and password in the database, and only logging the user in if a user is found. However, if someone were to enter a username of ‘” OR password LIKE “%” — ‘ then the query becomes:

Code:
SELECT id FROM users WHERE username="" OR password LIKE "%" -- " AND password="9cdfb439c7876e703e307864c9167a15"

That query fetches the id of all users in the users table (since LIKE “%” matches all rows and — comments the rest of the line) meaning it would log them in regardless of the actual values in the database. To prevent things like this, we can use sanitisation functions like mysql_real_escape_string(). Applying this function to the user input means that characters like ” which can be used to inject SQL are escaped to with a backslash (e.g. \”). So with the following code:

PHP Code:
<?php
// connection to MySQL server
mysql_connect('localhost','username','password');
mysql_select_db('database');

// User input
$username = mysql_real_escape_string($_POST['username']); // sanitised input
$password = md5($_POST['password']); // already safe due to md5()

// Construct and run query.
$sql = 'SELECT id FROM users WHERE username="'.$username.'" AND password="'.$password.'"';
$result = mysql_query($sql);

// etc...
?>

The same input is sanitised, and the query becomes this:

Code:
SELECT id FROM users WHERE username="\" OR password LIKE \"%\" -- " AND password="9cdfb439c7876e703e307864c9167a15"

The code is no longer vulnerable to that SQL injection exploit. mysql_real_escape_string() is only applied to $username because $password is hashed, and hashing also sanitises data. Anything passed through a hashing function like md5() or sha1() is returned in hexadecimal. Meaning that only 0-9 and a-f characters can be returned by the function. This means any threatening characters like quotes and slashes are sanitised and we can use the resultant hash in a query without fear of injection

Step 5

There are also other ways of sanitising input, and a very useful one is typecasting. Taking another SQL example, say we were allowing the users to specify an offset to display data. In a query something like this:

PHP Code:
<?php
// code...
$sql = 'SELECT id,title FROM news LIMIT '.$_GET['offset'].',10';
$result = mysql_query($sql);
// more code...
?>

We could use the same function as before to sanitise this $_GET variable, but it is more appropriate to use typecasting to force it to be an integer. We can do this using intval(). Intval takes a variable, and returns its value as an integer. So, a string “14″ will become the number 14, and any input that is not numeric will become 0, making the input safe to work with.

PHP Code:
<?php
// code...
$sql = 'SELECT id,title FROM news LIMIT '.intval($_GET['offset']).',10'; // sanitised input
$result = mysql_query($sql);
// more code...
?>

An important thing to remember about sanitisation, is that it is not just required only when inputing data into something like a database! Outputting an unsanitised variable can be just as dangerous as taking it as input for another purpose. For example, say we had a simple script that took a $_GET variable called “name”, then output “Hello, [name]!”. If you do not sanitise the user input then a user can craft a malicious URL to your script that will send cookies associated with your domain to them. How could they do that? By placing HTML code in the URL which executes some Javascript when the page is loaded.

PHP Code:
<?php
// Dangerous!  $_GET['name'] has not been sanitised.
echo 'Hello, ',$_GET['name'],'!';
?>

Now we know what the vulnerability, how can we stop it? Luckily PHP provides a very useful function for just this purpose, called htmlspecialchars(). This function replaces possibly dangerous characters like < with their HTML Entities. In the case of < it would become &lt;. Below we can see htmlspecialchars in use, sanitising out user input to make the script safe.

PHP Code:
<?php
// This is now safe because the user input has been sanitised.
echo 'Hello, ',htmlspecialchars($_GET['name'], ENT_QUOTES),'!';
?>

Step 6

Conclusion: It is evident that both validation and sanitisation are very important considerations in any PHP application. If possible, you should validate over sanitising, but if you are in doubt as to what you want to recieve, or you want to allow possibly dangerous characters then you should definately sanitise it. Sanitising where you shouldn’t is much less trouble than not sanitising where you should! Sanitisation and validation should be a part of your planning stages, you might want to consider jotting down all the input you are taking from the user, and noting down exactly what you expect and make a note of whether the input might require sanitisation. Do this and you will be making much more secure, and more useful PHP applications with fewer bugs and which do not allow as much spam input from undesirable users.

Step 7

Closing Notes:

  • Be careful of validation by type when dealing with $_GET and $_POST variables. If someone inputs a number, is_numeric will not be true, because all information passed as GET and POST vars are strings. So to find out if a valid number has been supplied, you can either cast and then see if it is greater than 0. Or alternatively, check the string to see if it is only made of 0-9 characters.
  • Try to use application specific sanitisation functions where possible. For example mysql_real_escape_string is more likely to return a safe string to use in a mysql query than addslashes, because the latter does not check the character set used in the database. This can lead to inconsistencies that let injection through.
  • All PHP examples are assumed to be run on a system with magic_quotes_gpc OFF.
  • For more information consult the PHP manual